-
-
Notifications
You must be signed in to change notification settings - Fork 113
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #56 from fkie-cad/refactoring-base-class
base class
- Loading branch information
Showing
38 changed files
with
879 additions
and
4,049 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
from __future__ import annotations | ||
|
||
from abc import ABC | ||
from cgi import FieldStorage | ||
from contextlib import suppress | ||
from random import choice | ||
|
||
from twisted.web.resource import Resource | ||
|
||
from honeypots.base_server import BaseServer | ||
from honeypots.helper import load_template, get_headers_and_ip_from_request, check_bytes | ||
|
||
|
||
class BaseHttpServer(BaseServer, ABC): | ||
def __init__(self, **kwargs): | ||
super().__init__(**kwargs) | ||
self.mocking_server = choice( | ||
[ | ||
"Apache", | ||
"nginx", | ||
"Microsoft-IIS/7.5", | ||
"Microsoft-HTTPAPI/2.0", | ||
"Apache/2.2.15", | ||
"SmartXFilter", | ||
"Microsoft-IIS/8.5", | ||
"Apache/2.4.6", | ||
"Apache-Coyote/1.1", | ||
"Microsoft-IIS/7.0", | ||
"Apache/2.4.18", | ||
"AkamaiGHost", | ||
"Apache/2.2.25", | ||
"Microsoft-IIS/10.0", | ||
"Apache/2.2.3", | ||
"nginx/1.12.1", | ||
"Apache/2.4.29", | ||
"cloudflare", | ||
"Apache/2.2.22", | ||
] | ||
) | ||
|
||
class MainResource(Resource): | ||
isLeaf = True | ||
home_file = load_template("home.html") | ||
login_file = load_template("login.html") | ||
|
||
def __init__(self, *args, hp_server=None, **kwargs): | ||
super().__init__(*args, **kwargs) | ||
self.hp_server = hp_server | ||
self.headers = {} | ||
|
||
def render(self, request): | ||
client_ip, headers = get_headers_and_ip_from_request(request, self.hp_server.options) | ||
|
||
with suppress(Exception): | ||
log_data = { | ||
"server": self.hp_server.NAME, | ||
"action": "connection", | ||
"src_ip": client_ip, | ||
"src_port": request.getClientAddress().port, | ||
"dest_ip": self.hp_server.ip, | ||
"dest_port": self.hp_server.port, | ||
} | ||
if "capture_commands" in self.hp_server.options: | ||
log_data["data"] = headers | ||
self.hp_server.logs.info(log_data) | ||
|
||
if self.hp_server.mocking_server != "": | ||
request.responseHeaders.removeHeader("Server") | ||
request.responseHeaders.addRawHeader("Server", self.hp_server.mocking_server) | ||
|
||
if request.method in (b"GET", b"POST"): | ||
self.hp_server.logs.info( | ||
{ | ||
"server": self.hp_server.NAME, | ||
"action": request.method.decode(), | ||
"src_ip": client_ip, | ||
"src_port": request.getClientAddress().port, | ||
"dest_ip": self.hp_server.ip, | ||
"dest_port": self.hp_server.port, | ||
} | ||
) | ||
|
||
if request.method == b"GET": | ||
if ( | ||
request.uri == b"/login.html" | ||
and self.hp_server.username != "" | ||
and self.hp_server.password != "" | ||
): | ||
request.responseHeaders.addRawHeader( | ||
"Content-Type", "text/html; charset=utf-8" | ||
) | ||
return self.login_file | ||
|
||
request.responseHeaders.addRawHeader("Content-Type", "text/html; charset=utf-8") | ||
return self.login_file | ||
|
||
if request.method == b"POST": | ||
self.headers = request.getAllHeaders() | ||
if ( | ||
request.uri in (b"/login.html", b"/") | ||
and self.hp_server.username != "" | ||
and self.hp_server.password != "" | ||
): | ||
form = FieldStorage( | ||
fp=request.content, | ||
headers=self.headers, | ||
environ={ | ||
"REQUEST_METHOD": "POST", | ||
"CONTENT_TYPE": self.headers.get( | ||
b"content-type", | ||
b"application/x-www-form-urlencoded", | ||
), | ||
}, | ||
) | ||
if "username" in form and "password" in form: | ||
username = check_bytes(form["username"].value) | ||
password = check_bytes(form["password"].value) | ||
self.hp_server.log_login( | ||
username, password, client_ip, request.getClientAddress().port | ||
) | ||
|
||
request.responseHeaders.addRawHeader("Content-Type", "text/html; charset=utf-8") | ||
return self.home_file |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
from __future__ import annotations | ||
|
||
import inspect | ||
from abc import ABC, abstractmethod | ||
from os import getenv | ||
from shlex import split | ||
from subprocess import Popen | ||
from uuid import uuid4 | ||
|
||
from honeypots.helper import ( | ||
setup_logger, | ||
set_local_vars, | ||
set_up_error_logging, | ||
close_port_wrapper, | ||
kill_server_wrapper, | ||
get_free_port, | ||
check_if_server_is_running, | ||
) | ||
|
||
|
||
class BaseServer(ABC): | ||
NAME = "base" | ||
DEFAULT_PORT = 0 | ||
DEFAULT_USERNAME = "test" | ||
DEFAULT_PASSWORD = "test" | ||
|
||
def __init__(self, **kwargs): | ||
self.auto_disabled = None | ||
self.process = None | ||
self.uuid = f"honeypotslogger_{__class__.__name__}_{str(uuid4())[:8]}" | ||
self.config = kwargs.get("config", "") | ||
if self.config: | ||
self.logs = setup_logger(__class__.__name__, self.uuid, self.config) | ||
set_local_vars(self, self.config) | ||
else: | ||
self.logs = setup_logger(__class__.__name__, self.uuid, None) | ||
self.ip = kwargs.get("ip", None) or (hasattr(self, "ip") and self.ip) or "0.0.0.0" | ||
self.port = ( | ||
(kwargs.get("port", None) and int(kwargs.get("port", None))) | ||
or (hasattr(self, "port") and self.port) | ||
or self.DEFAULT_PORT | ||
) | ||
self.username = ( | ||
kwargs.get("username") | ||
or (hasattr(self, "username") and self.username) | ||
or self.DEFAULT_USERNAME | ||
) | ||
self.password = ( | ||
kwargs.get("password") | ||
or (hasattr(self, "password") and self.password) | ||
or self.DEFAULT_PASSWORD | ||
) | ||
self.options = ( | ||
kwargs.get("options", "") | ||
or (hasattr(self, "options") and self.options) | ||
or getenv("HONEYPOTS_OPTIONS", "") | ||
or "" | ||
) | ||
self.logger = set_up_error_logging() | ||
|
||
def close_port(self): | ||
return close_port_wrapper(self.NAME, self.ip, self.port, self.logs) | ||
|
||
def kill_server(self): | ||
return kill_server_wrapper(self.NAME, self.uuid, self.process) | ||
|
||
@abstractmethod | ||
def server_main(self): | ||
pass | ||
|
||
def run_server(self, process: bool = False, auto: bool = False) -> bool | None: | ||
status = "error" | ||
run = False | ||
if not process: | ||
self.server_main() | ||
return None | ||
|
||
if auto and not self.auto_disabled: | ||
port = get_free_port() | ||
if port > 0: | ||
self.port = port | ||
run = True | ||
elif self.close_port() and self.kill_server(): | ||
run = True | ||
|
||
if run: | ||
file = inspect.getfile(self.__class__) | ||
command = ( | ||
f"python3 {file} --custom --ip {self.ip} " f"--port {self.port} --uuid {self.uuid}" | ||
) | ||
if self.options: | ||
command += f" --options '{self.options}'" | ||
if self.config: | ||
command += f" --config '{self.config}'" | ||
self.process = Popen(split(command)) | ||
if self.process.poll() is None and check_if_server_is_running(self.uuid): | ||
status = "success" | ||
|
||
self.logs.info( | ||
{ | ||
"server": self.NAME, | ||
"action": "process", | ||
"status": status, | ||
"src_ip": self.ip, | ||
"src_port": self.port, | ||
"dest_ip": self.ip, | ||
"dest_port": self.port, | ||
} | ||
) | ||
|
||
if status == "success": | ||
return True | ||
self.kill_server() | ||
return False | ||
|
||
def log_login(self, username: str, password: str, ip: str, port: int): | ||
status = "success" if self._login_is_correct(username, password) else "failed" | ||
self.logs.info( | ||
{ | ||
"server": self.NAME, | ||
"action": "login", | ||
"status": status, | ||
"src_ip": ip, | ||
"src_port": port, | ||
"username": username, | ||
"password": password, | ||
"dest_ip": self.ip, | ||
"dest_port": self.port, | ||
} | ||
) | ||
|
||
def _login_is_correct(self, username: str, password: str) -> bool: | ||
return username == self.username and password == self.password |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<link rel='stylesheet' href='https://stackpath.bootstrapcdn.com/bootstrap/4.0.0-beta.3/css/bootstrap.min.css' /> | ||
<link rel='stylesheet' href='https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css' /> | ||
<meta http-equiv='content-type' content='text/html;charset=utf-8' /> | ||
<title>Login</title> | ||
<style> | ||
body,html{height: 100%;text-align: center;} | ||
</style> | ||
</head> | ||
<body> | ||
<div class='container-fluid h-100'> | ||
<div class='row justify-content-center h-100 align-items-center'> | ||
<div class='col col-xl-3'> | ||
<b>We'll back soon..</b> | ||
</div> | ||
</div> | ||
</div> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<link rel='stylesheet' href='https://stackpath.bootstrapcdn.com/bootstrap/4.0.0-beta.3/css/bootstrap.min.css' /> | ||
<link rel='stylesheet' href='https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css' /> | ||
<meta http-equiv='content-type' content='text/html;charset=utf-8' /> | ||
<title>Login</title> | ||
<style>body,html {height: 100%;}</style> | ||
</head> | ||
<body> | ||
<div class='container-fluid h-100'> | ||
<div class='row justify-content-center h-100 align-items-center'> | ||
<div class='col col-xl-3'> | ||
<form id='login' action='' method='post'> | ||
<div class='form-group'> | ||
<input class='form-control form-control-sm' name='username' type='text' placeholder='username' id='username'> | ||
</div> | ||
<div class='form-group'> | ||
<input class='form-control form-control-sm' name='password' type='password' placeholder='password' id='password'> | ||
</div> | ||
<div class='form-group'> | ||
<button class='btn btn-default btn-sm btn-block' type='submit'>login</button> | ||
</div> | ||
</form> | ||
</div> | ||
</div> | ||
</div> | ||
</body> | ||
</html> |
Oops, something went wrong.