PoC & Exploit for CVE-2025-32023 / PlaidCTF 2025 "Zerodeo"# CVE-2025-32023
PoC & Exploit for CVE-2025-32023 ([GHSA-rp2m-q4j6-gr43](https://github.com/redis/redis/security/advisories/GHSA-rp2m-q4j6-gr43)) / PlaidCTF 2025 "Zerodeo"
### Repro / Patch
Tested against `redis:7.4.2-alpine3.21@sha256:02419de7eddf55aa5bcf49efb74e88fa8d931b4d77c07eff8a6b2144472b6952`
Affects Redis versions >= 2.8. Patched on 8.0.3, 7.4.5, 7.2.10, 6.2.19, see [redis/redis@5018874](https://github.com/redis/redis/commit/50188747cbfe43528d2719399a2a3c9599169445).
### Bug
HyperLogLog in Redis is just another string with its own custom encodings. Iterating over a sparse HLL encoding requires adding up run lengths of each sparse representation, which may overflow the total length counted in `int i` into a negative value when operated on a malformed HLL. This allows an attacker to overwrite to negative offsets on the HLL structure, leading to out-of-bounds write on the stack/heap depending on where the HLL structure is from (e.g. `hllMerge()` takes a stack-allocated one, `hllSparseToDense()` takes a heap-allocated one).
See the patch snippet below:
```diff
int hllMerge(uint8_t *max, robj *hll) {
struct hllhdr *hdr = hll->ptr;
int i;
if (hdr->encoding == HLL_DENSE) {
hllMergeDense(max, hdr->registers);
} else {
uint8_t *p = hll->ptr, *end = p + sdslen(hll->ptr);
long runlen, regval;
+ int valid = 1;
p += HLL_HDR_SIZE;
i = 0;
while(p < end) {
if (HLL_SPARSE_IS_ZERO(p)) {
runlen = HLL_SPARSE_ZERO_LEN(p);
+ if ((runlen + i) > HLL_REGISTERS) { /* Overflow. */
+ valid = 0;
+ break;
+ }
i += runlen;
p++;
} else if (HLL_SPARSE_IS_XZERO(p)) {
runlen = HLL_SPARSE_XZERO_LEN(p);
+ if ((runlen + i) > HLL_REGISTERS) { /* Overflow. */
+ valid = 0;
+ break;
+ }
i += runlen;
p += 2;
} else {
runlen = HLL_SPARSE_VAL_LEN(p);
regval = HLL_SPARSE_VAL_VALUE(p);
- if ((runlen + i) > HLL_REGISTERS) break; /* Overflow. */
+ if ((runlen + i) > HLL_REGISTERS) { /* Overflow. */
+ valid = 0;
+ break;
+ }
while(runlen--) {
if (regval > max[i]) max[i] = regval;
i++;
}
p++;
}
}
- if (i != HLL_REGISTERS) return C_ERR;
+ if (!valid || i != HLL_REGISTERS) return C_ERR;
}
return C_OK;
}
```
### Exploit
Exploit is standard Redis pwnables:
1. Corrupt an sds object on the jemalloc heap to make its length large
2. Spray embstr objects to corrupt into a fake module object
3. Dump the heap using the corrupted sds object to find target embstr object & leak addresses
4. Create a fake module object on the target embstr object
5. Delete the fake module object, triggering destructor & gaining RCE
[4.0K] /data/pocs/535d1a079c78ce5e3a8fe689dc0400ac45136071
├── [ 561] poc.py
├── [3.1K] README.md
└── [4.9K] solver-f0b22e429fa6c984f39a409744ff954d3a45d843edd29428ef3a68085d696a7d.py
0 directories, 3 files