各种特性
PHP语言特性
弱类型
PHP中比较两个值是否相等可以用 “ == ” 或者 “ === ” ,前者可以自动进行类型转换而不改变原来的值,所以存在漏洞的地方用的往往是“==”
比如
这里需要使两个变量值不相等而他们的MD5值相等.
这样就有两个思路来解题:
两个MD5相同,内容不同的字符串
Param1=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2
Param2=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2
sha1也有类似碰撞
a=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1&
b=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1
注意提交时使用bp抓包,不能直接提交,否则的话浏览器解码,数据会不完整,其实就相当于比较两个文件的hash
- 利用PHP的弱类型:
MD5函数的返回值是一个32位的字符串,如果这个字符串以0e开头的话,类型转换机制会将它识别为一个科学计数法表示的0
攻击者可以利用这一漏洞,通过输入一个经过哈希后以”0E”开头的字符串,即会被PHP解释为0,如果数据库中存在这种哈希值以”0E”开头的密码的话,他就可以以这个用户的身份登录进去,尽管并没有真正的密码。
但并不是所有MD5值0e开头的都弱相等,只有一些特殊的字符串,即后面是纯数字的
0e开头的md5和原值:
字符串 | MD5 |
---|---|
240610708 | 0e462097431906509019562988736854 |
QNKCDZO | 0e830400451993494058024219903391 |
s878926199a | 0e545993274517709034328855841020 |
s155964671a | 0e342768416822451524974117254469 |
s214587387a | 0e848240448830537924465865611904 |
s214587387a | 0e848240448830537924465865611904 |
s878926199a | 0e545993274517709034328855841020 |
s1091221200a | 0e940624217856561557816327384675 |
s1885207154a | 0e509367213418206700842008763514 |
s1502113478a | 0e861580163291561247404381396064 |
s1885207154a | 0e509367213418206700842008763514 |
s1836677006a | 0e481036490867661113260034900752 |
s155964671a | 0e342768416822451524974117254469 |
s1184209335a | 0e072485820392773389523109082030 |
s1665632922a | 0e731198061491163073197128363787 |
s1502113478a | 0e861580163291561247404381396064 |
s1836677006a | 0e481036490867661113260034900752 |
s1091221200a | 0e940624217856561557816327384675 |
s155964671a | 0e342768416822451524974117254469 |
s1502113478a | 0e861580163291561247404381396064 |
s155964671a | 0e342768416822451524974117254469 |
s1665632922a | 0e731198061491163073197128363787 |
s155964671a | 0e342768416822451524974117254469 |
s1091221200a | 0e940624217856561557816327384675 |
s1836677006a | 0e481036490867661113260034900752 |
s1885207154a | 0e509367213418206700842008763514 |
s532378020a | 0e220463095855511507588041205815 |
s878926199a | 0e545993274517709034328855841020 |
s1091221200a | 0e940624217856561557816327384675 |
s214587387a | 0e848240448830537924465865611904 |
s1502113478a | 0e861580163291561247404381396064 |
s1091221200a | 0e940624217856561557816327384675 |
s1665632922a | 0e731198061491163073197128363787 |
s1885207154a | 0e509367213418206700842008763514 |
s1836677006a | 0e481036490867661113260034900752 |
s1665632922a | 0e731198061491163073197128363787 |
s878926199a | 0e545993274517709034328855841020 |
还有MD5和双MD5以后的值都是0e开头的
CbDLytmyGm2xQyaLNhWn
770hQgrBOjrcqftrlaZk
7r4lGXCH2Ksu2JNT3BYM
上一个例子的进阶版本:
if($_GET['a']!=$_GET['b']&&md5($_GET['a'])===md5($_GET['b'])){
echo $flag;
}
这里用了‘===’之后,之前的字符串就不能成功了。但仍可利用PHP语言函数错误处理上的特性,在URL栏提交a[]=1&b[]=2
绕过。
因为当我们令MD5函数的参数为一个数组时,函数会报错并且返回NULL值。虽然参数是两个不同的数组,但返回值是相同的NULL
哈希比较
md5的相关内容看上面
payload | 作用 |
---|---|
hash1=0e251288019 | 可以让md4过后的字符串和原来字符串在弱比较时表示相等 |
hash2[]=1&hash3[]=2 | 绕过两个变量的强比较 |
hash4=ffifdyop | 使md5过后显示的值表示为'or' |
除了ffifdyop,129581926211651571912466741651878684928 也可
一道总结题:
https://blog.csdn.net/weixin_45844670/article/details/108199965
一些函数漏洞
strcmp()函数
strcmp() 函数用于比较两个字符串
假设比较的两字符串为string1和string2,函数的返回值将有下面三种情况:
返回0 - 如果两个字符串相等
返回<0 - 如果 string1 小于 string2
返回>0 - 如果 string1 大于 string2
当比较数组和字符串的时候,返回是NULL
is_numeric()函数
用于检测变量是否为数字或数字字符串,是数字和数字字符串则返回 TRUE,否则返回 FALSE,包括数组
is_numeric()函数检测的时候会自动过滤掉前面的 ‘ ‘, ‘\t’, ‘\n’, ‘\r’, ‘\v’, ‘\f’等字符,但是不会过滤 ‘\0’,如果这些字符出现在字符串尾,也不会过滤,而是返回 false。
var_dump(is_numeric("\01")); // false
var_dump(is_numeric(" 1")); // true
var_dump(is_numeric("\t1")); // true
var_dump(is_numeric("\n1")); // true
var_dump(is_numeric("\r1")); // true
var_dump(is_numeric("\v1")); // true
var_dump(is_numeric("\f1")); // true
var_dump(is_numeric("\f\f1")); // true
var_dump(is_numeric("1\f")); // false
如果有两个is_numeric判断的时候用and连接起来,and后面的is_numberic判断可忽略
is_numeric('1') and is_numeric('a') //true
strpos()函数
用于查找字符串首次出现的位置
int strpos( string $haystack, mixed $needle[, int $offset = 0] )
h a y s t a c k \color{red}{haystack} haystack 在该字符串中进行查找。
n e e d l e \color{red}{needle} needle 如果 needle 不是一个字符串,那么它将被转换为整型并被视为字符的顺序值。
o f f s e t \color{red}{offset} offset 如果提供了此参数,搜索会从字符串该字符数的起始位置开始统计。如果是负数,搜索会从字符串结尾指定字符数开始。
如果没找到 needle,将返回 FALSE。
利用
当
h
a
y
s
t
a
c
k
为
数
组
时
,
函
数
返
回
N
U
L
L
,
当
haystack为数组时,函数返回NULL,当
haystack为数组时,函数返回NULL,当needle为数组时函数返回flase
或者
if(strpos($str1,$str2)==false){//str1不包含str2的时候
敏感操作
}
这时候当str2在str1开头时,函数返回值是0,而0==false成立
preg_match()函数
执行匹配正则表达式
以制作免杀马为例:
在制作免杀马的过程,根据php的语言特性对字符进行!
运算会将字符类型转为bool类型,而bool类型遇到运算符号时,true会自动转为数字1,false会自动转为数字0
如果将bool类型进行计算,并使用chr()函数转为字符,使用”.”进行连接,便可以绕过preg_match匹配。
详情了解php不同于其他语言部分
但是很多的preg_match会过滤掉.
所以需要使用异或运算进行绕过,很多的免杀马都是这样制作的。php对字符进行异或运算是先将字符转换成ASCII码然后进行异或运算,并且php能直接对一串字符串进行异或运算,例如”123”^”abc”是”1”与”a”进行异或然后”2”与”b”进行异或,以此类推,在异或结束后就获得了想要的字符串。
注意点:进行异或运算时要将数字转换成字符形式,如果数字(int)和字符异或的话,结果只会是数字,例如1^”a”=1, ”a”^2=2,将数字转换成字符串可以使用trim()函数。
拓展:
php特性use of undefined constant,会将没有引号的字符都自动视为字符串,ASCII码大于0x7F的都会被当作字符串,由此可知可以简化异或过程,任何字符与0xff异或都会取相反,这样就能减少运算量了。
以GET或POST传入字符绕preg_match为例:
php的eval()函数在执行时如果内部有类似”abc”^”def”的计算式,那么就先进行计算再执行,我们可以利用再创参数来实现更方便的操作,例如传入?a=$_GET[b],由于b不受限制就可以任意传值了,不过
注意1:
在测试过程中发现问题,类似phpinfo();的,需要将后面的();放在第个参数的后面,例如url?a={_GET}{b}();&b=phpinfo,也就是?a=KaTeX parse error: Expected '}', got 'EOF' at end of input: …hpinfo,在传入后实际上为{???^???}{?}();但是到了eval()函数内部就会变成${_GET}{?}();成功执行。
注意2:
测试中发现,传值时对于要计算的部分不能用括号括起来,因为括号也将被识别为传入的字符串,可以使用{}代替,原因是php的use of undefined constant特性,例如 G E T a 这 样 的 语 句 p h p 是 不 会 判 为 错 误 的 , 因 为 使 用 来 界 定 变 量 的 , 这 句 话 就 是 会 将 G E T 自 动 看 为 字 符 串 , 也 就 是 {_GET}{a}这样的语句php是不会判为错误的,因为{}使用来界定变量的,这句话就是会将_GET自动看为字符串,也就是 GETa这样的语句php是不会判为错误的,因为使用来界定变量的,这句话就是会将GET自动看为字符串,也就是_GET[‘a’]
<?php $hhh = @$_GET['_']; if (!$hhh){ highlight_file(__FILE__); } if(strlen($hhh)>18){ die('One inch long, one inch strong!'); } if ( preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', $hhh) ) die('Try something else!'); $character_type = count_chars($hhh, 3); if(strlen($character_type)>12) die("Almost there!"); eval($hhh); ?>用户传入
?_=${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=phpinfo
成功显示phpinfo页面
反序列化漏洞
PHP提供serialize和unserialize函数将任意类型的数据转换成string类型或者从string类型还原成任意类型。当unserialize函数的参数被用户控制的时候就会形成反序列化漏洞
与之相关的是PHP语法中的类,PHP的类中可能会包含一些特殊的函数,名为magic函数,magic函数的命名方式是以符号__
开头的,比如__construct、__destruct、__toString、__sleep、__wakeup等。这些函数在一些情况会被自动调用。
我写的一个关于php反序列化漏洞的讲解:
https://blog.csdn.net/weixin_45844670/article/details/108171963
需要注意的是,除了PHP,Ruby和Java,python等语言都存在反序列化漏洞
尽管许多实验和示例都是基于PHP的,但是大多数利用技术对于其他语言也同样有效。
截断
ereg()函数
ereg()函数用指定的模式搜索一个字符串中指定的字符串,如果匹配成功返回true,否则,则返回false。
如果没有传递入可选参数 regs 或者所匹配的字符串长度为 0,则本函数返回 1
int ereg ( string $pattern , string KaTeX parse error: Expected 'EOF', got '&' at position 17: …tring [, array &̲regs ] )
利用:
ereg函数存在00截断漏洞,当ereg读取字符串string时,如果遇到了%00,后面的字符串就不会被解析。
ereg函数处理数组,会返回NULL