天天看點

CTF必備技能丨Linux Pwn入門教程——環境配置

Pwn入門難,入門後更難,資料零散,習題少,調試環境難搭建…i春秋精品好課,幫你快速解決Pwn入門難題!

說在前面

這是一套Linux Pwn入門教程系列,作者依據Atum師傅在i春秋上的Pwn入門課程中的技術分類,并結合近幾年賽事中出現的一些題目和文章整理出一份相對完整的Linux Pwn教程。

問:為什麼要花費精力去整理這套系統的Linux Pwn教程?

答:網上關于Pwn的資料比較零散;經常會碰到解題過程略的Writeup和沒有注釋;存在大量寫死偏移的腳本;練習題目難找;調試環境難搭建;GDB沒有IDA好操作等等問題。

問:這個Linux Pwn教程适合新手學習嗎?

答:本套課程非常适合萌新們進行入門學習,内容通俗易懂,化繁為簡,同時結合曆年賽題,實操性強,對于快速提升技能可起到很大的幫助。

問:關于Linux Pwn教程是否有視訊講解?

答:在i春秋官網的課程庫中有很多關于Pwn的視訊課程,立即去學習!https://www.ichunqiu.com/courses/pwn?from=weixin

本系列教程僅針對i386/amd64下的Linux Pwn常見的Pwn手法,如棧,堆,整數溢出,格式化字元串,條件競争等進行介紹,所有環境都會封裝在Docker鏡像當中,并提供調試用的教學程式,來自曆年賽事的原題和帶有注釋的python腳本。

教程中的題目和腳本若有使用不妥之處,歡迎各位大佬批評指正。

今天是Linux Pwn入門教程第一章:環境配置,閱讀用時約7分鐘。

一、Docker容器的使用與簡單操作

在搭建環境之前我們需要準備一個裝有Docker的64位Linux系統,核心版本高于3.10(可以通過uname -r檢視),可以運作在實體機或者是虛拟機中。關于Docker的安裝與啟動此處不再贅述,讀者可以根據自己的Linux發行版本自行搜尋。

在成功安裝了Docker并驗證其可用性後,我們就可以定制自己的實驗用容器了。這部分内容可以在各個地方找到教程,且與Pwn的學習不相關,此處不再贅述。為了友善實驗,我把實驗環境打包成了幾個容器快照,可以直接導入成鏡像使用。

以ubuntu.17.04.amd64為例,導入的指令為:

cat ubuntu.17.04.amd64 | docker import - ubuntu/17.04.amd64

 導入成功後使用指令docker images會看到鏡像倉庫中出現了一個新的鏡像。

 運作docker run -it -p 23946:23946 ubuntu/17.04.amd64 /bin/bash

就可以以這個鏡像建立一個容器,開啟一個shell,并且将IDA調試伺服器監聽的23946端口轉發到本地的23946端口。

通過指令docker container ls -a 我們發現容器清單裡多了一個剛剛建立的容器,并且被賦予了一個随機的名字,在我的實驗中它是nostalgic_raman。

 我們可以通過指令docker container rename nostalgic_raman ubuntu.17.04.amd64把這個容器重命名為ubuntu.17.04.amd64或者其他你認為合适的名字。

 使用docker exec -it ubuntu.17.04.amd64 /bin/bash 我們可以打開目标容器的一個新的bash shell。這使得我們在後續的調試中可以在容器中啟動IDA調試伺服器并用socat部署Pwn題目。

此外,可以使用docker container cp指令在docker容器内外雙向傳輸檔案等等。需要注意的是,對容器的各種操作需要在容器運作時進行,若容器尚未運作(運作docker container ls未顯示對應容器),需使用指令docker start運作對應容器。此外,若同時運作多個容器,為了避免端口沖突,在啟動容器時,可以将指令docker run -it -p 23946:23946 ubuntu/17.04.amd64 /bin/bash 中的第一個端口号23946改為其他數字。

二、IDA的簡單使用及遠端調試配置

成功搭建了Docker環境之後,我們接下來熟悉一下IDA和IDA的遠端調試環境搭建。首先我們在IDA所在的檔案夾的dbgsrv檔案夾下找到需要的調試伺服器Linux_server(32位)和Linux_serverx64(64位)并複制到kali中。

然後使用指令:

docker container cp linux_server ubuntu.17.04.i386:/root/linux_server

将linux_server複制到32位容器中的/root目錄下。此時我們登入容器可以看到linux_server,運作該server會提示正在監聽23946端口。

接着我們打開32位的ida,載入一個後面會用于示範堆漏洞的程式heapTest_x86,在左側的Functions window中找到main函數,随便挑一行代碼按F2下一個斷點。然後通過Debugger->Process options...打開選項視窗設定遠端調試選項。

在彈出的選項視窗中配置Hostname為kali的ip位址,Port為容器映射到kali中的端口。

