
PoC & Exploit for CVE-2025-32023 / PlaidCTF 2025 "Zerodeo"
PoC & Exploit pour CVE-2025-32023 (GHSA-rp2m-q4j6-gr43) / PlaidCTF 2025 "Zerodeo"
Testé avec redis:7.4.2-alpine3.21@sha256:02419de7eddf55aa5bcf49efb74e88fa8d931b4d77c07eff8a6b2144472b6952
Affecte les versions de Redis >= 2.8. Corrigé dans les versions 8.0.3, 7.4.5, 7.2.10, 6.2.19, voir redis/redis@5018874.
HyperLogLog dans Redis n'est qu'une chaîne de caractères avec ses propres encodages personnalisés. L'itération sur un encodage HLL sparse nécessite l'addition des longueurs de runs de chaque représentation sparse, ce qui peut faire déborder la longueur totale comptée dans int i vers une valeur négative lorsqu'elle est manipulée sur un HLL malformé. Cela permet à un attaquant d'écrire à des offsets négatifs sur la structure HLL, conduisant à une écriture hors limites sur la pile/tas selon l'origine de la structure HLL (par exemple, hllMerge() prend une structure allouée sur la pile, hllSparseToDense() prend une structure allouée sur le tas).
Voir l'extrait du correctif ci-dessous :
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;
}
L'exploit est un pwnable Redis standard :