[Web]Cache Poisoning/Cache Deception

김민주·2025년 8월 6일

security

목록 보기
1/13

아래 참고 자료를 기반으로 학습한 Cache Poisoning과 Cache Deception에 대해 정리해보고자 한다.

참고자료


Background

1. Web Caches

Cache(캐시)

특정 데이터를 저장해 두었다가, 동일한 데이터를 요청할 때 빠르게 제공하기 위해 사용된다. 자주 요청되는 정보들을 임시 저장소에 보관하여, 불필요한 네트워크 요청을 줄이고 서버 자원을 절약할 수 있다.
최근 몇 년 동안 대부분의 시스템은 콘텐츠 전송 네트워크(CDNs, Content Delivery Networks)를 구축하여 캐싱을 통합했다.

CDN : 전 세계에 분산된 웹 캐시 프록시 네트워크

2. Poisoning

클라이언트가 언제 요청을 보내든, body나 header에 부가적인 정보가 포함되더라도 같은 요청은 같은 응답을 반환해야 한다. 그렇기 때문에 캐시 키(Cache key)를 생성하기 위한 설계가 중요하다.

응답이 특정 헤더의 값에 따라 달라진다면, 그 헤더 값도 반드시 키에 포함되어야 한다. 예를 들어, 요청에 포함된 특정 body나 header에 의해 응답이 달라지다면, 그 값은 반드시 키에 포함되어야 한다.

알지 못하는 키를 맞추기 위해 의도적인 악성 페이로드를 가진 메시지를 저장하는 것은 web cache poisoning이라고 한다.

3. Deception

캐시 규칙은 응답이 정적인지, 그리고 저장할 수 있는지를 판단하도록 설계되어 있다. 정적 리소스를 캐시하는 것을 실패하면 수행에 영향을 줄 수도 있지만, 민감한 정보가 있는 동적 응답을 저장하는 것은 애플리케이션에 치명적인 결과를 초래할 수 있다. 예를 들어, 공격자가 사용자 데이터를 가져와 캐시하도록 유도하는 악의적인 요청을 만들어낼 수 있다면, 토큰이나 API 키를 탈취해 계정 전체를 장악할 수도 있다.
이것을 web cache deception이라고 한다.

URL discrepancies

캐시 규칙을 비교하고, 캐시 키를 계산하고, 엔드포인트 핸들러를 매핑하려면, 원본 서버는 요청 리소스의 절대 경로를 추출해야 한다. 이것은 경로 구분자(path delimiter)와 정규화(normalization)을 사용한 URL을 파싱(parsing)함으로써 수행된다.

※ URL parsing
URL 문자열을 구성하는 여러 부분(프로토콜, 호스트, 포트, 경로, 쿼리 문자열 등)을 분석하고, 이러한 부분을 읽거나 수정하는 것

캐시 서버와 애플리케이션 서버의 파서(parser)가 다르다면, URL의 의미를 바꾸는 불일치(discrepancy)를 이용해 공격이 가능해진다. 이것은 공격자가 어떤 응답이 저장되도록 할지, 사용하고 있는 키가 접근가능하게 할지 조작할 수 있다

1. Delimiters (구분자)

URL RFC는 세미콜론(;)이나 물음표(?) 같은 특정 문자를 구분자(delimiter)로 정의하고 있다. 하지만 프레임워크나 서버 구현체에 따라 추가적인 구분자를 정의할 수 있도록 허용하고 있다.
이로 인해 원본 서버와 캐시 서버 간의 경로(path) 해석 불일치가 발생할 수 있다.

Origin Delimiters

