当前位置:网站首页>PHP security: the past and present of variables

PHP security: the past and present of variables

2020-11-07 17:19:00 PHP open source community

Abstract

Variable security is PHP An important part of security , In this paper, a systematic analysis of a variable is given “ The journey of life ” What are the security issues in . Variable life path : Pass in the parameter → Variable generation → Variable handling -> Variable storage .

Part1 Pass in the parameter

Pass through is a front desk pass GET perhaps POST Method to transfer parameters , Here we often encounter URL-WAF Safety judgment of .URL-WAF Of a request URL The function of judging by a series of regular matches .

1、 The use of reference is abnormal HTTP Method , quite a lot WAF Check only POST perhaps GET Method

ABCDEFG /lab_value/get.php?num_value=hhh HTTP/1.1

GET /lab_value/get.php?num_value=hhh HTTP/1.1

The above two are equivalent , fill HTTP Methods can be filled in any non reserved words . As shown in the figure below .

attachments-2020-11-puOX2GTy5fa650a38f850.jpg

attachments-2020-11-UzLGT7D05fa650b1bcf6a.jpg

attachments-2020-11-k4jnNNim5fa650b7e044e.jpg

attachments-2020-11-4yJtdn2u5fa650c237e97.jpg

2、 Regular matching of parameters bypass:URL-WAF There are some common problems

(1).HPP Parameter pollution . part WAF When checking for duplicate parameters , Often only the first one is checked , We can repeat the reference bypass, Such as /?password=admin&&pasword=’ order by 1—+ But note that only parsing PHP Only when the last parameter is replaced by the previous one ( Repeat parameters , The example above ).

(2). Cut and bypass .

① Length truncation : part WAF Checking URL Parameter time , In order to save resources , It often intercepts a certain length of parameters for security check , And ignore the following parameters .

② Terminator truncation . part WAF encounter %00 It will judge that the parameter reading is complete , Check only part of the content .

Data fragmentation bypasses .

(3).URL-WAF Each request is often checked individually or only for the first time in consecutive but divided requests .

① Using block coding to transmit bypass . When the header of the message body (header) There is Transfer-Encoding:chunked when , Represents the use of block coded transmission , Several requests can be merged .

The message body consists of an indefinite number of blocks , Each non empty block is based on the number of bytes of data contained in the block ( The number of bytes is expressed in hexadecimal ) Start , Follow a CRLF ( Carriage return and line change ), And then the data itself , The last piece CRLF end . The last piece is a single line , By block size (0), Some optional white space filling , as well as CRLF. The last block no longer contains any data , But you can send optional tails , Include header fields . The news ended with CRLF ending .

② utilize pipline Bypass . When the header of the message body exists Connection:keep-alive when , Represents the connection established by this request in Connection To change the value of close There's no break before . Be careful to close burpsuit Of repeater Modular Content-Length Auto update .

attachments-2020-11-5DR9aDEr5fa650e151105.jpg

 

3、 The data type matching of the transmitted parameters bypass: The variable type passed in was unexpected

