一步一步學ROP之linux_x86篇

来源:http://www.cnblogs.com/alisecurity/archive/2016/07/06/5646656.html
-Advertisement-
Play Games

ROP的全稱為Return-oriented programming(返回導向編程),這是一種高級的記憶體攻擊技術可以用來繞過現代操作系統的各種通用防禦(比如記憶體不可執行和代碼簽名等)。雖然現在大家都在用64位的操作系統,但是想要扎實的學好ROP還是得從基礎的x86系統開始,但看官請不要著急。 ...


一步一步學ROP之linux_x86篇

作者:蒸米@阿裡聚安全

 

一、序

ROP的全稱為Return-oriented programming(返回導向編程),這是一種高級的記憶體攻擊技術可以用來繞過現代操作系統的各種通用防禦(比如記憶體不可執行和代碼簽名等)。雖然現在大家都在用64位的操作系統,但是想要扎實的學好ROP還是得從基礎的x86系統開始,但看官請不要著急,在隨後的教程中我們還會帶來linux_x64以及android (arm)方面的ROP利用方法,歡迎大家繼續學習。

小編備註:文中涉及代碼可在文章最後的github鏈接找到。

 

二、Control Flow Hijack 程式流劫持

比較常見的程式流劫持就是棧溢出,格式化字元串攻擊和堆溢出了。通過程式流劫持,攻擊者可以控制PC指針從而執行目標代碼。為了應對這種攻擊,系統防禦者也提出了各種防禦方法,最常見的方法有DEP(堆棧不可執行),ASLR(記憶體地址隨機化),Stack Protector(棧保護)等。但是如果上來就部署全部的防禦,初學者可能會覺得無從下手,所以我們先從最簡單的沒有任何保護的程式開始,隨後再一步步增加各種防禦措施,接著再學習繞過的方法,循序漸進。

首先來看這個有明顯緩衝區溢出的程式:

 

這裡我們用

#bash
gcc -fno-stack-protector -z execstack -o level1 level1.c

 

這個命令編譯程式。-fno-stack-protector和-z execstack這兩個參數會分別關掉DEP和Stack Protector。同時我們在shell中執行:

這幾個指令。執行完後我們就關掉整個linux系統的ASLR保護。

接下來我們開始對目標程式進行分析。首先我們先來確定溢出點的位置,這裡我推薦使用pattern.py這個腳本來進行計算。我們使用如下命令:

來生成一串測試用的150個位元組的字元串:

隨後我們使用gdb ./level1調試程式。

我們可以得到記憶體出錯的地址為0x37654136。隨後我們使用命令:

就可以非常容易的計算出PC返回值的覆蓋點為140個位元組。我們只要構造一個”A”*140+ret字元串,就可以讓pc執行ret地址上的代碼了。

接下來我們需要一段shellcode,可以用msf生成,或者自己反編譯一下。

這裡我們使用一段最簡單的執行execve ("/bin/sh")命令的語句作為shellcode。

溢出點有了,shellcode有了,下一步就是控制PC跳轉到shellcode的地址上:

[shellcode][“AAAAAAAAAAAAAA”….][ret]
^------------------------------------------------|

 

對初學者來說這個shellcode地址的位置其實是一個坑。因為正常的思維是使用gdb調試目標程式,然後查看記憶體來確定shellcode的位置。但當你真的執行exp的時候你會發現shellcode壓根就不在這個地址上!這是為什麼呢?原因是gdb的調試環境會影響buf在記憶體中的位置,雖然我們關閉了ASLR,但這隻能保證buf的地址在gdb的調試環境中不變,但當我們直接執行./level1的時候,buf的位置會固定在別的地址上。怎麼解決這個問題呢?

最簡單的方法就是開啟core dump這個功能。

開啟之後,當出現記憶體錯誤的時候,系統會生成一個core dump文件在tmp目錄下。然後我們再用gdb查看這個core文件就可以獲取到buf真正的地址了。

因為溢出點是140個位元組,再加上4個位元組的ret地址,我們可以計算出buffer的地址為$esp-144。通過gdb的命令 “x/10s $esp-144”,我們可以得到buf的地址為0xbffff290。

