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