about _GET[‘num_value’]( also GET[numvalue]( and And _POST[‘num_value’] It's the same thing ) Come on , It's not just /?num_value=xxx As a valid parameter passing format .PHP When the parameter is accepted, the obtained parameter name will be transformed .

attachments-2020-11-aWWbyd0j5fa6522832432.jpg

If input /?num_value[]=xxx It's also legal , But the data type is the same as above string Different , Pass in an array . stay ctf Li often takes advantage of this , because md5( Array )==0.

attachments-2020-11-7efmh5ZQ5fa6511d9f908.jpg

attachments-2020-11-vGICXTNf5fa65204f0023.jpg

4、 The coding problem when transmitting parameters

(1). When there is a file operation function in the source code ,url Decode it twice , Now you can code twice urlencode.( Such as %27 Turn into %25%27)

(2).Url When decoding , If you encounter %+ Letter , It will automatically filter %. If you pass in sel%ect, Decode to get select.

(3).Base64 When decoding , If the number of characters is not triple , Can't decode and throw errors .

 

Part2  Variable generation

After passing in the parameters ,php Variables are generated according to certain rules .

(1). Server usage REQUEST To obtain parameters , It can go through POST and GET At the same time, the contract bypasses the part WAF.

(2). Server usage extract( ) function , The key and value of the obtained variable are generated into corresponding variables , May cause variable coverage , And it creates security problems .Ctf It's often used to cover the white list .

(3). Variable name plus [] Pass in array , Bypass about md5 Some checks on functions .

Such as md5(aaa[])===md5(bbb[])

(4). Deserialization . Server usage unserialize( ) Functions handle parameters , Instantiate as an object . Here's a reference to PHP On the special properties of variable generation .

Var_dump(“x66x6cx61x67”==”flag”); //  The output is bool(ture)
 alike , Deserialization 
O5Guess:1:{s:3:”key”;s:16:”x66x6cx61x67”;}
 And deserialization 
O5Guess:1:{s:3:”key”;s:16:”flag”;}
 There is no difference between 

x66 It's string ascii The hexadecimal form of the value is preceded by x, You can use the following script to generate

<?php
$string = 'flag';
// Enter the string to be processed here 
$arr = str_split(bin2hex($string), 2);
foreach ($arr as $value) {
print('x'.$value);
}
// The result is  x66x6cx61x67
?>

(5). Follow 4 There are similarities in the principle of .md5(xxx,ture) Will output one 16 Bits of binary data , This binary data also has a chance to be php decode . therefore xxx yes ffifdyop when , Will be php Think of it as a universal code ’ or 1=1

( There's actually a little difference , Not in the back 1=1, But also TURE)

 

Part3  Variable handling

After generating a variable ,PHP There are only three kinds of treatment —— Variable comparison , Regular matching , Deserialization , Let's analyze them one by one .( Deserialization is not mentioned in this article , I'll talk about it later )

 

1、 Variable comparison

PHP Since its birth, the weak type has been criticized .PHP There are two symbols that compare whether they are equal , Namely ”==” and ”===”, The former only compares whether the values are equal , When different types are compared with each other, they automatically transform , That's where the security problem is , The latter compares the types first , Then compare the value , Comparison of different types returns false.

The following table :

var_dump("abcd"==0); //true
var_dump("1abcd"==1);//true
var_dump("abcd1"==1) //false  String and number comparison , Compare the previous parts of the same type 
var_dump(abdc1==0)   //true  But at the same time, it will report an error 
var_dump(abdc1==1)   //false  But at the same time, it will report an error 
var_dump(False==0)  //true
var_dump("abcd1"==0) //true
var_dump("0e123456789"=="0e888888") //true php hold 0e At the beginning, it was explained as scientific counting , by 0
 however , Strings and Booleans cannot be compared 

 

2、 Regular matching

(1). Exclusive or bypass

PHP There's a magical feature , Exclusive or . XOR itself is not a magic thing , however PHP You can make a string with ascii Code to XOR
Simple rules of XOR : If a、b The two values are different , So the XOR result is 1. If a、b The two values are the same , So the XOR result is 0. There can only be one on both sides of the comparison for true And then it returns to true Otherwise return to false. Letters and numbers ( similar int The real number of plastic ) The XOR result is the original number , Letters without quotation marks are considered strings .

3 xor 2==1
2 xor 2==0
'`'^'*'=='J' (ascii Code XOR )
a^2==2 ( But it will report a mistake )
 Attach a python Script 
def xor():
for x in range(0,127):
for y in range(0,127):
z=x^y
print("  "+chr(x)+"ascii:"+str(x)+' xor '+chr(y)+" ascii:"+str(y)+' == '+chr(z)+" ascii:"+str(z))
// Copy and paste note that this is the same line as the previous line , Otherwise an error 
if __name__ == "__main__":
xor()

Here is a simple example .

attachments-2020-11-FBQ8YH6a5fa6514028183.jpg

attachments-2020-11-cGR0hiah5fa6516fda0bc.jpg

(2).pcre Backtracking times bypass

PHP In the regular expression of , The matching pattern has wildcards ( For example, or ?) There's a possibility of backtracking . There are other matching requirements before and after the wildcard , It's easy to cause backtracking , Every symbol of a regular expression matches the entire string , The temporary result of the match causes the next regular match symbol to match the entire string again .

such as /^<.>/, It will match a html What's in the label . When we type in bcdefg When used for matching ,< Match to the opening angle bracket , Match to the end of the line , No angle brackets were found , The result is the opening angle bracket . Continue matching from the result of removing the first angle bracket , Because everything can match , Match directly to the end of the line . here > Begin to match , Find that there is no string at the end of the line and start backtracking , matching g, Find out wrong , Remove from the interim results g, Keep going back , matching f, incorrect , to flash back , So again and again >, Match the end result . When bcdefg When it reaches a million ,PHP Not going back , Just skip matching and return false, So as to bypass the regularities .PHP To avoid this problem , A new statement specification is proposed , Regular matching if it is not matched to a character , Returns the 0, Too many backtracking times , return false. Use === Comparison results , It won't bypass if Judge .

attachments-2020-11-8hEIFIo45fa6517aead7e.jpgattachments-2020-11-7CfLdaNK5fa65180e5908.jpgattachments-2020-11-1CzR67wQ5fa651884eaed.jpgattachments-2020-11-XzwaK1wq5fa6519030093.jpg

 

Part4 Variable storage

A variable sometimes has the last step after it has been processed , Store ( The grave ). After storage , There will still be WAF To check for threats ( Deceitful corpse ). But no matter what , Now the storage check is static , So it's not difficult to get around .( Even if it's D shield )

 

1、 Static bypass

(1). The use of the namespace

 Stick a PHP Chinese manual content <?phpnamespace A;use BD, CE as F;//  Function call foo(); //  First try calling defined in the namespace "A" The function in foo()//  Try calling the global function again  "foo"foo(); //  Call global space functions  "foo"myfoo(); //  Calls are defined in the namespace "Amy" Middle function  "foo"F(); //  First try calling defined in the namespace "A" The function in  "F"//  Try calling the global function again  "F"//  Class reference new B(); //  Create a namespace  "A"  Class defined in  "B"  An object of //  If not found , Try loading classes automatically  "AB"new D(); //  Use import rules , Create a namespace  "B"  Class defined in  "D"  An object of //  If not found , Try loading classes automatically  "BD"new F(); //  Use import rules , Create a namespace  "C"  Class defined in  "E"  An object of //  If not found , Try loading classes automatically  "CE"new B(); //  Create a class defined in global space  "B"  An object of //  If you don't find , Try loading classes automatically  "B"new D(); //  Create a class defined in global space  "D"  An object of //  If you don't find , Try loading classes automatically  "D"new F(); //  Create a class defined in global space  "F"  An object of //  If you don't find , Try loading classes automatically  "F"//  Call a static method or namespace function in another namespace Bfoo(); //  Call the namespace  "AB"  Middle function  "foo"B::foo(); //  Call the namespace  "A"  Class defined in  "B"  Of  "foo"  Method //  If the class is not found  "AB" , Try loading classes automatically  "AB"D::foo(); //  Use import rules , Call the namespace  "B"  Class defined in  "D"  Of  "foo"  Method //  If a class  "BD"  Not found , Try loading classes automatically  "BD"Bfoo(); //  Call the namespace "B"  The function in  "foo"B::foo(); //  Calling classes in global space  "B"  Of  "foo"  Method //  If a class  "B"  Not found , Try loading classes automatically  "B"//  Static methods or functions in the current namespace AB::foo(); //  Call the namespace  "AA"  Class defined in  "B"  Of  "foo"  Method //  If a class  "AAB"  Not found , Try loading classes automatically  "AAB"AB::foo(); //  Call the namespace  "AB"  Class defined in  "B"  Of  "foo"  Method //  If a class  "AB"  Not found , Try loading classes automatically  "AB"?>

Static check stored variables ( Like ponies ), A callback can be added to a function , There's too much in the manual , Generally, 90 percent of the WAF, Add one before the callback function and it's done .

(2). Custom function

Use custom function to splice string or function name , Abridgement , Replace , Except to bypass WAF, There are also some excellent hazard codes that can bypass people , For example, the number of spaces after the code is converted into characters .

Here is a simple custom function , Everything is one , It's all similar .

<?phpfunction x($a,$b){call_user_func_array($a,$b);}x(‘assert’,array($_POST[‘a’]));// Even for assert This keyword can also be spliced again with variables  $y=’a’+’ssert’;?>

Except to call the reserved function twice , You can also create encryption functions to achieve similar results , As long as static to dynamic, you can avoid scanning .

版权声明
本文为[PHP open source community]所创,转载请带上原文链接,感谢