HeatKeeb
Description
CTF: STACK the Flags 2022
Jaga had gained interest in custom keyboards and has created a platform to create your own keebs! We know we created his custom keeb on the 22nd of September 2022, at 09:41:17 SGT. Oddly specific but we know it’s true.
misc_heatkeeb.zip
Solution
Pwned by @skytect and @fishjojo1
Let’s open up the webpage.

It seems that we need a token. Let’s check out the source code.
# app.py L98–L135@app.post("/build")def build( request: Request, name: str = Form(...), frameColor: str = Form(...), keyColor: str = Form(...), textColor: str = Form(...), specialColor: str = Form(...),): t = datetime.datetime.now(pytz.timezone("Asia/Singapore")) seed = int(t.timestamp()) random.seed(seed) token = "".join( random.choices( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", k=16 ) ) with shelve.open("keebdb") as db: db[token] = { "name": name, "frameColor": hex_to_rgb(frameColor), "keyColor": hex_to_rgb(keyColor), "textColor": hex_to_rgb(textColor), "specialColor": hex_to_rgb(specialColor), "text": "default", } img = draw_keeb( name, hex_to_rgb(frameColor), hex_to_rgb(keyColor), hex_to_rgb(textColor), hex_to_rgb(specialColor), ) img.save(f"keebs/keeb-{token}.png") request.session["token"] = token return templates.TemplateResponse( "build.html", {"request": request, "resp": "Success!", "token": token} )
The seed is based on the time :0 Let’s try to generate the seed based on the time given in the description.
import datetimeimport pytzimport random
t = datetime.datetime(2022, 9, 22, 9, 41, 17, 0)seed = int(t.timestamp())random.seed(seed)token = "".join( random.choices( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", k=16 ))print(token) # rMwwbpMkzAwyRoWs
Using the seed, we can access Jaga’s keeb!

Let’s go back to the code to find where the flag is.
# app.py L237–L251@app.post("/text")def flag(request: Request, text: str = Form(...)): if "token" in request.session: with shelve.open("keebdb") as db: token = request.session["token"] if token in db: if token == ADMIN_TOKEN and text.upper() == KEY: return templates.TemplateResponse( "flag.html", {"request": request, "word": text, "flag": FLAG} ) elif text.upper() == db[token]["text"]: return templates.TemplateResponse( "flag.html", {"request": request, "word": text} ) return templates.TemplateResponse("flag.html", {"request": request})
It seems that we need to key the matching word in the following text field somehow.

If we explore further around the GUI, we find that we can generate a heatmap of the word.

Looking at our keyboard, the hotspots appear to make out the letters asertghnil
. Let’s try to unscrable the letters.

When we key in earthlings
to check the latest favourite word, we get the flag!
STF22{h34t_k3yb04rD}