OK,現在溢出點,shellcode和返回值地址都有了,可以開始寫exp了。寫exp的話,我強烈推薦pwntools這個工具,因為它可以非常方便的做到本地調試和遠程攻擊的轉換。本地測試成功後只需要簡單的修改一條語句就可以馬上進行遠程攻擊。

最終本地測試代碼如下:

執行exp:

接下來我們把這個目標程式作為一個服務綁定到伺服器的某個埠上,這裡我們可以使用socat這個工具來完成,命令如下:

隨後這個程式的IO就被重定向到10001這個埠上了,並且可以使用 nc 127.0.0.1 10001來訪問我們的目標程式服務了。

因為現在目標程式是跑在socat的環境中,exp腳本除了要把p = process('./level1')換成p = remote('127.0.0.1',10001) 之外,ret的地址還會發生改變。解決方法還是採用生成core dump的方案,然後用gdb調試core文件獲取返回地址。然後我們就可以使用exp進行遠程溢出啦!

 

三、Ret2libc – Bypass DEP 通過ret2libc繞過DEP防護

現在我們把DEP打開,依然關閉stack protector和ASLR。編譯方法如下:

這時候我們如果使用level1的exp來進行測試的話,系統會拒絕執行我們的shellcode。如果你通過sudo cat /proc/[pid]/maps查看,你會發現level1的stack是rwx的,但是level2的stack卻是rw的。

level1: bffdf000-c0000000 rw-p 00000000 00:00 0 [stack]
level2: bffdf000-c0000000 rwxp 00000000 00:00 0 [stack]

 

那麼如何執行shellcode呢?我們知道level2調用了libc.so,並且libc.so里保存了大量可利用的函數,我們如果可以讓程式執行system(“/bin/sh”)的話,也可以獲取到shell。既然思路有了,那麼接下來的問題就是如何得到system()這個函數的地址以及”/bin/sh”這個字元串的地址。

如果關掉了ASLR的話,system()函數在記憶體中的地址是不會變化的,並且libc.so中也包含”/bin/sh”這個字元串,並且這個字元串的地址也是固定的。那麼接下來我們就來找一下這個函數的地址。這時候我們可以使用gdb進行調試。然後通過print和find命令來查找system和”/bin/sh”字元串的地址。

我們首先在main函數上下一個斷點,然後執行程式,這樣的話程式會載入libc.so到記憶體中,然後我們就可以通過”print system”這個命令來獲取system函數在記憶體中的位置,隨後我們可以通過” print __libc_start_main”這個命令來獲取libc.so在記憶體中的起始位置,接下來我們可以通過find命令來查找”/bin/sh”這個字元串。這樣我們就得到了system的地址0xb7e5f460以及"/bin/sh"的地址0xb7f81ff8。下麵我們開始寫exp:

要註意的是system()後面跟的是執行完system函數後要返回地址,接下來才是”/bin/sh”字元串的地址。因為我們執行完後也不打算乾別的什麼事,所以我們就隨便寫了一個0xdeadbeef作為返回地址。下麵我們測試一下exp:

OK。測試成功。

 

四、ROP– Bypass DEP and ASLR 通過ROP繞過DEP和ASLR防護

接下來我們打開ASLR保護。

現在我們再回頭測試一下level2的exp,發現已經不好用了。

如果你通過sudo cat /proc/[pid]/maps或者ldd查看,你會發現level2的libc.so地址每次都是變化的。

那麼如何解決地址隨機化的問題呢?思路是:我們需要先泄漏出libc.so某些函數在記憶體中的地址,然後再利用泄漏出的函數地址根據偏移量計算出system()函數和/bin/sh字元串在記憶體中的地址,然後再執行我們的ret2libc的shellcode。既然棧,libc,heap的地址都是隨機的。我們怎麼才能泄露出libc.so的地址呢?方法還是有的,因為程式本身在記憶體中的地址並不是隨機的,如圖所示:

 

Linux記憶體隨機化分佈圖

所以我們只要把返回值設置到程式本身就可執行我們期望的指令了。首先我們利用objdump來查看可以利用的plt函數和函數對應的got表:

