php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #78322 Integer overflow in mb_strpos allows to bypass security related checks
Submitted: 2019-07-22 19:24 UTC Modified: 2019-07-22 20:02 UTC
From: contact at scannell-infosec dot net Assigned: nikic (profile)
Status: Closed Package: mbstring related
PHP Version: 7.2.20 OS: N/A
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: contact at scannell-infosec dot net
New email:
PHP Version: OS:

 

 [2019-07-22 19:24 UTC] contact at scannell-infosec dot net
Description:
------------
The PHP function mb_strpos returns the index of the needle within the haystack.

PHP_FUNCTION(mb_strpos)
{
...
n = mbfl_strpos(&haystack, &needle, offset, reverse);
	if (n >= 0) {
		RETVAL_LONG(n);
	} else {
		switch (-n) {
		case 1:
			break;
		case 2:
			php_error_docref(NULL, E_WARNING, "Needle has not positive length");
			break;
		case 4:
			php_error_docref(NULL, E_WARNING, "Unknown encoding or conversion error");
			break;
		case 8:
			php_error_docref(NULL, E_NOTICE, "Argument is empty");
			break;
		default:
			php_error_docref(NULL, E_WARNING, "Unknown error in mb_strpos");
			int(1073741824)
PHP Warning:  mb_strpos(): Unknown error in mb_strpos in /tmp/testscript.php on line 4
bool(false)
break;
		}
		RETVAL_FALSE;
	}

depending on the return value of mbfl_strpos, either the index is returned if it positive or if it is negative, false is returned.

It is possible to force mb_strpos to always return false. This is due to an integer overflow in mbfl_strpos.

int
mbfl_strpos(
    mbfl_string *haystack,
    mbfl_string *needle,
    int offset,
    int reverse)
{
	int result;

This is because the resulting index is stored in a signed 32 bit integer. If the index of the needle is outside of the range 2^31, the result is negative, thus false is returned.

This makes it possible to hide dangerous values that are searched for with mb_strpos, eg. PHP code or invalid characters



Test script:
---------------
<?php

ini_set("memory_limit", -1);

var_dump(mb_strpos(str_repeat('A', pow(2, 30)) . 'B', 'B'));
var_dump(mb_strpos(str_repeat('A', pow(2, 31)) . 'B', 'B'));


Expected result:
----------------
int(1073741824)
int(2147483649)

Actual result:
--------------
int(1073741824)
PHP Warning:  mb_strpos(): Unknown error in mb_strpos in /tmp/testscript.php on line 4
bool(false)


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2019-07-22 19:48 UTC] stas@php.net
-Type: Security +Type: Bug
 [2019-07-22 19:48 UTC] stas@php.net
Looks like a contrived example, requiring special settings to reproduce. Per https://wiki.php.net/security not a security issue.
 [2019-07-22 20:02 UTC] nikic@php.net
-Status: Open +Status: Closed -Assigned To: +Assigned To: nikic
 [2019-07-22 20:02 UTC] nikic@php.net
This is already fixed in PHP 7.3, where libmbfl was migrated to use size_t.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Dec 27 04:01:29 2024 UTC