WriteUp-2019.5.6

题目来源

bugku:https://ctf.bugku.com/

代码审计专题

extract变量覆盖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
$flag='xxx';
extract($_GET);
if(isset($shiyan))
{
$content=trim(file_get_contents($flag));
if($shiyan==$content)
{
echo'flag{xxx}';
}
else
{
echo'Oh.no';
}
}
?>

1、文件将get方法传输进来的值通过extrace()函数处理,将键名和键值转换为变量名和变量的值

2、通过两个if语句分别判断是否存在shiyan变量,和变量shiyan的值和变量content的值是否相等。变量content的值是通过读取变量flag的值获取到的。如果两个变量相等输出flag。如果不相等,输出错误。

3、但是我们并不知道flag的值是什么?所以我们使用变量覆盖漏洞,重新给test赋值。GET请求 ?shiyan=&flag=,extract()会将$shiyan和$flag的值覆盖了,将变量赋值为空,(因为file_get_contents()函数,所以只能赋值为空),这样就可以满足$shiyan == $content。

strcmp比较字符串

1
2
3
4
5
6
7
8
9
10
<?php
$flag = "flag{xxxxx}";
if (isset($_GET['a'])) {
if (strcmp($_GET['a'], $flag) == 0) //如果 str1 小于 str2 返回 < 0; 如果 str1大于 str2返回 > 0;如果两者相等,返回 0。
//比较两个字符串(区分大小写)
die('Flag: '.$flag);
else
print 'No';
}
?>

这里利用了strcmp()函数的漏洞,如果传入的值不是字符串,则返回0

这里直接传入数组?a[]=1

成功得到flag

urldecode二次编码绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
if(eregi("hackerDJ",$_GET[id])) {
echo("

not allowed!

");
exit();
}
$_GET[id] = urldecode($_GET[id]);
if($_GET[id] == "hackerDJ")
{
echo "
Access granted!

";
echo "
flag

";
}
?>

1、get传入的id最终经过了两次解码,第一次是浏览器默认对传入参数进行了一次解码,第二次由代码$_GET[id] = urldecode($_GET[id]);解码,所以我们需要将最终的id进行两次编码再传入

2、利用了字母h的编码%68和符号%的的编码%25,对其绕过,构造id=%2568ackerDJ,第一次浏览器先解码,将%25解码为%,变为id=%68ackerDJ;
第二次urldecode()函数解码,将%68解码为h,最后变为id=hackerDJ;
从而最后满足if条件,输出flag。

md5()函数

1
2
3
4
5
6
7
8
9
10
11
12
<?php
error_reporting(0);
$flag = 'flag{test}';
if (isset($_GET['username']) and isset($_GET['password'])) {
if ($_GET['username'] == $_GET['password'])
print 'Your password can not be your username.';
else if (md5($_GET['username']) === md5($_GET['password']))
die('Flag: '.$flag);
else
print 'Invalid password';
}
?>

username != password并且md5(username)==md5(password) 时即可得到flag

方法一:由于md5()函数无法处理数组,会直接返回NULL,因此可以直接构造username[]=1&password[]=2,此时md5(username)==md5(password) =NULL

方法二:利用了php中的0e漏洞,php再利用“==”比较字符串的哈希值时,会把0e开头的哈希值都解释为0,如

md5(‘240610708’) //0e462097431906509019562988736854.
md5(‘QNKCDZO’) //0e830400451993494058024219903391

虽然两个字符串的哈希值不同,但是md5(‘240610708’)==md5(‘QNKCDZO’)

PS:使用严格模式比较“===”可解决这一漏洞,md5(‘240610708’)===md5(‘QNKCDZO’)不成立

前女友

1
2
3
4
5
6
7
8
9
10
11
12
<?php
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
if($v1 != $v2 && md5($v1) == md5($v2)){
if(!strcmp($v3, $flag)){
echo $flag;
}
}
}
?>

原理同上

构造v1=240610708,v2=QNKCDZO,使得v1 != $v2 && md5($v1) == md5($v2)成立

构造v3[]=1,使得strcmp函数返回0

各种绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  <?php 
highlight_file('flag.php');
$_GET['id'] = urldecode($_GET['id']);
$flag = 'flag{xxxxxxxxxxxxxxxxxx}';
if (isset($_GET['uname']) and isset($_POST['passwd'])) {
if ($_GET['uname'] == $_POST['passwd'])

print 'passwd can not be uname.';

else if (sha1($_GET['uname']) === sha1($_POST['passwd'])&($_GET['id']=='margin'))

die('Flag: '.$flag);

else

print 'sorry!';

}
?>

只要使uname的sha1的值与passwd的sha1的值相等,同时他们两个的值又不能相等即可

同md5()函数一样

sha1()也无法处理数组

所以构造uname[]=1&id=magin,并用POST发送passwd[]=2

即可得到flag

ereg正则%00截断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
if (isset ($_GET['password'])) {
if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)
{
echo '<p>You password must be alphanumeric</p>';
}
else if (strlen($_GET['password']) < 8 && $_GET['password'] > 9999999)
{
if (strpos ($_GET['password'], '*-*') !== FALSE)
{
die('Flag: ' . $flag);
}
else
{
echo('<p>*-* have not been found</p>');
}
}
else
{
echo '<p>Invalid password</p>';
}
}

需要满足以下要求:

1、要求变量中存在字母、数字

2、要求变量长度<8且值>9999999,可以用科学计数法来绕过

3、要求变量中有”-“,用%00漏洞来绕过

综上,构造?password=1e9%00-

strpos数组绕过

1
2
3
4
5
6
7
8
9
10
11
<?php
$flag = "flag";
if (isset ($_GET['ctf'])) {
if (@ereg ("^[1-9]+$", $_GET['ctf']) === FALSE)
echo '必须输入数字才行';
else if (strpos ($_GET['ctf'], '#biubiubiu') !== FALSE)
die('Flag: '.$flag);
else
echo '骚年,继续努力吧啊~';
}
?>

strpos() 函数无法处理数组

如果参数是数组,返回null

null!==false成立

所以构造?ctf[]=1