我們發現除了程式本身的實現的函數之外,我們還可以使用read@plt()和write@plt()函數。但因為程式本身並沒有調用system()函數,所以我們並不能直接調用system()來獲取shell。但其實我們有write@plt()函數就夠了,因為我們可以通過write@plt ()函數把write()函數在記憶體中的地址也就是write.got給列印出來。既然write()函數實現是在libc.so當中,那我們調用的write@plt()函數為什麼也能實現write()功能呢? 這是因為linux採用了延時綁定技術,當我們調用write@plit()的時候,系統會將真正的write()函數地址link到got表的write.got中,然後write@plit()會根據write.got 跳轉到真正的write()函數上去。(如果還是搞不清楚的話,推薦閱讀《程式員的自我修養 - 鏈接、裝載與庫》這本書)

因為system()函數和write()在libc.so中的offset(相對地址)是不變的,所以如果我們得到了write()的地址並且擁有目標伺服器上的libc.so就可以計算出system()在記憶體中的地址了。然後我們再將pc指針return回vulnerable_function()函數,就可以進行ret2libc溢出攻擊,並且這一次我們知道了system()在記憶體中的地址,就可以調用system()函數來獲取我們的shell了。

使用ldd命令可以查看目標程式調用的so庫。隨後我們把libc.so拷貝到當前目錄,因為我們的exp需要這個so文件來計算相對地址:

最後exp如下:

接著我們使用socat把level2綁定到10003埠:

最後執行我們的exp:

 

五、小結

本章簡單介紹了ROP攻擊的基本原理,由於篇幅原因,我們會在隨後的文章中會介紹更多的攻擊技巧:如何利用工具尋找gadgets,如何在不知道對方libc.so版本的情況下計算offset;如何繞過Stack Protector等。歡迎大家到時繼續學習。另外本文提到的所有源代碼和工具都可以從我的github下載:https://github.com/zhengmin1989/ROP_STEP_BY_STEP

 

六、參考文獻

  1. The geometry of innocent flesh on the bone: return-into-libc without function calls (on the x86)
  2. picoCTF 2013: https://github.com/picoCTF/2013-Problems
  3. Smashing The Stack For Fun And Profit: http://phrack.org/issues/49/14.html
  4. 程式員的自我修養
  5. ROP輕鬆談

 

作者:蒸米@阿裡聚安全,更多技術文章,請訪問阿裡聚安全博客


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一. 準備工作 1. 下載ubuntu鏡像文件:ubuntu-12.04.3-desktop-amd64.iso(4G及以上記憶體建議64位),註意這個amd並不是指amd晶元。 2. 下載硬碟分區工具:Acronis Disk Director 11 3. 下載系統引導軟體:EasyBCD2.2 二 ...
  • 我會用幾篇博客總結一下在Linux中進程之間通信的幾種方法,我會把這個開頭的摘要部分在這個系列的每篇博客中都打出來 進程之間通信的方式 管道 消息隊列 信號 信號量 共用存儲區 套接字(socket) 進程間通信(四)—共用存儲區傳送門:http://www.cnblogs.com/lenomire ...
  • 藉助於開源百度雲客戶端`bypy`實現將網站備份的數據上傳至網盤存儲。 ...
  • 添加網卡之後,網卡無法被正確的識別和使用排錯方法 查看/etc/udev/rules.d/70-persistent-net.rules的內容,該文件中可以查看到新添加的網卡的MAC地址 修改/etc/sysconfig/network-scripts/ifcfg-eth0的網卡的MAC地址為正確的 ...
  • 最近因為工作的需要看了看powershell相關的知識,個人總結了一點有關於powershell遠程連接需要做的步驟,希望對別人有所幫助。 使用powershell遠程連接,需要進行 設備的配置: 1. 開啟ps遠程管理:enable-psremoting -force (如果出現提示: 按 y 鍵 ...
  • Windows 7 與 Vmware Ubuntu 15.10_64 共用文件夾 ...
  • FreeBSD用戶手冊學習筆記 freeBSD安裝:http://my.oschina.net/lsgx/blog/540980 第一章 介紹 1.1 FreeBSD歷史和簡介 FreeBSD 自身的源代碼是完全公開的,所以可以對系統進行最大程度的定製。 FreeBSD 項目的目標是無附加條件地提供 ...
  • 安裝openssh-serversudo apt-get install openssh-server 查看server是否啟動: ps -ef |grep ssh 如果看到/usr/sbin/sshd -D,說明服務已經啟動,否則服務尚未啟動,那麼需要啟動server: /etc/init.d/s ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...