CTF Writeups
WebsiteDiscordSocials..
  • CryptoCat's CTF writeups
  • 2025
    • Tsuku
      • Web
        • len_len
        • Flash
        • YAMLwaf
    • CTF@CIT
      • Web
        • Breaking Authentication
        • Commit & Order: Version Control Unit
        • How I Parsed your JSON
        • Mr. Chatbot
        • Keeping Up with the Credentials
  • 2024
    • CryptoCat
      • Summar-AI-ze
    • Intigriti
      • Warmup
        • BabyFlow
        • In Plain Sight
        • IrrORversible
        • Layers
        • Rigged Slot Machine 1
      • Game
        • Bug Squash 1
        • Bug Squash 2
      • Rev
        • Secure Bank
      • Web
        • Biocorp
        • Cat Club
        • Pizza Paradise
        • SafeNotes 2.0
      • Misc
        • Quick Recovery
        • Triage Bot 2
      • Pwn
        • Floormat Sale
        • Retro2Win
        • Rigged Slot Machine 2
        • UAP
      • Crypto
        • Schrodinger's Pad
      • Mobile
        • Cold Storage
      • OSINT
        • No Comment
        • Trackdown
        • Trackdown 2
      • Forensics
        • CTF Mind Tricks
        • Hoarded Flag
        • Password Management
    • CSAW
      • Web
        • Playing on the Backcourts
        • Log Me In
        • Lost Pyramid
        • BucketWars
    • CyberSpace
      • Web
        • Feature Unlocked
    • UIU
      • Web
        • Fare Evasion
        • Log Action
    • Wani
      • Web
        • Bad Worker
        • PoW
        • One Day One Letter
    • Akasec
      • Web
    • HTB Cyber Apocalypse
      • Web
        • Flag Command
        • TimeKORP
        • KORP Terminal
        • Labyrinth Linguist
        • Locktalk
        • SerialFlow
        • Testimonial
  • 2023
    • Intigriti
      • Gamepwn
        • Dark Secrets
      • Misc
        • Triage Bot
      • OSINT
        • Photographs
      • Pwn
        • Floormat Store
      • Web
        • Bug Report Repo
        • My Music
    • Imaginary
      • Web
        • Blank
        • IDORiot
        • Inspection
        • Login
        • Perfect Picture
        • Roks
    • Google
      • Pwn
        • Write-Flag-Where
    • Cyber Apocalypse
      • AI
        • Last Hope
        • Mysterious Learning
      • Crypto
        • Perfect Synchronization
      • Pwn
        • Getting Started
        • Labyrinth
        • Pandora's Box
        • Void
      • Rev
        • Cave System
        • Hunting License
        • Needle in a Haystack
        • Shattered Tablet
        • She Sells Sea Shells
    • Sekai
      • Rev
        • Azusawa's Gacha World
    • Amateurs
      • Web
        • Sanity
        • Waiting an Eternity
    • NahamCon
      • Web
        • Hidden Figures
        • Marmalade 5
        • Obligatory
        • Star Wars
        • Stickers
    • Angstrom
      • Pwn
        • Leek
  • 2022
    • Imaginary (iCTF)
      • Crypto
        • ASE
      • Pwn
        • Links 1
        • Links 2
        • Links 3
        • Open Doors
    • SEETF
      • Pwn
        • 4mats
        • Easy Overflow
      • Rev
        • BabyReeee
      • Web
        • Super-Secure-Requests-Forwarder
    • HTB Cyber Apocalypse
      • Pwn
        • Hellbound
    • Angstrom
      • Pwn
        • Really Obnoxious Problem
        • Wah
        • Whats My Name
        • Where Am I
      • Web
        • Crumbs
        • Xtra Salty Sardines
    • NahamCon
      • Pwn
        • Baby Steps
      • Web
        • Flaskmetal Alchemist
        • Hacker Ts
        • Two for One
    • Pico
      • Forensics
        • Side Channel
      • Pwn
        • Buffer Overflow 1
        • Buffer Overflow 2
        • Buffer Overflow 3
        • Flag Leak
        • Function Overwrite
        • ROPfu
        • RPS
        • Stack Cache
        • Wine
        • X-Sixty-What
      • Rev
        • Wizardlike
      • Web
        • Noted
    • Space Heroes
      • Pwn
        • Vader
      • Web
        • Flag in Space
    • Intigriti
      • Pwn
        • Bird
        • Cake
        • Easy Register
        • Search Engine
    • Dice
      • Pwn
        • Interview Opportunity
  • 2021
    • Pico
      • Pwn
        • Unsubscriptions Are Free
    • Crusaders of Rust (COR)
      • Crypto
        • Fibinary
      • Pwn
        • Chainblock
    • HTB Cyber Santa
      • Crypto
        • Meet Me Halfway
        • Xmas Spirit
      • Pwn
        • Minimelfistic
        • Mr. Snowy
        • Naughty List
        • Sleigh
      • Rev
        • Infiltration
        • Intercept
    • K3rn3l
      • Crypto
        • Badseed
        • Twizzty Buzzinezz
    • HTB x Synack RedTeamFive
      • Misc
        • Context
        • Hotel
      • Pwn
        • Air Supplies
        • Injection Shot
        • Library
        • Recruitment
      • Rev
        • Knock Knock
        • Split
    • KillerQueen
      • Pwn
        • A Kind of Magic
        • Tweety Birb
        • Zoom2Win
    • HacktivityCon
      • Pwn
        • Retcheck
        • The Library
        • Yabo
      • Web
        • Availability
    • CSAW
      • Pwn
        • Alien Math
        • Password Checker
      • Rev
        • Checker
    • HackyHolidays
      • Crypto
        • Cute Invoice
        • Mineslazer
      • Forensics
        • Injection Traffic
        • Power Snacks
      • Pwn
        • Deleted Flag
        • Engine Control
      • Web
        • Skylark
    • HTB Cyber Apocalypse
      • Crypto
        • Phasestream
      • Misc
        • Alien Camp
        • Build Yourself In
      • Pwn
        • Controller
        • System Drop
      • Web
        • Blitzprop
        • E-Tree
        • Wild Goose Hunt
    • Angstrom
      • Pwn
        • Sanity Checks
        • Secure Login
        • Sticky Stacks
        • Tranquil
      • Rev
        • Free Flags
        • Jailbreak
      • Web
        • Jar
