Merge branch 'develop' of https://github.com/wh1te909/tacticalrmm into develop

This commit is contained in:
tremor021
2021-04-14 15:48:37 +02:00
21 changed files with 399 additions and 13 deletions

View File

@@ -1,6 +1,7 @@
from django.contrib import admin
from .models import CoreSettings, CustomField
from .models import CoreSettings, CustomField, CodeSignToken
admin.site.register(CoreSettings)
admin.site.register(CustomField)
admin.site.register(CodeSignToken)

View File

@@ -0,0 +1,20 @@
# Generated by Django 3.2 on 2021-04-13 05:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0018_auto_20210329_1709'),
]
operations = [
migrations.CreateModel(
name='CodeSignToken',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('token', models.CharField(blank=True, max_length=255, null=True)),
],
),
]

View File

@@ -266,3 +266,16 @@ class CustomField(models.Model):
return self.default_value_bool
else:
return self.default_value_string
class CodeSignToken(models.Model):
token = models.CharField(max_length=255, null=True, blank=True)
def save(self, *args, **kwargs):
if not self.pk and CodeSignToken.objects.exists():
raise ValidationError("There can only be one CodeSignToken instance")
super(CodeSignToken, self).save(*args, **kwargs)
def __str__(self):
return "Code signing token"

View File

@@ -1,7 +1,7 @@
import pytz
from rest_framework import serializers
from .models import CoreSettings, CustomField
from .models import CoreSettings, CustomField, CodeSignToken
class CoreSettingsSerializer(serializers.ModelSerializer):
@@ -27,3 +27,9 @@ class CustomFieldSerializer(serializers.ModelSerializer):
class Meta:
model = CustomField
fields = "__all__"
class CodeSignTokenSerializer(serializers.ModelSerializer):
class Meta:
model = CodeSignToken
fields = "__all__"

View File

