Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lerc_client/lercConsole/App.config
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
<appSettings>
<add key="logTarget" value="console" />
Expand Down
4 changes: 3 additions & 1 deletion lerc_client/lercConsole/lercConsole.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>lercConsole</RootNamespace>
<AssemblyName>lercConsole</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
Expand All @@ -22,6 +22,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
Expand All @@ -31,6 +32,7 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
Expand Down
2 changes: 1 addition & 1 deletion lerc_client/lercLib/lercLib.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>lercLib</RootNamespace>
<AssemblyName>lercLib</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
Expand Down
2 changes: 1 addition & 1 deletion lerc_client/lercService/App.config
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
<appSettings>
<add key="logTarget" value="file" />
Expand Down
6 changes: 3 additions & 3 deletions lerc_client/lercService/lercService.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>lercService</RootNamespace>
<AssemblyName>lerc</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<PublishUrl>publish\</PublishUrl>
Expand Down Expand Up @@ -92,9 +92,9 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.5.2">
<BootstrapperPackage Include=".NETFramework,Version=v4.8">
<Visible>False</Visible>
<ProductName>Microsoft .NET Framework 4.5.2 %28x86 and x64%29</ProductName>
<ProductName>Microsoft .NET Framework 4.8 %28x86 and x64%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
Expand Down
2 changes: 1 addition & 1 deletion lerc_client/packages.config
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="11.0.2" targetFramework="net452" />
<package id="Newtonsoft.Json" version="11.0.2" targetFramework="net48" />
</packages>
21 changes: 10 additions & 11 deletions lerc_control/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def main():
parser_run = subparsers.add_parser('run', help="Run a shell command on the host.")
parser_run.add_argument('hostname', help="the host you'd like to work with")
parser_run.add_argument('command', help='The shell command for the host to execute`')
parser_run.add_argument('-a', '--async', action='store_true', help='Set asynchronous to true (do NOT wait for output or command to complete)')
parser_run.add_argument('-a', '--asynchronous', action='store_true', help='Set asynchronous to true (do NOT wait for output or command to complete)')
parser_run.add_argument('-p', '--print-only', action='store_true', help='Only print results to screen.')
parser_run.add_argument('-w', '--write-only', action='store_true', help='Only write results to file.')
parser_run.add_argument('-o', '--output-filename', default=None, action='store', help='Specify the name of the file to write any results to.')
Expand Down Expand Up @@ -324,25 +324,24 @@ def main():
sys.exit(0)
logger.info("Attempting to deploy lerc with CarbonBlack..")
try:
from cbapi.response import CbResponseAPI
from cbapi.psc.threathunter import CbThreatHunterAPI
from cbc_sdk import CBCloudAPI
from cbinterface.cli import load_configured_environments
from cbinterface.psc.device import find_device_by_hostname
from cbinterface.enterprise_edr.device import find_device_by_hostname
from lerc_control.deploy_lerc import deploy_lerc, CbSensor_search
except:
logger.error("Failed to import deployment functions. Install and configure cbinterface, if you have Carbon Black.")
except Exception as e:
logger.error(f"{e}Failed to import deployment functions. Install and configure cbinterface, if you have Carbon Black.")
sys.exit(1)
logging.getLogger('lerc_control.deploy_lerc').setLevel(logging.ERROR)

device_or_sensor = None
configured_environments = load_configured_environments()
if "psc" in configured_environments or "cbc" in configured_environments:
if "enterprise_edr" in configured_environments or "cbc" in configured_environments:
# search here first
logger.info(f"searching for device...")
profiles = configured_environments.get("psc", [])
profiles = configured_environments.get("enterprise_edr", [])
profiles.extend(configured_environments.get("cbc", []))
for profile in profiles:
cb = CbThreatHunterAPI(profile=profile)
cb = CBCloudAPI(profile=profile)
device_or_sensor = find_device_by_hostname(cb, args.hostname)
if device_or_sensor:
break
Expand Down Expand Up @@ -509,8 +508,8 @@ def main():
# Else, see if we're running a command directly
cmd = None
if args.instruction == 'run':
if args.async:
cmd = client.Run(args.command, async=args.async)
if args.asynchronous:
cmd = client.Run(args.command, asynchronous=args.asynchronous)
else:
cmd = client.Run(args.command)

Expand Down
19 changes: 7 additions & 12 deletions lerc_control/collect.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import pprint
import logging
import configparser
import sys

from lerc_control.scripted import load_script, execute_script
from lerc_control import lerc_api
Expand Down Expand Up @@ -206,10 +207,10 @@ def full_collection(lerc):

