뭉크테크
Targeted web cache poisoning using exploiting responses that expose too much information 본문
Targeted web cache poisoning using exploiting responses that expose too much information
뭉크테크 2025. 4. 8. 23:33너무 많은 정보를 노출하는 응답 활용
- 서버 응답:
- HTTP/1.1 200 OK
Via: 1.1 varnish-v4
Age: 174
Cache-Control: public, max-age=1800
- HTTP/1.1 200 OK
- 뜻: "이 응답은 Varnish 캐시를 거쳤고, 캐시에 저장된 지 174초 됐어요. 앞으로 1800초(30분) 동안 유효해요."
- 공격자가 알아낸 것: "아, 캐시가 30분마다 새로고침 되는구나. 174초 지났으니 26분 뒤에 악성 요청 보내면 캐시에 저장되겠네!"
- 공격자는 정확한 타이밍(26분 뒤)에 악성 페이지를 삽입해서 캐시를 오염시킬 수 있게 된다. 그러면 그 뒤로 30분 동안 정상 사용자들이 악성 페이지를 보게 된다.
- 서버가 Age와 max-age를 알려주지 않았다면, 공격자는 캐시 갱신 시점을 알아내느라 여러 번 시도해야 했을 것이다
Vary 헤더와 사용자 타겟팅
Vary 헤더는 캐시가 어떤 기준으로 응답을 저장할지 알려준다. 예를 들어, Vary: User-Agent라면 캐시가 사용자의 브라우저 종류에 따라 다른 응답을 저장한다. 공격자는 이걸 이용해 특정 사용자만 공격하거나 더 많은 사람을 노릴 수 있다.
쉽게 설명
- Vary는 "캐시가 이 헤더를 보고 다르게 저장해요"라는 뜻이다.
- 공격자가 이걸 알면, 특정 브라우저를 쓰는 사람만 골라서 공격하거나, 가장 많이 쓰는 브라우저를 노릴 수 있다.
예시
- 서버 응답:
- Vary: User-Agent Content: "모바일 페이지입니다."
- 뜻: 캐시가 User-Agent 값에 따라 다른 응답을 저장한다. 예를 들어, 모바일 브라우저와 PC 브라우저가 다른 페이지를 보게 된다.
- 공격자가 할 수 있는 일:
- 특정 사용자 공격:
- 피해자가 크롬 브라우저를 쓴다고 알면, 공격자가 User-Agent: Chrome으로 악성 요청을 보내 캐시를 오염시킨다.
- 결과: 크롬 사용자만 "해킹당했어요!" 페이지를 보게 된다.
- 대량 공격:
- 가장 많이 쓰이는 User-Agent(예: "Safari")를 찾아서 그 값으로 캐시를 오염시킨다.
- 결과: Safari 쓰는 모든 사람이 피해를 봄.
- 특정 사용자 공격:
전체적인 그림
- 핵심 포인트: 서버가 캐시 동작(Age, max-age)이나 캐시 기준(Vary)을 너무 자세히 알려주면, 공격자가 그 정보를 이용해 캐시를 조작할 수 있게 된다.
- 쉽게 비유하면: 은행이 "내 금고는 매일 밤 12시에 문 열고 30분 동안 돈 꺼낼 수 있어요"라고 광고하는 꼴이다. 도둑이 그 시간에 맞춰서 돈을 훔치기 쉬워지는 것이다.
현실적인 예시
- 상황: 쇼핑몰 사이트가 캐시에 max-age=3600(1시간)을 설정하고, Vary: User-Agent를 사용.
- 공격:
- 공격자가 User-Agent: iPhone으로 "사이트가 해킹됨" 페이지를 1시간 뒤 캐시에 삽입.
- 결과: 1시간 뒤 iPhone으로 접속한 모든 사용자가 해킹 메시지를 봄.
- 정보 노출의 역할: max-age와 Vary가 없었다면 공격자는 타이밍과 타겟을 맞추느라 훨씬 힘들었을 것.
실습 시나리오
이번 시간의 시나리오는 아래와 같다.
- Param Miner 라는 툴을 이용하여 대상 사이트의 취약한 부분을 찾아낸 뒤 이용하도록한다.
- 웹 캐싱 테스트를 거친 뒤, 실제 웹 캐싱 주입에도 이어나갈 것이다.
- 이번 실습 특성상, vary 라는 헤더로 인해 요청하는 사용자에 따라 캐싱이 달라지므로 이 또한 고려하여 사용자들의 user-agent 값을 알아 낸뒤, 해당 에이전트 값을 이용하여 요청 헤더를 수정하여 캐싱을 시도하도록 한다.
이번 공격은 취약한 부분을 자동으로 찾아주는 도구인 Param Miner 라는 도구를 대상 서버의 home 위치에 사용해보았다. Burpsuite에서 무료로 제공해주는 툴이며, Extensions 에서 따로 다운로드 받아 사용가능하다. 해당 툴의 원리는 아래와 같다. 함 읽어보는 것도 추천한다.
Param Miner의 동작 원리
- 사전 정의된 리스트 사용:
- Param Miner는 HTTP 헤더, 쿼리 파라미터, 쿠키 등에 사용할 수 있는 방대한 이름 리스트를 내부적으로 가지고 있다. 이 리스트는 일반적으로 많이 사용되는 표준 헤더(예: Host, Origin, User-Agent)뿐만 아니라, 비표준적이거나 숨겨진 파라미터(예: X-Forwarded-For, X-Host)도 포함된다.
- 사용자가 커스텀 리스트를 추가할 수도 있지만, 기본적으로 제공되는 데이터베이스를 활용한다.
- 무차별 대입(Brute Force):
- Param Miner는 이 리스트에 있는 각 항목을 대상 요청에 하나씩 삽입하거나 수정하며 서버에 전송한다. 예를 들어, HTTP 헤더에 X-Host: test를 추가하거나, Origin: https://example.com을 넣는 식으로 요청을 변형한다.
- 이 과정에서 "헤더 브루트포스"라는 표현이 로그에 나타난 것처럼, 가능한 모든 조합을 시도해 본다.
- 서버 응답 분석:
- 서버가 요청에 대해 어떻게 반응하는지를 분석한다. 반응은 다음과 같은 방식으로 확인된다:
- 응답 코드 변경: 예를 들어, 404에서 200으로 바뀌거나, 오류가 발생하는 경우.
- 응답 내용 변경: 응답 본문에 새로운 데이터가 추가되거나 특정 문자열이 반영되는 경우.
- 응답 시간 차이: 서버가 특정 파라미터를 처리하느라 응답 시간이 달라지는 경우(타이밍 기반 탐지).
- Param Miner는 이러한 변화를 감지해 "이 파라미터가 서버에서 인식된다"고 판단한다.
- 서버가 요청에 대해 어떻게 반응하는지를 분석한다. 반응은 다음과 같은 방식으로 확인된다:
- 결과 필터링:
- 단순히 서버가 반응했다고 해서 모두 유효한 파라미터로 간주하지 않는다. Param Miner는 의미 있는 반응(예: 응답에 파라미터 값이 반영되거나 동작에 영향을 미치는 경우)만 골라서 사용자에게 보고한다.
- 당신의 로그에서 x-host, origin, via가 "Identified parameter"로 나온 것은, 이 헤더들이 서버에서 실제로 처리되었거나 응답에 영향을 준 것으로 판단된 결과다.
예시로 이해하기
가정해보죠. Param Miner가 다음 헤더를 시도했다고 가정하면:
- X-Foo: test → 서버 응답 변화 없음 (무시됨).
- X-Host: example.com → 서버가 example.com을 기반으로 응답을 다르게 생성 (발견됨).
- Via: 1.1 proxy → 응답에 프록시 관련 정보 추가됨 (발견됨).
이 과정에서 X-Host와 Via는 서버가 반응한 파라미터로 기록되고, X-Foo는 무시된다.

