Protostar introduces the following in a friendly way:
- Network programming
- Byte order
- Handling sockets
- Stack overflows
- Format strings
- Heap overflows
Net0
This level takes a look at converting strings to little endian integers. This level is at /opt/protostar/bin/net0
Source Code
#include "../common/common.c"
#define NAME "net0"
#define UID 999
#define GID 999
#define PORT 2999
void run()
{
unsigned int i;
unsigned int wanted;
wanted = random();
printf("Please send '%d' as a little endian 32bit int\n", wanted);
if(fread(&i, sizeof(i), 1, stdin) == NULL) {
errx(1, ":(\n");
}
if(i == wanted) {
printf("Thank you sir/madam\n");
} else {
printf("I'm sorry, you sent %d instead\n", i);
}
}
int main(int argc, char **argv, char **envp)
{
int fd;
char *username;
/* Run the process as a daemon */
background_process(NAME, UID, GID);
/* Wait for socket activity and return */
fd = serve_forever(PORT);
/* Set the client socket to STDIN, STDOUT, and STDERR */
set_io(fd);
/* Don't do this :> */
srandom(time(NULL));
run();
}
The net0 process listens on port 2999. Let us interact with it using netcat.
user@protostar:/opt/protostar/bin$ clear
user@protostar:/opt/protostar/bin$ nc localhost 2999
Please send '827889081' as a little endian 32bit int
^C
user@protostar:/opt/protostar/bin$ nc localhost 2999
Please send '1477868572' as a little endian 32bit int
827889081
I'm sorry, you sent 943141432 instead
user@protostar:/opt/protostar/bin$
On connecting to port 2999, the net0 process sends a text containing a number. We are expected to send back the number in little endian 32 bit.
Solution
Below is what needs to be done in order to solve this challenge.
- Connect to target process on port 2999.
- Read the text sent by net0.
- Identify the number to be sent back.
- Convert the number to little endian 32 bit.
- Send back the result.
- Read the response.
Below is the net0 solution written in python.
# Author : RIZAL MUHAMMED [UB3RSiCK]
# Desc : Exploit Exercises Protostar - Net0 Solution
from socket import create_connection as cc
import struct
import sys
import re
try:
con = cc(('localhost', 2999))
except:
print '[-] Connection Failed'
sys.exit(0)
print '[*] Connected'
dat = con.recv(1024)
# Get the wanted number from the received string - enclosed within single quotes
wanted_num = re.findall(r"'(.*?)'", dat, re.DOTALL)[0]
print '[*] Received data \n\t', dat
print '[*] Number to send back in little endian 32 bit', wanted_num
# convert int to little endian
res = struct.pack("<I", int(wanted_num))
print '[*] Sending back result', repr(res)
con.send(res)
print '[*] Reading response from server'
print '\t', con.recv(1024)
Terminal Output
user@protostar:/opt/protostar/bin$ python /tmp/net0.py
[*] Connected
[*] Received data
Please send '312988072' as a little endian 32bit int
[*] Number to send back in little endian 32 bit 312988072
[*] Sending back result '\xa8\xd1\xa7\x12'
[*] Reading response from server
Thank you sir/madam
Net1
This level tests the ability to convert binary integers into ascii representation. This level is at /opt/protostar/bin/net1
Source Code
#include "../common/common.c"
#define NAME "net1"
#define UID 998
#define GID 998
#define PORT 2998
void run()
{
char buf[12];
char fub[12];
char *q;
unsigned int wanted;
wanted = random();
sprintf(fub, "%d", wanted);
if(write(0, &wanted, sizeof(wanted)) != sizeof(wanted)) {
errx(1, ":(\n");
}
if(fgets(buf, sizeof(buf)-1, stdin) == NULL) {
errx(1, ":(\n");
}
q = strchr(buf, '\r'); if(q) *q = 0;
q = strchr(buf, '\n'); if(q) *q = 0;
if(strcmp(fub, buf) == 0) {
printf("you correctly sent the data\n");
} else {
printf("you didn't send the data properly\n");
}
}
int main(int argc, char **argv, char **envp)
{
int fd;
char *username;
/* Run the process as a daemon */
background_process(NAME, UID, GID);
/* Wait for socket activity and return */
fd = serve_forever(PORT);
/* Set the client socket to STDIN, STDOUT, and STDERR */
set_io(fd);
/* Don't do this :> */
srandom(time(NULL));
run();
}
Solution
Below is what needs to be done in order to solve this challenge.
- Connect to target process on port 2998.
- Read in data sent by net0.
- Unpack the data and convert to unsigned int.
- Send back the result in string format.
- Read the response.
Below is the net1 solution written in python.
# Author : RIZAL MUHAMMED [UB3RSiCK]
# Desc : Exploit Exercises Protostar - Net1 Solution
from socket import create_connection as cc
import struct
import sys
try:
con = cc(('localhost', 2998))
except:
print '[-] Connection Failed'
sys.exit(0)
print '[*] Connected to localhost on port 2998'
dat = con.recv(1024)
# int to unsigned int
unpacked_dat = struct.unpack("<I", dat)[0]
print '[*] Received data \n\t', repr(dat)
print '[*] Unpacked unsigned data', unpacked_dat
# Convert the unpacked data to string
res = str(unpacked_dat)
print '[*] Sending back result', res
con.send(res)
print '[*] Reading response from server'
print '\t', con.recv(1024)
Terminal Output
user@protostar:/opt/protostar/bin$ python /tmp/net1.py
[*] Connected to localhost on port 2998
[*] Received data
'$y*o'
[*] Unpacked unsigned data 1865054500
[*] Sending back result 1865054500
[*] Reading response from server
you correctly sent the data
user@protostar:/opt/protostar/bin$
Net2
This code tests the ability to add up 4 unsigned 32-bit integers.
Hint: Keep in mind that it wraps.
This level is at /opt/protostar/bin/net2
Source Code
#include "../common/common.c"
#define NAME "net2"
#define UID 997
#define GID 997
#define PORT 2997
void run()
{
unsigned int quad[4];
int i;
unsigned int result, wanted;
result = 0;
for(i = 0; i < 4; i++) {
quad[i] = random();
result += quad[i];
if(write(0, &(quad[i]), sizeof(result)) != sizeof(result)) {
errx(1, ":(\n");
}
}
if(read(0, &wanted, sizeof(result)) != sizeof(result)) {
errx(1, ":<\n");
}
if(result == wanted) {
printf("you added them correctly\n");
} else {
printf("sorry, try again. invalid\n");
}
}
int main(int argc, char **argv, char **envp)
{
int fd;
char *username;
/* Run the process as a daemon */
background_process(NAME, UID, GID);
/* Wait for socket activity and return */
fd = serve_forever(PORT);
/* Set the client socket to STDIN, STDOUT, and STDERR */
set_io(fd);
/* Don't do this :> */
srandom(time(NULL));
run();
}
Solution
The net0 process sends back four 32 bit numbers in little endian. We are expecteed to send back their sum. Below is what needs to be done in order to solve this challenge.
- Connect to target process on port 2997.
- Read in the 4 numbers (they are in little endian).
- Unpack the 4 numbers to unsigned integer.
- Add the four numbers (handle wrapping as well).
- Send back the result.
- Read the response.
Below is the solution for net2.
# Author : RIZAL MUHAMMED [UB3RSiCK]
# Desc : Exploit Exercises Protostar - Net2 Solution
from socket import create_connection as cc
import struct
import sys
# Handle wrapping when adding
M32 = 0xffffffffL
def m32(n):
return n & M32
def madd(a, b):
return m32(a+b)
try:
con = cc(('localhost', 2997))
except:
print '[-] Connection Failed'
sys.exit(0)
print '[*] Connected to localhost on port 2997'
dat_list = []
# Read the four numbers
for i in range(4):
dat = con.recv(1024)
dat_list.append(dat)
print '[*] Received Data ', dat_list
# unpack the data from int to unsigned int
unpacked_dat_list = [struct.unpack("<I", item)[0] for item in dat_list]
sum = 0
print '[*] Unpacked unsigned integer data ', unpacked_dat_list
for item in unpacked_dat_list:
sum = madd(sum, item)
print '[*] Final added sum : ', sum
res = str(struct.pack("<I", sum))
print '[*] Sending sum [string, little endian] to server : ', repr(res)
con.send(res)
print '[*] Response from server : \033[92m', con.recv(1024), '\033[0m'
Terminal Output
user@protostar:/opt/protostar/bin$ python /tmp/net2.py
[*] Connected to localhost on port 2997
[*] Received Data ['\xd6\xe4e\x1a', '\xd3\x94k\x17', '\xf0mN\x05', '\xc2\x1b\xaa9']
[*] Unpacked unsigned integer data [442885334, 392926419, 89026032, 967449538]
[*] Final added sum : 1892287323
[*] Sending sum [string, little endian] to server : '[\x03\xcap'
[*] Response from server : you added them correctly
user@protostar:/opt/protostar/bin$
Net3
This level tests the ability to understand code, and implement a simple network protocol. This level is at /opt/protostar/bin/net3
Source Code
#include "../common/common.c"
#define NAME "net3"
#define UID 996
#define GID 996
#define PORT 2996
/*
* Extract a null terminated string from the buffer
*/
int get_string(char **result, unsigned char *buffer, u_int16_t len)
{
unsigned char byte;
byte = *buffer;
if(byte > len) errx(1, "badly formed packet");
*result = malloc(byte);
strcpy(*result, buffer + 1);
return byte + 1;
}
/*
* Check to see if we can log into the host
*/
int login(unsigned char *buffer, u_int16_t len)
{
char *resource, *username, *password;
int deduct;
int success;
if(len < 3) errx(1, "invalid login packet length");
resource = username = password = NULL;
deduct = get_string(&resource, buffer, len);
deduct += get_string(&username, buffer+deduct, len-deduct);
deduct += get_string(&password, buffer+deduct, len-deduct);
success = 0;
success |= strcmp(resource, "net3");
success |= strcmp(username, "awesomesauce");
success |= strcmp(password, "password");
free(resource);
free(username);
free(password);
return ! success;
}
void send_string(int fd, unsigned char byte, char *string)
{
struct iovec v[3];
u_int16_t len;
int expected;
len = ntohs(1 + strlen(string));
v[0].iov_base = &len;
v[0].iov_len = sizeof(len);
v[1].iov_base = &byte;
v[1].iov_len = 1;
v[2].iov_base = string;
v[2].iov_len = strlen(string);
expected = sizeof(len) + 1 + strlen(string);
if(writev(fd, v, 3) != expected) errx(1, "failed to write correct amount of bytes");
}
void run(int fd)
{
u_int16_t len;
unsigned char *buffer;
int loggedin;
while(1) {
nread(fd, &len, sizeof(len));
len = ntohs(len);
buffer = malloc(len);
if(! buffer) errx(1, "malloc failure for %d bytes", len);
nread(fd, buffer, len);
switch(buffer[0]) {
case 23:
loggedin = login(buffer + 1, len - 1);
send_string(fd, 33, loggedin ? "successful" : "failed");
break;
default:
send_string(fd, 58, "what you talkin about willis?");
break;
}
}
}
int main(int argc, char **argv, char **envp)
{
int fd;
char *username;
/* Run the process as a daemon */
background_process(NAME, UID, GID);
/* Wait for socket activity and return */
fd = serve_forever(PORT);
/* Set the client socket to STDIN, STDOUT, and STDERR */
set_io(fd);
/* Don't do this :> */
srandom(time(NULL));
run(fd);
}
Solution
The net3 process reads from socket a value and converts the unsigned short integer netshort from network byte order to host byte order - the ntohs() function. This value is saved in a variable named len.
nread(fd, &len, sizeof(len));
len = ntohs(len);
buffer = malloc(len);
Then, len amount of memory is allocated for buffer in heap using malloc(). After this, the process again reads from socket len amount of data and stores in buffer.
The process then checks if the first element, ie buffer[0] is 23 or 0x17. If the value is 0x17, then the login() function is invoked with parameters buffer[1:] and (len-1).
buffer[1:] - buffer starting from first element. The zeroth value, ie the 0x17 is avoided. len-1 is the lenth of the buffer now.
The login function checks the received buffer for the presence of three null terminated strings. Each should be prepended with their respective lengths, taking into consideration the null byte at the end.
\x05net3\x00 \x0dawesomesauce\x00 \x09password\x00
Below is the net3 solution.
# Author : RIZAL MUHAMMED [UB3RSiCK]
# Desc. : Exploit Exercises Protostar - Net3 Solution
from socket import create_connection as cc
import sys
import struct
try:
con = cc(('localhost', 2996))
except:
print '[-] Connection Failed'
sys.exit(0)
print '[*] Connected to localhost on port 2996'
login_string = '\x17'
login_string += '\x05net3\x00'
login_string += '\x0dawesomesauce\x00'
login_string += '\x09password\x00'
login_len = len(login_string)
print '[*] Login string : ', repr(login_string)
print '[*] Login string length : ', login_len
print '[*] Sending Login string length'
# ! - network (= big-endian) /
# > - big-endian
# H - unsigned short
# Either !H or >H would work
con.send(struct.pack('!H', login_len))
print '[*] Sending Login string'
con.send(login_string)
print '[*] Response from net3 : ', con.recv(1024)
Terminal Output
user@protostar:/opt/protostar/bin$ python /tmp/net3.py
[*] Connected to localhost on port 2996
[*] Login string : '\x17\x05net3\x00\rawesomesauce\x00\tpassword\x00'
[*] Login string length : 31
[*] Sending Login string length
[*] Sending Login string
[*] Response from net3 :
!successful
user@protostar:/opt/protostar/bin$