想复现命令执行的漏洞,情不知所起,来漏洞库看看。就这个哥们了。开始下载安装。
点击进去发现,命令执行,,,还是tp5。不就是那个RCE吗。。来来,上个payload看效果。
那就带大家看看代码流程图吧。首先分析下这个payload,\think\app这个是类文件,invokefunction这个是方法名,后面的都是参数。打断点发请求,过程如下图所示:
图中的这段代码,反射的高级用法,触发函数调用。也就是call_user_func_array这个方法,此方法调用说明如下:
/**
* Call a user function given with an array of parameters
* @link https://php.net/manual/en/function.call-user-func-array.php
* @param callback $function <p>
* The function to be called.
* </p>
* @param array $param_arr <p>
* The parameters to be passed to the function, as an indexed array.
* </p>
* @return mixed the function result, or false on error.
* @since 4.0.4
* @since 5.0
*/function call_user_func_array ($function, array $param_arr) {}
第一个参数传入的system方法。这个方法调用说明如下:
/**
* Execute an external program and display the output
* @link https://php.net/manual/en/function.system.php
* @param string $command <p>
* The command that will be executed.
* </p>
* @param int $return_var [optional] <p>
* If the return_var argument is present, then the
* return status of the executed command will be written to this
* variable.
* </p>
* @return string|false the last line of the command output on success, and false
* on failure.
* @since 4.0
* @since 5.0
*/function system ($command, &$return_var = null) {}
接着给system传入的系统命令whoami/ls作为验证判断条件,在验证环节,就出来了计算机当前用户。
代码看着贼简单,但是要明白里面的访问方式,这个就得聊聊了。同时也会明白为什么会产生这样的漏洞。
在tp里,如果你访问了一个不存在的类文件时,系统会帮助用户搜索加载,通过这个spl_autoload_register函数。如下代码:
// 注册自动加载机制
public static function register($autoload = '')
{ // 注册系统自动加载
spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true); // 注册命名空间定义
self::addNamespace([ 'think' => LIB_PATH . 'think' . DS, 'behavior' => LIB_PATH . 'behavior' . DS, 'traits' => LIB_PATH . 'traits' . DS,
]); // 加载类库映射文件
if (is_file(RUNTIME_PATH . 'classmap' . EXT)) { self::addClassMap(__include_file(RUNTIME_PATH . 'classmap' . EXT));
} // Composer自动加载支持
if (is_dir(VENDOR_PATH . 'composer')) { self::registerComposerLoader();
} // 自动加载extend目录
self::$fallbackDirsPsr4[] = rtrim(EXTEND_PATH, DS);
}
从过控制器和方法名的转换,解析url中的参数,最终实例出APP的类,调用invokefunction方法。
其中Loader::parseName($controller, 1)方法,这个可以控制这种直接传递命名空间的参数,
/**
* 字符串命名风格转换
* type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格
* @param string $name 字符串
* @param integer $type 转换类型
* @param bool $ucfirst 首字母是否大写(驼峰规则)
* @return string
*/
public static function parseName($name, $type = 0, $ucfirst = true)
{ if ($type) {
$name = preg_replace_callback('/_([a-zA-Z])/', function ($match) { return strtoupper($match[1]);
}, $name); return $ucfirst ? ucfirst($name) : lcfirst($name);
} else { return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
}
}
方法中只匹配了/_([a-zA-Z])/的正则,反斜线过滤掉,就可以防止这种rce的执行。看一下官方的处理方式:
它这种就是检验这种直接传递命名空间违规的方式,给过滤掉。加载位置:
再请求出现了正确的拦截:
这里编号CNVD-2021-65112漏洞复现完成。
记录几个RCE的涉及函数吧,积累后续挖掘的突破口,希望大家来补充!
array_walk_recursive
call_user_func
call_user_func_array
array_map()
preg_replace
array_filter()
还有很多,,,常遇到频率比较高的应该是前四个,没有具体统计。感兴趣的小伙伴,可以关注TIDE安全团队,看下
代码审计wiki,代码执行漏洞篇
http://wiki.tidesec.com/docs/codeaudit/3d336f68c2686b738d86cd65e479173f
E
N
D
关
于
我
们
Tide安全团队正式成立于2019年1月,是新潮信息旗下以互联网攻防技术研究为目标的安全团队,团队致力于分享高质量原创文章、开源安全工具、交流安全技术,研究方向覆盖网络攻防、系统安全、Web安全、移动终端、安全开发、物联网/工控安全/AI安全等多个领域。
团队作为“省级等保关键技术实验室”先后与哈工大、齐鲁银行、聊城大学、交通学院等多个高校名企建立联合技术实验室。团队公众号自创建以来,共发布原创文章400余篇,自研平台达到31个,目有18个平台已开源。此外积极参加各类线上、线下CTF比赛并取得了优异的成绩。如有对安全行业感兴趣的小伙伴可以踊跃加入或关注我们。