First, we're given the following 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);
}
Now because my C is a bit rusty, and I didn't always understand what was going on well, I re-wrote some of it to look like this:
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
int get_string(char **result, unsigned char *buffer, u_int16_t len)
{
unsigned char byte;
byte = *buffer;
printf("[*] Byte as hex: %x\n", byte);
if(byte > len)
{
printf("[*] Culprit: %s. %x is greater than %x\n", &buffer, byte, len);
errx(1, "badly formed packet");
}
*result = malloc(byte);
strcpy(*result, buffer + 1);
printf("[*] Returning: %i\n", byte+1);
return byte + 1;
}
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);
printf("Resource: %s\n", resource);
printf("Username: %s\n", username);
printf("Password: %s\n", password);
success = 0;
success |= strcmp(resource, "net3");
printf("[*] Success (iteration 1): %x\n", success);
success |= strcmp(username, "awesomesauce");
printf("[*] Success (iteration 2): %x\n", success);
success |= strcmp(password, "password");
printf("[*] Success (iteration 3): %x\n", success);
free(resource);
free(username);
free(password);
return ! success;
}
void main(int argc, char **argv)
{
unsigned char *buffer;
u_int16_t len;
int loggedin;
buffer = "string values go here yo";
len = strlen(buffer);
loggedin = login(buffer, len);
printf("[*] Logged in: %x\n", loggedin);
}
This allowed me to get a LOT more debug information, and not worry about my python program failing somewhere.
In my analysis, I found this program to be the most complicated (of course). It has a daemon running on port 2996. It needs a login string to be sent to it just perfectly. That login string needs to have the first byte be in little-endian format, the length of the login string. Then each of the 3 strings, the resource, username, and password, must be sent with their length in little-endian prepended to them, and a null string terminator appended. However, before any of that login string is sent, it needs a control character of "\x17" (23) to go into the login logic.
Eventually, I ended up with the following code:
#!/usr/bin/env python
# Protostar Net 3
# http://exploit-exercises.com/protostar/net3
# Matt Andreko
# twitter: @mandreko
# contact: matt [at] mattandreko.com
from socket import *
from struct import *
from optparse import OptionParser
import select
def main(host, port):
s = socket(AF_INET, SOCK_STREAM)
s.connect((host, port))
login_string = ("\x17" # \x17 = 23, which lets us by the switch statement.
# It appears to be a control character
"\x05net3\x00" # Send the resource name, prepended with
#it's length in hex, and appended with a null
# byte
"\x0dawesomesauce\x00" # Send the user name, prepended with
# it's length in hex, and appended
# with a null byte
"\x0apassword\x00") # Send the password, prepended with
# it's length in hex, and appended
# with a null byte
login_length = len(login_string) # The initial byte needs to be the length
# of the entire login string, so that it
# knows how much memory to malloc()
s.send(pack(">H", login_length))
s.send(login_string)
print s.recv(1024)
s.close()
if __name__ == "__main__":
parser = OptionParser("usage: %prog [options]")
parser.add_option("-H", "--host", dest="hostname", default="127.0.0.1",
type="string", help="Target to run against")
parser.add_option("-p", "--port", dest="portnum", default=2996,
type="int", help="Target port")
(options, args) = parser.parse_args()
main(options.hostname, options.portnum)
When I run that code, I get:
C:\Protostar>net3.py -H 192.168.1.132 ♂!successfulThat is the last of all the Net challenges that I see documented. But I do wonder, since in the virtual machine, there is a net4 binary. :)
No comments:
Post a Comment