---
# System prepended metadata

title: Final CTF
tags: [2024 NISRA Enlightened]

---

# 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` 
![image](https://hackmd.io/_uploads/HJ6wl-LnR.png)

>2. 然後將圖片高度改為 `04 F4` 就能夠找到 flag 了

<!-- NISRA{JP3G_15_FuN} -->

### 0x2 - sicc
>1. 看到結束的地方有一串 base64 編碼的字串
>![image](https://hackmd.io/_uploads/H1Mv7-8nC.png)

>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 就能夠覆蓋
![image](https://hackmd.io/_uploads/HJ5awrUhR.png)
>3. 這時候輸入 1 隨便買一個東西就會噴出 flag 
<!-- NISRA{WhY_u_c4n_kn0W_L3N_15_32??!!} -->

### moneyANDskill - sicc
>1. 同上題，只是輸入長度要長一點
![image](https://hackmd.io/_uploads/B1V0OSLn0.png)

>2. 發現錢不夠，想辦法讓錢變多，分析 check_funds 後發現可以藉由輸入負數增加資金
![image](https://hackmd.io/_uploads/SyYxtrUhC.png)

>3.最後再去買下 flag
<!-- NISRA{U_h4V3_MOn3y_4nD_5k1ll!!} -->

### moneyANDskill_rev - sicc
>1. 同上題，只是輸入的時候發現輸太長會直接跳出去，所以要控制好大小
![image](https://hackmd.io/_uploads/H1V7nB82C.png)
<!-- 因為 sicc 在上面那題偷偷有幫你們把輸入過濾掉，所以只要輸入超過大小即可 -->
>2. 輸入不能輸入負數了，發現只要輸入超過 int 大小的就有一樣的效果
![image](https://hackmd.io/_uploads/BkpR3SUnR.png)
>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檢查該網頁的原始碼有沒有線索
![image](https://hackmd.io/_uploads/SyiKDfRj0.png)
2. 看到提示說跟"機器人"有關，聯想到 <b>robots.txt</b>
3. robots.txt會放在網頁的根目錄之下，用找尋檔案路徑的概念，修改網址找到該網頁的robots.txt
4. 將網址修改成```http://chall2.nisra.net:41032/robots.txt```之後，看到其內容如下
![image](https://hackmd.io/_uploads/r1Oc_f0s0.png)
5. 將網址修改成```http://chall2.nisra.net:41032/secret.html```，看到寫著flag的連結
![image](https://hackmd.io/_uploads/SyOHo9k3R.png)
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
![image](https://hackmd.io/_uploads/ryMhAB160.png)
<!-- NISRA{X55_1s_50_c0o0o01!} -->


### Invisible - 101
1. 點開題目連結，打開F12找尋可疑的東西
2. 在主頁點開右上角的選單後，再次檢查F12，發現有個文字顏色被設定成跟背景一樣的黑色的顏文字超連結
![image](https://hackmd.io/_uploads/SygUEik2R.png)
3. 點按(•̀ω•́)y連結，來到看似一片空白的網頁，但是從F12可以看到有一張QRcode的visibility屬性被設定成hidden，也就是圖片被藏起來了，不可見的意思
![image](https://hackmd.io/_uploads/B1TRPjk2A.png)
4. 可以透過修改visibility屬性，將hidden改成visible，圖片就會顯現出來了
或是把鼠標移到圖片連結上，也能預覽圖片![image](https://hackmd.io/_uploads/BJlWKiJhR.png)
5. 用手機掃描QRcode得到flag

#### 不需要用手機掃描QRcode的方法
在body標籤裡面的script標籤中，可以明顯看到有一坨用```[]()!+```組合成的東西
![image](https://hackmd.io/_uploads/ByzAFjk2A.png)
這個詭異的東西其實是一種叫做<b>JSFuck</b>的JavaScript的寫法，可以用查詢線上加密解密工具的方式，去Google搜尋JSFuck decode，利用線上工具解密，就能得到這串東西寫了什麼JavaScript的語法

解密前:
![image](https://hackmd.io/_uploads/B1Hd2okhC.png)
解密後，就可以從這串JavaScript複製flag了```var elText = "NISRA{ja^asCr1pt_w1th_qrC0de}";```
![image](https://hackmd.io/_uploads/H1Bj2o12A.png)

### GETGETGET - 101
1. 點開題目連結後，畫面上看到的是用<b>PHP</b>這個後端語言寫的程式碼，它在做的動作是判斷使用者輸入的帳密是否正確
2. 閱讀程式碼可以發現它多次提到<b>GET</b>這個請求方法，也就是說我們現在看到的PHP程式碼，就是前端那邊的表單標籤\<form>的action屬性，所設定的目的地(將使用者輸入的資料傳送到這個PHP檔案做處理)
    也可以發現用來儲存使用者輸入的帳號的變數名稱為"user"，密碼則是"password"，![image](https://hackmd.io/_uploads/HyP4lnynA.png)
且最終user必須等於"admin"、password必須等於"ji32k7au4a83"，才能通過if判斷式，得到flag訊息![image](https://hackmd.io/_uploads/HyBBg3kh0.png)
3.知道了帳號密碼各自的變數名稱和正確數值後，可以利用GET方法的特性，修改URL，在網址後方加上"?"再打上帳號密碼的參數和數值，就能在不依靠輸入框的情況下，向後端傳送"使用者輸入的內容"
```http://chall2.nisra.net:41016?user=admin&password=ji32k7au4a83```
在下方文字訊息中得到flag
![image](https://hackmd.io/_uploads/SyA6W2kh0.png)
    
### 🍪 - 101
1. 透過題目標題的餅乾emoji，聯想到這題應該是跟網頁的cookie有關
2. 點開題目連結，打開F12，找到Application的分頁，點進去後在左側欄位找到Cookies
點進去就能看到名為Flag的cookie，其值為```NISRA%7By0u_F1nd_53t_C0ok135%7D```
    ![image](https://hackmd.io/_uploads/S13gSh1n0.png)
補充: flag中的```%7B```跟```%7D```是左右大括號{}各自在<b>URL編碼</b>後的結果，URL編碼的特徵就是有百分比的符號%為開頭，可以按下畫面下方的"Show URL-decoded"的框框，讓網頁自動解碼得到正確的flag格式```NISRA{y0u_F1nd_53t_C0ok135}```
![image](https://hackmd.io/_uploads/r1VKLny20.png)

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