【整理】HITBCTF 部分 WriteUp
目錄
MIsc
- IRC checkin
- tpyx
- pix
- read file
PWN
- once
- babypwn
- d
- gundam
Web
- upload
- Pythons revenge
Reverse
- hacku
Crypto
- base
Mobile
- multicheck
- ivysimple
Misc
IRC checkin
簽到題,加入IRC flag寫在公告裏就行了
HITBXCTF{W3lcome_To_HITBXCTF_2018_Online_Qualifications}tpyx
拿到圖片第一反應是丟進Stegsolve發現打不開,用winhex打開通過觀察發現是IDAT的length存在問題,對長度進行修復。然後使用binwalk分析
發現存在一個zlib,對其進行分離得到兩個新文件
繼續對29進行分析發現還存在一個zlib,繼續分離
得到兩個新文件
打開28D28D,發現裡面存在一串十六進位,通過分析發現這是一個7z文件,用winhex構造7z文件
密碼寫在注釋裡面,解壓得到flag
HITB{0c88d56694c2fb3bcc416e122c1072eb}
pix
下載後得到文件: aee487a2-49cd-4f1f-ada6-b2d398342d99.SteinsGate
執行file命令,發現是一個png圖片用pngcheck檢查執行,沒發現錯誤,binwalk一下也沒任何發現,就直接用stegslove打開,選擇數據導出:
保存後用file命令查看一下,如下:
通過以上關鍵字搜索到如下信息:
將從圖片中導出的文件重命名為1.kdbx,通過以下鏈接https://fileinfo.com/extension/kdbx下載keepass,打開1.kdbx,發現需要輸入密碼,由hint可知密碼為hint+6位數字
參考如下鏈接:https://bbs.ichunqiu.com/thread-36593-1-1.html 生成了相應的字典進行爆破,得到密碼為hitb180408hashcat64.exe -a 0 -m 13400 c:UserswangztDesktoppix.hash
c:UserswangztDesktopsuperdic1.txt --forcehashcat64.exe -a 0 -m 13400 c:UserswangztDesktoppix.hashc:UserswangztDesktopsuperdic1.txt --force --show
read file
echo $input | grep -o "[[:punct:]]*"
從表面上看是隻顯示標點符號,但當輸入$$時,輸出了pid:由此可以判斷他實際過濾的是輸入,即只能輸入標點符號,利用通配符尋到了flag的位置:
但不能用直接輸入cat之類的,於是用相同方法找到/bin/cat位置,用$()執行裡面的命令,
最後payload:$(~/../../???/??? ~/../????????????/????.???)
在文件底部找到flag:
Pwn
once
程序主體
程序實現了一個雙鏈表,允許改變一次鏈表節點 ,選項4進入子選項允許申請一次非fastbin堆塊,允許寫入一次,釋放一次。
- 漏洞:
- 當輸入選項不符合要求是會列印puts地址
- 一次輸入一次
- 雙鏈表解連時存在一次任意內存寫,內容不可控。
- 漏洞利用
- 首先通過列印puts獲得libc地址
- 通過一次內存任意寫改global_max_fast,
- 此時申請堆塊大小都會被計算為fastbin,尋找fastbin時能夠得到libcdata段地址,通過改寫file vtabl getshell
- 最終腳本
Babypwn
- fmt盲pwn
- 通過格式串dump bin文件
- 修復ida解析得到printf got表
- 通過對比發現libc和once相同,格式串改寫printf got getshell
- 最終腳本
d
- 程序主體
- 實現功能
puts("1. read message");
puts("2. edit message");puts("3. wipe message");puts("4. exit");- 漏洞
- 存在單位元組溢出
- 利用
- 通過單位元組溢出漏洞製造unlink拿到指向bss自身指針
- 通過指針更改got內容完成info leak 和getshell
- 輸入長度由strlen控制需要將strlen改為alarm_plt+6使輸入長度變大
- 最終腳本
gundam
- 程序主體
- 結構體 struct { int inuse; char* name; char type[0x18] }
- 程序在bss段保存列表和計數
- libc版本位2.26
- 漏洞
- 在選項3刪除時bss列表中的指針沒有清空,可以double free
- 利用
- 通過多次刪除堆塊,滿足tcahce的上限(7),將堆塊放入arena的bin中得到libc中得地址,完成地址泄露
- 利用tcache實現任意地址寫
- 最終腳本
Web
Upload
經過掃描開放的web埠的指紋,得知伺服器為windows,IIS7,php。這裡考
察的是windows下<<符號可以,用於替代路徑上的未知字元。
1.上傳test.pHp2.訪問
http://47.90.97.18:9999/pic.php?filename=default.jpg
3.使用繞過gd庫的腳本生成圖片馬,再次上傳,一樣有出現 width和height。
有上述步驟確認,可以上傳為後綴為pHp,文件,目錄與default.jpg 同級。
於是開始爆破default.jpg 所在目錄。
得知
wwwroot/87194f13726af7cee27ba2cfe97b60df/default.jpg
訪問第一次上傳的pHp
使用kali自帶的weevely生成php後門,可getshell
Pythons revenge
這裡考察的就是pickle反序列化的問題,我們可以控制反序列化的內容,就可
以直接構造__reduce__
魔術方法任意命令執行
但這裡加了一個hook裝飾器對callback進行過濾,然後用的是黑名單過濾,發
現可以用map函數去繞過黑名單的限制
整理一下大概思路就兩步,關鍵主要是黑名單繞過吧:
- 訪問
/reminder
生成location, 本地爆破secret,得到secret : hitb - 利用map函數繞過黑名單任意命令執行, 構造惡意的location, 訪問首頁即可觸發反序列化漏洞
沒有回顯的命令執行,直接用weblog方式帶出來
Reverse
Hacku
解壓縮包得到一個流量包和一個chm
Chm裏很顯然包含著後門執行程序
通過hh.exe可以將其解包,在doc.htm中發現了後門程序的執行
通過powershell隱蔽執行了一段b64,解密後發現是一段Unicode
又是一段b64,解b64後又用了DeflateStream解壓縮
查了一下zlib什麼的亂七八糟很麻煩,乾脆直接去掉IEX即可
得到一段腳本
觀察發現通過nslookup對http://x.hacku.org發起解析,返回的字元串解b64後執行
於是去流量包裏找DNS
將流量包dump出來並清洗後解析,得到下一段腳本
僅混淆了函數名,通讀一遍後標記即可
邏輯為向cc.php發包,再解析返回的包來執行指定的命令
發包和收包都是先b64,再逐字元加密,再b64一次
於是分別寫出發包和收包的解密函數,從流量包中dump出data,然後解密
Recv中可以看到data有兩種,指定傳輸moliboom文件和下載並執abs_stage3
Moliboom的發包中可以解出path和txt,在fix後的數據中找到flag的前半段
Abs_stage3返回的字元串被ps執行了,所以顯然也是腳本
但是混淆很嚴重,包括大小寫混雜、逆序、replace等
粗看一遍大概能發現是寫入了某個exe並執行,然後刪除
由於是exe,因此一定會落地在磁碟裏,在ISE中調試即可捕捉到
在執行前斷住,然後通過路徑和文件名去找這個文件
抓住以後進行反編譯
值得一提的是,sub_401050是個花指令控制函數,通過堆棧的操作使得ret跳到[eax]個位元組之後的地址。需要手動/腳本將其後的
花指令nop掉才能使IDA正常反編譯
程序開啟了磁碟的交互
對主扇區的512和1024處進行了寫入
這裡是MBR(主引導程序)的地址,因此實際上是個勒索病毒吧233
分別將418DB0和4187b0dump下來
查了一下,MBR是直接送給CPU執行的彙編程序,因此同樣按照x86反彙編
Dump下來以後用IDA打開即可查看
注意MBR是16bit模式
16bit模式無法使用F5,所以只能生啃彙編
好消息是MBR的空間有限,程序不會太過複雜
猜測應該是簡單的加密以後直接對硬編碼進行cmp
因此通過查找cmp可以在一定程度上加快速度
裡面的讀寫調用都是通過int 10/13/16來執行的,具體方法跟ax參數有關
不進行動態調試的話難度有點大,因為沒法準確鎖定讀寫的函數
Sub_0x457是input函數,循環接受最多16個字元,值得一提的是沒有退格,輸入一個算一個。或者遇到0x0d,即回車也會提前結束
Sub_0x494是check函數,對input進來的內容進行一些變換,然後與硬編碼cmp
主要邏輯在這裡
做題的時候最好還是動態調試,因為猜邏輯太難了233
動態調試主要參考52論壇的@willJ的方法
https://www.52pojie.cn/thread-173889-1-1.html
在此附加一些個人經驗,和對本題的特殊之處
首先要重新啟動一個ida,在進入界面選擇「GO」
直接附加會內存指令無法正常顯示
因為MBR的入口點在0x7c00處,該dump程序會自動被裝載到0x7c00起點的地方。
自己用彙編程序編譯出來的MBR似乎會自動將入口點放在合適的0x7c00,使得動態調試直接命中。
但是dump下來的idb起點是0x00,也許前面放上一些00使其偏移過去也行?
按照文章附加上以後在0x7c00處下斷,此時該處為空值,無視它繼續下斷,再F9即會斷下
單步運行到check函數中
發現ax中放著輸入的字元,先ror3,sub_4df是花指令,無視即可,再依次執行xor0x74, rol5,add0x47
最後按照最低位為0/1來-1/+1
之後與bx+141處的值cmp,全部dump下來後逆向求解即可
腳本:
r = [37, 161, 57, 137, 166, 157, 213, 165, 117, 141, 74, 146, 241, 89, 94, 145]
a = [i+1 if(i&1) else i-1 for i in r]for i in range(16):print(chr(rol(ror((a[i]-0x47)&0xff, 5)^0x74, 3)), end=)
cmp成功以後會輸出一個」}n」來補齊
與之前流量包中的前半段flag合併後提交完成
Flag:
HITBXCTF{W0rk_1n_th3_dark_ Tu_s4v@thr#1Ig&q}
Crypto
Base
這個一開始沒思路,然後就開始爆破,最終爆破出flag。但是題目提示了不用爆破,顯然我們使用了非預期演算法。Mobile
Multicheck
反編譯發現load了"libcheck.so",但是沒有導入native函數
反射了com.a.Check類中的check方法,這個類暫時沒有發現
順手瞟一眼claz.dex,逐字元比較了一個看起來像是flag但顯然不是flag的字元串
出題師傅的惡趣味真是~
於是Java層中暫時沒什麼線索了,反編譯so看看
在init_array中發現了一個函數,跟過去往裡看
Sub_1380裏有一些令人在意的東西
新建了一個文件,往裡寫了一些內容
把byte_4004裏的值異或後dump下來
打開發現是個dex,反編譯得到com.a.check類
Check方法裏將arg和硬編碼b進行了比較
然後一路跟著a進去,是4個同名不同參的重載函數
先整理了個數,將bytes後移,第一個byte存儲偏移,之後補0
然後每8個byte一組,轉成兩個int(大端序)
然後是核心變換,與之前強網杯的hide演算法相同
32輪迭代,每輪先對key自增,然後互相運算
逆起來很簡單,按著邏輯先把v1自增32次,然後將v2自減,再將v3自減,最後v1自減即可
然後有一個關鍵問題就是,之前的hide是elf格式的,而這次在安卓中的jvm中執行,寫了python腳本怎麼跑都不對
由於是不同的dex,jeb也跟不進反射的方法,Xp hook的話又太麻煩。
最後突發奇想複製到java環境裏執行,果然得到了正確的邏輯:
Java中>>為帶符號的右移,要考慮各個參數的正負,如果是負數>>時應補1。
Python調了半天都沒修正好,默認的int是無限寬度的,必須用0xffffffff去修正,而這會導致符號丟失。
調了一萬年以後突然想起來何苦非要用python寫腳本呢,遂將java的check類邏輯小改,得到flag
解碼腳本:
import java.util.Arrays;public class HelloWorld{ public static void main(String[] args) { int i; byte[] t = { 99, 124, 101, -23, -114, 81, -47, -39, -102, 79, 22, 52, -39, -94, -66, -72, 101, -18, 73, -27, 53, -5, 46, -20, 97, 11, -56, 36, -19, -49, -112, -75 }; byte[] tt = re(t); for(i=0;i<tt.length;i++) { System.out.print((char)(tt[i]&0xff)); /*System.out.print(", "); if((i+1)%8==0 && i!=0) { System.out.println(); }*/ } } private static int[] a = { -1414812757, -842150451, -269488145, 305419896 }; private static byte[] b = { 99, 124, 101, -23, -114, 81, -47, -39, -102, 79, 22, 52, -39, -94, -66, -72, 101, -18, 73, -27, 53, -5, 46, -20, 97, 11, -56, 36, -19, -49, -112, -75 }; private static byte[] a(int[] paramArrayOfInt, int paramInt) { byte[] arrayOfByte = new byte[paramArrayOfInt.length << 2]; int i = 0; while (paramInt < arrayOfByte.length) { arrayOfByte[(paramInt + 3)] = ((byte)(paramArrayOfInt[i] & 0xFF)); arrayOfByte[(paramInt + 2)] = ((byte)(paramArrayOfInt[i] >> 8 & 0xFF)); arrayOfByte[(paramInt + 1)] = ((byte)(paramArrayOfInt[i] >> 16 & 0xFF)); arrayOfByte[paramInt] = ((byte)(paramArrayOfInt[i] >> 24 & 0xFF)); i += 1; paramInt += 4; } return arrayOfByte; } private static int a(byte paramByte) { int i = paramByte; if (paramByte < 0) { i = paramByte + 256; } return i; } private static int[] b(byte[] paramArrayOfByte, int paramInt) { int[] arrayOfInt = new int[paramArrayOfByte.length >> 2]; int i = 0; while (paramInt < paramArrayOfByte.length) { arrayOfInt[i] = (a(paramArrayOfByte[(paramInt + 3)]) | a(paramArrayOfByte[(paramInt + 2)]) << 8 | a(paramArrayOfByte[(paramInt + 1)]) << 16 | paramArrayOfByte[paramInt] << 24); i += 1; paramInt += 4; } return arrayOfInt; } public static byte[] re(byte[] paramString) { return re_a(paramString); } public static byte[] re_a(byte[] arrayOfByte) { /*int i = 8 - paramArrayOfByte.length % 8; byte[] arrayOfByte = new byte[paramArrayOfByte.length + i]; arrayOfByte[0] = ((byte)i); System.arraycopy(paramArrayOfByte, 0, arrayOfByte, i, paramArrayOfByte.length);*/ int i; byte[] paramArrayOfByte = new byte[arrayOfByte.length]; i = 0; while (i < paramArrayOfByte.length) { System.arraycopy(re_c(arrayOfByte, i, a, 32), 0, paramArrayOfByte, i, 8); i += 8; } return paramArrayOfByte; } static byte[] re_c(byte[] paramArrayOfByte, int paramInt1, int[] paramArrayOfInt, int paramInt2) { int[] ArraryInt = b(paramArrayOfByte, paramInt1); int i = ArraryInt[0]; paramInt1 = ArraryInt[1]; //System.out.println(Integer.toHexString(i)); //System.out.println(Integer.toHexString(paramInt1)); int k = 0; int m = paramArrayOfInt[0]; int n = paramArrayOfInt[1]; int i1 = paramArrayOfInt[2]; int i2 = paramArrayOfInt[3]; int j = 0; while(j<paramInt2) { k -= 1640531527; j += 1; } while (j > 0 ) { paramInt1 -= ((i << 4) + i1 ^ i + k ^ (i >> 5) + i2); i -= ((paramInt1 << 4) + m ^ paramInt1 + k ^ (paramInt1 >> 5) + n); /*System.out.print((paramInt1)); System.out.println(" ");*/ k += 1640531527; j -= 1; } ArraryInt[0] = i; ArraryInt[1] = paramInt1; return a(ArraryInt, 0); }}
Kivysimple
先查了一下,發現kivy是python開發apk的套件
那麼很顯然,這種套件的邏輯應該是在安卓中安裝python虛擬機,然後將py編譯成pyc/pyo來執行
掃了一圈assest和lib,沒有發現pyo
不過lib中看到了python27.so,基本可以確定我們的判斷
Libmain.so這個名字很吸引人,進去看一眼
查字元串,發現
都是python的原始代碼,也就是說虛擬機確實在安卓中解釋執行代碼了
雖然還是沒有發現程序的代碼,但是注意到有很多自帶的log,於是進行安裝運行嘗試
發現虛擬機在這個路徑裏,過去看看
一下就抓到了關鍵,將它pull出來並反編譯
出題師傅的惡趣味真是~x2
顯然關鍵在b64解出來的代碼裏,於是將eval去掉,查看code object的信息
PS:由於源代碼為python2所寫,因此必須使用python2來解碼。Python3的marshal格式不同,會報錯。
可以看到有一個check函數,也是code object
先通過dis模塊來解析位元組碼
啥都沒有╮(╯_╰)╭就是調用了check函數而已
於是再解析check
還好沒有很複雜的結構,基本上看單詞和值就能大差不離的猜出來
s = flagif(len(s)!=31): return Falseif(s[17]!=』7』): jmp 54…
下面都是類似的結構
因此只要把對應的下標和值提取出來即可得到flag
數據清洗使用正則匹配,或者手動複製都可233
得到flag:
HITB{1!F3_1S_&H%r7_v$3_pY7#ON!}
ps:公眾號更新會比較快,歡迎大家關注白帽100安全攻防實驗室公眾號
------------W&P CTF戰隊納新------------
W&P名字來自於Whitecap100 CTF of Web&Pwn的縮寫。而把Pwn放進戰隊名字是因當時參加了百度杯總決賽但因沒有Pwn選手(當時我們只有三個Web選手)使我們從穩穩的第三一路掉到第八。希望能夠牢記Pwn的重要性,幾個團隊創始人一拍即合決定以原先參加百度杯的人為基礎組建一支屬於自己的CTF戰隊。
現戰隊特向廣大CTF愛好者,白帽子招收MISC、PPC、CRYPTO、PWN、REVERSE、WEB、STEGA各類賽題的選手。
當然最重要的是福利了
1.成為白帽100安全攻防實驗室核心成員擁有各項特權
2.參加各類比賽,讓你一戰成名
3.提供就業機會,與多家公司有良好的合作關係。
4.團隊專屬徽章,彰顯你的獨特
5.獎金!!
6.過年過節小禮品什麼的還是有的
有意加入的請將簡歷投至 #[email protected]#
推薦閱讀: