浅谈CRLF注入

本文最后更新于 2024年10月22日 下午

简介

CRLF(Carriage Return Line Feed)又称HRS(HTTP Response Splitting)是一种网络安全漏洞,利用的是在HTTP响应中未能正确处理用户输入的漏洞。CRLF注入攻击通过在用户输入中插入换行符(回车符和换行符),可能导致HTTP响应头被篡改,进而造成响应拆分、跨站脚本(XSS)或其他攻击。

漏洞原理

CRLF(Carriage Return Line Feed)注入漏洞利用的是HTTP响应中未能正确处理用户输入的缺陷。攻击者可以通过插入回车符(CR,\r)和换行符(LF,\n)来操控HTTP响应,导致HTTP响应头被篡改或分割,从而实现各种恶意攻击。

漏洞危害

1. HTTP响应拆分(HTTP Response Splitting)

CRLF注入最常见的危害是HTTP响应拆分。攻击者利用该漏洞可以将一个HTTP响应拆分为多个响应,从而引发多种安全问题。

2. 跨站脚本(XSS)攻击

通过HTTP响应拆分,攻击者可以注入恶意的JavaScript代码。当用户浏览器解析并执行这些代码时,可能导致跨站脚本(XSS)攻击,窃取用户的敏感信息(如Cookies、会话令牌)或进行其他恶意操作。

示例

1
2
3
4
5
6
7
8
HTTP/1.1 200 OK
Content-Type: text/html

<html>
<body>
<script>alert('XSS Attack');</script>
</body>
</html>

3. 会话固定

CRLF注入可以使攻击者插入会话,使其固定为攻击者设置的凭证

示例:

1
2
3
4
5
6
7
8
9
10
HTTP/1.1 200 OK
Set-Cookie: sessionID=hacker; HttpOnly
Content-Type: text/html
Content-Length: 100

<html>
<body>
Hello, world!
</body>
</html>

4. 缓存中毒

攻击者可以通过CRLF注入将恶意内容缓存到服务器上。如果Web应用程序使用了缓存机制(如CDN或代理服务器),恶意内容可能被缓存,影响后续请求的用户。

示例: 攻击者注入一个恶意响应,使得缓存服务器缓存了带有恶意脚本的响应内容。随后,每次用户请求相同资源时,都会从缓存中获取到恶意内容。

5. HTTP请求/响应劫持

通过CRLF注入,攻击者可以注入多个响应,导致浏览器或中间代理服务器解析错误的响应内容,从而进行HTTP请求/响应劫持,劫持用户会话或劫持Web页面内容。

6. 信息泄露

攻击者可以通过注入额外的响应内容来窃取服务器的内部信息,如敏感的响应头或错误信息,进一步加深攻击。

漏洞出现场景

  • 用户可控的自定义头部字段(但没什么用)
  • CORS漏洞中的Access-Allow-Origin直接接收Origin值导致的该响应头可控(但没什么用)
  • URL重定向漏洞中Location头可控
  • Referer头注入,某些Web应用程序可能会根据用户输入动态生成HTTP头部用于跨站点追踪(但没什么用)
  • GET、POST参数值出现在了响应包头部且可控
  • 日志注入,输入直接写入日志文件,导日志文件可伪造条目
  • 邮件头注入,发送电子邮件时,把用户输入的内容直接插入到电子邮件头中
  • API注入,某些Web应用程序可能会接受用户输入并将其作为HTTP头的一部分发送到外部API
  • 文件上传中的文件名,将文件名直接包含在HTTP响应头

换行Payload

  1. 回车符 (Carriage Return, \r%0D): 表示回到当前行的开头
  2. 换行符 (Line Feed, \n%0A): 表示换到下一行

当这两个字符组合在一起(\r\n%0D%0A)时,表示一个完整的换行操作。HTTP协议中的头部字段和实际内容通常使用CRLF来分隔,因此这种组合在注入攻击中尤为关键

