一.Introducation
0x01 簡介CTF
0x02 什麼是pwn
”Pwn”是一個駭客語法的俚語詞,是指攻破裝置或者系統 。發音類似“砰”,對駭客而言這就是成功實施駭客攻擊的聲音--砰的一聲,被“黑”的電腦或手機就被你操縱了。
CTF中的pwn
CTF中的PWN主要是針對於二進位制漏洞挖掘與利用,通常情況下選手需要對於一個有漏洞的可執行檔案進行分析,找到漏洞,然後利用漏洞讀取遠端伺服器上的FLAG。
0x03 CTF中pwn的出臺方向
傳統的glibc PWN:堆、棧、shellcode編寫、iofile等。
進階:
arm架構、loT、核心、vm、瀏覽器等。
注:什麼是glibc
glibc是GNU釋出的libc庫,是Linux系統中最底層的API,其他執行庫通常依賴它。它提供系統服務封裝及多種功能實現,如字串處理、動態載入、執行緒庫、加密演算法等。glibc的廣泛使用使其成為GNU/Linux作業系統的重要組成部分。透過getconf命令可獲取版本資訊,ldd命令則能顯示其在程式中的連結位置。
glibc簡而言之就是linux下的可執行環境 glibc是linux下重要的c語言庫也關係到系統執行的核心部分
注:什麼是shellcode
Shellcode 是指經過精心設計的一串指令,一旦注入正在執行的應用程式中即可執行,常用於棧和基於堆的溢位。術語Shellcode意思指的便是用於啟動一個命令Shell的已編寫好的可執行程式碼。
shellcode就基於漏洞利用的基礎之上 編寫可執行的檔案 類似於windows下exe 檔案
注:什麼shell
Shell 是一個命令直譯器,使用者可以用shell來啟動、掛起、停止甚至是編寫一些程式(也是一個程式語言)。
shell其實就是Linux裡的一個翻譯官,負責把命令翻譯成二進位制機器語言數字。
核心擁有最高階別、最底層的許可權,在接受到shell命令以後可以直接控制硬體。
0x04 生活中的攻擊面
office 瀏覽器 作業系統 硬體驅動 通訊協議 路由器
0x05 實際生活中PWN
心臟滴血(cve-2014-0160)——洩露通訊資料
髒牛dirty cow(cve-2016-5195)——linux本地提權root
永恆之藍——wannacry
0x05 教練,我也想學pwn
1.pwn只是一個簡單稱謂或者叫做泛稱,我們學習的主要目的是為了解出ctf裡面的題目,給自己一個上升的空間和機會,也算一個小小的跳板吧,如你有志向於網路安全方向,CTF的給你提供一個就業的基礎和出名的機會。如果你志向於考研,學習網安可以為你的考研跨方向提供一個選擇,並且可以成為帶動你學習專業的契機,此外二進位制漏洞方向也網路安全技術論文領域的絕對高地,是發表各大刊物的有力契機,說不定下一個發表SCI就是你!
pwn一直是CTF中的熱門方向 同時也各大戰隊所需要和缺少的重要部分 每一個方向的入門和深入都需要一番功夫 江山代有人才出各領風騷數百年 希望大家能在這個方面有所建樹
2.glibc的學習路線
想要入門 狠多同學面對繁雜的知識體系 感到無從下手 面對一些的繁雜的知識
感到無從下手 如果從頭學習的話 一門門尖酸晦澀的科目 如組合語言 資料結構 作業系統 計算機組成原理讓大家望而卻步 於是就有一句話叫 pwn從入門到放棄
學習pwn的難點不在知識的難度 而在於找到一個切入點慢慢深入 逢山開路 遇水架橋 慢慢就會前路復明
理論+實踐+實踐才能更近一步
3.推薦一些刷題網站(pwn)
https://www.hetianlab.com/合天網安實驗室
https://buuoj.cn/北京聯合大學CTF
https://pwnable.kr 適合新手入門pwn
https://adworld.xctf.org.cn/攻防世界
https://www.jarvisoj.com/作者篩選過的質量不錯的題目
https://pwnable.tw 質量很好,但是有一定難度
0x06 開學吧!
二.組合語言基礎
0x01 1byte =8bit
1個16進位制數佔半個位元組
0x02 計算機的定址方式
主流作業系統都是以B(位元組)來進行定址
0x03前言 真正能被執行的是什麼?
計算機並不能直接執行高階語言
我們編寫的高階語言程式需要進行編譯後才能在計算機上執行高階語言經過編譯之後,經過編譯器處理,被打包成一個可執行檔案的格式
那麼,計算機真正能夠被執行的是什麼?
真正能被執行的只有機器碼也就是一個個01 為了遍觀看 人們把機器碼翻譯為16進位制數字 又把一個個16進位制數字翻譯為組合語言
組合語言就是機器碼的助記符
0x04 暫存器
計算機的指令都是由CPU來執行,
在計算機系統結構中,CPU和記憶體是分開的,
暫存器存在於CPU中,是CPU的直接操作物件。
0x05 定址方式
立即定址 1234h 直接訪問1234h
直接定址 [1234h] 記憶體地址1234h
暫存器定址 RAX 訪問RAX暫存器
暫存器間接尋 [RAX] 訪問RAX暫存器儲存的值這樣記憶體地址
變址定址 [RAX+1234H] 訪問RAX暫存器的值+1234h這一記憶體地址
0x06數
1.基礎知識
數學是科學的基石,任何科技都離不開數學的支撐。
在計算機中,無論資料是以二進位制或者十六進位制十進位制表示,本質都是代表個數。
儘管資料在計算機內部有很多儲存的形式(補碼、原碼、反碼等)。但是本質都是數。
2.計算機不能儲存無線大的數,這個數值有一定的上限和下限
3.數字的符號
如果是unsigned 也就是無符號數,資料的每一位都是代表資料
如果是signed有符號數,那麼資料的最高位會被當作符號位處理
0代表正數,1代表負數。
0x07數字的溢位
1.什麼是溢位
數值有上下限範圍,那麼就不可避免的會有溢位情況。
以32位int為例,有以下四種溢位
無符號上溢:0xffffffff+1變成0
無符號下溢:0-1變成0xffffffff
有符號上溢:有符號正數0x7fffffff+1變成負數0x80000000
無符號下溢:有符號數0x80000000-1變成正數0x7fffffff
2.溢位的原因
1.儲存位數不夠
2.符號位溢位
到這裡其實我就學習到了pwn方向中的第一個溢位漏洞,我們稱之為整數溢位,整數溢位一般配合別的漏洞來使用.
三.linux基礎
0x01 學習Linux的主要知識方面
1.linux的保護機制的核心 2.Linux的組成部分 其中包括shellcode的編寫 3.linux下基本的操作命令
0x02linux的基礎結構和許可權
1.linux的保護層級 分為4個層級 ring0-ring3 常用的是兩個0和3 0是核心 3是使用者
2.許可權等級 root admin user 等等等級
使用者和使用者組 多個使用者組合在一起就是使用者組 同一個使用者組下的使用者許可權一般都相同
3.許可權 一般就是三種 r w x read 讀許可權 wirte 寫許可權 x 可執行許可權
4.許可權操作的一些命令 ls -a 檢視所有檔案以及許可權 例子 -rw-rw-rw- 第一位-或者d代表檔案或者目錄 後面3位為一組 -rw 為當前檔案的許可權 -rw是當前使用者所屬的使用者組對檔案的許可權 最後一組是其他使用者組對檔案的許可權
0x03 實體記憶體和虛擬記憶體
1.實體記憶體就是真正的記憶體 我們買電腦時候 16G+512G中的16就是記憶體
2.虛擬記憶體是實體記憶體經過MMU轉換(頁表) 目前無論是windows或linux都是使用的虛擬記憶體
0x04學會看原始碼
1.Iinux是開源的,他的程式碼實現都可以查到,我們研究Iinux的機制,最重要的武器就是原始碼,分析原始碼是一個安全研究者必備的技能。
2.https://elixir.bootlin.com https://code.woboq.org
0x05計算機內部儲存資料的形式
1.計算機內部有兩種資料的儲存形式:大端序、小端序
2.大端序:資料高位儲存在計算機地址的低位,資料低位儲存在地址的高位 (高低低高)
小端序:資料高位儲存在計算機地址的高位,資料低位儲存在地址(高高低低)
3.linux資料儲存的格式是小端序
4.linux是小端序,所以如果我們以字串的形式輸入一個數字時,要注意格式
比如輸入0xdeadbeef這個數字
字串輸入就是”\xeflxbelxadlxde”傳入給程式
好在有pwntools,p32(0xdeadbeef)即可完成自動轉換
0x06 檔案描述符號
1.Linux 系統中,把一切都看做是檔案,當程序開啟現有檔案或建立新檔案時,核心向程序返回一個檔案描述符,檔案描述符就是核心為了高效管理已被開啟的檔案所建立的索引,用來指向被開啟的檔案,所有執行I/0操作的系統呼叫都會透過檔案描述符。
2.每個檔案描述符會與一個開啟的檔案相對應,不同的檔案描述符也可能指向同一個檔案
3.相同的檔案可以被不同的程序開啟,也可以在同一個程序被多次開啟
招全長口
4.我們會在open、read、write這些常見函式中見到,
5.0標準輸入(stdin)、1標準輸出(stdout)、2標準錯誤(stderr)
6.read(0,buf,size) 從stdin中讀size個資料到buf中
7.write(1,buf,size)從buf中取size個資料到stdout中
0x07 基本的資料結構
1.棧 stack 我們可以把棧看成 成一個薯片桶 我們對棧的操作都是在棧的頂端完成
2.棧可以被看做一個閹割版的陣列 那我們為社麼不用陣列呢 一把菜刀可以幹很多事情 切菜削皮剁骨 但是我們在削皮時候還是採用削皮刀 原因就是簡單方面
3.棧是一種資料結構,他是一種先進後出(LIFO)的資料結構。
4.棧的基本操作有兩種:push(壓棧)和pop(彈棧)
5.由於函式呼叫順序也是LIFO,所以我們能接觸到的絕大多數系統,都是透過棧這一資料結構來維護函式呼叫關係。
6.函式呼叫順序也先進後出
0x08函式的呼叫流程
1.函式的呼叫流程也和棧一樣遵循lifo 系統在執行程式會給每一個函式分配一個棧幀
2.小明蒸飯的例子 小明要去蒸飯(main)然後要去淘米(fun a),結果發現米沒有了,要去買米(fun b)
事情的發生結果是 main——fun a ——fun b
實際的幹活順序 funb——funa——main
3.c語言舉例
發生順序 期待輸出a——需要定義a——呼叫main
實際執行順序 呼叫main——定義a——輸出a
0x09Linux中的棧
1.我們接觸到一些演算法,很多都是用棧來實現的,比如DFS深度優先搜尋演算法
2.很多的這種LIFO演算法都會以遞迴的形式實現
3.其實,遞迴的形式實現這些演算法本質上來說也是利用棧結構,只不過他沒有在程式中另外申請一個棧,而是用的函式呼叫棧。
4.Linux中的棧是從高地址向地址生長的 這裡我產生了一個疑問 所有的作業系統中的棧都是由高地址向低地址生長的嗎?
以下是chatgpt的解答
四.函式的呼叫流程和呼叫約定
0x01基礎知識
1.函式呼叫關係透過棧來維護,函式呼叫關係符合LIFO.
2.作業系統為每個程式都設定了一個棧,程式每個獨立的函式都有獨立的棧幀。
3.棧地址從高地址向低地址生長。
4.棧有兩種基本操作:push和pop
pop指令就是我們所說的出棧指令也叫彈棧指令,將棧頂指標彈出到暫存器,將棧頂資料彈出到暫存器,然後將棧頂指標下移一個單位 pop rax,作用就是 mov rax [rsp], add rsp 8;
push 指令就是我們所說入棧指令也叫壓棧指令,將棧頂指標向上移動一個單位的距離,然後將一個暫存器的值放在棧頂,具體來說push rax,sub rsp 8;mov [rsp] rax;
5.函式呼叫指令:call,返回指令return,jmp跳轉指令
a.jmp指令是立即跳轉,不涉及函式呼叫,用於迴圈,條件句(if else)這種場合 具體來說就是jmp 1234h,效果就是mov rip 1234h
b.call指令就是函式呼叫,需要返回地址 具體來說就是call 1234h,效果就是push rip;mov rip 1234h;
6.棧中儲存函式返回地址、棧幀、區域性變數等資訊
0x02真正的函式呼叫流程
接下來我將透過自己的學習以圖畫的形式幫助理解真正的函式呼叫流程
1.函式的呼叫順序是main——>fun_b——>fun_a
在這裡我手寫了一些過程進行分享
a.左上的話就是main函式沒被呼叫之前的一個初始狀態
b.右上的話就是開始呼叫main函式之後的一個狀態 mian函式中巢狀有fun_b函式 其實在這期間執行了3步
push rsp 儲存棧頂地址
mov rbp rsp 讓棧底指標指向棧頂
sub rbp xx 棧頂部指標向下移動數個位置 開闢新的空間
經過以上三部的操作就到了右上方的圖示
c.左下方就是繼續執行程式 呼叫fun_a 函式就和前一步差不多了 這裡不再多寫
2.leave指令和retun
在一個函式執行結束返回時,會執行leave;ret;
實際效果就是:movrsp rbp;pop rbp;pop eip:
eip暫存器用於儲存當前正在執行的指令的地址
func_a執行完畢返回後,棧佈局如圖
可以與之前的func b未呼叫func a前的棧幀對比
一模一樣,說明已經恢復了棧幀。
唯一不同之處在於此時程式的rip已經指向了c=1
後面一條指令,說明func_a已經執行完畢。
0x03函式呼叫小結
1.呼叫函式:只需要將rip壓棧,即push rip,然後講rip賦值為被呼叫函式的起始地址,這一操作被隱性的內建在call指令中。
2.被呼叫函式:push rbp;mov rbprsp;sub rsp 0xxxx。即儲存呼叫函式的rbp指標,將自己的rbp指標指向棧頂,然後開闢棧空間給自己用,此時rbp就變成了被呼叫函式的棧底。
3.函式返回:leave;ret;翻譯過來就是:movrsprbp;poprbp;poprip;即恢復棧幀,返回撥用函式的返回地址。
0x04呼叫約定
1.返回值:一般來說,一個函式的返回值會儲存到RAX暫存器
2.X86-64函式的呼叫約定為:
3.從左至右引數一次傳遞給rdi,rsi,rdx,rcx,r8,r9。
4.如果一個函式的引數多於6個,則從右至左壓入棧中傳遞
5.syscall指令
用於呼叫系統函式,請呼叫時需要指明系統呼叫號
系統呼叫號存在rax暫存器中,然後佈置好引數,執行syscal即可
注:在高階語言我們呼叫函式或者自己定義函式直接寫函式名稱即可但是在作業系統的內部我們都要呼叫syscall函式
6.常見的系統呼叫號
0x05函式呼叫流程小結
1.呼叫函式:只需要將rip壓棧,即pushrip,然後講rip賦值為被呼叫函式的起始地址,這一操作被隱性的內建在call指令中。
2.被呼叫函式:push rbp;mov rbprsp;sub rsp 0xxxx。即儲存呼叫函式的rbp指標,將自己的rbp指標指向棧頂,然後開闢空間給自己用,此時rbp就變成了被呼叫函式的棧底。
3.函式返回:leave;ret;翻譯過來就是:movrsprbp;poprbp;poprip:即恢復棧幀,返回撥用函式的返回地址,