3rsh1
/single dog/college student/ctfer
3rsh1's Blog

php代码审计1

php代码审计

正则匹配绕过

这里复现了以下环境,是关于如何绕过preg_match的。

数组绕过:

代码如下:

<?php 
#a=['pp','<','>']; 
var_dump(preg_match("/[<>?php]+/",_POST['a'])); 
file_put_contents('./2.php',$_POST['a']); 
?>

如果传递的a的值是一个数组的话会出现以下结果。

bool(false) 其实在意思上也就相当于没找到。
但是同样post的内容会被写到2.php里面

如果换为strpos

bool(false)

php大部分函数对数组都不是很感冒。

长度绕过:

https://www.3rsh1.cool/wp-content/uploads/2020/04/wp_editor_md_392901f076a7deab3161001cd6f26c9e.jpg

第一行是最大回溯数,第三行是最大嵌套数。
这边有个回溯次数,其实也就是正则匹配进行检测的次数。只要我们的传递的值大于这个长度的话。正则匹配函数就会报错。貌似进行正则匹配的过程就是进行回溯的过程,这个以后再进行添加。

import requests

data = {"a":"ershisan" + "aaaaa" * 1000009+"ershisan"}
res = requests.post(url, data=data, allow_redirects=False)
print (res.content)

貌似对于贪婪匹配和非贪婪匹配,非贪婪匹配要回溯的次数更多。所以对于某些情况下不适用。

aaab
.*?b

这个问题, 就和设置项backtrack_limit有关系. 现在要弄清这个问题的原因, 关键就是什么是”回溯”.
这个正则, 使用非贪婪模式, 非贪婪模式匹配原理简单来说是, 在可配也可不配的情况下, 优先不匹配. 记录备选状态, 并将匹配控制交给正则表达式的下一个匹配字符, 当之后的匹配失败的时候, 再溯, 进行匹配。

匹配过程开始的时候, “.*?”首先取得匹配控制权, 因为是非贪婪模式, 所以优先不匹配, 将匹配控制交给下一个匹配字符”b”, “b”在源字符串位置1匹配失败(“a”), 于是回溯, 将匹配控制交回给”.*?”, 这个时候, “.*?”匹配一个字符”a”, 并再次将控制权交给”b”, 如此反复, 最终得到匹配结果, 这个过程中一共发生了3次回溯.

弱类型:

==是若比较,会把等于号两边转化成相同类型然后进行比较。字符串和数字比较的话,会先把字符串转化成数字。===是强类型比较,要求两边类型和值都要相同。
SQL语言也是弱类型语言。

''==0==false    //intval(false)结果为0
'123'==123
'123a'==123     //前面有数字后面有字母转化时会忽略后面的字母即使子母中掺杂数字
'aaa'==0        //字母转化为0
0x01==1         //16进制表示
'0e123'=='0e456'//0ea即0*10的a次方,显然也是0;前面的0是系数后面的ea是10的a次方,后面只能是数字
null==false==0
1==true

所以非0的值如果是弱类型比较的话都是true。

如果md5的值为0e开头且后面为数字的形式在弱类型比较的时候,会被看作0。如果函数报错的话即返回值为false或者null,也可实现一个弱类型比较的利用。如果是null的话在强类型比较下也可以使用。重点是看函数的返回值是什么。

var_dump(null===md5([]));//bool(true)

或者可以让两个值的md5值真的一样,即md5强碰撞。可用工具fastcoll来找到两个md5值相同的字符串。

这里的md5值通常会被用来检验文件是否被篡改过。生成的两个文件虽然md5值相同但是文件内容却不同,所以用md5值来检验文件是不安全的。

fastcoll.exe -p 1.txt -o 2 3    //指定一个文件生成和此文件相同但是md5值不相同的两个文件且生成的两个文件md5值相同。   
fastcoll.exe -q     //直接生成两个文件且md5值相同
MD5($str,true);     //就是将结果返回为字符串

在注入中如果遇到单引号闭合但是传入值为md5值的时候可以用字符串ffifdyop

拓展知识:

json_encode ( mixed value [, intoptions = 0 [, int $depth = 512 ]] ) : string

对变量进行json编码。

<?php
arr = array ('a'=>1,'b'=>2,'c'=>3,'d'=>4,'e'=>5);
echo json_encode(arr);
?>
//{"a":1,"b":2,"c":3,"d":4,"e":5}
json_decode ( string json [, boolassoc = FALSE [, int depth = 512 [, intoptions = 0 ]]] ) : mixed

接受json编码的字符串并将其转换为php变量。

<?php
json = '{"a":1,"b":2,"c":3,"d":4,"e":5}';

var_dump(json_decode(json));
var_dump(json_decode($json, true));

?>
<!--
    object(stdClass)#1 (5) {
    ["a"] => int(1)
    ["b"] => int(2)
    ["c"] => int(3)
    ["d"] => int(4)
    ["e"] => int(5)
}

array(5) {
    ["a"] => int(1)
    ["b"] => int(2)
    ["c"] => int(3)
    ["d"] => int(4)
    ["e"] => int(5)
}
-->
strcmp ( string str1 , stringstr2 ) : int

其实两个字符串做的是一个前面的减去后面的操作。所以如果前面的字符串是十六进制表示比后面的大,就会返回一个大于0的值,反之会返回一个小于0的值。且返回值为int类型(1,-1,0)。

还有个奇奇怪怪的地方就是他好像是逐个字母对应比较的,若是一个字符串为三位第二个字符串为两位,则视为第二个字符串的第三位为空。好像linux和windows有些不一样,Linux返回的是ASCII码的差,而windows返回的是1或者-1。

