With the popularity of code security, more and more developers know how to defend against Sqli, XSS and other language-independent vulnerabilities, but the development of the language itself is related to some of the vulnerabilities and flaws are not known, so these points is our code audit when the focus of attention. The purpose of this article is to summarize some of the points that often cause problems in PHP code, as well as the focus of our audit. (PS: This article is simply to list the problem, as to the underlying cause of the problem is not explained in detail, interested crossing can Google or look at the underlying C code. Know it, and know why)
If there is a wrong place in this article, please treatise The big boys:)
TODO: Continue to enrich and increase the actual vulnerability case for each point
file_put_contents, copy, file_get_contents, and so on read write operation and unlink, file_exists and other delete judgment file functions for path processing differences caused by the deletion bypass
For example, the following code
<?php$filename = __DIR__ . ‘/tmp/‘ . $user[‘name‘];$data = $user[‘info‘];file_put_contents($filename, $data);if (file_exists($filename)) { unlink($filename);}?>
The explanation of the P-bull in the small dense circle is quoted here
View PHP source code, in fact, we can find that PHP read, write to the file, will call PHP_STREAM_OPEN_WRAPPER_EX to open the stream, and to determine the existence of files, rename, delete files and other operations without opening the file stream.
We'll find out with php_stream_open_wrapper_ex that the Tsrm_realpath function will eventually be used to standardize the filename to an absolute path. And the deletion of files and other operations will not, this is the difference between the two.
So, if we pass in a file name that contains a path that does not exist, it is written because it will be processed. /"Relative path, so there is no error, no such file or directory error occurs because it will not be processed when judging or deleting.
So Linux can pass xxxxx/../test.php
, test.php/.
windows can test.php:test
test.ph<
bypass file deletion
It is also found that you can use pseudo-protocol to php://filter/resource=1.php
read file contents in file_ge_contents, copy, etc., but can bypass file deletion
Extract (), Parse_str () and other variable overrides
The Extract function imports variables (such as \$_get, \$_post) from an array, and takes the key name of the array as the value of the variable. The PARSE_STR function, in other words, parses a variable from a format string similar to name=bill&age=60. If the first function is not set or if the EXTR_SKIP
EXTR_PREFIX_SAME
argument The second function does not use the array to accept the variable when it causes the variable to overwrite the problem
Intval () integer overflow, down rounding, and shaping judgment problems
- The 32-bit system has the largest signed range of 2147483648 to 2147483647, 64 bits the largest is 9223372036854775807
? Therefore, intval (' 1000000000000 ') on a 32-bit system returns 2147483647
- Also Intval (10.99999) returns the rounding of 10,intval and int is ' truncated ' rounding, not rounding
- When the Intval function goes in, it is not converted until it encounters a number or sign, and then ends the conversion when a non-digit or end symbol is encountered.
Size comparison problems caused by floating-point precision problems
When the decimal number is less than 10^-16, PHP does not divide the decimal size.
Var_dump (1.000000000000000 = = 1) >> TRUE
Var_dump (1.0000000000000001 = = 1) >> TRUE
Is_numeric () and intval () feature differences
- The Is_numeric function ignores the ', ' \ t ', ' \ n ', ' \ R ', ' \v ', ' \f ' at the beginning of the string when judging whether it is a number.
? and '. ' Can appear in any position, E, E can appear in the middle of the parameter, can still be judged as a number. That means Is_numeric ("\r\n\t 0.1e2") >> TRUE
- The Intval () function ignores ' \ n ', ' \ R ', ' \ t ', ' \v ', ' + ', which means intval ("\r\n\t") >> 12
strcmp () array comparison bypass
int strcmp (String $ str1, string \ $str 2)
The parameter str1 the first string. STR2 a second string. If STR1 is less than str2 returns < 0;
Returns > 0 if STR1 is greater than str2, or 0 if both are equal.
However, if the two variables passed in are arrays, the function will return a null error, and if it is only judged by strcmp () ==0, you can bypass
SHA1 (), MD5 () function pass-through array comparison bypass
The SHA1 () MD5 () function receives the default parameter as a string type, but if the passed parameter is an array, the function will error returns NULL. Similar to SHA1 (\$_get[' name ']) = = = = SHA1 (\$_get[' password ')) can be bypassed by comparison
Weak type = = Comparison Bypass
There is a lot of problems in this area, not too much explanation.
MD5 (' 240610708 '); 0e462097431906509019562988736854
MD5 (' Qnkcdzo '); 0e830400451993494058024219903391
MD5 (' 240610708 ') = = MD5 (' Qnkcdzo ')
MD5 (' aabg7xss ') = = MD5 (' Aabc9rqs ')
SHA1 (' aarozmok ') = = SHA1 (' Aak1stfy ')
SHA1 (' aao8zkzf ') = = SHA1 (' aa3off9m ')
?
' 0010e2 ' = = ' 1e3 '
' 0x1234ab ' = = ' 1193131 '
' 0xABCdef ' = = ' 0xABCdef '
When converted to Boolean, the following are considered to be false:false, 0, 0.0, "", "0", Array (), NULL
In previous versions of PHP 7, if an illegal number (that is, 8 or 9) was passed to the octal number, the remaining digits would be ignored. Var_dump (0123) =var_dump (01239) =83
After PHP 7, a Parse Error is generated.
When a string is converted to a numeric value, if the string starts with a number, it is converted to a number and the subsequent non-numeric character is omitted. If there is no number at the beginning, convert to 0.
\ $foo = 1 + "bob-1.3e3"; $foo is integer (1)
\ $foo = 1 + "BOB3"; $foo is integer (1)
\ $foo = 1 + "Small Pigs"; $foo is integer (11)
' = = 0 = = False
' 123 ' = = 123
' abc ' = = 0
' 123a ' = = 123
' 0x01 ' = = 1
' 0e123456789 ' = = ' 0e987654321 '
[FALSE] = = [0] = = [NULL] = = ["]
NULL = = False = = 0? true = = 1
Eregi () match bypass
Eregi () receives the string parameter by default, and if passed in the array, the function will error and return null. You can also bypass%00 truncation
PHP variable name can not take a little [.] and space, otherwise it will be converted to underline [_]
parse_str("na.me=admin&pass wd=123",$test);var_dump($test); array(2) { ["na_me"]=> string(5) "admin" ["pass_wd"]=> string(3) "123"}
In_arrary () function is loosely compared by default (for type conversion)
in_arrary(“1asd”,arrart(1,2,3,4)) => truein_arrary(“1asd”,arrart(1,2,3,4),TRUE) => false \\(需要设置strict参数为true才会进行严格比较,进行类型检测)
The Htmlspecialchars () function defaults to escaping only double quotes, not escaping single quotes, and if both are escaped, you need to add the arguments ent_quotes in PhP4, php<5.2.1, the key value of the variable is not affected by Magic_quotes_ GPC affects sprintf () formatting Vulnerability (can eat escaped single quotes)
The printf () and sprintf () functions can be padding by using the%-by-one character
For example, the%10s string will be padded to the left by default to a length of 10, and%010s will be populated with the character 0, but if we want to fill it with a different character, we need to use ' single quotation marks for identification, such as% ' #10s this is using # Fill (the percent will not only eat ' single quotation marks, but also eat \ Slash)
also sprintf () can be used to specify the position of the parameter
] (http://or48znikk.bkt.clouddn.com/dmsj/dmsj17.png)
The number after the% represents the first few arguments, and the $ post represents the formatted type
So when we enter special characters to be escaped in quotation marks, but when we use the sprintf function for stitching
For example, '% ' in%1$ '%s ' is treated as using% for padding, resulting in a ' runaway
Another situation is ' escaped to ', for example input% ' and 1=1# entered, there is SQL filtering, ' was turned into \ '
The SQL statement becomes the SELECT * from user where username = '%\ ' and 1=1# ';
If the statement is spliced using the sprintf function, the% after \ is eaten, causing the ' escape
<?php$sql = "select * from user where username = ‘%\‘ and 1=1#‘;";$args = "admin";echo sprintf( $sql, $args ) ;//result: select * from user where username = ‘‘ and 1=1#‘?>
But such PHP Warning: sprintf(): Too few arguments
an error is easy to encounter
This time we can use%1$ to eat the transfer added \
<?php$sql = "select * from user where username = ‘%1$\‘ and 1=1#‘ and password=‘%s‘;";$args = "admin";echo sprintf( $sql, $args) ;//result: select * from user where username = ‘‘ and 1=1#‘ and password=‘admin‘;?>
In PHP, the and condition is not all true to return true, but when the first one returns true directly, skip the second condition to judge
For example, the is_numeric($a) and is_numeric($b)
program intended to be a, B is a number to continue, but when $ A is a number and that returns True, that is true and false >> true
Parse_url and Libcurl parsing differences with URLs can lead to SSRF
- When there are multiple @ symbols in the URL, the host obtained in Parse_url is the host after the last @ sign, and Libcurl is after the first @ sign obtained. Therefore, when the code is
http://[email protected]:[email protected]
parsed, PHP gets the host is baidu.com is allowed to access the domain name, and the last call Libcurl to make the request is the requested eval.com domain name, can cause SSRF bypass
- In addition
https://[email protected]
, for such a domain name to parse, PHP gets the host is [email protected]
, but Libcurl gets the host is evil.com
The flexibility of the URL standard leads to bypassing Filter_var and Parse_url ssrf
The Filter_var () function http://evil.com;google.com
returns true for false, which is considered URL format error, but for 0://evil.com:80;google.com:80/
, 0://evil.com:80,google.com:80/
0://evil.com:80\google.com:80/
.
Getting Web content through file_get_contents and returning to the client can cause XSS
For example, the following code
if (Filter_var ($argv [ 1], Filter_validate_url)) {//parse URL $r = Parse_url ($argv [1]); Print_r ($R); //check if host ends with google.com if (Preg_match ( '/baidu\.com$/', $r [ ' host ']) {//get page from URL $a = file_get_contents ($argv [1]); echo ($a);} else {echo "Error:host not allowed "; } } else {echo "Error:invalid URL";}
Although the format of the URL is checked by the Filter_var function, and the host of the URL is qualified with a regular
However data://baidu.com/plain;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pgo=
, the page will be <script>alert(1)</script>
returned to the client, which may cause XSS
PHP Trick (Code audit focus)