CrossCTF_2018: Fitblips

Category: Crypto Points: 50 Description:

How many steps does your Fitblip beep? nc ctf.pwn.sg 4003 Creator - amon (@nn_amon)

Write-up

Firstly, let's look at the source code.

#!/usr/bin/env python

import sys
import flag
from bitstring import BitArray
import time
import signal


def write(data, endl='\n'):
    sys.stdout.write(data + endl)
    sys.stdout.flush()


def readline():
    return sys.stdin.readline().strip()


def convert_to_bitstream(data):
    return BitArray(bytes=data).bin


def check(a, b, user_times):
    bs_a = convert_to_bitstream(a)
    bs_b = convert_to_bitstream(b)
    bs_a = bs_a.ljust(len(bs_b), "0")
    bs_b = bs_b.ljust(len(bs_a), "0")
    counter = 0
    for i in range(len(bs_a)):
        if bs_a[i] != bs_b[i]:
            return counter
        counter += 1
    return counter


def main():
    signal.alarm(4)

    secret_key = flag.flag
    write(open(__file__).read())
    write("Password: ", endl="")
    user_supplied = readline()
    write("How many times do you want to test: ", endl="")
    user_times_supplied = readline()
    try:
        int(user_supplied, 16)
        user_data = user_supplied.decode("hex")
        user_times = int(user_times_supplied)
    except Exception:
        write("Evil.")
        return

    if user_times > 5000:
        write("Too many times.")
        return

    result = len(flag.flag) * 8 * user_times
    start = time.time()
    for i in range(user_times):
        result -= check(user_data, secret_key, user_times)
    end = time.time()
    elapsed = end - start

    if result == 0:
        write("Flag is %s" % flag.flag)
    else:
        write("Impossible.")

    write("Request completed in: %.4fs (%d)" % (elapsed, result))


if __name__ == "__main__":
    main()

Let's see what the program responds with when we connect to it on the socket

$ nc ctf.pwn.sg 4003
Password: FF
How many times do you want to test: 1
Impossible.
Request completed in: 0.0001s (304)

Let's see if we can get a flag by timing attack!

#! /usr/bin/env python3
##
import string
import queue
import threading
import binascii
from pwn import *


flag = ""


def connect(q, r):
    while True:
        attempt = q.get()

        while True:
            try:
                with context.local(log_level="critical"):
                    t = remote("ctf.pwn.sg", 4003)

                t.recvuntil("\n\nPassword: ")
                t.sendline(attempt)
                t.recvuntil("test: ")
                t.sendline("1")
                t.recvuntil("(")
                result = int(t.recvuntil(")")[:-1])
                r.put((attempt, result))

                with context.local(log_level="critical"):
                    t.close()

                break
            except Exception as e:
                print(e)
                pass

        q.task_done()


q = queue.Queue()
r = queue.Queue()
threads = []

for i in range(16):
    thread = threading.Thread(target=connect, args=(q, r))
    thread.daemon = True
    thread.start()
    threads.append(thread)

while True:
    threads = []

    for c in string.printable:
        attempt = flag + c
        formatted = binascii.hexlify(attempt.encode("latin-1")).decode()
        q.put(formatted)

    best_attempt = ""
    best_result = 9999999

    for i in range(len(string.printable)):
        attempt, result = r.get()

        if result < best_result:
            best_result = result
            best_attempt = attempt
            log.info((best_result, best_attempt))

        r.task_done()

    flag += chr(int(best_attempt[-2:], 16))
    log.success(f"FLAG: {flag}")

An alternative solution found after the CTF involves testing 0 times.

Password: AA
How many times do you want to test: 0
Flag is CrossCTF{t1m1ng_att4ck5_r_4_th3_d3vil}
Request completed in: 0.0000s (0)

Therefore, the flag is CrossCTF{t1m1ng_att4ck5_r_4_th3_d3vil}.

results matching ""

    No results matching ""