This repository has been archived by the owner on May 14, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mini_server.py
155 lines (132 loc) · 5 KB
/
mini_server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
"""
Simple server that adheres to the DeadDrop protocol and can be used to
manually send messages to the agent without needing to set up the backend
yourself.
"""
# region imports
from pathlib import Path
from typing import Any
import base64
import datetime
import json
import logging
import time
import sys
from deaddrop_meta.protocol_lib import (
DeadDropMessage,
DeadDropMessageType,
CommandRequestPayload,
CommandResponsePayload,
)
from deaddrop_meta.interface_lib import (
MessagingObject,
EndpointMessagingData,
ServerMessagingData,
)
# Make all protocols visible so that PyginConfig works correctly
from src.protocols import * # noqa: F401, F403
# Make the server entrypoint visible
from src.meta import exec_message as messaging
logging.basicConfig(
handlers=[logging.StreamHandler(sys.stdout)],
level=logging.DEBUG,
format="%(filename)s:%(lineno)d | %(asctime)s | [%(levelname)s] - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
logger = logging.getLogger()
# endregion
# Endpoint configuration, normally stored by the server. Corresponds to the Endpoint
# model in Django.
ENDPOINT_NAME = "my_endpoint"
ENDPOINT_HOSTNAME = "my_hostname"
ENDPOINT_ADDRESS = "127.0.0.1"
# Either a base64-encoded PEM Ed25519 key, or None.
SERVER_PRIVATE_KEY = "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1DNENBUUF3QlFZREsyVndCQ0lFSU1qdnhsSEUzcTFKVEZYQ0RnMG1lVGxpSXIyRGgxdDB4Q2xkLzZXMmp5ZC8KLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQ=="
# The command to issue (this is protocol independent)
# CMD_NAME: str = "ping"
# CMD_ARGS: dict[str, Any] = {
# "message": "test",
# "ping_timestamp": datetime.utcnow().timestamp(),
# }
CMD_NAME: str = "shell"
CMD_ARGS: dict[str, Any] = {
"command": "cat Makefile",
"use_shell": True,
}
# CMD_NAME: str = "empyrean_exec"
# CMD_ARGS: dict[str, Any] = {}
# Path to agent_cfg.json. Note that the selected protocol is derived
# from this file, consistent with how the server/agent currently interacts.
# It's assumed that this agent configuration is what you're currently running.
if __name__ == "__main__":
# Load configuration
with open(Path("./agent_cfg.json"), "rt") as fp:
cfg = json.load(fp)
# Construct the command_request message
msg = DeadDropMessage(
destination_id=cfg["agent_config"]["AGENT_ID"],
payload=CommandRequestPayload(
message_type=DeadDropMessageType.CMD_REQUEST,
cmd_name=CMD_NAME,
cmd_args=CMD_ARGS,
),
)
print(msg.model_dump_json())
# Construct messaging object - note that we don't have any sort
# of state management, so the protocol state dict is empty. The rest
# of this is identical to the backend implementation.
msg_obj = MessagingObject(
agent_config=cfg["agent_config"],
protocol_config=cfg["protocol_config"],
protocol_state={},
endpoint_model_data=EndpointMessagingData(
name=ENDPOINT_NAME, hostname=ENDPOINT_HOSTNAME, address=ENDPOINT_ADDRESS
),
server_config=ServerMessagingData(
action="send",
listen_for_id=None,
server_private_key=base64.b64decode(SERVER_PRIVATE_KEY),
preferred_protocol=None,
),
)
# Invoke server-side entrypoint
messaging.send_message(msg_obj, msg)
recv_msg_obj = MessagingObject(
agent_config=cfg["agent_config"],
protocol_config=cfg["protocol_config"],
protocol_state={},
endpoint_model_data=EndpointMessagingData(
name=ENDPOINT_NAME, hostname=ENDPOINT_HOSTNAME, address=ENDPOINT_ADDRESS
),
server_config=ServerMessagingData(
action="receive",
listen_for_id=msg.message_id,
server_private_key=None,
preferred_protocol=None,
),
)
# Read back all messages being sent by the server, then select just the
# response to our message (if multiple messages exist)
while True:
time.sleep(1)
logger.info("Waiting for response...")
recv_msgs = messaging.receive_msgs(recv_msg_obj)
logger.info(
f"Got the following message ids back: {[msg.message_id for msg in recv_msgs]}"
)
for recv_msg in recv_msgs:
if not isinstance(recv_msg.payload, CommandResponsePayload):
continue
payload: CommandResponsePayload = recv_msg.payload
if payload.request_id != msg.message_id:
continue
logger.info(f"Got response to original message: {recv_msg}")
# Only if ping was used
if CMD_NAME == "ping":
start_time: datetime.datetime = payload.result["ping_timestamp"]
end_time: datetime.datetime = payload.result["pong_timestamp"]
return_time = datetime.datetime.now(datetime.UTC)
logger.info(
f"The ping time was {(end_time-start_time).total_seconds():.2f} seconds to receive, {(return_time-start_time).total_seconds():.2f} seconds RTT"
)
exit()