<<< Date Index >>>     <<< Thread Index >>>

Maksymilian Arciemowicz



Trust unworthy variables in PHP

by SecurityReason.Com
Maksymilian Arciemowicz
max [at] jestsuper [dot] pl 
cxib [at] securityreason [dot] com
http://securityreason.com/key/Arciemowicz.Maksymilian.gpg

Recently, I have published a simple 'Full Path Disclosure and SQL Errors' bug, 
which has presented lack of knowledge or secuirty of many programmists. All in 
all, Full Path Disclosure is something not dangerous, but is also an error. The 
majority of people tries to protect their PHP scripts, but they do not know 
everything.

I had a dilemma with publishing the 'Full Path Disclosure and SQL Errors' 
phpBB's bug  - it is not harmful, but should not exist. We can easily find such 
errors (phpMyAdmin, PostNuke, PHP-Nuke and many others). Even official PHP 
website contains this bug.

Example:

http://php.net/?lang[]=BMS

- Result ---
Warning: setcookie() expects parameter 2 to be string, array given in 
/home/local/Web/sites/www.php.net/include/site.inc on line 155
- Result ---

Let us see this code:
http://php.net/include/site.inc

--- Code ---
function mirror_setcookie($name, $content, $exptime)
{
    if (!headers_sent()) {
        if (is_official_mirror()) {
            return setcookie($name, $content, time() + $exptime, '/', 
'.php.net');
        } else {
            return setcookie($name, $content, time() + $exptime, '/');
        }
    } else {
        return FALSE;
    }
}
--- Code ---

By using setcookie() we are obligated to use it in accordance with 
documentation:
http://pl2.php.net/manual/en/function.setcookie.php

bool setcookie ( string name [, string value [, int expire [, string path [, 
string domain [, bool secure]]]]] )

So, we have to obey the rules. We have to use string (lang=PL) and nothing else 
(for example: array lang[]=cx).
PHP checks what was in the majority of functions declared.

- setcookie() code ---
PHP_FUNCTION(setcookie)
{
char *name, *value = NULL, *path = NULL, *domain = NULL;
long expires = 0;
zend_bool secure = 0;
int name_len, value_len, path_len, domain_len;

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|slssb", &name,
  &name_len, &value, &value_len, &expires, &path,
  &path_len, &domain, &domain_len, &secure) == FAILURE) {
return;
}
...
- setcookie() code ---

As you can see there is no array(), so this function returns an error causing 
Full Path Disclousure.

There are also many other functions, which structure is similar to this 
(htmlspecialchars is probably one of most popular).
Many people would say that this script:

<? echo htmlspecialchars($_GET['x']); ?>

is secure. And they are in big mistake, because - let us see the documentation 
and find:

string htmlspecialchars ( string string [, int quote_style [, string charset]] )

_GET['x'] could be a string but it does not have to. If it is an array it 
causes Path Disclosure.

But there are many things which are not mentioned in PHP documentation. For 
example: function parse_url(). Recently, I have seen its implementation in some 
script and autor seemed not to make a mistake but he did. Let us see and 
analyze how parse_url() works

http://pl2.php.net/manual/en/function.parse-url.php

array parse_url ( string url )

There must be a string for the input.

---
Return Values

On seriously malformed URLs, parse_url() may return FALSE and emit a E_WARNING. 
Otherwise an associative array is returned, whose components may be (at least 
one):

    *  scheme - e.g. http
    *  host
    *  port
    *  user
    *  pass
    * path
    * query - after the question mark ?
    * fragment - after the hashmark 
---

Let us say we have this script:

---
<?
$formatuj='';
if(is_string($_GET['url'])){
$formatuj=parse_url($_GET['url']);
}
?>
---

Somebody will say that it will not cause any problems. In fact, in 
documentation there is no information that port cannot be higher than 5 digit 
number. Let us check it:

?url=http://securityreason.com:000080/

- Result ---
Warning: parse_url(http://securityreason.com:000080/) [function.parse-url]: 
Unable to parse url in /www/hig.php on line 4
- Result ---

Using @ before parse_url() would be the solution, but it is not a difficult to 
hide our bugs, because in this case the variable $formatuj is empty and 
_GET['url'] exists as string.

Disabling errors would be the global solution, but it will not change the fact 
that script should be resistant to these attacks.
We can play with PHP functions in many ways. We can even try to find XSS etc, 
but it requires to put quite a lot effort in this game.

The solution is to analyze all input variables and all checkings (for example: 
is_string). Error displaying could be disabled, but I would rather suggest 
correcting the code.

- Example Solusion for phpBB 2.0.20 ---
File: memberlist.php
-line 38-45-
if ( isset($HTTP_GET_VARS['mode']) || isset($HTTP_POST_VARS['mode']) )
{
        $mode = ( isset($HTTP_POST_VARS['mode']) ) ? 
htmlspecialchars($HTTP_POST_VARS['mode']) : 
htmlspecialchars($HTTP_GET_VARS['mode']);
}
else
{
        $mode = 'joined';
}
-line 38-45-

Replace to:

- fix -
if ( (isset($HTTP_GET_VARS['mode']) || isset($HTTP_POST_VARS['mode'])) && 
(is_string($HTTP_GET_VARS['mode']) || is_string($HTTP_POST_VARS['mode'])) )
{
        $mode = ( isset($HTTP_POST_VARS['mode']) ) ? 
htmlspecialchars($HTTP_POST_VARS['mode']) : 
htmlspecialchars($HTTP_GET_VARS['mode']);
}
else
{
        $mode = 'joined';
}
- fix -
- Example Solusion for phpBB 2.0.20 ---


SecurityReason.Com 2006.05.06