Demo演示分析

Demo-1

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
if (isset($_GET['header'])) {
$headerValue = $_GET['header'];
// 故意没有对输入进行过滤,从而造成CRLF漏洞
header("X-Custom-Header: $headerValue");
}
?>

<!DOCTYPE html>
<html>
<head>
<title>CRLF Injection Demo</title>
</head>
<body>
<h1>CRLF Injection Demo</h1>
<form method="GET" action="">
<label for="header">Enter header value:</label>
<input type="text" id="header" name="header">
<input type="submit" value="Submit">
</form>
<p>Check the HTTP response headers after submitting the form.</p>
</body>
</html>

直接接收用户输入来设置X-Custom-Header响应头部,没有任何过滤

演示

image注意:这里就不要再输入框直接输入了,因为是GET提交,”%”会被URL编码一次,就不再是换行符了,采用抓包修改或者Hackbar

image

image

都出现了警告,翻译是**”警告:标头不能包含多个标头,在 D:\phpStudy\PHPTutorial\WWW\crlf\index.php 第 5 行检测到换行符”**,也就是被拦截了,查了一下原因,是php版本太高了,已经有了防御CRLF攻击的机制

于是切换一个低版本的php:5.2

image

切换为了现有的最低的版本,还是不行。没事换一个demo来看呢,或许这种demo场景太”露骨”了,于是很多版本都会拦截,换一种场景演示

Demo-2

  • 由于php对头部某些危险字符有着严格的过滤拦截机制,于是采用node.js来搭建demo-2
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
const http = require('http');
const querystring = require('querystring');

const server = http.createServer((req, res) => {
if (req.method === 'POST' && req.url === '/submit') {
let body = '';

req.on('data', chunk => {
body += chunk.toString(); // Convert Buffer to string
});

req.on('end', () => {
const parsedBody = querystring.parse(body);
const userInput = parsedBody.input || '';

// Set HTTP headers using user input (vulnerable to CRLF injection)
res.writeHead(200, {
'Content-Type': 'text/html',
'X-Injected-Header': userInput // CRLF injection can be exploited here
});

res.end(`
<html>
<head><title>CRLF Injection Demo</title></head>
<body>
<h1>CRLF Injection Demo</h1>
<p>Your input has been processed.</p>
</body>
</html>
`);
});
} else {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(`
<html>
<head><title>CRLF Injection Demo</title></head>
<body>
<h1>CRLF Injection Demo</h1>
<form action="/submit" method="POST">
<label for="input">Enter your text:</label>
<textarea id="input" name="input" rows="4" cols="50"></textarea><br>
<input type="submit" value="Submit">
</form>
</body>
</html>
`);
}
});

const PORT = 3000;
server.listen(PORT, () => {
console.log(`Server running at http://localhost:${PORT}`);
});

演示

image

image

image

还是被拦截了呜呜

image

看来,Node.js 对于 HTTP 头部的内容也有严格的字符限制,不允许有控制字符(如 \r\n)出现在头部字段的内容中

那就不折腾了,反正就是这么个道理对吧哈哈,之后搞懂了怎么搭建再来补充这篇文章……

漏洞利用

1. Web缓存投毒

攻击者可以通过 CRLF 注入修改 Web 缓存服务器的缓存内容,影响后续用户的缓存数据。这种攻击可以导致缓存中注入恶意内容或伪造的响应。

示例攻击:

假设一个 Web 应用程序在某些条件下返回不同的内容,但没有对用户输入进行验证。攻击者可以提交以下内容:

1
Cache-Control: no-cache\r\nX-Custom-Header: injected

如果缓存服务器处理用户输入不当,这可能会导致缓存中的内容被恶意修改,之后再专门研究Web缓存投毒,此处简单提一句

2. 跨站脚本攻击(XSS)

