# Final CTF
:::success
網址: [final.nisra.net](https://final.nisra.net)
token: NISRA{W3lc0M3_2_F1na1_CTF}
CTF 持續日期: 2024/8/29 ~ 2024/9/6
:::
題目表
---
- MISC
- [0x0](#0x0---sicc)
- [0x1](#0x1---sicc)
- [0x2](#0x2---sicc)
- [0x3](#0x3---sicc)
- [0x4](#0x4---sicc)
- [0x5](#0x5---sicc)
- [0x6](#0x6---sicc)
- [0x7](#0x7---hA)
- [0x8](#0x8---hA)
- [0x9](#0x9---hA)
- Pwn
- [HowLong](#HowLong---sicc)
- [moneyANDskill](#moneyANDskill---sicc)
- [moneyANDskill_rev](#moneyANDskill_rev---sicc)
- Crypto
- [bababase64](#bababase64---sicc)
- [ROT](#ROT---sicc)
- [Password_good](#Password_good---sicc)
- [維吉尼亞密碼+社交工程](#維吉尼亞密碼+社交工程---sicc)
- OS
- [/?/?/flag.txt](#/?/?/flag.txt---sicc)
- Web
- [ψ(`∇´)ψ](#ψ(`∇´)ψ---101)
- [Hello](#Hello---101)
- [Pick cat!](#Pick cat!---sicc)
- [Invisible](#Invisible---101)
- [GETGETGET](#GETGETGET---101)
- [🍪](#🍪---101)
## MISC
### 0x0 - sicc
>Flag 在 0x0.txt 中
<!-- NISRA{I_4CC3Pt_tH3_CH4LL3nG3_4nD_I_4m_r34dY} -->
### 0x1 - sicc
>1. 看到標頭是 `89 50 4E 47 0D 0A 1A 0A`(PNG 格式),但結尾是 `FF D9`(jpg 格式),所以選擇將標頭改成 `FF D8 FF E0 00 10 4A 46 49 46`

>2. 然後將圖片高度改為 `04 F4` 就能夠找到 flag 了
<!-- NISRA{JP3G_15_FuN} -->
### 0x2 - sicc
>1. 看到結束的地方有一串 base64 編碼的字串
>
>2. 拿去 Cyberchef 解碼就能得到解答
<!-- NISRA{w0w_Y0u_F1nD_Th3_C0mm3nT} -->
### 0x3 - sicc
>1. 將兩張圖下載下來後拿去 Stegsolve 進行 Analyse -> Image Combiner 進行分析, XOR 的情況下會得到一張 QR code
>2. 拿去線上解析(cyberchef 也可以) 或是拿手機掃就能夠得到 flag
<!-- NISRA{Y0u_G0t_m3} -->
### 0x4 - sicc
>在 0x3_B.png 中,發現 R G B 的 0 bit 左上角都有不明的黑色
>利用 analyse -> Data Extract ,選擇 LSB 與 RGB 是 0
<!-- NISRA{10v3_10v3_NISRA_F03v3R} -->
### 0x5 - sicc
>1. 根據 `encrypt3.py` 我們知道它是由 png 跟一個 key 經過 xor 生成 crack.png
>2. png 的標頭組成是固定的,所以我們將隨便一張 png 作為 flag.png 與 crack.png 進行 xor 就能夠得到 flag
>原理: A~.png~ xor B~.txt~ = C~.png~
>B~.txt~ = A~.png~ xor C~.png~ (兩邊同時 xor A~.png~)
:::spoiler 自寫 pyhton
```python=
# XOR 函式
def xor(bt1, bt2):
res = [0]*16
for i in range(len(res)):
q = bt1[i]
d = bt2[i]
k = q ^ d
res[i] = k
return bytes(res)
def add_pad(msg):
l = 16 - len(msg) % 16
msg += bytes([l] * l)
return msg
# 讀取 ox3_B.png 作為 A.png
with open('0x3_B.png', 'rb') as f:
data = f.read()
data = add_pad(data)
# 讀取 carck.png 作為 C.png
with open('crack.png', 'rb') as f:
key = f.read()
enc_data = bytearray()
for i in range(0, len(data), 16):
enc = xor(data[i:i+16], key[:16])
enc_data.extend(enc)
# 輸出 a.txt = flag
with open('a.txt', 'wb') as f:
f.write(enc_data)
```
:::
<!-- NISRA{3Nl1GHt3N} -->
### 0x6 - sicc
>1. 解題辦法與上題類似,只需要將腳本修改一下
:::spoiler 改過的腳本
```python=
def xor(bt1, bt2):
res = [0]*16
for i in range(len(res)):
q = bt1[i]
d = bt2[i]
k = q ^ d
res[i] = k
return bytes(res)
def add_pad(msg):
l = 16 - len(msg) % 16
msg += bytes([l] * l)
return msg
with open('crack.png', 'rb') as f:
data = f.read()
data = add_pad(data)
with open('a.txt', 'rb') as f:
key = f.read()
enc_data = bytearray()
for i in range(0, len(data), 16):
enc = xor(data[i:i+16], key[:16])
enc_data.extend(enc)
with open('flag.png', 'wb') as f:
f.write(enc_data)
```
:::
<!-- NISRA{s0_Y0u_r34llY_Kn0w_wH4t_X0r_15} -->
### 0x7 - hA
> 我們只需要知道RGB各個資訊的最小有效位就可以了,但是嵌入的順序是隨機的......
> 所幸我們有sequence.txt,只需要把其中的兩個數字當作x,y值,
> 就可以按照sequence的順序得到flag了
:::spoiler 自寫python
```python=
from PIL import Image
def load_sequence_from_file(filename):
sequence = []
with open(filename, "r") as f:
for line in f:
w, h = map(int, line.strip().split(","))
sequence.append((w, h))
return sequence
def lsbDecodeWithRandom(l, infile, outfile, sequence_file):
img = Image.open(infile)
sequence = load_sequence_from_file(sequence_file)
f = open(outfile, 'wb')
abyte = 0
lenth = l * 8
count = 0
for w, h in sequence:
if count >= lenth:
break
pixel = img.getpixel((w, h))
for i in range(3):
abyte = (abyte << 1) + (int(pixel[i]) & 1)
count += 1
if count % 8 == 0:
f.write(bytes([abyte]))
abyte = 0
if count >= lenth:
break
f.close()
input = '0x7.png'
flag = 'flag.txt'
sequenceFile = 'sequence.txt'
lsbDecodeWithRandom(50,input,flag,sequenceFile)
```
:::
<!-- NISRA{r4nD0m_L5B_53nP41_sk} -->
### 0x8 - hA
> LSB可以寫在音訊中,音訊一般會用16bits去儲存聲音,但我們只需要最小有效位就可以了
:::spoiler 自寫python
```python=
import wave
def extract_message_from_audio(l, infile, outfile):
with wave.open(infile, 'rb') as audio:
frame_bytes = bytearray(audio.readframes(audio.getnframes()))
extracted_bits = [str(frame_bytes[i] & 1) for i in range(l * 8)]
extracted_message = ''.join(chr(int(''.join(extracted_bits[i:i+8]), 2)) for i in range(0, len(extracted_bits), 8))
with open(outfile, 'w') as f:
f.write(extracted_message)
input = '0x8.wav'
flag = 'flag.txt'
extract_message_from_audio(50,input,flag)
```
:::
<!-- NISRA{L5b_w1Th_Mu51c_15_FuN} -->
### 0x9 - hA
> 與上題的題目相似,但是一樣要用sequence.txt去解決嵌入順序隨機的問題
:::spoiler 自寫python
```python=
import wave
def load_sequence_from_file(filename):
sequence = []
with open(filename, "r") as f:
for line in f:
index = int(line.strip())
sequence.append(index)
return sequence
def lsbDecodeWithRandom(l, infile, outfile, sequence_file):
with wave.open(infile, 'rb') as audio:
frame_bytes = bytearray(audio.readframes(audio.getnframes()))
sequence = load_sequence_from_file(sequence_file)
message_length = l * 8
extracted_bits = []
count = 0
for index in sequence:
if count >= message_length:
break
extracted_bits.append(str(frame_bytes[index] & 1))
count += 1
extracted_message = ''.join(chr(int(''.join(extracted_bits[i:i+8]), 2)) for i in range(0, len(extracted_bits), 8))
with open(outfile, "w") as f:
f.write(extracted_message)
input = '0x9.wav'
output = 'flag.txt'
sequence = 'sequence.txt'
lsbDecodeWithRandom(50,input,output,sequence)
```
:::
<!-- NISRA{r4nD0m_L5B_w1Th_Mu51c_15_50_c00L} -->
## Pwn
### HowLong - sicc
>1. 分析 check_status() 發現他對輸入沒有限制,而輸入的 buffer 最大格數為 20,可以對其進行 overflow 去覆蓋 status,使其變為 'Y'
>2. nc 連上之後,選擇 2 進行輸入,輸入一定數量的 Y 就能夠覆蓋

>3. 這時候輸入 1 隨便買一個東西就會噴出 flag
<!-- NISRA{WhY_u_c4n_kn0W_L3N_15_32??!!} -->
### moneyANDskill - sicc
>1. 同上題,只是輸入長度要長一點

>2. 發現錢不夠,想辦法讓錢變多,分析 check_funds 後發現可以藉由輸入負數增加資金

>3.最後再去買下 flag
<!-- NISRA{U_h4V3_MOn3y_4nD_5k1ll!!} -->
### moneyANDskill_rev - sicc
>1. 同上題,只是輸入的時候發現輸太長會直接跳出去,所以要控制好大小

<!-- 因為 sicc 在上面那題偷偷有幫你們把輸入過濾掉,所以只要輸入超過大小即可 -->
>2. 輸入不能輸入負數了,發現只要輸入超過 int 大小的就有一樣的效果

>3. 最後進行購買
<!-- NISRA{FRu17_M4S73r_Y0u_4r3_7H3_bes7_hacker_1n_7h3_SH0P} -->
## Crypto
### bababase64 - sicc
- 一堆 base 64 encode 的句子
- 根據題目的提示可以知道經過了三次
- 解題工具
- [cyberchef](https://gchq.github.io/CyberChef/)
> 在 Recipe 之中塞入 3 個 From Base 64 即可得到解答
<!-- NISRA{B@_bA_B@s364!!!} -->
### ROT - sicc
- 根據題目知道句子是經過 ROT 轉換的
- 解題工具
- [cyberchef](https://gchq.github.io/CyberChef/)
> 在 Recipe 中放入 ROT 13 Brute Force 即可進行暴力硬解
<!-- NISRA{R0T_TT_:DD_XDD} -->
### Password_good - sicc
- Password: 密碼, good: 棒 -> 密碼棒
- 解題工具
- [Scytale Cipher](https://www.a.tools/Tool.php?Id=266)
- 根據不同的解題網站可能解出來的答案會有大小寫
> 已知文句長度為 24 ,所以我們 Decrypt 先測試 6 ,發現即為答案
<!-- NISRA{finalctfisgoodanduserful} -->
<!-- 對 user 是我打錯 但也是為了補字 -->
### 維吉尼亞密碼+社交工程 - sicc
- 根據題目可以知道是維吉尼亞密碼
- 密鑰根據 hint 是在 sicc 身上娃娃的名字
<!-- sicc 為了這隻娃娃花費了很多錢錢還有時間(找了很久),它也是 sicc 去日本的旅伴 -->
- 解題工具
- [cyberchef](https://gchq.github.io/CyberChef/)
> Recipe 中拉取 Vigenère Decode
> 再從 sicc 身上找到 key = Sullyoon
> 最後進行 Decode 就能得到結果
<!-- NISRA{WherEEstHEKeEYEEEEE} -->
## OS
### /?/?/flag.txt - sicc
- 根據題目可以知道 flag.txt 藏在根目錄中其中一個資料中
> 在 srv 資料夾中發現一個 Thereisnoflag 資料夾
> `sudo su root` 變更為 root 權限進入
> `cat flag.txt` 即可得到 flag
<!-- NISRA{L1unx_1S_@_go0D_T0OL} -->
## Web
### ψ(`∇´)ψ - 101
1. 點開題目連結,然後打開F12檢查該網頁的原始碼有沒有線索

2. 看到提示說跟"機器人"有關,聯想到 <b>robots.txt</b>
3. robots.txt會放在網頁的根目錄之下,用找尋檔案路徑的概念,修改網址找到該網頁的robots.txt
4. 將網址修改成```http://chall2.nisra.net:41032/robots.txt```之後,看到其內容如下

5. 將網址修改成```http://chall2.nisra.net:41032/secret.html```,看到寫著flag的連結

6. 點進去就能看到正在瘋狂旋轉的flag
(抓到flag的方法: 可以開F12複製,或是全選複製網頁文字之類的)
### Hello - 101
1. 在輸入框中輸入東西並送出,可以看到下方出現 Hello, XXX
2. 打開 F12,可以發現表單是用 GET 方式傳送,所以參數會出現在網址列
3. 輸入 HTML 標籤,如 `<h1>123</h1>` 會發現標籤會以標題形式呈現在網頁上
4. 點擊 F12 的 Source,看到 hello.js 檔案,發現他使用 `.innerHTML`,所以 HTML 標籤可以直接被貼進 HTML 檔中
5. 在看到 alert.js 檔案中複寫了 alert,會彈出奇怪的 text,很有可能是 flag
6. 但因為 `.innerHTML` 會擋 <script\> 標籤,所以要嘗試其他種方式把 alert() 塞進去,可以透過 `onload`、`onclick` 或是 `onerror`
7. 我們可以在輸入框或是網址列中輸入 `<img src onerror="alert();">`,因為圖片路徑是空的,所以會導致讀取錯誤,觸發 onerror 裡面的 alert,因此得到 flag
### Pick cat! - sicc
1. 點開來發現中間圖片,點選他會進行切換
2. 打開 F12 將他改為不存在的圖片後,並在後面使用 `onerror = alert()` 使網頁發出 alert

<!-- NISRA{X55_1s_50_c0o0o01!} -->
### Invisible - 101
1. 點開題目連結,打開F12找尋可疑的東西
2. 在主頁點開右上角的選單後,再次檢查F12,發現有個文字顏色被設定成跟背景一樣的黑色的顏文字超連結

3. 點按(•̀ω•́)y連結,來到看似一片空白的網頁,但是從F12可以看到有一張QRcode的visibility屬性被設定成hidden,也就是圖片被藏起來了,不可見的意思

4. 可以透過修改visibility屬性,將hidden改成visible,圖片就會顯現出來了
或是把鼠標移到圖片連結上,也能預覽圖片
5. 用手機掃描QRcode得到flag
#### 不需要用手機掃描QRcode的方法
在body標籤裡面的script標籤中,可以明顯看到有一坨用```[]()!+```組合成的東西

這個詭異的東西其實是一種叫做<b>JSFuck</b>的JavaScript的寫法,可以用查詢線上加密解密工具的方式,去Google搜尋JSFuck decode,利用線上工具解密,就能得到這串東西寫了什麼JavaScript的語法
解密前:

解密後,就可以從這串JavaScript複製flag了```var elText = "NISRA{ja^asCr1pt_w1th_qrC0de}";```

### GETGETGET - 101
1. 點開題目連結後,畫面上看到的是用<b>PHP</b>這個後端語言寫的程式碼,它在做的動作是判斷使用者輸入的帳密是否正確
2. 閱讀程式碼可以發現它多次提到<b>GET</b>這個請求方法,也就是說我們現在看到的PHP程式碼,就是前端那邊的表單標籤\<form>的action屬性,所設定的目的地(將使用者輸入的資料傳送到這個PHP檔案做處理)
也可以發現用來儲存使用者輸入的帳號的變數名稱為"user",密碼則是"password",
且最終user必須等於"admin"、password必須等於"ji32k7au4a83",才能通過if判斷式,得到flag訊息
3.知道了帳號密碼各自的變數名稱和正確數值後,可以利用GET方法的特性,修改URL,在網址後方加上"?"再打上帳號密碼的參數和數值,就能在不依靠輸入框的情況下,向後端傳送"使用者輸入的內容"
```http://chall2.nisra.net:41016?user=admin&password=ji32k7au4a83```
在下方文字訊息中得到flag

### 🍪 - 101
1. 透過題目標題的餅乾emoji,聯想到這題應該是跟網頁的cookie有關
2. 點開題目連結,打開F12,找到Application的分頁,點進去後在左側欄位找到Cookies
點進去就能看到名為Flag的cookie,其值為```NISRA%7By0u_F1nd_53t_C0ok135%7D```

補充: flag中的```%7B```跟```%7D```是左右大括號{}各自在<b>URL編碼</b>後的結果,URL編碼的特徵就是有百分比的符號%為開頭,可以按下畫面下方的"Show URL-decoded"的框框,讓網頁自動解碼得到正確的flag格式```NISRA{y0u_F1nd_53t_C0ok135}```

#### 讓網頁噴cookie的另一種方法
1. 在JavaScript中,可用```document.cookie```讀取cookie,搭配alert()或console.log()進行輸出
2. 去到F12的Console分頁,在這裡打上JavaScript指令,得到flag(但記得"{}"要進行URL解碼才是正確的flag格式)
```alert(document.cookie)```


或 ```console.log(document.cookie)```
