[php-src] Issue #24640: ext/dba: tcadb backend path allows injecting Tokyo Cabinet connection-string parameters (e.g. bnum) via unescaped '#'
| From: | kunwaratulhax0r | Date: | Sat, 24 Oct 2026 12:27:32 +0000 |
| Subject: | [php-src] Issue #24640: ext/dba: tcadb backend path allows injecting Tokyo Cabinet connection-string parameters (e.g. bnum) via unescaped '#' | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-253156@lists.php.net to get a copy of this message | ||
Issue: https://github.com/php/php-src/issues/24640
Author: kunwaratulhax0r
ext/dba's tcadb backend concatenates the dba_open() path directly
into Tokyo Cabinet's #key=value connection-string syntax with no escaping
(ext/dba/dba_tcadb.c):
```c
spprintf(&path_string, 0, "%s#mode=r", ZSTR_VAL(info->path));
...
tcadbopen(tcadb, path_string);
```
If the path an application passes to dba_open() contains an unescaped #
(e.g. built from a user-influenceable component), an attacker can inject additional Tokyo Cabinet
tuning parameters into the open call.
**Confirmed, live PoC** (--enable-dba --with-tcadb, libtokyocabinet-dev
1.4.48):
```php
<?php
$db1 = dba_open('/tmp/test_normal.tcb', 'n', 'tcadb');
dba_close($db1);
// filesize('/tmp/test_normal.tcb') === 135680
$db2 = dba_open('/tmp/test_injected.tcb#bnum=100000000', 'n',
'tcadb');
dba_close($db2);
// filesize('/tmp/test_injected.tcb') === 402658048 (~384MB, ~3000x larger)
```
```
-rw-r--r-- 1 root root 402658048 Oct 23 15:32 /tmp/test_injected.tcb
-rw-r--r-- 1 root root 135680 Oct 23 15:32 /tmp/test_normal.tcb
```
**Mechanism:** on a create-mode open, PHP's own generic dispatcher (ext/dba/dba.c,
php_stream_open_wrapper() on the literal path) creates a harmless 0-byte placeholder
file at the literal injected name. Separately, Tokyo Cabinet's own tcadbopen()
parses the string itself — splits on #, takes everything before the first
# as the real path (a different file than the one PHP's generic layer touched),
and applies every subsequent #key=value segment as a tuning parameter, including the
injected bnum.
I also tested whether this could override the intended open mode (inject #mode=wct
ahead of PHP's appended #mode=r for a DBA_READER open) — that
specific path does **not** work: PHP's generic php_stream_open_wrapper() pre-check
requires the literal full path (injected #... included) to already exist for
reader-mode opens, so the call fails before reaching the tcadb-specific handler at all. The
confirmed impact above is specific to create-mode opens.
**Suggested fix:** reject or escape # in the path before use in
DBA_OPEN_FUNC(tcadb), same spirit as validating the path in other dba
backends.