117 Commits

Author SHA1 Message Date
Dmitry Ng 74831c4c97 feat: improved localization for new modules: shell, osquery, auditd 2023-06-06 15:19:42 +03:00
Dmitry Ng f1c1e49db1 fix: issue with default server module implementation in osquery modules 2023-05-27 20:17:58 +03:00
Dmitry Ng 0a3f02bc5f fix: rpath value to /usr/lib/vxagent for linux modules 2023-05-25 14:53:54 +03:00
Dmitry Ng a8be3ba0af fix: auditd module tests 2023-05-25 14:50:02 +03:00
Dmitry Ng af941e1c71 fix: tests for osquery and shell modules 2023-05-25 13:47:45 +03:00
Dmitry Ng be3651d827 style: changed liter issues and bringing to a single style of modules 2023-05-25 11:53:27 +03:00
Dmitry Ng a1df667c98 feat: bumped wineventlog module to the latest version 2023-05-25 11:43:15 +03:00
Dmitry Ng dfbdbbc495 feat: bumped file_reader module to the latest version 2023-05-25 11:42:36 +03:00
Dmitry Ng c18af383fd feat: bumped sysmon version up to 14.16 to avoid security issue https://msrc.microsoft.com/update-guide/vulnerability/CVE-2023-29343 2023-05-24 19:46:04 +03:00
Dmitry Ng 599b5180e9 feat: major update of yara scanner module which brings new caching of scans result with policy timeout 2023-05-24 19:39:50 +03:00
Dmitry Ng 1e827cf5a0 feat: bump wineventlog module to the latest version and added topic mechanism to transmit raw events data 2023-05-24 19:09:42 +03:00
Dmitry Ng d19f506558 fix: update correlator_linux bundle to the latest version 2023-05-24 17:34:26 +03:00
Dmitry Ng 8e032785d7 feat: added secure config to osquery_linux module 2023-05-24 17:04:59 +03:00
Dmitry Ng d28ff67b4f feat: added osquery_linux to the target build 2023-05-24 16:57:04 +03:00
Dmitry Ng 094fd0d190 Merge pull request #39 from vxcontrol/osquery_linux
add module osquery_linux v1.0.0
2023-05-24 16:51:02 +03:00
Dmitry Ng ad90fa13b3 fix: added empty security config for osquery module 2023-05-24 15:51:49 +03:00
Dmitry Ng 34a0c99813 Merge pull request #27 from vxcontrol/osquery
add module osquery v1.0.0
2023-05-24 14:39:51 +03:00
Dmitry Ng ce42366da7 Merge branch 'master' into osquery 2023-05-24 14:39:05 +03:00
Dmitry Ng f3b4b61be3 feat: added auditd module to the target build 2023-05-24 14:34:47 +03:00
Dmitry Ng fdcd94b4d9 Merge pull request #46 from cyberok-org/auditd
New module: auditd
2023-05-24 14:21:32 +03:00
Dmitry Ng 4baa30e8c7 Merge pull request #47 from cyberok-org/shell_module
Shell module
2023-05-24 14:17:51 +03:00
Dmitry Ng bf545f1a7a Merge pull request #48 from vxcontrol/bugfix/win_correlator
Bumped windows correlator implementation
2023-05-24 14:11:58 +03:00
Dmitry Ng b3ce04b5c6 fix: bump correlator last update field to the latest version 2023-05-24 13:32:33 +03:00
Dmitry Ng 39c72a5577 fix: update correlator bundle to avoid issue with running it on windows 10 22H2 2023-05-24 12:54:29 +03:00
jnovikov faa553a310 Shell: remove comments and debug prints. 2023-02-12 23:11:19 +00:00
jnovikov afda35651c Shell: add tests, config, fixes issues. 2023-02-12 20:33:12 +00:00
Aleksey Nikitin 03856d1336 auditd: revert luapower 2023-02-10 17:45:45 +03:00
Aleksey Nikitin 7ec96dd84c auditd: fix tests on Oracle Linux 8 2023-02-10 17:08:54 +03:00
Aleksey Nikitin 75dd83eecf auditd: disable installing make inside the testing docker images 2023-02-10 17:08:54 +03:00
Aleksey Nikitin 19514048aa auditd: disable installing systemd inside the testing docker images 2023-02-10 17:08:54 +03:00
Aleksey Nikitin b10cc7bfc0 auditd: update default config 2023-02-10 17:08:54 +03:00
Aleksey Nikitin a0ca8fd1dc auditd: watch after online audit rules 2023-02-10 17:08:54 +03:00
Aleksey Nikitin 2445f9bb0c auditd: refactoring 2023-02-10 17:08:54 +03:00
Aleksey Nikitin 69886543bb auditd: use try helper, refactoring 2023-02-10 17:08:54 +03:00
Aleksey Nikitin ffb0b15cca auditd: handle "error" event 2023-02-10 17:08:54 +03:00
Aleksey Nikitin 5a04c3a8a5 auditd: exec: fix typo 2023-02-10 17:08:54 +03:00
Aleksey Nikitin 77048d99ce auditd: implement audit configuration watcher 2023-02-10 17:08:54 +03:00
Aleksey Nikitin cd842fce75 auditd: provide configuration settings in UI 2023-02-10 17:08:54 +03:00
Aleksey Nikitin e168a06f78 auditd: implement main module loop 2023-02-10 17:08:54 +03:00
Aleksey Nikitin 6687c3e552 auditd: Makefile: rafactor running tests in docker 2023-02-10 17:08:54 +03:00
Aleksey Nikitin 08d0876321 auditd: merge with the empty module template 2023-02-10 17:08:54 +03:00
Aleksey Nikitin 10cc0f34ae auditd: exec: close stdin 2023-02-10 17:08:54 +03:00
Aleksey Nikitin 29ab89ff01 auditd: implement audit+config+rules setup 2023-02-10 17:08:54 +03:00
Aleksey Nikitin 977496eb24 auditd: add helper: managed.ensure_all() 2023-02-10 17:08:54 +03:00
Aleksey Nikitin 298e80e243 auditd: refactoring 2023-02-10 17:08:54 +03:00
Aleksey Nikitin e715eaaf41 auditd: refactor testing inside docker 2023-02-10 17:08:54 +03:00
Aleksey Nikitin 1141e4f8fd auditd: disable sync command for DNF/YUM 2023-02-10 17:08:54 +03:00
Aleksey Nikitin fb768ad945 auditd: refactoring 2023-02-10 17:08:54 +03:00
Aleksey Nikitin 2b13bef44b auditd: set package.path via .busted 2023-02-10 17:08:54 +03:00
Aleksey Nikitin 0a521c7b0a auditd: add helper to manage configuration files 2023-02-10 17:08:54 +03:00
Aleksey Nikitin 0edb31670c auditd: refactoring 2023-02-10 17:08:54 +03:00
Aleksey Nikitin 665fc773ec auditd: pkg.Manager supports alternative names in install() 2023-02-10 17:08:54 +03:00
Aleksey Nikitin 4ea8a4bb93 auditd: refactoring 2023-02-10 17:08:54 +03:00
Aleksey Nikitin 8df2641f71 auditd: support package managers: YUM, DNF 2023-02-10 17:08:54 +03:00
Aleksey Nikitin ab2ae0e487 auditd: refactoring 2023-02-10 17:08:54 +03:00
Aleksey Nikitin c0fd490a93 auditd: refactoring 2023-02-10 17:08:53 +03:00
Aleksey Nikitin 862659a747 auditd: implement package installation 2023-02-10 17:08:53 +03:00
Aleksey Nikitin 0eafc0cfb5 auditd: luajit-busted: fix PATH 2023-02-10 17:08:53 +03:00
Aleksey Nikitin f9ef7b0dcc auditd: improve luajit/busted helpers 2023-02-10 17:08:53 +03:00
Aleksey Nikitin a2c586c809 auditd: add helpers to run luajit/busted 2023-02-10 17:08:53 +03:00
Aleksey Nikitin e017a46a59 auditd: add format_cmd helper 2023-02-10 17:08:53 +03:00
Aleksey Nikitin 281f6d71f8 auditd: add exec helper 2023-02-10 17:08:53 +03:00
jnovikov 9640b655ff Shell: Implemented module.
This change introduces new module that allow to spawn interactive
web-shell on agent.
Module supports Windows, Linux and MacOS.
2023-02-09 23:54:01 +00:00
Dmitry Ng 06b2fab216 fix: yara_scanner module rules json file running on linux 2023-01-26 21:52:40 +03:00
Roman 5aab1e119f add module osquery_linux v1.0.0 2023-01-25 22:29:15 +03:00
Roman fd8b19d458 add module osquery v1.0.0 2023-01-25 21:24:47 +03:00
Dmitry Ng a943faf956 Merge pull request #38 from vxcontrol/fix-luacov-import
fix importing luacov for tests running
2023-01-16 11:23:41 +03:00
Mikhail Kochegarov d5324301ed Feature/improve protocol (#43)
* add args and sent msg validation for vxproto mock

* fix linter config

* remove unused/unpublished events for file_remover module

* add 'data' messages proxying similar to actions
2023-01-16 11:19:36 +03:00
Roman 89c123afa7 fix importing luacov for tests running 2023-01-16 10:59:04 +03:00
Mikhail Kochegarov e26ea21191 preserve actions list while getting them from callback data (#36)
* preserve actions list while getting them from callback data

* small fixes

Co-authored-by: Dmitry Ng <19asdek91@gmail.com>
2023-01-16 10:47:26 +03:00
Dmitry Ng 0e3500583e Merge pull request #41 from cyberok-org/tests-fix-setup
Tests: move effecting code from description to setup
2023-01-16 10:36:57 +03:00
Aleksey Nikitin 127a97795c Suppress debug output by default in the tests (#40)
* Suppress debug output by default in the tests

* Simplify log_level usage
2023-01-15 15:16:06 +03:00
Aleksey Nikitin 909306160c Tests: move effecting code from description to setup 2023-01-10 14:40:28 +03:00
Dmitry Ng 89005d4e34 Merge pull request #7 from vxcontrol/issue-2-add-github-templates
issue-2: add .github templates
2022-12-26 11:42:18 +03:00
Dmitry Ng 43df33625b feat: add recommended extensions 2022-12-22 21:59:04 +03:00
Dmitry Ng 989cc8a0dd fix: linter issues in file_reader and wineventlog modules 2022-12-22 21:57:31 +03:00
Dmitry Ng c5bfb2bc8a fix: bug in file_remover module with proxying browser requests to the agent 2022-12-22 20:05:04 +03:00
Dmitry Ng 16202c0b3c fix: using utils in file_remover module 2022-12-22 19:01:29 +03:00
Dmitry Ng 2e09f2fb59 fix: unexpected args.json file format in file_remover module 2022-12-22 18:41:19 +03:00
Aleksandr Abroskin 8c0cdb92df [issue-2] add templates 2022-12-22 18:28:06 +03:00
Dmitry Ng 40647f6cde Merge pull request #35 from vxcontrol/sync_modules
sync modules 20.12.22
2022-12-22 10:34:34 +03:00
Dmitry Ng 357bb09836 chore: bump last_update modules field to synchronize it on the product installations; affected modules: file_uploader 2022-12-21 23:23:19 +03:00
Dmitry Ng ba8311d7e6 Merge pull request #34 from cyberok-org/ab/XDR-78-2
fix to upload files with cyrillic symbols in filename on windows
2022-12-21 23:20:59 +03:00
Dmitry Ng 5469e06baa chore: bump last_update modules field to synchronize it on the product installations; affected modules: sysmon, file_remover, proc_terminator, file_reader, correlator_linux 2022-12-21 23:05:27 +03:00
Dmitry Ng 7cf37a9683 feat: add tests for proc_terminator module 2022-12-21 22:56:29 +03:00
Dmitry Ng 94c1543b1e fix: missing class name for luacheckrc config 2022-12-21 21:17:53 +03:00
Dmitry Ng 938e000ba3 feat: bump version of sysmon executable files up to v14.13 2022-12-21 21:17:16 +03:00
Dmitry Ng 8ed3604ea2 fix: declaration of winapi constants in proc_terminator module 2022-12-21 21:10:31 +03:00
Dmitry Ng a5030cf001 fix: change vue file style for file_reader module 2022-12-21 21:05:53 +03:00
Dmitry Ng fdd581324c fix: update linux libraries for correlator_linux and file_reader modules to old linux support 2022-12-21 21:04:41 +03:00
BurovAV 793f835932 XDR-78: fix to upload files with cyrillic sympols in filename (on windows) 2022-12-21 20:36:42 +03:00
Dmitry Ng e0f94fda6f Merge pull request #33 from vxcontrol/module_protocol
Common module protocol implementation
2022-12-21 17:39:21 +03:00
Dmitry Ng 67a94ae3b3 Merge pull request #22 from cyberok-org/rewind-log-file-reading-on-demand
Rewind log file reading on demand
2022-12-21 17:35:50 +03:00
Mikhail Kochegarov 701cf72ea1 add junit test results output formatter 2022-12-21 15:24:35 +10:00
Mikhail Kochegarov 616faaa8d5 add file_remover unit tests 2022-12-21 15:24:34 +10:00
Mikhail Kochegarov c6b6df1ce6 refactor file_remover to use new common module protocol implementation 2022-12-21 15:24:33 +10:00
Mikhail Kochegarov 8d829e8c88 remove outdated module tests 2022-12-21 15:24:32 +10:00
Mikhail Kochegarov d962eb358d add common module protocol implementation 2022-12-21 15:24:31 +10:00
Mikhail Kochegarov 7fc9e94903 fix utils/mock 2022-12-21 15:24:30 +10:00
Dmitry Ng 90c1c37c64 Merge pull request #32 from vxcontrol/luapower_repository_link_update
update luapower submodule link
2022-12-20 17:49:08 +03:00
Egor Artemov e1bb789913 Replace eventsAPI and modulesAPI by just api
New modules should use the "api" instead of old ones - "eventsAPI" and
"modulesAPI".
2022-12-20 09:07:10 +00:00
Mikhail Kochegarov 65d769a8c1 update luapower submodule link 2022-12-20 18:56:35 +10:00
Egor Artemov 397de6cbc7 Remove redundant tag
The script tag is already closed.
2022-12-20 08:50:47 +00:00
Egor Artemov 2550dfa54d Rewind log file reading on demand
The file_reader module starts to read files from the end by default. To
start the reading from the beginning, you must rewind this process
manually, which has not yet been implemented.

To do this, now, you may use the new button added to the module's UI to
reset the reading position for the chosen file.
2022-12-20 08:50:25 +00:00
Dmitry Ng a9efaacefb Merge pull request #21 from vxcontrol/add-cmd-exec
add utils/system
2022-12-19 12:07:45 +03:00
Roman f00abef04a add utils/system 2022-12-14 19:54:28 +03:00
Dmitry Ng 36d3b30079 fix typo in the license 2022-12-13 21:36:30 +03:00
Dmitry Ng cec71fe343 Merge pull request #16 from cyberok-org/remove-file_reader-log_files-ui-validation
Remove validation of the file_path parameter in file_reader
2022-12-09 21:00:10 +03:00
Dmitry Ng a9ed215b2c feat: change browser modules api and using css styles (classes) in the vue files 2022-12-09 19:03:48 +03:00
Egor Artemov b6f5fa510c Remove validation of the file_path parameter in file_reader
This parameter had been validating using a simple regexp that doesn't
allow specifying absolute paths and other correct windows paths like
network shares.

Checking the validity of the path for both Windows and Unix systems
simultaneously seems unnecessary in UI since it is almost impossible to
check all correct variants using just regexp.  So instead of correcting
the regexp - I obliterated it.
2022-12-09 13:57:40 +00:00
Dmitry Ng 19b6424398 Merge pull request #5 from vxcontrol/fix_workflow
fix workflow
2022-12-09 15:17:58 +03:00
Dmitry Ng 701fbfc4e7 Merge pull request #6 from vxcontrol/bugfix/yara_validation
fix: validation in yara scanner browser module
2022-12-09 15:13:45 +03:00
Anna Manchenkova b4e61a30bc fix: yara validation 2022-12-07 16:26:08 +03:00
sseuzss 4135277bd8 Optimized Dockerfile 2022-12-06 10:24:57 +05:00
sseuzss 053dc5c142 Fix workflow and optimized Dockerfile 2022-12-06 10:20:54 +05:00
Sergey Uzinger 4d8c670161 Fix build and upload modules (#4)
* Fix upload modules

* Disable latest tag for git tags

* Fix CRLF to LF
2022-12-05 12:17:44 +03:00
Sergey Uzinger a6d05ce4ed Add workflow CI (#3)
* Add workflow

* Print modules after put to DB

* fix path to workflow yml file

Co-authored-by: Dmitry Ng <19asdek91@gmail.com>
2022-12-04 18:18:34 +03:00
292 changed files with 14424 additions and 3695 deletions
+64
View File
@@ -0,0 +1,64 @@
name: "\U0001F41B Bug report"
description: "Report a bug with this project."
title: "[Bug]: "
labels: ["bug"]
assignees:
- asdek
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report! Please fill in as much of the template below as you can.
- type: textarea
attributes:
label: Describe the bug
description: Please write a clear and concise description of the bug, including what you expect to happen and what is currently happening.
placeholder: |
Feature '...' is not working properly. I expect '...' to happen, but '...' happens instead
validations:
required: true
- type: textarea
attributes:
label: Steps to Reproduce
description: Please write the steps needed to reproduce the bug.
placeholder: |
1. Go to '...'
2. Click on '...'
3. Scroll down to '...'
4. See error
validations:
required: true
- type: textarea
attributes:
label: Screenshots, screen recording, code snippet
description: |
If possible, please upload a screenshot or screen recording which demonstrates the bug. You can use [LICEcap](https://www.cockos.com/licecap/) to create a GIF screen recording.
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
For small snippets paste it directly here, or you can use [GitHub Gist](https://gist.github.com) to share multiple code files.
Please ensure the shared code can be used by a developer to reproduce the issue—ideally it can be copied into a local development environment or executed in a browser console to help debug the issue
validations:
required: false
- type: textarea
attributes:
label: Environment information
placeholder: |
- UI server version: <!-- [e.g. 3.2.0] -->
- Agent server version: <!-- [e.g. 3.2.0] -->
- Agent version: <!-- [e.g. 3.2.0] -->
- Module version: <!-- [e.g. 1.0.1] -->
- Link Module code: <!-- [link-to-zip-archive-with-helpful-code] -->
validations:
required: true
- type: checkboxes
id: operating-systems
attributes:
label: Which agent binary used?
description: We have several different builds, choose which ones have the problem.
options:
- label: darwin-amd64
- label: linux-386
- label: linux-amd64
- label: windows-386
- label: windows-amd64
validations:
required: true
+36
View File
@@ -0,0 +1,36 @@
name: "\U0001F680 Enhancement"
description: "Suggest an idea for this project."
title: "[Enhancement]: "
labels: ["enhancement"]
assignees:
- asdek
body:
- type: markdown
attributes:
value: |
Thank you for suggesting an idea to make things better. Please fill in as much of the template below as you can.
- type: textarea
attributes:
label: Is your enhancement related to a problem? Please describe.
description: Please describe the problem you are trying to solve.
placeholder: |
I use this project as a `...` and I would like `...` so that `...describe benefit...`.
validations:
required: true
- type: textarea
attributes:
label: Designs
description: |
If applicable, add mockups/screenshots/etc. to help explain your idea.
Tip: You can attach images or videos by clicking this area to highlight it and then dragging files in.
validations:
required: false
- type: textarea
attributes:
label: Describe alternatives you've considered
description: |
Please describe alternative solutions or features you have considered.
placeholder: |
I have also considered `...describe alternative...`, however I feel that my solution described above is better because of `...reason...`.
validations:
required: false
+37
View File
@@ -0,0 +1,37 @@
<!--
Filling out this template is required. Any PR that does not include enough information to be reviewed may be closed at a maintainers' discretion.
-->
### Description of the Change
<!--
We must be able to understand the design of your change from this description. The maintainer reviewing this PR may not have worked with this code recently, so please provide as much detail as possible.
Where possible, please also include:
- verification steps to ensure your change has the desired effects and has not introduced any regressions
- any benefits that will be realized
- any alternative implementations or possible drawbacks that you considered
- screenshots or screencasts
-->
<!-- Enter any applicable Issue number(s) here that will be closed/resolved by this PR. -->
Closes #
### How to test the Change
<!-- Please provide steps on how to test or validate that the change in this PR works as described. -->
### Changelog Entry
<!--
Please include a summary for this PR, noting whether this is something being Added / Changed / Deprecated / Removed / Fixed / or Security related. You can replace the sample entries after this comment block with the single changelog entry line for this PR. -->
> Added - New feature
> Changed - Existing functionality
> Deprecated - Soon-to-be removed feature
> Removed - Feature
> Fixed - Bug fix
> Security - Vulnerability
### Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you are unsure about any of these, please ask for clarification. We are here to help! -->
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my change.
- [ ] All new and existing tests pass.
+75
View File
@@ -0,0 +1,75 @@
# Saved Replies
In order to help reduce the time it takes to respond to open Issues and Pull Requests, we should leverage [GitHubs Saved Replies](https://help.github.com/en/articles/about-saved-replies) to help when we are continually responding in the same manner. The following are various Saved Replies that the soldr team should use to respond to Issues and Pull Requests to ensure our community responses are similar and to minimize the amount of time crafting the same response to different requests.
Since GitHub currently does not allow us to have a repository-wide or organization-wide list of [saved replies](https://help.github.com/articles/working-with-saved-replies/), these replies need to be maintained by individual team members. Since the replies can be modified in the future, all responses are versioned to simplify the process of keeping the replies up to date.
While these Saved Replies attempt to give you the ability to quickly reply to Issues and Pull Requests, they are not meant to be the _exact_ response you should use every time. Consider customizing them to fit the context of the Issue or Pull Request contribution you are replying to. You will be best served by welcoming the contributor to the project (if its their first issue/PR), thanking them for their contribution, giving them context to your response (especially if you're closing their issue/PR), and noting next steps (e.g., issue milestoned for a specific release, open to them submitting a PR to resolve an issue, sending a PR to someone else to review).
You can add these saved replies to [your personal GitHub account here](https://github.com/settings/replies).
_Sources: [1](https://github.com/angular/angular/blob/master/docs/SAVED_REPLIES.md), [2](https://github.com/angular/angular-cli/blob/master/.github/SAVED_REPLIES.md), [3](https://github.com/prometheus/docs/blob/master/snippets/saved_replies.md), & [4](https://gist.github.com/jywarren/c9a80e0e53f42208974683aa01c623c8)._
## Issue: already fixed (v1)
```
Thanks for filing this Issue! Fortunately it is now obsolete due to changes in a recent release. Please update to the most recent version to resolve the problem.
If you are still having problems after updating, then please provide additional details for us to try and replicate your issue.
```
## Issue: don't understand (v1)
```
I'm sorry but I don't understand the problem you are reporting. Would you please rephrase your issue so I can attempt to better understand it?
```
## Issue: can't reproduce (v1)
```
Thanks for filing this Issue! Unfortunately I cannot reproduce the problem following the instructions you provided. We require that reported issues have reproduction steps that highlights the problem.
If the problem still exists for you, then please include any additional information on how to reproduce it.
```
## Issue: behaving as expected (v1)
```
It appears this behaves as expected. If you still feel there is an issue, please provide further details on your problem.
```
## Issue: template missing (v1)
```
Thanks for filing this Issue! Please note that we have Issue templates for [bug](https://github.com/vxcontrol/soldr/blob/master/.github/ISSUE_TEMPLATE/1-bug-report.md), [enhancement](https://github.com/vxcontrol/soldr/blob/master/.github/ISSUE_TEMPLATE/2-enhancement.md). I would appreciate it if you could edit your Issue and add in the missing details.
```
## Issue: PR please? (v1)
```
I would love your help on this Issue, would be happy to review a PR for it, and will attempt to provide any assistance you might need.
```
## Issue or PR: duplicate (v1)
```
Thanks for filing this (issue, PR). However this is a duplicate of an existing (issue, PR) #NUMBER, so I'm closing this but if you think this was in error then don't hesitate to comment. Otherwise please subscribe to #NUMBER for future updates.
```
## Issue or PR: close as inactive (v1)
```
I'm closing this issue due to inactivity, but please let me know if you're still having problems so I can try to help... thanks!
```
## Issue or PR: send for help! (v1)
```
Thanks for filing this! Unfortunately Im uncertain on how to proceed here, so Im pinging (@maintainer, @10up/open-source-practice) for their input.
```
## PR: merging and more (v1)
```
This looks perfect, so I'll merge it. If you are looking for another challenge, then please take a look at our `help-wanted` list: https://github.com/vxcontrol/soldr/labels/help-wanted. Thanks!
```
## PR: template missing (v1)
```
Thanks for filing this PR! Please note that we have a [PR template](https://github.com/vxcontrol/soldr/blob/master/.github/PULL_REQUEST_TEMPLATE.md) that is required. I would appreciate it if you could edit your PR and add in the missing details.
```
## PR: missing related Issue (v1)
```
Thanks for filing this PR! Please note that we require an Issue for each PR so that approach and other details can be discussed in the Issue while code review can happen in the PR. I would appreciate it if you could [open an Issue](https://github.com/vxcontrol/soldr/issues/new/choose) and link it to this PR for further discussion.
```
+52
View File
@@ -0,0 +1,52 @@
name: 'Docker build'
description: 'Build docker in workdir'
inputs:
docker_name:
description: 'Name of creation docker image'
required: true
default: ''
builddir:
description: 'Name of creation docker image'
required: false
default: '.'
docker_login:
description: 'Login to docker hub'
required: true
default: ''
docker_password:
description: 'Password to docker hub'
required: true
default: ''
runs:
using: "composite"
steps:
- name: Generate Docker tags
id: meta
uses: docker/metadata-action@v4
with:
images: |
docker.io/vxcontrol/${{ inputs.docker_name }}
tags: |
type=ref,event=branch
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'master') }}
type=semver,pattern={{version}}
flavor: |
latest=false
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ inputs.docker_login }}
password: ${{ inputs.docker_password }}
- name: Build and push
uses: docker/build-push-action@v3
env:
DOCKER_BUILDKIT: 1
with:
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
context: ${{ inputs.builddir }}
@@ -0,0 +1,23 @@
name: VXModules build and push
on: workflow_call
jobs:
build_and_push_docker:
name: Docker Build and Publish
environment:
name: production
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Docker build dbmigrate
uses: ./.github/actions/docker_build
with:
workdir: "build/package/dbmigrate"
docker_name: "soldr-modules"
docker_login: ${{ secrets.DOCKER_LOGIN }}
docker_password: ${{ secrets.DOCKER_PASSWORD }}
+16
View File
@@ -0,0 +1,16 @@
name: Docker build and push
on:
push:
branches:
- master
tags:
- v[0-9]+.[0-9]+.[0-9]+
workflow_dispatch:
jobs:
# BUILD DOCKER
build_docker_vxmodules:
uses: vxcontrol/soldr-modules/.github/workflows/build-docker-modules.yml@master
if: github.ref_name == 'master' || startsWith(github.ref, 'refs/tags')
secrets: inherit
+4 -2
View File
@@ -10,7 +10,8 @@ tools/sysmon.xml
.idea/
.venv/
.vscode/
.vscode/launch.json
.vscode/settings.json
tmpcwd/*
tmpdir/*
@@ -18,5 +19,6 @@ tmpdir/*
luacov-html
luacov.*
!luacov.lua
coverage-report
junit.xml
junit.xml
+1 -1
View File
@@ -1,3 +1,3 @@
[submodule "luapower"]
path = luapower
url = git@github.com:capr/multigit.git
url = git@github.com:vxcontrol/multigit.git
+12 -4
View File
@@ -6,11 +6,11 @@ self = false
files["**/*_spec.lua"].std = "+busted"
files["**/tests/*_spec.lua"].std = "+busted"
max_line_length=160
max_string_line_length=250
max_comment_line_length=320
max_line_length = 160
max_string_line_length = 250
max_comment_line_length = 320
exclude_files = {"tests_framework/*"}
exclude_files = { "tests_framework/*" }
read_globals = {
-- table
@@ -22,6 +22,9 @@ read_globals = {
-- assert
"assert.is_true",
"assert.is_false",
"assert.equal",
"assert.not_nil",
"assert.is_nil",
-- yaci API
"newclass",
@@ -74,12 +77,14 @@ globals = {
"__mock",
-- utils classes
"CSystemInfo",
"CFileReader",
"CReader",
-- correlator classes
"CEventEngine",
"CActionEngine",
"CActionsValidator",
"CCorrEngine",
"CBaseEngine",
@@ -103,9 +108,12 @@ globals = {
"setfenv",
-- busted API
"context",
"describe",
"teardown",
"setup",
"it",
"test",
"before_each"
}
+17
View File
@@ -0,0 +1,17 @@
{
"recommendations": [
"eriklynd.json-tools",
"nickdemayo.vscode-json-editor",
"bubuabu.busted-test-explorer",
"keyring.Lua",
"yinfei.luahelper",
"actboy168.lua-debug",
"satoren.lualint",
"octref.vetur",
"Vue.volar",
"sdras.vue-vscode-snippets",
"hollowtree.vue-snippets",
"ElemeFE.vscode-element-helper",
"infosec-intern.yara"
]
}
+13 -21
View File
@@ -1,8 +1,8 @@
FROM minio/mc
FROM minio/mc as minio_client
FROM mysql:5.7-debian
FROM python:3.10-alpine
COPY --from=0 /usr/bin/mc /usr/bin/mc
COPY --from=minio_client /usr/bin/mc /usr/bin/mc
ARG VXVERSION=unknown
@@ -17,6 +17,7 @@ COPY startup.sh /opt/vxmodules/
# SQL file generator script
COPY gen_sql.py /opt/vxmodules/
# Content for test loading modules from S3
COPY auditd /opt/vxmodules/mon/auditd
COPY syslog /opt/vxmodules/mon/syslog
COPY sysmon /opt/vxmodules/mon/sysmon
COPY file_remover /opt/vxmodules/mon/file_remover
@@ -28,31 +29,22 @@ COPY correlator /opt/vxmodules/mon/correlator
COPY correlator_linux /opt/vxmodules/mon/correlator_linux
COPY yara_scanner /opt/vxmodules/mon/yara_scanner
COPY lua_interpreter /opt/vxmodules/mon/lua_interpreter
COPY osquery /opt/vxmodules/mon/osquery
COPY osquery_linux /opt/vxmodules/mon/osquery_linux
COPY shell /opt/vxmodules/mon/shell
COPY utils /opt/vxmodules/mon/utils
COPY config.json /opt/vxmodules/mon/
RUN chmod +x /opt/vxmodules/startup.sh
RUN chmod +x /opt/vxmodules/gen_sql.py
RUN \
apt update && \
apt install -y ca-certificates && \
apt install -y jq && \
apt install -y --no-install-recommends python3 python3-pip && \
apt clean -y && \
apt autoremove -y && \
rm -rf /tmp/* /var/tmp/* && \
rm -rf /var/lib/apt/lists/* && \
echo 'hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4' >> /etc/nsswitch.conf
RUN pip3 install pypika
RUN chmod +x /opt/vxmodules/startup.sh && \
chmod +x /opt/vxmodules/gen_sql.py && \
apk add --no-cache mysql-client bash jq && \
echo 'hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4' >> /etc/nsswitch.conf && \
pip3 install pypika && \
echo ${VXVERSION} > /opt/vxmodules/version
WORKDIR /opt/vxmodules/mon/
# Generate new dump sql files
RUN python3 /opt/vxmodules/gen_sql.py /opt/vxmodules/
# Write version file
RUN echo ${VXVERSION} > /opt/vxmodules/version
ENTRYPOINT ["/opt/vxmodules/startup.sh"]
+2 -2
View File
@@ -3,7 +3,7 @@ END USER LICENSE AGREEMENT FOR SOLDR MODULES
1. General provisions
1.1. This License License (hereinafter the License) represents an agreement between A-Lab FZE (hereinafter the Rightholder) and the end user (hereinafter the User) with regard to SOLDR modules (hereinafter the Software) made publicly available by the Rightholder at the repository address https://github.com/vxcontrol/soldr-modules (hereinafter referred to as the Software) and/or the Software Modifications as defined herebelow made available as SOLDR modules forks at other github repositories.
1.1. This License (hereinafter the License) represents an agreement between A-Lab FZE (hereinafter the Rightholder) and the end user (hereinafter the User) with regard to SOLDR modules (hereinafter the Software) made publicly available by the Rightholder at the repository address https://github.com/vxcontrol/soldr-modules (hereinafter referred to as the Software) and/or the Software Modifications as defined herebelow made available as SOLDR modules forks at other github repositories.
1.2. Whenever the Rigtholder provide any updates, fixes and/or patches to the Software (hereinafter Software update installation package) such Software update installation packages are considered a part of the Software and provided strictly under the License as well.
@@ -33,7 +33,7 @@ END USER LICENSE AGREEMENT FOR SOLDR MODULES
3. Copyright
3.1. The Rightholder of the Software is JSC Positive Technologies.
3.1. The Rightholder of the Software is A-Lab FZE.
3.2. All rights (including the right for the content that can become available as a result of the Software use) are the property of the Rightholder.
+3
View File
@@ -0,0 +1,3 @@
return {
_all = { lpath = "cmodule/?.lua" }
}
+14
View File
@@ -0,0 +1,14 @@
test:
$(SOLDR_MODULES)/bin/busted $(BUSTED_FLAGS) .
OS_LIST := $(subst docker/Dockerfile.,,$(wildcard docker/Dockerfile.*))
test-inside-docker: $(OS_LIST:%=test-inside-%)
test-inside-%: docker/Dockerfile.%
@echo === $* ===
@docker build -t test/$* -f docker/Dockerfile.$* . >&-
@docker run --rm \
-e SOLDR_MODULES=/src \
-v $(SOLDR_MODULES):/src:ro \
-w /src/auditd/1.0.0 \
test/$* /src/bin/busted --exclude-tags=systemd $(BUSTED_FLAGS) .
+15
View File
@@ -0,0 +1,15 @@
<template>
<div>
</div>
</template>
<script>
const name = "empty";
module.exports = {
name,
props: ["protoAPI", "hash", "module", "api", "components", "viewMode"],
data: () => ({
})
};
</script>
+1
View File
@@ -0,0 +1 @@
{}
+80
View File
@@ -0,0 +1,80 @@
local exec = require "exec"
local managed = require "managed"
local try = require "try"
-- Checks if systemd is available on current OS.
--:: () -> ok?, err::string?
local function check_systemd()
return exec("type systemctl"),
string.format("unsupported linux distro: systemd not found")
end
-- Ensures that audit is installed and available to use.
--:: pkg.Manager -> ok::boolean, err::string?
local function ensure_audit(pm)
if exec("type auditctl") then
return true end
local ok, err = try(function()
-- TODO: sync packages?
-- assert(pm:sync())
assert(pm:install("audit", "auditd"))
assert(exec("type auditctl"))
return true
end)
return ok, string.format("audit not available: %s", err)
end
-- Abstraction over a managed state of Linux Auditing System.
local State = {}; State.__index = State
function State.new()
return setmetatable({}, State)
end
-- Performs installation and configuration of Linux Auditing System.
-- It's idempotent and can be kept calling repeatedly, without any significant
-- performance cost, to ensure the system meets the required configuration.
--:: pkg.Manager, [managed.File] -> ok?, err::string?
function State:setup(pm, files)
return try(function()
assert(ensure_audit(pm))
local rules = assert(exec("auditctl -l"))
local status = assert(managed.ensure_all(files))
if self._rules ~= rules or status == managed.MODIFIED then
assert(exec("systemctl daemon-reload"))
assert(exec("systemctl try-reload-or-restart auditd.service"))
end
assert(exec("systemctl start auditd.service"))
self._rules = assert(exec("auditctl -l"))
return status
end)
end
local file_auditd_conf = managed.file("/etc/audit/auditd.conf", "644")
local file_audit_rules = managed.file("/etc/audit/audit.rules", "400")
-- Overrides configuration of auditd service to always read audit rules
-- from `/etc/audit/audit.rules` with `auditctl`.
-- (By default most linux distros are configured to use `augenrules` and
-- read audit rules from `/etc/audit/rules.d`)
local file_auditd_service_override =
managed.file("/etc/systemd/system/auditd.service.d/override.conf", "644")
file_auditd_service_override:set[[
# Managed by SOLDR(auditd)
[Service]
ExecStartPost=
ExecStartPost=-/sbin/auditctl -R /etc/audit/audit.rules
ExecReload=
ExecReload=kill -HUP $MAINPID
ExecReload=-/sbin/auditctl -R /etc/audit/audit.rules
]]
return {
State = State,
check_systemd = check_systemd,
file_auditd_conf = file_auditd_conf,
file_audit_rules = file_audit_rules,
file_auditd_service_override = file_auditd_service_override,
}
+74
View File
@@ -0,0 +1,74 @@
local audit = require "audit"
local exec = require "exec"
local pkg = require "pkg"
local glue = require "glue"
local pm, state
local os = glue.gsplit(tostring(glue.readfile("/etc/issue", "r")) or "unknown", "\n")()
local function has_prefix(str, prefix)
return string.find(str, prefix, 1, true) == 1,
string.format("expected %q has prefix %q", str, prefix)
end
context(os .. " | audit", function()
test("check_systemd() #systemd", function()
assert(audit.check_systemd())
end)
end)
context(os .. " | audit setup() #root #systemd #network #write", function()
setup(function()
pm = assert(pkg.find_manager())
state = audit.State.new()
end)
local function setup_and_assert()
local files = {
audit.file_auditd_service_override,
audit.file_auditd_conf,
audit.file_audit_rules,
}
audit.file_auditd_conf:set("#TEST\nspace_left = 1\n")
audit.file_audit_rules:set("-D\n-w /TEST\n")
assert(state:setup(pm, files))
assert.equals("active", assert(exec("systemctl is-active auditd.service")))
local conf = assert(exec("cat /etc/audit/auditd.conf"))
assert(has_prefix(conf, "#TEST\n"))
assert.equals("-w /TEST -p rwxa", assert(exec("auditctl -l")))
end
local function reset()
assert(exec("systemctl disable --now auditd.service; true"))
assert(exec("rm -rf /etc/systemd/system/auditd.service.d"))
assert(exec("systemctl daemon-reload"))
assert(exec("auditctl --signal TERM; true"))
assert(exec("auditctl -D; true"))
assert(exec("rm -rf /etc/audit"))
end
teardown(reset)
test("case: audit is not installed", function()
-- TODO: actually remove package audit
reset()
setup_and_assert()
end)
test("case: auditd is running with some rules in rules.d", function()
reset()
assert(pm:install("audit", "auditd"))
assert(exec("mkdir -p /etc/audit/rules.d"))
assert(exec("echo -w /UNWANTED > /etc/audit/rules.d/unwanted.rules"))
assert(exec("systemctl start auditd.service"))
setup_and_assert()
end)
test("case: someone's changed audit rules", function()
setup_and_assert()
assert(exec("auditctl -D"))
setup_and_assert()
end)
end)
+31
View File
@@ -0,0 +1,31 @@
require("engine")
local M = {}
local event_engine
local action_engine
function M.update_config()
local prefix_db = __gid .. "."
local fields_schema = __config.get_fields_schema()
local event_config = __config.get_current_event_config()
local module_info = __config.get_module_info()
event_engine = CEventEngine(fields_schema, event_config, module_info, prefix_db, false)
action_engine = CActionEngine({}, false)
end
M.update_config()
local function send_event(name, data)
local result, list = event_engine:push_event{
name = name,
data = data or {},
}
if result then action_engine:exec(__aid, list) end
end
function M.error(err)
__log.error(err)
send_event("auditd_error", {message=tostring(err)})
end
return M
+30
View File
@@ -0,0 +1,30 @@
local try = require "try"
local function show_output(s)
local MAX_LENGTH = 1024
s = string.gsub(s, "\n", "\\n")
s = string.sub(s, 1, MAX_LENGTH)
return s
end
local function trim_end(s)
return string.gsub(s, "%s+$", "")
end
-- Executes the given command in a shell.
-- NOTE: stderr is redirecting to stdout.
--:: string -> output::string?, err::string?
local function exec(cmd)
local result, err = try(function()
local stdout = assert(
io.popen("exec <&- 2>&1;"..cmd), "io.popen failed")
local output = stdout:read("a") or ""
local ok, status, code = stdout:close()
assert(ok,
string.format("%s=%d: %s", status, code, show_output(output)))
return trim_end(output)
end)
return result, string.format("exec(%s): %s", cmd, err)
end
return exec
+36
View File
@@ -0,0 +1,36 @@
local exec = require "exec"
local function string_contains(str, sub)
return string.find(str, sub, 1, true) ~= nil
end
describe("exec", function()
it("runs a given command", function()
assert(exec("true"))
end)
it("should return stdout+stderr", function()
local output = assert(exec("echo OUTPUT"))
assert.equals("OUTPUT", output)
end)
it("must fail if exit code != 0", function()
local ok, err = exec("echo ERROR; false")
assert(not ok)
assert(string_contains(err, 'exit=1: ERROR'),
string.format("unexpected error message: %s", err))
end)
it("stdin should be closed to avoid blocking", function()
assert.has_error(function()
assert(exec("cat"))
end)
end)
test("a bad command", function()
local ok, err = exec("unknown-command")
assert(not ok)
assert(string_contains(err, "not found"),
string.format("unexpected error message: %s", err))
end)
end)
+45
View File
@@ -0,0 +1,45 @@
local audit = require "audit"
local pkg = require "pkg"
local cjson = require "cjson"
local event = require "event"
local ok, err = audit.check_systemd()
if not ok then
event.error(err)
__api.await(-1); return "success"
end
local pm, err = pkg.find_manager()
if not pm then
event.error(err)
__api.await(-1); return "success"
end
local austate = audit.State.new()
local watcher = require("watcher").new()
local function update_config()
event.update_config()
local c = cjson.decode(__config.get_current_config())
audit.file_auditd_conf:set(c.auditd_conf)
audit.file_audit_rules:set(c.audit_rules)
watcher:reset(c.check_interval)
end
update_config()
__api.add_cbs{
control = function(cmtype, data)
if cmtype == "update_config" then update_config() end
return true
end,
}
watcher:run(function()
local ok, err = austate:setup(pm, {
audit.file_auditd_service_override,
audit.file_auditd_conf,
audit.file_audit_rules,
})
if not ok then event.error(err) end
end)
return "success"
+93
View File
@@ -0,0 +1,93 @@
local exec = require "exec"
local try = require "try"
local MODIFIED = {}
local File = {}; File.__index = File
-- Introduces a "managed" file.
-- 1. Set the required access `mode` to the file.
-- 2. Set the required content of the file with `set`.
-- 3. Then use `ensure` to adjust the file on disk to required state.
-- NOTE: `mode` is a string in `chmod` format, see: chmod(1).
--:: string, string -> File
local function file(path, mode)
return setmetatable({
_path = path,
_mode = mode,
_data = "",
}, File)
end
function File:path()
return self._path
end
-- Sets target content of the file.
--:: string -> ()
function File:set(data)
self._data = data
end
-- Adjust the file on disk to required state.
-- Does nothing if the file on disk matches the required state.
--:: () -> boolean|MODIFIED, err::string?
function File:ensure()
if self:compare() == MODIFIED then
local ok, err = self:write()
return ok and MODIFIED,
string.format("%s: %s", self._path, err)
end
return true
end
-- Entirely overwrites the file on disk with the required content.
--:: () -> ok::boolean, err::string?
function File:write()
return try(function()
assert(exec(
string.format("mkdir -p $(dirname %q)", self._path)))
local f = assert(io.open(self._path, "wb"), "failed to open")
local w, err = f:write(self._data);
f:close()
assert(w, err)
assert(exec(
string.format("chmod %q %q", self._mode, self._path)))
return true
end)
end
-- Compares the required content with the file's content on disk.
--:: () -> true|MODIFIED
function File:compare()
local data
local f = io.open(self._path, "rb")
if f then
data = f:read("a")
f:close()
end
return self._data == data or MODIFIED
end
-- Calls `ensure` for every item of `list`.
-- Returns MODIFIED if any of list returns MODIFIED.
-- Immediately returns false if any of list failed.
--:: [File] -> boolean|MODIFIED, err::string?
local function ensure_all(list)
return try(function()
local status = true
for _, item in ipairs(list) do
if assert(item:ensure()) == MODIFIED then
status = MODIFIED end
end
return status
end)
end
return {
MODIFIED = MODIFIED,
file = file,
ensure_all = ensure_all,
}
+87
View File
@@ -0,0 +1,87 @@
local managed = require "managed"
local exec = require "exec"
local function rm(path)
local cmd = string.format("rm -rf %q", path)
assert(exec(cmd))
end
local function read_file(filename)
local f = assert(io.open(filename, "rb"),
string.format("failed to open: %s", filename))
return assert(f:read("a")), f:close()
end
local function read_mode(filename)
local cmd = string.format("ls -l %q | cut -c-10", filename)
return assert(exec(cmd))
end
describe("managed File #write", function()
setup(function()
tmp = assert(exec("mktemp -d"))
f_name = tmp .. "/sub/file"
end)
teardown(function() rm(tmp) end)
describe("ensure()", function()
it("should create a file unless it exists", function()
local f = managed.file(f_name, "ug=rw,o=r")
f:set("CREATE")
local status = assert(f:ensure())
assert.equals(managed.MODIFIED, status)
assert.equals("CREATE", read_file(f_name))
assert.equals("-rw-rw-r--", read_mode(f_name))
end)
it("should return `true` while file's content == required content", function()
local f = managed.file(f_name, "INVALID")
f:set("CREATE")
local status = assert(f:ensure())
assert.is_true(status)
end)
it("should overwrite if file's content != required content", function()
local f = managed.file(f_name, "0400")
f:set("UPDATE")
local status = assert(f:ensure())
assert.equals(managed.MODIFIED, status)
assert.equals("UPDATE", read_file(f_name))
assert.equals("-r--------", read_mode(f_name))
end)
end)
end)
describe("ensure_all() #write", function()
setup(function()
tmp = assert(exec("mktemp -d"))
file_a = managed.file(tmp.."/a", "a=rw")
file_b = managed.file(tmp.."/b", "a=rw")
end)
teardown(function() rm(tmp) end)
it("should call ensure() for every item", function()
file_a:set("CREATE")
file_b:set("CREATE")
local status = managed.ensure_all{file_a, file_b}
assert.equals(managed.MODIFIED, status)
assert.equals("CREATE", read_file(file_a:path()))
assert.equals("CREATE", read_file(file_b:path()))
end)
it("should return MODIFIED if any of item is modified", function()
file_a:set("UPDATE")
assert.equals(managed.MODIFIED, managed.ensure_all{file_a, file_b})
file_b:set("UPDATE")
assert.equals(managed.MODIFIED, managed.ensure_all{file_a, file_b})
end)
it("otherwise returns `true`", function()
assert.is_true(managed.ensure_all{file_a, file_b})
end)
end)
+94
View File
@@ -0,0 +1,94 @@
local exec = require "exec"
-- Replaces each occurrence of `{name}` in cmd with the corresponding
-- value of key `name` from table vars.
--:: string, {string => string} -> string
local function format_cmd(cmd, vars)
for name, var in pairs(vars) do
cmd = string.gsub(cmd, "{"..name.."}", var)
end
return cmd
end
-- Manager is an abstraction over system package managers.
-- See the list of supported package managers below.
local Manager = {}; Manager.__index = Manager
local backend_commands = {
test = "type {bin}",
sync = "true",
install = "false",
}
function Manager.new(name, bin, commands)
commands = commands or {}
for type, cmd in pairs(backend_commands) do
commands[type] = commands[type] or cmd
end
return setmetatable({
name = name,
bin = bin,
_cmd = commands,
}, Manager)
end
-- Tests if the manager is available on current OS.
--:: () -> ok?, err::string?
function Manager:test()
local cmd = format_cmd(self._cmd.test, {name=self.name, bin=self.bin})
return exec(cmd)
end
-- Used to synchronize the package index files from their sources.
--:: () -> ok?, err::string?
function Manager:sync()
local cmd = format_cmd(self._cmd.sync, {name=self.name, bin=self.bin})
return exec(cmd)
end
-- Performs installation procedure for the given package.
-- Additional args are used as alternative names of the package.
--:: string... -> ok::boolean, err::string?
function Manager:install(package, ...)
local ok, err
for _, package in ipairs{package, ...} do
local cmd = format_cmd(self._cmd.install,
{name=self.name, bin=self.bin, package=package})
ok, err = exec(cmd)
if ok then return true end
end
return false, err
end
-- List of supported package managers.
local managers = {
Manager.new("APT", "apt-get", {
sync = "{bin} update --quiet",
install = "{bin} install --quiet --yes {package}",
}),
Manager.new("DNF", "dnf", {
install = "{bin} --quiet -y install {package}",
}),
Manager.new("YUM", "yum", {
install = "{bin} --quiet -y install {package}",
}),
Manager.new("Pacman", "pacman", {
sync = "{bin} -Sy --noconfirm",
install = "{bin} -S --asdeps --noconfirm {package}",
}),
}
-- Tries to guess a package manager available on current OS.
--:: () -> Manager?, err::string?
local function find_manager()
for _, manager in ipairs(managers) do
if manager:test() then return manager end
end
return nil, "unsupported linux distro: package manager not found"
end
return {
find_manager = find_manager,
testing = {
format_cmd = format_cmd },
}
+50
View File
@@ -0,0 +1,50 @@
local pkg = require "pkg"
local exec = require "exec"
local function is_installed(name)
return exec("type "..name)
end
describe("format_cmd()", function()
local format_cmd = pkg.testing.format_cmd
it("replaces {name} placeholders", function()
local cmd = format_cmd("one={one} two={two}", {
one = "ONE",
two = "TWO",
})
assert.equals("one=ONE two=TWO", cmd)
end)
it("replaces ALL {name} occurrences", function()
assert.equals("a=VAR b=VAR",
format_cmd("a={var} b={var}", {var="VAR"}))
end)
it("ignores {unknown} variables", function()
assert.equals("{unknown}", format_cmd("{unknown}", {}))
end)
it("skips unused variables", function()
assert.equals("", format_cmd("", {unused="UNUSED"}))
end)
end)
context("package manager #root", function()
setup(function()
pm = assert(pkg.find_manager())
end)
test("sync() #network", function()
assert(pm:sync())
end)
test("install() #network", function()
local package = "lsof"
-- Uncomment to assert clean installation:
-- assert(not is_installed(package),
-- string.format("expected %q to be not installed", package))
assert(pm:install("alternative", package))
assert(is_installed(package))
end)
end)
+17
View File
@@ -0,0 +1,17 @@
-- Strips "FILE:LINE:" prefix from an error message.
--:: string -> string
local function strip_place(err)
return string.gsub(err, "^.-:.-: ", "")
end
-- Customized protected call around assert/error.
-- In contrast to pcall/xpcall returns unchanged list of the result arguments
-- of `f` on success.
local function try(f, ...)
local args = table.pack(xpcall(f, strip_place, ...))
if args[1] == true then
return table.unpack(args, 2) end
return table.unpack(args)
end
return try
+28
View File
@@ -0,0 +1,28 @@
local Watcher = {}; Watcher.__index = Watcher
--:: seconds? -> Watcher
function Watcher.new(interval)
return setmetatable({
_interval = interval or 0,
}, Watcher)
end
--:: seconds? -> ()
function Watcher:reset(interval)
self._last_run = 0
self._interval = interval or self._interval
end
function Watcher:run(f)
self._last_run = 0
while not __api.is_close() do
local now = os.time()
if now - self._last_run >= self._interval then
self._last_run = now
f()
end
__api.await(1000)
end
end
return Watcher
@@ -0,0 +1,6 @@
{
"additionalProperties": false,
"properties": {},
"required": [],
"type": "object"
}
+14
View File
@@ -0,0 +1,14 @@
{
"1.0.0": {
"en": {
"date": "05-25-2023",
"title": "Base functionality",
"description": "Added the possibility to install and configure the auditd component"
},
"ru": {
"date": "25.05.2023",
"title": "Базовая функциональность",
"description": "Добавлена возможность устанавливать и конфигурировать компонент auditd"
}
}
}
+41
View File
@@ -0,0 +1,41 @@
{
"additionalProperties": false,
"properties": {
"audit_rules": {
"rules": {},
"type": "string",
"ui": {
"columns": 6,
"widget": "textarea",
"widgetConfig": {
"rows": 8
}
}
},
"auditd_conf": {
"rules": {},
"type": "string",
"ui": {
"columns": 6,
"widget": "textarea",
"widgetConfig": {
"rows": 8
}
}
},
"check_interval": {
"rules": {},
"type": "integer",
"ui": {
"columns": 4,
"widgetConfig": {}
}
}
},
"required": [
"audit_rules",
"auditd_conf",
"check_interval"
],
"type": "object"
}
@@ -0,0 +1 @@
{}
+5
View File
@@ -0,0 +1,5 @@
{
"audit_rules": "# Managed by SOLDR(auditd)\n-D\n",
"auditd_conf": "# Managed by SOLDR(auditd)\nwrite_logs = yes\nlog_file = /var/log/audit/audit.log\nlog_format = ENRICHED\nmax_log_file = 50\nmax_log_file_action = ROTATE\nnum_logs = 2\nspace_left = 100\nspace_left_action = SUSPEND\n",
"check_interval": 600
}
@@ -0,0 +1,16 @@
{
"auditd_error": {
"actions": [
{
"fields": [],
"module_name": "this",
"name": "log_to_db",
"priority": 10
}
],
"fields": [
"message"
],
"type": "atomic"
}
}
@@ -0,0 +1 @@
{}
+5
View File
@@ -0,0 +1,5 @@
{
"audit_rules": "# Managed by SOLDR(auditd)\n-D\n",
"auditd_conf": "# Managed by SOLDR(auditd)\nwrite_logs = yes\nlog_file = /var/log/audit/audit.log\nlog_format = ENRICHED\nmax_log_file = 50\nmax_log_file_action = ROTATE\nnum_logs = 2\nspace_left = 100\nspace_left_action = SUSPEND\n",
"check_interval": 600
}
@@ -0,0 +1,16 @@
{
"auditd_error": {
"actions": [
{
"fields": [],
"module_name": "this",
"name": "log_to_db",
"priority": 10
}
],
"fields": [
"message"
],
"type": "atomic"
}
}
@@ -0,0 +1 @@
[]
@@ -0,0 +1,38 @@
{
"additionalProperties": false,
"properties": {
"auditd_error": {
"allOf": [
{
"$ref": "#/definitions/events.atomic"
},
{
"properties": {
"fields": {
"default": [
"message"
],
"items": {
"enum": [
"message"
],
"type": "string"
},
"maxItems": 1,
"minItems": 1,
"type": "array"
}
},
"required": [
"fields"
],
"type": "object"
}
]
}
},
"required": [
"auditd_error"
],
"type": "object"
}
+14
View File
@@ -0,0 +1,14 @@
{
"additionalProperties": true,
"properties": {
"message": {
"rules": {},
"type": "string",
"ui": {
"widgetConfig": {}
}
}
},
"required": [],
"type": "object"
}
+24
View File
@@ -0,0 +1,24 @@
{
"name": "auditd",
"template": "empty",
"version": {
"major": 1,
"minor": 0,
"patch": 0
},
"os": {
"linux": [
"386",
"amd64"
]
},
"system": false,
"actions": [],
"events": [
"auditd_error"
],
"fields": [
"message"
],
"tags": []
}
+75
View File
@@ -0,0 +1,75 @@
{
"module": {
"en": {
"title": "Auditd installer (cyberok)",
"description": "Installs and configures the auditd component"
},
"ru": {
"title": "Установщик auditd (cyberok)",
"description": "Устанавливает и конфигурирует компонент auditd"
}
},
"config": {
"audit_rules": {
"en": {
"title": "Rules",
"description": "The /etc/audit/audit.rules file contents"
},
"ru": {
"title": "Правила",
"description": "Содержимое файла /etc/audit/audit.rules"
}
},
"auditd_conf": {
"en": {
"title": "Auditd configuration",
"description": "The /etc/audit/auditd.conf file contents"
},
"ru": {
"title": "Конфигурация auditd",
"description": "Содержимое файла /etc/audit/auditd.conf"
}
},
"check_interval": {
"en": {
"title": "Configuration check interval on an agent (in seconds)",
"description": ""
},
"ru": {
"title": "Интервал проверки конфигурации на агенте, сек",
"description": ""
}
}
},
"secure_config": {},
"fields": {
"message": {
"en": {
"title": "message",
"description": "message"
},
"ru": {
"title": "message",
"description": "message"
}
}
},
"actions": {},
"events": {
"auditd_error": {
"en": {
"title": "The module failed to install auditd on the agent",
"description": "An error occurred during auditd installation"
},
"ru": {
"title": "Модуль не смог установить auditd на агенте",
"description": "Ошибка при установке auditd"
}
}
},
"action_config": {},
"event_config": {
"auditd_error": {}
},
"tags": {}
}
@@ -0,0 +1,6 @@
{
"additionalProperties": false,
"properties": {},
"required": [],
"type": "object"
}
@@ -0,0 +1 @@
{}
@@ -0,0 +1 @@
{}
@@ -0,0 +1 @@
[]
+3
View File
@@ -0,0 +1,3 @@
FROM debian:10
RUN apt-get update;
RUN apt-get install --yes --download-only auditd;
+3
View File
@@ -0,0 +1,3 @@
FROM debian:11
RUN apt-get update;
RUN apt-get install --yes --download-only auditd;
@@ -0,0 +1,2 @@
FROM oraclelinux:8
RUN dnf install -y --downloadonly audit;
@@ -0,0 +1,3 @@
FROM ubuntu:20.04
RUN apt-get update;
RUN apt-get install --yes --download-only auditd;
@@ -0,0 +1,3 @@
FROM ubuntu:22.04
RUN apt-get update;
RUN apt-get install --yes --download-only auditd;
+1
View File
@@ -0,0 +1 @@
{}
+5
View File
@@ -0,0 +1,5 @@
__api.add_cbs{
control = function() return true end
}
__api.await(-1)
return "success"
Executable
+6
View File
@@ -0,0 +1,6 @@
#!/bin/sh
# Configurable by user:
SOLDR_MODULES="${SOLDR_MODULES:-$PWD}"
exec "$SOLDR_MODULES/bin/luajit" "$SOLDR_MODULES/tests_framework/lua/bin/busted" "$@"
Executable
+23
View File
@@ -0,0 +1,23 @@
#!/bin/sh
# Configurable by user:
SOLDR_MODULES="${SOLDR_MODULES:-$PWD}"
LUAPOWER_PLATFORM="${LUAPOWER_PLATFORM:-linux64}"
LUA_BIN="$SOLDR_MODULES/luapower/bin/$LUAPOWER_PLATFORM/luajit-bin"
export LUA_PATH="\
$SOLDR_MODULES/tests_framework/lua/?.lua;\
$SOLDR_MODULES/tests_framework/lua/?/init.lua;\
$SOLDR_MODULES/luapower/?.lua;\
$SOLDR_MODULES/luapower/?/init.lua;\
$SOLDR_MODULES/utils/?.lua;\
$SOLDR_MODULES/utils/?/init.lua;\
$LUA_PATH"
export LUA_CPATH="\
$SOLDR_MODULES/luapower/bin/$LUAPOWER_PLATFORM/lib?.dylib;\
$SOLDR_MODULES/luapower/bin/$LUAPOWER_PLATFORM/clib/?.so;\
$LUA_CPATH"
exec "$LUA_BIN" "$@"
+181 -430
View File
@@ -37,8 +37,8 @@
"reason",
"version"
],
"last_module_update": "2022-10-26 00:00:00",
"last_update": "2022-10-26 00:00:00"
"last_module_update": "2023-05-25 00:00:00",
"last_update": "2023-05-25 00:00:00"
},
{
"group_id": "",
@@ -66,8 +66,8 @@
"fields": [
"reason"
],
"last_module_update": "2022-10-26 00:00:00",
"last_update": "2022-10-26 00:00:00"
"last_module_update": "2023-05-25 00:00:00",
"last_update": "2023-05-25 00:00:00"
},
{
"group_id": "",
@@ -96,8 +96,8 @@
"actions": [],
"events": [],
"fields": [],
"last_module_update": "2022-04-29 00:00:00",
"last_update": "2022-04-29 00:00:00"
"last_module_update": "2022-12-08 00:00:00",
"last_update": "2022-12-08 00:00:00"
},
{
"group_id": "",
@@ -131,8 +131,8 @@
"syslog_module_stopped"
],
"fields": [],
"last_module_update": "2022-04-29 00:00:00",
"last_update": "2022-04-29 00:00:00"
"last_module_update": "2022-12-08 00:00:00",
"last_update": "2022-12-08 00:00:00"
},
{
"group_id": "",
@@ -181,8 +181,8 @@
"subject.fullpath",
"subject.process.fullpath"
],
"last_module_update": "2022-04-29 00:00:00",
"last_update": "2022-04-29 00:00:00"
"last_module_update": "2022-12-23 00:00:00",
"last_update": "2022-12-23 00:00:00"
},
{
"group_id": "",
@@ -249,8 +249,8 @@
"subject.process.id",
"subject.process.name"
],
"last_module_update": "2022-08-26 00:00:00",
"last_update": "2022-06-26 00:00:00"
"last_module_update": "2022-12-20 00:00:00",
"last_update": "2022-12-20 00:00:00"
},
{
"group_id": "",
@@ -275,9 +275,11 @@
},
"actions": [
"yr_object_scan_proc",
"yr_object_scan_proc_non_cached",
"yr_object_task_scan_proc",
"yr_scan_fs",
"yr_subject_scan_proc",
"yr_subject_scan_proc_non_cached",
"yr_subject_task_scan_proc",
"yr_task_fastscan_fs",
"yr_task_fastscan_proc",
@@ -293,12 +295,18 @@
"yr_module_started",
"yr_module_stopped",
"yr_object_process_matched_high",
"yr_object_process_matched_high_cached",
"yr_object_process_matched_low",
"yr_object_process_matched_low_cached",
"yr_object_process_matched_medium",
"yr_object_process_matched_medium_cached",
"yr_process_matched_custom",
"yr_subject_process_matched_high",
"yr_subject_process_matched_high_cached",
"yr_subject_process_matched_low",
"yr_subject_process_matched_medium"
"yr_subject_process_matched_low_cached",
"yr_subject_process_matched_medium",
"yr_subject_process_matched_medium_cached"
],
"fields": [
"malware_class",
@@ -314,8 +322,8 @@
"subject.process.fullpath",
"subject.process.id"
],
"last_module_update": "2022-11-07 09:11:39",
"last_update": "2022-11-07 09:11:39"
"last_module_update": "2023-01-26 00:00:00",
"last_update": "2023-01-26 00:00:00"
},
{
"group_id": "",
@@ -364,8 +372,8 @@
"subject.fullpath",
"subject.process.fullpath"
],
"last_module_update": "2022-09-30 00:00:00",
"last_update": "2022-09-30 00:00:00"
"last_module_update": "2022-12-20 00:00:00",
"last_update": "2022-12-20 00:00:00"
},
{
"group_id": "",
@@ -385,113 +393,42 @@
},
"actions": [],
"events": [
"Auxiliary_SuspiciousWeightsTrojan_Linux_subject_a",
"Local_Recon_by_Web_User",
"Malware_Exploit_Elf_CVE_2019_14287_a",
"Malware_Exploit_Elf_CVE_2021_4034_a",
"Malware_Trojan_Linux_Generic_a",
"Malware_Trojan_Spy_Linux_Generic_a",
"Raw_Socket",
"Reverse_Tunneling_via_SSH",
"Suspicious_Create_File_Attribute_Hidden",
"Suspicious_Create_File_Boot_Modification",
"Suspicious_Create_File_Boot_RCScripts",
"Suspicious_Create_File_Ldd_PreloadDynamicLibrary",
"Suspicious_Create_File_Scheduler_At",
"Suspicious_Create_File_Scheduler_Cron",
"Suspicious_Create_File_Scheduler_SystemdTimer",
"Suspicious_Create_File_Shell_Config",
"Suspicious_Create_File_Ssh_AuthorizedKeys",
"Suspicious_Create_File_Systemd_Service",
"Suspicious_Create_File_Xdg_Autostart",
"Suspicious_Create_Process_Chkconfig_DisableRCScripts",
"Suspicious_Create_Process_Iptables_ModifyFirewall",
"Suspicious_Create_Process_Service_Disable",
"Suspicious_Create_Process_Setenforce_ModifySELinux",
"Suspicious_Create_Process_Sudo_Bruteforce",
"Suspicious_Create_Process_WipeData_Destruction",
"Suspicious_Read_File_Email_Local",
"Suspicious_Read_File_Fstab_Configuration",
"Suspicious_Read_File_Passwd_CredentialsEnumeration",
"Suspicious_Read_File_Polkit_Policy",
"Suspicious_Read_File_Shadow_CredentialsDumping",
"Suspicious_Read_System_BIOS_Reconnaissance",
"Suspicious_Read_System_DMI_CheckVM",
"Suspicious_Read_System_Kernel_ASLR",
"Suspicious_Read_System_Kernel_PtraceScope",
"Suspicious_Read_System_KeyState_Keylogger",
"Suspicious_Read_System_PCI_GPU",
"Suspicious_Read_System_PCI_USB",
"Suspicious_Read_System_Proc_ARPSessions",
"Suspicious_Read_System_Proc_BootCmdline",
"Suspicious_Read_System_Proc_Modules",
"Suspicious_Read_System_Proc_Route",
"Suspicious_Read_System_Proc_SCSI",
"Suspicious_Read_System_Proc_TCPSessions",
"Suspicious_Write_Disk_Data_DirectAccess",
"Suspicious_Write_File_PAM_Persistence",
"Suspicious_Write_Process_Inject_Ptrace",
"Tunneling_via_SSH",
"Tunneling_via_SSHuttle_Client",
"Tunneling_via_SSHuttle_Server"
"Suspicious_Write_Process_Inject_Ptrace"
],
"fields": [
"action",
"alert.key",
"category.generic",
"category.high",
"category.low",
"correlation_name",
"correlation_type",
"event_src.asset",
"event_src.host",
"event_src.hostname",
"event_src.id",
"event_src.ip",
"event_src.subsys",
"event_src.title",
"event_src.vendor",
"importance",
"incident.aggregation.key",
"incident.aggregation.timeout",
"incident.category",
"incident.severity",
"labels",
"numfield1",
"numfield2",
"object",
"object.account.group",
"object.account.id",
"object.account.name",
"object.fullpath",
"object.name",
"object.path",
"object.process.cmdline",
"object.process.cwd",
"object.process.fullpath",
"object.process.id",
"object.process.meta",
"object.process.name",
"object.process.parent.id",
"object.process.path",
"object.state",
"object.value",
"status",
"subject",
"subject.account.id",
"subject.account.name",
"subject.account.privileges",
"subject.account.session_id",
"subject.process.fullpath",
"subject.process.id",
"subject.process.meta",
"subject.process.name",
"subject.process.parent.id",
"subject.process.path",
"subject.type"
"subject.process.path"
],
"last_module_update": "2022-11-07 12:52:12",
"last_update": "2022-11-07 12:52:12"
"last_module_update": "2023-05-25 12:00:00",
"last_update": "2023-05-25 12:00:00"
},
{
"group_id": "",
@@ -511,379 +448,48 @@
},
"actions": [],
"events": [
"Abnormal_Directory_for_Process",
"Abusing_CredSSP",
"Access_System_Credential_files_via_cmdline",
"Access_into_Sensitive_Files_via_Network_Share",
"Accessibility_Feature_Tool_Abuse",
"Account_Created_on_Local_System",
"Account_Discovery",
"Account_or_Group_discovery_via_SAM_R",
"Add_new_user_in_commandline",
"Alternate_Data_Stream",
"Audit_XP_params_change",
"Auxiliary_SuspiciousWeightsTrojan_Windows_subject_a",
"Auxiliary_SuspiciousWeightsTrojan_Windows_subject_b",
"BitsJob_Download_and_Run",
"COM_object_persistence",
"CVE_2021_41379_Subrule_Elevation_service_chain",
"CVE_2021_41379_Subrule_Start_elevation",
"CVE_2021_41379_exploitation",
"CVE_2022_26500_26501_exploitation",
"CVE_2022_26503_Subrule_1",
"CVE_2022_26503_Subrule_2",
"CVE_2022_26503_exploitation",
"Client_Side_Execution_via_DCOM",
"Clipboard_Access",
"Clipboard_Access_Powershell",
"Cmstp_AWL_Bypass",
"Cobalt_Strike_Assembly",
"Cobalt_Strike_Payload_Delivery_Check",
"Cobalt_Strike_Pipe_from_AdminShare",
"Cobalt_Strike_Process_Injection",
"Cobalt_Strike_Psexec_Jump",
"Cobalt_Strike_SMB_Beacon",
"Cobalt_Strike_Service_Move",
"Code_Execution_Via_JDK_Tools",
"Command_Processor_Autorun_Modify",
"Computer_object_ldap_request",
"ControlPanel_AWL_Bypass",
"Copy_Mimikatz_To_Share",
"Credential_Access_to_Passwords_Storage",
"Credential_Dump_in_Local_Registry",
"Csc_AWL_Bypass",
"DRSUAPI_User_Enumeration",
"DSInternals_Usage",
"Disable_Credential_Guard",
"Disable_LSA_Protection",
"Disable_Restricted_Admin_Mode",
"Dnscmd_AWL_Bypass",
"Domain_Controllers_Discovery",
"Domain_Trust_Discovery",
"Download_File_Through_Curl",
"Download_File_Through_Windows_Defender",
"Downloading_Remote_File_Via_Lolbas",
"Empire_Stager",
"Esentutil_Copy_File",
"Execute_Encoded_Powershell",
"Execute_Malicious_Command",
"Execute_Malicious_Powershell_Cmdlet",
"Execute_PSEXEC",
"Execute_Suspicious_Command_via_cmd",
"Failed_LSASS_Injection",
"Fast_Create_and_Delete_Account",
"Finger_AWL_Bypass",
"Groups_And_Users_Enumeration",
"Hidden_Scheduled_Task",
"Hidden_Service_Create",
"Hide_Account_from_Logon_Screen",
"IEExec_AWL_Bypass",
"Impacket_SMBEXEC",
"Impacket_Secretsdump",
"InstallUtil_AWL_Bypass",
"Intercept_Creds_from_MSTSC",
"Internal_Monologue_Attack",
"KeePass_Keys_Extraction",
"KeePass_Persistence",
"Koadic_MSHTA_Stager",
"Koadic_REGSVR32_Stager",
"Koadic_Rundll32_Stager",
"Koadic_WMIC_Stager",
"LAPS_Enumeration",
"LOLBin_Copying",
"LSA_SSP_Change",
"Lazagne_Usage",
"Local_Pass_the_Hash",
"Lsass_Dump_via_SilentProcessExit",
"Lsass_SilentProcessExit_Keys",
"MSBuild_AWL_Bypass",
"MSHTA_AWL_Bypass",
"MSXSL_AWL_Bypass",
"Malicious_CHM_File",
"Malicious_Office_Document",
"Malware_Backdoor_Win32_Evilnum_a",
"Malware_Backdoor_Win32_Havex_a",
"Malware_Backdoor_Win32_NetWire_b",
"Malware_Backdoor_Win32_Pteredo_a",
"Malware_Backdoor_Win64_Throwback_b",
"Malware_Exploit_Win32_CVE_2022_30190_a",
"Malware_Exploit_Win32_PrintSpooler_a",
"Malware_Hacktool_Win32_CrackMapExec_a",
"Malware_Trojan_Dropper_MSOffice_Launcher_a",
"Malware_Trojan_Dropper_Script_Generic_a",
"Malware_Trojan_Dropper_Win32_Generic_k",
"Malware_Trojan_Ransom_Win32_Generic_a",
"Malware_Trojan_Ransom_Win32_Generic_b",
"Malware_Trojan_Win32_Generic_a",
"Malware_Trojan_Win32_Generic_o",
"Malware_Trojan_Win32_Generic_s",
"Malware_Trojan_Win32_Generic_t",
"Mavinject_AWL_Bypass",
"Metasploit_Payload",
"Microsoft_Teams_AWL_Bypass",
"Mimikatz_Command",
"Msiexec_AWL_Bypass",
"NetCat_Usage",
"Network_Share_Discovery",
"Odbcconf_AWL_Bypass",
"Office_File_with_Macros",
"Office_Normal_dotm_modification",
"Office_XLL_modification",
"Password_Policy_Discovery",
"Pcalua_AWL_Bypass",
"Permission_Groups_Discovery",
"Persistence_Netsh_DLL",
"Port_Forwarding_or_Tunneling",
"Potential_Users_Or_Groups_Enumeration_Process",
"Potential_domain_groups_and_users_enumeration_handle",
"Potential_localgroups_and_administrators_enumeration_handle",
"Powershell_Remoting",
"Process_Discovery",
"Proxy_Tools_Usage",
"RDP_Session_Hijacking",
"Reading_Registry_Objects",
"RegAsm_or_RegSvcs_AWL_Bypass",
"Registry_Winlogon_Helper",
"Regsvr32_AWL_Bypass",
"Remote_Admin_Share_Access",
"Remote_Code_Execution_Via_AtSvc",
"Remote_Connection_through_SMBEXEC_WinXP",
"Remote_Copy_Credential_Dump_Artifact",
"Remote_Copying_Malicious_File",
"Remote_File_Download_Via_Certutil",
"Remote_Password_Dump",
"Remoting_Impacket_PsExec",
"Remoting_SysInternals_PsExec",
"Remoting_WMI",
"Remoting_WinExec",
"Remoting_Windows_Shell",
"Remove_Access_To_Sensitive_Account",
"Remove_Account_From_Sensitive_Group",
"Rubeus_Usage",
"RunAs_Subrule_Login",
"RunAs_System_or_External_tools",
"Run_Malicious_Msbuild_Project",
"Run_whoami_as_System",
"Rundll32_AWL_Bypass",
"SPN_LDAP_requests",
"Scheduled_Task_Was_Created_Or_Updated_Via_Schtasks",
"Search_Stored_Credentials",
"Service_Created_or_Modified",
"Shadow_Copies_Deletion_with_Builtin_Tools",
"Shadow_Key_Creation",
"Shadow_Screen_save",
"Shadow_Screen_saves_PowerShell",
"SharpSploit_Usage",
"SharpWMI_Usage",
"Sharphound_Client_Side",
"Sharphound_Server_Side",
"SilentTrinity_Stager",
"Sliver_Shell_Usage",
"Software_Discovery",
"Spoolsv_Priv_Escalation",
"Stop_Important_Service",
"Stop_Important_Service_registry",
"Subrule_Sharphound_Client_Side",
"Subrule_Sharphound_Server_Side",
"Suspicious_Access_To_Users_Folder",
"Suspicious_Child_from_Messenger_Process",
"Suspicious_Create_File_FromBrowser_SuspiciousExtension",
"Suspicious_Create_File_FromMessenger_SuspiciousExtension",
"Suspicious_Create_File_PartitionMaster_DirectAccess",
"Suspicious_Create_File_RawDisk_DirectAccess",
"Suspicious_Create_Object_Account_Persistence",
"Suspicious_Create_Process_At_Persistence",
"Suspicious_Create_Process_BitsAdmin_RestrictionBypass",
"Suspicious_Create_Process_Debugger_Inject",
"Suspicious_Create_Process_DirectoryMock_UACBypass",
"Suspicious_Create_Process_NetSh_NetShell",
"Suspicious_Create_Process_Ping_SelfDelete",
"Suspicious_Create_Process_RunScript_WScript",
"Suspicious_Create_Process_Schtasks_Persistence",
"Suspicious_Create_Process_TaskKill_TerminateProcess",
"Suspicious_Create_Process_VSTOInstaller_OfficeAddIns",
"Suspicious_Create_Process_VirtualInstance_Evasion",
"Suspicious_Create_Process_WinDef_AddExclusion",
"Suspicious_Create_Process_WinDef_ChangeSettings",
"Suspicious_Create_Process_WithDebugger_Execution_Predetect_2_1",
"Suspicious_Create_Process_WusaExtract_UACBypass",
"Suspicious_Create_Registry_Key_SafeBoot",
"Suspicious_Delete_Registry_Key_ETWProvider",
"Suspicious_Delete_Registry_Key_RunAsPPL",
"Suspicious_Delete_Registry_Key_SafeBoot",
"Suspicious_Delete_Registry_Key_Service",
"Suspicious_Delete_Registry_Key_TaskSD",
"Suspicious_File_Created_by_Legal_Process",
"Suspicious_Windows_Kernel_creating",
"Suspicious_Wmic_Command",
"Suspicious_Write_File_EfiSystemPartition_Persistence",
"Suspicious_Write_File_USB_AirSpread",
"Suspicious_Write_Process_Inject_CreateRemoteThread",
"Suspicious_Write_Registry_Key_CPL",
"Suspicious_Write_Registry_Key_ChangeDefaultFileAssociation",
"Suspicious_Write_Registry_Key_ChangeFirewallPolicy",
"Suspicious_Write_Registry_Key_ChangeRdpPort",
"Suspicious_Write_Registry_Key_CredentialsDelegation",
"Suspicious_Write_Registry_Key_DisableAppLaunch",
"Suspicious_Write_Registry_Key_DisableTaskManager",
"Suspicious_Write_Registry_Key_DisableUAC",
"Suspicious_Write_Registry_Key_FlashConfigEnrollee",
"Suspicious_Write_Registry_Key_ImagePath",
"Suspicious_Write_Registry_Key_InjectAppCertDlls",
"Suspicious_Write_Registry_Key_InjectAppInitDLLs",
"Suspicious_Write_Registry_Key_InjectImageFileExecutionOptions",
"Suspicious_Write_Registry_Key_InjectLoadAppInitDLLs",
"Suspicious_Write_Registry_Key_InjectSilentProcessExit",
"Suspicious_Write_Registry_Key_KillAntivirus",
"Suspicious_Write_Process_Inject_ProcessTampering",
"Suspicious_Write_Registry_Key_LsaComponents",
"Suspicious_Write_Registry_Key_ModifyRdpSettings",
"Suspicious_Write_Registry_Key_OfficeTemplate",
"Suspicious_Write_Registry_Key_ProxyHijack",
"Suspicious_Write_Registry_Key_SafeBoot",
"Suspicious_Write_Registry_Key_ScreenSaver",
"Suspicious_Write_Registry_Key_SvchostContextService",
"Suspicious_Write_Registry_Key_TerminalContextService",
"Suspicious_Write_Registry_Key_TimeProviders",
"Suspicious_process_execution_sequence",
"Sysmon_Driver_Unload",
"System_Information_Discovery",
"System_Network_Configuration_Discovery",
"System_Network_Connections_Discovery",
"System_Service_Discovery",
"TikiTorch_Process_Injection",
"Universal_Windows_Platform_Apps_Modify",
"User_object_ldap_request",
"Userinitmprlogonscript_Modify",
"WDAC_Bypass_via_Dbgsrv",
"WDigest_Enable",
"WMI_Subscriptions",
"WinAPI_Access_from_Powershell",
"Windows_Accessibility_StickyKey_modification",
"Windows_Autorun_Modification",
"Windows_Defender_Disable",
"Windows_Eventlog_cleaning",
"Windows_Hacktool_Usage",
"Windows_Malicious_service_registration",
"Windows_Registry_sensitive_keys_modification",
"Windows_Service_Installed",
"Windows_Shadow_copy_removal",
"Windows_WMI_event_consumer_registration",
"Windows_WMI_event_subscription_removal",
"Windows_firewall_enable_local_RDP",
"XSL_Script_WMIC_Execution"
"Suspicious_Write_Registry_Key_ScreenSaver"
],
"fields": [
"action",
"alert.context",
"alert.key",
"category.generic",
"category.high",
"category.low",
"chain_id",
"correlation_name",
"correlation_type",
"dst.asset",
"dst.fqdn",
"dst.host",
"dst.hostname",
"dst.ip",
"dst.mac",
"dst.port",
"event_src.asset",
"event_src.category",
"event_src.fqdn",
"event_src.host",
"event_src.hostname",
"event_src.ip",
"event_src.rule",
"event_src.subsys",
"event_src.title",
"event_src.vendor",
"importance",
"incident.aggregation.key",
"incident.aggregation.time_window",
"incident.aggregation.timeout",
"incident.attacking_addresses",
"incident.category",
"incident.severity",
"labels",
"numfield1",
"object",
"object.account.domain",
"object.account.fullname",
"object.account.id",
"object.account.name",
"object.account.privileges",
"object.account.session_id",
"object.domain",
"object.fullpath",
"object.hash",
"object.id",
"object.name",
"object.new_value",
"object.path",
"object.process.cmdline",
"object.process.cwd",
"object.process.fullpath",
"object.process.guid",
"object.process.hash",
"object.process.id",
"object.process.meta",
"object.process.name",
"object.process.original_name",
"object.process.parent.cmdline",
"object.process.parent.fullpath",
"object.process.parent.guid",
"object.process.parent.id",
"object.process.parent.name",
"object.process.parent.path",
"object.process.path",
"object.process.version",
"object.property",
"object.query",
"object.state",
"object.storage.fullpath",
"object.storage.id",
"object.storage.name",
"object.storage.path",
"object.type",
"object.value",
"object.vendor",
"object.version",
"reason",
"src.asset",
"src.fqdn",
"src.host",
"src.hostname",
"src.ip",
"src.mac",
"src.port",
"status",
"subject",
"subject.account.domain",
"subject.account.fullname",
"subject.account.id",
"subject.account.name",
"subject.account.privileges",
"subject.account.session_id",
"subject.name",
"subject.process.cmdline",
"subject.process.cwd",
"subject.process.fullpath",
"subject.process.guid",
"subject.process.hash",
"subject.process.id",
"subject.process.meta",
"subject.process.name",
"subject.process.original_name",
"subject.process.parent.id",
"subject.process.path",
"subject.process.version",
"subject.type",
"subject.version"
"subject.process.path"
],
"last_module_update": "2022-10-26 00:00:00",
"last_update": "2022-10-26 00:00:00"
"last_module_update": "2023-05-25 00:00:00",
"last_update": "2023-05-25 00:00:00"
},
{
"group_id": "",
@@ -913,7 +519,152 @@
"fields": [
"reason"
],
"last_module_update": "2022-11-02 00:00:00",
"last_update": "2022-11-02 00:00:00"
"last_module_update": "2023-05-25 12:00:00",
"last_update": "2023-05-25 12:00:00"
},
{
"group_id": "",
"policy_id": "",
"state": "release",
"template": "generic",
"os": {
"darwin": [
"amd64"
],
"linux": [
"386",
"amd64"
],
"windows": [
"386",
"amd64"
]
},
"name": "shell",
"version": {
"major": 1,
"minor": 0,
"patch": 0
},
"actions": [
"shell_start",
"shell_stop"
],
"events": [
"shell_action_exec_failed",
"shell_action_exec_success"
],
"fields": [
],
"last_module_update": "2023-06-06 00:00:00",
"last_update": "2023-06-06 00:00:00"
},
{
"group_id": "",
"policy_id": "",
"state": "release",
"template": "empty",
"os": {
"linux": [
"386",
"amd64"
]
},
"name": "auditd",
"version": {
"major": 1,
"minor": 0,
"patch": 0
},
"actions": [],
"events": [
"auditd_error"
],
"fields": [
"message"
],
"tags": [],
"last_module_update": "2023-06-06 00:00:00",
"last_update": "2023-06-06 00:00:00"
},
{
"group_id": "",
"policy_id": "",
"state": "release",
"template": "empty",
"os": {
"windows": [
"386",
"amd64"
]
},
"name": "osquery",
"version": {
"major": 1,
"minor": 0,
"patch": 0
},
"actions": [],
"events": [
"osquery_already_installed",
"osquery_already_started",
"osquery_config_updated_error",
"osquery_config_updated_success",
"osquery_flagfile_updated_error",
"osquery_flagfile_updated_success",
"osquery_installed_error",
"osquery_installed_success",
"osquery_started_error",
"osquery_started_success",
"osquery_unexpected_stopped",
"osquery_unexpected_uninstalled",
"osquery_uninstalled_error",
"osquery_uninstalled_success"
],
"fields": [
"reason",
"version"
],
"last_module_update": "2023-06-06 00:00:00",
"last_update": "2023-06-06 00:00:00"
},
{
"group_id": "",
"policy_id": "",
"state": "release",
"template": "empty",
"os": {
"linux": [
"386",
"amd64"
]
},
"name": "osquery_linux",
"version": {
"major": 1,
"minor": 0,
"patch": 0
},
"actions": [],
"events": [
"osquery_linux_already_installed",
"osquery_linux_already_started",
"osquery_linux_config_updated_error",
"osquery_linux_config_updated_success",
"osquery_linux_installed_error",
"osquery_linux_installed_success",
"osquery_linux_started_error",
"osquery_linux_started_success",
"osquery_linux_unexpected_stopped",
"osquery_linux_unexpected_uninstalled",
"osquery_linux_uninstalled_error",
"osquery_linux_uninstalled_success"
],
"fields": [
"reason",
"version"
],
"last_module_update": "2023-06-06 00:00:00",
"last_update": "2023-06-06 00:00:00"
}
]
+1 -1
View File
@@ -8,7 +8,7 @@ const name = "empty";
module.exports = {
name,
props: ["protoAPI", "hash", "module", "eventsAPI", "modulesAPI", "components", "viewMode"],
props: ["protoAPI", "hash", "module", "api", "components", "viewMode"],
data: () => ({
})
};
+132 -10
View File
@@ -3,6 +3,7 @@ require("strict")
require("engines.base_engine")
require("engines.corr_engine")
local rex = require("rex_pcre2")
local json = require("cjson.safe")
CActsEngine = newclass("CActsEngine", CBaseEngine)
@@ -19,7 +20,7 @@ function CActsEngine:init(cfg)
self.super:init(cfg)
self.correlator = CCorrEngine(
function(event)
function (event)
self:push_result(event)
end
)
@@ -27,13 +28,14 @@ function CActsEngine:init(cfg)
if not self.correlator.valid then
__log.info("try to restore correlator instance")
self.correlator = CCorrEngine(
function(event)
function (event)
self:push_result(event)
end,
true
)
end
self.excludes = {}
self.proc_id_fields = {
"object.process.id",
"object.process.parent.id",
@@ -41,6 +43,13 @@ function CActsEngine:init(cfg)
"subject.process.parent.id",
}
self.topic_name = "raw_events"
if __imc.subscribe_to_topic then
assert(__imc.subscribe_to_topic(self.topic_name, __gid), "can't subscribe to topic")
else
__log.debug("topics mechanism unsupported on agent side")
end
-- initialization of object after base class constructing
self:update_config_cb()
end
@@ -60,7 +69,7 @@ function CActsEngine:timer_cb()
-- __log.debug("timer_cb CActsEngine")
local acts_engine = CActsEngine:cast(self)
acts_engine.correlator:pullEvents()
__metric.add_int_gauge_counter("corr_agent_mem_usage", collectgarbage("count")*1024)
__metric.add_int_gauge_counter("corr_agent_mem_usage", collectgarbage("count") * 1024)
return 500
end
@@ -70,6 +79,9 @@ function CActsEngine:quit_cb()
__log.debug("quit_cb CActsEngine")
-- here will be triggered before closing vxproto object and destroying the state
if __imc.unsubscribe_from_topic then
assert(__imc.unsubscribe_from_topic(self.topic_name, __gid), "can't unsubscribe from topic")
end
end
-- in: string
@@ -90,6 +102,10 @@ end
-- out: nil
function CActsEngine:update_config_cb()
__log.debug("update_config_cb CActsEngine")
local acts_engine = CActsEngine:cast(self)
acts_engine:load_excludes()
-- actual current configuration contains into next fields
-- self.config.actions
-- self.config.events
@@ -115,8 +131,10 @@ function CActsEngine:recv_data_cb(src, data)
__metric.add_int_counter("corr_agent_events_recv_count", #odata)
__metric.add_int_counter("corr_agent_events_recv_size", #data)
for _,v in ipairs(odata) do
v.body = json.encode(v.body)
for _, v in ipairs(odata) do
if v.mime == "application/x-pt-eventlog" or v.mime == "application/json" then
v.body = json.encode(v.body)
end
while acts_engine.correlator:sendEvent(json.encode(v)) == false do
__api.await(50)
end
@@ -157,6 +175,108 @@ function CActsEngine:recv_action_cb(src, data, name)
return false
end
-- in: nil
-- out: nil
function CActsEngine:load_excludes()
self.excludes = {}
for _, exclude in ipairs(self.config.module.excludes) do
local compiled = {}
for _, cond in ipairs(exclude) do
if type(cond) ~= "table" then
__log.errorf("condition isn't a table (array) value: '%s'", json.encode(cond))
goto continue
end
if type(cond.fields) ~= "table" then
__log.errorf("fields isn't a table (array) value: '%s'", json.encode(cond.fields))
goto continue
end
if #cond.fields == 0 then
__log.errorf("fields is empty array")
goto continue
end
for _, field in ipairs(cond.fields) do
if type(field) ~= "string" then
__log.errorf("field isn't a string value: '%s'", json.encode(field))
goto continue
end
end
if type(cond.regex) ~= "string" then
__log.errorf("regex isn't a string value: '%s'", json.encode(cond.regex))
goto continue
end
local status, regex = pcall(rex.new, cond.regex)
if not status then
__log.errorf("can't compile regexp: '%s' with error '%s'", cond.regex, regex)
goto continue
end
table.insert(compiled, {
fields = cond.fields,
sregex = cond.regex,
cregex = regex,
})
::continue::
end
if #compiled ~= 0 then
table.insert(self.excludes, compiled)
end
end
__log.infof("loaded %d excludes from '%s'", #self.excludes, json.encode(self.config.module.excludes))
end
-- in: table
-- out: bool, table
function CActsEngine:match_excludes(event)
local body = {}
for fname, fvalue in pairs(event) do
if type(fname) ~= "string" then
fname = tostring(fname) or ""
end
if type(fvalue) ~= "string" then
fvalue = json.encode(fvalue) or ""
end
body[fname] = fvalue
end
for _, exclude in ipairs(self.excludes) do
local matches = {}
for _, cond in ipairs(exclude) do
for _, fname in ipairs(cond.fields) do
local fvalue = body[fname]
if not fvalue then
-- field '{fname}' isn't exist in body
goto next_field
end
local match = cond.cregex:match(fvalue)
if match then
table.insert(matches, {
field = fname,
match = match,
regex = cond.sregex,
value = fvalue,
})
-- field '{fname}' with value '{fvalue}' matches '{match}' by regex '{cond.sregex}'
__log.debugf("field '%s' with value '%s' matches '%s' by regex '%s'", fname, fvalue, match, cond.sregex)
goto next_condition
end
-- field '{fname}' with value '{fvalue}' doesn't match by regex '{cond.sregex}'
__log.debugf("field '%s' with value '%s' doesn't match by regex '%s'", fname, fvalue, cond.sregex)
::next_field::
end
-- fields '{cond.fields}' don't match by regex '{cond.sregex}'
__log.debugf("fields '%s' don't match by regex '%s'", json.encode(cond.fields), cond.sregex)
goto next_exclude
::next_condition::
end
if #matches == #exclude then
__log.debugf("all exclude conditions matched with event body '%s'", json.encode(matches))
return true, matches
end
::next_exclude::
end
return false
end
-- in: table
-- out: nil
function CActsEngine:push_result(event)
@@ -170,11 +290,11 @@ function CActsEngine:push_result(event)
if event_name == nil or event_name == "" then return end
local config_events = self.config["events"] or {events={}}
local config_event = config_events[event_name] or {fields={}}
local config_fields = self.config["fields"] or {properties={}}
local config_events = self.config["events"] or { events = {} }
local config_event = config_events[event_name] or { fields = {} }
local config_fields = self.config["fields"] or { properties = {} }
local _fields = config_event["fields"] or {}
local defaults = {string = "", number = 0, integer = 0, object = {}, array = {}, boolean = false, null = nil}
local defaults = { string = "", number = 0, integer = 0, object = {}, array = {}, boolean = false, null = nil }
for _, v in ipairs(self.proc_id_fields) do
result[v] = tonumber(result[v])
@@ -184,5 +304,7 @@ function CActsEngine:push_result(event)
end
__log.debugf("perform logic to push result: '%s'", json.encode(result))
self:push_event(event_name,result)
if not self:match_excludes(result) then
self:push_event(event_name, result)
end
end
@@ -156,7 +156,7 @@ function CBaseEngine:commit_success(src, action_name, action_data)
-- case to notify other side about action execution result
if type(action_data.retaddr) == "string" and action_data.retaddr ~= "" then
local data = cjson.encode(glue.merge({status = "success"}, action_data))
local data = cjson.encode(glue.merge({ status = "success" }, action_data))
__api.send_data_to(src, data)
end
end
@@ -174,7 +174,7 @@ function CBaseEngine:commit_failed(src, action_name, action_data)
-- case to notify other side about action execution result
if type(action_data.retaddr) == "string" and action_data.retaddr ~= "" then
local data = cjson.encode(glue.merge({status = "error"}, action_data))
local data = cjson.encode(glue.merge({ status = "error" }, action_data))
__api.send_data_to(src, data)
end
end
@@ -200,7 +200,7 @@ end
-- out: string
-- destination token (string) it'll be empty if agent disconnected
function CBaseEngine:get_server_token()
local tablelength = function(t)
local tablelength = function (t)
local count = 0
for _ in pairs(t) do count = count + 1 end
return count
@@ -85,17 +85,17 @@ end
function CCorrEngine:init(receiveEvents, restore)
zip.unzip(__tmpdir .. "\\data\\graphs.zip", "-d", __tmpdir .. "\\data\\")
self.callbacks = {
receive = function(type, data, size)
receive = function (type, data, size)
if type == 1 and receiveEvents then
receiveEvents(ffi.string(data,size))
receiveEvents(ffi.string(data, size))
elseif type == 2 then
self.statistics = json.decode(ffi.string(data,size))
self.statistics = json.decode(ffi.string(data, size))
elseif type == 3 then
__log.errorf("caught error from corr lib: '%s'", ffi.string(data,size))
__log.errorf("caught error from corr lib: '%s'", ffi.string(data, size))
end
return size
end
end,
}
self.statistics = {}
@@ -179,12 +179,12 @@ function CCorrEngine.get_file_hash_md5(filepath)
end
function CCorrEngine.copyFile(src, dst, force)
local f, err = io.open(src, 'rb')
local f, err = io.open(src, "rb")
if not f then return nil, err end
local t, ok
if not force then
t = io.open(dst, 'rb')
t = io.open(dst, "rb")
if t then
f:close()
t:close()
@@ -192,7 +192,7 @@ function CCorrEngine.copyFile(src, dst, force)
end
end
t, err = io.open(dst, 'w+b')
t, err = io.open(dst, "w+b")
if not t then
f:close()
return nil, err
+6 -8
View File
@@ -2,7 +2,7 @@ require("engines.acts_engine")
-- base config to actions engine
local cfg = {
config = {}
config = {},
}
-- actions engine initialize
@@ -12,24 +12,22 @@ local acts_engine = CActsEngine(cfg)
__api.set_recv_timeout(5000) -- 5s
__api.add_cbs({
data = function(src, data)
data = function (src, data)
__log.debugf("receive data from '%s' with data", src)
assert(acts_engine ~= nil, "actions engine instance is not initialized")
return acts_engine:recv_data(src, data)
end,
file = function(src, path, name)
file = function (src, path, name)
__log.infof("receive file from '%s' with name '%s' path '%s'", src, name, path)
assert(acts_engine ~= nil, "actions engine instance is not initialized")
return acts_engine:recv_file(src, path, name)
end,
-- text = function(src, text, name)
-- msg = function(src, msg, mtype)
action = function(src, data, name)
action = function (src, data, name)
__log.infof("receive action '%s' from '%s' with data %s", name, src, data)
assert(acts_engine ~= nil, "actions engine instance is not initialized")
@@ -37,8 +35,7 @@ __api.add_cbs({
__log.infof("requested action '%s' was executed: %s", name, action_result)
return action_result
end,
control = function(cmtype, data)
control = function (cmtype, data)
__log.debugf("receive control msg '%s' with data %s", cmtype, data)
assert(acts_engine ~= nil, "actions engine instance is not initialized")
@@ -61,6 +58,7 @@ __api.add_cbs({
__log.infof("module '%s' was started", __config.ctx.name)
acts_engine:run()
__api.del_cbs({ "data", "file", "action", "control" })
__log.infof("module '%s' was stopped", __config.ctx.name)
-- explicit destroy engine
+5 -6
View File
@@ -50,9 +50,8 @@ function CModule:init(moduleName)
self.api = {
create = self.module.module_create,
destroy = self.module.module_destroy,
version = nil,--self.module.Module__Version,
is_inited = false
version = nil, --self.module.Module__Version,
is_inited = false,
}
self.transport = ffi.new("api_module_transport[1]", {})
end
@@ -101,7 +100,7 @@ function CModule:register(profile, callbacks)
self.functions = {}
self.functions["receive"] = function(transport, type, data, size)
self.functions["receive"] = function (transport, type, data, size)
if callbacks and transport == self.transport and callbacks["receive"] then
return callbacks["receive"](type, data, size)
end
@@ -116,7 +115,7 @@ function CModule:register(profile, callbacks)
local ctmpdir = ffi.new("char[?]", 256)
lk32.GetDllDirectoryA(256, ctmpdir)
lk32.SetDllDirectoryA(__tmpdir)
self.api.is_inited = self.module_i.init(self.transport,self.profile, #profile)
self.api.is_inited = self.module_i.init(self.transport, self.profile, #profile)
lk32.SetDllDirectoryA(ctmpdir)
return self.api.is_inited, ""
@@ -131,7 +130,7 @@ function CModule:start()
self.module_i.start(self.transport)
end
function CModule:send(type,data)
function CModule:send(type, data)
if self.transport[0].to_module == nil then
return
end
+90 -1
View File
@@ -1,6 +1,95 @@
{
"additionalProperties": false,
"properties": {},
"properties": {
"excludes": {
"items": {
"items": {
"properties": {
"fields": {
"rules": {
"required": true
},
"type": "array",
"ui": {
"columns": 6,
"label": "dx: window.__$ncform.lang === 'en' ? 'Variables' : 'Переменные'",
"widget": "select",
"widgetConfig": {
"allowCreate": true,
"clearable": true,
"defaultFirstOption": false,
"enumSourceRemote": {
"otherParams": {
"filters[]": "{\"field\": \"module_name\", \"value\": [\"correlator\"]}",
"group": "name",
"lang": "en",
"page": 1,
"pageSize": -1,
"type": "init"
},
"remoteUrl": "/api/v1/options/fields",
"resField": "data.fields"
},
"filterLocal": true,
"filterable": true,
"itemDataKey": "item",
"itemLabelField": "name",
"itemValueField": "name",
"multiple": true
}
},
"value": []
},
"regex": {
"rules": {
"customRule": [
{
"errMsg": "regexp format required",
"script": "dx: ((val) =\u003e { try { const v = new RegExp(val); return typeof(v) !== undefined } catch(e) { return false } })(__get({{$root}}, {{$path}}))"
}
],
"required": true
},
"type": "string",
"ui": {
"columns": 6,
"label": "dx: window.__$ncform.lang === 'en' ? 'Regular expression' : 'Регулярное выражение'"
}
}
},
"required": [],
"type": "object",
"ui": {
"label": "dx: ((fields) =\u003e { const or = (window.__$ncform.lang === 'en' ? ' OR ' : ' ИЛИ '); const retIsArray = ((val, def) =\u003e (typeof val === 'object' \u0026\u0026 Array.isArray(val) ? val : def)); fields = retIsArray(fields, []); switch(fields.length) { case 0: return (window.__$ncform.lang === 'en' ? 'empty' : 'пустой список'); case 1: return fields[0]; default: return '(' + [...new Set(fields.map(f =\u003e '...' + f.split('.').slice(-1)[0]))].join(or) + ')'; } })(__get({{$root}}, {{$path}} + '.fields'))"
}
},
"type": "array",
"ui": {
"label": "dx: (() =\u003e { const and = (window.__$ncform.lang === 'en' ? ' AND ' : ' И '); const or = (window.__$ncform.lang === 'en' ? ' OR ' : ' ИЛИ '); const retIsArray = ((val, def) =\u003e (typeof val === 'object' \u0026\u0026 Array.isArray(val) ? val : def)); const getLabel = ((fields) =\u003e { fields = retIsArray(fields, []); switch(fields.length) { case 0: return (window.__$ncform.lang === 'en' ? 'empty' : 'пустой список'); case 1: return JSON.stringify(fields[0]); default: return '(' + [...new Set(fields.map(f =\u003e JSON.stringify('...' + f.split('.').slice(-1)[0])))].join(or) + ')'; } }); return retIsArray(__get({{$root}}, {{$path}}), []).map(e =\u003e getLabel(e.fields)).join(and);})()",
"noLabelSpace": true,
"showIdxLabel": false,
"showLegend": false,
"widget": "array-tabs",
"widgetConfig": {
"showOneIfEmpty": true
}
}
},
"rules": {},
"type": "array",
"ui": {
"noLabelSpace": true,
"showIdxLabel": false,
"showLabel": false,
"showLegend": true,
"widgetConfig": {
"collapsed": true,
"disableReorder": true,
"itemCollapse": true
}
}
}
},
"required": [],
"type": "object"
}
+3 -1
View File
@@ -1 +1,3 @@
{}
{
"excludes": []
}
+3 -1
View File
@@ -1 +1,3 @@
{}
{
"excludes": []
}
+12 -1
View File
@@ -1,7 +1,18 @@
{
"action_config": {},
"actions": {},
"config": {},
"config": {
"excludes": {
"en": {
"description": "Exclusion list",
"title": "Exclusions"
},
"ru": {
"description": "Список исключений",
"title": "Исключения"
}
}
},
"event_config": {
"Suspicious_Create_Process_BitsAdmin_RestrictionBypass": {},
"Suspicious_Create_Process_NetSh_NetShell": {},
+2 -4
View File
@@ -1,13 +1,11 @@
__api.add_cbs({
-- data = function(src, data)
-- file = function(src, path, name)
-- text = function(src, text, name)
-- msg = function(src, msg, mtype)
-- action = function(src, data, name)
control = function(cmtype, data)
control = function (cmtype, data)
__log.debugf("receive control msg '%s' with payload: %s", cmtype, data)
-- cmtype: "quit"
@@ -25,4 +23,4 @@ __api.await(-1)
__log.infof("module '%s' was stopped", __config.ctx.name)
return 'success'
return "success"
+1 -1
View File
@@ -8,7 +8,7 @@ const name = "empty";
module.exports = {
name,
props: ["protoAPI", "hash", "module", "eventsAPI", "modulesAPI", "components", "viewMode"],
props: ["protoAPI", "hash", "module", "api", "components", "viewMode"],
data: () => ({
})
};
@@ -3,6 +3,7 @@ require("strict")
require("engines.base_engine")
require("engines.corr_engine")
local rex = require("rex_pcre2")
local json = require("cjson.safe")
CActsEngine = newclass("CActsEngine", CBaseEngine)
@@ -19,7 +20,7 @@ function CActsEngine:init(cfg)
self.super:init(cfg)
self.correlator = CCorrEngine(
function(event)
function (event)
self:push_result(event)
end
)
@@ -27,7 +28,7 @@ function CActsEngine:init(cfg)
if not self.correlator.valid then
__log.info("try to restore correlator instance")
self.correlator = CCorrEngine(
function(event)
function (event)
self:push_result(event)
end,
true
@@ -36,6 +37,7 @@ function CActsEngine:init(cfg)
assert(self.correlator.valid == true, "failed to initilize correlator")
self.excludes = {}
self.proc_id_fields = {
"object.process.id",
"object.process.parent.id",
@@ -43,6 +45,13 @@ function CActsEngine:init(cfg)
"subject.process.parent.id",
}
self.topic_name = "raw_events"
if __imc.subscribe_to_topic then
assert(__imc.subscribe_to_topic(self.topic_name, __gid), "can't subscribe to topic")
else
__log.debug("topics mechanism unsupported on agent side")
end
-- initialization of object after base class constructing
self:update_config_cb()
end
@@ -62,7 +71,7 @@ function CActsEngine:timer_cb()
-- __log.debug("timer_cb CActsEngine")
local acts_engine = CActsEngine:cast(self)
acts_engine.correlator:pullEvents()
__metric.add_int_gauge_counter("corr_agent_mem_usage", collectgarbage("count")*1024)
__metric.add_int_gauge_counter("corr_agent_mem_usage", collectgarbage("count") * 1024)
return 500
end
@@ -72,6 +81,9 @@ function CActsEngine:quit_cb()
__log.debug("quit_cb CActsEngine")
-- here will be triggered before closing vxproto object and destroying the state
if __imc.unsubscribe_from_topic then
assert(__imc.unsubscribe_from_topic(self.topic_name, __gid), "can't unsubscribe from topic")
end
end
-- in: string
@@ -92,6 +104,10 @@ end
-- out: nil
function CActsEngine:update_config_cb()
__log.debug("update_config_cb CActsEngine")
local acts_engine = CActsEngine:cast(self)
acts_engine:load_excludes()
-- actual current configuration contains into next fields
-- self.config.actions
-- self.config.events
@@ -117,8 +133,10 @@ function CActsEngine:recv_data_cb(src, data)
__metric.add_int_counter("corr_agent_events_recv_count", #odata)
__metric.add_int_counter("corr_agent_events_recv_size", #data)
for _,v in ipairs(odata) do
v.body = json.encode(v.body)
for _, v in ipairs(odata) do
if v.mime == "application/x-pt-eventlog" or v.mime == "application/json" then
v.body = json.encode(v.body)
end
while acts_engine.correlator:sendEvent(json.encode(v)) == false do
__api.await(50)
end
@@ -159,6 +177,108 @@ function CActsEngine:recv_action_cb(src, data, name)
return false
end
-- in: nil
-- out: nil
function CActsEngine:load_excludes()
self.excludes = {}
for _, exclude in ipairs(self.config.module.excludes) do
local compiled = {}
for _, cond in ipairs(exclude) do
if type(cond) ~= "table" then
__log.errorf("condition isn't a table (array) value: '%s'", json.encode(cond))
goto continue
end
if type(cond.fields) ~= "table" then
__log.errorf("fields isn't a table (array) value: '%s'", json.encode(cond.fields))
goto continue
end
if #cond.fields == 0 then
__log.errorf("fields is empty array")
goto continue
end
for _, field in ipairs(cond.fields) do
if type(field) ~= "string" then
__log.errorf("field isn't a string value: '%s'", json.encode(field))
goto continue
end
end
if type(cond.regex) ~= "string" then
__log.errorf("regex isn't a string value: '%s'", json.encode(cond.regex))
goto continue
end
local status, regex = pcall(rex.new, cond.regex)
if not status then
__log.errorf("can't compile regexp: '%s' with error '%s'", cond.regex, regex)
goto continue
end
table.insert(compiled, {
fields = cond.fields,
sregex = cond.regex,
cregex = regex,
})
::continue::
end
if #compiled ~= 0 then
table.insert(self.excludes, compiled)
end
end
__log.infof("loaded %d excludes from '%s'", #self.excludes, json.encode(self.config.module.excludes))
end
-- in: table
-- out: bool, table
function CActsEngine:match_excludes(event)
local body = {}
for fname, fvalue in pairs(event) do
if type(fname) ~= "string" then
fname = tostring(fname) or ""
end
if type(fvalue) ~= "string" then
fvalue = json.encode(fvalue) or ""
end
body[fname] = fvalue
end
for _, exclude in ipairs(self.excludes) do
local matches = {}
for _, cond in ipairs(exclude) do
for _, fname in ipairs(cond.fields) do
local fvalue = body[fname]
if not fvalue then
-- field '{fname}' isn't exist in body
goto next_field
end
local match = cond.cregex:match(fvalue)
if match then
table.insert(matches, {
field = fname,
match = match,
regex = cond.sregex,
value = fvalue,
})
-- field '{fname}' with value '{fvalue}' matches '{match}' by regex '{cond.sregex}'
__log.debugf("field '%s' with value '%s' matches '%s' by regex '%s'", fname, fvalue, match, cond.sregex)
goto next_condition
end
-- field '{fname}' with value '{fvalue}' doesn't match by regex '{cond.sregex}'
__log.debugf("field '%s' with value '%s' doesn't match by regex '%s'", fname, fvalue, cond.sregex)
::next_field::
end
-- fields '{cond.fields}' don't match by regex '{cond.sregex}'
__log.debugf("fields '%s' don't match by regex '%s'", json.encode(cond.fields), cond.sregex)
goto next_exclude
::next_condition::
end
if #matches == #exclude then
__log.debugf("all exclude conditions matched with event body '%s'", json.encode(matches))
return true, matches
end
::next_exclude::
end
return false
end
-- in: table
-- out: nil
function CActsEngine:push_result(event)
@@ -172,11 +292,11 @@ function CActsEngine:push_result(event)
if event_name == nil or event_name == "" then return end
local config_events = self.config["events"] or {events={}}
local config_event = config_events[event_name] or {fields={}}
local config_fields = self.config["fields"] or {properties={}}
local config_events = self.config["events"] or { events = {} }
local config_event = config_events[event_name] or { fields = {} }
local config_fields = self.config["fields"] or { properties = {} }
local _fields = config_event["fields"] or {}
local defaults = {string = "", number = 0, integer = 0, object = {}, array = {}, boolean = false, null = nil}
local defaults = { string = "", number = 0, integer = 0, object = {}, array = {}, boolean = false, null = nil }
for _, v in ipairs(self.proc_id_fields) do
result[v] = tonumber(result[v])
@@ -186,5 +306,7 @@ function CActsEngine:push_result(event)
end
__log.debugf("perform logic to push result: '%s'", json.encode(result))
self:push_event(event_name,result)
if not self:match_excludes(result) then
self:push_event(event_name, result)
end
end
@@ -156,7 +156,7 @@ function CBaseEngine:commit_success(src, action_name, action_data)
-- case to notify other side about action execution result
if type(action_data.retaddr) == "string" and action_data.retaddr ~= "" then
local data = cjson.encode(glue.merge({status = "success"}, action_data))
local data = cjson.encode(glue.merge({ status = "success" }, action_data))
__api.send_data_to(src, data)
end
end
@@ -174,7 +174,7 @@ function CBaseEngine:commit_failed(src, action_name, action_data)
-- case to notify other side about action execution result
if type(action_data.retaddr) == "string" and action_data.retaddr ~= "" then
local data = cjson.encode(glue.merge({status = "error"}, action_data))
local data = cjson.encode(glue.merge({ status = "error" }, action_data))
__api.send_data_to(src, data)
end
end
@@ -200,7 +200,7 @@ end
-- out: string
-- destination token (string) it'll be empty if agent disconnected
function CBaseEngine:get_server_token()
local tablelength = function(t)
local tablelength = function (t)
local count = 0
for _ in pairs(t) do count = count + 1 end
return count
@@ -87,17 +87,17 @@ function CCorrEngine:init(receiveEvents, restore)
local tmpdir_data = luapath.combine(__tmpdir, "data")
zip.unzip(luapath.combine(tmpdir_data, "graphs.zip"), "-d", tmpdir_data)
self.callbacks = {
receive = function(type, data, size)
receive = function (type, data, size)
if type == 1 and receiveEvents then
receiveEvents(ffi.string(data,size))
receiveEvents(ffi.string(data, size))
elseif type == 2 then
self.statistics = json.decode(ffi.string(data,size))
self.statistics = json.decode(ffi.string(data, size))
elseif type == 3 then
__log.errorf("caught error from corr lib: '%s'", ffi.string(data,size))
__log.errorf("caught error from corr lib: '%s'", ffi.string(data, size))
end
return size
end
end,
}
self.statistics = {}
@@ -189,12 +189,12 @@ function CCorrEngine.get_file_hash_md5(filepath)
end
function CCorrEngine.copyFile(src, dst, force)
local f, err = io.open(src, 'rb')
local f, err = io.open(src, "rb")
if not f then return nil, err end
local t, ok
if not force then
t = io.open(dst, 'rb')
t = io.open(dst, "rb")
if t then
f:close()
t:close()
@@ -202,7 +202,7 @@ function CCorrEngine.copyFile(src, dst, force)
end
end
t, err = io.open(dst, 'w+b')
t, err = io.open(dst, "w+b")
if not t then
f:close()
return nil, err
@@ -229,7 +229,7 @@ end
function CCorrEngine:copyDir(dir_from, dir_to, force)
for file in lfs.dir(dir_from) do
if file ~= "." and file ~= ".." then
local f = dir_from .. '/' .. file
local f = dir_from .. "/" .. file
local attr = lfs.attributes(f)
if attr.mode == "directory" then
+6 -8
View File
@@ -2,7 +2,7 @@ require("engines.acts_engine")
-- base config to actions engine
local cfg = {
config = {}
config = {},
}
-- actions engine initialize
@@ -12,24 +12,22 @@ local acts_engine = CActsEngine(cfg)
__api.set_recv_timeout(5000) -- 5s
__api.add_cbs({
data = function(src, data)
data = function (src, data)
__log.debugf("receive data from '%s' with data", src)
assert(acts_engine ~= nil, "actions engine instance is not initialized")
return acts_engine:recv_data(src, data)
end,
file = function(src, path, name)
file = function (src, path, name)
__log.infof("receive file from '%s' with name '%s' path '%s'", src, name, path)
assert(acts_engine ~= nil, "actions engine instance is not initialized")
return acts_engine:recv_file(src, path, name)
end,
-- text = function(src, text, name)
-- msg = function(src, msg, mtype)
action = function(src, data, name)
action = function (src, data, name)
__log.infof("receive action '%s' from '%s' with data %s", name, src, data)
assert(acts_engine ~= nil, "actions engine instance is not initialized")
@@ -37,8 +35,7 @@ __api.add_cbs({
__log.infof("requested action '%s' was executed: %s", name, action_result)
return action_result
end,
control = function(cmtype, data)
control = function (cmtype, data)
__log.debugf("receive control msg '%s' with data %s", cmtype, data)
assert(acts_engine ~= nil, "actions engine instance is not initialized")
@@ -61,6 +58,7 @@ __api.add_cbs({
__log.infof("module '%s' was started", __config.ctx.name)
acts_engine:run()
__api.del_cbs({ "data", "file", "action", "control" })
__log.infof("module '%s' was stopped", __config.ctx.name)
-- explicit destroy engine
+6 -7
View File
@@ -52,16 +52,15 @@ function CModule:init(moduleName)
end
end
self.wrap_load(function()
self.wrap_load(function ()
self.module = ffi.load(moduleName)
end)
self.api = {
create = self.module.module_create,
destroy = self.module.module_destroy,
version = nil,--self.module.Module__Version,
is_inited = false
version = nil, --self.module.Module__Version,
is_inited = false,
}
self.transport = ffi.new("api_module_transport[1]", {})
end
@@ -112,7 +111,7 @@ function CModule:register(profile, callbacks)
self.functions = {}
self.functions["receive"] = function(transport, type, data, size)
self.functions["receive"] = function (transport, type, data, size)
if callbacks and transport == self.transport and callbacks["receive"] then
return callbacks["receive"](type, data, size)
end
@@ -124,7 +123,7 @@ function CModule:register(profile, callbacks)
self.module_i = self.api.create(self.transport, 0, nil)
self.profile = ffi.new("const char[?]", #profile + 1, profile)
self.wrap_load(function()
self.wrap_load(function ()
self.api.is_inited = self.module_i.init(self.transport, self.profile, #profile)
end)
@@ -140,7 +139,7 @@ function CModule:start()
self.module_i.start(self.transport)
end
function CModule:send(type,data)
function CModule:send(type, data)
if self.transport[0].to_module == nil then
return
end
@@ -1,6 +1,95 @@
{
"additionalProperties": false,
"properties": {},
"properties": {
"excludes": {
"items": {
"items": {
"properties": {
"fields": {
"rules": {
"required": true
},
"type": "array",
"ui": {
"columns": 6,
"label": "dx: window.__$ncform.lang === 'en' ? 'Fields' : 'Переменные'",
"widget": "select",
"widgetConfig": {
"allowCreate": true,
"clearable": true,
"defaultFirstOption": false,
"enumSourceRemote": {
"otherParams": {
"filters[]": "{\"field\": \"module_name\", \"value\": [\"correlator\"]}",
"group": "name",
"lang": "en",
"page": 1,
"pageSize": -1,
"type": "init"
},
"remoteUrl": "/api/v1/options/fields",
"resField": "data.fields"
},
"filterLocal": true,
"filterable": true,
"itemDataKey": "item",
"itemLabelField": "name",
"itemValueField": "name",
"multiple": true
}
},
"value": []
},
"regex": {
"rules": {
"customRule": [
{
"errMsg": "regexp format required",
"script": "dx: ((val) =\u003e { try { const v = new RegExp(val); return typeof(v) !== undefined } catch(e) { return false } })(__get({{$root}}, {{$path}}))"
}
],
"required": true
},
"type": "string",
"ui": {
"columns": 6,
"label": "dx: window.__$ncform.lang === 'en' ? 'Regular expression' : 'Регулярное выражение'"
}
}
},
"required": [],
"type": "object",
"ui": {
"label": "dx: ((fields) =\u003e { const or = (window.__$ncform.lang === 'en' ? ' OR ' : ' ИЛИ '); const retIsArray = ((val, def) =\u003e (typeof val === 'object' \u0026\u0026 Array.isArray(val) ? val : def)); fields = retIsArray(fields, []); switch(fields.length) { case 0: return (window.__$ncform.lang === 'en' ? 'empty' : 'пустой список'); case 1: return fields[0]; default: return '(' + [...new Set(fields.map(f =\u003e '...' + f.split('.').slice(-1)[0]))].join(or) + ')'; } })(__get({{$root}}, {{$path}} + '.fields'))"
}
},
"type": "array",
"ui": {
"label": "dx: (() =\u003e { const and = (window.__$ncform.lang === 'en' ? ' AND ' : ' И '); const or = (window.__$ncform.lang === 'en' ? ' OR ' : ' ИЛИ '); const retIsArray = ((val, def) =\u003e (typeof val === 'object' \u0026\u0026 Array.isArray(val) ? val : def)); const getLabel = ((fields) =\u003e { fields = retIsArray(fields, []); switch(fields.length) { case 0: return (window.__$ncform.lang === 'en' ? 'empty' : 'пустой список'); case 1: return JSON.stringify(fields[0]); default: return '(' + [...new Set(fields.map(f =\u003e JSON.stringify('...' + f.split('.').slice(-1)[0])))].join(or) + ')'; } }); return retIsArray(__get({{$root}}, {{$path}}), []).map(e =\u003e getLabel(e.fields)).join(and);})()",
"noLabelSpace": true,
"showIdxLabel": false,
"showLegend": false,
"widget": "array-tabs",
"widgetConfig": {
"showOneIfEmpty": true
}
}
},
"rules": {},
"type": "array",
"ui": {
"noLabelSpace": true,
"showIdxLabel": false,
"showLabel": false,
"showLegend": true,
"widgetConfig": {
"collapsed": true,
"disableReorder": true,
"itemCollapse": true
}
}
}
},
"required": [],
"type": "object"
}
@@ -1 +1,3 @@
{}
{
"excludes": []
}
@@ -1 +1,3 @@
{}
{
"excludes": []
}
+12 -1
View File
@@ -1,7 +1,18 @@
{
"action_config": {},
"actions": {},
"config": {},
"config": {
"excludes": {
"en": {
"description": "Exclusions list",
"title": "Exclusions"
},
"ru": {
"description": "Список исключений",
"title": "Исключения"
}
}
},
"event_config": {
"Malware_Exploit_Elf_CVE_2021_4034_a": {},
"Suspicious_Create_File_Boot_Modification": {},
+2 -4
View File
@@ -1,13 +1,11 @@
__api.add_cbs({
-- data = function(src, data)
-- file = function(src, path, name)
-- text = function(src, text, name)
-- msg = function(src, msg, mtype)
-- action = function(src, data, name)
control = function(cmtype, data)
control = function (cmtype, data)
__log.debugf("receive control msg '%s' with payload: %s", cmtype, data)
-- cmtype: "quit"
@@ -25,4 +23,4 @@ __api.await(-1)
__log.infof("module '%s' was stopped", __config.ctx.name)
return 'success'
return "success"
+12
View File
@@ -0,0 +1,12 @@
version: "3"
services:
tests:
tty: true
entrypoint:
bash -c "cd soldr-modules && ./tests_framework/lua/bin/busted.linux64.cmd tests/."
build:
context: .
dockerfile: tests/Dockerfile
volumes:
- .:/soldr-modules
# command: tail -f /dev/null
+1 -1
View File
@@ -8,7 +8,7 @@ const name = "empty";
module.exports = {
name,
props: ["protoAPI", "hash", "module", "eventsAPI", "modulesAPI", "components", "viewMode"],
props: ["protoAPI", "hash", "module", "api", "components", "viewMode"],
data: () => ({
})
};

Some files were not shown because too many files have changed in this diff Show More