웹 서버와 애플리케이션 프레임워크는 요청된 URL을 해석할 때 고유의 구분자 규칙을 따른다.
이러한 차이는 캐시 서버와의 해석 불일치(path confusion)를 유발할 수 있고, 보안 취약점으로 이어질 수 있다.

  1. Spring에서의 세미콜론(;)
    Spring을 포함한 많은 Java 프레임워크에서는 세미콜론(;)은 매트릭스 변수(Matrix variable)를 포함하기 위한 구분자로 사용된다. 매트릭스 변수는 각각의 경로에 포함되지만 절대 경우의 일부는 아니다.
  2. Rails에서의 닷(.)
    Ruby on Rails는 클라이언트가 반환할 뷰(view)를 정의하는 확장자 형식을 포함해 경로를 보내는 것을 허용한다. 이는 다양한 콘텐츠 타입(Js, Html,css 등)의 응답을 반환하는데 사용된다. 확장자 없거나 서버에 의해 인식되지 못하면 기본적으로 HTML 뷰가 반환된다. 그러므로 닷(.) 문자도 경로 구분자처럼 사용할 수 있다.
  3. OpenLiteSpeed에서의 Null 바이트 인코딩(%00)
    이 HTTP 서버는 전통적인 경로 구분자로 널(null) 인코딩 바이트를 사용한다.
  4. Nginx에서의 개행 문자 인코딩(%0a)
    Nginx가 요청 경로를 재작성하도록 설정된 경우, 개행 문자 인콛ㅇ이 경로 구분자로서 사용된다. 재작성 규칙은 전체 경로가 아닌 경로 접두사나 URL 일부만을 대상으로 한다.

Detecting origin delimiters (Origin delimiter 탐지 방법)

  1. 캐시에 저장되지 않는 요청을 찾는다.
    POST 요청과 같이 멱등(idempotent)하지 않는 요청이나 Cache-Control: no-store 또는 Cache-Control: private가 포함된 요청을 찾는다. 받은 응답(R0)은 URL의 구분자의 동작을 비교하는데 사용된다.
  2. 랜덤으로 경로의 끝에 접미어(suffix)를 붙여 동일한 요청을 보낸다.
    원래 경로가 /home이었다면, /homeabcd 와 같이 보낸다. 여기서 받은 응답(R1)과 R0가 동일하다면, 다른 엔드포인트로 1,2단계를 다시 진행한다.
  3. 2단계의 요청에서 랜덤으로 추가한 접미어 앞에 구분자 후보 분자를 추가한다.
    테스트 중인 구분자가 $라면 /home $abcd로 요청을 보낸다
    여기서 받은 응답(R2)를 R0과 비교한다.
    R2가 R0과 동일하다면, 그 특수문자는 구분자이다.

    여러 구분자를 한 번에 테스트 하기 위해서는 Burp Suite의 Intruder 기능을 하용하면 된다. 이때는 원본 문자(unencoded)와 URL 인코딩된 문자(URL-encoded) 둘 다 테스트해보는 것이 중요하다.

Detecting cache delimiters (Cache delimiter 탐지 방법)

캐시 서버는 ?(쿼리 스트림 구분자) 외의 구분자는 거의 사용하지 않는다. 1. 응답이 캐시로부터 생성된다는 것을 표현하는 부분을 찾으며 캐시 가능한 요청을 식별한다.
응답 속도(response time) 분석이나, hit 값이 포함된 X-Cache 헤더 찾기가 있다. 이 응답(R0)은 URL의 구분자 동작을 비교하는 데 사용된다.
2. 가능한 구분자 후보와 랜덤 값을 URL 경로 뒤에 접미어(suffix)로 붙여 동일한 요청을 보낸다.

R0과 응답을 비교했을 때 메시지가 동일하다면 그 문자는 구분자이다.

2. Normalization (정규화)

URL parser는 캐시 서버와 오리진 서버 둘 다 엔드 포인트 매핑 경로, 캐시 키 등을 추출하기 위해 사용된다. 첫번째로, 경로 구분자가 경로의 시작과 끝을 파악하기 위해 식별된다. 정규화 과정에서는 문자를 디코딩하고 점-세그먼트(dot-segment)를 제거함으로써 절대적인 형식으로 정규화된다.

Encodings

때때로, 구분자가 HTTP 파서가 아니라 애플리케이션 레벨에서 해석되도록 전달해야 할 수도 있다. 이럴 때는, URI RFC가 지정한 URL 인코딩을 사용한다. 이것은 경로의 의미가 바뀌지 않도록 하기 위해 문자를 인코딩하는 것이다.

많은 HTTP 서버와 프록시는 경로를 해석하기 전에 구분자 문자를 디코딩한다. 그런데, 이 디코딩 과정이 일관되지 않아 문제가 된다. 즉, 같은 URL에 대해서도 대부분의 CDNs나 오리진 서버에서 다르게 해석될 수 있다. 심지어, 이런 현상은 특별한 설정 없이도 발생한다.

