Net 2
Write-up for: https://exploit.education/phoenix/net-two/.
The challenge
This level is similar to the previous one, only a little more tricky. We can connect on port 64002
to examine the service manually:
user@phoenix-amd64:~$ nc 127.0.0.1 64002
Welcome to phoenix/net-two, brought to you by https://exploit.education
For this level, sizeof(long) == 8, keep that in mind :)
_>K70%!($v12345678
Whoops, better luck next time. Receieved 4050765991979987505, wanted 8877065176632751666
This time, the service requires us to send a fairly large number. Since this number is not immediately linked to the bytes we see, let’s look at the code.
unsigned long quad[sizeof(long)], result, wanted;
getrandom((void *)&quad, sizeof(quad), 0) != sizeof(quad)
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()");
}
}
These are the key parts. The program generates 8 random long values and sums them up in result
. But what is printed to the service is each individual value. Thus, our script must replicate this behavior: reading 8 long values from the service, summing them up and send them back in the right byte order. Here we go:
import struct
import socket
HOST = "127.0.0.1"
PORT = 64002
def read_line_from_sock(socket):
buf = ""
while True:
b = s.recv(1)
if b == b'\n':
break
buf += b.decode("ascii")
return buf
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
print(read_line_from_sock(s))
print(read_line_from_sock(s))
query = s.recv(8*8)
to_send = 0
print("raw query: " + str(query))
print("long values received")
for i in range(0, len(query), 8):
to_send += struct.unpack("Q", query[i:i+8])[0]
print(struct.unpack("Q", query[i:i+8])[0])
print("sum: " + str(to_send))
to_send &= 0xffffffffffffffff
print("without overflow: " + str(to_send))
s.sendall(struct.pack('Q', to_send))
print(read_line_from_sock(s))
Again, we start by reading the banner fully using the read_line_from_sock
function. Then we read 64 (8 * 8) bytes, representing long values. In a loop, we unpack each value individually using the format specifier Q
for unsigned long
and sum them up in to_send
. Last but not least, we need to account for a potential integer overflow of the sum within the service, we AND
the sum with a 8-byte mask, removing any leading bytes. Then, we send it all back to the service.
Remember, integers can overflow when summed up:
#include <stdio.h>
#include <limits.h>
int main() {
unsigned i = UINT_MAX / 2 + 100;
unsigned j = i;
unsigned x = i + j;
printf("%d\n", x);
return 0;
}
~ » gcc test.c
~ » ./a.out
198
Now, let’s run our script.
user@phoenix-amd64:~$ python3 net2.py
Welcome to phoenix/net-two, brought to you by https://exploit.education
For this level, sizeof(long) == 8, keep that in mind :)
raw query: b'\xf0\xe8Jp\x05.v\xec\x97LC\xb6\xfa\x90MI\x8e\x15K\xceR\x90\x01\x87\x01\x80\xfc\xc0F\xd0\xe4\xbc\x0e\x172\xf1\xe82D\xed5G\xac#\x11\xd2\rB\xb3Q?\xaa-7y\xb3\xb8\xa2k\x94\xa7\xd1\xc7\x86'
long values received
17038856841096521968
5282037344449547415
9728215355419727246
13611232976124542977
17096846061465638670
4759691352255252277
12932428474240422323
9711961639127589560
sum: 90161270044179242436
without overflow: 16374293749341035972
You have successfully passed this level, well done!
Done!