前言
刷题刷到弱类型绕过,一时兴起,觉得这个部分也是相当有必要做总结的,就开篇博客记录一下。
关于弱类型等比较
php中的等比较运算符有两种,==
和===
。
- ==:先将左右两边类型转化成一致,再进行比较值是否一致
- ===:先比较左右类型是否一致,若一致再比较值是否一致
字符串与数字比较
字符串与数字相等
1 | $key = $_GET['key']; |
key的值要与str这个字符串相等,但是key只能是数字,不过根据==
的特性,str会被转化为数字,因此构造payload:
1 | ?key=123 |
整数比大小
1 | $temp = $_GET['password']; |
password的值比1336大,但是password不能为数字,同样的原理,等式两边比较时==
会把两者变为相同的类型,构建payload:
1 | ?password=9999a |
urldecode二次编码
1 | <?php |
由于浏览器在解析URL时已经进行过一次URL解码,而程序又再次进行解码,因此二次编码URL就可以了,构建payload:
1 | ?id=%25%36%38%25%36%31%25%36%33%25%36%42%25%36%35%25%37%32%25%34%34%25%34%41 |
md5()/sha()的绕过
1 | <?php |
当要输入两个值,这两个值不能一致但是其md5/sha编码后的值要相等,这里有两种解题方法:
数组法
md5()/sha()这类函数无法处理数组,如果传入的是数组,md5()返回NULL,加密后得到的也是NULL,满足两个md5值相等;sha()返回false,使条件成立,构建payload:1
?username[]=1&password[]=2
pdf法
这个方法之前提到过,当数组被过滤或者md5/sha值不能为空时,就要用到google放出两个sha1值相同而不一样(sha256的值不通)的pdf文件。原理可以参考这篇博客:关于SHA1碰撞——比较两个binary的不同之处,构建payload:1
?username=%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&password=%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
strcmp比较字符串
1 | <?php |
这里用strcmp()对两个字符串进行了比较(区分大小写)。
strcmp(string1,string2),该函数返回:
- 0 - 如果两个字符串相等
- <0 - 如果 string1 小于 string2
0 - 如果 string1 大于 string2
对于传入非字符串类型的数据的时候,strcmp函数会报错,将返回0。所以,strcmp()在比较字符串和数组的时候直接返回0,这样通过把目标变量设置成数组就可以绕过该函数的限制,构建payload:
1 | ?a[]=1 |
过滤数字进行比较
1 | <?php |
ord():返回字符的ascii码值。
这里过滤了1-9的数字,又要求传入password的值为3735929054,这里需要将值化成十六进制再传入,构建payload:
1 | ?password=0xdeadc0de |
extract变量覆盖
1 | <?php |
extract():函数从数组中将变量导入到当前的符号表
trim(string[,charlist]):移除字符串两侧的空白字符或其他预定义字符,若省略后面一个参数,则去除\0
、\t
、\n
、\x0B
、\r
和空格。
运用extract()将GET方式获得的变量导入到当前的符号表中,然后判断$ flag和$shiyan两个变量的内容是否相等。那么我们将$flag和$shiyan这两个变量的内容都会被设置成空字符串。构建payload:
1 | Payload:?flag=&shiyan= |
限制传入的匹配
1 | <?php |
ereg():限制传入内容,例如上面就是限制了只能传入数字以及大小写字母。
strpos():查找字符串在另一字符串中第一次出现的位置。
对传入进行限制,但是又要求密码中含有–,因此我们这里有两种绕过方法:
数组法
同样strpos()如果传入数组,会返回NULL,从而绕过对--
的检测,构建payload:1
?password[]=1
截断法
在%00
后的函数无法识别,因此构建payload:1
?password=1%00--
JSON
1 | <?php |
输入一个数组进行json解码,解码后的message与key值相同才会得到flag,使用弱类型进行绕过,key肯定是字符串,两个等号时会转化成同一类型再进行比较,直接构造一个0就可以相等了,通过0==”admin”这种形式绕过,构建payload:
1 | ?message={"key":0} |
科学计数法
编码加密相等
1 | if ($_GET["a"] != hash("md4", $_GET["a"])) { |
使传入的参数a与经过md4编码后的值相同,这里我们找以0e开头,以及经过md4编码后仍然以0e开头的a值,构建payload:
1 | ?a=0e251288019 |
如果是md5的话构建payload:
1 | ?a=0e215962017 |
长度限制绕过
1 | <?php |
要求输入password的长度小于8位,且值要大于9999999,并且需要匹配到-
,这里就要用到科学计数法了:
- 1e10 = 10^10
由于还限制了输入内容,因此还需要进行%00截断,构建payload:
1 | ?password=1e10%00- |
总结
以上这些便是常见的php弱类型的绕过,以上代码来源均为bugkuctf-代码审计有兴趣的可以自己去尝试。