mirror of
https://github.com/reyammer/shellnoob
synced 2026-06-08 17:08:51 +00:00
327 lines
12 KiB
Python
Executable File
327 lines
12 KiB
Python
Executable File
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
import os
|
|
import sys
|
|
import shutil
|
|
import re
|
|
import traceback
|
|
from os.path import dirname, join
|
|
from hashlib import md5
|
|
from tempfile import mkdtemp
|
|
from subprocess import Popen, PIPE
|
|
try:
|
|
import IPython
|
|
except ImportError:
|
|
pass
|
|
|
|
from shellnoob import ShellNoob, cstr, cbytes
|
|
|
|
GREEN = '\033[92m'
|
|
RED = '\033[91m'
|
|
ENDC = '\033[0m'
|
|
|
|
SHELLNOOB_FP = os.path.join(os.path.dirname(__file__), 'shellnoob.py')
|
|
SECRET_STR = 'thesecretisthedolphin'
|
|
|
|
# expcted values for the different tests / architecture-OS
|
|
|
|
# test functions
|
|
|
|
supported_features = {
|
|
# TODO use regex!
|
|
'Linux#i686' : {
|
|
'test_resolve_const' : [
|
|
(['O_RDONLY'], ('^O_RDONLY ~> 0\n$', '', 0)),
|
|
(['O_RDWR'], ('^O_RDWR ~> 2\n$', '', 0)),
|
|
],
|
|
'test_resolve_syscall' : [
|
|
(['read'], ('^i386 ~> 3\n$', '', 0)),
|
|
(['write'], ('^i386 ~> 4\n$', '', 0)),
|
|
],
|
|
'test_conversion' : [
|
|
([True], ('^$', '^$', 0)),
|
|
],
|
|
'test_interactive' : [
|
|
(['1\nmov %ebx,%eax\nnop\nquit\n',''], ('[^\n]+~> 89d8\n[^\n]+~> 90', '', 0)),
|
|
(['1\nmov eax,ebx\nnop\nquit\n','--intel'], ('[^\n]+~> 89d8\n[^\n]+~> 90', '', 0)),
|
|
(['2\n89d890\nquit\n',''], ('0:.*mov\s+%ebx,%eax\n\s+2:.*nop\n', '', 0)),
|
|
(['2\n89d890\nquit\n','--intel'], ('0:.*mov\s+eax,ebx\n\s+2:.*nop\n', '', 0)),
|
|
],
|
|
'test_get_start_address' : [
|
|
([], ('^$', '^$', 0)),
|
|
],
|
|
},
|
|
'Linux#x86_64' : {
|
|
'test_resolve_const' : [
|
|
(['O_RDONLY'], ('^O_RDONLY ~> 0\n$', '', 0)),
|
|
(['O_RDWR'], ('^O_RDWR ~> 2\n$', '', 0)),
|
|
],
|
|
'test_resolve_syscall' : [
|
|
(['read'], ('^x86_64 ~> 0\ni386 ~> 3\n$', '', 0)),
|
|
(['write'], ('^x86_64 ~> 1\ni386 ~> 4\n$', '', 0)),
|
|
],
|
|
'test_conversion' : [
|
|
([True], ('^$', '^$', 0)),
|
|
],
|
|
'test_interactive' : [
|
|
(['1\nmov %ebx,%eax\nnop\nquit\n',''], ('[^\n]+~> 89d8\n[^\n]+~> 90', '', 0)),
|
|
(['1\nmov eax,ebx\nnop\nquit\n','--intel'], ('[^\n]+~> 89d8\n[^\n]+~> 90', '', 0)),
|
|
(['1\nmov %rbx,%rax\nnop\nquit\n','--64'], ('[^\n]+~> 4889d8\n[^\n]+~> 90', '', 0)),
|
|
(['1\nmov rax,rbx\nnop\nquit\n','--intel --64'], ('[^\n]+~> 4889d8\n[^\n]+~> 90', '', 0)),
|
|
(['2\n89d890\nquit\n',''], ('0:.*mov\s+%ebx,%eax\n\s+2:.*nop\n', '', 0)),
|
|
(['2\n89d890\nquit\n','--intel'], ('0:.*mov\s+eax,ebx\n\s+2:.*nop\n', '', 0)),
|
|
(['2\n4889d890\nquit\n','--64'], ('0:.*mov\s+%rbx,%rax\n\s+3:.*nop\n', '', 0)),
|
|
(['2\n4889d890\nquit\n','--intel --64'], ('0:.*mov\s+rax,rbx\n\s+3:.*nop\n', '', 0)),
|
|
],
|
|
'test_get_start_address' : [
|
|
([], ('^$', '^$', 0)),
|
|
],
|
|
},
|
|
'Linux#armv5tejl' : {
|
|
'test_resolve_const' : [
|
|
(['O_RDONLY'], ('^O_RDONLY ~> 0\n$', '', 0)),
|
|
(['O_RDWR'], ('^O_RDWR ~> 2\n$', '', 0)),
|
|
],
|
|
},
|
|
}
|
|
|
|
def test_resolve_const(const):
|
|
stdout, stderr, retval = run_with_args('--get-const %s' % const)
|
|
return stdout, stderr, retval
|
|
|
|
def test_resolve_syscall(syscall):
|
|
stdout, stderr, retval = run_with_args('--get-sysnum %s' % syscall)
|
|
return stdout, stderr, retval
|
|
|
|
def test_conversion(with_breakpoint=False):
|
|
|
|
stdout, stderr = '', ''
|
|
|
|
tmp_dir = mkdtemp()
|
|
asm_fp = join(tmp_dir, 'shellcode.asm')
|
|
asm2_fp = join(tmp_dir, 'shellcode2.asm')
|
|
obj_fp = join(tmp_dir, 'shellcode.obj')
|
|
exe_fp = join(tmp_dir, 'shellcode.exe')
|
|
bin_fp = join(tmp_dir, 'shellcode.bin')
|
|
bin2_fp = join(tmp_dir, 'shellcode2.bin')
|
|
hex_fp = join(tmp_dir, 'shellcode.hex')
|
|
c_fp = join(tmp_dir, 'shellcode.c')
|
|
python_fp = join(tmp_dir, 'shellcode.py')
|
|
bash_fp = join(tmp_dir, 'shellcode.sh')
|
|
pretty_fp = join(tmp_dir, 'shellcode.pretty')
|
|
safeasm_fp = join(tmp_dir, 'shellcode.safeasm')
|
|
|
|
secret_fp = '/tmp/secret'
|
|
os.system('echo "%s" > %s' % (SECRET_STR, secret_fp))
|
|
|
|
kernel = ShellNoob.get_kernel()
|
|
if kernel == 'Linux':
|
|
shutil.copyfile(join(dirname(__file__), 'samples/x86-linux/open-read-write.asm'), asm_fp)
|
|
elif kernel == 'FreeBSD':
|
|
shutil.copyfile(join(dirname(__file__), 'samples/x86-freebsd/open-read-write.asm'), asm_fp)
|
|
else:
|
|
raise Exception('testing on kernel %s not supported' % kernel)
|
|
|
|
_out, _err, _val = run_with_args('%s --to-bin' % asm_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('%s --to-hex' % bin_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('%s --to-obj' % hex_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('%s --to-hex' % obj_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('%s --to-bin %s' % (hex_fp, bin2_fp))
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
|
|
assert (md5(open(bin_fp,'rb').read()).hexdigest() ==
|
|
md5(open(bin2_fp,'rb').read()).hexdigest())
|
|
|
|
_out, _err, _val = run_with_args('%s --to-obj' % asm_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('%s --to-exe' % asm_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('%s --to-bin' % asm_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('%s --to-hex' % asm_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('%s --to-c' % asm_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('%s --to-python' % asm_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('%s --to-bash' % asm_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('%s --to-pretty' % asm_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('%s --to-safeasm' % asm_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
|
|
_out, _err, _val = run_with_args('%s --to-asm %s' % (obj_fp, asm2_fp))
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('%s --to-exe' % obj_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('%s --to-bin' % obj_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('%s --to-hex' % obj_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('%s --to-c' % obj_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('%s --to-python' % obj_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('%s --to-bash' % obj_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('%s --to-pretty' % obj_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('%s --to-safeasm' % obj_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
|
|
_out, _err, _val = run_with_args('-c %s --to-obj' % asm_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('-c %s --to-exe' % asm_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('-c %s --to-bin' % asm_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('-c %s --to-hex' % asm_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('-c %s --to-c' % asm_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('-c %s --to-python' % asm_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('-c %s --to-bash' % asm_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('-c %s --to-pretty' % asm_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
_out, _err, _val = run_with_args('-c %s --to-safeasm' % asm_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
|
|
# TODO add "chain" tests
|
|
|
|
_out, _err, _val = run_with_args('%s --to-exe' % asm_fp)
|
|
assert os.popen(exe_fp).read().rstrip() == SECRET_STR
|
|
print('Output of the shellcode matches!')
|
|
|
|
shutil.rmtree(tmp_dir)
|
|
os.unlink(secret_fp)
|
|
return stdout.strip(), stderr, 0
|
|
|
|
|
|
def test_get_start_address():
|
|
stdout, stderr = '', ''
|
|
|
|
tmp_dir = mkdtemp()
|
|
asm_fp = join(tmp_dir, 'shellcode.asm')
|
|
exe_fp = join(tmp_dir, 'shellcode.exe')
|
|
|
|
secret_fp = '/tmp/secret'
|
|
os.system('echo "%s" > %s' % (SECRET_STR, secret_fp))
|
|
|
|
kernel = ShellNoob.get_kernel()
|
|
if kernel == 'Linux':
|
|
shutil.copyfile(join(dirname(__file__), 'samples/x86-linux/open-read-write.asm'), asm_fp)
|
|
elif kernel == 'FreeBSD':
|
|
shutil.copyfile(join(dirname(__file__), 'samples/x86-freebsd/open-read-write.asm'), asm_fp)
|
|
else:
|
|
raise Exception('testing on kernel %s not supported' % kernel)
|
|
|
|
_out, _err, _val = run_with_args('%s --to-exe' % asm_fp)
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
|
|
snoob = ShellNoob()
|
|
start_addr = snoob.get_start_address(exe_fp)
|
|
assert re.match('0x[0-9a-f]+', start_addr)
|
|
|
|
shutil.rmtree(tmp_dir)
|
|
os.unlink(secret_fp)
|
|
return stdout, stderr, 0
|
|
|
|
|
|
def test_interactive(_input, args):
|
|
stdout, stderr = '', ''
|
|
_out, _err, _val = run_with_args_input(_input, args + ' -i')
|
|
stdout += _out; stderr += _err; assert _val == 0;
|
|
|
|
return stdout, stderr, 0
|
|
|
|
|
|
def run_with_args_input(_input='', args=''):
|
|
args += ' -q'
|
|
cmd = '%s %s' % (SHELLNOOB_FP, args)
|
|
print('Launching: %s (with input)' % (cmd))
|
|
p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)
|
|
stdout, stderr = p.communicate(input=cbytes(_input))
|
|
retval = p.returncode
|
|
|
|
return cstr(stdout), cstr(stderr), int(retval)
|
|
|
|
|
|
def run_with_args(args=''):
|
|
args += ' -q'
|
|
cmd = '%s %s' % (SHELLNOOB_FP, args)
|
|
print('Launching: %s' % cmd)
|
|
p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE)
|
|
stdout, stderr = p.communicate(input='')
|
|
retval = p.returncode
|
|
|
|
return cstr(stdout), cstr(stderr), int(retval)
|
|
|
|
def run_all_tests():
|
|
kernel, hardware = ShellNoob.get_kernel(), ShellNoob.get_hardware()
|
|
entry = '%s#%s' % (kernel, hardware)
|
|
|
|
tot_test = 0
|
|
ok_test = 0
|
|
|
|
try:
|
|
tests = supported_features[entry]
|
|
except KeyError:
|
|
print('ERROR: No tests for this setup "%s"' % entry)
|
|
sys.exit(1)
|
|
for test_name, entries in tests.items():
|
|
for e in entries:
|
|
tot_test += 1
|
|
_input, expected = e
|
|
print('Running test %s - %s - %s' % (test_name, _input, str(expected)))
|
|
e_stdout, e_stderr, e_retval = expected
|
|
try:
|
|
stdout, stderr, retval = globals()[test_name](*_input)
|
|
if not re.search(e_stdout, stdout):
|
|
print(RED + 'ERROR STDOUT %s != %s (expected)' % (stdout, e_stdout) + ENDC)
|
|
continue
|
|
if not re.search(e_stderr, stderr):
|
|
print(RED + 'ERROR STDERR?g %s != %s (expected)' % (stderr, e_stderr) + ENDC)
|
|
continue
|
|
if retval != e_retval:
|
|
print(RED + 'ERROR RETVAL %s != %s (expected)' % (retval, e_retval) + ENDC)
|
|
continue
|
|
print(GREEN + 'OK' + ENDC)
|
|
ok_test += 1
|
|
except Exception as e:
|
|
print(RED + 'ERROR Exception while executing %s' % test_name + ENDC)
|
|
print(traceback.format_exc())
|
|
print('---------------------------')
|
|
|
|
if ok_test == tot_test:
|
|
print(GREEN + '%s/%s OK' % (ok_test, tot_test) + ENDC)
|
|
else:
|
|
print(RED + '%s/%s ERROR' % (ok_test, tot_test) + ENDC)
|
|
|
|
|
|
|
|
def main():
|
|
args = sys.argv
|
|
|
|
if '-t' in args:
|
|
t_idx = args.index('-t')
|
|
test_name = args[t_idx+1]
|
|
try:
|
|
globals()[test_name]()
|
|
except Exception as e:
|
|
print('ERROR EXCEPTION while running test %s' % test_name)
|
|
print(traceback.format_exc())
|
|
sys.exit(0)
|
|
|
|
run_all_tests()
|
|
|
|
if __name__== '__main__':
|
|
main()
|