攻击者可以利用 CRLF 注入来实现跨站脚本攻击,特别是当注入的内容被后续用户渲染时。

示例攻击:

假设一个应用程序允许用户输入内容并将其回显到 HTML 页面中。攻击者可以提交如下内容:

1
<script>alert('XSS');</script>\r\nX-Injection-Header: injected

如果用户输入未经过滤直接嵌入到 HTML 中,脚本将被执行,造成 XSS 攻击。

不仅如此,通过注入两个CRLF还能造成一个无视浏览器Filter的反射型XSS:

比如一个网站接受url参数http://test.sina.com.cn/?url=xxx,xxx放在Location后面作为一个跳转。如果我们输入的是:

1
http://test.sina.com.cn/?url=%0d%0a%0d%0a<img src=1 onerror=alert(/xss/)>

我们的返回包就会变成这样:

1
2
3
4
5
6
7
HTTP/1.1 302 Moved Temporarily 
Date: Fri, 27 Jun 2014 17:52:17 GMT
Content-Type: text/html
Content-Length: 154
Connection: close
Location:
<img src=1 onerror=alert(/xss/)>

为什么说是无视浏览器filter的,这里涉及到另一个问题。

浏览器的Filter是浏览器应对一些反射型XSS做的保护策略,当url中含有XSS相关特征的时候就会过滤掉不显示在页面中,所以不能触发XSS。

怎样才能关掉filter?一般来说用户这边是不行的,只有数据包中http头含有X-XSS-Protection并且值为0的时候,浏览器才不会开启filter。

说到这里应该就很清楚了,HRS不正是注入HTTP头的一个漏洞吗,我们可以将X-XSS-Protection:0注入到数据包中,再用两个CRLF来注入XSS代码,这样就成功地绕过了浏览器filter,并且执行我们的反射型XSS。

所以说HRS的危害大于XSS,因为它能绕过一般XSS所绕不过的filter

我们来一个真实案例吧。 新浪某分站含有一个url跳转漏洞,危害并不大,于是我就想到了CRLF Injection,当我测试

1
http://xxx.sina.com.cn/?url=%0a%0d%0a%0d%3Cimg%20src=1%3E

的时候,发现图片已经输出在页面中了,说明CRLF注入成功了:

image

那么我们试试XSS看看:

image

看控制台,果然被XSS Filter拦截了。

那么我们就注入一个

1
X-XSS-Protection:0

成功绕过XSS Filter!image

3.固定会话攻击

1
http://www.sina.com.cn%0aSet-cookie:JSPSESSID%3Dwooyun

注入了一个换行,此时的返回包就会变成这样:

1
2
3
4
5
6
7
HTTP/1.1 302 Moved Temporarily 
Date: Fri, 27 Jun 2014 17:52:17 GMT
Content-Type: text/html
Content-Length: 154
Connection: close
Location: http://www.sina.com.cn
Set-cookie: JSPSESSID=wooyun

这个时候这样我们就给访问者设置了一个SESSION,造成一个“会话固定漏洞”

4. 邮件头注入

如果 Web 应用程序允许用户提供邮件头部字段(如通过电子邮件发送功能),CRLF 注入可以用来在邮件中注入额外的邮件头部。

示例攻击:

假设一个应用程序允许用户指定电子邮件的“发件人”字段,攻击者可以提交如下内容:

1
Sender: attacker@example.com\r\nBcc: victim@example.com

这将导致电子邮件发送到一个额外的 Bcc 收件人,可能导致信息泄露。

工具推荐

漏洞修复

​ 过滤 \r 、\n 之类的行结束符,避免输入的数据污染其他 HTTP 首部字段。

部分摘自:https://wooyun.js.org/drops/CRLF%20Injection%E6%BC%8F%E6%B4%9E%E7%9A%84%E5%88%A9%E7%94%A8%E4%B8%8E%E5%AE%9E%E4%BE%8B%E5%88%86%E6%9E%90.html