Drifter Level 0
Drifter can be accessed on drifter.labs.overthewire.org via SSH on port 2230. Level 0 listens on port 1111.
Level0 is an extremely trivial, encrypted, remote syscall proxy. Your aim is to read the contents of a file called "drifter0.password" to get the password for user drifter0.
Upon connection, it sets up an encrypted rc4 key (based on the connecting IP address / port), read()'s in 9 integers, and then decrypts them, and handles them off to syscall(). This allows the network client to execute arbitrary syscalls in a safe way.
To give you an idea of what's needed:
You'll need to
- If you are behind a NAT/PAT, you might want to do vortex level0, and complete this level from vortex, as otherwise your known IP address and port information changes.
- Examine how the RC4 keys are generated, and how they are applied
- mmap2() some memory. You will get the address where the memory was allocated in the response from the server. mmap2() takes its arguments from registers, not the stack.
- read() into that allocated buffer to get the file name. The source fd that you need will be 4. (0-2 = stdin/stdout/stderr, 3 will be network socket, so 4 in next in line. You will want to read in enough data for the filename "instructions"
- Write the filename ("instructions" sans quotes) to the allocated memory location to your client socket
- open() the "instructions" file. You will get the fd it was allocated to in response
- read() from the allocated file descriptor to your allocated memory
- write() from the allocated buffer to the socket on the server (fd 4)
Once all that is done, you will have the contents of the instructions file printed to your screen.
Of course - this does not prevent you from using other mechanisms to access the server, such as using "shelldemo" from metasploit 2.x to examine the environment. In fact, using shelldemo is probably a good exercise as well.
In order to get the values for the parameters you need, you can use cross-references of linux code on the net, for example, to look up the value for __NR_read (linux read syscall()), we can use this which will lead us to http://lxr.linux.no. From there we can use the search facility to find the values we need, such as:
#define __NR_read 3 If this level is too complicated / involved, please leave a comment. While it's complicated than adding several little endian integers together, I don't want the initial level to be overkill.
The following sourcecode is located in /drifter/drifter0_src/ :
drifter0.c
/*
* Level 0 for drifter - Jan 16th, 2008
*
* This is an extremely simple / stupid remote procedure call implementation
* that allows system calls and parameters to be supplied directly, and
* return codes written to the caller.
*
* For security reasons, it is chrooted and drops privileges, and in addition,
* the connection is encrypted. (The previous sentence is a joke..)
*
* In the chroot directory, there is a file called 'instructions', open that,
* read the contents, and you'll know what to do next.
*
* In order to make things easier for yourself, there is plenty of tools
* out there already - I suggest that you read up and learn about them.
*
* - a scripting language (I humbly suggest Python, but whatever you're most
* comfortable with)
* - http://oss.coresecurity.com/projects/inlineegg.html
* - mosdef - http://www.immunitysec.com/resources-freesoftware.shtml
* - http://archives.neohapsis.com/archives/vuln-dev/2003-q4/0006.html
* - metasploit.com
* - Plenty of other things :)
*
* To compile this code, gcc level0.c rc4.c -o /drifter/level0
*/
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <string.h>
#include <sys/syscall.h>
#include <signal.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include "rc4.h"
void dropprivs()
{
int groups[1];
#ifndef LEVELUID
#error LEVELUID is not defined
#endif
#define LEVEL0 LEVELUID
groups[0] = LEVEL0;
if(setgroups(1, groups) != 0) exit(15);
if(setresgid(LEVEL0, LEVEL0, LEVEL0) != 0) exit(4);
if(setresuid(LEVEL0, LEVEL0, LEVEL0) != 0) exit(5);
#undef LEVEL0
}
void setup_stuff()
{
/* Become a daemon, and drop privileges, chroot off, etc. */
signal(SIGCHLD, SIG_IGN); // ignore children.. seems to work :p
//if(daemon(0, 0) != 0) exit(1);
if(chroot("/home/drifter0/chroot") != 0) exit(2);
if(chdir("/") != 0) exit(3);
dropprivs();
}
int create_socket()
{
/* Create / bind / listen on socket */
int ret;
struct sockaddr_in sin;
ret = socket(AF_INET, SOCK_STREAM, 0);
if(ret == -1) exit(6);
memset(&sin, 0, sizeof(struct sockaddr_in));
sin.sin_family = AF_INET,
sin.sin_addr.s_addr = htonl(INADDR_ANY);
sin.sin_port = htons(1111);
if(bind(ret, (void *)&sin, sizeof(struct sockaddr_in)) == -1) exit(7);
if(listen(ret, 5) == -1) exit(8);
return ret;
}
void handle_client(int skt, struct sockaddr_in *sin)
{
// Handle the client connection, and perform the requested operations.
rc4_key key_in, key_out;
unsigned char keymat[6];
unsigned int args[9];
int i, ret;
//dropprivs();
memcpy(keymat, &(sin->sin_addr.s_addr), 4);
memcpy(keymat+4, &(sin->sin_port), 2);
prepare_key(keymat, 6, &key_in);
prepare_key(keymat, 6, &key_out);
for(i=0;i<42;i++) {
// cycle through first 256 bytes
rc4(keymat, 6, &key_in);
rc4(keymat, 6, &key_out);
}
while(1) {
alarm(60);
sleep(1);
if(read(skt, &args, sizeof(args)) != sizeof(args)) exit(EXIT_SUCCESS);
rc4(&args, sizeof(args), &key_in);
ret = syscall(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
rc4(&ret, sizeof(int), &key_out);
if(write(skt, &ret, sizeof(ret)) != sizeof(ret)) exit(EXIT_SUCCESS);
}
}
void mainloop()
{
int skt;
int cli;
int so;
int ret;
struct sockaddr_in sin;
skt = create_socket();
while(1) {
so = sizeof(struct sockaddr_in);
cli = accept(skt, (void *)&sin, &so);
if(cli == -1) exit(9);
ret = fork();
if(ret == -1) exit(10);
// parent
if(ret) {
close(cli);
continue;
}
// child
close(skt);
handle_client(cli, &sin);
exit(EXIT_SUCCESS);
}
}
int main(int argc, char **argv)
{
setup_stuff();
mainloop();
} rc4.c
#include "rc4.h"
void prepare_key(unsigned char *key_data_ptr, int key_data_len, rc4_key *key)
{
int i;
unsigned char t;
unsigned char swapByte;
unsigned char index1;
unsigned char index2;
unsigned char* state;
short counter;
state = &key->state[0];
for(counter = 0; counter < 256; counter++)
state[counter] = counter;
key->x = 0;
key->y = 0;
index1 = 0;
index2 = 0;
for(counter = 0; counter < 256; counter++)
{
index2 = (key_data_ptr[index1] + state[counter] + index2) % 256;
swap_byte(&state[counter], &state[index2]);
index1 = (index1 + 1) % key_data_len;
}
}
void rc4(unsigned char *buffer_ptr, int buffer_len, rc4_key *key)
{
unsigned char t;
unsigned char x;
unsigned char y;
unsigned char* state;
unsigned char xorIndex;
short counter;
x = key->x;
y = key->y;
state = &key->state[0];
for(counter = 0; counter < buffer_len; counter++)
{
x = (x + 1) % 256;
y = (state[x] + y) % 256;
swap_byte(&state[x], &state[y]);
xorIndex = (state[x] + state[y]) % 256;
buffer_ptr[counter] ^= state[xorIndex];
}
key->x = x;
key->y = y;
} rc4.h
typedef struct rc4_key
{
unsigned char state[256];
unsigned char x;
unsigned char y;
} rc4_key;
#define swap_byte(x,y) t = *(x); *(x) = *(y); *(y) = t