1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?php
$users = array(
"0:9b5c3d2b64b8f74e56edec71462bd97a" ,
"1:4eb5fb1501102508a86971773849d266",
"2:facabd94d57fc9f1e655ef9ce891e86e",
"3:ce3924f011fe323df3a6a95222b0c909",
"4:7f6618422e6a7ca2e939bd83abde402c",
"5:06e2b745f3124f7d670f78eabaa94809",
"6:8e39a6e40900bb0824a8e150c0d0d59f",
"7:d035e1a80bbb377ce1edce42728849f2",
"8:0927d64a71a9d0078c274fc5f4f10821",
"9:e2e23d64a642ee82c7a270c6c76df142",
"10:70298593dd7ada576aff61b6750b9118"
);

$valid_user = false;

$input = $_COOKIE['user'];
$input[1] = md5($input[1]);

foreach ($users as $user)
{
$user = explode(":", $user);
if ($input === $user) {
$uid = $input[0] + 0;
$valid_user = true;
}
}

if (!$valid_user) {
die("not a valid user\n");
}

if ($uid == 0) {

echo "Hello Admin How can I serve you today?\n";
echo "SECRETS ....\n";

} else {
echo "Welcome back user\n";
}

拿到这道题,我们先要找问题的突破点

1
2
3
4
if ($uid == 0) {

echo "Hello Admin How can I serve you today?\n";
echo "SECRETS ....\n";

从这里,我们可以看到,如果我们要拿到flag,那么uid的值必须为0
那么这个uid是什么呢?

我们继续看前面一段代码

1
2
3
4
5
6
7
8
9
10
11
$input = $_COOKIE['user'];
$input[1] = md5($input[1]);

foreach ($users as $user)
{
$user = explode(":", $user);
if ($input === $user) {
$uid = $input[0] + 0;
$valid_user = true;
}
}

我们先传入一个数组user赋值给input
然后将input的第二项进行md5加密
然后对users数组进行遍历,然后判断其是否和通过cookie传入的user数组相等
如果相等的话,就把user数组的第一个元素赋值给uid,并通过加0的方式来进行int型的强制转换
并将valid_user置为true

那么如果我们要得到flag,那么需要满足两个条件:
1.user[0] 的值为 0
2.valid_user = true

结合这两个条件,那么解出这道题的最直接的方法就是得到users数组中第一个用户的密码的明文。
通过前面的

1
$input[1] = md5($input[1]);

以及

1
if ($input === $user) {

我们可以得出前面的密文是经过md5加密后的密文
我们尝试通过在线解密的平台来获得该密文对应的明文

通过解密平台并不能得到我们想要的明文,那么我们该怎么办呢?

为了介绍下一种拿到flag的姿势,我们先要谈一个php的漏洞
在php5.6.11之前,php的数组在进行”==”和”===”比较的时候有一个截断漏洞:
就是当数组的键名的值大于232的时候,会造成一种截断,此时,如果键名如果为232那么其实就和0是等价的,如果为232+1,那么其实就和1是等价的,以此类推……..
可能有些读者不是很懂我说的是什么意思
我们可以通过几个demo来更直观的解释
我们先计算232的值:

得到232的值为:4294967296
首先是”==”的比较的demo:

1
2
<?php
var_dump([0 => 0] == [4294967296 => 0]);

运行结果:

1
2
<?php
var_dump([1 => 0] == [4294967297 => 0]);

运行结果:

然后下面是 “===”的demo:

1
2
<?php
var_dump([0 => 0] === [4294967296 => 0]);

运行结果:

1
2
<?php
var_dump([1 => 0] === [4294967297 => 0]);

运行结果:

但是我们需要注意的是,这个漏洞只针对键名,而数组键所对应的值则不存在。
demo如下:

1
2
<?php
var_dump([0 => 0] === [0 => 4294967296 ]);


我们可以看到,值的232就不和0相等了

好了,这个漏洞就介绍到这里了,现在我们再回到我们面临的问题中来。
我们知道的怎样 才能拿到flag,可是users列表中第一个用户密码的明文我们拿不到。

这时我们就要用另外一种思路了,uid为0,不一定就是匹配到了users列表中的第一位用户。如果我们的user[0]没有值,又匹配到了users列表中的任意一个用户,不就可以达到uid为0,valid_user为true吗

但是问题又来了,user[0]要不存在值,那么又怎么能满足匹配到users列表中的用户呢?
这时就要利用我们前面提到的php在数组比较时产生的漏洞了
我们再看这段代码

1
2
3
4
5
6
7
8
foreach ($users as $user)
{
$user = explode(":", $user);
if ($input === $user) {
$uid = $input[0] + 0;
$valid_user = true;
}
}

在比较的时候,input和user这两个数组时用的”==”在比较,那么我们可以不设置input[0]的值,而是用input[4294967296]来代替,然后比较的时候时用的input[4294967296]和input[1]来和列表中的用户比较,而uid又是通过input[0]+0来得到的,这样就完美绕过了它的限制。
通过对上面users列表中用户的哈希进行md5解密,可以发现5号用户的密码明文时可以得到的:

然后我们完整的思路就是令 user[1] = “hund” user[4294967296] = “5”就可以了

最后更新: 2018年03月13日 00:35

原始链接: http://drac0nids.top/2018/03/12/challenge1/

× 请我吃糖~
打赏二维码