Skip to content

set_redirect & set_header are susceptible to http response splitting attack #425

@shouc

Description

@shouc

ref: https://owasp.org/www-community/attacks/HTTP_Response_Splitting

Analysis

// L2766
inline void Response::set_header(const char *key, const char *val) {
  headers.emplace(key, val);
}

inline void Response::set_header(const char *key, const std::string &val) {
  headers.emplace(key, val);
}

inline void Response::set_redirect(const char *url) {
  set_header("Location", url);
  status = 302;
}
...
// L3090
inline bool Server::write_response(Stream &strm, bool last_connection,
                                   const Request &req, Response &res) {
...
if (!detail::write_headers(bstrm, res, Headers())) { return false; }
...
// L1967
template <typename T>
inline ssize_t write_headers(Stream &strm, const T &info,
                             const Headers &headers) {
  ssize_t write_len = 0;
  for (const auto &x : info.headers) {
    ...
        // write_format never replaces/parses \r\n in x.first.c_str() or x.second.c_str() 
        strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str());
    ...
  }
  for (const auto &x : headers) {
    ...
        // write_format never replaces/parses \r\n in x.first.c_str() or x.second.c_str() 
        strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str()); 
    ...
}

PoC

#include "cpp-httplib/httplib.h"
using namespace httplib;
int main() {
    Server svr;
    svr.Get("/1", [](const Request& req, Response& res) {
        res.set_redirect("1\r\nSet-Cookie: a=1");
    });
    svr.Get("/2", [](const Request& req, Response& res) {
        res.set_header("a", "1\r\nSet-Cookie: a=1");
    });
    svr.listen("localhost", 3000);
}

Lastly, this library is gorgeous. Thank you!!

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions