bugku代码审计
前言
想参加一波比赛,要学习一下了。
正文
extract变量覆盖
先来了解一下extract
定义和用法
extract() 函数从数组中将变量导入到当前的符号表。
该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量。
第二个参数 type 用于指定当某个变量已经存在,而数组中又有同名元素时,extract() 函数如何对待这样的冲突。
该函数返回成功导入到符号表中的变量数目。
实例
将键值 “Cat”、”Dog” 和 “Horse” 赋值给变量 $a、$b 和 $c:
1 |
|
运行结果:
$a = Cat; $b = Dog; $c = Horse
然后看题目,
1 |
|
这里的extract函数没有设置extract_rules,而extract默认的规则是如果有冲突则覆盖掉已有的变量。
直接构造payload:?shiyan=&flag
strcmp比较字符串
先了解一下strcmp
strcmp( string
$str1
, string$str2
) : int
如果 str1 小于 str2 返回 < 0; 如果 str1 大于 str2 返回 > 0;两者相等,返回0。
PHP的strcmp()
函数在PHP5.3版本之前使用数组可以绕过验证
看题目:
1 |
|
函数期望传入的类型是字符串类型的数据,要是我们传入非字符串类型的数据的话,这个函数将发生错误,但是在5.3之前的php中,显示了报错的警告信息后,将return 0 。也就是说虽然报了错,但却判定其相等了,也就绕过了判断。
payload:?a[]=flag
(其实只要有a[]就行)
url二次编码绕过
1 |
|
eregi()— 不区分大小写的正则表达式匹配
①int eregi(string pattern, string string, [array regs]);
eregi()函数在一个字符串搜索指定的模式的字符串,搜索不区分大小写。eregi()可以检查有效性字符串,如密码。如果匹配成功返回true,否则返回false。
eregi()函数漏洞:字符串对比解析,当ereg读取字符串string时%00后面的字符串不会被解析。
具体查看ereg
题目要求id不等于hackerDJ
,根据提示将hackerDJ进行url二次编码,
payload:?id=hackerD%254A
(只需要对一个字符进行二次编码,当然,所有衣服编码也可以)
md5函数
1 |
|
显然我们得使username!=password
&&md5(username)===md5(password)
利用md5()无法处理数组的漏洞,会返回0.
payload:?username[]=1&password[]=2
数组返回NULL绕过
1 |
|
**ereg()**区分大小写。^表示字符串开始,$表示字符串结束,+表示一次到多次,[a-zA-Z0-9]表示字符集。
源码分析:
if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)
password值,必须是数字或字母开头,
if (strpos ($_GET['password'], '--') !== FALSE)
password中包含**–**。看到eregi()函数,想到%00截断,构造payload
payload1:?password=1%00--
也可以绕过strpos函数,也可以直接用数组截断,就不要写–了。
payload2:?password[]=
1
弱类型整数大小比较绕过
1 | $temp = $_GET['password']; |
is_numeric() — 检测变量是否为数字或数字字符串,数字和数字字符串则返回 TRUE,否则返回 FALSE。
is_numeric()在处理类似123abc
这样的字符串时,会返回0。
payload1:?password=1337abc
is_numeric()`函数对于空字符%00,无论是%00放在前后都可以判断为非数值,而%20空格字符只能放在数值后。所以,查看函数发现该函数对于第一个空格字符会跳过空格字符判断,接着后面的判断。
payload2:?password=1337%00
payload3:password=1337%20
sha()函数比较绕过
1 |
|
解读源码:
name不等于password
&他们的sha1值相等
sha()函数和md5()函数一样,都不能处理数组数据,考虑数组绕过。
payload:?name[]=1&password[]=2
md5加密相等绕过
1 |
|
md5弱比较:
PHP在处理哈希字符串时,在利用”!=”或”==”来对哈希值进行比较时,会把每一个以”0e”开头的哈希值都解释为0,所以如果两个不同的密码处理后其哈希值都是以”0e”开头的,那么PHP将会认为它们都是0。
常见payload: QNKCDZO,240610708,s878926199a,s155964671a,s214587387a,s214587387a
直接?a=240610708
十六进制与数字比较
1 |
|
$temp = $_GET['password'];
$number == $temp
首先分析代码,函数要求变量$temp不能存在1~9之间的数字,
最后,又要求$temp=3735929054;
这本来是自相矛盾的,但php在转码时会把16进制转化为十进制.
于是把将number的值3735929054转化成十六进制,来绕过判断
payload:?password=0xdeadc0de
(转化成十六进制是deadc0de,前面要加上0x表示成十六进制)
变量覆盖
参考第一题
ereg正则%00截断
1 |
|
if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)
要求只能有数字和字符,这里可以%00截断
if (strlen($_GET['password']) < 8 && $_GET['password'] > 9999999)
要求password<8并且password>9999999,可以用数组绕过,也可以用科学计数法绕过
if (strpos ($_GET['password'], '-') !== FALSE)
要求password中找不到’-‘,也可以数组绕过。
payload1:?password[]=1%00
payload2:?password=1e10*-*
strpos数组绕过
1 |
|
if (@ereg ("^[1-9]+$", $_GET['ctf']) === FALSE)
ereg要求ctf是数字,用%00截断
else if (strpos ($_GET['ctf'], '#biubiubiu') !== FALSE)
strpos要求ctf中包含’#biubiubiu‘,数组绕过。
这题中直接数组就可以了
payload:?ctf[]=1
数字验证正则绕过
1 |
|
1 int preg_match ( string $pattern , string $subject [, array &$matches [, int $flags = 0 [, int $offset = 0 ]]] )
搜索 subject 与 pattern 给定的正则表达式的一个匹配。
参数说明:
- $pattern: 要搜索的模式,字符串形式。
- $subject: 输入字符串。
- $matches: 如果提供了参数matches,它将被填充为搜索结果。 $matches[0]将包含完整模式匹配到的文本, $matches[1] 将包含第一个捕获子组匹配到的文本,以此类推。
- $flags:flags 可以被设置为以下标记值:
- PREG_OFFSET_CAPTURE: 如果传递了这个标记,对于每一个出现的匹配返回时会附加字符串偏移量(相对于目标字符串的)。 注意:这会改变填充到matches参数的数组,使其每个元素成为一个由 第0个元素是匹配到的字符串,第1个元素是该匹配字符串 在目标字符串subject中的偏移量。
- offset: 通常,搜索从目标字符串的开始位置开始。可选参数 offset 用于 指定从目标字符串的某个未知开始搜索(单位是字节)。
返回值
返回 pattern 的匹配次数。 它的值将是 0 次(不匹配)或 1 次,因为 preg_match() 在第一次匹配后 将会停止搜索。preg_match_all() 不同于此,它会一直搜索subject 直到到达结尾。 如果发生错误preg_match()返回 FALSE。
还是没看懂。。。。。。
看别人写的
preg_match(‘/^[[:graph:]]{12,}$/‘, $password)
即匹配password中除空格和tab键之外的字符12次以上,那如果我们传进去的password长度小于12或者是数组的话,preg_match
返回的就是0,就能输出flag
payload:`password[]=’ 要用$_POST提交。
简单的waf
这题好像GG了。
这样的话代码审计就做完了。