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