├── README.md └── phpscan.py /README.md: -------------------------------------------------------------------------------- 1 | ## phpscan 2 | A hacky python3 script to search for php files with traditionally vulnerable functions. 3 | Currently looks for: 4 | + system 5 | + shell_exec 6 | + exec 7 | + passthru 8 | + popen 9 | + proc_open 10 | + pcntl_exec 11 | + eval 12 | + assert 13 | 14 | ## usage 15 | To just search the current working directory: 16 | ``` 17 | python3 phpscan.py 18 | ``` 19 | 20 | To search the current working directory recursively: 21 | ``` 22 | python3 phpscan.py -r 23 | ``` 24 | 25 | To search a specific directory: 26 | ``` 27 | python3 phpscan.py -d /usr/local/open-audit/code-igniter 28 | ``` 29 | 30 | To search a specific directory recurisively: 31 | ``` 32 | python3 phpscan.py -d /usr/local/open-audit/code-igniter -r 33 | ``` 34 | 35 | To display the syntax of the vulnerable function call, enable verbose mode: 36 | ``` 37 | python3 phpscan.py -r -v 38 | ``` 39 | 40 | ## non-verbose output example 41 | ``` 42 | > found 127 php files 43 | > core/Security.php 44 | eval -- line 446 45 | > core/Loader.php 46 | eval -- line 830 47 | > libraries/Upload.php 48 | shell_exec -- line 1034 49 | exec -- line 1034 50 | popen -- line 1034 51 | exec -- line 1044 52 | exec -- line 1048 53 | shell_exec -- line 1058 54 | exec -- line 1058 55 | popen -- line 1072 56 | > libraries/Email.php 57 | popen -- line 1575 58 | > libraries/Xmlrpc.php 59 | eval -- line 1338 60 | eval -- line 1348 61 | eval -- line 1377 62 | eval -- line 1380 63 | > libraries/Image_lib.php 64 | exec -- line 604 65 | > database/DB.php 66 | eval -- line 115 67 | eval -- line 130 68 | eval -- line 137 69 | > database/drivers/odbc/odbc_driver.php 70 | exec -- line 154 71 | > database/drivers/postgre/postgre_driver.php 72 | exec -- line 222 73 | exec -- line 246 74 | exec -- line 270 75 | > database/drivers/sqlite/sqlite_driver.php 76 | popen -- line 86 77 | ``` 78 | 79 | ## verbose output example 80 | ``` 81 | > found 127 php files 82 | > core/Security.php 83 | eval -- line 446: eval('some code') 84 | > core/Loader.php 85 | eval -- line 830: eval('?>'.preg_replace("/;*\s*\?>/", "; ?>", str_replace(' libraries/Upload.php 87 | shell_exec -- line 1034: shell_exec(), popen() and similar functions 88 | exec -- line 1034: exec(), shell_ 89 | popen -- line 1034: popen() and similar functions 90 | exec -- line 1044: exec(), and as such - it overwrites 91 | exec -- line 1048: exec($cmd, $mime, $return_status); 92 | shell_exec -- line 1058: shell_exec($cmd); 93 | exec -- line 1058: exec($cmd); 94 | popen -- line 1072: popen($cmd, 'r'); 95 | > libraries/Email.php 96 | popen -- line 1575: popen($this->mailpath . " -oi -f ".$this->clean_email($this->_headers['From'])." -t", 'w'); 97 | > libraries/Xmlrpc.php 98 | eval -- line 1338: eval($val2); 99 | eval -- line 1348: eval($val[$i]); 100 | eval -- line 1377: eval($this); 101 | eval -- line 1380: eval($o) 102 | > libraries/Image_lib.php 103 | exec -- line 604: exec($cmd, $output, $retval); 104 | > database/DB.php 105 | eval -- line 115: eval() 106 | eval -- line 130: eval('class CI_DB extends CI_DB_active_record { }'); 107 | eval -- line 137: eval('class CI_DB extends CI_DB_driver { }'); 108 | > database/drivers/odbc/odbc_driver.php 109 | exec -- line 154: exec($this->conn_id, $sql); 110 | > database/drivers/postgre/postgre_driver.php 111 | exec -- line 222: exec($this->conn_id, "begin"); 112 | exec -- line 246: exec($this->conn_id, "commit"); 113 | exec -- line 270: exec($this->conn_id, "rollback"); 114 | > database/drivers/sqlite/sqlite_driver.php 115 | popen -- line 86: popen($this->database, FILE_WRITE_MODE, $error)) 116 | ``` 117 | 118 | -------------------------------------------------------------------------------- /phpscan.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import argparse 3 | import os 4 | 5 | ### arg parsing ### 6 | parser = argparse.ArgumentParser() 7 | 8 | parser.add_argument("-d", "--directory", default=os.getcwd(), 9 | type=str, 10 | help="directory to search for php files from, default=cwd") 11 | 12 | parser.add_argument("-r", "--recursive", 13 | help="recurisvely search from the searchdir", 14 | action="store_true") 15 | 16 | parser.add_argument("-v", "--verbose", 17 | help="display actual function call syntax", 18 | action="store_true") 19 | 20 | args = parser.parse_args() 21 | search_dir = args.directory 22 | ################### 23 | 24 | ### scary php functions ### 25 | bad_func = ["system(", "shell_exec(", "exec(", "passthru(", "popen(", 26 | "proc_open(", "pcntl_exec(", "eval(", "assert("] 27 | ########################### 28 | 29 | 30 | def search_files(search_dir): 31 | # search through our current directory for php files and return list 32 | if args.recursive: 33 | files = glob.glob(search_dir + '/**/*.php', recursive=True) 34 | # search through our current dir recusrively for php files 35 | else: 36 | files = glob.glob(search_dir + '/*.php', recursive=False) 37 | 38 | print("> found {} php files".format(str(len(files)))) 39 | return files 40 | 41 | def parse_files(files): 42 | findings = {} 43 | final_findings = {} 44 | for x in files: 45 | file = open(x, 'r', errors='replace') 46 | lst = [] 47 | findings["{}".format(file.name)] = lst 48 | lines = file.readlines() 49 | for y in lines: 50 | for z in bad_func: 51 | if z in y: 52 | lst.append( 53 | "\033[36;1m{}\033[0m -- line {}: {}".format(z.replace("(", ""), 54 | "\033[33;1m{}\033[0m".format(lines.index(y) + 1), 55 | z + y.split(z)[1]).rstrip()) 56 | 57 | for key, value in findings.items(): 58 | key = os.path.relpath(key, os.getcwd()) 59 | if value != []: 60 | new_value = [] 61 | for x in value: 62 | if x not in new_value: 63 | new_value.append(x) 64 | final_findings[key] = new_value 65 | 66 | 67 | return final_findings 68 | 69 | def present_findings(final_findings): 70 | for key, value in final_findings.items(): 71 | print("> \033[35;1m{}\033[0m".format(key)) 72 | for x in value: 73 | if args.verbose: 74 | print(" {}".format(x)) 75 | else: 76 | print(" {}".format(x.split(":")[0])) 77 | 78 | php_files = search_files(search_dir) 79 | final_findings = parse_files(php_files) 80 | present_findings(final_findings) 81 | --------------------------------------------------------------------------------