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
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
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

Add a Patch

Pull Requests

Add a Pull Request

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: Tue Apr 16 11:01:29 2024 UTC