logger.info("Starting full Live Response collection on {}.".format(lerc.hostname))

# for contriving the output filename
local_date_str_cmd = lerc.Run('date /t')
# Delete any existing LR artifacts
lerc.Run("DEL /S /F /Q lr && rmdir /S /Q lr")
lerc.Run("DEL /F /Q lr.exe && DEL /S /F /Q lr && rmdir /S /Q lr")
# for contriving the output filename
local_date_str_cmd = lerc.Run('powershell -NoProfile -Command "Get-Date -Format \'yyyyMMdd\'"')
# download the package
lr_download = lerc.Download(lr_path)
logger.info("Issued CID={} for client to download {}.".format(lr_download.id, lr_path))
Expand All @@ -226,14 +227,8 @@ def full_collection(lerc):
if local_date_str_cmd.status == 'COMPLETE':
dateStr = local_date_str_cmd.get_results(return_content=True).decode('utf-8')
logger.debug("Got date string of '{}'".format(dateStr))
try:
# Mon 11/19/2018 -> 20181119
dateStr = dateStr.split(' ')[1].split('/')
dateStr = dateStr[2]+dateStr[0]+dateStr[1]
# hostname.upper() because streamline.py expects uppercase
output_filename = lerc.hostname.upper() + "." + dateStr + ".7z"
except IndexError:
logger.warning("Unexpected date string obtained. Got '{}'".format(dateStr))
# hostname.upper() because streamline.py expects uppercase
output_filename = lerc.hostname.upper() + "." + dateStr.strip() + ".7z"
break
# wait five seconds before asking the server again
time.sleep(5)
Expand Down Expand Up @@ -293,7 +288,7 @@ def full_collection(lerc):
upload_command.get_results(file_path=output_filename)
# Call steamline on the 7z lr package
logger.info("[+] Starting streamline on {}".format(output_filename))
args = shlex.split(streamline_path + " " + output_filename)
args = shlex.split(f"{sys.executable} {streamline_path} {output_filename}")
try:
subprocess.Popen(args).wait()
logger.info("[+] Streamline complete")
Expand Down
20 changes: 10 additions & 10 deletions lerc_control/deploy_lerc.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@
logger = logging.getLogger("lerc_control."+__name__)

try:
from cbapi.psc import Device
from cbapi.psc.threathunter import CbThreatHunterAPI
from cbc_sdk.platform.devices import Device
from cbc_sdk import CBCloudAPI
from cbapi.response import CbResponseAPI, Sensor
from cbapi.errors import ConnectionError, UnauthorizedError, ServerError, ClientError
from cbc_sdk.errors import ConnectionError, UnauthorizedError, ServerError, ClientError

from cbinterface.cli import load_configured_environments
from cbinterface.config import get_default_cbapi_product, get_default_cbapi_profile
from cbinterface.config import get_default_cb_product, get_default_cb_profile
from cbinterface.helpers import input_with_timeout
from cbinterface.commands import ExecuteCommand, PutFile, GetFile, DeleteFile

