close

本文為看雪論壇精華文章

看雪論壇作者ID:windy_ll


棧溢出原理

    
什麼是棧溢出?棧溢出就是緩衝區溢出的一種。由於緩衝區溢出而使得有用的存儲單元被改寫,往往會引發不可預料的後果。程序在運行過程中,為了臨時存取數據的需要,一般都要分配一些內存空間,通常稱這些空間為緩衝區。如果向緩衝區中寫入超過其本身長度的數據,以致於緩衝區無法容納,就會造成緩衝區以外的存儲單元被改寫,這種現象就稱為緩衝區溢出。緩衝區長度一般與用戶自己定義的緩衝變量的類型有關。(PS:摘自百度百科)
    
簡單來說,就是程序沒有檢查用戶輸入的數據長度,導致攻擊者覆蓋棧上不是程序希望寫入的地方,比如說返回地址。(PS:本人是這麼理解的,如有問題,還請斧正)
    
在x86中,對於調用一個函數,棧的變化如下:首先,將被調用的函數的參數從右到左依次壓入棧中,然後將被調用的函數的返回地址壓入棧中,然後跳轉到被調用函數的地址去,在被調用的函數中,首先將ebp(這時的ebp是調用者的ebp)壓入棧中,最後,將此時的棧頂esp賦值給ebp寄存器(此時的ebp便是被調用函數的棧底了)。

以一個實際程序為例,源碼如下所示:

對於進入test函數後,棧的變化情況如下圖所示:

接着利用gdb調試驗證棧的情況如上圖所示,首先在test函數處打下斷點,然後start命令將程序運行到main函數開頭處,查看一下此時的ebp寄存器的值和call test指令後下一條指令的地址,這裡可以看到此時ebp寄存器的值為0xffffd1d8,call test指令後的下一條指令地址為0x565561f2,如下圖所示:




然後運行r命令,進入test函數內部,使用x命令查看此時的棧情況,可以發現棧的情況如上圖示意圖一樣,如下圖所示:



在test函數內部,有個ver字符數組,在上面的源碼中,我們對其進行了手動賦值,假如該數組通過strcpy等函數完成賦值,並且賦值的字符串由用戶輸入,那麼在用戶輸入的字符串超過12個字節大小之後,ver數組就會接着往高地址增長,在這其中,可以覆蓋掉test函數的返回地址、main函數的ebp等等,這就是棧溢出漏洞。下面來看一個具體的程序實例,有漏洞的程序源碼如下所示:

從源碼中可以看到,程序對用戶的輸入無限制,並且strcpy函數拷貝也無限制,就造成了棧溢出漏洞,下面使用gdb確定偏移地址和getshell函數的首地址,首先斷點打在call strcpy的前一行,查看此時的buf數組的地址,也就是eax寄存器的值,再看ebp寄存器的值,發現其兩者相距0x10個字節,加上ebp 4個字節,也就是0x14個字節即可到達返回地址,再利用命令disassemble getshell查看getshell函數的首地址,如下圖所示:



有了偏移量和getshell函數地址就可以寫exp了,如下圖所示:


‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍ret2shellcode


ret2shellcode就是直接部署一段shellcode到棧上或者內存其他位置,當然,要使用的前提是,部署的shellcode所在的位置要具有執行權限,要關閉地址隨機化,下面以一道ctf題為例,題目來自ctfhub,首先下載好題目,丟到ida裡面反編譯一下,如下圖所示:


可以看到,read函數允許輸入的大小為0x400,遠大於buf數組的長度,這就造成了棧溢出,並且距離ebp偏移距離為0x10。利用checksec檢查一下程序,啥保護都沒開,利用file查看一下,是64位的,如下圖所示:


有了偏移量,並且題目輸出給出了buf數組的地址,那麼就可以寫exp了,首先外面用socat將程序發布到某個端口上去,如下圖所示:

最後執行exp即可,如下圖所示:
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍


Rop