게다가 RFC는 요청이 어떻게 전달되거나(forwarded), 재작성(rewritten)되어야 하는지 명확하게 정의되어 있지 않다. 그래서 많은 프록시들은 URL을 디코딩하고, 이 상태로 메시지를 전달한다. 이 경우, 그 다음 파서(parser)는 디코딩된 문자열을 구분자로서 인식하게 된다.

다음 요청이 프록시에 도착하면 %3F는 디코딩되어 ?로 바뀌고, 이는 쿼리 스트링 시작 문자로 인식된다.

이 외에도 많은 캐시 프록시에 의해 지원되는 인코딩 방식이 많다. 대부분이 기본값으로 사용되지 않더라도, CDNs를 사용자 설정을 통해 캐싱 경로를 디코드하고 제어 로직에 접근하도록 설정할 수 있다.

Detecting decoding behaviors

특정 문자가 디코딩되는지 테스트하려면, 원래 요청과 해당 요청을 인코딩한 버전을 비교한다.

/home/index → /%68%6f%6d%65%2f%69%6e%64%65%78
슬래시(/)나 다른 예약 문자 같은 특정 문자는 일부 환경에서 디코딩되지 않기 때문에 모든 문자를 개별적으로 인코딩하는 것이 유용할 수 있다.

만약 응답이 원래 요청과 동일하고 캐시에 의해 얻어진 응답이 아니라면(ex. X-Cache: MISS 등), 원래 오리진 서버가 경로를 사용하기 이전에 디코딩하는 것이다.
인코딩한 버전에 의해 원래 요청을 보내본다. 두 응답 모두 같은 캐시 헤더를 포함하고 있다면, 이것은 프록시가 요청을 비교하기 전에 인코딩을 디코딩한 것이다.

Dot-segment normalization (Dot-segment 정규화)

URI RFC는 URL에서 dot-segments(./..)를 다루는 방법을 정의하고, 경로를 정규화하는 간단한 알고리즘도 제공한다. 이 기능은 상대 경로에서 모든 절대경로로 바꾸는 데 사용되지만, 많은 보안 취약점을 유발할 수 있다.

캐시 규칙의 동작을 바꾸거나 특정 캐시 키를 얻기 위해 파서(parser) 사이의 구분자를 활용하여 dot-segment 정규화를 악용하는 것이 가능하다. 예를 들어, Apache나 Nginx와 같은 인기있는 HTTP 서버는 URL을 해석하는 방식이 완전히 다르다. 이것은 다른 캐시 프록시를 사용할 때 경로 혼동(path confusion)이 발생할 수 있다는 것을 의미한다.

Detecting dot-segment normalizaion (Dot-segment 정규화 탐지)

이 기술은 캐시 서버와 오리진 서버에서 dot-segment 정규화를 탐지하는데 사용될 수 있다. 이는 다른 디코딩이 적용되었다면, dot-segment를 인코딩된 버전으로 바꿔 요청을 보내면 특수한 디코딩이 적용되었는지도 확인할 수 있다.

오리진 서버의 정규화를 탐지하기 위해, 캐시되지 않은 요청을 정상적인 경로로 보낸다. 이후 경로 탐색 시퀀스 (path traversal sequence)를 사용하여 같은 메시지를 보낸다.

만약 두 응답이 동일하면 이것은 경로가 resource에 매핑되기 전에 정규화되는 것을 의미한다. 이 정규화는 오리진 서버에서 일어날 수도 있고, 프록시에서 요청을 전달하기 전에 발생할 수도 있다. 어쨋든, dot-segment는 정리되고, 결국 기존 자원을 참조하는데 사용된다.

웹 캐시에서 정규화를 탐지하기 위해서는, 캐시 가능한 응답에 대해 이 과정을 반복하고, 해당 자원이 캐시 메모리에서 제공되었는지 확인하기 위해 X-Cache 또는 Cache-Control 헤더를 비교한다.

Normalization discrepancies (정규화 구분자)

아래 사진은 /hello/..%2fworld 라는 경로를 HTTP 서버와 웹 캐시 프록시가 어떻게 다르게 정규화하는지를 보여준다. 일부는 /world로 해석하지만, 다른 것은 전혀 정규화하지 않는다.

Arbitrary Web Cache Deception

웹 캐시가 오리진 서버에서 응답을 받을 때, 해당 리소스가 정적인지 판단하고 저장되어야 한다. 이 과정은 요청과 응답에 미리 정의되고 커스터마이징 가능한 규칭들이 적용된다.

