localhost
Random ramblings from someone working in InfoSec

contact me@localhost.re
archive - rss
2013/10/18

Do you know why PHP's Register Globals have been REMOVED (don't worry, WHMCS developers implemented the exact same function, in their own little way) in recent versions? Let me show you.

Same file, same type of stupid programming.

/includes/dbfunctions.php:

<?php
function select_query($table, $fields, $where, $orderby = '', $orderbyorder = '', $limit = '', $innerjoin = '') {
    global $CONFIG;
    global $query_count;
    global $mysql_errors;
    global $whmcsmysql;
    if (!$fields) {
    $fields = '*';
    }
    $query = 'SELECT ' . $fields . ' FROM ' . db_make_safe_field($table);
    if ($innerjoin) {
    $query .= ' INNER JOIN ' . db_escape_string($innerjoin);
    }
    if ($where) {
    if (is_array($where)) {
        $criteria = array();
        foreach ($where as $origkey => $value) {
        $key = db_make_safe_field($origkey);
        if (is_array($value)) {
            if ($key == 'default') {
            $key = '`default`';
            }
            if ($value['sqltype'] == 'LIKE') {
            $criteria[] = $key . ' LIKE \'%' . db_escape_string($value['value']) . '%\'';
            continue;
            }
            if ($value['sqltype'] == 'NEQ') {
            $criteria[] = $key . '!=\'' . db_escape_string($value['value']) . '\'';
            continue;
            }
            if ($value['sqltype'] == '>') {
            $criteria[] = $key . '>' . db_escape_string($value['value']);
            continue;
            }
            if ($value['sqltype'] == '<') {
            $criteria[] = $key . '<' . db_escape_string($value['value']);
            continue;
            }
            if ($value['sqltype'] == '<=') {
            $criteria[] = $origkey . '<=' . db_escape_string($value['value']);
            continue;
            }
            if ($value['sqltype'] == '>=') {
            $criteria[] = $origkey . '>=' . db_escape_string($value['value']);
            continue;
            }
            if ($value['sqltype'] == 'TABLEJOIN') {
            $criteria[] = $key . '=' . db_escape_string($value['value']);
            continue;
            }
            if ($value['sqltype'] == 'IN') {
            $criteria[] = $key . ' IN (\'' . implode('\',\'', db_escape_array($value['values'])) . '\')';
            continue;
            }
            continue;
        }
[...]
?>

This is their special select_query() function that gets used whenever they need to do a MySQL SELECT. The funny thing is that we can manipulate the GET/POST variables and end up with something like $key = array('sqltype' => 'TABLEJOIN', 'value' = '[SQLI]');

no, whmcs, no!

That's right. Register globals are awesome.

This opens up a lot of other holes, for example we can write to /configuration.php whatever we want (PHP code included)

Eeeeeeexploit time:

  • Edit the whmcs2.py exploit
  • rub hands and watch how the WHMCS team takes days to patch this!

Have fun!