Powered by GitBook
On this page
  • Description
  • Source Code
  • Solution
  1. 2024
  2. CSAW
  3. Web

Log Me In

Writeup for Log Me In (Web) - CSAW CTF (2024) 💜

Description

I (definitely did not) have found this challenge in the OSIRIS recruit repository

Source Code

from flask import make_response, session, Blueprint, request, jsonify, render_template, redirect, send_from_directory
from pathlib import Path
from hashlib import sha256
from utils import is_alphanumeric
from models import Account, db
from utils import decode, encode

flag = (Path(__file__).parent / "flag.txt").read_text()

pagebp = Blueprint('pagebp', __name__)

@pagebp.route('/')
def index():
    return send_from_directory("static", 'index.html')

@pagebp.route('/login', methods=["GET", "POST"])
def login():
    if request.method != 'POST':
        return send_from_directory('static', 'login.html')
    username = request.form.get('username')
    password = sha256(request.form.get('password').strip().encode()).hexdigest()
    if not username or not password:
        return "Missing Login Field", 400
    if not is_alphanumeric(username) or len(username) > 50:
        return "Username not Alphanumeric or longer than 50 chars", 403
    # check if the username already exists in the DB
    user = Account.query.filter_by(username=username).first()
    if not user or user.password != password:
        return "Login failed!", 403
    user = {
        'username':user.username,
        'displays':user.displayname,
        'uid':user.uid
    }
    token = encode(dict(user))
    if token == None:
        return "Error while logging in!", 500
    response = make_response(jsonify({'message': 'Login successful'}))
    response.set_cookie('info', token, max_age=3600, httponly=True)
    return response

@pagebp.route('/register', methods=['GET', 'POST'])
def register():
    if request.method != 'POST':
        return send_from_directory('static', 'register.html')
    username = request.form.get('username')
    password = sha256(request.form.get('password').strip().encode()).hexdigest()
    displayname = request.form.get('displayname')
    if not username or not password or not displayname:
        return "Missing Registration Field", 400
    if not is_alphanumeric(username) or len(username) > 50:
        return "Username not Alphanumeric or it is longer than 50 chars", 403
    if not is_alphanumeric(displayname) or len(displayname) > 50:
        return "Displayname not Alphanumeric or it is longer than 50 chars", 403
    # check if the username already exists in the DB
    user = Account.query.filter_by(username=username).first()
    if user:
        return "Username already taken!", 403
    acc = Account(
        username=username,
        password=password,
        displayname=displayname,
        uid=1
        )
    try:
        # Add the new account to the session and commit it
        db.session.add(acc)
        db.session.commit()
        return jsonify({'message': 'Account created successfully'}), 201
    except Exception as e:
        db.session.rollback()  # Roll back the session on error
        return jsonify({'error': str(e)}), 500

