Protostar – net3

Information

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

Again, it is a server waiting for a connection on port 2996.
After the connection, it reads a value from the network and it converts it in host byte order (big endian) :

nread(fd, &len, sizeof(len));
len = ntohs(len);

Then, it reads again and it puts the content in ‘buffer’. The first byte needs to be ’23’ (0x17) then it calls the function login with the buffer+1 (avoid ’23’) and len-1 (idem)

case 23:
loggedin = login(buffer + 1, len – 1);

Then, it splits the content of buffer into three strings and it compares them with the values requested :

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’);

Looking at the function get_string, I identify the format that I need to send.
Each word needs to start with it len :

*result = malloc(byte);

Then, each word needs to finish with a null byte :

strcpy(*result, buffer + 1);

I use the following python script :

#!/usr/bin/python

import socket
import struct

host='192.168.0.46'
port=2996

sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect((host,port))

# start by 23 (0x17) then len+word+null
payload = "\x17" + "\x05net3\x00" + "\x0dawesomesauce\x00" + "\x09password\x00"
size_payload = len(payload)

# convert to unsigned short big endian for ntohs
size = struct.pack("!H",size_payload)

sock.send(size)

sock.send(payload)

recv = sock.recv(1024)

print recv

sock.close()

Let’s fire up :

$ python net3.py

!successful

That’s it, net3 is done.


Laisser un commentaire