initial commit
This commit is contained in:
commit
8bede90bba
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
.idea
|
||||||
|
venv
|
||||||
|
|
||||||
|
__pycache__
|
||||||
|
*.py[cod]
|
5
README.md
Normal file
5
README.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Python C2
|
||||||
|
An exercise in learning and experimenting with a C2 agent and server.
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
- agathanonymous
|
0
agent/README.md
Normal file
0
agent/README.md
Normal file
0
agent/requirements.txt
Normal file
0
agent/requirements.txt
Normal file
0
agent/src/__init__.py
Normal file
0
agent/src/__init__.py
Normal file
0
server/Dockerfile
Normal file
0
server/Dockerfile
Normal file
0
server/README.md
Normal file
0
server/README.md
Normal file
0
server/config/config.yaml
Normal file
0
server/config/config.yaml
Normal file
2
server/requirements.txt
Normal file
2
server/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
loguru
|
||||||
|
SQLAlchemy
|
0
server/src/__init__.py
Normal file
0
server/src/__init__.py
Normal file
BIN
server/src/agents.db
Normal file
BIN
server/src/agents.db
Normal file
Binary file not shown.
13
server/src/database/__init__.py
Normal file
13
server/src/database/__init__.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy.orm import sessionmaker, scoped_session
|
||||||
|
from .models import Base
|
||||||
|
|
||||||
|
|
||||||
|
def init_db():
|
||||||
|
engine = create_engine('sqlite:///agents.db')
|
||||||
|
Base.metadata.create_all(engine)
|
||||||
|
session_factory = sessionmaker(bind=engine)
|
||||||
|
return scoped_session(session_factory)
|
||||||
|
|
||||||
|
|
||||||
|
Session = init_db()
|
32
server/src/database/models.py
Normal file
32
server/src/database/models.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from sqlalchemy import Column, Integer, String, DateTime
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
|
||||||
|
class Agent(Base):
|
||||||
|
"""
|
||||||
|
Represents an agent that has connected to the server.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
id (int): A unique identifier for the agent.
|
||||||
|
platform (str): The platform the agent is running on.
|
||||||
|
processor (str): The processor the agent is running on.
|
||||||
|
memory (int): The amount of memory the agent has.
|
||||||
|
disk (int): The amount of disk space the agent has.
|
||||||
|
first_seen (datetime): The first time the agent was seen by the server.
|
||||||
|
last_seen (datetime): The last time the agent was seen by the server.
|
||||||
|
"""
|
||||||
|
__tablename__ = "agents"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
platform = Column(String)
|
||||||
|
processor = Column(String)
|
||||||
|
memory = Column(Integer)
|
||||||
|
disk = Column(Integer)
|
||||||
|
first_seen = Column(DateTime, default=datetime.utcnow)
|
||||||
|
last_seen = Column(DateTime, default=datetime.utcnow)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"Agent(id={self.id}, platform={self.platform}"
|
161
server/src/main.py
Normal file
161
server/src/main.py
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
import json
|
||||||
|
import socket
|
||||||
|
import threading
|
||||||
|
import database
|
||||||
|
from database.models import Agent
|
||||||
|
from loguru import logger
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
|
class Server:
|
||||||
|
def __init__(self, host, port):
|
||||||
|
"""
|
||||||
|
Initializes a new Server instance.
|
||||||
|
|
||||||
|
This method will initialize a new Server instance. It takes in
|
||||||
|
the host and port to listen on, and will create a new database
|
||||||
|
session and a lock to protect access to the self.commands
|
||||||
|
dictionary.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
host (str): The host to listen on.
|
||||||
|
port (int): The port to listen on.
|
||||||
|
"""
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
self.commands = {}
|
||||||
|
self.lock = threading.Lock()
|
||||||
|
self.session = database.init_db()
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
"""Starts the server.
|
||||||
|
|
||||||
|
This method will start the server and listen on the specified host and
|
||||||
|
port. It will then accept incoming connections and start a new thread to
|
||||||
|
handle each connection.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
Exception: If there is an error starting the server.
|
||||||
|
"""
|
||||||
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||||
|
s.bind((self.host, self.port))
|
||||||
|
s.listen()
|
||||||
|
logger.info(f"Listening on {self.host}:{self.port}")
|
||||||
|
while True:
|
||||||
|
conn, addr = s.accept()
|
||||||
|
logger.info(f"Connection from {addr}")
|
||||||
|
threading.Thread(target=self.handle_agent, args=(conn, addr)).start()
|
||||||
|
|
||||||
|
def handle_agent(self, conn, addr):
|
||||||
|
"""Handles a connection from an agent.
|
||||||
|
|
||||||
|
This method is responsible for handling a connection from an agent. It
|
||||||
|
will either register the agent if it's the first time it's connecting,
|
||||||
|
or update the agent's information. It will then send a command to the
|
||||||
|
agent based on the command registered for that agent. If the agent is
|
||||||
|
new, the command will be to set the agent's id. If the agent is already
|
||||||
|
known, the command will be the last command registered for that agent.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
conn (socket.socket): The socket object for the connection to the
|
||||||
|
agent.
|
||||||
|
addr (tuple): The address of the agent.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
Exception: If there is an error handling the agent.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
agent_info = json.loads(conn.recv(1024).decode())
|
||||||
|
agent_id = agent_info.get("id", None)
|
||||||
|
|
||||||
|
if not agent_id:
|
||||||
|
agent_id = self.register_agent(agent_info)
|
||||||
|
logger.info(f"Agent {agent_id} registered from {addr}")
|
||||||
|
self.commands[agent_id] = f"set_id {agent_id}"
|
||||||
|
else:
|
||||||
|
self.update_agent(agent_id, agent_info)
|
||||||
|
logger.info(f"Agent {agent_id} connected from {addr}")
|
||||||
|
|
||||||
|
with self.lock:
|
||||||
|
if agent_id in self.commands:
|
||||||
|
command = self.commands[agent_id]
|
||||||
|
del self.commands[agent_id]
|
||||||
|
else:
|
||||||
|
command = "nop"
|
||||||
|
conn.send(json.dumps({"command": command}).encode())
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error handling agent {agent_id}: {e}")
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
def register_agent(self, agent_info):
|
||||||
|
"""
|
||||||
|
Registers a new agent with the server.
|
||||||
|
|
||||||
|
This method will register a new agent with the server. It will create a new
|
||||||
|
Agent instance with the provided agent_info and add it to the database.
|
||||||
|
If the registration is successful, it will return the id of the newly
|
||||||
|
registered agent. If there is an error registering the agent, it will
|
||||||
|
log the error and return None.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
agent_info (dict): The information about the agent to register.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int or None: The id of the newly registered agent, or None if there
|
||||||
|
is an error.
|
||||||
|
"""
|
||||||
|
session = self.session()
|
||||||
|
try:
|
||||||
|
agent = Agent(**agent_info)
|
||||||
|
session.add(agent)
|
||||||
|
session.commit()
|
||||||
|
agent_id = agent.id
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error registering agent: {e}")
|
||||||
|
session.rollback()
|
||||||
|
agent_id = None
|
||||||
|
finally:
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
return agent_id
|
||||||
|
|
||||||
|
def update_agent(self, agent_id, agent_info):
|
||||||
|
"""
|
||||||
|
Updates an existing agent with the server.
|
||||||
|
|
||||||
|
This method will update an existing agent with the server. It will query
|
||||||
|
the database for the agent with the provided agent_id, and then update
|
||||||
|
the agent's information with the provided agent_info. If the agent does
|
||||||
|
not exist, it will create a new Agent instance with the provided agent_info
|
||||||
|
and add it to the database. If there is an error updating the agent, it
|
||||||
|
will log the error.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
agent_id (int): The id of the agent to update.
|
||||||
|
agent_info (dict): The information about the agent to update.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
session = self.session()
|
||||||
|
try:
|
||||||
|
agent = session.query(Agent).filter_by(id=agent_id).first()
|
||||||
|
if not agent:
|
||||||
|
agent = Agent(**agent_info)
|
||||||
|
session.add(agent)
|
||||||
|
else:
|
||||||
|
agent_info["last_seen"] = datetime.utcnow()
|
||||||
|
for key, value in agent_info.items():
|
||||||
|
setattr(agent, key, value)
|
||||||
|
session.commit()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error updating agent {agent_id}: {e}")
|
||||||
|
session.rollback()
|
||||||
|
finally:
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
server = Server("0.0.0.0", 9999)
|
||||||
|
server.start()
|
0
server/src/utils/__init__.py
Normal file
0
server/src/utils/__init__.py
Normal file
0
server/tests/__init__.py
Normal file
0
server/tests/__init__.py
Normal file
Loading…
Reference in New Issue
Block a user