@@ -1,3 +1,4 @@
import requests
from unittest.mock import patch
from channels.db import database_sync_to_async
@@ -12,6 +13,28 @@ from .serializers import CustomFieldSerializer
from .tasks import core_maintenance_tasks
class TestCodeSign(TacticalTestCase):
def setUp(self):
self.setup_coresettings()
self.authenticate()
self.url = "/core/codesign/"
def test_get_codesign(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
self.check_not_authenticated("get", self.url)
@patch("requests.post")
def test_edit_codesign_timeout(self, mock_post):
mock_post.side_effect = requests.exceptions.ConnectionError()
data = {"token": "token123"}
r = self.client.patch(self.url, data, format="json")
self.assertEqual(r.status_code, 400)
self.check_not_authenticated("patch", self.url)
class TestConsumers(TacticalTestCase):
def setUp(self):
self.setup_coresettings()

View File

@@ -12,4 +12,5 @@ urlpatterns = [
path("servermaintenance/", views.server_maintenance),
path("customfields/", views.GetAddCustomFields.as_view()),
path("customfields/<int:pk>/", views.GetUpdateDeleteCustomFields.as_view()),
path("codesign/", views.CodeSign.as_view()),
]

View File

@@ -11,8 +11,12 @@ from rest_framework.views import APIView
from tacticalrmm.utils import notify_error
from .models import CoreSettings, CustomField
from .serializers import CoreSettingsSerializer, CustomFieldSerializer
from .models import CoreSettings, CustomField, CodeSignToken
from .serializers import (
CoreSettingsSerializer,
CustomFieldSerializer,
CodeSignTokenSerializer,
)
class UploadMeshAgent(APIView):
@@ -179,3 +183,48 @@ class GetUpdateDeleteCustomFields(APIView):
get_object_or_404(CustomField, pk=pk).delete()
return Response("ok")
class CodeSign(APIView):
def get(self, request):
token = CodeSignToken.objects.first()
return Response(CodeSignTokenSerializer(token).data)
def patch(self, request):
import requests
errors = []
for url in settings.EXE_GEN_URLS:
try:
r = requests.post(
f"{url}/api/v1/checktoken",
json={"token": request.data["token"]},
headers={"Content-type": "application/json"},
timeout=15,
)
except Exception as e:
errors.append(str(e))
else:
errors = []
break
if errors:
return notify_error(", ".join(errors))
if r.status_code == 400 or r.status_code == 401: # type: ignore
return notify_error(r.json()["ret"]) # type: ignore
elif r.status_code == 200: # type: ignore
t = CodeSignToken.objects.first()
if t is None:
CodeSignToken.objects.create(token=request.data["token"])
else:
serializer = CodeSignTokenSerializer(instance=t, data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response("Token was saved")
try:
ret = r.json()["ret"] # type: ignore
except:
ret = "Something went wrong"
return notify_error(ret)

View File

@@ -116,6 +116,15 @@
"shell": "powershell",
"category": "TRMM (Win):Storage"
},
{
"guid": "72b93487-0266-43f0-97cc-03d4c7ee0b44",
"filename": "Win_Bitlocker_Get_Recovery_Keys.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "Bitlocker - Get Recovery Keys",
"description": "Retreives a Bitlocker Recovery Keys",
"shell": "powershell",
"category": "TRMM (Win):Storage"
},
{
"guid": "cfa14c28-4dfc-4d4e-95ee-a380652e058d",
"filename": "Win_Bios_Check.ps1",
@@ -125,6 +134,15 @@
"shell": "powershell",
"category": "TRMM (Win):Hardware"
},
{
"guid": "e1c27982-b955-4766-85b6-d92527a177cf",
"filename": "Win_Hardware_Monitor_Get_Info.ps1",
"submittedBy": "https://github.com/MaxAnderson95/",
"name": "Monitor - Get Info",
"description": "Retreives and reports on Monitor info: Manufacturer, Model, Serial",
"shell": "powershell",
"category": "TRMM (Win):Hardware"
},
{
"guid": "ae231ac4-b01f-4a39-a9d2-3d817af75260",
"filename": "Win_Hardware_RAM_Status.ps1",
@@ -260,6 +278,15 @@
"shell": "cmd",
"category": "TRMM (Win):Power"
},
{
"guid": "f628a02b-16c3-4ab5-b788-dec5bc2af1d9",
"filename": "Win_Power_Disable_Hibernation.bat",
"submittedBy": "https://github.com/silversword411",
"name": "Power - Hibernate Disable",
"description": "Disables Hibernation",
"shell": "cmd",
"category": "TRMM (Win):Power"
},
{
"guid": "2472bbaf-1941-4722-8a58-d1dd0f528801",
"filename": "Win_Update_Tactical_Exclusion.ps1",
@@ -318,11 +345,38 @@
"guid": "71090fc4-faa6-460b-adb0-95d7863544e1",
"filename": "Win_Check_Events_for_Bluescreens.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Event Viewer - Check for Bluescreens",
"description": "This will check for Bluescreen events on your system",
"name": "Event Viewer - Bluescreen Notification",
"description": "Event Viewer Monitor - Notify Bluescreen events on your system",
"shell": "powershell",
"category": "TRMM (Win):Monitoring"
},
{
"guid": "8373846f-facc-49b9-9891-3a780a394c89",
"filename": "Win_Local_User_Created_Monitor.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Event Viewer - New User Notification",
"description": "Event Viewer Monitor - Notify when new Local user is created",
"shell": "powershell",
"category": "TRMM (Win):Monitoring"
},
{
"guid": "65e5cef1-8338-4180-a0bc-cd54e62de690",
"filename": "Win_Task_Scheduler_New_Items_Monitor.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Event Viewer - Task Scheduler New Item Notification",
"description": "Event Viewer Monitor - Notify when new Task Scheduler item is created",
"shell": "powershell",
"category": "TRMM (Win):Monitoring"
},
{
"guid": "08ca81f2-f044-4dfc-ad47-090b19b19d76",
"filename": "Win_User_Logged_in_with_Temp_Profile.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "User Logged in with temp profile check",
"description": "Check if users are logged in with a temp profile",
"shell": "powershell",
"category": "TRMM (Win):Other"
},
{
"guid": "5d905886-9eb1-4129-8b81-a013f842eb24",
"filename": "Win_Rename_Computer.ps1",
@@ -453,6 +507,15 @@
"shell": "powershell",
"category": "TRMM (Win):Azure>AD"
},
{
"guid": "7d81859a-1ba3-42b0-8664-29844f0dd765",
"filename": "Win_Azure_Mars_Cloud_Backup_Status.ps1",
"submittedBy": "https://github.com/dinger1986",
"name": "Azure - Mars Cloud backup Status",
"description": "Azure - Mars Cloud backup Check Status",
"shell": "powershell",
"category": "TRMM (Win):Azure>Backup"
},
{
"guid": "e18c64d0-b783-4b52-b44b-9bb7592b439b",
"filename": "Win_FileSystem_Enable_Long_Paths.bat",
@@ -462,6 +525,24 @@
"shell": "cmd",
"category": "TRMM (Win):Storage"
},
{
"guid": "c6252ca8-5172-42ea-9114-e447f80868f5",
"filename": "Win_USB_Disable_Access.bat",
"submittedBy": "https://github.com/silversword411",
"name": "USB - Disable Access",
"description": "USB - Disable Plugged in USB devices",
"shell": "cmd",
"category": "TRMM (Win):Storage"
},
{
"guid": "3785952f-69fb-4bda-b2fe-5e3e8642738a",
"filename": "Win_USB_Enable_Access.bat",
"submittedBy": "https://github.com/silversword411",
"name": "USB - Enable Access",
"description": "USB - Enable Plugged in USB devices",
"shell": "cmd",
"category": "TRMM (Win):Storage"
},
{
"guid": "c6014da2-b188-4e1b-b96a-e3440ade3a6a",
"filename": "Win_RecycleBin_Empty.ps1",

View File

@@ -245,12 +245,39 @@ class TestScriptViews(TacticalTestCase):
Script.load_community_scripts()
community_scripts = Script.objects.filter(script_type="builtin").count()
self.assertEqual(len(info), community_scripts)
community_scripts_count = Script.objects.filter(script_type="builtin").count()
if len(info) != community_scripts_count:
raise Exception(
f"There are {len(info)} scripts in json file but only {community_scripts_count} in database"
)
# test updating already added community scripts
Script.load_community_scripts()
self.assertEqual(len(info), community_scripts)
community_scripts_count2 = Script.objects.filter(script_type="builtin").count()
self.assertEqual(len(info), community_scripts_count2)
def test_community_script_has_jsonfile_entry(self):
with open(
os.path.join(settings.BASE_DIR, "scripts/community_scripts.json")
) as f:
info = json.load(f)
filenames = [i["filename"] for i in info]
# normal
if not settings.DOCKER_BUILD:
scripts_dir = os.path.join(Path(settings.BASE_DIR).parents[1], "scripts")
# docker
else:
scripts_dir = settings.SCRIPTS_DIR
with os.scandir(scripts_dir) as it:
for f in it:
if not f.name.startswith(".") and f.is_file():
if f.name not in filenames:
raise Exception(
f"{f.name} is missing an entry in community_scripts.json"
)
def test_script_filenames_do_not_contain_spaces(self):
with open(
@@ -259,4 +286,5 @@ class TestScriptViews(TacticalTestCase):
info = json.load(f)
for script in info:
fn: str = script["filename"]
self.assertTrue(" " not in fn)
if " " in fn:
raise Exception(f"{fn} must not contain spaces in filename")

View File

@@ -34,8 +34,8 @@ DL_64 = f"https://github.com/wh1te909/rmmagent/releases/download/v{LATEST_AGENT_
DL_32 = f"https://github.com/wh1te909/rmmagent/releases/download/v{LATEST_AGENT_VER}/winagent-v{LATEST_AGENT_VER}-x86.exe"
EXE_GEN_URLS = [
"https://exe2.tacticalrmm.io/api/v1/exe",
"https://exe.tacticalrmm.io/api/v1/exe",
"https://exe2.tacticalrmm.io",
"https://exe.tacticalrmm.io",
]
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"

View File

@@ -19,6 +19,7 @@ from rest_framework import status
from rest_framework.response import Response
from agents.models import Agent
from core.models import CodeSignToken
logger.configure(**settings.LOG_CONFIG)
@@ -56,6 +57,11 @@ def generate_winagent_exe(
else f"winagent-v{settings.LATEST_AGENT_VER}-x86.exe"
)
try:
codetoken = CodeSignToken.objects.first().token
except:
codetoken = ""
data = {
"client": client,
"site": site,
@@ -68,6 +74,7 @@ def generate_winagent_exe(
"inno": inno,
"url": settings.DL_64 if arch == "64" else settings.DL_32,
"api": api,
"codesigntoken": codetoken,
}
headers = {"Content-type": "application/json"}
@@ -76,7 +83,7 @@ def generate_winagent_exe(
for url in settings.EXE_GEN_URLS:
try:
r = requests.post(
url,
f"{url}/api/v1/exe",
json=data,
headers=headers,
stream=True,

View File

@@ -0,0 +1,23 @@
$ErrorActionPreference= 'silentlycontinue'
$TimeSpan = (Get-Date) - (New-TimeSpan -Day 1)
##Check for Errors in Backup
if (Get-WinEvent -FilterHashtable @{LogName='CloudBackup/Operational';ID='11','18';StartTime=$TimeSpan})
{
Write-Host "Cloud Backup Mars Ended with Errors"
Get-WinEvent -FilterHashtable @{LogName='CloudBackup/Operational';ID='1','14','11','18','16';StartTime=$TimeSpan}
exit 1
}
else
{
Write-Host "Cloud Backup Mars Backup Is Working Correctly"
Get-WinEvent -FilterHashtable @{LogName='CloudBackup/Operational';ID='1','14','16'}
exit 0
}
Exit $LASTEXITCODE

View File

@@ -0,0 +1 @@
manage-bde -protectors C: -get

View File

@@ -0,0 +1,17 @@
$ErrorActionPreference= 'silentlycontinue'
$TimeSpan = (Get-Date) - (New-TimeSpan -Day 1)
if (Get-WinEvent -FilterHashtable @{LogName='security';ID='4720','4720','4728','4732','4756','4767';StartTime=$TimeSpan})
{
Write-Output "A change has been made to local users"
Get-WinEvent -FilterHashtable @{LogName='security';ID='4720','4720','4728','4732','4756','4767';StartTime=$TimeSpan}
exit 1
}
else
{
Write-Output "No changes all looks fine"
exit 0
}
Exit $LASTEXITCODE

View File

@@ -0,0 +1 @@
%SYSTEMROOT%\System32\powercfg.exe -H OFF

View File

@@ -0,0 +1,18 @@
$ErrorActionPreference= 'silentlycontinue'
$TimeSpan = (Get-Date) - (New-TimeSpan -Day 1)
if (Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-TaskScheduler/Operational';ID='106';StartTime=$TimeSpan} | Where-Object -Property Message -notlike *$env:COMPUTERNAME*)
{
Write-Output "New Task Has Been Added"
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-TaskScheduler/Operational';ID='106';StartTime=$TimeSpan}
Get-WinEvent -FilterHashtable @{LogName='Microsoft-Windows-TaskScheduler/Operational';ID='141';StartTime=$TimeSpan}
exit 1
}
else
{
Write-Output "No changes with Task Scheduler"
exit 0
}
Exit $LASTEXITCODE

View File

@@ -0,0 +1 @@
reg add HKLM\SYSTEM\CurrentControlSet\Services\UsbStor /v "Start" /t REG_DWORD /d "4" /f

View File

@@ -0,0 +1 @@
reg add HKLM\SYSTEM\CurrentControlSet\Services\UsbStor /v "Start" /t REG_DWORD /d "3" /f

View File

@@ -0,0 +1,17 @@
$ErrorActionPreference= 'silentlycontinue'
$TimeSpan = (Get-Date) - (New-TimeSpan -Day 1)
if (Get-WinEvent -FilterHashtable @{LogName='Application';ID='1511';StartTime=$TimeSpan})
{
Write-Output "An account has been logged in with a Temporary profile"
Get-WinEvent -FilterHashtable @{LogName='Application';ID='1511';StartTime=$TimeSpan}
exit 1
}
else
{
Write-Output "All looks fine"
exit 0
}
Exit $LASTEXITCODE

View File

@@ -89,6 +89,10 @@
<q-item clickable v-close-popup @click="showEditCoreSettingsModal = true">
<q-item-section>Global Settings</q-item-section>
</q-item>
<!-- code sign -->
<q-item clickable v-close-popup @click="showCodeSign = true">
<q-item-section>Code Signing</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
@@ -179,6 +183,10 @@
<q-dialog v-model="showServerMaintenance">
<ServerMaintenance @close="showMaintenance = false" />
</q-dialog>
<!-- Code Sign -->
<q-dialog v-model="showCodeSign">
<CodeSign @close="showCodeSign = false" />
</q-dialog>
</q-bar>
</div>
</template>
@@ -201,6 +209,7 @@ import AuditManager from "@/components/AuditManager";
import BulkAction from "@/components/modals/agents/BulkAction";
import Deployment from "@/components/Deployment";
import ServerMaintenance from "@/components/modals/core/ServerMaintenance";
import CodeSign from "@/components/modals/coresettings/CodeSign";
export default {
name: "FileBar",
@@ -217,6 +226,7 @@ export default {
BulkAction,
Deployment,
ServerMaintenance,
CodeSign,
},
data() {
return {
@@ -233,6 +243,7 @@ export default {
showDeployment: false,
showDebugLog: false,
showScriptManager: false,
showCodeSign: false,
};
},
methods: {

View File

@@ -0,0 +1,67 @@
<template>
<q-card style="min-width: 85vh">
<q-card-section class="row items-center">
<div class="text-h6">Code Signing</div>
<q-space />
<q-btn icon="close" flat round dense v-close-popup />
</q-card-section>
<q-form @submit.prevent="editToken">
<q-card-section class="row">
<div class="col-2">Token:</div>
<div class="col-1"></div>
<q-input
outlined
dense
v-model="settings.token"
class="col-9 q-pa-none"
:rules="[val => !!val || 'Token is required']"
/>
</q-card-section>
<q-card-section class="row items-center">
<q-btn label="Save" color="primary" type="submit" />
</q-card-section>
</q-form>
</q-card>
</template>
<script>
import mixins from "@/mixins/mixins";
export default {
name: "CodeSign",
mixins: [mixins],
data() {
return {
settings: {
token: "",
},
};
},
methods: {
getToken() {
this.$axios
.get("/core/codesign/")
.then(r => {
this.settings = r.data;
})
.catch(e => this.notifyError(e.response.data));
},
editToken() {
this.$q.loading.show();
this.$axios
.patch("/core/codesign/", this.settings)
.then(r => {
this.$q.loading.hide();
this.notifySuccess(r.data);
this.$emit("close");
})
.catch(e => {
this.$q.loading.hide();
this.notifyError(e.response.data, 4000);
});
},
},
created() {
this.getToken();
},
};
</script>