來自 | i春秋社群,作者:Tangerine@SAINTSEC
https://bbs.ichunqiu.com/thread-42239-1-1.html
前言
作為一個畢業一年多的辣雞CTF選手,一直苦於pwn題目的入門難,入了門更難的問題。本來網上關於pwn的資料就比較零散,而且經常會碰到師傅們堪比解題過程略的writeup和沒有註釋,存在大量硬編碼偏移的指令碼,還有練習題目難找,除錯環境難搭建,GDB沒有IDA好操作等等問題。作為一個老萌新(霧),決定依據Atum師傅在i春秋上的pwn入門課程中的技術分類,結合近幾年賽事中出現的一些題目和文章整理出一份自己心目中相對完整的Linux pwn教程。
本系列教程僅針對i386/amd64下的Linux pwn常見的pwn手法,如棧,堆,整數上限溢位,格式化字串,條件競爭等進行介紹。為了方便和我一樣的萌新們進行學習,所有環境都會封裝在docker映象當中,並提供除錯用的教學程式,來自歷年賽事的原題和帶有註釋的python指令碼。教程歡迎各位師傅吐槽,若對題目和指令碼的使用有不妥之處,會在當事師傅反饋之後致歉並應要求進行處理。
docker容器的使用與簡單操作
在搭建環境之前我們需要準備一個裝有docker的64位Linux系統,核心版本高於3.10(可以透過uname -r檢視),可以執行在物體機或者是虛擬機器中。關於docker的安裝與啟動此處不再贅述,讀者可以根據自己的Linux發行版本自行搜尋。此處提供兩個連結,供Ubuntu和Kali使用者參考:
Kali:《kali Rolling安裝docker》http://www.cnblogs.com/Roachs/p/6308896.html
Ubuntu:《Ubuntu 16.04安裝Docker》http://blog.csdn.net/qq_27818541/article/details/73647797
在成功安裝了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中。
然後使用命令
dockercontainercplinux_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配置遠端除錯,但是在除錯中我們可能會有一些特殊的需求,比如自動化完成一些操作或者向程式傳遞一些包含不可見字元的地址,如Pƒ(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地址,然後執行命令
socattcp-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與這個行程互動的時候,輸入選項後需要按回車鍵以“告訴”這個行程我們的輸入結束了。那麼在這裡我們同樣需要再傳送一個回車,所以我們再執行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即可退出除錯樣式。
●編號546,輸入編號直達本文
●輸入m獲取文章目錄
駭客技術與網路安全
更多推薦《18個技術類微信公眾號》
涵蓋:程式人生、演演算法與資料結構、駭客技術與網路安全、大資料技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、資料庫、運維等。