이번 섹션에서는 응답이 캐시될 때 URL을 기준으로 결정하는 규칙들에 초점을 둔다. 이러한 규칙들은 실제 운영 환경에서 흔히 사용되고, 대부분의 CDNs는 이러한 규칙들을 기본적으로 가지고 있다.

캐시 규칙을 유출하고, 동적 응답을 저장하고, 피해자로부터 만들어지는 민감한 정보를 가로채기 위해 URL 파싱의 불일치를 이용할 수 있다. 경로 혼동(path confusion)을 유도하기 위한 URL 매핑상의 불일치를 이용하는 방법은 Omer Gil의 백서 “Web Cache Deception Attack” 에 상세히 설명되어 있다.

이 문서는 그 외의 다른 유형의 불일치(discrepancy)에 초점을 맞추며, 이를 통해 오리진 서버의 특수한 엔드포인트 매핑 없이도 임의의 응답을 탈취할 수 있는 공격을 설명한다.

1. Limitations(한계)

공격자는 피해자의 브라우저가 사용할 링크를 생성해야 하므로, URL에는 브라우저가 전송 전에 인코딩하지 않는 안전한 문자들만 포함되어야 한다.
이 시나리오를 이해하기 위해, 브라우저를 일부 문자를 인코딩하거나 일부를 제거하는 방식으로 요청 URL을 재작성하는 하나의 프록시처럼 생각해봐라.

2. Static extensions(정적 확장자)

대부분의 CDN 제공업체는 정적 확장자를 가진 리소스의 응답을 저장합니다. 이는 요청 경로가 .js, .css 등과 같은 확장자로 끝나면, 캐시 프록시가 이 응답을 정적으로 처리한다는 것을 의미한다. 응답을 저장하고, 같은 경로를 요청하는 다른 클라이언트에게 이 캐시된 응답을 제공한다.
각 CDN이나 캐시 프록시는 고유의 정적 확장자 목록을 가지고 있다.

해당 이미지는 CloudFlare가 인식하는 정적 확장자들의 나열이다.

Exploiting static extensions (정적 확장자 악용)

오리진 서버에서는 구분자로 인식되지만 캐시 서버에서는 구분자로 인식되지 않는 경우가 있다. 이 경우, 요청 경로에 캐시 규칙을 속이고 민감한 응답을 저장하는 임의의 접미사(suffix)를 추가할 수 있다.

예를 들어, $(달러 기호)가 오러지 서버에서는 구분자지만, 프록시에서는 단순 문자로 인식된다면 다음과 같은 URL이 동작한다.

/myAccount로의 응답을 저장하는 링크가 민감한 정보를 탈취하기가 가능하게 한다.

위와 같은 기술을 인코딩된 문자나 문자열에서도 사용할 수 있다. 이는 오리진 서버가 URL을 파싱하기 전에 구분자를 디코딩할 때 또는 캐시가 경로를 재작성해서 요청을 전달할 때 유용하다.

#(해시태그 기호)는 브라우저에 의한 요청에 포함되지 않기 때문에 cache deception 공격에 사용이 되지 않는다. 하지만, 인코딩된 형태로 사용하면 공격할 수 있다.

이 공격은 요청 전달중 발생하는 URL 재작성(transforming) 과정의 불일치(discrepancy)를 악용하는 방식으로 응용할 수 있다. 만약 여러개의 파서(parser)가 요청을 재작성하면, 중첩 인코딩(multiple encodings) 또는 다중 구분자(multiple delimiters)를 이용해 특정 캐시 프록시를 공격할 수 있다.


%25 -> %, %23 -> #으로 디코딩 된다.
따라서, 최종적으로 오리진 서버는 /myAccount#a.css로 인식하지만 캐시는 /myAccount%23.css를 키로 저장하게 된다. 이 역시 불일치를 이용한 cache deception 시나리오가 된다.

3. Static directories (정적 디렉터리)

모든 CDNs에서 흔히 사용되는 규칙 중 하나는, 사용자가 URL 경로 접두사(prefix)에 대해 캐시 규칙을 정의할 수 있다는 것이다. 이는 특정 디렉토리에 있는 모든 리소스가 변경되지 않는(immutable) 것으로 인식되어 이름이나 확장자에 상관없이 저장되어야 한다는 것을 웹 캐시가 인지하게 하는데 사용된다.

