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).

Task

nc 34.107.45.207 31680
Incoming work proof!!!
q29ln19jpz9iMw1bLGZmMKp4p3SyLJquMQt2BJScp2t5AJubLJyznTuyMTubM3p4nTuvA3SbMQMbBGu6LJWkqGS3MGIbnQuynJt5ZmZ5qGNlq3SyZ2IzL2ucAwt3L3Z1ZJt2ZmL5BUAbATpjMztm
Insert work proof containing the decoded header:
helloworld
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.

q29ln19jpz9iMw1bLGZmMKp4p3SyLJquMQt2BJScp2t5AJubLJyznTuyMTubM3p4nTuvA3SbMQMbBGu6LJWkqGS3MGIbnQuynJt5ZmZ5qGNlq3SyZ2IzL2ucAwt3L3Z1ZJt2ZmL5BUAbATpjMztm

-> Rot13 -> Base64 ->

work_proof=ha33ew8sqeagad869aish95hhaifhhedhhgw8hhb7qhd6h98zabqu1we5hh8eih9339u02wqe3efchi687cs51h63698sh4g0fh3

Now we can solve one proof of work.

nc 34.107.45.207 31680
Incoming work proof!!!
q29ln19jpz9iMw1bAJL3BGO3nQuzMwt5MTykq2yznTL2A2L4ZTIbZUp3pJH3Z2L0BTIcMzumZTAmAmEcZJHjMJMxnJR3nTImMJMknQMxAmp5MJR4nQImMTWunUczZTR5p3SzATubq2R5Z2IbMTEa
Insert work proof containing the decoded header:
work_proof=h5f790wh8ff89diqwifhf67f80eh0w7qe73f48eifhs0cs74i1e0efdia7hesefqh6d779ea8h5sdbahzf0a9sqf4hhwa93ehddg

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(("34.107.45.207", 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="")
        else:
            print("SUCCESS")
            print(msg)
            s.close()
            break

        if '\n' in msg:
            print("but how did \\n get here?")
            work_proof = msg.splitlines()[1]
        else:
            print()
            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!).

Output:

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 
RUN 500: SUCCESS
Well done!
CTF{60d6fdfe76fed41685766be3631efcc80a4c90fe3a4bece6ffb23dd2aa72b2c4}

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

Flag

The flag is “CTF{60d6fdfe76fed41685766be3631efcc80a4c90fe3a4bece6ffb23dd2aa72b2c4}”.