<?php
str1 = "bear";str2 = "tear";
str3 = "";
echo strcmp(str1, str2)."\n"; //-1
echo strcmp(str2, str1)."\n"; //1
echo strcmp(str2, str2)."\n"; //0
echo strcmp(str2, str3)."\n"; //4
echo strcmp(str3, str2)."\n"; //-4
echo strcmp(str3, $str3)."\n"; //0
echo strcmp("tc", "tb")."\n"; //1
echo strcmp("tc", "td")."\n"; //-1
echo strcmp("tc", "tca")."\n"; //-1
echo strcmp("tc", "tca")."\n"; //-1
echo strcmp('a','z');//-1
?>

对于Linux三位以上的话,貌似返回的就是不同的那一位的ASCII码的差。

in_array ( mixed needle , arrayhaystack [, bool $strict = FALSE ] ) : bool

如果第三个参数是true,则是严格的比较。第一个参数是要搜索的字符串,第二个参数是数组。在比较的过程中默认为弱类型比较。且区分大小写比较。

array_search ( mixed needle , arrayhaystack [, bool $strict = false ] ) : mixed

array_search — 在数组中搜索给定的值,如果成功则返回首个相应的键名 ,要注意这里返回的是键值。区分大小写。

<?php
array = array(0 => 'blue', 1 => 'red', 2 => 'green', 3 => 'red');key = array_search('green', array); //key = 2;
key = array_search('red',array);   // $key = 1;
?>
strpos ( string haystack , mixedneedle [, int $offset = 0 ] ) : int

查找字符串首次出现的位置。
如果没找到则返回false,第一个参数是在此字符串中查找,第二个参数是查找的字符串,第三个参数是偏移量。如果第二个参数是不是字符串类型,则会被转化为整数类型并按照ASCII表查找指定的字符串。
若是其中某个参数不不和要求的话也是会返回false的。

变量覆盖:

extract ( array &array [, intflags = EXTR_OVERWRITE [, string $prefix = NULL ]] ) : int

将变量从数组引入到当前的环境中,其实就是新建变量而已。且会判断变量的重名情况。
第一个参数是关联的一个数组。函数的作用是将数组的键当做变量名,数组的值当做变量的值。且受到后面两个参数的影响。返回值是成功的变量的个数。

<?php
array = array( 'a'=> 'blue', 'b'=> 'red', 'c'=> 'green', 'd'=> 'red');
extract(array);
echo a;array1=array(1=>'a');
var_dump(extract(array1)==null);array2=array(1=>'a','a2'=>'cc');
var_dump(extract($array2)==null);
?>
//blue bool(true) bool(false)

要注意如果变量的名不能为数字,如果是数字的话不能转化为变量。

https://www.3rsh1.cool/wp-content/uploads/2020/04/wp_editor_md_22dcf16526a048edf6467dca681b8cb8.jpg
https://www.3rsh1.cool/wp-content/uploads/2020/04/wp_editor_md_bbcadfca88a46fcb8da2f29887967c13.jpg

php中可以用变量符+变量符来声明变量。

$test1='test2';
$$test1='aaa';
echo $test2;//aaa

上述demo可见,第二个变量符和后面的test1构成一个变量并且引用其变量值test2,第一个
符和test2构成一个新的变量test2,并给其赋值。

foreach(array askey => value){
}
echokey.$value;

foreach用于遍历数组内的内容,且第一个参数需要是数组的引用。key,value,会在最后一次遍历之后保留下来,因此建议是要unset函数来销毁变量。根据数组的形式,

foreach (array_expression as value)
    statement
foreach (array_expression askey => $value)
    statement

由于 foreach依赖内部数组指针,在循环中修改其值将可能导致意外的行为。

parse_str ( string encoded_string [, array &result ] ) : void

第一个参数是要传入的字符串,第二个参数是变量保存的数组。
这个函数的作用其实就是将字符串解析为多个变量。而且这里不推荐在没有第二个参数的时候使用该函数,因为会起到一个变量覆盖的问题。

函数无返回值。 由于 PHP 的变量名不能带「点」和「空格」,所以它们会被转化成下划线。 用本函数带 result 参数,也会应用同样规则到数组的键名。 且此条规则在url传参的时候也适用。

<?php
a='ccc';parse1='ptest1=hahaha&ptest2=hehehe&a=xixixi';
output=array();
parse_str(parse1,output);
echooutput['ptest1'];//hahaha
echo a;//ccc
parse_str(parse1);
echo $a;//xixixi
?>

且存到数组里面的值都已经urldecode()过了。

<?php
str = "first=value&arr[]=foo+bar&arr[]=baz";

// 推荐用法
parse_str(str, output);
echooutput['first'];  // value
echo output['arr'][0]; // foo bar
echooutput['arr'][1]; // baz

// 不建议这么用
parse_str(str);
echofirst;  // value
echo arr[0]; // foo bar
echoarr[1]; // baz
?>
import_request_variables ( string types [, stringprefix ] ) : bool

需要注意的是使用版本为 (PHP 4 >= 4.1.0, PHP 5 < 5.4.0)!!

将get/post/cookie的值引用为全局变量。
第一个参数可以是g p c,分别表示get,post,cookie,且不区分大小写。第二个参数是在引用为全局变量的时候在变量名称前添加的前缀。且第一个参数内部的字母顺序也会影响到结果,也会存在一个覆盖的问题.

发表评论

textsms
account_circle
email

3rsh1's Blog

php代码审计1
php代码审计 正则匹配绕过 这里复现了以下环境,是关于如何绕过preg_match的。 数组绕过: 代码如下: <?php #$a=['pp','<','>']; var_dump(preg_match("/[<>?php]…
扫描二维码继续阅读
2020-04-22