정적 디렉터리의 일반적인 예시이다

  • /static
  • /assets
  • /wp-content
  • /media
  • /templates
  • /public
  • /shared

Exploiting static directories with delimiters (구분자를 활용한 정적 디렉터리 악용)

만약 오리진 서버에서는 어떤 문자를 구분자로 인식하지만, 캐시 서버는 그렇지 않고, 캐시는 정규화를 한 다음에 정적 디렉터리 규칙을 적용한다면, 구분자 뒤에 경로 탐색(path traversal) segment를 숨길 수 있다.

이때, dot-segment를 인코딩하는 것은 매우 중요하다. 그렇지 않으면 피해자의 브라우저가 직접 dot-segment를 처리해버려서 원래 의도한 악성 경로가 서버에 전달되지 않는다.

Amazon CloudFront, Microsoft Azure, Imperva는 기본적으로 경로를 정규화하고 캐시 규칙을 적용한다.

Exploiting static directories with normalization (정규화를 활용한 정적 디렉터리 악용)

오리진 서버는 정규화를 하고 엔드포인트를 매핑하고, 캐시 서버는 정규화 없이 캐시 규칙을 적용하는 경우, 공격자는 경로 탐색 구문을 추가해 오리진 서버에서만 처리되도록 할 수 있다.


Cloudflare, Google Cloud, Fastly는 경로를 정규화하지 않고 캐시 규칙을 적용한다.
Nginx, Microsoft IIS, OpenLiteSpeed는 오리진 서버가 경로를 정규화하고 요청을 엔드포인터 핸들러와 매핑한다. 이는 모든 정적 디렉토리 규칙을 악용하는 것이 가능하게 한다.
Microsoft IIS를 역슬래시()를 변환하지 않는 웹 캐시와 결합하면서 또다른 정규화 불일치가 발생했다. 이 캐시들은 인코딩된 역슬래시를 단순한 슬래시로 해석한다. 아무도 CDN이 이 변화를 인식하는 것을 확인하지 못했기 때문에, IIS는 다음과 같이 사용하면 취약하다.

4. Static files (정적 파일)

/robots.txt, /favicon.ico, /index.html 같은 파일은은 정적 디렉터리에 위치해 있거나 정적 확장자를 가지고 있지 않더라도 모든 웹사이트에서 변하는 않는(immutable) 것으로 간주된다. 이런 파일들을 저장하면 파일 이름과 정확히 일치하는 경로에 대해 캐시 규칙을 만드는 게 가능하다. CloudFlare와 같은 CDNs는 이런 규칙을 기본적으로 가지고 있고, 항상 robots.txt와 favicon.ico에 대한 응답을 저장한다.

Exploiting static files (정적 파일 악용하기)

정적 파일을 악용하기 위해 프론트엔드에서 정규화가 일어나고, 백엔드에서는 구분자를 사용하는 경우 정적 파일 규칙도 동일하게 악용할 수 있다. 이 경우, 정적 디렉터리 대신 파일 이름으로 사용되고, 캐시 버스터(cache buster)가 캐시된 자원에 접근하지 않도록 한다.

Cache buster : unique file version identifier를 사용하여 브라우저에게 새로운 버전이 있음을 알려 준다.

Arbitrary Web Cache Poisoning

웹 서버가 응답을 정적(static)이라고 판단하면, 해당 응당은 원래 요청을 기반으로 만든 키를 사용한 캐시에 저장이 된다. 추후에 동일한 키를 가진 요청이 오면, 저장된 리소스를 그대로 반환한다.

키는 주로 URL과 host 헤더로 만들어진다. 경우에 따라 다른 헤더나 요청 요소들을 포함하도록 커스터마이징할 수 있다.

고전적인 cache poisoning(캐시 포이즈닝)에서 공격자는 사용자가 취약한 웹사이트를 사용하는 동안 요청받는 URL 키를 사용하여 악성 응답을 저장하려고 시도한다. 더 많이 그 주소에 방문할수록, 더 많은 피해자들이 악성 페이로드에 의해 영향을 받는다. James Kettle의 연구 'Practical Web Cache Poisoning'과 'Web Cache Entanglements:Novel pathways to poisoning' 에서 웹 캐시 중독(web cache poisoning)을 탐지하는 것에 대해 더 자세히 볼 수 있다.

