我已經開始寫下有關我觀看的安全性視頻的註釋(作為快速召回的方式)。
這些可能對初學者更有用。
這裡的筆記順序不是按難度順序排列的,而是按照我的編寫方式的反向時間順序(即,最新的第一)。
這項工作是根據創意共享歸因於非商業共享4.0國際許可證的許可。
撰寫於2017年8月12日
受Gynvael的信心CTF 2017年的直播的影響;以及他的Google CTF Quals 2017 Rivestream在這裡
有時,挑戰可能通過實施VM來實現複雜的任務。並非總是有必要完全逆轉VM並致力於解決挑戰。有時,您可以稍微恢復一點,一旦知道發生了什麼事,就可以掛接到VM,並訪問所需的東西。此外,基於計時的側通道攻擊在VM中變得更加容易(主要是由於執行了更多的“真實”指令。
僅通過尋找常數並在線搜索它們,就可以識別并快速重新識別二進製文件中的密碼有趣的功能。對於標準加密函數,這些常數足以快速猜測功能。可以更輕鬆地識別更簡單的加密功能。如果您看到很多XOR和類似的事情發生,並且不容易識別的常數,則可能是手工滾動的加密貨幣(也可能損壞)。
有時,在將IDA與十六進制一起使用時,拆卸視圖可能比倒數式觀點更好。如果您注意到在解碼視圖中似乎發生了很多並發症,但是您會在拆卸視圖中註意到重複性模式,尤其如此。 (您可以使用太空欄快速切換兩者)。例如,如果實現了(固定尺寸的)大型庫,那麼解碼視圖很糟糕,但是拆卸視圖很容易理解(並且由於重複的“帶有攜帶”的說明,例如adc並且可以易於識別)。此外,在這樣的分析時,使用IDA圖表視圖中的“組節點”功能非常有用,對於快速降低圖形的複雜性非常有用,因為您了解每個節點的作用。
對於怪異的體系結構,擁有良好的仿真器非常有用。特別是,一旦您將內存從仿真器中拿出來,可以使用可以給您帶來內存的模擬器來快速弄清楚發生了什麼,並識別有趣的部分。此外,使用以舒適的語言實現的模擬器(例如Python),這意味著您可以準確地運行自己喜歡的東西。例如,如果您可能希望多次運行的代碼中有一些有趣的部分(例如,要蠻力或其他東西),則使用模擬器,您可以快速編碼僅執行代碼那部分的內容,而不是必須運行完整的程序。
懶惰時,懶惰是好的。不要浪費時間逆向工程,而要花足夠的時間進行偵察(即使在重新挑戰中!),以便能夠減少實際完成更艱鉅的雷(Reing)的時間。在這種情況下,偵察的意思是,只需快速查看不同的功能,而無需花費太多時間來徹底分析每個功能。您只需快速衡量該功能的意義(例如“看起來像加密貨幣的東西”或“看起來像記憶管理的事物”,等等)
對於未知的硬件或體系結構,請花足夠的時間在Google上查找它,您可能會使用大量有用的工具或文檔來幸運,這些工具或文檔可能會幫助您更快地構建工具。通常,您會發現玩具模擬器等實現可能是可以從一開始就可以從中開始的。另外,您可能會獲得一些有趣的信息(例如如何存儲位圖,或者如何存儲字符串或其他內容),您可以使用這些信息來編寫一個快速的“修復”腳本,然後使用普通工具來查看是否有有趣的內容。
GIMP(圖像操縱工具)具有非常酷的打開/負載功能,可以查看原始像素數據。您可以使用它快速尋找原始二進制數據中的資產或重複結構。一定會花時間弄亂設置,以查看是否可以從中收集更多信息。
寫於2017年7月2日
受 @p4n74和 @h3rcul35的討論的影響。我們正在討論有時初學者如何從更大的挑戰開始,尤其是當它被剝離時。
要么解決重新挑戰,要么能夠在PWN上進行PWN,必須首先分析給定的二進製文件,以便能夠有效利用它。由於二進製文件可能會被剝離等(使用file找到),因此必須知道在哪裡開始分析,以使立足點從中構成。
在尋找二進製文件的漏洞時(從我收集到的漏洞中,不同的CTF團隊都有不同的偏好):
1.1。將完整的代碼轉換為C
這種分析很少見,但對於較小的二進製文件非常有用。這個想法是進入倒數工程師的整個代碼。每個功能均在IDA中打開(使用反編譯器視圖),並使用重命名(快捷方式:N)和重新啟動(快捷方式:Y)來快速使分解代碼更具可讀性。然後,將所有代碼複製/導出到一個單獨的.c文件中,可以將其編譯以獲得與原始的等效(但不相同)的二進製文件。然後,可以進行源代碼級別的分析,找到漏洞等。一旦找到了脆弱點,然後通過在IDA中的良好倒數源來構建的原始二進制構建中,並與拆卸視圖並肩(使用選項卡(使用選項卡)(使用tab)快速切換兩者之間的空間;並在圖形和文本視圖之間快速切換空間,以換取分配視圖)。
1.2。最小化分析
這是經常進行的,因為大多數二進制都是相對毫無用處的(從攻擊者的角度來看)。您只需要分析可疑的功能,或者可能導致您進入vuln。為此,有一些方法可以開始:
1.2.1。從Main開始
現在,對於被剝離的二進製文件,即使是MAIN也沒有標記(儘管IDA 6.9開始為您標記為您),但是隨著時間的流逝,您將學會識別如何從入口點到達主(IDA在其中打開的位置默認打開)。您可以跳到這一點,然後從那裡開始分析。
1.2.2。找到相關的字符串
有時,您知道可能會輸出的一些特定字符串,您知道可能很有用(例如“恭喜,您的標誌為%s”,以進行重新挑戰)。您可以跳到字符串視圖(快捷方式:Shift+F12),找到字符串,並使用Xrefs(快捷方式:X)向後工作。 XREF可讓您通過在該鏈中的所有功能上使用XREF來找到該字符串的功能路徑,直到到達MAIM(或您知道的某個點)。
1.2.3。從某些隨機功能
有時,沒有特定的字符串可能有用,並且您不想從Main開始。因此,相反,您迅速瀏覽整個功能列表,尋找看起來可疑的功能(例如有很多常數,很多XOR等)或調用重要功能(Malloc的Xrefs,free等),然後從那裡開始,然後向前啟動並向前(遵循函數)和向後(函數)(Xrefs of the函數)(XREFS)
1.3。純拆卸分析
有時,您無法使用偏這次視圖(由於怪異的結構,反編譯技術,或者手寫的組裝,或者看起來太複雜)。在這種情況下,純粹看一下拆卸視圖是完全有效的。 (對於新體系結構)打開自動註釋非常有用,該評論顯示了說明每個說明的評論。另外,節點著色和組節點功能非常有用。即使您不使用任何一種,拆卸中的定期標記評論很有幫助。如果我個人這樣做,我更喜歡寫下類似python的評論,這樣我就可以快速地手動轉移到python中(對於重新挑戰尤其有用,您可能必須使用Z3等)。
1.4。使用BAP等平台,等等。
這種分析是(半)自動化的,通常對於更大的軟件更有用,很少直接在CTF中使用。
模糊可能是快速進入漏洞的有效技術,而無需最初實際理解它。通過使用模糊器,人們可以獲得許多低懸掛的漏洞風格,然後需要對其進行分析和分析以進入實際的vuln。有關更多信息,請參閱我關於模糊和遺傳模糊基礎的筆記。
在使用靜態分析找到VULN後,可以使用動態分析,以幫助快速構建漏洞。另外,它可以用來找到vuln本身。通常,一個人在調試器內部啟動可執行文件,並嘗試沿觸發錯誤的代碼路徑。通過在正確的位置放置斷點,並分析寄存器/堆/堆棧/等的狀態,可以很好地了解正在發生的事情。人們還可以使用調試者快速識別有趣的功能。例如,可以通過最初在所有功能上設置臨時斷點來完成這一點。然後繼續進行2次步行 - 一個穿過所有無趣的代碼路徑;一條只有一條有趣的道路。第一次步行旅行所有無趣的功能並禁用這些斷點,從而使有趣的功能在第二次步行期間出現為斷點。
我的個人分析方式是從靜態分析開始,通常是從主(或基於非輔助的應用程序,來自字符串)開始,然後努力快速找到一個看起來很奇怪的函數。然後,我花時間並從這裡開始向前和向後分支,定期寫下評論,並不斷重命名和重新定型變量以改善不合情可及。像其他人一樣,我確實使用諸如Apple,Banana,Carrot等的名稱似乎有用,但是截至尚不清楚的功能/變量/等,它可以更易於分析(跟踪Func_123456名稱樣式對我來說太難了)。我還定期使用IDA中的結構視圖來定義結構(和枚舉),以使解構更加更好。一旦找到vuln,我通常會使用pwntools編寫腳本(並用它來調用gdb.attach() )。這樣,我可以控制正在發生的事情。在GDB內部,我通常使用普通的GDB,儘管我添加了一個命令peda ,該命令在需要時立即加載PEDA。
不過,我的風格肯定在不斷發展,因為我對工具變得更加滿意,並且使用了為加快速度加快速度的定制工具。我很高興聽到其他分析樣式的聲音,以及可能會更改我的風格,這可能有助於我更快地變得更快。對於您所擁有的任何評論/批評/讚美,我可以通過Twitter @jay_f0xtr0t與我聯繫。
寫於2017年6月4日
受Gynvael Coldwind的真棒現場直播的影響,他在那裡討論了ROP的基礎知識,並提供了一些技巧和技巧
面向返回的編程(ROP)是經典的開發技術之一,用於繞過NX(非可執行內存)保護。微軟已將NX納入DEP(數據執行預防)。甚至Linux等,都具有有效的效果,這意味著有了此保護,您再也無法將ShellCode放在堆/堆棧上,並僅通過跳到它來執行它。因此,現在,為了執行代碼,您可以跳入預先存在的代碼(Main Binary或其庫 - Linux上的LIBC,LDD等; Windows上的kernel32,ntdll等)。 ROP通過重新使用已經存在的代碼的片段而出現,並找出一種將這些片段結合起來做您想做的事情的方法(當然,這是破解星球!!!)。
最初,ROP從RET2LIBC開始,然後通過使用更多的小型代碼隨著時間的推移而變得更加先進。有人可能會說,由於其他保護措施,ROP現在已經“死了”,但是在許多情況下仍然可以利用它(對於許多CTF來說絕對必要)。
ROP最重要的部分是小工具。小工具是“ ROP的可用代碼”。這通常意味著以ret結束的代碼(但是其他類型的小工具也可能有用;例如以pop eax; jmp eax等結尾的小工具)。我們將這些小工具鏈在一起以形成利用,即被稱為ROP鏈。
ROP最重要的假設之一是您可以控制堆棧(即,堆棧指針指向您控制的緩衝區)。如果這不是真的,那麼您將需要應用其他技巧(例如堆棧旋轉)以在構建ROP鏈之前獲得此控制。
您如何提取小工具?使用可下載的工具(例如Ropgadget)或在線工具(例如Ropshell)或編寫自己的工具(有時可能會對更困難的挑戰更有用,因為如果需要的話,您可以將其調整為特定的挑戰)。基本上,我們只需要可以跳到這些小工具的地址即可。這是ASLR等問題的地方(在這種情況下,您會在繼續進行ROP之前會洩漏地址)。
因此,現在,我們如何使用這些小工具製作ropchain?我們首先尋找“基本小工具”。這些是可以為我們完成簡單任務的小工具(例如pop ecx; ret ,可通過放置小工具將值加載到ecx中,然後放置要加載的值,然後是鏈的其餘鏈,在值加載後返回到該值之後)。通常最有用的基本小工具通常是“設置寄存器”,“存儲寄存器值在地址指向寄存器”,等等。
我們可以從這些原始功能中建立以獲得更高級別的功能(類似於我的帖子標題為“剝削抽象”)。例如,使用Set-Register和Att-At-At-At-Ad-At-At-Ad-At-Atdress小工具,我們可以提出一個“ oke”功能,使我們可以設置具有特定值的任何特定地址。使用此功能,我們可以構建一個“ oke string”功能,使我們可以將任何特定字符串存儲在內存中的任何特定位置。現在我們已經有了戳弦,我們基本上已經完成了,因為我們可以在內存中創建任何想要的結構,並且還可以用所需的參數調用我們想要的任何功能(因為我們可以設置註冊並可以將值放在堆棧上)。
從這些低階原語構建到更複雜的事情的較大功能的最重要原因之一是減少犯錯的機會(否則在ROP中很常見)。
ROP有更多複雜的想法,技巧和技巧,但這可能是不同時間的單獨的主題:)
PS:Gyn有一個關於返回剝削的博客文章,可能值得一讀。
撰寫於2017年5月27日;延長於2017年5月29日
受Gynvael Coldwind的驚人現場直播的影響,他在那裡談論了遺傳模糊背後的基本理論,並開始建立一個基本的遺傳絨毛。然後,他繼續完成此直播中的實現。
“高級”模糊(與我的“模糊基本”註釋中描述的盲絨毛相比)。它也會修改/突變字節等,但比盲人“ Dumb” Fuzzer更聰明。
為什麼我們需要遺傳絨毛?
某些程序可能對愚蠢的模糊劑“討厭”,因為漏洞可能需要滿足一大堆條件才能達到。在愚蠢的魔力中,我們對這種情況的可能性很低,因為它是否不知道它是否取得了任何進展。作為一個特定示例,如果我們有代碼if a: if b: if c: if d: crash! (我們將其稱為崩潰代碼),然後在這種情況下,我們需要4個條件才能滿足使程序崩潰。但是,愚蠢的模糊器可能無法超越a條件,僅僅是因為所有4個突變a , b , c , d ,同時發生的可能性很小。實際上,即使僅通過a進行進展,下一個突變也可能會恢復!a只是因為它對程序一無所知。
等等,這種“壞案例”程序何時出現?
以文件格式解析器非常普遍,以一個例子為例。要達到某些特定的代碼路徑,可能需要超越多個檢查“這個值必須就是這個,並且該值必須就是那個,而其他一些值必須是其他東西”等等。此外,幾乎沒有現實世界的軟件“簡單”,並且大多數軟件都有許多可能的代碼路徑,其中一些可能只有在該州的許多內容正確設置後才能訪問。因此,其中許多程序的代碼路徑基本上是愚蠢的模糊基本上無法訪問的。此外,有時,由於沒有足夠的突變,某些路徑可能完全無法訪問(而不是瘋狂地不可能)。如果這些路徑中的任何一個都有錯誤,那麼愚蠢的偽造者將永遠無法找到它們。
那麼,我們如何比愚蠢的模糊劑做得更好?
考慮上述碰撞器代碼的控制流程圖(CFG)。如果偶然的愚蠢的魔力突然得到正確的a ,那麼它也不會認識到它達到了一個新節點,但是它會繼續忽略它,丟棄樣本。另一方面,AFL(以及其他遺傳或“智能”模糊器)是他們將其視為新信息(“新近到達的路徑”),並將此樣本存儲為語料庫的新初始點。這意味著現在,模糊器可以從a塊開始並進一步移動。當然,有時候,它可能會返回b a樣品中的!a這再次是一個新的節點,因此在語料庫中添加了一個新樣本。這繼續存在,允許檢查越來越多的路徑,最後到達了crash! 。
為什麼這起作用?
通過將突變的樣品添加到語料庫中,可以探索圖形更多(即以前未探索的部分),我們可以到達以前無法到達的區域,因此可以掩蓋此類區域。由於我們可以掩蓋此類區域,因此我們也許可以在這些地區發現錯誤。
為什麼被稱為遺傳模糊?
這種“智能”模糊類似於遺傳算法。標本的突變和交叉引起新標本。我們保留更適合經過測試條件的標本。在這種情況下,條件是“圖中有多少個節點?”。可以保留越多的人。這並不像遺傳算法,而是一種變化(因為我們將所有標本都保留在遍歷未開發的領域,並且我們不進行跨界),但是足夠相似,可以得到相同的名稱。基本上,從預先存在的人群中進行選擇,其次是突變,然後進行健身測試(無論是看到新區域)和重複。
等等,所以我們只是跟踪未觸及的節點嗎?
不,不是真的。 AFL跟踪圖表中的邊緣遍歷,而不是節點。此外,它不僅說“邊緣旅行或不旅行”,還跟踪了邊緣穿越多少次。如果邊緣遍歷0、1、2、4、8、16,...時間,它被認為是“新路徑”,並導致添加到語料庫中。之所以這樣做,是因為查看邊緣而不是節點是區分應用程序狀態的更好方法,並且使用指數增加的邊緣遍歷計數可提供更多信息(邊緣遍歷一次與遍歷兩次完全不同,但是遍歷10與11次沒有太大不同)。
那麼,您在遺傳爆炸器中需要什麼?
我們需要兩件事,第一部分稱為示踪劑(或示踪儀器)。它基本上告訴您在應用程序中執行了哪些說明。 AFL通過在編譯階段之間跳入簡單的方式來做到這一點。生成組件後,但是在組裝程序之前,它會尋找基本塊(通過查看結局,檢查躍升/分支類型的指令),並將代碼添加到每個塊中,以將塊/邊緣標記為執行的塊/邊緣(可能是某些陰影存儲器之類的東西)。如果我們沒有源代碼,我們可以使用其他技術進行跟踪(例如PIN,調試器等)。事實證明,即使是Asan也可以提供覆蓋信息(請參閱此文件)。
然後,在第二部分中,我們使用示踪劑給出的覆蓋範圍信息來跟踪新路徑的顯示,並將這些生成的樣本添加到語料庫中,以便將來隨機選擇。
有多種機制可以製作示踪劑。它們可以基於軟件,也可以基於硬件。對於基於硬件的情況,例如,存在一些英特爾CPU功能,其中給定存儲器中的緩衝區,它記錄了所有基本塊的信息。它是一個內核功能,因此內核必須支持它並將其作為API(Linux具有)。對於基於軟件的,我們可以通過添加代碼或使用調試器(使用臨時斷點,或通過單個步進)來做到這一點,或使用地址消毒器的跟踪能力,或使用掛鉤或模擬器,或其他許多其他方法。
區分機制的另一種方法是通過Black-Box跟踪(您只能使用未修改的二進製文件)或軟件White-Box跟踪(您可以訪問源代碼,並修改代碼本身以添加跟踪代碼)。
AFL在編譯過程中使用軟件儀器作為追踪方法(或通過QEMU仿真)。 Honggfuzz支持基於軟件和基於硬件的跟踪方法。其他智能模糊可能會有所不同。 Gyn構建的一種使用地址消毒劑(ASAN)提供的跟踪/覆蓋範圍。
一些模糊器使用“ Speedhacks”(即提高模糊速度),例如通過提出叉子或其他此類想法。可能值得研究這些:)
寫於2017年4月20日
受Gynvael Coldwind的真棒現場直播的影響,他在那裡談論了什麼模糊,還從頭開始建立了基本的模糊!
首先,什麼是模糊器?我們為什麼要使用它?
考慮到我們有一個獲取輸入數據的庫/程序。輸入可以以某種方式結構(例如PDF,PNG或XML等;但不需要任何“標準”格式)。從安全的角度來看,如果輸入和過程 /庫 /程序之間存在安全邊界,那麼有趣的是,我們可以傳遞一些“特殊輸入”,這會導致超出該邊界以外的意外行為。模糊器就是這樣做的。它是通過“突變”輸入中的事物(從而使其損壞)來實現這一目標,以導致正常執行(包括安全處理錯誤)或崩潰。由於邊緣案例邏輯不能很好地處理,因此可能發生這種情況。
崩潰是錯誤條件的最簡單方法。可能還有其他。例如,使用ASAN(地址消毒劑)等可能也導致檢測更多內容,這可能是安全問題。例如,緩衝區的單個字節溢出可能不會自行造成崩潰,但是通過使用ASAN,我們甚至可以使用Fuzzer捕獲它。
fuzzer的另一個可能用途是,通過模糊一個程序生成的輸入也可以在另一個庫/程序中使用,並查看是否存在差異。例如,這樣的一些高精度數學庫錯誤是這樣的。但是,這通常不會導致安全問題,因此我們不會專注於這麼多。
模糊如何工作?
fuzzer基本上是一種突變的執行循環,它探討了應用程序的狀態空間,以嘗試“隨機”查找崩潰 /安全性vuln的狀態。它找不到漏洞,只是一個vuln。模糊的主要部分是突變器本身。稍後再詳細介紹。
來自fuzzer的輸出?
在模糊器中,(有時)將調試器附加到應用程序中,以從崩潰中獲取某種報告,以便稍後將其分析為安全液體與良性(但可能很重要的)崩潰。
如何首先確定哪些程序最能陷入模糊?
當模糊時,我們通常希望專注於一組或一組程序。這通常主要是為了減少要執行的執行量。通常,我們只專注於解析和處理。同樣,安全邊界在確定哪些部分對我們很重要時很重要。
類型的模糊?
給出的輸入樣本稱為copus 。在Oldschool Fuzzers(又名“盲人”/“ Dumb” Fuzzzers)中,有必要進行大型語料庫。較新的(例如,又名“遺傳”模糊器,例如AFL),不一定需要如此大的語料庫,因為它們會自己探索國家。
模糊如何有用?
模糊劑主要用於“低懸掛果”。它不會發現複雜的邏輯錯誤,但是很容易找到錯誤(實際上,在手動分析中,這實際上很容易錯過)。雖然我可能會在整個註釋中說輸入,並且通常是指輸入文件,但不僅僅是這樣。 Fuzzer可以處理可能是stdin或輸入文件或網絡套接字的輸入。但是,如果沒有過多的一般性損失,我們可以將其視為目前的文件。
如何編寫(基本)絨毛?
同樣,它只需要是一個突變的重複循環即可。我們需要能夠經常調用目標( subprocess.Popen )。我們還需要能夠將輸入傳遞到程序(例如:文件)中並檢測崩潰( SIGSEGV等)導致例外,可以捕獲)。現在,我們只需要為輸入文件編寫一個突變器,然後繼續在突變文件上調用目標。
突變器?什麼? ! ?
可能有多個可能的突變器。容易(即易於實現)可能是突變,突變字節或突變為“魔術”值。為了增加崩潰的機會,而不是僅更改1位或其他東西,我們可以更改多個(也許是某些參數化百分比?)。我們還可以(而不是隨機突變),將字節/單詞/dwords/等更改為某些“魔術”值。魔術值可能為0 , 0xff , 0xffff , 0xffffffff (32位INT_MIN ), 0x7fffffff (32- 0x80000000 INT_MAX )等。基本上,選擇與引起安全問題的共同點(因為它們可能觸發某些邊緣情況)。如果我們知道有關該程序的更多信息,我們可以編寫更智能的突變器(例如,對於基於字符串的整數,我們可能會寫一些將整數字符串更改為"65536"或-1等的內容)。基於塊的突變器可能會移動零件(基本上是重新組織輸入)。添加/附加突變器也有效(例如,導致更大的輸入到緩衝區中)。截斷器也可能起作用(例如,有時EOF可能無法很好地處理)。基本上,嘗試一系列創造性的方式來弄髒事物。關於該程序的經驗(一般而言)的經驗越多,可能會有更多有用的突變器。
但是,這種“遺傳”模糊是什麼?
這可能是以後的討論。但是,一些指向某些現代(開源)模糊的鏈接是AFL和Honggfuzz。
寫於2017年4月7日
受到2017年Picoctf的挑戰的影響(由於比賽仍在進行中,因此挑戰的名稱)
警告:對於某些讀者來說,這張註釋似乎很簡單/顯而易見,但這需要說,因為直到最近我才清楚地層。
當然,當編程時,我們所有人都使用抽象,無論它們是類,對象,功能,元功能,多態性,單子或單調或函數,或所有爵士樂。但是,在剝削過程中,我們真的可以有這樣的事情嗎?顯然,我們可以利用實施上述抽象時犯的錯誤,但是在這裡,我談論的是不同的事情。
在多個CTF中,每當我以前寫過exploit時,它一直是一個掉落的腳本,它會丟棄外殼。我將驚人的pwntools用作一個框架(用於連接到服務,轉換事物以及Dynelf等),但僅此而已。每個漏洞利用往往是為實現任意代碼執行目標的臨時方式。但是,當前的挑戰以及我先前關於“高級”格式弦剝削的註釋,使我意識到我可以以一致的方式分層漏洞,並通過不同的抽象層進行移動以最終達到必要的目標。
例如,讓我們將漏洞視為邏輯錯誤,這使我們可以進行4個字節的讀/寫入,但緩衝區後的範圍很小。我們想一直濫用這一點,以獲得代碼執行,最後是標誌。
在這種情況下,我認為這種抽像是一個short-distance-write-anything原始的。有了這個本身,顯然我們不能做太多。但是,我製作了一個小的python函數vuln(offset, val) 。但是,由於在緩衝區之後,可能會有一些數據/元數據可能有用,因此我們可以濫用它來構建任何read-anywhere ,又write-anything-anywhere內容。這意味著,我編寫了簡短的Python函數,該函數調用先前定義的vuln()函數。這些get_mem(addr)和set_mem(addr, val)函數僅通過使用vuln()函數覆蓋指針而簡單地(在當前示例中)製作,然後可以在二進制中的其他地方重新定位。
現在,在使用這些get_mem()和set_mem()抽象之後,我通過從get_mem()中洩漏了2個地址,並與libc數據庫進行比較(感謝@niklasb的數據庫)。這些來自這些的偏移使我有一個可靠的libc_base ,這使我可以用libc的另一個功能替換got中的任何功能。
從本質上講,這使我可以控制EIP(當我可以在我想要的時候準確地“觸發”其中一個功能的那一刻)。現在,剩下的就是用正確的參數調用觸發器。因此,我將參數設置為單獨的抽象,然後調用trigger() ,並且在系統上具有shell訪問權限。
tl; dr:一個人可以建立小的剝削原語(沒有太多的功率),並且通過將它們結合併建立更強原始的層次結構,我們可以完全執行。
寫於2017年4月6日
受Gynvael Coldwind的真棒現場直播的影響,他在那裡談論格式剝削
簡單格式字符串利用:
您可以使用%p查看堆棧上的內容。如果格式字符串本身在堆棧上,則可以將地址(例如foo )放在堆棧上,然後使用位置指定符n$進行搜索(例如, AAAA %7$p可能返回AAAA 0x41414141 ,如果7是堆棧上的位置)。然後,我們可以使用它來構建一個讀取的原始詞,而是使用%s格式指定符(例如, AAAA %7$s將在地址0x41414141上返回該值,以繼續上一個示例)。我們還可以使用%n格式指定符將其變成原始的寫入。通常,我們使用%hhn (glibc擴展名,iirc),這使我們可以一次編寫一個字節。
我們使用上述原語最初擊敗ASLR(如果有),然後在got中覆蓋條目(例如exit()或fflush()或...),然後將其提高到任意eip-controllol原始性,這基本上為我們提供了任意的代碼執行。
可能的困難(這使其成為“高級”開發):
如果我們有部分ASLR ,那麼我們仍然可以使用格式字符串並擊敗它,但是如果我們只有一擊漏洞,這將變得更加困難(即,我們的利用需要即時運行,並且每次運行中的地址是隨機的,說)。我們擊敗這一點的方式是使用已存儲器中已經存在的地址,並部分覆蓋它們(因為ASLR僅影響高階位)。這樣,我們可以在執行過程中獲得可靠性。
如果我們只有一個讀取.got部分,那麼覆蓋GOT的“標準”攻擊將無效。在這種情況下,我們尋找可以被覆蓋的替代區域(最好是功能指針)。某些這樣的領域是: __malloc_hook (請參閱man頁面相同), stdin的VTable Pointer以write或flush等。在這種情況下,可以訪問LIBC源非常有用。至於覆蓋__malloc_hook ,即使應用程序未調用malloc ,它也可以工作,因為它調用printf (或類似),並且在內部,如果我們通過了大於64K(例如%70000c )的寬度指定符,則它將調用Malloc,因此它將調用Malloc,因此將在全球可變__malloc_hook上指定任何地址。
如果我們的格式字符串緩衝區不在堆棧上,那麼我們仍然可以獲得一些原始的文字,儘管它更為複雜。首先,我們需要停止使用位置指定器n$ ,因為如果使用了該位置,則在內部將printf複製堆棧(我們將在繼續時進行修改)。現在,我們發現了兩個指向堆棧本身的指針,並使用這些指針覆蓋堆棧上的兩個指向指向指向指向的下階字節,以便它們現在指向x+0和x+2 ,其中x在堆棧上方的某個位置。使用這兩個覆蓋物,我們能夠完全控制x處的4個字節,這成為我們在原始中的位置。現在,我們只需要忽略格式字符串上的更多位置,直到我們到達這一點上,並且我們有一個原始的文字。
寫於2017年4月1日
受Gynvael Coldwind的驚人現場直播的影響,他在那裡解釋了比賽條件
如果訪問了內存區域(或文件或任何其他資源) ,則假設它保持相同,但是由於線程的切換,我們能夠更改值,我們有一個賽車條件。
最常見的類型是tocttou(檢查時間的檢查時間),其中首先檢查一個變量(或文件或任何其他資源)以獲取某些值,如果某些條件的通過,則使用它。在這種情況下,我們可以通過連續“垃圾郵件”入住一個線程來攻擊它,在另一個線程中,在另一個線程中不斷“翻轉”它,以使由於隨機性,我們可以在“窗口窗口窗口”的中間進行翻轉,這是檢查和使用之間的(短)時間範圍。
通常,出現窗口可能很小。我們可以使用多種技巧,以將此機會窗口增加3倍甚至最高〜100倍。我們通過控制價值如何緩存或分頁來做到這一點。如果值(假設一個long int )不與緩存線對齊,則可能需要訪問2條緩存線,這會導致同一指令執行的延遲。或者,打破頁面上的對齊(即,將其放在頁面邊界上)可能會導致更大的訪問時間。這可能會給我們更大的機會觸發比賽狀況。
有更明智的方法來改善這種種族狀況狀況(例如清除TLB等,但有時甚至不需要這些)。
在(可能)在極端情況下,可以使用種族條件來獲得RING0代碼執行(因為它是內核模式的執行,因此“高於root”)。
可以通過在架構模擬器上構建工具/插件“自動”找到種族條件。有關更多詳細信息,http://vexillium.org/pub/005.html
寫於2017年3月31日
受Gynvael Coldwind的驚人現場直播的影響,他正在堆上試驗
免費使用:
讓我們說我們有很多指針到堆中,並且在不確保所有這些指針都更新的情況下被釋放。這將使一些懸空的指針進入自由的空間。通常,通常將不同類型的另一種分配給同一區域,以便您控制不同的區域,然後您可以濫用此區域以獲得(可能是)任意代碼執行。
雙重:
釋放一個內存區域,然後再次將其釋放。如果可以這樣做,則可以通過控制Malloc使用的內部結構來控制。與無用的使用相比,這可能會變得複雜,因此,如果可能的話,最好使用該使用。
堆上的經典緩衝區溢出(堆流):
If you can write beyond the allocated memory, then you can start to write into the malloc's internal structures of the next malloc'd block, and by controlling what internal values get overwritten, you can usually gain a read-what-where primitive, that can usually be abused to gain higher levels of access (usually arbitrary code execution, via the GOT PLT , or __fini_array__ or similar).