정확히 뭔가?
- X-Host는 비표준 HTTP 헤더로, 요청의 호스트 이름을 재정의하거나 서버가 요청을 처리하는 데 사용할 수 있는 값을 전달한다. 일반적으로 Host 헤더가 표준으로 사용되지만, 일부 웹 애플리케이션이나 프록시/캐시 서버가 X-Host를 추가적으로 인식하도록 설정된 경우가 있다.
- Param Miner 로그에서 x-host~%h:%s로 표시된 것은 이 헤더가 호스트 이름(%h)과 서브도메인이나 기타 문자열(%s)을 조합한 형식으로 동작에 영향을 미친다는 뜻아다.
어떻게 활용될 수 있나?
- 웹 캐시 포이즈닝(Web Cache Poisoning):
- X-Host를 조작해 서버나 중간 캐시(예: CDN, Varnish)가 잘못된 호스트 이름으로 응답을 캐싱하도록 유도할 수 있다.
- 예: X-Host: evil.com을 설정하면, 캐시가 evil.com에서 온 응답으로 저장될 수 있고, 이후 정상 사용자가 해당 캐시를 받게 되어 악성 콘텐츠를 보게 된다.
- 서버 라우팅 조작:
- 백엔드에서 X-Host를 신뢰하고 이를 기반으로 요청을 라우팅한다면, 공격자가 이를 조작해 내부 시스템(예: 관리자 페이지)으로 접근을 시도할 수 있다.
- 취약점 확인:
- X-Host: localhost나 X-Host: internal-server.local 같은 값을 넣어 서버가 내부 호스트로 요청을 처리하는지 확인할 수 있다.
예시 시나리오
- 요청: GET /page HTTP/1.1 + X-Host: attacker.com
- 응답: 서버가 attacker.com에서 온 것처럼 응답을 생성하거나 캐시를 오염시킴.
2. origin 헤더
정확히 뭔가?
- Origin은 HTTP 표준 헤더로, 주로 CORS(Cross-Origin Resource Sharing) 요청에서 사용된다. 브라우저가 크로스-도메인 요청(예: AJAX 호출)을 보낼 때, 요청이 시작된 출처(도메인)를 서버에 알려준다.
- 로그에서 origin~https://%s.%h로 나온 것은, 이 헤더가 https://[서브도메인].[호스트] 형식으로 값을 받아 서버가 이를 처리한다는 의미다.
어떻게 활용될 수 있나?
- CORS 설정 우회:
- 서버가 Origin 헤더를 기반으로 접근을 허용한다면, 공격자가 Origin: https://trusted.com처럼 신뢰할 만한 도메인을 설정해 제한된 리소스에 접근할 수 있는지 테스트할 수 있다.
- 예: 서버가 Origin 값을 검증하지 않고 응답에 Access-Control-Allow-Origin을 설정한다면, 크로스-사이트 스크립팅(XSS)과 결합해 데이터 유출이 가능하다.
- 오리진 스푸핑:
- Origin: null이나 Origin: https://evil.com처럼 조작된 값을 넣어 서버가 이를 잘못 신뢰하는지 확인해본다.
- 취약점 탐지:
- 서버가 Origin에 따라 다른 응답을 반환한다면, 이를 통해 내부 로직(예: 인증 우회)을 노출시킬 수도 있다.
예시 시나리오
- 요청: GET /api/data HTTP/1.1 + Origin: https://evil.com
- 응답: 서버가 Access-Control-Allow-Origin: https://evil.com을 반환하면, 공격자가 브라우저에서 이 데이터를 읽을 수 있음.
3. via 헤더
정확히 뭔가?
- Via는 HTTP 표준 헤더로, 요청이 프록시 서버나 게이트웨이를 거쳐 전달될 때 각 중간 노드가 자신의 정보를 추가한다. 예: Via: 1.1 proxy.example.com은 요청이 해당 프록시를 거쳤음을 나타낸다.
- Param Miner가 이를 식별했다는 것은, 서버나 중간 시스템이 Via 헤더를 인식하고 처리한다는 뜻.
어떻게 활용될 수 있나?
- 프록시 동작 조작:
- Via 헤더를 조작해 프록시 서버가 요청을 잘못 처리하거나 캐싱 동작을 변경하도록 유도할 수 있다.
- 예: Via: 1.1 fake-proxy를 추가해 프록시가 이를 신뢰하고 특정 동작을 수행하는지 확인.
- 캐시 포이즈닝:
- 캐시 서버가 Via 값을 기반으로 응답을 캐싱한다면, 이를 악용해 악성 응답을 캐시에 저장할 수 있다.
- 정보 수집:
- 서버가 Via 헤더를 반영해 응답에 추가 데이터를 포함한다면, 이를 통해 프록시나 백엔드 구조를 파악할 수 있다.
예시 시나리오
- 요청: GET /resource HTTP/1.1 + Via: 1.1 malicious-proxy
- 응답: 서버가 Via 값을 로그에 남기거나 응답에 반영하면, 공격자가 이를 확인해 추가 공격을 설계.
활용 가능성 요약
이 세 개의 헤더는 웹 애플리케이션의 보안 취약점을 탐색하거나 공격에 활용될 수 있는 강력한 단서. 주요 활용법을 정리하면:
- x-host: 캐시 포이즈닝, 라우팅 조작, 내부 시스템 접근 시도.
- origin: CORS 우회, 인증 로직 노출, 데이터 유출.
- via: 프록시 조작, 캐시 오염, 인프라 정보 수집.
실습 제안
Burp Suite의 Repeater나 Intruder를 사용해 다음 테스트 가능:
- x-host: attacker.com → 응답이 어떻게 변하는지 확인.
- origin: https://malicious.com → CORS 헤더가 반영되는지 확인.
- via: 1.1 fake-proxy → 서버나 프록시의 반응 관찰.
정상적인 요청을 하였을 때, 응답 결과를 살펴보았다. 일단 vary 라는 헤더를 통해 캐시가 응답 브라우저의 종류에 따라 다른 응답을 저장한다는 것과, cache-contorol: max-age=30 을 통해 캐시가 갱신되는 최대 주기가 30초라는 것과 age 를 통해 캐시가 가장 쵝든에 변경된 시간(초)이 0초임을 알 수 있고, x-cache: miss 임을 통해 요청 사항이 캐싱을 성공하지 못했다 라는 것도 알 수 있었다. 나아가, 요청 페이지가 참조하는 페이지의 위치가 0a8000cd03d73f948187169c0092008d.h1-web-security-academy.net/resources/js/tracking.js 임을 알 수 있다.
x-host: example.com 라는 헤더를 붙이고, 테스트 목적으로 하기에 캐시 버스터 설정도 한 요청을 대상서버로 전송하였다.
그 결과, 오른쪽 응답을 보면, 참조하는 스크립트 위치가 example.com/resources/js/tracking.js 로 바뀌었음을 알 수 있다. 즉, 참조 대상이 x-host 헤더를 이용한 캐시 포이즈닝에 의해 스크립트의 참조 호스트가 바뀐 것이다.
캐싱할 자바스크립트 파일을 제공해줄 공격 페이지이다. 파일 위치는 대상 서버가 host주소/resources/js/tracking.js 라는 파일을 참조하기에, 그와 유사하게 공격 페이지에서 파일 위치를 /resources/js/tracking.js 로 설정하였고, 응답 악성 스크립트는 alert(document.cookie)로 작성하였다. 그리고 맨 위에 호스트 주소를 복사한 다음,
응답 헤더 중 X-host 헤더에 붙여넣어 대상 서버로 전송하였고, 서버가 참조하는 자바스크립트 파일 위치가 exploit-0a0b007703593f9d812e1515010b0035.exploit-server.net/resources/js/tracking.js 이라는 것과, X-Cache : hit 를 통해 캐싱에 성공했다는 결과와 age: 2 를 통해 캐싱된지 2초가 흘렀음을 확인하였다.
테스트가 성공했음을 확인한 뒤, 캐시버스터를 제거하여 대상 서버에 직접적으로 캐시 포이즈닝을 시도하였고,
그 결과, 웹 캐시 포이즈닝에 성공한 것을 위 그림을 보면 확인할 수 있다. 즉, 캐싱하는 자바스크립트 위치가 나의 악성 페이지의 악성스크립트 파일 위치로 변조되었고, 그걸 참조한 사이트는 자연스럽게 내 악성 스크립트(alert(document.cookie.))가 실행된 것이다. 그러나 방금 위에서 봤다 시피 응답 헤더의 Vary 라는 헤더로 인해 요청 브라우저의 종류에 따라 캐싱이 달라진다는 것이다. 그래서 다른 브라우저를 쓰는 유저의 캐싱을 오염시킬 뿐만 아니라, 다른 브라우저를 쓰는 유저에서는 어떻게 될지 확인하고자 아래와 같은 과정을 거쳤다.
일단 사람들이 종종 이용하는 포스트에 댓글을 위와 같이 남겼다. 이미지 주소를 남기면, 해당 이미지를 요청하기위해 해당 서버 즉, 나의 악성 사이트에 요청을 한다. 그 요청한 사용자의 브라우저 종류를 보기 위해서 저렇게 남긴것이다.
그리고 내 공격 사이트의 엑세스 log를 살펴본 결과, 내 ip(119.~) 이외 10.0.3.63 이라는 IP를 가진 다른 사용자가 접근한 것을 볼 수 있었다. 즉, 해당 포스트를 클릭한 것으로 인해 내가 남겨둔 댓글 속 이미지를 요청하기위해 내 공격 사이트에 접근 하게되었고, 그로인해 접근 로그 기록이 저렇게 남게된 것이다. 암튼, 저 사용자의 user-agent의 값을 가져와서
요청 헤더 중 하나로 붙인 뒤 대상 서버로 전송하여 웹 캐싱을 시도하였고, 그 결과, 제대로 캐싱 되었음을 위 그림을 보면 알 수 있다. 즉, 10.0.3.63 이라는 IP를 가진 이의 브라우저의 종류를 띄는 사용자 또한 대상 서버의 자바스크립트 참조 위치가 exploit-0a0b007703593f9d812e1515010b0035.exploit-server.net/resources/js/tracking.js 로 캐싱되었고, 해당 파일의 악성스크립트가 실행되어 해당 사용자에게도 영향이 갈 것이다.
대응 방안
1. 서버에서 불필요한 헤더 제거
서버 소프트웨어(예: Nginx, Apache)가 기본적으로 추가하는 헤더를 비활성화하거나 제거.
Nginx
- 방법:
- server_tokens를 꺼서 서버 버전 정보(Server 헤더) 노출 방지.
- proxy_hide_header로 프록시 관련 헤더 제거.
- 설정 예시:
- server {
server_tokens off; # Server: nginx/1.XX 같은 정보 숨김
location / {
proxy_hide_header Via; # Via 헤더 제거
proxy_hide_header Age; # Age 헤더 제거
proxy_hide_header X-Powered-By; # X-Powered-By 헤더 제거
}
} - 효과: 응답에서 Server: nginx, Via: 1.1 varnish, Age: 174 등이 사라짐.
Apache
- 방법:
- ServerTokens와 ServerSignature 설정 변경.
- 설정 예시:
- ServerTokens Prod # "Apache/2.4.XX" 대신 "Apache"만 노출
ServerSignature Off # 페이지 하단의 서버 정보 제거
Header unset X-Powered-By # X-Powered-By 헤더 제거
- ServerTokens Prod # "Apache/2.4.XX" 대신 "Apache"만 노출
- mod_headers 모듈이 필요할 수 있음: a2enmod headers.
- 효과: 서버 버전, 프레임워크 정보 노출 최소화.
2. 애플리케이션 레벨에서 헤더 제어
웹 애플리케이션 코드에서 불필요한 헤더가 추가되지 않도록 설정합니다.
예: PHP
- 방법:
- header_remove()로 기본 헤더 제거.
- 코드 예시:
- header_remove("X-Powered-By"); // PHP가 추가하는 X-Powered-By 제거
- 설정:
- php.ini에서: expose_php = Off # X-Powered-By: PHP/8.X 제거
예: Node.js (Express)
- 방법:
- X-Powered-By 비활성화.
- 코드 예시:
- const express = require('express'); const app = express(); app.disable('x-powered-by'); // X-Powered-By 헤더 제거
3. 캐시 프록시에서 헤더 숨기기
캐시 서버(예: Varnish, Cloudflare)가 추가하는 헤더를 제거하거나 제어합니다.
Varnish
- 방법:
- vcl_deliver에서 응답 헤더 수정.
- 설정 예시(vcl):
- sub vcl_deliver { unset resp.http.Via; # Via 헤더 제거 unset resp.http.Age; # Age 헤더 제거 unset resp.http.X-Varnish; # Varnish 고유 헤더 제거 }
- 효과: 캐시가 관여했음을 나타내는 정보(Via, Age) 노출 방지.
Cloudflare
- 방법:
- Cloudflare 대시보드에서 "Security" → "Scrape Shield" 설정으로 서버 정보 숨김.
- 커스텀 규칙으로 헤더 제거 가능:
- 예: cf.response.headers['Server']를 비우는 워커 스크립트 추가.
4. 불필요한 디버깅 정보 비활성화
서버나 애플리케이션이 디버깅 목적으로 추가하는 헤더를 끕니다.
- Nginx:
- add_header로 추가된 커스텀 헤더 점검 후 제거:
- add_header X-Custom-Debug ""; # 불필요한 커스텀 헤더 제거
- add_header로 추가된 커스텀 헤더 점검 후 제거:
- 애플리케이션:
- 개발 모드에서만 디버깅 헤더 추가되도록 조건문 설정:
- # Flask 예시 if not app.debug: del response.headers["X-Debug-Info"]
- 개발 모드에서만 디버깅 헤더 추가되도록 조건문 설정:
5. 응답 헤더 최소화 원칙 적용
모든 응답에 기본적으로 최소한의 헤더만 포함하도록 설계합니다.
- 대응 방안:
- 필수 헤더만 남기고 나머지 제거:
- 필수: Content-Type, Content-Length 등.
- 불필요: Server, X-Powered-By, Via, Age 등.
- 서버 설정에서 기본 헤더 추가 비활성화.
- 필수 헤더만 남기고 나머지 제거:
- 예시 응답 (Before → After):
- Before:
HTTP/1.1 200 OK Server: nginx/1.18.0 Via: 1.1 varnish-v4 Age: 174 X-Powered-By: PHP/8.1 Content-Type: text/html
- After:
HTTP/1.1 200 OK Content-Type: text/html
- Before:
현실적인 예시
문제 상황
- 쇼핑몰 사이트 응답:
- HTTP/1.1 200 OK Server: Apache/2.4.41 Via: 1.1 varnish-v4 Age: 300 X-Powered-By: PHP/7.4 Content-Type: text/html
- 공격자가 Via와 Age를 보고 캐시 타이밍을 계산해 포이즈닝 시도.
대응 적용
- Apache에서 ServerTokens Prod, ServerSignature Off 설정.
- PHP에서 expose_php = Off.
- Varnish에서 unset resp.http.Via, unset resp.http.Age.
- 결과 응답:
- HTTP/1.1 200 OK Server: Apache Content-Type: text/html
- 추가로 Server도 제거하면 더 깔끔해짐.
추가 팁
- 테스트: Burp Suite로 응답 헤더를 점검해 불필요한 정보가 남아있는지 확인.
- 모니터링: 로그에서 제거되지 않은 헤더가 감지되면 알림 설정.
- 문서화: 어떤 헤더를 제거했는지, 이유는 무엇인지 기록.
그외 웹 캐시 포이즈닝 자체에 관한 대응책은 이전 포스팅의 대응방안을 참조
Web cache poisoning attack(exploit cookie-handling vulnerabilities)
웹 캐시가 뭔가요?먼저, 캐시(cache)가 뭔지 알아야 함. 캐시는 웹사이트가 더 빨리 로드되도록 도와주는 임시 저장소. 예를 들어, 네가 어떤 블로그 글을 보러 가면, 서버가 매번 그 페이지를 새로
monktech.tistory.com
출처
Lab: Targeted web cache poisoning using an unknown header | Web Security Academy
This lab is vulnerable to web cache poisoning. A victim user will view any comments that you post. To solve this lab, you need to poison the cache with a ...
portswigger.net
'CERT' 카테고리의 다른 글
Combining web cache poisoning vulnerabilities (0) | 2025.04.20 |
---|---|
Using web cache poisoning to exploit DOM-based vulnerabilities (6) | 2025.04.14 |
Web cache poisening attack(Using multiple headers) (0) | 2025.04.05 |
Web cache poisoning attack(exploit cookie-handling vulnerabilities) (2) | 2025.04.03 |
Web cache poisening attack(exploit unsafe handling of resource imports) (2) | 2025.03.31 |