대부분의 경우 오염된 경로는 공격자만으로 컨트롤되지 않고 사용자의 상호작용이 요구되지 때문에 공격이 제한적이다.

예를 들어, /home?param=XSS와 같이 특정 파라미터를 요구하거나, 경로 자체 만으로 /< script >alert()</ script > 구문을 포함하기 때문에 방문한 적이 없는 URL을 생각해봐라.

그러나, 경로 혼동(Path Confusion)과 결합한 캐시 중독(Cache poisoning) 취약점은 웹사이트 홈페이지에서 캐시키를 수정하고 자주 요청되는 리소스를 덮어쓰는 것이 가능하다. 이 경우, 공격이 사용자와의 상호작용을 필요로 하지 않기 때문에, 사용 가능한 문자의 제한이 없다. 이는 페이로드가 Burp Suit와 같은 HTTP 에디터/리피터를 통해 전송될 수 있음을 의미한다.

1. Key normalization (키 정규화)

URL을 정규화하는 것은 주로 요청된 리소스의 절대 경로를 얻는 데 도와주는 안전한 작업으로 간주된다. 그러나, 캐시 키에서 닷(.) 세그먼트(dot-segment)와 인코딩을 해석하는 과정에서 오리진 서버가 같은 방식으로 경로를 해석하지 못할 때, 공격자는 임의의 리소스를 오염시킬수 있다.

이후의 모든 공격들은 URL이 캐시 키를 생성하기 전에 정규화된다고 가정한다. 이는 대부분의 CDNs에서 설정할 수 있고, Microsoft Azure나 Imperva에서는 기본 동작이다.

Exploiting mapping discrepancies (매핑 불일치 악용하기)

오리진 서버가 특별한 매핑을 사용하거나 응답을 생성하기 전에 정규화를 하지 않을 때, 리소스를 저장하기위해 사용되는 키를 조작하는 것이 가능하다. 고전적인 예시로는 존재하지 않는 엔드포인트에 접근했을 때, self-reflected XSS취약점이 발생하는 애플리케이션이다.

다음과 같은 요청/응답을 생각해보자.

악성 페이로드는 URL의 일부로 포함되고, 캐시 가능한 응답에 반영된다. 그러나, 정상정인 사용자는 공격자와의 상호작용이 없다면 절대 /< script> X< /script> 와 같은 경로로 요청을 보낼 필요가 없다. 그러므로 응답이 인코딩된 버전인 /%3Cscript%3EX%3C/script%3E 를 이용해서 접근이 가능할지라도, 공격자는 reflected XSS 시나리오로 피해자에게 링크를 보낼 필요가 있을 것이다.

하지만 캐시 키가 정규화가 되는 경우에는, 다음과 같은 페이로드로 /home과 같은 자주 방문하는 엔드포인트로 악성 응답으로 오염시킬 수 있다

이 예시에서는 더블 닷 세그먼트(double dot-segment)는 경로 내에 이미 /(슬래시)가 포함되어 있기 때문에 사용된다.
원하는 오염된 엔드포인트에 공격하기 위해 경로 탐색(path traversal)에 적응하여라.
이와 같은 기술은 플레이스홀더(placeholder)에 대해 특별한 매핑이 적용되는 경우에서 적용될 수 있다.

2. Exploiting back-end delimiters (백엔드 구분자 악용하기)

한 문자가 오리진 서버에서는 구분자로 사용되고, 캐시에서는 그렇지 않다면, 캐시 가능한 리소스에 대해 임의의 키를 생성하는 것이 가능하다. 이 구분자는 닷 세그먼트를 해석하는 것을 중단시키는 역할을 할 것이다.

3. Exploiting front-end delimiters (프론트엔드 구분자 악용하기)

웹 캐시 디셉션(Web Cache Deception)공격에서, 파싱 불일치는 캐시 서버가 아닌 오리진 서버에서만 사용되는 구분자에 의해 발생한다. 브라우저를 통해 보내질 수 있는 캐시 서버에 특별한 의미를 가진 문자를 찾는 것은 어렵지만, 캐시 포이즈닝은 사용자와의 상호작용을 필요로하지 않기 때문에, #과 같은 구분자는 경로 혼동을 일으킬 수 있다. 부분 부분들이 많은 HTTP 서버, CDNs, 백엔드 프레임워크에서 다르게 해석되기 때문에 이 점을 악용할 수 있다.