ROP的全稱是Return Oriented Programming,簡單來說就是修改返回地址,指向內存中的指令片段,也就是gadget,通過ret指令將程序的控制器拿在手裡。例如一個存在棧溢出的程序,再將返回地址覆蓋為pop eax;ret指令地址後,會將返回地址後的4個字節彈到eax寄存器中,然後esp+4,之後又將棧上4個字節彈到eip寄存器中去,而攻擊者要做的就是給棧上返回地址後面覆蓋要賦給eax的值,下一條gadget的地址,通過這種方式就組合成了一條rop攻擊鏈,棧情況如下圖所示(PS:下面圖片來自https://zhuanlan.zhihu.com/p/25892385文章中):

下面以一道ctf題為例(PS:題目來自ctfwiki),首先下載好題目,然後查看一下常規信息,可以發現為x86架構,開啟了NX,也就是說無法在棧上執行shellcode,如下圖所示:

丟到IDA裡面去反編譯一下,可以發現是gets函數引起的棧溢出漏洞,並且沒有直接可以getshell的函數,如下圖所示:

那麼可以考慮利用rop來getshell,這是一個32為的程序,我們可以通過系統調用來獲取shell,常規的執行命令函數的系統調用匯編代碼如下圖所示:
mov eax, 0xb mov ebx, "/bin/sh" mov ecx, 0 mov edx, 0 int 80

要給eax賦值,那麼我們尋找pop eax;ret之類的代碼片段,之所以要有ret指令,是為了要把程序的控制權拿在手中,給其他寄存器賦值也類似,接下來利用ROPgadget工具來尋找相應的代碼片段,如下圖所示:



在這裡,我們在尋找控制ecx寄存器的代碼片段中,發現可以同時控制ebx、ecx以及edx的一條指令,使用它即可(在圖中地址為0x806eb90),接下來,我們需要/bin/sh字符串的地址,打開IDA,鍵入shift+F12,發現了/bin/sh字符串的地址,如下圖所示:

當然,我們還需要最重要的偏移量數據,使用gdb調試程序,將斷點打在調用gets函數處,然後r,查看當前eax與ebp的距離為0x68(PS:此時eax存放着v4數組的首地址),如下圖所示:

所有我們編寫exp的數據都有了,接下來使用socat將程序發布到某個端口上去,然後執行exp即可,如下圖所示:


ret2libc


ret2libc從名字上來看,就是通過覆蓋返回地址為libc庫中的函數來getshell的一種技術,通常來說,我們會選擇system等函數來getshell,但是一般無法獲取到這些函數在內存中的絕對地址的,這就需要通過got和plt表泄露以經加載的函數在內存中的地址然後減去其偏移地址,從而拿到libc庫的基地址,然後加上system等函數的偏移地址,從而得到system等函數在內存中的地址。

plt表和got表是保存程序動態鏈接的函數地址,程序通過查詢plt表獲取函數在got表中保存的位置,plt表就相當於一個索引數組,指向got表,程序獲取到plt表中相關位置之後,然後查詢got表獲取到函數地址,之後跳轉到該地址去。下面以一道ctf題為例,題目來自ctfwiki,如下所示:

首先還是老一套,查看一下程序信息,如下圖所示:

可以發現,程序是32位的,並且開啟了堆棧不可執行保護,也就是說不可以將shellcode寫入棧上執行。再將程序拖進IDA裡面看一下,如下圖所示:

可以看到,溢出點在gets函數,下面用gdb調試一下尋找到偏移量,首先斷點下載gets函數,然後查看此時eax與ebp的距離,如下圖所示:

可以看到,此時eax距離ebp一共為108個字節,再加上這是32位程序,ebp本身占4個字節,也就是說,填充108 + 4 = 112字節後,便是返回地址,找到返回地址後,便是找到system函數地址。

這裡通過訪問got表來拿到一個已經運行過的函數在內存中的地址,在linux中,如果一個函數被調用運行過,那麼它的真實地址就會被寫進got表中,我們可以通過打印函數將其打印出來,獲取其地址,之後通過該地址的後三位(PS:之所以找後三位,是因為即使開啟了aslr也不會影響低12位的地址)來確定libc的版本(PS:可以通過在線網站https://libc.blukat.me/來查詢),從而獲取該版本的libc庫中函數的偏移地址,最後泄露地址 - 偏移地址即可得到libc的基地址。

然後libc基地址 + 該版本libc庫中system偏移地址即可得到system函數在內存中的地址,同理,/bin/sh字符串也是一樣的,此處選用的泄露函數地址的函數為puts函數,將程序利用socat發布後,最後exp結果如下圖所示(PS:程序最好運行在ubuntu上,經過實際測試,kali上失敗,下同):


格式化字符串


格式化字符串漏洞,個人理解就是格式字符串參數與其餘參數的個數不匹配造成的,網上將原理的文章一大堆,這裡就不在重複了。

對于格式化字符串漏洞,可以做到讀取任意地址的值,也可以往任意地址寫入任意值。

對於利用格式化字符串漏洞讀取任意地址的值,首先需要確定偏移量,此處的偏移量不是值上面棧溢出的偏移量,而是格式化字符串函數參數的地址相對于格式化字符串參數的偏移量,確定偏移量可以利用形如AAAA%n$x(PS:裡面的n就是偏移量)的格式化字符串參數來確定,或者利用AAAA%x%x%x%x%x%x%x%x...(PS:這裡也可以使用%p來,但為了防止讀到不可讀的地址導致程序崩潰,還是推薦使用%x來讀取)這種形式來確定。

確定的偏移量之後,即可通過addr%n$x來讀取任意地址的值(PS:這裡的addr指要讀取的地址,n為偏移量,當然addr也可以寫在後面,把n加1即可,因為%n$x是第一個參數,addr自然是第二個參數,所以n要加上1)

對於利用格式化字符串漏洞往任意地址寫入值,也是需要先確定偏移量,方法和上面一樣,寫主要利用%n,%n作用為將前面所寫字節數寫入指定地址,我們可以利用形如addr%kc%n$n這種形式寫入,其中addr為要寫入的地址,k為要寫入的大小(PS:這裡需要減去addr所占用的字節數),n為偏移量,$n表示寫入四個字節,當然,也可以使用$hn寫入雙字節,可以使用$hhn寫入一個字節,當然,確定好偏移量之後,最簡單的方法是使用pwntools提供的函數即可。

下面以一道ctf題舉例,題目來自ctfwik,首先下載好題目,解壓後,丟到kali裡面去,看一下常規信息,可以發現,該程序為32位,開了NX等,如下圖所示:

丟到IDA中反編譯一下,可以發現程序實現了類似ftp的功能,如下圖所示:

首先程序調用了ask_usename和ask_password兩個函數獲取一個密碼,密碼就是將sysbdmin字符串加一,如下圖所示:


然後程序獲取命令,命令有get、put、dir三個命令,獲取命令之後,便執行相應的功能,首先來看一下put對應的功能函數,該函數首先要求用戶輸入一個字符串作為文件名,然後要求用戶再輸入一個字符串作為文件內容,該函數沒什麼漏洞,如下圖所示:


接下來再看一下dir對應的功能函數,該函數作用就是將所有文件名打印出來,也沒有什麼漏洞,如下圖所示:
    
最後來看一下get對應的功能函數,該函數首先要求用戶輸入文件名,然後將文件名對應的內容拷貝到一個數組中去,最後直接將該數組作為參數傳入到printf函數中去,典型的格式化字符串漏洞,如下圖所示:

通過以上的代碼分析,解決該ctf的思路已經很明顯了,我們可以首先利用put建立一個文件,將payload寫入到該文件中去,之後調用get指令讀取該文件,觸發格式化字符串漏洞。首先我們先確定偏移量,這裡使用BBBB%x....來確定,如下圖所示:

我們可以通過上圖發現,偏移量為7,那麼怎麼getshell喃,我們可以通過修改got表來實現,將dir指令對應的功能函數中的puts函數修改為system函數的地址,這樣調用dir指令後,裡面的puts函數實際上會指向system函數,我們通過提前新建一個名為/bon/sh;的文件,即可解決參數的問題,這裡怎麼寫前面ret2libc已經講了,不在重複,最後使用socat發布程序,指向exp即可,如下圖所示:


參考鏈接


exp腳本和題目github鏈接:
https://github.com/windy-purple/pwn_study_summary

參考鏈接:https://www.cnblogs.com/ichunqiu/p/11122229.htmlhttps://www.cnblogs.com/ichunqiu/p/11156155.html

https://www.cnblogs.com/ichunqiu/p/11162515.html

http://drops.xmd5.com/static/drops/tips-4225.html

https://blog.csdn.net/xiaoi123/article/details/80899155

https://bbs.pediy.com/thread-230148.htm

https://sploitfun.wordpress.com/2015/

https://www.jianshu.com/p/187b810e78d2

https://zhuanlan.zhihu.com/p/25816426

https://www.cnblogs.com/Donoy/p/5690402.html

http://shell-storm.org/shellcode/

https://bbs.pediy.com/thread-259723.htm

https://zhuanlan.zhihu.com/p/25892385

http://events.jianshu.io/p/9214e84139eb

https://www.cnblogs.com/wulitaotao/p/13909451.html

https://www.cnblogs.com/hktk1643/p/15218090.html

https://zhuanlan.zhihu.com/p/367387964

https://blog.csdn.net/xiaoi123/article/details/80985646

https://ctf-wiki.org/pwn/windows/readme/

https://libc.blukat.me/

https://blog.csdn.net/qq_41918771/article/details/90665950

https://bbs.pediy.com/thread-253638.htm

https://www.anquanke.com/post/id/83835

https://bbs.pediy.com/thread-254869.htm

https://bbs.pediy.com/thread-262816.htm



看雪ID:windy_ll

https://bbs.pediy.com/user-home-851220.htm

*本文由看雪論壇 windy_ll原創,轉載請註明來自看雪社區


#往期推薦

1.某視頻app的學習記錄

2.Chrom V8分析入門——Google CTF2018 justintime分析

3.Typora 授權解密與剖析

4.內核漏洞學習-HEVD-StackOverflowGS

5.人人都可以拯救正版硬件受害者(Jlink提示Clone)

6.frida內存檢索svc指令查找sendto和recvfrom進行hook抓包




球分享

球點讚

球在看

點擊「閱讀原文」,了解更多!

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 鑽石舞台 的頭像
    鑽石舞台

    鑽石舞台

    鑽石舞台 發表在 痞客邦 留言(0) 人氣()