Artificial truth

The more you see, the less you believe.

[archives] [latest] | [homepage] | [atom/rss]

A few (lame) post-compromission Suhosin workarounds
Sat 15 July 2017 — download

Suhosin is breaking a lot of webshells, including meterpreter (this is now fixed ;) ), but it's possible to walk around it in certain configurations. I haven't found a self-contained way to bypass suhosin's functions blacklist, even by messing pretty hard with classes/namespaces, but most of the time, you can just abuse Apache's CGI handling anyway.

Priority-base bypass

Suhosin is using the filename of the executed file to get the codetype of the current execution (see execute.c):

static int suhosin_detect_codetype(zend_op_array *op_array TSRMLS_DC)
{
    char *s;
    int r;

    s = (char *)op_array->filename;

    /* eval, assert, create_function, preg_replace  */
    if (op_array->type == ZEND_EVAL_CODE) {
        if (s == NULL) {
            return SUHOSIN_CODE_TYPE_UNKNOWN;
        }

        if (strstr(s, "eval()'d code") != NULL) {
            return SUHOSIN_CODE_TYPE_EVAL;
        }

        if (strstr(s, "regexp code") != NULL) {
            return SUHOSIN_CODE_TYPE_REGEXP;
        }

        if (strstr(s, "mbregex replace") != NULL) {
            return SUHOSIN_CODE_TYPE_MBREGEXP;
        }

        if (strstr(s, "assert code") != NULL) {
            return SUHOSIN_CODE_TYPE_ASSERT;
        }

        if (strstr(s, "runtime-created function") != NULL) {
            return SUHOSIN_CODE_TYPE_CFUNC;
        }

        if (strstr(s, "Command line code") != NULL) {
            return SUHOSIN_CODE_TYPE_COMMANDLINE;
        }

        if (strstr(s, "Command line begin code") != NULL) {
            return SUHOSIN_CODE_TYPE_COMMANDLINE;
        }

        if (strstr(s, "Command line run code") != NULL) {
            return SUHOSIN_CODE_TYPE_COMMANDLINE;
        }

        if (strstr(s, "Command line end code") != NULL) {
            return SUHOSIN_CODE_TYPE_COMMANDLINE;
        }

        if (strstr(s, "suhosin internal code") != NULL) {
            return SUHOSIN_CODE_TYPE_SUHOSIN;
        }
    } else {
        r = suhosin_check_filename(s, strlen(s) TSRMLS_CC);
        return r;
    }

    return SUHOSIN_CODE_TYPE_UNKNOWN;
}

This means that if the feature that you're trying to bypass has its codetype placed bellow a non-blacklisted one, you can simply rename your file to bypass it.

For example, if the e flag for preg_replace is disabled, you can still use it by putting the string eval()'d code in your file name.

$ cat eval\(\)\'d\ code.php 
<?php var_dump(preg_replace('/[a-z]/e', "strtoupper('\\0')", "capslock"));  
$ php eval\(\)\'d\ code.php 
string(5) "CAPSLOCK"
$

Alternatives to eval

Suhosin can block eval, but there are many ways to emulate it

  • assert, as said in the documentation, if its parameter is a string, it'll be evaluated as PHP code. A minor caveat is that not everything can be executed this way, for example echo will fail.
  • create_function, with something like create_function('', '}phpinfo();//'); (yes, it's working, thanks to kpcyrd for the reminder ♥).
  • mb_ereg_replace is explicitly ignored by suhosin, while providing the same primitives than preg_replace.