@pagebp.route('/user')
def user():
    cookie = request.cookies.get('info', None)
    name='hello'
    msg='world'
    if cookie == None:
        return render_template("user.html", display_name='Not Logged in!', special_message='Nah')
    userinfo = decode(cookie)
    if userinfo == None:
        return render_template("user.html", display_name='Error...', special_message='Nah')
    name = userinfo['displays']
    msg = flag if userinfo['uid'] == 0 else "No special message at this time..."
    return render_template("user.html", display_name=name, special_message=msg)

@pagebp.route('/logout')
def logout():
    session.clear()
    response = make_response(redirect('/'))
    response.set_cookie('info', '', expires=0)
    return response

Solution

To get the flag, we need to visit the /user endpoint with our UID set to zero.

msg = flag if userinfo['uid'] == 0 else "No special message at this time..."

We can register an account and log in; notice how the UID is set?

user = {
	'username':user.username,
	'displays':user.displayname,
	'uid':user.uid
}
token = encode(dict(user))

It uses a custom encode function, imported from utils.py

def is_alphanumeric(text):
    pattern = r'^[a-zA-Z0-9]+$'
    if re.match(pattern, text):
        return True
    else:
        return False

def LOG(*args, **kwargs):
    print(*args, **kwargs, flush=True)

# Some cryptographic utilities
def encode(status: dict) -> str:
    try:
        plaintext = json.dumps(status).encode()
        out = b''
        for i,j in zip(plaintext, os.environ['ENCRYPT_KEY'].encode()):
            out += bytes([i^j])
        return bytes.hex(out)
    except Exception as s:
        LOG(s)
        return None

def decode(inp: str) -> dict:
    try:
        token = bytes.fromhex(inp)
        out = ''
        for i,j in zip(token, os.environ['ENCRYPT_KEY'].encode()):
            out += chr(i ^ j)
        user = json.loads(out)
        return user
    except Exception as s:
        LOG(s)
        return None

The JSON object is XORd with the key. Sounds like an OTP issue? If we encode two different user objects (c1 and c2) and then XOR them together, we should recover the key!

My first attempt at this failed; the username/display name probably needed to be longer (hinted at by the 50-char length limits). The problem was finding 50 char names that hadn't already been taken lol.

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB = 48674c3731025651282f614a4d5437132579332603202236367628351513723226782c30060a3939302a351b0e313339000f0b28190738107417743b0209702d535e551417281f1c2114361540494e6b36767573360e340e02122a25181b251370220a05280c0d0a083923112904280f3b247604247231760a25071523360c733330114a55604c0f02724d6e7027

BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAA = 48674c3731025651282f614a4d543410267a30250023213535752b3616107131257b2f3305093a3a332936180d32303a030c082b1a043b1377147738010a732e535e551417281f1c2114361540494e6b35757670350d370d011129261b182610732109062b0f0e090b3a20122a072b0c38277507277132750926041620350f703033114a55604c0f02724d6e7027
3E9DTp80EJCpmvvRd8rgBacww7itTR3sg9mqGKxxqktZOprxANJiXFyQ5V5zCH2oqru6sAllMuOfbsnIw742wOuOCSkdYZdR1cKDiMLKIxbPhEiNze7Ee3p7KdFTbwM2qr3fuB9ffPwN@Z
48674c3731025651282f614a4f3737132579332603202236367628351513723226782c30060a3939302a351b0e313339000f0b28190738107417743b020a704d5d50115f0031000d34066d5c40322f0836767573360e340e02122a25181b251370220a05280c0d0a083923112904280f3b247604247231760a25071523350f105d50460f116003561b

Update the cookie in the browser, then visit the /user endpoint and you will receive the flag!

Flag: csawctf{S3NS1T1V3_D4T4_ST0R3D_CL13NTS1D3D_B4D_B4D}

PreviousPlaying on the BackcourtsNextLost Pyramid

Last updated 4 months ago

I used CyberChef to :

Last step is to :

recover the key
generate a new (signed) cookie with the UID set to zero