Skip to content

Commit

Permalink
Merge pull request #248 from BC-SECURITY/sponsors-dev
Browse files Browse the repository at this point in the history
Empire 4.2 release
  • Loading branch information
Cx01N authored Nov 3, 2021
2 parents def7d52 + ce0a807 commit 6d8169a
Show file tree
Hide file tree
Showing 130 changed files with 14,104 additions and 2,039 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
4.1.3
4.2.0
19 changes: 19 additions & 0 deletions changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
11/1/2021
------------
- Version 4.2.0 Master Release
- Added revershell & cmd launchers with reversehell (@Cx01N)
- Added ironpython to compile through empire with embedded std lib (@Cx01N)
- Added proxy (SOCKS/TOR/HTTP) pivots to python agents (@Cx01N)
- Added notifications in bottom toolbar for plugins and agents (@Cx01N)
- Added C# VNC server (@Cx01N)
- Added extended rights for certificate templates (@daem0nc0re)
- Added donut for shellcode generation (@Cx01N)
- Updated WMI persistence and bug fixes (@janit0rjoe)
- Updated covenant compiler (@Hubbl3)
- Updated csharp powershell launcher to compile through empire (@Hubbl3)
- Fixed formatting error in enable_rdp (@jamarir)
- Fixed nim launcher to run internal to exe (@Cx01N)
- Fixed misc python module errors (@Cx01N)
- Fixed outfile message displaying wrong directory (@Cx01N)
- Removed sRDI for shellcode (@Cx01N)

9/28/2021
------------
- Version 4.1.3 Master Release
Expand Down
25 changes: 15 additions & 10 deletions empire/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from empire.client.src.menus.MainMenu import main_menu
from empire.client.src.menus.PluginMenu import plugin_menu
from empire.client.src.menus.ShellMenu import shell_menu
from empire.client.src.menus.ProxyMenu import proxy_menu
from empire.client.src.menus.EditListenerMenu import edit_listener_menu
from empire.client.src.menus.UseListenerMenu import use_listener_menu
from empire.client.src.menus.UseModuleMenu import use_module_menu
Expand Down Expand Up @@ -103,19 +104,12 @@ def __init__(self) -> None:
'UsePluginMenu': use_plugin_menu,
'AdminMenu': admin_menu,
'ChatMenu': chat_menu,
'SponsorsMenu': sponsors_menu
'SponsorsMenu': sponsors_menu,
'ProxyMenu': proxy_menu
}
for menu in self.menus.values():
state.register_menu(menu)

@staticmethod
def bottom_toolbar():
if state.connected:
return HTML(
f'Connected to {state.host}:{state.port}. {len(state.agents)} agents. {len(chat_menu.chat_cache)} unread messages.')
else:
return ''