from cbinterface.psc.device import find_device_by_hostname, is_device_online
from cbinterface.enterprise_edr.device import find_device_by_hostname, is_device_online
from cbinterface.response.sensor import make_sensor_query, is_sensor_online
except ModuleNotFoundError:
sys.stderr.write("[ERROR] deploy_lerc only supports deployment with carbon black and cbinterface.")
Expand Down Expand Up @@ -105,7 +105,7 @@ def deploy_lerc(device_or_sensor: Union[Device, Sensor], install_command: str, l

hostname = device = sensor = None
if isinstance(device_or_sensor, Device):
from cbinterface.psc.sessions import CustomLiveResponseSessionManager
from cbinterface.enterprise_edr.sessions import CustomLiveResponseSessionManager
device = device_or_sensor
hostname = device.name[device.name.rfind('\\')+1:] if '\\' in device.name else device.name
elif isinstance(device_or_sensor, Sensor):
Expand Down Expand Up @@ -138,7 +138,7 @@ def deploy_lerc(device_or_sensor: Union[Device, Sensor], install_command: str, l
cb = device_or_sensor._cb

offline = False
timeout = 1200 # default 20 minutes (same used by Cb)
timeout = 900 # default 15 minutes (same used by Cb)
if device and not is_device_online(device):
# Decision point: if the device is NOT online, give the analyst and option to wait
logger.warning(f"{device.id}:{device.name} is offline.")
Expand Down Expand Up @@ -170,18 +170,18 @@ def deploy_lerc(device_or_sensor: Union[Device, Sensor], install_command: str, l
timeout = timeout * 86400

logger.info(f"waiting for active session on device ...")
session_manager = CustomLiveResponseSessionManager(cb, custom_session_keepalive=True)
session_manager = CustomLiveResponseSessionManager(cb, custom_session_keepalive=False)
if not session_manager.wait_for_active_session(device_or_sensor, timeout=timeout):
logger.error(f"reached timeout waiting for active session.")
return False

download = PutFile(lerc_installer_path, 'lercSetup.msi')
download = PutFile(lerc_installer_path, 'C:\\Windows\\System32\\lercSetup.msi')
execute = ExecuteCommand(install_command, wait_for_output=False, wait_timeout=60, wait_for_completion=True)

logger.info(f"submitting commands to download and install lerc.")
if previously_installed:
# delete any old msi package, just in-case
session_manager.submit_command(DeleteFile('lercSetup.msi'), device_or_sensor)
session_manager.submit_command(DeleteFile('C:\\Windows\\System32\\lercSetup.msi'), device_or_sensor)
session_manager.submit_command(download, device_or_sensor)
session_manager.submit_command(execute, device_or_sensor)
session_manager.process_completed_commands() # wait
Expand Down
12 changes: 6 additions & 6 deletions lerc_control/lerc_api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,10 @@ def load_config(profile='default', required_keys=[]):
QUERY_FIELD_DESCRIPTIONS.append({'field': key, 'description': QUERY_FIELD_DICT[key]})

def parse_lerc_server_query(query_str):
"""This function converts a string from field:value pairs into \*\*args that lerc_session.query can recognize.
"""This function converts a string from field:value pairs into args that lerc_session.query can recognize.

:param str query_str: A query string to be parsed.
:return: \*\*args ready for lerc_session.Query()
:return: args ready for lerc_session.Query()
"""
logger = logging.getLogger(__name__+".parse_lerc_server_query")
query_parts = query_str.split()
Expand Down Expand Up @@ -241,13 +241,13 @@ def _issue_command(self, command):
return False


def Run(self, shell_command, async=False):
def Run(self, shell_command, asynchronous=False):
"""Execute a shell command on the host.

:param str shell_command: The command to run on the host
:param bool async: (optional) ``False``: LERC client will stream any results and wait until for completion. ``True``: Execute the command and return immediately.
"""
command = { "operation":"run", "command": shell_command, "async": async }
command = { "operation":"run", "command": shell_command, "async": asynchronous }
return self._issue_command(command)

def Download(self, server_file_path, client_file_path=None, analyst_file_path=None):
Expand Down Expand Up @@ -297,7 +297,7 @@ def list_directory(self, dir_path):
for line in dir_lines:
content = {}
if line:
if line[2] == '/':
if line[2] == '/' or line[2] == '-':
content['name'] = line[line.rfind(' ')+1:]
if 'DIR' in line:
content['type'] = 'DIR'
Expand Down Expand Up @@ -348,7 +348,7 @@ def contain(self):
self.Run('del {}'.format(bat_name))

self.Download(safe_contain_bat_path)
containment_command = self.Run(contain_cmd.format(int(self.sleep_cycle)+5), async=True)
containment_command = self.Run(contain_cmd.format(int(self.sleep_cycle)+5), asynchronous=True)

# Dummy command to give the containment command enough time to execute before lerc kills it with wmic
flag_cmd = self.Run("dir")
Expand Down
2 changes: 1 addition & 1 deletion lerc_control/scripted.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ def execute_script(lerc, script_path, return_result_commands=False, execute_clea
# assuming we never will want to async_run OR write_results_path OR print_results
COMMON_CLEANUP_COMMANDS['RUN'].append(run_string)
continue
cmd = lerc.Run(run_string, async=async_run)
cmd = lerc.Run(run_string, asynchronous=async_run)
command_history[command] = cmd
command_history[command].get_the_results = get_results
command_history[command].write_results_path = write_results_path
Expand Down
5 changes: 3 additions & 2 deletions lerc_control/scripts/collect_browsing_history.ini
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ get_results=no

[command_2]
operation=run
command=BrowsingHistoryView.exe /sort 2 /HistorySource 1 /VisitTimeFilterType 1 /scomma browserhistory.csv
command=BrowsingHistoryView.exe /sort 2 /HistorySource 1 /VisitTimeFilterType 1 /LoadIE 1 /LoadEdge 1 /LoadOpera 1 /LoadBrave 1 /LoadFirefox 1 /LoadChrome 1 /LoadSafari 1 /StopIECacheTask 1 /scomma browserhistory.csv
async_run=no

[command_3]
Expand Down Expand Up @@ -44,4 +44,5 @@ write_results_path={HOSTNAME}_Chrome_Preferences

[command_cleanup]
operation=run
command=del browserhistory.csv Chrome_* BrowsingHistoryView.exe get_chrome_data.bat Firefox*
command=del browserhistory.csv Chrome_* BrowsingHistoryView.exe get_chrome_data.bat Firefox*

Binary file modified lerc_control/tools/BrowsingHistoryView.exe
Binary file not shown.
4 changes: 2 additions & 2 deletions lerc_control/upgrade_clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def upgrade_host(hostname, upgrade_bat_path, lerc_msi_path):

#. Drop lercSetup.msi
#. Drop upgrade.bat
#. Execute upgrade.bat async=True with correct params ex. -> upgrade.bat 0 15 2048 "https://your-server-address/" -> company=0 reconnectdelay=15 chunksize=2048 serverurls="https://your-server-address/"
#. Execute upgrade.bat async_run=True with correct params ex. -> upgrade.bat 0 15 2048 "https://your-server-address/" -> company=0 reconnectdelay=15 chunksize=2048 serverurls="https://your-server-address/"
#. Issue Quit command to host
"""

Expand All @@ -62,7 +62,7 @@ def upgrade_host(hostname, upgrade_bat_path, lerc_msi_path):
host_commands.append(result)

run_cmd = config['default']['upgrade_cmd']
result = host.Run(run_cmd.format(host.id), async=True)
result = host.Run(run_cmd.format(host.id), async_run=True)
host_commands.append(result)

result = host.Quit()
Expand Down
22 changes: 11 additions & 11 deletions lerc_server/host_status_maintainer.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import pymysql
import pymysql.cursors

from datetime import datetime, timedelta
from datetime import datetime, timedelta, UTC


BASE_DIR = os.path.dirname(os.path.realpath(__file__))
Expand All @@ -26,8 +26,13 @@
offline_timeout = int(config['host_checker']['offline_timeout'])

# Connect to Db
db = pymysql.connect(DB_server, DB_user, DB_userpass, "lerc",
cursorclass=pymysql.cursors.DictCursor)
db = pymysql.connect(
host=DB_server,
user=DB_user,
password=DB_userpass,
database="lerc",
cursorclass=pymysql.cursors.DictCursor
)

# configure some logging
logging.basicConfig(format='[%(levelname)s] %(asctime)s - %(name)s - %(message)s',
Expand All @@ -46,21 +51,17 @@ def status_update():
c.execute("SELECT * FROM clients")
for client in c.fetchall():
if client['status'] == "BUSY":
away_time = datetime.utcnow() - client['last_activity']
away_time = datetime.now(UTC) - client['last_activity'].replace(tzinfo=UTC)
with db.cursor() as tmp_c:
tmp_c.execute("SELECT operation,command_id FROM commands WHERE hostname='{}' AND status='STARTED'".format(client['hostname']))
command = tmp_c.fetchone()
if command is None:
LOGGER.error("{} is in BUSY state with no STARTED commands in queue. Away time='{}' - Setting UNKNOWN".format(client['hostname'], away_time))
tmp_c.execute("UPDATE clients SET status='UNKNOWN' WHERE hostname='{}'".format(client['hostname']))
elif away_time > timedelta(seconds=client['sleep_cycle']+2):
c.execute("UPDATE clients SET status='OFFLINE' WHERE hostname='{}'".format(client['hostname']))
LOGGER.info("Set {} to OFFLINE: Exceeded it's next expected check-in by '{}'".format(client['hostname'],
str(away_time - timedelta(seconds=client['sleep_cycle']))))
else:
LOGGER.info("{} is in a BUSY state working on CID={} since '{}'".format(client['hostname'], command['command_id'], client['last_activity']))
elif client['status'] != "UNKNOWN" and client['status'] != "UNINSTALLED" and client['status'] != "OFFLINE":
away_time = datetime.utcnow() - client['last_activity']
away_time = datetime.now(UTC) - client['last_activity'].replace(tzinfo=UTC)
LOGGER.debug("{} hasn't fetched in '{}'".format(client['hostname'], away_time))
if away_time > timedelta(days=offline_timeout):
c.execute("UPDATE clients SET status='UNKNOWN' WHERE hostname='{}'".format(client['hostname']))
Expand All @@ -76,9 +77,8 @@ def status_update():
return

if __name__ == '__main__':

status_update()
time.sleep(30)
time.sleep(25)
status_update()
db.close()

Expand Down
Loading