예를 들어, Microsoft Azure와 같은 경우는 경로를 정규화하고 #을 구분자로 인식하기 때문에 저장된 리소스에 대해 캐시키를 수정하는 것에 악용하는 것이 가능하다.

이 기술은 캐시에 의해 사용되는 모든 구분자에 적용할 수 있다. 유일한 필요 조건은 키가 정규화되고, 경로가 구분자 뒤에 접미어와 함꼐 전달되어야 한다는 것이다.

Cache-What-Where

웹사이트를 모의해킹하거나 버그바운티를 진행하다보면, 브라우저의 제약과 한계 때문에 악용 가능하지 않은 취약점을 발견하는 것은 흔한 일이다. 이러한 취약점들은 사용자와의 상호작용을 필요로 하며, 요청에 특정 헤더나 인코딩 된 URL에서의 문자를 필요로 하기 때문에, 브라우저를 통해서는 요청이 전송되지 않는다.

이전에 설명한 캐시 포이즈닝과 캐시 디셉션을 이러한 취약점들과 결합함으로써, 공격자는 이러한 취약점도 악용할 수 있고, 캐시에 악성 페이로드를 저장할 수 있다.

예를 들어, 대상 주소가 X-Forwarded-Host 헤더와 함께 생성되는 오픈 리다이렉트(open redirect)가 가능한 웹사이트를 생각해보자.

이 리다이렉션 자체는 캐시에 저장되지 않기 때문에, 이것만으로 캐시를 오염시킬 수는 없다. 그러나, 캐시 키와 백엔드 파서 사이에 불일치가 존재한다면, "악용할 수 없는: 취약점도 전체 도메인 탈취 공격으로 이어질 수 있다.

예를 들어 웹 애플리케이션이 /main.js 스크립트를 홈페이지에 로드한다면, 캐시에 있는 경로를 오염시키고 브라우저가 악성 스크립트를 로드하도록 만들 수 있다.

이렇게 하면 캐시 프록시가 /main.js 경로에 대해 evil.com으로 리다이렉트하는 응답을 저장하게 한다. 피해자가 홈페이지를 로드하고 /main.js 리소스에 접근하려고 하면, 악의적인 리다이렉트가 공격자가 제어하는 Javascript 파일을 로드하게 되고, 결국 사용자의 브라우저는 감염될 것이다.

최악의 시나리오로, 오픈 리다이렉트는 캐시 헤더때문에 응답이 저장된다

이 경우, 오염된 경로는 정적 확장자를 필요로 하지 않고, 취약점은 완전한 임의 캐시 포이즈닝과 완전한 웹사이트 해킹에 영향을 받을 것이다.

이 기술은 자기반사적(self-reflected)이고 악용불가능한 xss 같이 사용자와의 상호작용이 사용자와의 상호작용인 필요한 다른 취약점과 사용될 수 있다.

Defence(방어)

웹 캐시 디셉션을 방어하는 가장 쉬운 방법은 모든 동적으로 생성된 응답에 Cache-Control 헤더를 설정하고, no-store나 private 값을 사용하는 것이다. 이것은 웹 캐시에 해당 리소스를 절대 저장하지 말라고 지시한다.

캐시 규칙이 Cache-Control 헤더보다 우선되지 않도록 설정하는 것도 매우 중요하다. 대부분의 CDNs에서 이 설정은 가능하다. 설정을 할 수 없다면, 캐시 규칙을 비활성화하거나 CDN에 URL을 다르게 파싱하는 오리진 서버나 프레임워크를 피해야 한다.

캐시 키 혼동(Cache Key Confusion)을 방지하기 위해서는 캐시 키가 정규화되지 않고, 캐시 구분자 뒤의 접미사가 애플리케이션 서버에 전달되지 않아야 한다는 것을 명심해야한다. 이것이 불가능하다면, URL을 최대한 유사한 방식으로 파싱하는 CDN이나 HTTP 서버로 교체하는 것을 고려해봐야한다.

Takeaways(핵심정리)

URL 파싱 불일치는 웹 캐시 포이즈닝과 디셉션에서 쉽게 악용될 수 있다.

이 공격 기번들은 수많은 시스템과 버그 바운티 프로그램에 적용 가능하다.

웹 캐시 포이즈닝과 디셉션을 연계하면, 취약점의 심각도를 크게 높이고 사이트 전체를 장악할 수도 있다.

0개의 댓글