Desync Attack이란, 프론트와 백엔드가 나뉘어 동작하는 웹서버에서 각 서버가 받아들이는 Request의 내용이 다를 때 발생하는 취약점이다.
HTTP 헤더에서Content-Length
는 request의 body 길이를 알려주기 위해 사용한다.
웹 서버는 이 헤더를 이용해 request의 내용을 구분할 수 있는 것이다.
다음과 같은 형태로 사용한다.
POST /files/ HTTP/1.1
Host: uploooadit.oooverflow.io
Connection: close
x-guid: 00000000-1212-1212-1212-000000000001
Content-Type: text/plain
Content-Length: 4
A
또 하나의 방법으로 Transfer-Encoding
헤더가 있다.
이 헤더는 내용을 표시하는 방식을 결정할 수 있는데, chunked로 설정할 경우 다음과 같은 형태로 내용을 전달한다.
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked
7\r\n
Mozilla\r\n
9\r\n
Developer\r\n
7\r\n
Network\r\n
0\r\n
\r\n
\r\n으로 구분하며, 처음에 데이터의 길이를 알려주고 다음 라인에 그 길이의 데이터가 나온다.
데이터의 끝에는 마지막이라는 표시로 0이 오고, 가장 마지막 줄에 라인피드가 한 번 더 온다.
request에 두 가지 방식의 헤더가 모두 삽입되었을 때,
각 서버는 서로 다른 방식으로 length를 판단할 수 있다.
웹서버는 만약 처리되고 남은 데이터가 있다면 다음에 들어오는 request와 합치게 되는데 이 특징과 Desync Attack을 이용하여 다른 유저의 request를 조작할 수 있다.
프론트는 Content-length
로, 백엔드는 Transfer-encoding: chunked
로 데이터를 판단하는 환경에서 공격자는 다음의 request를 요청한다.
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 13
Transfer-Encoding: chunked
0
WOOU
request를 받은 프론트 서버는 Content-Length
가 13이므로 전달받은 request를 그대로 백엔드 서버로 넘겨줄 것이다.
하지만, 백엔드 서버는 Transfer-Encoding: chunked
를 확인한다.
그리고 0을 데이터의 끝이라고 생각한다.
남겨진 SMUGGLED는 서버에 그대로 캐싱되어, 다음 request가 들어올 때 가장 앞에 추가된다.
WOOUMPOST / HTTP/1.1
Host: example.com
...
...
정상적인 요청이 아니므로 서버는 해당 클라이언트에게 에러를 반환할 것이다.
Desync Attack에 대한 점검은 직접 request가 조작되는지까지 확인해봐야 한다.
또한, 가장 중요한 것은 백엔드와 프론트가 각각 어떤 방식으로 length를 판단하는지 확인하는 것이다.
이것은 상식적으로 판단하면 된다.
만약 프론트가 Content-length
이고 백엔드가 Transfer-encoding
일 때
다음의 request를 전송한다면
POST /about HTTP/1.1
Host: example.com
Transfer-Encoding: chunked
Content-Length: 7
0
X
앞서 본 것처럼 프론트는 X까지 모두 백엔드로 전달하지만, 백엔드는 0까지만 처리하여 X는 다음 request에 합쳐진다.
반대로 프론트가 Transfer-encoding
이고 백엔드가 Content-length
라면 프론트는 0까지 백엔드로 전달하지만 백엔드가 예상하는 길이는 7이므로 추가 데이터를 기다린다. 추가 데이터가 없다면 결국 time out을 반환할 것이다.
https://portswigger.net/research/http-desync-attacks-request-smuggling-reborn