fast-proof is a misc challenge that was part of the 2022 DefCamp CTF qualifiers. To receive the flag we have to decode the proof of work (fast).


nc 31680
Incoming work proof!!!
Insert work proof containing the decoded header:
Wrong work proof!

To find the decryption method we can use CyberChef. The “magic”-“Recipe” told us that the “Rot13”- and “Base64”-“Recipes” were used to decrypt the string.


-> Rot13 -> Base64 ->


Now we can solve one proof of work.

nc 31680
Incoming work proof!!!
Insert work proof containing the decoded header:

But can’t receive the flag. The connection closes after we send the proof of work. Maybe we have to solve multiple proofs of work (fast) to receive the flag.

To do this we had to write a script:

import socket
import codecs
import base64

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:

    print("Connecting to server...")
    s.connect(("", 31680))
    print("Connected to server!")

    num = 0
    while True:
        num += 1
        print(f"RUN {num}: ", end="")

        msg = s.recv(1024).decode().strip()

        if "Incoming work proof!!!" in msg:
            print("OK ", end="")
        elif "Insert work proof containing the decoded header:" in msg:
            print("WTF! ", end="")

        if '\n' in msg:
            print("but how did \\n get here?")
            work_proof = msg.splitlines()[1]
            work_proof_and_text = s.recv(1024).decode().strip()
            work_proof = work_proof_and_text.splitlines()[0]

        rot13 = codecs.encode(work_proof, 'rot_13')
        fromBase64 = base64.b64decode(rot13)
        s.send(fromBase64 + b"\n")

The real challenge is to filter all the “noise” from the server to get the correct strings:

  • msg: “Incoming work proof!!!”

  • work_proof_and_text: q29ln19...2IbMTEa\nInsert work proof containing the decoded header: (separated by a newline)

  • work_proof: q29ln19...2IbMTEa

Sometimes the server doesn’t send the newlines and we have to filter them out. The script can handle most cases. But if the server sends all rows in one chunk, the script will fail (WTF!).


Connecting to server...
Connected to server!
RUN 1: OK 
RUN 2: OK 
RUN 3: OK 
RUN 4: OK 
RUN 5: OK 
RUN 6: OK 
RUN 7: OK 
RUN 8: OK 
RUN 9: OK 
RUN 10: OK 
RUN 11: OK 
RUN 12: OK 
RUN 13: OK 
RUN 14: OK 
RUN 15: OK 
RUN 16: OK 
RUN 17: OK 
RUN 18: OK but how did \n get here?
RUN 19: OK but how did \n get here?
RUN 20: OK 
RUN 490: OK 
RUN 491: OK 
RUN 492: OK 
RUN 493: OK 
RUN 494: OK 
RUN 495: OK 
RUN 496: OK 
RUN 497: OK but how did \n get here?
RUN 498: OK 
RUN 499: OK 
Well done!

Another problem was that IP and port were changed several times without notice.


The flag is “CTF{60d6fdfe76fed41685766be3631efcc80a4c90fe3a4bece6ffb23dd2aa72b2c4}”.