@staticmethod
def strip(options):
return {re.sub('[^A-Za-z0-9 _]+', '', k): v for k, v in options.items()}
Expand Down Expand Up @@ -165,7 +159,7 @@ def main(self):
history=history,
completer=self.completer,
complete_in_thread=True,
bottom_toolbar=self.bottom_toolbar,
bottom_toolbar=state.bottom_toolbar,
)
t = threading.Thread(target=self.update_in_bg, args=[session])
t.daemon = True
Expand Down Expand Up @@ -296,6 +290,17 @@ def parse_command_line(self, text: str, cmd_line: List[str], resource_file=False
menu_state.push(self.menus['InteractMenu'], selected=menu_state.current_menu.selected)
else:
menu_state.current_menu.shell(menu_state.current_menu.selected, text)
elif text.strip() == 'proxy':
if menu_state.current_menu_name == 'InteractMenu':
if menu_state.current_menu.agent_options['language'] not in ['python', 'ironpython']:
print(print_util.color(f'[!] Agent proxies are not available in {menu_state.current_menu.agent_options["language"]} agents'))
pass
elif state.listeners[menu_state.current_menu.agent_options['listener']]['module'] not in ['http', 'http_hop', 'redirector']:
print(print_util.color(f"[!] Agent proxies are not available in {state.listeners[menu_state.current_menu.agent_options['listener']]['module']} listeners"))
else:
menu_state.push(self.menus['ProxyMenu'], selected=menu_state.current_menu.selected)
else:
pass
elif text.strip() == 'back':
menu_state.pop()
elif text.strip() == 'exit':
Expand Down
51 changes: 51 additions & 0 deletions empire/client/src/EmpireCliState.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from empire.client.src.MenuState import menu_state
from empire.client.src.menus import Menu
from empire.client.src.utils import print_util
from prompt_toolkit import HTML, ANSI


class EmpireCliState(object):
Expand All @@ -32,6 +33,7 @@ def __init__(self):
self.credentials = {}
self.empire_version = ''
self.cached_plugin_results = {}
self.chat_cache = []

# { session_id: { task_id: 'output' }}
self.cached_agent_results = {}
Expand Down Expand Up @@ -158,6 +160,40 @@ def add_plugin_cache(self, data) -> None:
else:
self.cached_plugin_results[plugin_name][data['message']] = data['message']

def bottom_toolbar(self):
if self.connected:
agent_tasks = list(self.cached_agent_results.keys())
plugin_tasks = list(self.cached_plugin_results.keys())

toolbar_text = [("bold", f'Connected: ')]
toolbar_text.append(("bg:#FF0000 bold", f'{self.host}:{self.port} '))
toolbar_text.append(("bold", f'| '))
toolbar_text.append(("bg:#FF0000 bold", f'{len(state.agents)} '))
toolbar_text.append(("bold", f'agent(s) | '))
toolbar_text.append(("bg:#FF0000 bold", f'{len(state.chat_cache)} '))
toolbar_text.append(("bold", f'unread message(s) '))

agent_text = ''
for agents in agent_tasks:
if self.cached_agent_results[agents]:
agent_text += f' {agents}'
if agent_text:
toolbar_text.append(("bold", '| Agent(s) received task results:'))
toolbar_text.append(("bg:#FF0000 bold", f'{agent_text} '))

plugin_text = ''
for plugins in plugin_tasks:
if self.cached_plugin_results[plugins]:
plugin_text += f' {plugins}'
if plugin_text:
toolbar_text.append(("bold", f'| Plugin(s) received task result(s):'))
toolbar_text.append(("bg:#FF0000 bold", f'{plugin_text} '))

return toolbar_text

else:
return ''

# I think we we will break out the socketio handler and http requests to new classes that the state imports.
# This will do for this iteration.
def get_listeners(self):
Expand Down Expand Up @@ -342,6 +378,21 @@ def update_agent_killdate(self, agent_name: str, kill_date: str):

return response.json()

def update_agent_proxy(self, agent_name: str, options: list):
response = requests.put(url=f'{self.host}:{self.port}/api/agents/{agent_name}/proxy',
json={'proxy': options},
verify=False,
params={'token': self.token})

return response.json()

def get_proxy_info(self, agent_name: str):
response = requests.get(url=f'{self.host}:{self.port}/api/agents/{agent_name}/proxy',
verify=False,
params={'token': self.token})

return response.json()

def update_agent_working_hours(self, agent_name: str, working_hours: str):
response = requests.put(url=f'{self.host}:{self.port}/api/agents/{agent_name}/workinghours',
json={'working_hours': working_hours},
Expand Down
14 changes: 7 additions & 7 deletions empire/client/src/menus/ChatMenu.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class ChatMenu(Menu):
def __init__(self):
super().__init__(display_name='chat', selected='')
self.my_username = ''
self.chat_cache = []
state.chat_cache = []

def autocomplete(self):
return self._cmd_registry + super().autocomplete()
Expand Down Expand Up @@ -43,10 +43,10 @@ def on_enter(self):
print(print_util.color('[*] Exit Chat Menu with Ctrl+C'))
self.my_username = state.me['username']

for message in self.chat_cache:
for message in state.chat_cache:
print(message)

self.chat_cache = []
state.chat_cache = []

return True

Expand All @@ -59,14 +59,14 @@ def on_chat_join(self, data):
if self.is_chat_active() == 'ChatMenu':
print(message)
else:
self.chat_cache.append(message)
state.chat_cache.append(message)

def on_chat_leave(self, data):
message = print_util.color('[+] ' + data['message'])
if self.is_chat_active():
print(message)
else:
self.chat_cache.append(message)
state.chat_cache.append(message)

def on_chat_message(self, data):
if data['username'] != state.me['username'] or data.get('history') is True:
Expand All @@ -75,13 +75,13 @@ def on_chat_message(self, data):
if self.is_chat_active():
print(message)
else:
self.chat_cache.append(print_util.color(message))
state.chat_cache.append(print_util.color(message))
else:
message = print_util.color(data['username'], 'red') + ': ' + data['message']
if self.is_chat_active():
print(message)
else:
self.chat_cache.append(message)
state.chat_cache.append(message)

def send_chat(self, text):
state.sio.emit('chat/message', {'message': text})
Expand Down
9 changes: 9 additions & 0 deletions empire/client/src/menus/InteractMenu.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,15 @@ def workinghours(self, working_hours: str) -> None:
elif 'error' in response.keys():
print(print_util.color('[!] Error: ' + response['error']))

@command
def proxy(self, agent_name: str) -> None:
"""
Proxy management menu for configuring agent proxies
Usage: proxy
"""
pass

@command
def display(self, property_name: str):
"""
Expand Down
161 changes: 161 additions & 0 deletions empire/client/src/menus/ProxyMenu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import base64
import os
import textwrap
from typing import List

from prompt_toolkit.completion import Completion
from prompt_toolkit import HTML

from empire.client.src.EmpireCliState import state
from empire.client.src.Shortcut import Shortcut
from empire.client.src.ShortcutHandler import shortcut_handler
from empire.client.src.menus.UseMenu import UseMenu
from empire.client.src.utils import table_util, print_util
from empire.client.src.utils.autocomplete_util import filtered_search_list, position_util
from empire.client.src.utils.cli_util import register_cli_commands, command


@register_cli_commands
class ProxyMenu(UseMenu):
def __init__(self):
super().__init__(display_name='', selected='')
self.stop_threads = False

def autocomplete(self):
return self._cmd_registry + super().autocomplete()

def get_completions(self, document, complete_event, cmd_line, word_before_cursor):
if cmd_line[0] in ['set', 'unset'] and position_util(cmd_line, 2, word_before_cursor):
for option in filtered_search_list(word_before_cursor, self.record_options):
yield Completion(option, start_position=-len(word_before_cursor))
elif cmd_line[0] == 'set' and position_util(cmd_line, 3, word_before_cursor):
if len(cmd_line) > 1 and len(self.suggested_values_for_option(cmd_line[1])) > 0:
for suggested_value in filtered_search_list(word_before_cursor,
self.suggested_values_for_option(cmd_line[1])):
yield Completion(suggested_value, start_position=-len(word_before_cursor))
else:
yield from super().get_completions(document, complete_event, cmd_line, word_before_cursor)

def on_enter(self, **kwargs) -> bool:
if 'selected' not in kwargs:
return False
else:
self.use(kwargs['selected'])
return True

def get_prompt(self) -> str:
return f"(Empire: <ansired>{self.selected}</ansired>/<ansiblue>proxy</ansiblue>) > "

def use(self, agent_name: str) -> None:
"""
Use proxy
Usage: proxy
"""
try:
self.record = state.get_proxy_info(agent_name)['proxy']
self.record_options = self.record['options']
if agent_name in state.agents.keys():
self.selected = agent_name
self.session_id = state.agents[self.selected]['session_id']
self.agent_options = state.agents[agent_name] # todo rename agent_options
self.agent_language = self.agent_options['language']
self.proxy_list = self.agent_options['proxy']
if not self.proxy_list:
self.proxy_list = []
self.list()
except:
print(print_util.color(f'[!] Error: Proxy menu failed to initialize'))

@command
def add(self, position: int) -> None:
"""
Tasks an the specified agent to update proxy chain
Usage: add_proxy [<position>]
"""
self.agent_options = state.agents[self.session_id]
if not self.proxy_list:
self.proxy_list = []

if position:
self.proxy_list.insert(int(position), {'proxytype': self.record_options['Proxy_Type']['Value'],
'addr': self.record_options['Address']['Value'],
'port': int(self.record_options['Port']['Value'])})
else:
self.proxy_list.append({'proxytype': self.record_options['Proxy_Type']['Value'],
'addr': self.record_options['Address']['Value'],
'port': int(self.record_options['Port']['Value'])})

# print table of proxies
self.list()

@command
def delete(self, position: int) -> None:
"""
Tasks an the specified agent to remove proxy chain
Usage: delete_proxy <position>
"""
self.agent_options = state.agents[self.session_id]
if not self.proxy_list:
self.proxy_list = []

self.proxy_list.pop(int(position))

# print table of proxies
self.list()

@command
def execute(self) -> None:
"""
Tasks an the specified agent to update its proxy chain
Usage: execute
"""
if self.proxy_list:
response = state.update_agent_proxy(self.session_id, self.proxy_list)
print(print_util.color(f'[*] Tasked agent to update proxy chain'))
else:
print(print_util.color(f'[!] No proxy chain to configure'))

@command
def list(self) -> None:
"""
Display list of current proxy chains
Usage: list
"""
proxies = list(map(lambda x: [self.proxy_list.index(x)+1, x['addr'], x['port'], x['proxytype']], self.proxy_list))
proxies.insert(0, ['Hop', 'Address', 'Port', 'Proxy Type'])

table_util.print_table(proxies, 'Active Proxies')

@command
def options(self):
"""
Print the current record options
Usage: options
"""
record_list = []
for key, value in self.record_options.items():
name = key
record_value = print_util.text_wrap(value.get('Value', ''))
required = print_util.text_wrap(value.get('Required', ''))
description = print_util.text_wrap(value.get('Description', ''))
record_list.append([name, record_value, required, description])

record_list.insert(0, ['Name', 'Value', 'Required', 'Description'])

table_util.print_table(record_list, 'Record Options')

def suggested_values_for_option(self, option: str) -> List[str]:
try:
lower = {k.lower(): v for k, v in self.record_options.items()}
return lower.get(option, {}).get('SuggestedValues', [])
except AttributeError:
return []


proxy_menu = ProxyMenu()
Loading

0 comments on commit 6d8169a

Please sign in to comment.