Exploit Education | Phoenix | Net Two Solution

The description and source code can be found here:
http://exploit.education/phoenix/net-two/

Only problem is, this time, there isn’t much of a description. We’ll have to look at the source code to figure this one out. Let’s break it down. First, it assigns a few variables and prints a message with a hint, that a “long int” is 8 bytes in size. Note that the “quad” variable is an unsigned long array with 8 elements:

int main(int argc, char **argv) {
  int i;
  unsigned long quad[sizeof(long)], result, wanted;

  setvbuf(stdout, NULL, _IONBF, 0);
  setvbuf(stderr, NULL, _IONBF, 0);
  printf("%s\nFor this level, sizeof(long) == %d, keep that in mind :)\n",
      BANNER, (int)sizeof(long));
...

Next, it’s saving a random value to the entire “quad” variable, which is 64 bytes in size (8 elements of 8 byte integers):

  if (getrandom((void *)&quad, sizeof(quad), 0) != sizeof(quad)) {
    errx(1, "unable to getrandom(%d bytes)", sizeof(quad));
  }

Then, it uses a for loop to add each element of “quad” together and save to the “result” variable. It also sends the value of each element to stdout:

  result = 0;
  for (i = 0; i < sizeof(long); i++) {
    result += quad[i];
    if (write(1, (void *)&quad[i], sizeof(long)) != sizeof(long)) {
      errx(1, "Why have you foresaken me, write()");
    }
  }

Lastly, it reads in a value from the input and saves it to the “wanted” variable. That is compared to the “result” variable and sends a message depending on the outcome:

  if (read(0, (void *)&wanted, sizeof(long)) != sizeof(long)) {
    errx(1, "Unable to read\n");
  }

  if (result == wanted) {
    printf("You have successfully passed this level, well done!\n");
  } else {
    printf("Whoops, better luck next time. Receieved %lu, wanted %lu\n", wanted,
        result);
  }

  return 0;
}

The objective seems pretty clear now. Accept a 64 byte value, break it into 8 byte chunks, sum them together, and send the result back (in little endian format). For me, there were two things that hung me up a little bit. First of all, the Python socket module’s “recv” method looks for a newline character (\n) to end the buffer. That character is not sent at the end of the 64 byte random value OR at the beginning. So I had to specify exactly how many bytes were in the message of the second line (24) and tell it to save only 64 bytes after that. Second, it’s important to note that in C, when two integers are added and their sum is greater than what the destination variable can hold, the overflown bits are ignored. In this case, eight 8-byte integers (64 bits each) are added together and usually results in an overflow, 66 bits for instance. The “result” variable is stored without those 2 extra bits.

Python does not have such limitations. It’s only limited by how much memory is available to it. So when writing the script, I decided to convert the sum into a string of binary bits and cut off any extra bits that may have resulted from the arithmetic using Python’s “list comprehension.”

#!/usr/bin/env python3

import socket
import time

IP = "127.0.0.1"
PORT = 64002

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((IP, PORT))

# Get the first 2 lines
print(s.recv(1024).decode(), end='')
time.sleep(0.1)
print(s.recv(24).decode())
msg = s.recv(64)

# The entire 64 bytes need to be split into 8 byte long integers & added
total = 0
for i in range(8):
    total += int.from_bytes(msg[i*8:(i+1)*8], byteorder='little')

# Convert the total into a string of at least 64 binary bits & only grab the last 64
bitval = "{0:064b}".format(total)[-64:]
# Convert the string of bits into an integer
intval = int(bitval, 2)
# Convert the integer back into bytes
byteval = intval.to_bytes(8, byteorder='little')

# Send the data as bytes
print("Sending: {}".format(intval))
s.send(byteval)

# Print the last message
print(s.recv(1024).decode())

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.