개요
- HTTP Request Smuggling 취약점 문제이다. HTTP 프로토콜2를 사용하는 서버에 대한 스머글링을 사용한다.
- 문제 주소: https://portswigger.net/web-security/request-smuggling/advanced/lab-request-smuggling-h2-request-splitting-via-crlf-injection
- 취약점 설명페이지: https://portswigger.net/web-security/request-smuggling/advanced#http-2-request-splitting
- 난이도: PRACTITIONER (보통)
취약점 개요 (HTTP/2 request splitting)
- HTTP/2에서는 개행문자(
\r\n
)가 특별한 의미를 가지지 않기 때문에 헤더 값에 연속되는 개행문자(\r\n\r\n
)를 지정하는 것으로 이후를 새로운 요청으로 인식시킬 수 있다. - 백엔드에서 프로토콜이 HTTP/1.1로 다운그레이드되는 경우에 공격 가능하다.
랩 개요
- 이 랩은 프론트 엔드 서버와 백엔드 서버로 구성되어 있다. 프론트 엔드 서버는 HTTP/2 요청을 백엔드에 전송할 때 HTTP1으로 다운그레이드한다. 그리고 인커밍 헤더를 적절히 새니타이즈하지 못한다. 이로 인해서 스머글링이 가능하다.
- 랩을 풀려면 응답 큐 포이즈닝(response queue poisoning)을 이용해서 admin패널에 접근하여 carlos유저를 삭제하면 된다.
- admin은 10초마다 웹 사이트를 방문한다.
This lab is vulnerable to request smuggling because the front-end server downgrades HTTP/2 requests and fails to adequately sanitize incoming headers.
To solve the lab, delete the user carlos by using response queue poisoning to break into the admin panel at /admin. An admin user will log in approximately every 10 seconds.
The connection to the back-end is reset every 10 requests, so don't worry if you get it into a bad state - just send a few normal requests to get a fresh connection.
풀이 시도
여기서부터는 HTTP 요청 분리(Splitting)문제다. CRLF Injection을 응용해서 HTTP 요청을 분리시키는 것으로 백엔드 서버에 요청을 스머글링한다. 그러면 결과적으로 응답 큐 포이즈닝이 발생해서 (운이 좋으면) 관리자 유저에게 갈 응답을 얻어낼 수 있다. 이를 통해 관리자 유저로 접근, carlos 유저를 삭제하면 된다.
- 일단 먼저 TE 패턴의 스머글링 요청을 보내본다. HTTP/2요청 헤더에
Foo: bar\r\nTransfer-Encoding: chunked
를 추가하고 보디 부분은 다음과 같이 설정한다.
0
SMUGGLED
-
요청을 몇 번 보내보면 항상 200응답이 회신된다. 이 패턴으로는 스머글링이 안되는 것 같다.
-
헤더를
Foo: bar\r\n\r\n
으로 지정한다. 보디는 GET /404 HTTP/1.1 로 지정해서 보내본다. 몇 번 보내면 다음과 같이 400 Bad Request응답이 회신된다. 스머글링한 요청자체는 제대로 된 HTTP 요청으로 처리되지 않았지만 CRLF 인젝션자체는 통하는 것 같다.
- 스머글링용 요청을 다음과 같이 이것 저것 헤더를 추가해봐도 마찬가지로 400응답이 돌아온다. HTTP/2헤더의
Foo: bar
헤더에 지정한\r\n
의 개수를 한개에서 세개까지 조정해가며 테스트해봐도 마찬가지다.
GET / HTTP/1.1
Host: 0abe000304827a5e819f215a00ac003c.web-security-academy.net
Cookie: session=G8pUSmjVnlzcmiu0rrx2vbc3of5vzCEf
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36
Foo: bar
- 조금 생각을 바꿔본다. 스머글링 요청을 아예 헤더에 삽입할 수도 있지 않을까? 다음과 같이 foo헤더에 스머글링 요청을 지정하고 요청을 보내자 404응답이 돌아왔다! 스머글링에 성공한 것이다.
-
이제 관리자 유저의 응답을 낚아채면 된다. 원본 요청(HTTP/2 요청)도 /404를 지정한다. 이렇게 하면 거의 항상 404응답이 돌아올 것이다. 응답 큐 포이즈닝 덕분에 운 좋게 관리자의 응답을 받았을 경우에는 302라던가 다른 응답 코드일 것이다.
-
그런데 수동으로 수십번 요청을 보내봤지만 항상 404응답이었다. Intruder는 HTTP/2 요청 헤더를 바꾼 부분(Kettled Request)를 보낼 수 없으므로 사용할 수 없다.
-
Turbo Intruder를 검토해보자. Turbo Intruder로 Kettled Request를 보낼 수 있는지를 테스트하기 위해 다음과 같이 설정한 후에 보내봤다.
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=10,
requestsPerConnection=10,
pipeline=False
)
for i in range(0, 1000):
engine.queue(target.req)
def handleResponse(req, interesting):
if req.status == 404:
table.add(req)
-
테스트 결과 404응답이 돌아오지 않았다. Turbo Intruder로는 Kettled Request를 보낼 수 없어 보인다.
-
따라서 Repeater로 참을성있게 Kettled Request를 계속 보내는 방법 밖에 없어 보인다.
-
음… 그러나 몇 번 시도해봐도 역시 404이외의 응답은 얻을 수 없었다. 어쩔 수 없다. 답을 보자.
-
답을 봤으나 별다른 건 없었다. 대부분 404응답이 오지만 302를 얻을 때까지 반복한다! 라는게 답이었다… 😂
-
다시 새로운 랩에서 시도해본다. 운이 좋게도 10번정도 시도 후에 302응답을 얻어냈다!
- 위에서 얻은 관리자 세션토큰으로 랩에 억세스하면 관리자 메뉴가 보인다. Carlos 유저를 삭제한다.
- 삭제에 성공하면 랩이 풀렸다는 메세지가 표시된다!