Login Bruteforcing: Sockets and Pins

Today we'll go through the Login Bruteforce Module and I'll be translating some Python to C. Since I am interested in malware development, I thought it would be a cool exercise to translate the code they provide to C.

This translation will be done for two motives:

  1. Dust off my C knowledge

  2. See if C is faster than Python when it comes to HTTP requests

Translating Pin-solver.py

The following code is given to us by HTB Academy:

import requests

ip = "127.0.0.1"  # Change this to your instance IP address
port = 1234       # Change this to your instance port number

# Try every possible 4-digit PIN (from 0000 to 9999)
for pin in range(10000):
    formatted_pin = f"{pin:04d}"  # Convert the number to a 4-digit string (e.g., 7 becomes "0007")
    print(f"Attempted PIN: {formatted_pin}")

    # Send the request to the server
    response = requests.get(f"http://{ip}:{port}/pin?pin={formatted_pin}")

    # Check if the server responds with success and the flag is found
    if response.ok and 'flag' in response.json():  # .ok means status code is 200 (success)
        print(f"Correct PIN found: {formatted_pin}")
        print(f"Flag: {response.json()['flag']}")
        break

Let's break down some functionalities we need to do:

  • connect to the host or send a get request

  • receive the response and look for the flag

I'll use curl to see what the JSON looks like and give me an idea of what I'll be looking for:

Making a socket in c

I have made sockets before in Python for CTFs, but never in C. I am using Beej's Guide and the book Hacking, The Art of Exploitation. The connection to the socket will look like:

Now, to explain the shenanigans I'm doing.

  1. int_connect_my_socket() receives a string and an integer, an IP, and a port.

  2. sockfd is a socket file descriptor. A file descriptor is like a hospital wristband that holds your information. You know how you go to the hospital and they put a wristband on you with your name and a funny number on it? Then they use this wristband to apply any treatment you need. They're like: "perform_test(#231456, X-Rays)". This is what file descriptors do. They identify a file or a socket in this case. This is because, in UNIX systems, input and output is done through files. So this line declares the variable where we'll store our file descriptor.

  3. struct sockaddr_in host_addr this structure handles internet access

  4. (sockfd = socket(AF_INET, SOCK_STREAM, 0)) we're declaring the socket to use IPv4 Internet Protocols (AF_INET), be a steam socket (a.k.a provide two-way communication, like a phone call) and the protocol to use with the socket. There are several protocols but we usually put 0 because there is usually just a single protocol.

  5. host_addr.* is defining the address family, port, and host. htons changes the byte ordering and inet_addr turns the host from IPv4 to a value that'll be used as an Internet Address.

  6. memset is setting everything in sin_zero to \0 values to ensure there is no garbage or random stuff in the padding.

  7. bind() assigns the address and the host to the file descriptor

  8. Finally, we test the connection. If it's successful, we return the file descriptor.

If we were doing this in Python, it would look in fact, very similar (much more readable):

And now, we can use the code. My code looks something like this:

And we get on with the hacking

Troubleshooting Interlude

If you observe the code, you'll see I'm starting a connection at the beginning of each iteration and closing it at the end. Do you wonder what I did and spam GET requests in a single connection? If you do, you're welcome to stay and read this bit, else, I'll continue with the benchmarking in the next header.

I tried connecting before the loops. I'll attach the Python code I used to troubleshoot this:

I used python because I was having trouble running Wireshark and WSL at the same time. If Wireshark was capturing, WSL would lose it's internet conection and therefore no requests would go out. This was fixed by running WSL in admin mode.

I wondered why I could not do this. It was a very nice and very demure way of doing this, spamming GET requests would have been so fast. But because I would only get one JSON back, I opened Wireshark and saw the following:

The key lies in the server's response. Every time I get the response, the connection is set to close. It's like knocking on someone's door and the owner opening the door only to tell you not to come back and close it back to your face. If you keep knocking, you won't get any response. In fact, Wireshark shows packets with the RSTflag, telling us that the port is closed.

Which makes the next section a bit predictable.

Benchmarking: Python vs. C

Finally, we run both codes. This is how a sample output would look like:

For C:

For Python:

After running the codes several times, the differences were always by seconds. Sometimes Python would be faster than C by 5 to 30 seconds, and sometimes it would be the other way around. In the end of the day, there is no difference because the overhead is in the connection to the server and that is the same both in Python and C.


Thank you for reading! This is it for today, next post will go through dictionary attacks in both bash and PowerShell. If you have any comments or would like to connect, I am always eager to chat on my linkedin

Last updated