weird legacy


소스코드

index.js

  • 소스코드는 크게 index.html, index.js 파일로 구성됩니다.
  • 인덱스(/) 경로로 접근하면 index.html 자원이 반환되며 URL에 들어가는 값에 따라 API가 호출됩니다.
    • ”/”, “/fetch” - 서버에 보낼 수 있는 요청은 이 2가지밖에 없습니다.

  • /fetch 경로로 요청을 보내면 URL에 함께 전달된 url 파라미터 값을 url 변수에 저장하고, URL 인스턴스를 하나 생성해서 호스트명을 host 변수에 저장하고 있습니다. 이 때, 호스트(host) 값은 localhost 이거나, localhost 로 끝나는 임의의 값이어야 합니다.
    • 만약 그렇지 않으면 서버로부터 “rejected” 혹은 “Invalid Url” 응답이 반환됩니다.
  • 만약 url 파라미터에 전달받은 값이 검증을 통과하면 서버는 입력된 url 경로로 요청을 보내는데, 이 때 요청 헤더에 플래그 값이 함께 전달됩니다. 그러므로 요 헤더 값을 탈취하는게 궁극적인 목표라고 할 수 있겠네요.

  • 백그라운드에서 대상 서버로 보내지는 요청 패킷 정보는 어떻게 확인할 수 있을까요? 공격자가 서버로 요청을 직접 보내는 게 아니라 중개 서버를 거쳐서 대상 서버로 요청이 보내지는 경우에는 외부에서 이를 확인하기가 어렵습니다. 이럴 때 유용하게 사용할 수 있는 게 RequestBin, RequestCatcher 입니다. (물론 여기서는 중개 서버와 대상 서버가 동일합니다.)

EXPLOIT

  • 먼저 정상적인 URL 정보를 입력해주면 “Hello!” 메시지가 반환됩니다.
    • /fetch?url=http://localhost:3000

  • url 파라미터에 RequestBin URL을 넣으면 localhost 관련 조건이 충족되지 않기 때문에 “rejected” 메시지가 출력되는데요.
  • 이를 우회하기 위해서는 url 파라미터에 입력된 (1) URL의 호스트명이 localhost 이거나, (2) URL이 localhost 로 끝나야 합니다. 여기서 플래그를 확인하기 위해서는 반드시 리퀘스트빈으로 요청을 보내야 하기 때문에 (1) 을 우회할 수는 없습니다.
  • 그럼 (2)를 우회해야 하는데요. RFC 3986(URI 표준)에서는 호스트 부분에서 허용되는 문자들을 정의하고 있습니다.

  • 아래와 같이 ! $ * ," 등의 문자를 사용해서 값을 전달해주면, tkedeow.request.dreamhack.games!localhost 문자열 전체를 호스트명으로 인식하게 되고, 호스트 맨 끝에 localhost 값이 오는 조건을 만족하게 됩니다. 응답 값을 살펴 봐도 rejected 가 아닌 임의의 IP 주소가 반환됩니다.

  • 이제 리퀘스트빈으로 가보면 요청 패킷의 헤더(Cookie)에 플래그가 찍힌 걸 확인할 수 있습니다.

🐛.. 🐛.. 🐛..



REFERENCE

  1. https://datatracker.ietf.org/doc/html/rfc3986#section-1.1.1