Knocker 2017 CTF | RC3 (half) Writeup

Let me start by saying that I could not finish this challenge. I started doing it too late at night the last day, and time ran out. But I got left with a very nice half-way script and the first two letters of the flag.

So let's proceed.

First you were given a simple command:

nc 18.216.59.235 7747

Ecpc right? You just had to start a new connection to the server and through that port, which greeted you with this promt:

b'Welcome to the third annual short python challenge.\n'
b"You'll get one letter at a time and a new problem to solve each time. Left vague on purpose. You'll figure it out. Hit enter when you're ready..."

Followed by a letter and a (usually) five digit number that could vary in presentation from connection to connection. Let me explain.
Maybe you could be given the number (which is the 'new problem to solve') in an int format, like 20740, other times you were given the number in a writen format, as in fifty-two thousand and twenty-nine, and lastly you could be given the number in a math operations format (from the python math library), like trunc(( -222 * -255) | 196) % 65535. The operation could be either a trunc, floor or ceil. I did not see more.

I did spend a lot of time trying to figure out why on every single connection, only the letter R was given but the number changed all the time. The it hit me.
What's something that could be related to netcat that also has a theoretical limit of 65535?

Boop.

Port numbers.

Those mystery numbers were port numbers, and they were opened and closed in a matter of seconds after the first connection. The challenge consisted on connecting to those port numbers and grabbing the next letter of the flag and also grabbing the next port number, which could be in any format of those three specified.

So then I started writing my script. However, the conversion of letters to integers made it too difficult for me, and I spent a lot of time making it work, only having a bit of luck sometimes and getting the second letter, but no more.

I wrote a script that can connect to the server, enter the 'enter' command, print the letter and connect to the next port (only while it's an integer) and print the server response from that one.

Maybe in the future this script will be useful for someone somewhere :)

# - port-knocker.py
# This script will connect to an IP address and a port number, then reconnect to
#   the next port number thats given from the current connection

import math, socket, time

host = '18.216.59.235'
port = 7747
client = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
client.connect(( host, port ))

# hardcoded af
time_end = time.time() + 0.3 * 1

def tcp_client():
    
    # only prints the first line of the response, so i'm grabbing all response
    # by creating a loop
    while time.time() < time_end:
        response = client.recv( 65535 )
        print ( response )
        
    enter = '\r'
    space = '\n'
    percentage = '%'
    
    # server will only accept byte-type input, so we convert it
    enter_as_bytes = str.encode( enter )
    space_as_bytes = str.encode( space )
    
    client.send( enter_as_bytes )
    
    time.sleep(1)
    
    server_response = client.recv( 65535 )
    print ( server_response )
    ports = server_response.split( space_as_bytes )
    
    # imma get rid to the b letter that wraps every goddarn response
    port_number = bytes.decode( ports[1] )

    # time to find out if it's a string, operation or number
    try:
        int( port_number )
        int_port( port_number )
    except ValueError:
        if percentage in port_number:
            op_port( port_number )
        else:
            str_port( port_number )
    

def int_port( port_number ):
    
    port = int( port_number )
    
    client = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
    client.connect(( host, port ))
        
    while time.time() < time_end:
        response = client.recv( 65535 )
        print ( response )

def str_port( port_number ):
    print ('im a string')

def op_port( port_number ):
    print ('im an op')


if __name__=='__main__':
    tcp_client()


Comments

Popular Posts