diff --git a/empire/server/common/malleable/implementation.py b/empire/server/common/malleable/implementation.py index 54f543a38..1e6e7956a 100644 --- a/empire/server/common/malleable/implementation.py +++ b/empire/server/common/malleable/implementation.py @@ -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/"] diff --git a/empire/server/common/malleable/profile.py b/empire/server/common/malleable/profile.py index 731d88483..ad8eacff7 100644 --- a/empire/server/common/malleable/profile.py +++ b/empire/server/common/malleable/profile.py @@ -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] @@ -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. diff --git a/empire/server/core/listener_service.py b/empire/server/core/listener_service.py index 2be74f835..3c8c1e7e9 100755 --- a/empire/server/core/listener_service.py +++ b/empire/server/core/listener_service.py @@ -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( diff --git a/empire/server/listeners/http_malleable.py b/empire/server/listeners/http_malleable.py index 6c8c13d35..bb5b9ad5c 100644 --- a/empire/server/listeners/http_malleable.py +++ b/empire/server/listeners/http_malleable.py @@ -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, @@ -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"] @@ -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 ==== @@ -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"] diff --git a/empire/test/test_listener_api.py b/empire/test/test_listener_api.py index 17d3972b3..c3faa0982 100644 --- a/empire/test/test_listener_api.py +++ b/empire/test/test_listener_api.py @@ -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( diff --git a/empire/test/test_listener_generate_launcher.py b/empire/test/test_listener_generate_launcher.py index e48fcc0e8..63de0c1fb 100644 --- a/empire/test/test_listener_generate_launcher.py +++ b/empire/test/test_listener_generate_launcher.py @@ -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')