填好後點選OK,按快捷鍵F9運作程式。若連接配接正常可能提示Input file is missing:xxxxx,一路OK就行,IDA會将被調試的檔案複制到伺服器所在目錄下,然後彙編代碼所在視窗背景會變成淺藍色并且視窗布局發生變化。若IDA僵死一段時間後跳出Warning視窗,則需要檢查IDA所在機器與kali是否能ping通,容器對應端口是否映射,參數是否填錯等問題。

調試器連接配接成功後我們就可以使用各種快捷鍵對目标程式進行調試,常用的快捷鍵有以下斷點/取消斷點F2,運作程式F9,單步跨過函數F8,單步進入函數F7,運作到選中位置F4等等。在調試模式下主要使用到的視窗有彙編視窗IDA View-EIP,寄存器視窗General registers,棧視窗Stack view,記憶體視窗Hex View,系統日志視窗Output window等。

切回到kali,我們會看到随着程式運作,運作調試伺服器的shell視窗會顯示出新的内容:

當IDA中的程式執行完call ___isoc99_scanf或者類似的等待輸入的指令後會陷入阻塞狀态,F4,F7,F8,F9等和運作相關的快捷鍵都不生效。此時我們可以在shell中輸入内容,IDA中的程式即可恢複執行。

三、使用Pwntools和IDA調試程式

在上一節中我們嘗試了使用IDA配置遠端調試,但是在調試中我們可能會有一些特殊的需求,比如自動化完成一些操作或者向程式傳遞一些包含不可見字元的位址,如\x50\x83\x04\x08(0x08048350)。這個時候我們就需要使用腳本來完成此類操作。我們選用的是著名的python庫pwntools。pwntools庫可以使用pip進行安裝,其官方文檔位址為http://docs.pwntools.com/en/stable/ 。在本節中我們将使用pwntools和IDA配合調試程式。

首先我們在kali中安裝pwntools,安裝完成後輸入python進入python環境,使用from pwn import * 導入pwntools庫。

使用docker exec在32位的容器中新開一個bash shell,跳轉到heapTest_x86所在目錄/root,檢視容器的IP位址,然後執行指令socat tcp-listen:10001,reuseaddr,fork EXEC:./heapTest_x86,pty,raw,echo=0

将heapTest_x86的IO轉發到10001端口上。

我們可以看到我的容器中的IP位址是172.17.0.2。回到python中,使用io = remote("172.17.0.2", 10001)打開與heapTest_x86的連接配接。

這個時候我們傳回到IDA中設定斷點。需要注意的是此時heapTest_x86已經開始運作,我們的目标是附加到其運作的程序上,是以我們需要把斷點設定在call ___isoc99_scanf等等待輸入的指令運作順序之後,否則由于計算機的運作速度,我們的斷點将會因為目标指令已經執行完而失效,達不到斷下來的效果。

選擇Debugger->Attach to process...,附加到./heapTest_x86的程序上。

此時EIP将指向vdso中的pop ebp指令上。

這幾行指令實際上是執行完sys_read後的指令,此處我們不需要關心它,直接按F9,選中标志會消失。

回到python視窗,我們使用pwntools的recv/send函數族來與運作中的heapTest_x86進行互動。首先輸入io.recv( ),我們發現原先會在shell視窗出現的菜單被讀出到python視窗裡了。

同樣的,我們通過io.send( )也可以向這個程序傳遞輸入。我們使用io.send('1')告訴這個程序我們要選擇選項1。這個時候我們切換到IDA視窗,發現IDA還是處于挂起狀态,這是為什麼呢?

回想一下我們通過shell與這個程序互動的時候,輸入選項後需要按Enter鍵以“告訴”這個程序我們的輸入結束了。那麼在這裡我們同樣需要再發送一個回車,是以我們再執行io.send(' '),切換到IDA視窗就會發現EIP停在了熟悉的程式領空。這時候我們再使用IDA的快捷鍵就可以進行調試,随心所欲地觀察程序的記憶體,棧,寄存器等的狀态了。當然,我們也可以直接使用io.sendline( ),就可以直接在輸入的結尾自動加上' '了。

在上圖的狀态中,我們在python中再次輸入io.recv( ),發現并沒有讀取到輸出,并且python處于阻塞狀态。這是因為程式此時沒有輸出可讀取。我們在IDA中按F8到call mallocChunk一行,此時按F7進入函數,在函數中運作到call _fflush的下一行,就會發現python的阻塞狀态解除了。

當我們希望結束調試時,應該使用io.close( )關閉掉這個io。否則下一次試圖attach時會發現有兩個./heapTest_x86程序。在IDA中按Ctrl+F2即可退出調試模式。

配置實驗環境打包如下:

連結:https://pan.baidu.com/s/1xr9n9EBs2dALOkmIFaIdcQ

密碼:bcd6

以上是今天的内容,大家看懂了嗎?後面我們将持續更新Linux Pwn入門教程的相關章節,希望大家及時關注。