2018巅峰极客writeup(Misc)

作为一个渣渣,带着去看各路神仙打架的想法报名了这次的“巅峰极客”CTF比赛,7.21第一场线上赛果然从头水到尾。。。
为了方便之后巩固,把近几天学习大佬们的writeup过程中总结整理的内容记录下来,第一次写成博客。

我只是个搬运工,以下是众大佬们的writeup:


1. warmup - 100pt

首先拿到一张图片warmup.bmp,用Stegsolve打开:

1
java -jar Stegsolve.jar

经大佬们发现,RGB三个通道的LSB都是明显有数据在的(我眼瞎,当时翻来覆去好几遍真没看出来):

分别查看各个通道的LSB,发现了存放的数据是用Ook!和brainfuck加密的:

分别将三块数据保存出来,用这个网站在线解密,得到以下三部分内容,拼起来就是flag:

  • flag{db640436-
  • 7839-4050-8339
  • -75a972fc553c

flag{db640436-7839-4050-8339-75a972fc553c}


2. loli - 150pt

拿到一张图片1.png,看起来像二维码,用工具扫描,得到一个提示255:

根据另一个提示0xFF,将1.png放到WinHex中,0xFF异或整个文件,在得到的文件末尾看到字符串“black and white”:

用binwalk查看得到的文件,发现其中包含了一个图片:

根据隐藏图片的偏移,用WinHex将图片提取出来,能够看到,图片中,按照行来看,以8个像素点(黑/白)为一组,每一组之间用白点来分隔,根据前面“black and white”提示,不难联想到应该是二进制流转换成字符:

参考各大佬的writeup,写出脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from PIL import Image

def getBinaryToChar():
count = 0
ans = ""
binStr = ""
# 转换成灰度图像(黑点0,白点255)
img = Image.open('./noname.png').convert('L')
# 遍历像素点(按行读)
width, height = img.size
for h in range(height - 1): # 是否-1均可,最后一行是11个0111111111
for w in range(width - 1): # 需要-1,最后一列全是0
pixel = img.getpixel((w, h))
if pixel == 0:
color = 1 # 黑点置1
else:
color = 0 # 白点置0
# 9个点一组,构成0xxxxxxxx
count += 1
ans += str(color)
if count == 9:
if ans != "011111111":
binStr += chr(int(ans,2))
count = 0
ans = ""
return binStr

if __name__ == '__main__':
strr = getBinaryToChar()
print strr
with open('res.txt', 'wb') as f:
f.write(strr)

打开得到的res.txt,看到flag:

flag{e0754197-e3ab-4d0d-b98f-96174c378a34}


3. flows - 200pt

拿到一个pcap包,用wireshark打开,发现是USB协议。
按照协议排序,一个个点击查看包内容,发现有个包(95)里面疑似有tips:

将数据另存到txt中,打开可以看到两个提示:

继续查看包,发现两个长度比较大的包很可疑(55和74):

分别将数据另存出来,用010打开,看到这两个文件头都是D4C3B2A1,就是pcap文件:

修改后缀为.pcap,用wireshark打开,有效数据存放在leftover capture data这里:

用wireshark自带的tshark把leftover capture data的内容提取出来:
【tshark命令行详细参见这里

1
2
tshark -r pack55.pcap -T fields -e usb.capdata > pack55.txt
tshark -r pack74.pcap -T fields -e usb.capdata > pack74.txt

接下来就需要从txt文件中过滤出键盘击键和鼠标相关的流量:
【这里主要参考这位大佬的博客以及这个博客

  • 键盘数据包的数据长度为8个字节。每次key stroke都会产生一个keyboard event usb packet(所以第一个tips所说第一个字节为02表示按下了Left Shift键)

    Byte1

    |--bit0:  Left Control  是否按下,按下为1 
    |--bit1:  Left Shift    是否按下,按下为1 
    |--bit2:  Left Alt      是否按下,按下为1 
    |--bit3:  Left GUI      是否按下,按下为1 
    |--bit4:  Right Control 是否按下,按下为1  
    |--bit5:  Right Shift   是否按下,按下为1 
    |--bit6:  Right Alt     是否按下,按下为1 
    |--bit7:  Right GUI     是否按下,按下为1 
    

    Byte2 暂不清楚,有的地方说是保留位
    Byte3-Byte8 这六个为普通按键,击键信息集中在第3个字节

  • 鼠标数据包的数据长度为4个字节。鼠标移动时表现为连续性,与键盘击键的离散性不一样,不过实际上鼠标动作所产生的数据包也是离散的

    Byte1 代表按键,当取0x00时,代表没有按键、为0x01时,代表按左键,为0x02时,代表当前按键为右键
    Byte2 可以看成是一个signed byte类型,其最高位为符号位,当这个值为正时,代表鼠标水平右移多少像素,为负时,代表水平左移多少像素
    Byte3 与第二字节类似,代表垂直上下移动的偏移

第一个包pack55.txt为键盘数据包,需要按照对应关系将键盘按键输出出来,根据第一个tips,注意第一个字节为02表示按了shift键,在大佬的脚本基础上稍作修改,脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
mappings = { 0x04:"a", 0x05:"b", 0x06:"c", 0x07:"d", 0x08:"e",
0x09:"f", 0x0A:"g", 0x0B:"h", 0x0C:"i", 0x0D:"j",
0x0E:"k", 0x0F:"l", 0x10:"m", 0x11:"n", 0x12:"o",
0x13:"p", 0x14:"q", 0x15:"r", 0x16:"s", 0x17:"t",
0x18:"u", 0x19:"v", 0x1A:"w", 0x1B:"x", 0x1C:"y",
0x1D:"z", 0x1E:"1", 0x1F:"2", 0x20:"3", 0x21:"4",
0x22:"5", 0x23:"6", 0x24:"7", 0x25:"8", 0x26:"9",
0x27:"0", 0x28:"\n",0x29:"[ESC]", 0x2A:"\b", 0X2B:"\t",
0x2C:" ", 0x2D:"-", 0x2E:"=", 0x2F:"[", 0x30:"]",
0x31:"\\",0x32:"`", 0x33:";", 0x34:"'", 0x36:",",
0x37:".", 0x38:"/" }

mappings_shift = { 0x04:"A", 0x05:"B", 0x06:"C", 0x07:"D", 0x08:"E",
0x09:"F", 0x0A:"G", 0x0B:"H", 0x0C:"I", 0x0D:"J",
0x0E:"K", 0x0F:"L", 0x10:"M", 0x11:"N", 0x12:"O",
0x13:"P", 0x14:"Q", 0x15:"R", 0x16:"S", 0x17:"T",
0x18:"U", 0x19:"V", 0x1A:"W", 0x1B:"X", 0x1C:"Y",
0x1D:"Z", 0x1E:"!", 0x1F:"@", 0x20:"#", 0x21:"$",
0x22:"%", 0x23:"^", 0x24:"&", 0x25:"*", 0x26:"(",
0x27:")", 0x28:"\r",0x29:"[ESC]", 0x2A:"\b", 0X2B:"\t",
0x2C:" ", 0x2D:"_", 0x2E:"+", 0x2F:"{", 0x30:"}",
0x31:"|", 0x32:"~", 0x33:":", 0x34:"\"",0x36:"<",
0x37:">", 0x38:"?" }

def keyboard_extract():
output = ""
keys = open('pack55.txt')
for line in keys:
if len(line)!= 24:
continue
list = line.split(":")
if list[2]=='00':
continue
num = int(list[2],16)
if num in mappings:
if list[0] == '02':
output += mappings_shift[num]
elif list[0] == '00':
output += mappings[num]
else:
output += '[unknown]'
keys.close()
print output

if __name__ == '__main__':
keyboard_extract()

第二个包pack74.txt为鼠标数据包,按照第二个tips,只关注第一个字节,猜测01表示0,02表示1,将其提取出来,脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import re

def bin2str(bin):
str = ''
mo = len(bin)%8
if (mo):
bin= '0'*(8-mo) + bin
chars = re.findall(r'.{8}',bin)
for char in chars:
str += chr(int(char, 2))
return str

def mouse_extract():
binstr = ""
keys = open('pack74.txt')
for line in keys:
list = line.split(":")
if list[0]=='00':
continue
elif list[0] == '01':
binstr += "0"
elif list[0]=='02':
binstr += "1"
str = bin2str(binstr)
keys.close()
print str

if __name__ == '__main__':
mouse_extract()

分别运行以上两个脚本,得到两个字符串,拼起来就是flag了:

  • flag{u5b_key
  • bo4rd_m0use}

flag{u5b_keybo4rd_m0use}


The End
0%