diff --git a/changelog/index.html b/changelog/index.html index d741045..69b8c28 100644 --- a/changelog/index.html +++ b/changelog/index.html @@ -2414,7 +2414,7 @@
The most straight forward and recommended way to use IRIS is with Docker. This is presented here.
+The most straightforward and recommended way to use IRIS is with Docker. This is presented here.
Disclaimer
IRIS is in its early stage. It can already be used in production, but please set backups of the database and DO NOT expose the interface on the Internet. @@ -1393,13 +1419,13 @@
IRIS is not very resourceful and can be run on a small laptop (4 cores, 8Gb of RAM). However, for large organization and heavy usage,
-it will need to be greatly scaled up.
-We don't have benchmarks yet but keep in mind that the database can grow rapidly and modules can be resourceful depending on their purposes.
IRIS does not require a lot of resources, and it can be run on a small laptop (4 cores, 8Gb of RAM). However, for large a organization and heavy usage,
+it will need to be significantly scaled up.
+We don't have benchmarks yet, but keep in mind that the database can grow rapidly and modules can require more resources depending on their purposes.
Docker and docker compose are needed to build and run the project. Depending on the OS you will find all the information to install them on the official website of Docker.
-The platform is officially support on most Linux and MacOS. While it should work on Windows, some path needed by the dockers to store permanent files might need to be changed in the dockerfiles.
+The platform is officially supported on most Linux and MacOS. While it should work on Windows, some path needed by the containers to store permanent files might need to be changed in the dockerfiles.
Starting from version 2.0.0, Iris is following the Semantic Versioning 2.0 guidelines.
The code ready for production is always tagged with a version number.
@@ -1455,13 +1481,17 @@
Please see configuration for more details.
+For enterprises wishing to run their instance of IRIS, utilizing the projects official Helm charts and/or Kustomize manifests, allows them to significantly enhance their deployment and management, presenting a powerful solution to streamline their IRIS deployment and management processes, efficiently running across a cluster of machines, ensuring high availability and seamless scaling as demand fluctuates.
+The deploy directory in the iris-web GitHub repository provides a practical starting point for deploying IRIS on their preferred managed k8s platform. We've created two variants: eks and gke, feel free to customize each with your own values.
+For more details, please visit the deploy directory on GitHub: deploy
Generate the hash of the new password with Python BCrypt in Python prompt
To set up LDAP without having run IRIS priorly, and as the app needs the accounts to be created first before using LDAP, one have to set the IRIS_ADM_EMAIL
environment with the LDAP Email of the administrator user.
To set up LDAP without having run IRIS priorly, and as the app needs the accounts to be created first before using LDAP, one has to set the IRIS_ADM_EMAIL
environment with the LDAP Email of the administrator user.
IRIS_AUTHENTICATION_TYPE=ldap
## IP address or FQDN of the ldap server
@@ -1576,7 +1579,7 @@ LDAP certificates
Last update:
- 2023-06-26
+ 2023-12-30
Head to the Access Control page and click Add group
.
Fill the form. All the fields can be change later on. The field Group name
has to be unique on the IRIS instance.
+
Fill the form. All the fields can be changed later on. The field Group name
has to be unique on the IRIS instance.
Access control and members can be set once the group is created.
The group can be configured once created by clicking on it in the list.
@@ -1474,24 +1474,24 @@
To add users to the group, go to the Members
tab and click Manage
.
The users manager should load and offers a list of users that can be added to the group. Select all the users you want to add to the group and press save.
+The User Manager
should load and offers a list of users that can be added to the group. Select all the users you want to add to the group and press Save
.
Permissions computation
-When a user is added/removed to a group, its effective cases access are recomputed. Depending on the amount of cases and users added/removed this can take some time. +
When a user is added/removed to a group, its effective cases access are recomputed. Depending on the number of cases and users added/removed this can take some time. This process helps reducing the DB load when using IRIS during normal operation.
To remove users from the group, go to the Members
tab and click Manage
.
The users manager should load and present a list of both users already in the group as well as the ones that can be added. To remove one or more users, un-tick them from the list and press Save
.
Alternatively, a user can be directly removed from withing the group manager. Click on the red trash next to the user to remove and confirm the deletion. +
The User Manager
should load and present a list of both users already in the group as well as the ones that can be added. To remove one or more users, un-tick them from the list and press Save
.
Alternatively, a user can be directly removed from within the group manager. Click on the red trash next to the user to remove and confirm the deletion.
Access to one or multiple existing cases can be granted to a group. From within the group manager
, go to the Cases access
tab and click Set case access
.
+
Access to one or multiple existing cases can be granted to a group. From within the Group Manager
, go to the Cases Access
tab and click Set case access
.
The cases access manager
loads and gives the possibility to set the access to one or more cases.
+
The Cases Access Manager
loads and gives the possibility to set the access to one or more cases.
Three choices of access are offered:
Once the desired access is selected, press Set access
.
Permissions computation
-As for the addition of users, when a case is added/removed to a group, all the users effective cases access are recomputed. Depending on the amount of cases added/removed and number of users this can take some time. +
As for the addition of users, when a case is added/removed to a group, all the users' effective cases access are recomputed. Depending on the amount of cases added/removed and number of users this can take some time. This process helps reducing the DB load when using IRIS during normal operation.
From within the group manager
, go to the Cases access
tab. Click on the red trash next to the case to remove and confirm the deletion.
+
From within the Group Manager
, go to the Cases Access
tab. Click on the red trash next to the case to remove and confirm the deletion.
A group can be deleted by clicking on its name in the list and then Delete
at the bottom of the Info
tab.
@@ -1517,7 +1517,7 @@
A user can be in one or multiple groups. The effective case access control of a user is deducted from its groups membership and its own cases access control.
-The effective permissions are deducted from its groups membership.
A user can be in one or multiple groups. The effective case access control of a user is deduced from its groups membership and its own cases access control.
+The effective permissions are deduced from its groups membership.
Cases access control offer three levels:
Advanced
> Server settings
. The password also has to be set when using LDAP. It is however not used for the authentication. A random password can be set when using LDAP. No password is required when the user is set a service account. Advanced
> Server settings
. The password also has to be set when using LDAP, it is then however not used for the authentication. A random password can be set when using LDAP. No password is required when the user is set as a service account.
A user can be edited by clicking on its name or ID in Advanced
> Access Control
> Users
. A window opens and display the user's information. Tabs at the top allows to configure multiple settings related to the user.
A user can be edited by clicking on its name or ID in Advanced
> Access Control
> Users
. A window opens and display the user's information. Tabs at the top allow to configure multiple settings related to the user.
Permissions of a user cannot be set directly. They are inherited from the groups membership. The tab Permissions
only displays the permissions the user has from its groups memberships.
See Groups for more info.
Groups can be set by clicking on the Groups
tab of the user's window and then Manage
.
-
A new window appear with the possibility to select the groups the user should belong to. +
A new window appears with the possibility to select the groups the user should belong to.
After saving, the permissions of the user are updated. This can be verified in the Permissions
tab.
Cases access are usually set through groups membership. However for granularity they can be set per user.
To set the access of a user on a case, click on the Cases access
tab of the user's window and then Set case access
.
-
As for the Groups, a selector appear and allows to select one or multiple cases and the access to associate.
+
As for the Groups, a selector appears and allows to select one or multiple cases and the access to associate.
Info
@@ -1458,7 +1458,7 @@Introduced in IRIS v2.1.0
Alerts can be feeded directly into IRIS using the Alerts API.
+
Alerts can be fed directly into IRIS using the Alerts API.
Any source can inject alerts into IRIS, as long as it can send HTTP requests and respects the alert format.
A service account with the alert_read
and alert_write
permission can be used to send alerts to IRIS.
Each alert can be expanded to show more details.
Alerts can be assigned to analysts. This can be done directly from the alert view.
-To self assign an unassigned alert, click on the hand icon on the left.
+To self-assign an unassigned alert, click on the hand icon on the left.
Clicking again on the hand icon will prompt with a list of analysts to assign the alert to.
-The right button Assign
when hovering an alert can also be used to assign the alert to an analyst.
The right button Assign
, when hovering above an alert, can also be used to assign the alert to an analyst.
Alerts can be escalated/merged into a new case. When hovering an alert, a Merge
button will appear.
Alerts can be escalated/merged into a new case. When hovering above an alert, a Merge
button will appear.
Once clicked, a new window appears, requesting additional information. In this window, the analyst can:
Alerts can also be escalated/merged into an existing case. When hovering an alert, a Merge
button will appear.
Once clicked a new window appears, requesting additional information. The button Merge into existing case
needs to be clicked.
+
Alerts can also be escalated/merged into an existing case. When hovering above an alert, a Merge
button will appear.
Once clicked, a new window appears requesting additional information. The button Merge into existing case
needs to be clicked.
A new dropdown appears and allows to select the case to merge the alert into.
Similar to the case creation, the analyst can:
The selections IOCs and assets are then added to the selected case.
-The selected IOCs and assets are then added to the selected case.
+Alerts can be unmerged from a case.
Info
When unmerging an alert, the alert is not deleted. It is only removed from the case. The alert state is not changed.
-The IOC and assets are not removed from the case.
When a case is merged, a new link appears on the alert and mentions the case it was merged into.
Clicking on this link allows to browse the case or to unmerge the alert.
The first step is to obtain an API key. Each user is automatically attributed one when it is created.
-It can be found on the left panel, under username and My Settings.
My Settings
.
Token exposure
-In case the token is exposed and needs to be change, a new one can be generated with the Renew
option.
+
In case the token is exposed and needs to be changed, a new one can be generated with the Renew
option.
Renewing a token revokes the previous.
This documentation serves as a comprehensive guide to the IRIS web application operations, modules, and development.
If you're new to IRIS, we recommend starting with our Getting Started guide to learn the basics.
For those who want to try out IRIS easily, we offer a free demonstration instance of the rolling beta version here.
Our documentation is constantly evolving, so if you don't find what you're looking for, please contact us so we can add the missing piece.
"},{"location":"#what-is-iris","title":"What is IRIS?","text":"IRIS is a collaborative platform for incident response analysts that helps to share investigations at a technical level. It's a web application that can be installed on a fixed-server or on a laptop for roaming investigations where internet might not be available.
IRIS was born out of the struggle to share long and complex investigations among analysts.
The project is available on our Github organization.
Disclaimer
IRIS is still in its early stages. It can already be used in production, but please set backups of the database and DO NOT expose the interface on the Internet. We highly recommend the use of a private dedicated and secured network.
"},{"location":"changelog/","title":"Changelog","text":"For upgrades instructions, please see the Upgrades page.
"},{"location":"changelog/#v237-december-14-2023","title":"v2.3.7 December 14, 2023","text":""},{"location":"changelog/#whats-changed","title":"What's Changed","text":"Fixed
Improved
Full Changelog
"},{"location":"changelog/#v236-december-7-2023","title":"v2.3.6 December 7, 2023","text":""},{"location":"changelog/#whats-changed_1","title":"What's Changed","text":"Fixed
Full Changelog
"},{"location":"changelog/#v235-november-30-2023","title":"v2.3.5 November 30, 2023","text":""},{"location":"changelog/#whats-changed_2","title":"What's Changed","text":"Fixed
Full Changelog
"},{"location":"changelog/#v234-november-29-2023","title":"v2.3.4 November 29, 2023","text":"A severe bug has been introduced in this version. Please upgarde to v2.3.5.
"},{"location":"changelog/#whats-changed_3","title":"What's Changed","text":"Added
Fixed
\u2757 The layout of the reporting has slightly changed. Custom report templates might not work anymore. You can use https://<server>/case/export?cid=<case_id>
to get all the possible fields.
Full Changelog
"},{"location":"changelog/#v233-october-5-2023","title":"v2.3.3 October 5, 2023","text":""},{"location":"changelog/#whats-changed_4","title":"What's Changed","text":"Fixed
@Scriptception made their first contribution in 313
Full Changelog
"},{"location":"changelog/#v232-august-2-2023","title":"v2.3.2 August 2, 2023","text":""},{"location":"changelog/#whats-changed_5","title":"What's Changed","text":"Fixed
Patches a critical issue corrupting files uploaded to the DataStore via the IOC option or when a password is set
Full Changelog
"},{"location":"changelog/#v231-july-23-2023","title":"v2.3.1 July 23, 2023","text":""},{"location":"changelog/#whats-changed_6","title":"What's Changed","text":"Improved
Fixed
Full Changelog
"},{"location":"changelog/#v230-july-09-2023","title":"v2.3.0 July 09, 2023","text":""},{"location":"changelog/#whats-changed_7","title":"What's Changed","text":"Added
Fixed
Full Changelog
"},{"location":"changelog/#v223-june-19-2023","title":"v2.2.3 June 19, 2023","text":""},{"location":"changelog/#whats-changed_8","title":"What's Changed","text":"Added
Fixed
Full Changelog
"},{"location":"changelog/#v222-may-30-2023","title":"v2.2.2 May 30, 2023","text":""},{"location":"changelog/#whats-changed_9","title":"What's Changed","text":"Added
Fixed - Delete character escaping for passwords by @juadde in #253 - Case template of tasks without tags - Shortening of case names during updates - Bad handling of certain requests without CID - Deletion of assets related to alerts once merge into a case
Full Changelog
"},{"location":"changelog/#v221-may-24-2023","title":"v2.2.1 May 24, 2023","text":""},{"location":"changelog/#whats-changed_10","title":"What's Changed","text":"Note 1: This version contains a security fix for iris-web (see CVE-2023-30615) Note 2: on_postload_case_info_update
hook has been renamed to on_postload_case_update
Added
Fixed
Full Changelog
"},{"location":"changelog/#v220-may-22-2023","title":"v2.2.0 May 22, 2023","text":""},{"location":"changelog/#whats-changed_11","title":"What's Changed","text":"Added
Fixes
New Contributors
Full Changelog
"},{"location":"changelog/#v210-may-15-2023","title":"v2.1.0 May 15, 2023","text":"If you are using custom SSL certificates, please read the upgrade instructions when upgrading from previous versions.
"},{"location":"changelog/#whats-changed_12","title":"What's Changed","text":"Added
Fixed
New Contributors
Full Changelog
"},{"location":"changelog/#v202-april-18-2023","title":"v2.0.2 April 18, 2023","text":""},{"location":"changelog/#whats-changed_13","title":"What's Changed","text":"Fixed * Update case_notes_db.py for bug fix #200 by @LoneWolf-96 in #208 * Do not escape_filter_chars for NTLM username by @juadde in #212 * docker-compose cleanup by @juadde in #213 * Listening port not being propagated in nginx docker
New Contributors * @LoneWolf-96 made their first contribution in #208 * @juadde made their first contribution in #212
Full Changelog
"},{"location":"changelog/#v201-april-05-2023","title":"v2.0.1 April 05, 2023","text":""},{"location":"changelog/#whats-changed_14","title":"What's Changed","text":"Fixed [FIX] Additional table header in case management breaking proper loading of data in #206
Full Changelog
"},{"location":"changelog/#v200-march-26-2023","title":"v2.0.0 March 26, 2023","text":""},{"location":"changelog/#whats-changed_15","title":"What's Changed","text":"In addition to the features listed below, we are changing the way we are issuing releases. From now on, IRIS follows the Semantic Versioning 2.0 guidelines. The code ready for production is always tagged with a version number. alpha
and beta
versions are not production-ready.
Do not use the master
branch in production.
A website with the current development version is also provided and freely accessible. It serves as a beta before public releases.
Added
Improved
Fixed
Full Changelog
"},{"location":"changelog/#v145-june-9-2022","title":"v1.4.5 June 9, 2022","text":"What's Changed
Full Changelog
Notes: Changes are made to the Nginx docker to allow upload of big files, hence specific upgrades are needed and auto-updates is not yet supported for this version. Please see the upgrades instruction for more details.
"},{"location":"changelog/#v144-may-18-2022","title":"v1.4.4 May 18, 2022","text":"What's Changed
Full Changelog
"},{"location":"changelog/#v143-may-13-2022","title":"v1.4.3 May 13, 2022","text":"What's changed
Full Changelog
"},{"location":"changelog/#v142-april-22-2022","title":"v1.4.2 April 22, 2022","text":"Improvements :
Fixes :
A bug fixe exists for this version. See v1.4.3
You can directly contact us, should you need direct support, a demo, further information or anything else related to the project.
contact@dfir-iris.org
Discord
IRIS uses dozen of OS modules and this list is not exhaustive. If you developped something which is used in IRIS and does not figures here, you can contact us.
The most straight forward and recommended way to use IRIS is with Docker. This is presented here.
Disclaimer
IRIS is in its early stage. It can already be used in production, but please set backups of the database and DO NOT expose the interface on the Internet. We highly recommended the use of a private dedicated and secured network.
"},{"location":"getting_started/#pre-requisites","title":"Pre-requisites","text":""},{"location":"getting_started/#hardware","title":"Hardware","text":"IRIS is not very resourceful and can be run on a small laptop (4 cores, 8Gb of RAM). However, for large organization and heavy usage, it will need to be greatly scaled up. We don't have benchmarks yet but keep in mind that the database can grow rapidly and modules can be resourceful depending on their purposes.
"},{"location":"getting_started/#docker","title":"Docker","text":"Docker and docker compose are needed to build and run the project. Depending on the OS you will find all the information to install them on the official website of Docker.
The platform is officially support on most Linux and MacOS. While it should work on Windows, some path needed by the dockers to store permanent files might need to be changed in the dockerfiles.
"},{"location":"getting_started/#versioning","title":"Versioning","text":"Starting from version 2.0.0, Iris is following the Semantic Versioning 2.0 guidelines. The code ready for production is always tagged with a version number. alpha
and beta
versions are not production-ready.
Do not use the master
branch in production.
To build and run IRIS, follow these steps:
Clone the iris-web
repository:
git clone https://github.com/dfir-iris/iris-web.git\ncd iris-web\n
Check out the latest non-beta tagged version:
git checkout v2.3.7\n
Copy the environment file
cp .env.model .env\n
Warning
The default configuration is suitable for testing only. To configure IRIS for production, see the configuration section.
Build the Docker containers:
docker-compose build\n
Start IRIS:
docker-compose up\n
IRIS should now be available on the host interface, port 443, using HTTPS protocol by default. You can access it by navigating to https://hostip in your web browser.
By default, an administrator account is created when IRIS is started for the first time. The password is printed in the console output. You can search for WARNING :: post_init :: create_safe_admin :: >>>
in the logs to find the password. Running docker compose logs app | grep 'admin'
should help to find it.
If you want to define an admin password at the first start, you can create and define the environment variable IRIS_ADM_PASSWORD
in the .env
. This has no effect once the administrator account is created.
Note that IRIS is split into five Docker services, each with a different role:
app
- iris_webapp: The core, including web server, database management, module management, etc.db
: A PostgreSQL databaseRabbitMQ
: A RabbitMQ engine to handle job queuing and processingworker
: A job handler relying on RabbitMQnginx
: A NGINX reverse proxyEach service can be built independently, which is useful when developing. In this QuickStart, all services are built at once.
"},{"location":"getting_started/#additional-configuration","title":"Additional configuration","text":"Please see configuration for more details.
"},{"location":"resources/","title":"Resources","text":"This page is a collection of external resources to help you get started with IRIS and integrate it into your workflow. Please note that some of these resources may be outdated. If you want to add your own resources, please open a pull request or contact us.
"},{"location":"resources/#blog-posts","title":"Blog posts","text":"Note of 25-05-2023
IRIS is growing both in code and interestes. To future-proof the project, we have decided to pause the development of new features and focus on the core functionalities and stability for a while. This includes a major refactoring of the codebase. We will still fix bugs and security issues.
We do not provide a long-term roadmap to prevent any frustration and unmet expectations. It is constantly evolving with feedbacks we receive.
We've thus moved it within a Github project. You can check it here.
"},{"location":"security-advisories/","title":"Security Advisories","text":"This page lists all security advisories that have been published for the code released by DFIR-IRIS.
"},{"location":"security-advisories/#cve-2023-50712-dec-18-2023","title":"CVE-2023-50712 Dec 18, 2023","text":"CVE ID Github ID Severity Impacted product CVE-2023-30615 GHSA-593r-747g-p92p Moderate - CVSS3 4.6/10 iris-web"},{"location":"security-advisories/#description","title":"Description","text":"A stored Cross-Site Scripting (XSS) vulnerability has been identified in iris-web, affecting multiple locations in versions prior to v2.3.7. The vulnerability may allow an attacker to inject malicious scripts into the application, which could then be executed when a user visits the affected locations. This could lead to unauthorized access, data theft, or other related malicious activities.
An attacker need to be authenticated on the application to exploit this vulnerability.
"},{"location":"security-advisories/#affected-versions","title":"Affected versions","text":"iris-web
< 2.3.7
iris-web
>= 2.3.7
No workaround is available.
"},{"location":"security-advisories/#acknowledgment","title":"Acknowledgment","text":"Thanks to Leonard Rapp (G DATA Advanced Analytics GmbH) for the responsible disclosure.
"},{"location":"security-advisories/#cve-2023-30615-may-24-2023","title":"CVE-2023-30615 May 24, 2023","text":"CVE ID Github ID Severity Impacted product CVE-2023-30615 GHSA-gc6j-6276-2m49 Moderate - CVSS3 4.6/10 iris-web"},{"location":"security-advisories/#description_1","title":"Description","text":"A stored Cross-Site Scripting (XSS) vulnerability has been identified in iris-web
, affecting multiple locations in versions prior to v2.2.1
. The vulnerability allows an attacker to inject malicious scripts into the application, which are then executed when a user visits the affected locations. This can lead to unauthorized access, data theft, or other related malicious activities.
An attacker need to be authenticated on the application to exploit this vulnerability.
"},{"location":"security-advisories/#affected-versions_1","title":"Affected versions","text":"iris-web
< 2.2.1
iris-web
> 2.0.0
and < 2.2.1
while not using the alerting feature are not impacted.iris-web
>= 2.2.1
No workaround is available.
"},{"location":"support_us/","title":"Support us","text":"As a free and open source project, we rely on the support of our community to continue development and improve our platform. If you find the platform useful and would like to help us sustain and grow, you may consider supporting us financially through OpenCollective.
Support us
"},{"location":"zqa/","title":"Q & A","text":""},{"location":"zqa/#general-questions","title":"General questions","text":""},{"location":"zqa/#which-version-should-i-install","title":"Which version should I install?","text":"The master branch is stable as all the development is done under the develop branch and merged once ready. To ease the identification, each new version is tagged and a new release is published. We recommend using these. git checkout <tagged_version>
Yes, IRIS is under heavy development. We are adding more and more features, led by feedbacks from the community.
"},{"location":"zqa/#what-is-the-future-of-the-project","title":"What is the future of the project?","text":"We aim to make it evolve as much as possible with the help of the community. We have long term goals to integrate it seamlessly with project like MISP and other OS project, but we don't provide any commitment on how and when to avoid any unmet expectations. For a short term roadmap, you can head to the Github project.
"},{"location":"zqa/#how-can-i-contact-the-dfir-iris-team","title":"How can I contact the DFIR-IRIS team?","text":"You can reach us on discord, Twitter or by email.
"},{"location":"zqa/#cases","title":"Cases","text":""},{"location":"zqa/#can-i-recover-a-deleted-case","title":"Can I recover a deleted case?","text":"No. Cases are deleted from the database and changes are committed. There is no coming back unless you have made backups of the database (which we recommend).
"},{"location":"zqa/#can-i-recover-a-deleted-case-object","title":"Can I recover a deleted case object?","text":"No. Every object such as IOCs, assets, events, notes, etc are immediately deleted from the database and changes are committed.
"},{"location":"zqa/#can-i-add-a-new-asset-type","title":"Can I add a new asset type?","text":"Yes. With a user that have administrative rights, go to Advanced
> Case Objects
.
Yes. Starting from v1.3.0, IOC types can be manipulated. Head to Advanced
> Case Objects
Yes. Starting from v1.4.0, all case objects can be extended thanks to custom attributes. With a user that have administrative rights, go to Advanced
> Custom Attributes
.
Not for now. The searches in each case objects page are done client-side, and the attributes are not fetched. We will however implement a server side search in next releases.
"},{"location":"zqa/#can-i-create-two-cases-with-the-same-name-for-the-same-customer","title":"Can I create two cases with the same name for the same customer?","text":"Yes. Cases are identified with a unique number, so they can have the same name.
"},{"location":"zqa/#can-i-restrict-the-view-of-case-to-a-set-of-users","title":"Can I restrict the view of case to a set of users?","text":"Yes it is since v2.0.0. See Access control.
"},{"location":"zqa/#can-i-change-the-name-or-customer-of-an-existing-case","title":"Can I change the name or customer of an existing case?","text":"Yes it is since v2.0.0.
"},{"location":"zqa/#operations","title":"Operations","text":""},{"location":"zqa/#what-is-the-password-policy-can-it-be-changed","title":"What is the password policy? Can it be changed?","text":"Before v1.4.5, the password policy is hardcoded and cannot be changed. It should be 12 characters minimum and contains a capital letter and a number.
"},{"location":"zqa/#can-i-change-my-profile-picture","title":"Can I change my profile picture?","text":"No, not for now. This wasn't a priority for us, it will be released in future versions.
"},{"location":"zqa/#i-lost-the-administrator-password-can-i-recover-it","title":"I lost the administrator password, can I recover it?","text":"Passwords are hashed so they can't be recovered. But you can change it. Please see changing a lost password.
"},{"location":"zqa/#can-i-delete-a-user","title":"Can I delete a user?","text":"No. To keep consistencies in the database, users unfortunately cannot be deleted if they have done some activities. You can however disable them to prevent them appearing in the UI and connecting to the plafeform.
"},{"location":"zqa/#can-i-delete-a-customer","title":"Can I delete a customer?","text":"No. To keep consistencies in the database, customers unfortunately cannot be deleted if they are linked to cases.
"},{"location":"zqa/#can-i-prevent-backrefs-of-assets-and-iocs","title":"Can I prevent backrefs of assets and IOCs?","text":"No. It might be possible in future versions but for now it is better to spin up a new instance for restricted cases. The backref is however automatically disabled for performance reasons, for cases with more than 300 assets. We are working on a more efficient way to backref.
"},{"location":"zqa/#my-report-template-is-not-generated-and-generates-an-error","title":"My report template is not generated and generates an error","text":"Please triple check typos in tags as there is no fault tolerance. You can reach us in case of troubles.
"},{"location":"zqa/#integration","title":"Integration","text":""},{"location":"zqa/#can-i-enrich-iocs-with-external-sources","title":"Can I enrich IOCs with external sources?","text":"Starting from v1.4.0, it is now possible to easily develop module to enrich case objects. A module Iris VT and IRIS MISP are already provided.
"},{"location":"zqa/#is-there-an-api-client","title":"Is there an API client?","text":"Yes, you can find it on our Github.
"},{"location":"zqa/#security","title":"Security","text":""},{"location":"zqa/#can-i-restrict-cases","title":"Can I restrict cases?","text":"Yes it is since v2.0.0. See Access control.
"},{"location":"zqa/#can-i-expose-iris-on-the-internet","title":"Can I expose IRIS on the Internet?","text":"NO! Please don't. This platform should only be accessible in a restricted environment.
"},{"location":"zqa/#i-found-a-security-issue-can-i-have-a-bounty","title":"I found a security issue, can I have a bounty?","text":"No - IRIS is free and open source so there is no bounty. Please report it as soon as possible so we can fix it.
"},{"location":"zqa/#misc","title":"MISC","text":""},{"location":"zqa/#what-does-iris-stand-for","title":"What does IRIS stand for?","text":"Originally Incident Response Investigation System. But it can be whatever you want really.
"},{"location":"development/","title":"Development","text":"This section is under construction and more elements will be added over time
This documentation is not a detailed how-to develop IRIS. It gives some insights to help understand the basic code of the project and how to contribute.
"},{"location":"development/#general-repositories-conventions","title":"General repositories conventions","text":""},{"location":"development/#branches","title":"Branches","text":"We are using the Gitflow Workflow to manage our git branches. In a nutshell :
master
contains only \"production-ready\" codedevelop
contains the major development code. When ready it is tagged and merged into master
develop
contains either : new_feature
iXX_issue_title
, with XX being the issue number The commits convention is the following :
[action] Commit message
is used, with action
being a 3 letters action related to the commit, eg ADD
for additions, DEL
for deletions, IMP
for improvements, etc.[#issue_id][action] Commit message
The following sections are available in this documentation :
IRIS does not defines a separate API for users, meaning the HTML pages are actually using the API themselves. Routes don't need to handle the authentication and roles. These are handles by wrappers (see snippets below).
"},{"location":"development/code-tips/#page-route","title":"Page route","text":"A page returns an HTML content and should use the following code structure : Example of page route
@blueprint.route('/a/good/route', methods=['GET']) # (1)\n@login_required # (2)\ndef view_a_good_route(caseid, url_redir): # (3)\nif url_redir:\nreturn redirect(url_for('bluprintname.method_name', cid=caseid)) # (4)\n# route code \nreturn render_template(\"a_good_route.html\", variable_1=var_1, ...) # (5)\n
@login_required
is used for users page and @admin_login_required
is used for admin restricted pages. caseid
and url_redir
are variable provided by @login_required
and @admin_login_required
wraps. caseid
indicates which case ID the user tried to access the route with. url_redir
indicates the caseid provided wasn't valid and a redirection is needed. variable_1
is a value that can be accessed from within the template itself. More variables can be added, or not at all. An API route returns a JSON content. Two types are pre-defined and should be used : Standard API returns
response_success(msg=\"A success message\", data=<data associated with the success feedback>)\nresponse_error(msg=\"An error message\", data=<data associated with the error feedback>, status=<status code, by default 400>)\n
Below is an example of standard API route. Example of page route
@blueprint.route('/a/good/api_route', methods=['GET']) # (1)\n@api_login_required # (2)\ndef view_a_good_route(caseid): # (3)\n# API route code \nreturn response_success(\"ok\", data=my_data_object) # (4)\n
@api_login_required
is used for users API endpoints and @api_admin_required
is used for admin restricted endpoints. caseid
is provided @api_login_required
and @api_admin_required
wraps. It indicates which case ID the user tried to access the endpoint with. In case a DB migration is needed, you need to provide an alembic migration script.
Test your migration
Please try out your migration as this is an important piece of the upgrades. Spin up an old version of IRIS, input some data and then try to start your new version. Ensure that everything is migrated as expected.
In a terminal and from within the IRIS virtual env :
source
alembic -c app/alembic.ini revision -m \"A few words to describe your changes\"
This creates a new revision file source > app > alembic > versions
. It's a Python file that basically describes what needs to be updated DB-wise. You can take example from the ones we already have generated in the same folder.
Hint
During your tests you might face the issue that Alembic does not apply your changes after you executed it once. It's because it keeps tracks of the latest applied revision in a table alembic_version
. It doesn't know you changed the revision file. In that case the trick is to connect to the DB, and then delete the entry in the alembic_version. This will force it to reapply all revisions at startup. If you're using the DB docker you can use the following:
docker exec -it <db_container_id> /bin/sh
su postgres
psql
\\c iris_db;
DELETE FROM alembic_version;
It follows an issue raised on the Github of the project.
It recommends the use of a hybrid development environment, as most of the time only the web-app needs to be changed:
For the webapp configuration, a specific .ini
need to be created.
config.priv.ini
in source/app by copying the config.docker.ini
present in the same directory. PG_SERVER = db
to PG_SERVER = 127.0.0.1
or whatever IP is the Postgresql/docker running withThat's the only configuration change needed for the app to run outside docker. The docker.priv.ini
is already excluded in gitignore.
Then Pycharm need to be setup with a dedicated environment, by adding a new configuration:
source/run.py
source
To have pylint, right click on source
in the directory tree and mark directory as
> sources root
. The requirements then need to be installed. Pycharm should detect the requirements.txt and propose to install the dependencies. Otherwise they can be installed with the following command (issued in the virtual environment) : pip3 install -r source\\requirements.txt
docker-compose up db
IRIS can now be developed and debugged on the fly.
"},{"location":"development/environment/#tests-in-docker","title":"Tests in docker","text":"Once the code is working by running on Pycharm, we highly recommend testing it on Docker. To do so, the app docker need to be erased and rebuilt.
docker-compose rm app
docker-compose build app
docker-compose up db app
Development considerations If the development results in DB modification, please use Alembic and add a migration script so users don't loose their data when they upgrade.
"},{"location":"development/hooks/","title":"Hooks","text":"Introduced in IRIS v1.4.0
Hooks are a mean for modules to react on specific events that occurs on IRIS. By subscribing to a hook, a module is automatically notified when the associated event occurs. This offers a multitude of possibilities, from adding insight to IRIS objects, to pushing information to another platform or even changing how IRIS works.
"},{"location":"development/hooks/#types","title":"Types","text":"There are 3 types of hooks.
On preload
: Triggered before an object is processed and committed to database. It is triggered right after a request is received, and the data associated with the hook is usually the request content itself. In most of the cases, modules should not subscribe to these hooks. On postload
: Triggered after an object is processed and committed to database. It is triggered after IRIS processed the request and the data associated with the hook is usually a list of SqlAlchemy objects (such as IOC, Assets, etc). Manual
: Triggered by manual action of a user. When a module subscribes to these hooks, it needs to provide a \"menu option name\" which will be displayed to users. When they click this option, the associated hook is triggered for this module only. Multiple manual hooks can be registered for one module. Danger
on_preload
hooks must run synchronously, i.e not queued in RabbitMQ. This effectively blocks the current user request until the module finishes the processing. We highly recommend to only use on_postload
hooks for a better user experience. These hooks are transparent for users and rely on already verified and committed data. Handling on_preload
hooks implies the received data is unsafe - directly coming from remote clients - and the module needs to process the data as fast as possible.
Two methods are provided by IrisModuleInterface
to subscribe and unsubscribe to hooks.
def register_to_hook(module_id: int, \niris_hook_name: str, \nmanual_hook_name: str = None, \nrun_asynchronously: bool = True)\ndef deregister_from_hook(module_id: int, \niris_hook_name: str)\n
The registration method expects the following arguments:
module_id
: The ID of the calling module. This information is given by IRIS when the register_hooks
method is called. iris_hook_name
: The name of the hook to which subscribe. This must be one of the hook listed in the section below. manual_hook_name
: The name of the UI menu that is provided to users if the registration concerns a manual hook. If nothing is provided, IRIS will create a name composed as follows: <module_name>::<hook_name>
. This value is ignored if the signal is not manual. run_asynchronously
: Set to True (default) to run the module in a RabbitMQ task upon hook triggering. If set to False, the module is called immediately, which have for effect to effectively block the current user request until the module finishes. This is the behavior to use for on_preload
hooks. However, we strongly recommend the use of on_postload
hooks to prevent any unwanted (see warning section above). The deregistration method expects the following arguments:
module_id
: The ID of the calling module. This information is given by IRIS when the register_hooks
methods is called. iris_hook_name
: The name of the hook to which unsubscribe. If the module is not subscribed to the specified hook the function returns without errors. Please see the modules documentation for more details on how to implement these methods.
"},{"location":"development/hooks/#available-hooks","title":"Available hooks","text":"The following hooks are natively available for subscription.
Hook name Description on_preload_case_create Triggered on case creation, before commit in DB on_postload_case_create Triggered on case creation, after commit in DB on_preload_case_delete Triggered on case deletion, before commit in DB on_postload_case_delete Triggered on case deletion, after commit in DB on_postload_case_update Triggered on case update, before commit in DB on_manual_trigger_case Triggered upon user action on_postload_alert_create Triggered on alert creation, after commit in DB on_postload_alert_update Triggered on alert update, after commit in DB on_postload_alert_delete Triggered on alert deletion, after commit in DB on_postload_alert_escalate Triggered on alert escalation, after commit in DB on_postload_alert_merge Triggered on alert merge, after commit in DB on_postload_alert_unmerge Triggered on alert unmerge, after commit in DB on_preload_asset_create Triggered on asset creation, before commit in DB on_postload_asset_create Triggered on asset creation, after commit in DB on_preload_asset_update Triggered on asset update, before commit in DB on_postload_asset_update Triggered on asset update, after commit in DB on_preload_asset_delete Triggered on asset deletion, before commit in DB on_postload_asset_delete Triggered on asset deletion, after commit in DB on_manual_trigger_asset Triggered upon user action on_preload_note_create Triggered on note creation, before commit in DB on_postload_note_create Triggered on note creation, after commit in DB on_preload_note_update Triggered on note update, before commit in DB on_postload_note_update Triggered on note update, after commit in DB on_preload_note_delete Triggered on note deletion, before commit in DB on_postload_note_delete Triggered on note deletion, after commit in DB on_manual_trigger_note Triggered upon user action on_preload_ioc_create Triggered on ioc creation, before commit in DB on_postload_ioc_create Triggered on ioc creation, after commit in DB on_preload_ioc_update Triggered on ioc update, before commit in DB on_postload_ioc_update Triggered on ioc update, after commit in DB on_preload_ioc_delete Triggered on ioc deletion, before commit in DB on_postload_ioc_delete Triggered on ioc deletion, after commit in DB on_manual_trigger_ioc Triggered upon user action on_preload_event_create Triggered on event creation, before commit in DB on_preload_event_duplicate Triggered on event duplication, before commit in DB. This event only received the event ID which will be duplicated on_postload_event_create Triggered on event creation, after commit in DB on_preload_event_update Triggered on event update, before commit in DB on_postload_event_update Triggered on event update, after commit in DB on_preload_event_delete Triggered on event deletion, before commit in DB on_postload_event_delete Triggered on event deletion, after commit in DB on_manual_trigger_event Triggered upon user action on_preload_evidence_create Triggered on evidence creation, before commit in DB on_postload_evidence_create Triggered on evidence creation, after commit in DB on_preload_evidence_update Triggered on evidence update, before commit in DB on_postload_evidence_update Triggered on evidence update, after commit in DB on_preload_evidence_delete Triggered on evidence deletion, before commit in DB on_postload_evidence_delete Triggered on evidence deletion, after commit in DB on_manual_trigger_evidence Triggered upon user action on_preload_task_create Triggered on task creation, before commit in DB on_postload_task_create Triggered on task creation, after commit in DB on_preload_task_update Triggered on task update, before commit in DB on_postload_task_update Triggered on task update, after commit in DB on_preload_task_delete Triggered on task deletion, before commit in DB on_postload_task_delete Triggered on task deletion, after commit in DB on_manual_trigger_task Triggered upon user action on_preload_global_task_create Triggered on global task creation, before commit in DB on_postload_global_task_create Triggered on global task creation, after commit in DB on_preload_global_task_update Triggered on task update, before commit in DB on_postload_global_task_update Triggered on global task update, after commit in DB on_preload_global_task_delete Triggered on task deletion, before commit in DB on_postload_global_task_delete Triggered on global task deletion, after commit in DB on_manual_trigger_global_task Triggered upon user action on_preload_report_create Triggered on report creation, before generation in DB on_postload_report_create Triggered on report creation, before download of the document on_preload_activities_report_create Triggered on activities report creation, before generation in DB on_postload_activities_report_create Triggered on activities report creation, before download of the document on_postload_asset_commented Triggered on asset comment, after commit in DB on_postload_asset_comment_update Triggered on asset comment update, after commit in DB on_postload_asset_comment_delete Triggered on asset comment deletion, after commit in DB on_postload_evidence_commented Triggered on evidence comment, after commit in DB on_postload_evidence_comment_update Triggered on evidence comment update, after commit in DB on_postload_evidence_comment_delete Triggered on evidence comment deletion, after commit in DB on_postload_task_commented Triggered on task comment, after commit in DB on_postload_task_comment_update Triggered on task comment update, after commit in DB on_postload_task_comment_delete Triggered on task comment deletion, after commit in DB on_postload_ioc_commented Triggered on ioc comment, after commit in DB on_postload_ioc_comment_update Triggered on ioc comment update, after commit in DB on_postload_ioc_comment_delete Triggered on ioc comment deletion, after commit in DB on_postload_event_commented Triggered on event comment, after commit in DB on_postload_event_comment_update Triggered on event comment update, after commit in DB on_postload_event_comment_delete Triggered on event comment deletion, after commit in DB on_postload_note_commented Triggered on note comment, after commit in DB on_postload_note_comment_update Triggered on note comment update, after commit in DB on_postload_note_comment_delete Triggered on note comment deletion, after commit in DB on_postload_alert_commented Triggered on alert comment, after commit in DB on_postload_alert_comment_update Triggered on alert comment update, after commit in DB on_postload_alert_comment_delete Triggered on alert comment deletion, after commit in DB"},{"location":"development/structure/","title":"Structure overview","text":""},{"location":"development/structure/#flask","title":"Flask","text":"IRIS uses Flask for the web engine.
"},{"location":"development/structure/#routes-and-blueprints","title":"Routes and blueprints","text":"Each page and API endpoints (eg /login
, /dashboard
, /case/assets/list
, etc) refers to a route in the IRIS Flask app. They define what the application should do when Flask receives a request on an URI. To keep structure in the projects, these routes are grouped by Blueprints
. The Blueprints reflects the structure shown in the IRIS UI left menu. For instance there is a case
and an activities
Blueprint.
The Blueprints and thus routes are defined in source > app > blueprints
. All the blueprints are registered in source > app > views.py
.
IRIS uses dynamic page templating when an URI is visited. These Jinja2 templates are filled at runtime with the needed information and then returned to the client. Each route offering a page (i.e non-API endpoints) thus relies on a template. These are set in a folder named templates
in each Blueprint. For instance, for the dashboard template : source > app > blueprints > dashboard > templates > index.html
.
Static content is served from a common folder under source > app > static > assets
. It contains CSS, JS and images. These can be accessed by pages using \"/static/assets/<the-resource>\"
.
For the database management, the application uses SQLAlchemy with a PostgreSQL backend. There is - normally - no need to directly deal with PostgreSQL, everything goes through SQLAlchemy. It provides a Python overlay which allows to talk to the DB with objects.
"},{"location":"development/structure/#models","title":"Models","text":"Each table of the app is defined by a model. These are defined in source > app > model
. When IRIS starts, it looks for the already created tables and creates the missing ones if needed. If changes are done on a table or field, then a migration is needed. This is explained in Alembic migrations.
To help structuring the code, we are trying to move the DB code from the routes code. This is partially done and work in progress. If your route requests the DB, please put the DB code in source > app > datamgmt
.
To apply schema migration without the need to rebuild the DB, IRIS uses Alembic. It allows to define migration scheme and IRIS calls it when it starts so users can upgrade without too much hassles.
"},{"location":"development/structure/#hooks-modules-and-tasks","title":"Hooks, modules and tasks","text":"Modules are handled via tasks thanks to Celery and RabbitMQ. More info here and here.
"},{"location":"development/structure/#iris-startup","title":"IRIS startup","text":"When starting-up, IRIS initiates a bunch of DB objects, whether it is started for the first time or just restarted. Objects already created are not recreated, but the missing ones are applied. This ensure a smooth migration between versions. These are defined in source > app > post_init.py
. The scripts also contains the code that runs th DB migration with Alembic.
A DFIR-IRIS Module (DIM) is a Python package allowing to extend IRIS features. DIMs are not running constantly and are only called following specific actions done by users.
We distinct two types of modules:
Pipeline modules
: Allow uploading and processing of evidences through modular pipelines (eg: EVTX parsing and injection into a database or data visualiser). These are called when a user queries Update case
and select evidences to process. Processor modules
: Allow processing of IRIS data upon predefined actions / hooks. (eg: be notified when a new IOC is created and get VT/MISP insights for it). These are either called automatically upon specific events, or if a user manually triggers them. Except for some triggers for processor modules, all tasks provided by DIMs are run asynchronously in RabbitMQ tasks, so they don't impact the UI.
Both types of DIMs have the same structure, they only differ in their configurations and how they handle the data they receive. For that purpose, every DIM inherit from a common class named IrisModuleInterface
- available here - which provides the basic structure and methods of a module.
Hint
To quickly start writing a new module, one can follow these tutorials.
"},{"location":"development/modules/#overview","title":"Overview","text":"Modules are instantiated upon actions (hooks, triggers, user actions) and this occurs each time the said actions occur. It implies the initiation of a module has to be very quick. In most of the case, the __init__
method should not even be overwritten.
They can live either in the worker or the web-app, depending on their type and action they are handling. They can also live in both. This implies multiple instances of the same module can run at the same time.
The graph below shows two modules of different types running in the worker and interacting with external elements.
Modules don't have to handle the task creations or resource locks. This is handled by IRIS. They just need to process the data they received and return results in a predefined manner.
"},{"location":"development/modules/#common-structure","title":"Common structure","text":"The section below describes the common structure of modules.
"},{"location":"development/modules/#directory-structure","title":"Directory structure","text":"setup.py # Setup configuration to build the module \nREADME.md # README \niris_example_name # Name of the package \n __init__.py # Declaration of the package and main class\n IrisExampleConfig.py # Configuration of the module to help keep the main file clean \n IrisExampleInterface.py # Main class of the module \n module_helper # Sub module containing the helper functions of the module \n helper.py # for instance access to ext resource, manipulation of data \n helper2.py # etc. \n
"},{"location":"development/modules/#the-initpy-file","title":"The init.py file","text":"Iris loads the modules dynamically. To do so, it needs to know the name of the main class of the module and relies on __init__.py
to find this information.
__iris_module_interface = \"IrisEXAMPLEInterface\"\n
Where IrisEXAMPLEInterface
is the main class of the module and inherits of the base class IrisModuleInterface
.
Caution
Failing to provide the main class in __init__.py
or having the main class inherit from IrisModuleInterface
will make IRIS fail each time it attempts to load the module.
Iris needs to know what the module is doing and what services it is providing. This is done via the attributes of the main class (let's say IrisEXAMPLEInterface
). The attributes are :
_module_name
: string - \"human\" name presented to users. _interface_version
: float - version of IrisModuleInterface
used. If the version is not supported, the server will refuse to register the module. _module_version
: float - version of the module itself to help users keep tracks of evolutions. _module_type
: string - Type of module. The available modules types are listed in IrisModuleInterface.IrisModuleTypes
_pipeline_support
: bool - should be set to True if it implements a pipeline process (aka module of type pipeline_module
)._pipeline_info
: dict - contains the configuration of the pipeline. The following structure must be followed:pipeline_info = {\n# Name of the pipeline used for internal tracking. This \n# must be unique among all modules so pick something really unique \n\"pipeline_internal_name\": \"example_pipeline\",\n# The name of the pipeline presented to the user. Use something \n# that will help the users to identify the right pipeline\n\"pipeline_human_name\": \"Example Pipeline\",\n# Arguments presented to the users when they select the pipeline\n\"pipeline_args\": [\n['some_index', 'required'],\n['example_argument', 'optional']\n]\n}\n
_module_configuration
: A list of dict. The list contains each field needed by the module. This list is shown in the Iris webpage of the module configuration. Each field in an entry is mandatory. _module_configuration = [\n{\n\"param_name\": \"vt_api_key\",\n\"param_human_name\": \"VT API Key\",\n\"param_description\": \"Virus total API key\",\n\"default\": None,\n\"mandatory\": True,\n\"type\": \"sensitive_string\"\n},\n{\n\"param_name\": \"vt_key_is_premium\",\n\"param_human_name\": \"VT Key is premium\",\n\"param_description\": \"Set to True if the VT key is premium\",\n\"default\": False,\n\"mandatory\": True,\n\"type\": \"bool\"\n},\n{\n\"param_name\": \"vt_ip_assign_asn_as_tag\",\n\"param_human_name\": \"Assign ASN tag to IP\",\n\"param_description\": \"Assign a new tag to IOC IPs with the ASN fetched from VT\",\n\"default\": True,\n\"mandatory\": True,\n\"type\": \"bool\"\n}\n]\n
The above example results in the following.
"},{"location":"development/modules/quick_start/processor/","title":"Processor modules","text":"In this tutorial, we demonstrate the steps to write a basic processor module which subscribes to a hook, and log what it receives when the hook is triggered. We will also add a configuration setting to offer our users the ability disable this feature.
We'll call it IrisDummyModule
.
As described in the development module main page, the module should have the following structure.
setup.py # Setup configuration to build the module \nREADME.md # README \niris_dummy_module # Name of the package \n__init__.py # Declaration of the package and main class\nIrisDummyConfig.py # Configuration of the module to help keep the main file clean \nIrisDummyModule.py # Main class of the module \n
While the module could have only one main file IrisDummyModule.py
, we recommend splitting its configuration into a new configuration file (here IrisDummyConfig.py
) to keep the code clear.
There is no mandatory naming convention for the files or the class or the methods. We chose this one to keep things clear, and we recommend following the same. But it's up to you really.
We will walk over these files one by one during this tutorial.
"},{"location":"development/modules/quick_start/processor/#creating-the-interface","title":"Creating the interface","text":"The interface is the code that talks with IRIS. It implements methods that call and are called by the server. It needs to inherit IrisModuleInterface class from the IrisModuleInterface package. This module handles most of the methods needed by IRIS to recognize, set up and call the module. By inheriting this class in our interface, we avoid writing that part ourselves.
Let's write our basic interface class. The name of the file has to be the name of the main class, that's the only constraint. We'll see later on why.
iris_dummy_module/IrisDummyModule.py#!/usr/bin/env python3\n# Import the IrisInterface class\nfrom iris_interface.IrisModuleInterface import IrisModuleInterface\n# Create our module class\nclass IrisDummyModule(IrisModuleInterface):\npass \n
That's it! Actually this class is not doing anything right now. We'll need to add a few methods to register our hook later.
But first we need to indicate to IRIS what is our main interface class. Remember, there is no convention restriction, so IRIS has no way to know which class it should instantiate to call our module.
To do so, we need to set a specific variable in our __init__.py
.
# Set the __iris_module_interface variable to the name of our main class. \n# When IRIS instantiates the python module, it looks for \"module.__iris_module_interface\"\n# And then tries to instantiate the class \"__iris_module_interface.__iris_module_interface\", here 'IrisDummyModule.IrisDummyModule'. \n# That's why the python file must have the same name as the class. \n__iris_module_interface = \"IrisDummyModule\"\n
Our module is now recognizable by IRIS Pretty simple right?
"},{"location":"development/modules/quick_start/processor/#writing-the-configuration","title":"Writing the configuration","text":"The next step is to describe what the module is doing, its name, its configuration, etc. This is done by overwriting predefined variables of the IrisModuleInterface
class.
Let's create our Python configuration file and go through each variables.
iris_dummy_module/IrisDummyConfig.py# Import the module types list, so we can indicate the type of our module \nfrom iris_interface.IrisModuleInterface import IrisModuleTypes \n# Human name displayed in the GUI Manage > Modules. This can be anything, \n# but try to put something meaningful, so users recognize your module. \nmodule_name = \"IrisDummy\"\n# Description displayed when editing the module configuration in the UI. \n# This can be anything, \nmodule_description = \"Provides a dummy module that replies to one hook\"\n# Set the interface version used. This needs to be the version of \n# the IrisModuleInterface package. This version is check by the server to\n# to ensure our module can run on this specific server \ninterface_version = 1.1\n# The version of the module itself, it can be anything \nmodule_version = 1.0\n# The type of the module, here processor \nmodule_type = IrisModuleTypes.module_processor\n# Our module is a processor type, so it doesn't offer any pipeline \npipeline_support = False\n# Provide no pipeline information as our module don't implement any \npipeline_info = {}\n# The configuration of the module that will be displayed and configurable \n# by administrators on the UI. This describes every parameter that can \n# be set. \nmodule_configuration = [\n{\n\"param_name\": \"log_received_hook\",\n\"param_human_name\": \"Log received hook\",\n\"param_description\": \"Logs a message upon hook receiving if set to true. Otherwise do nothing.\",\n\"default\": True,\n\"mandatory\": True,\n\"type\": \"bool\"\n}\n]\n
The module configuration parameters are the following :
param_name
: The internal parameter name. This will be used by the module itself to fetch the value when needed.param_human_name
: The name displayed on the UI for this specific parameterparam_description
: A description explaining what this parameter is doing to help administratorsdefault
: The default value of our parameter. Here we set to True, so after install our module is already configured and ready to log the hook. mandatory
: Indicates whether the parameter is mandatory or not. If set to True and no value is provided (either by admin or by default), the module is automatically disabled by IRIStype
: The type of parameter. Here a boolean, which will be rendered under the form of a checkbox. A module can have as many parameters as it needs.
We now need to update our main class to set this configuration.
iris_dummy_module/IrisDummyModule.py#!/usr/bin/env python3\n# Import the IrisInterface class\nfrom iris_interface.IrisModuleInterface import IrisModuleInterface\n# Create our module class\nclass IrisDummyModule(IrisModuleInterface):\n# Set the configuration\n_module_name = interface_conf.module_name\n_module_description = interface_conf.module_description\n_interface_version = interface_conf.interface_version\n_module_version = interface_conf.module_version\n_pipeline_support = interface_conf.pipeline_support\n_pipeline_info = interface_conf.pipeline_info\n_module_configuration = interface_conf.module_configuration\n_module_type = interface_conf.module_type\npass \n
Done! The module is now providing enough information to IRIS, so it knows exactly what our module do and what needs to be called to run it.
However, our module is still doing nothing. Let's make it subscribe to an IRIS hook.
"},{"location":"development/modules/quick_start/processor/#subscribing-to-a-hook","title":"Subscribing to a hook","text":"Hooks allow to be notified by IRIS when a specific event occurs (IOC creation, deletion, etc). For a comprehensive description of hooks, please see the Hooks section of this documentation.
The registration (or subscription) to a hook occurs at two moments during the life of a module:
These registration/deregistration events are triggered by IRIS, and are propagated to modules through the IrisModuleInterface
method register_hooks
[ref].
To register to a hook, we need to override this method and register our hook within this method. To do so, IrisModuleInterface
offers us another method register_to_hook
[ref], which we can call for each hook we want to subscribe.
Here is a summary of the events:
register_hooks
of our module. This indicates it is time for us to register our hooks. register_to_hook
for each hook we want to subscribeLet's add this to our main class and register to the on_postload_ioc_create
. This will notify use each time a new IOC is created and committed to the database.
#!/usr/bin/env python3\n# Import the IrisInterface class\nfrom iris_interface.IrisModuleInterface import IrisModuleInterface\n# Create our module class\nclass IrisDummyModule(IrisModuleInterface):\n# Set the configuration\n_module_name = interface_conf.module_name\n_module_description = interface_conf.module_description\n_interface_version = interface_conf.interface_version\n_module_version = interface_conf.module_version\n_pipeline_support = interface_conf.pipeline_support\n_pipeline_info = interface_conf.pipeline_info\n_module_configuration = interface_conf.module_configuration\n_module_type = interface_conf.module_type\ndef register_hooks(self, module_id: int):\n\"\"\"\n Called by IRIS indicating it's time to register hooks. \n :param module_id: Module ID provided by IRIS.\n \"\"\"\n# Call the hook registration method. We need to pass the \n# the module_id to this method, otherwise IRIS won't know \n# to whom associate the hook. \n# The hook name needs to be a well known hook name by IRIS. \nstatus = self.register_to_hook(module_id, iris_hook_name='on_postload_ioc_create')\nif status.is_failure():\n# If we have a failure, log something out \nself.log.error(status.get_message())\nelse:\n# Log that we successfully registered to the hook \nself.log.info(f\"Successfully subscribed to on_postload_ioc_create hook\")\n
That's it! Our module has now officially subscribed to a hook and will be notified each time an IOC is created.
So how the module is notified? Once again this is done by a method named hooks_handler
[ref] that IrisModuleInterface
provides, and we need to overwrite.
This method is called each time one of the event associated to the hook we subscribed is triggered. It provides the name of the hook and as well as the data associated to it. By overwriting this method, we can process the hook and the data!
We will add a condition in this method, that is if the administrator sets the module parameter log_received_hook
to False, then the module won't log anything and simply return the data.
Hint
The current configuration of the module can be accessed with the attribute self._dict_conf
.
#!/usr/bin/env python3\n# Import the IrisInterface class\nfrom iris_interface.IrisModuleInterface import IrisModuleInterface\n# Create our module class\nclass IrisDummyModule(IrisModuleInterface):\n# Set the configuration\n_module_name = interface_conf.module_name\n_module_description = interface_conf.module_description\n_interface_version = interface_conf.interface_version\n_module_version = interface_conf.module_version\n_pipeline_support = interface_conf.pipeline_support\n_pipeline_info = interface_conf.pipeline_info\n_module_configuration = interface_conf.module_configuration\n_module_type = interface_conf.module_type\ndef register_hooks(self, module_id: int):\n\"\"\"\n Called by IRIS indicating it's time to register hooks. \n :param module_id: Module ID provided by IRIS.\n \"\"\"\n# Call the hook registration method. We need to pass the \n# the module_id to this method, otherwise IRIS won't know \n# to whom associate the hook. \n# The hook name needs to be a well known hook name by IRIS. \nstatus = self.register_to_hook(module_id, iris_hook_name='on_postload_ioc_create')\nif status.is_failure():\n# If we have a failure, log something out \nself.log.error(status.get_message())\nelse:\n# Log that we successfully registered to the hook \nself.log.info(f\"Successfully subscribed to on_postload_ioc_create hook\")\ndef hooks_handler(self, hook_name: str, data):\n\"\"\"\n Called by IRIS each time one of our hook is triggered. \n \"\"\"\n# read the current configuration and only log the call if \n# our parameter is set to true \nif self._dict_conf.get('log_received_hook') is True:\nself.log.info(f'Received {hook_name}')\nself.log.info(f'Received data of type {type(data)}')\n# Return a standardized message to IRIS saying that everything is ok. \n# logs=list(self.message_queue) is needed, so the users can see the logs \n# our module generated during its execution. \nreturn InterfaceStatus.I2Success(data=data, logs=list(self.message_queue))\n
We are done! Our module is now fully ready to register, subscribe to a hook and act when notified.
"},{"location":"development/modules/quick_start/processor/#installing-and-registering-the-module","title":"Installing and registering the module","text":"We need now need to build and install the module. To do so, you'll need this script and an IRIS docker instance running on the same machine.
The script will build the module, copy it to the docker instances, install it within them and restart the dockers.
chmox +x buildnpush2iris.sh
./buildnpush2iris.sh -a
The module should now be installed. We can register it in IRIS as explained here.
"},{"location":"operations/alerts/","title":"Alerts","text":"This section is under construction.
Introduced in IRIS v2.1.0
Alerts can be feeded directly into IRIS using the Alerts API. Any source can inject alerts into IRIS, as long as it can send HTTP requests and respects the alert format.
A service account with the alert_read
and alert_write
permission can be used to send alerts to IRIS.
Warning
This section is only available for users with the alert_read
and alert_write
permissions.
Alerts can then be viewed in the Alerts
section of IRIS. Analysts can then triage the alerts and create cases from them.
Each alert can be expanded to show more details.
"},{"location":"operations/alerts/#alerts-assignment","title":"Alerts assignment","text":"Alerts can be assigned to analysts. This can be done directly from the alert view.
To self assign an unassigned alert, click on the hand icon on the left.
Clicking again on the hand icon will prompt with a list of analysts to assign the alert to.
The right button Assign
when hovering an alert can also be used to assign the alert to an analyst.
Alerts can be escalated/merged into a new case. When hovering an alert, a Merge
button will appear.
Once clicked, a new window appears, requesting additional information. In this window, the analyst can:
Alerts can also be escalated/merged into an existing case. When hovering an alert, a Merge
button will appear.
Once clicked a new window appears, requesting additional information. The button Merge into existing case
needs to be clicked. A new dropdown appears and allows to select the case to merge the alert into.
Similar to the case creation, the analyst can:
The selections IOCs and assets are then added to the selected case.
"},{"location":"operations/alerts/#umerge-alerts-from-a-case","title":"Umerge alerts from a case","text":"Alerts can be unmerged from a case.
Info
When unmerging an alert, the alert is not deleted. It is only removed from the case. The alert state is not changed. The IOC and assets are not removed from the case.
When a case is merged, a new link appears on the alert and mentions the case it was merged into. Clicking on this link allows to browse the case or to unmerge the alert.
"},{"location":"operations/alerts/#alerts-relationships","title":"Alerts relationships","text":"Each alert have a Relastionships
section. This section shows the relationships between the alert and other objects in IRIS. This feature is in preview and might report false relationships.
The relationships are computed using the following logic:
By default the view limits the relationships to 100 nodes and looks back 7 days. This can be updated directly in the alert view.
"},{"location":"operations/api/","title":"API","text":"IRIS is meant to be plug-able and be integrated with the existing environments.
Through the REST API, one can do almost as much as it is possible to do through the web interface. Under the hood, the web interface is actually talking to the API.
Hint
A Python client is available here to ease the automation.
"},{"location":"operations/api/#api-keys","title":"API Keys","text":"The first step is to obtain an API key. Each user is automatically attributed one when it is created. It can be found on the left panel, under username and My Settings.
Token exposure
In case the token is exposed and needs to be change, a new one can be generated with the Renew
option. Renewing a token revokes the previous.
The API constantly evolves with IRIS and thus multiple versions exists. Use the references below to check which API version applies to your IRIS version. Starting from IRIS v1.4.0, the supported API version can also be checked in the UI Advanced
> Server settings
.
The API token is used as a Bearer and needs to be present in the header Authorization
when issuing requests. For example, to list all the cases:
curl --request GET \\\n--url http://localhost:8000/manage/cases/list?cid=1 \\\n--header 'Authorization: Bearer mWpCUVNzBMU5EnbIAK50jLPhYjKBTHZjobdogc_n_yixpJTmt9tzAf8WYDI7m5XgB9wCJnlaXlHIh9RZjtp2fA' \\\n--header 'Content-Type: application/json'\n
The only way to revoke a token is to renew the current one. Once done, the previous API token does not exist anymore in the database and it becomes ineffective.
"},{"location":"operations/case_templates/","title":"Case templates","text":"Introduced in IRIS v2.1.0
Case templates are a way to pre-configure a case with a set of predefined informations. The case templates can be managed in Advanced
> Case templates
.
Info
This section is only available for users with the administrator role.
Case templates are made of a set of informations that will be used to pre-fill the case creation form. The following elements can be set:
Looking for case templates?
We are providing a set of case templates in the IRIS Resources repository.
"},{"location":"operations/case_templates/#structure-of-templates","title":"Structure of templates","text":"The following defines the structure of a case template:
{\n\"name\": \"ransomware_infection\",\n\"display_name\": \"Ransomware Infection Template\",\n\"description\": \"This case template describes first-response tasks to handle information system compromised by a ransomware.\",\n\"author\": \"DFIR-IRIS\",\n\"classification\": \"malicious-code:ransomware\",\n\"title_prefix\": \"[RANS]\",\n\"summary\": \"# Context \\n\\n\\n# Contact \\n\\n\\n# Actions \\n\\n\\n\",\n\"tags\": [\"ransomware\",\"malware\"],\n\"tasks\": [\n{\n\"title\": \"Identify the perimeter\",\n\"description\": \"The perimeter of compromise must be identified\",\n\"tags\": [\"identify\"]\n},\n{\n\"title\": \"Collect compromised hosts\",\n\"description\": \"Deploy Velociraptor and start collecting evidence\",\n\"tags\": [\"collect\", \"velociraptor\"]\n},\n{\n\"title\": \"Containment\"\n}\n],\n\"note_groups\": [\n{\n\"title\": \"Identify\",\n\"notes\": [\n{\n\"title\": \"Identify the compromised accounts\",\n\"content\": \"# Observations\\n\\n\"\n}\n]\n},\n{\n\"title\": \"Collect\",\n\"notes\": [\n{\n\"title\": \"Velociraptor deployment\"\n},\n{\n\"title\": \"Assets collected\",\n\"content\": \"# Assets collected\\n\\n# Assets not collected\"\n}\n]\n}\n]\n}\n
"},{"location":"operations/case_templates/#using-case-templates","title":"Using case templates","text":"Case templates can be used when creating a new case. On the UI, when creating a case, select the case template to use in the Case template
dropdown. The case will then automatically use the informations defined in the template.
Since v2.0.0 the entire configuration is done through the .env
file at the root of the IRIS directory.
The default configuration is provided through a .env.model
file at the root of the IRIS directory. One need to copy this file to .env
and modify it if needed.
The default configuration is suitable for testing only. See the section below to configure IRIS for production.
"},{"location":"operations/configuration/#production-configuration","title":"Production configuration","text":""},{"location":"operations/configuration/#secrets","title":"Secrets","text":""},{"location":"operations/configuration/#required-changes","title":"Required changes","text":"The following secrets in the .env
need to be changed for production. We recommend using OpenSSL to generate different values from each secret: openssl rand -base64 64
POSTGRES_PASSWORD
: Password of the postgres userPOSTGRES_ADMIN_PASSWORD
: Password of the db admin user IRIS_SECRET_KEY
: Key used by Flask to secure the session cookiesIRIS_SECURITY_PASSWORD_SALT
: A salt used for password encryption in the DB Critical configuration
These settings are critical and need to be set properly otherwise authentication bypass may occur.
"},{"location":"operations/configuration/#optionnal-changes","title":"Optionnal changes","text":"To automate the provisionning of IRIS, one might need to set the default administrator API token and password. This can be achieve with the following environment variables. If those variables are not set, random ones are generated during the very first boot of the application.
Warning
The administrator password is printed in the logs. It is recommended to change it as soon as possible. The set of the following environment variables has no effect once the administrator account is created, i.e after the very first boot.
IRIS_ADM_PASSWORD
: Password of the administrator account. The password need to match the default password policy or the administrator won't be able to login, IRIS_ADM_API_KEY
: API key of the administrator. A random long string. No verification for the complexity is done. We recommend using openssl rand -base64 64
IRIS is configured to use a self-signed certificate by default. This is suitable for testing only. To use your own certificate, you need to set the following environment variables:
KEY_FILENAME
: The filename of the key file in the certificates/web_certificates
directory at the root of the IRIS directoryCERT_FILENAME
: The filename of the certificate file in the certificates/web_certificates
directory at the root of the IRIS directoryOnce the changes are done, nginx docker container need to be rebuilt with the following command:
docker-compose stop nginx\ndocker-compose build nginx --no-cache\ndocker-compose up
"},{"location":"operations/configuration/#authentication","title":"Authentication","text":""},{"location":"operations/configuration/#ldap","title":"LDAP","text":"IRIS can be configured to use LDAP authentication. See the Authentication section for more details.
"},{"location":"operations/configuration/#available-settings","title":"Available settings","text":"These environment variables are availabled to be set.
Key Section Opt DescriptionSERVER_NAME
Nginx No Passed to the server_name in NGINX configuration KEY_FILENAME
Nginx No SSL Cert key filename passed to the NGINX configuration CERT_FILENAME
Nginx No SSL Cert filename passed to the NGINX configuration INTERFACE_HTTPS_PORT
Nginx Yes Listening interface of IRIS POSTGRES_USER
DB No Name of the POSTGRES user POSTGRES_PASSWORD
DB No Password of the POSTGRES user POSTGRES_ADMIN_USER
DB No Name of the admin user POSTGRES_ADMIN_PASSWORD
DB No Password of the ADMIN user POSTGRES_DB
DB No Name of the DB used by IRIS POSTGRES_SERVER
DB No Hostname or IP of the DB POSTGRES_PORT
DB No Port of the DB server DOCKERIZED
IRIS Yes Set to 1
when using dockers (default) IRIS_SECRET_KEY
IRIS No Secret key used to secure sessions - needs to be random IRIS_SECURITY_PASSWORD_SALT
IRIS No Secret used to salt the passwords in DB - needs to be random IRIS_UPSTREAM_SERVER
IRIS No WebApp upstream server - used to configure nginx reverse proxy IRIS_UPSTREAM_PORT
IRIS No WebApp upstream server port - used to configure nginx reverse proxy IRIS_ORGANISATION_NAME
IRIS No Name of the company / organisation. Used on the UI IRIS_LOGIN_BANNER_TEXT
IRIS No Text displayed on the login page IRIS_LOGIN_PTFM_CONTACT
IRIS No Contact information displayed on the login page IRIS_UPLOADED_PATH
IRIS No Path to store uploaded data. IRIS_BACKUP_PATH
IRIS No Path to store backup files. IRIS_TEMPLATES_PATH
IRIS No Path of the templates IRIS_DATASTORE_PATH
IRIS No Path of the datastore files IRIS_DEMO_ENABLED
Demo No Set to True to switch IRIS to Demo mode IRIS_DEMO_DOMAIN
Demo No URL of the demonstration server IRIS_DEMO_USER_SEED
Demo No Random seed to generate demo users IRIS_DEMO_ADM_SEED
Demo No Random seed to generate admin users for demo CELERY_BROKER
Celery No Broker URL used to handle IRIS tasks IRIS_AUTHENTICATION_TYPE
Auth No IRIS auth mode : local
or ldap
IRIS_ADM_PASSWORD
Auth Yes Set to use as initial password of the administrator account. Only works for the very first run of IRIS. Needs to match the password policy IRIS_ADM_API_KEY
Auth Yes Set to use as initial API Key of the administrator IRIS_ADM_EMAIL
Auth Yes Set to use as initial email of the administrator IRIS_ADM_USERNAME
Auth Yes Set to use as initial username of the administrator LDAP_SERVER
Auth Yes LDAP server IP or domain LDAP_PORT
Auth Yes LDAP server port LDAP_USER_PREFIX
Auth Yes Prefix to search the users within LDAP_USER_SUFFIX
Auth Yes Suffix to search the users within LDAP_USE_SSL
Auth Yes Set to True to use LDAPS LDAP_VALIDATE_CERTIFICATE
Auth Yes Set to True to verify the server certificate validity LDAP_TLS_VERSION
Auth Yes TLS version to use LDAPS LDAP_SERVER_CERTIFICATE
Auth Yes Path of the LDAP server certificate LDAP_PRIVATE_KEY
Auth Yes Path of the LDAP private certificate LDAP_PRIVATE_KEY_PASSWORD
Auth Yes Password of the private key LDAP_AUTHENTICATION_TYPE
Auth Yes Simple, SASL or NTLM LDAP_CUSTOM_TLS_CONFIG
Auth Yes If set to true, the TLS configuration is not set by IRIS and taken from the defined environment. Default to False"},{"location":"operations/custom_attributes/","title":"Custom Attributes","text":"Introduced in IRIS v1.4.0
All the case objects can be extended with custom attributes. These attributes can be added by :
VT Report
attribute to each objects it analyses)Attributes offer the ability to :
This section only describes how an administrator can add or delete attributes to an object.
Tip
We have publish a detailed article of custom attributes with advanced usage on our blog.
"},{"location":"operations/custom_attributes/#management-page","title":"Management page","text":"Custom attributes can be changed in the Advanced
> Custom Attributes
section on the left panel.
The page lists the objects for which custom attributes can be added or modified.
Attributes are defined in JSON which describes tabs and fields that makes the attributes.
{\n\"Tab Name 1\": { // Defines a new tab \n\"Field 1\": { // Defines a new field within the Tab Name 1\n\"type\": \"input_string\", // Defines the type of field, here a standard string input\n\"mandatory\": true, // Indicates whether the field is mandatory upon saving\n\"value\": \"\" // Default value if any, else empty\n},\n\"Field 2\": { // Defines a second field within the tab Tab Name 1\n\"type\": \"input_checkbox\", // Defines an input checkbox\n\"mandatory\": false, // Indicates whether the field is mandatory upon saving\n\"value\": false // Default value - must be set for booleans\n}\n},\n\"VT report\": { // Defines a second tab named VT report\n\"Content\": { // Defines a new field Content within the VT Report\n\"type\": \"html\", // Defines an HTML interpreted content\n\"value\": \"\" // Default value if any, else empty\n}\n}\n}\n
The code above would be rendered as :
With :
Tab Name 1
VT report
The available fields type are available for rendering :
input_string
: Standard input textinput_textfield
: Standard input textfieldinput_checkbox
: Standard checkboxinput_date
: Standard date inputinput_datetime
: Standard date and time inputinput_select
: Standard select input. Need \"options\" tag to describe the available options, as a list of string. raw
: A static content rendered in raw text. HTML is not be interpreted.html
: A static content rendered as HTML. This is by nature prone to abuse, but at the same time allows adding custom JS scripts. When an attribute is updated, it will try to update all the existing objects with the new attributes. To prevent any data loss from previous attributes and attributes pushed by modules, the update is only made on attributes which don't have any values set or are type-compatibles (ie string to textfield).
The migration of an attribute can however be forced in two ways, both resulting in potential attributes data loss.
Good to know
Migrating or overwriting attributes never change the native information of an object. It only applies to custom attributes.
Partial overwrite
basically resets all the values of every target objects that matches the current attribute definition. All associated values are lost. This does not impact attributes pushed by modules or previous configuration.
Complete overwrite
resets all attributes of every target objects, including the ones created by modules, and then applies the current attributes. All associated values are lost.
Custom attributes can be more complex than what presented above. With the html
type, it is possible to build almost anything. Below is an example of the custom attributes used in the IrisVT module. The {{ }}
are used withing the module to generates the page with data received from VT.
Note : This example won't work as is, the value field is expanded here for reability.
IrisVT default custom attribute{\n\"VT report\": { \"Content\": { \"type\": \"html\", \"value\": \"<div class='row'>\n <div class='col-12'>\n <h3>Basic information</h3>\n <dl class='row'>\n {% if results.as_owner %}\n <dt class='col-sm-3'>AS owner</dt>\n <dd class='col-sm-9'>{{ results.as_owner }}</dd>\n {% endif %}\n {% if country %}\n <dt class='col-sm-3'>Country</dt>\n <dd class='col-sm-9'>{{ results.country }}</dd>\n {% endif %}\n </dl>\n </div>\n </div> \n {% if nb_detected_urls %}\n <div class='row'>\n <div class='col-12'>\n <h3>Detected URLS</h3>\n <dl class='row'>\n <dt class='col-sm-3'>Total detected URLs</dt>\n <dd class='col-sm-9'>{{ nb_detected_urls }}</dd>\n <dt class='col-sm-3'>Average detection ratio</dt>\n <dd class='col-sm-9'>{{ avg_urls_detect_ratio }}</dd>\n </dl>\n </div>\n </div> \n {% endif %}\n {% if nb_detected_samples %}\n <div class='row'>\n <div class='col-12'>\n <h3>Detected communicating samples</h3>\n <dl class='row'>\n <dt class='col-sm-3'>Total detected samples</dt>\n <dd class='col-sm-9'>{{ nb_detected_samples }}</dd>\n <dt class='col-sm-3'>Average detection ratio</dt>\n <dd class='col-sm-9'>{{ avg_samples_detect_ratio }}</dd>\n </dl>\n </div>\n </div> \n {% endif %}\n <div class='row'>\n <div class='col-12'>\n <div class='accordion'>\n <h3>Additional information</h3>\n {% if results.resolutions %}\n <div class='card'>\n <div class='card-header collapsed' id='drop_res' data-toggle='collapse' data-target='#drop_resolutions' aria-expanded='false' aria-controls='drop_resolutions' role='button'>\n <div class='span-icon'>\n <div class='flaticon-file'></div>\n </div>\n <div class='span-title'>\n Resolutions history\n </div>\n <div class='span-mode'></div>\n </div>\n <div id='drop_resolutions' class='collapse' aria-labelledby='drop_res' style=''>\n <div class='card-body'>\n <ul>\n {% for resolution in results.resolutions %} \n <li>{{ resolution.hostname }} ( Last resolved on {{resolution.last_resolved}} )</li>\n {% endfor %}\n </ul>\n </div>\n </div>\n </div>\n {% endif %}\n </div>\n </div>\n </div>\n <div class='row'>\n <div class='col-12'>\n <div class='accordion'>\n <h3>Raw report</h3>\n <div class='card'>\n <div class='card-header collapsed' id='drop_r' data-toggle='collapse' data-target='#drop_raw' aria-expanded='false' aria-controls='drop_raw' role='button'>\n <div class='span-icon'>\n <div class='flaticon-file'></div>\n </div>\n <div class='span-title'>\n Raw report\n </div>\n <div class='span-mode'></div>\n </div>\n <div id='drop_raw' class='collapse' aria-labelledby='drop_r' style=''>\n <div class='card-body'>\n <div id='vt_raw_ace'>{{ results| tojson(indent=4) }}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div> \n <script>\n var vt_in_raw = ace.edit('vt_raw_ace',\n {\n autoScrollEditorIntoView: true,\n minLines: 30,\n });\n vt_in_raw.setReadOnly(true);\n vt_in_raw.setTheme('ace/theme/tomorrow');\n vt_in_raw.session.setMode('ace/mode/json');\n vt_in_raw.renderer.setShowGutter(true);\n vt_in_raw.setOption('showLineNumbers', true);\n vt_in_raw.setOption('showPrintMargin', false);\n vt_in_raw.setOption('displayIndentGuides', true);\n vt_in_raw.setOption('maxLines', 'Infinity');\n vt_in_raw.session.setUseWrapMode(true);\n vt_in_raw.setOption('indentedSoftWrap', true);\n vt_in_raw.renderer.setScrollMargin(8, 5);\n </script>\" }\n}\n}\n
"},{"location":"operations/datastore/","title":"Datastore","text":"Introduced in IRIS v1.4.5
The datastore offers a way to store files directly in the context of a case. Documents, IOCs, evidences, etc, anything can be uploaded and managed through IRIS.
"},{"location":"operations/datastore/#accessing-the-datastore","title":"Accessing the datastore","text":"The datastore can be accessed from any page with the top-right shortcut.
A new pane will open, with a default folder tree created for the case.
"},{"location":"operations/datastore/#files","title":"Files","text":""},{"location":"operations/datastore/#adding-a-file","title":"Adding a file","text":"To add a file to a folder, press the +
button near a folder where you want to place it, and select Add file
.
A new window appear and allows to upload the file. The following fields are available :
infected
unless specified otherwise in this field,infected
unless specified otherwise in the password field. The file is also added to the IOC tab of the case, A file can be both IOC and Evidence, in which case it is handled as an IOC and also added to the Evidence tab of the case.
Depending on the file size, the upload might take some time. We are aware that the Nginx introduces a delay compared to a direct upload. We have not yet found a configuration that does not impact the speed upload.
Once uploaded, and depending on the options selected, the file appear in the target folder with specific icons. A mouse hover explains what each icon means.
"},{"location":"operations/datastore/#files-operations","title":"Files operations","text":"Once a file is added, a left-click on it shows a dropdown with multiple options.
Batch operations such as moving and deleting are also available by clicking on Select
at the top right, and then selecting the files.
Images can now be directly pasted in notes an summary. Only images are supported. Once an image is pasted, the file is automatically uploaded in the datastore in the folder Notes Upload
and a link to the file is inserted.
Example of pasted image
The image is by default sized to 40%
. Changing the end of the link =SIZE%xSIZE%
allows to resize the image.
The file is now available in the DS and can be replaced if needed. The ID of the file is the one provided in the link, which can help finding out when names are updated.
Note
Under certain conditions (browser, version, OS) the image copy/paste cannot be done directly. This is a known issue, not directly linked to IRIS but related to how browsers handle files in clipboards. If you face this issue, try to open the image and copy it from there instead of the file manager. Otherwise you need to upload it via the Datastore and then get a link from it.
"},{"location":"operations/datastore/#folders","title":"Folders","text":""},{"location":"operations/datastore/#adding-a-folder","title":"Adding a folder","text":"To add folder, press the +
button near a folder where you want to place it, and select Add subfolder
.
A new window appear requesting the name of the folder to create. Validate and the new folder appears in the folder tree. Files can then be added to it.
"},{"location":"operations/datastore/#moving-folders","title":"Moving folders","text":"Folders can be moved within other folders. When doing so, every files and subfolders are also moved, like we are used to with files managers.
Click on the +
near the folder to move, and then select Move
. The target folder is underlined in blue. Select then the target folder which should appear underline in orange. Then validate the move.
The folder and all its children are moved in the target directory.
"},{"location":"operations/datastore/#searching","title":"Searching","text":"When dozens of files are added, the filtering bar can be used to quickly find a file. The filtering mechanism is similar to the one in the timeline.
The query schema is : target_element:search_value AND target_element2:search_value2
. There is no OR condition and searching without target does not work.
The following target elements can be used to filter :
Here a are a few concepts to better understand how the datastore is working.
Folders represented on the datastore are virtual and do not represent the folders on the system. This is to ensure smooth files operations. The files are never touched again (unless overwritten or deleted) once uploaded. When a file or directory is moved or renamed, only its parent references are updated.
Files are saved by GUID instead of their real names on the system. They are saved under the mapped volume /home/iris/server_data/datastore
by default. Then three directories are created :
Evidences
IOCs
Regulars
Within each of these, a new subdirectory with the case ID is created when a file is uploaded. This is ensure IOC, which can be harmful, are formally identified on the server itself. Files can be found on the system by looking up the Storage UUID
of the file (eg: dsf-f86926ec-513d-4e47-88fa-02110e7fb412
) in these directories.
All components of IRIS offers by-default logging in the docker instances. Depending on the OS of the hosts, the location of these logs may differ.
For Debian-based distributions, the logs are usually in /var/lib/docker/containers/
. The usually interesting logs in IRIS are the following:
iriswebapp_app
: Contains the logs of core of IRIS, including major stack traces and access control output iriswebapp_worker
: Contains the logs of the worker and output of modulesiriswebapp_nginx
: Contains the logs of the reverse proxy. Every request to IRIS is logged there. Logs of IRIS can be forwarded to a SIEM for monitoring. Below is discussed how to setup Splunk forwarding. Other drivers are available and detailed on the docker website.
/etc/docker/daemon.json
and specify the following content: {\n\"log-driver\": \"splunk\",\n\"log-opts\": {\n\"splunk-insecureskipverify\": \"true\", \"splunk-index\": \"iris\",\n\"splunk-token\": \"YOUR HEC TOKEN\",\n\"splunk-url\": \"https://SPLUNK_SERVER:8088\"\n}\n}\n
systemctl reload docker
. The logs should appear in the Splunk instance.IRIS has the ability to generate reports based on the data of an investigation. The reports templates can be managed in Advanced
> Templates
.
Info
This section is only available for users with the Admin role.
There is two types of reports :
The following report formats are supported:
Reports templates are made of tags, which are then processed and filed by the template engine of IRIS. The templates can have any forms as soon as they respect the tags. We are providing two example of reports.
Info
The templates includes a few lines that describes how to handle styles. These should not be removed. They are be present in the generated reports and need to be removed manually.
"},{"location":"operations/reports/#available-tags","title":"Available tags","text":"The following tags are available. None are mandatory. If a tag is mistyped, the generation step will produce an error message.
Hint
Standard objects are accessible with {{ objectname }}
. List objects can be looped:
{% for object in object_list %}\n {{ object.attribute }}\n {% endfor %} \n
case.name
: Name of the casecase.description
: Description of the casecase.open_date
: Case open date case.close_date
: Case close date case.opened_by
: User that initially opened the case case.for_customer
: Customer linked to the case case.soc_id
: SOC ID number linked to the case evidences
: List of evidence objects (see below - given evidence
as loop variable)
evidence.filename
: File name of the evidence evidence.date_added
: Date of registration evidence.file_hash
: Hash of the evidence evidence.added_by
: User who added the evidenceevidence.custom_attributes
: Custom attributes of the evidenceiocs
: List of IOCs objects (see below - given ioc
as loop variable)
ioc.ioc_value
: Value of the IOC ioc.ioc_description
: Description of the IOCioc.ioc_type
: Type of IOC ioc.ioc_tags
: Tags linked to the IOC ioc.custom_attributes
: Custom attributes of the IOCnotes
: List of notes objects (see below - given note
as loop variable)
note.note_title
: Title of the note note.note_content
: Content of the note note.note_creationdate
: Creation date of the note note.note_lastupdate
: Date of last update note.custom_attributes
: Custom attributes of the notetasks
: List of tasks objects (see below - given task
as loop variable)
task.task_title
: Title of the task task.task_description
: Description of the task task.task_open_date
: Open date of the task task.task_last_update
: Last update of the task task.task_close_date
: Date of closure task.task_status
: Status of the task task.task_tags
: Task for the tags task.custom_attributes
: Custom attributes of the tasktimeline
: List of events objects (see below - given event
as loop variable)
event.event_title
: Title of the event event.event_content
: Content of the event event.event_raw
: Raw content of the event event.event_date
: Date when the event happened event.event_source
: Source of the event event.category
: Category of the event event.event_tags
: Tags of the events event.last_edited_by
: User who last edited the event event.assets
: List of assets names linked to the eventevent.custom_attributes
: Custom attributes of the eventWe are providing two example of full reports.
The following snippets aimed to be placed directly in the DOCX documents.
"},{"location":"operations/reports/#loops-and-tables","title":"Loops and tables","text":""},{"location":"operations/reports/#standard-loops","title":"Standard loops","text":"A loop needs to be used for list objects. Loop on IOC example
The IOCs of this case are : \n\n{% for ioc in case.iocs %}\n - {{ ioc.ioc_value }}\n - {{ ioc.ioc_description }}\n{% endfor %}\n
"},{"location":"operations/reports/#table-loops","title":"Table loops","text":"To use a loop in a table, a tr
tag needs to be added to the loop and the loop directly integrated in the table. Loop on IOC table example
The IOCs of this case are in the following table : \n\n{%tr for ioc in case.iocs %}\n {{ ioc.ioc_value }}\n {{ ioc.type_name }}\n {{ ioc.ioc_description }}\n{%tr endfor %}\n
Such as : "},{"location":"operations/reports/#nested-loops","title":"Nested loops","text":"Loops can be nested. Don't forget to close each loop.
Nested loop{%for ioc in case.iocs %}\n\n Custom attributes of {{ ioc.ioc_value }} :\n\n {% for attribute in ioc.custom_attributes %}\n\n - {{ attribute }}\n\n {% endfor %}\n\n{% endfor %}\n
"},{"location":"operations/reports/#conditions","title":"Conditions","text":""},{"location":"operations/reports/#standard","title":"Standard","text":"Check if asset is compromised{% for asset in assets %} \n\n {% if asset.compromised %}\n Asset {{ asset.asset_name }} is compromised\n {% endif %}\n\n{% endfor %}\n
"},{"location":"operations/reports/#list-is-not-empty","title":"List is not empty","text":"To check if a list of objects is not empty, use the processor tag count
.
{% if assets|count %} \n The case has assets\n{% endif %}\n
"},{"location":"operations/reports/#markdown-handling","title":"Markdown handling","text":"The case summary and notes are in markdown. A processor tag should thus be added |markdown
. Summary as markdown
This is an example of summary : \n\n{{ case.description|markdown }}\n
Loop over notesThis is an example of recursive notes : \n\n{% for note in case.notes %}\n\n My note named {{ note.note_title }} : \n {{ note.note_content|markdown }}\n\n{% endfor %}\n
"},{"location":"operations/reports/#troubleshoot","title":"Troubleshoot","text":"Most of the time an error of generation is due to misspelled tag or a missing closing tag ({% endfor %}
, {% endif %}
, etc). In case you cannot figure out what is going wrong, don't hesitate to reach us on Discord.
Some basic settings can be set in the section Advanced
> Server settings
.
Behavior :
Prevent post-init step to register default modules again during boot
: By default if a module is deleted and the server is restart, the module will be registered again. Setting this will prevent this behavior. Prevent post-init step to register default case objects again during boot
: By default if case objects are deleted and the server is restart, the case objects will be registered again. Setting this will prevent this behavior. Password policy : the password policy can be changed and is applied for the new users and next changes of users password
The tutorials have been discared as we now provide a free demonstration instance on v200.beta.dfir-iris.org. Should you need more information or assistance to use IRIS, you can contact us here.
"},{"location":"operations/upgrades/","title":"Upgrades","text":"Most of the time, Iris handles upgrades of the database automatically when a new version is started, thus no specific actions are required. However, some breaking changes might need manual intervention. Please use the selectors below to assess if a manual action is required.
Your current version: --Please choose current version-- v1.2.1 v1.3.0 v1.3.1 v1.4.0 v1.4.1 v1.4.2 v1.4.3 v1.4.4 v1.4.5 v2.0.0 v2.0.1 v2.0.2 v2.1.0 v2.2.0 v2.2.1 v2.2.2 v2.2.3 v2.3.0 v2.3.1 v2.3.2 v2.3.3 v2.3.4 v2.3.5 v2.3.6 v2.3.7
Upgrading to: --Please choose target version-- v1.2.1 v1.3.0 v1.3.1 v1.4.0 v1.4.1 v1.4.2 v1.4.3 v1.4.4 v1.4.5 v2.0.0 v2.0.1 v2.0.2 v2.1.0 v2.2.0 v2.2.1 v2.2.2 v2.2.3 v2.3.0 v2.3.1 v2.3.2 v2.3.3 v2.3.4 v2.3.5 v2.3.6 v2.3.7
Check upgrades conditions
For production environments, it is highly recommended to make backups of the DB in case any issues occur during upgrades.
"},{"location":"operations/upgrades/#backing-up-db","title":"Backing-up DB","text":"Only if you run in production and/or data is critical.
docker container list
docker exec <container> pg_dump -U postgres iris_db | \\ \ngzip > ../iris_db_backup.gz\n
zcat ../iris_db_backup.gz | less
"},{"location":"operations/upgrades/#upgrading","title":"Upgrading","text":"Stop the dockers
docker-compose stop\n
Remove the application dockers
docker-compose rm app worker\n
Get the last version of Iris
git checkout <last_tagged_version>\n
eg git checkout v2.3.6
Build the new versions
docker-compose build --no-cache app worker\n
Run IRIS again. The app will handle the DB migration automatically.
docker-compose up\n
In case something went wrong, you can rollback to your previous version and restore data.
docker-compose down db --volumes
docker-compose build --no-cache
docker-compose up db
docker container list
zcat ../iris_db_backup.gz | docker exec -i <container> psql -U postgres -d iris_db
docker-compose up
\u2757 The layout of the reporting has slightly changed. Custom report templates might not work anymore. You can use https://<server>/case/export?cid=<case_id>
to get all the possible fields.
No other impact is to be expected.
"},{"location":"operations/upgrades/#v210","title":"v2.1.0","text":"The default location of the SSL certificates have been changed from dockers/nginx/dev_certs
to certificates/nginx/web_certificates
. The docker-compose.yml
has thus been updated to mount this volume on the nginx Docker.
Except these changes, users in v2.0.x can upgrade to v2.1.0 without any manual intervention. Users in v1.4.x need to follow the v2.0.0 upgrade instructions before upgrading to v2.1.0.
"},{"location":"operations/upgrades/#v200","title":"v2.0.0","text":""},{"location":"operations/upgrades/#breaking-changes","title":"Breaking changes","text":"This version brings breaking changes on the following:
.env
configurationWarning
Custom made modules need to be upgraded to IRIS Module Interface v1.2.0. Please see modules upgrade for v2.0.0
"},{"location":"operations/upgrades/#instance-migration","title":"Instance migration","text":"To migrate an instance from v1.4.5, one can use the script in upgrades/upgrade_to_2.0.0.py
located in the repository. These commands needs to be run from the root of the repository (pwd
should return something like /iris-web
):
# Pull the lastest version \ngit pull # Checkout to iris v2.0.0\ngit checkout v2.0.0 # Check if upgrades possible\npython3 upgrades/upgrade_to_2.0.0.py --check\n\n# Run the upgrade\npython3 upgrades/upgrade_to_2.0.0.py --install\n
The script will take care of migrating the environment variables to reflect the changes in v2.0.0. Please review the .env
file afterward.
The port have been changed 443. The script asks if the previous port should be kept or migrated to the new one.
Once validated, one can proceed with the usual upgrade methodology.
docker-compose stop \ndocker-compose build --no-cache \ndocker-compose up -d\n
"},{"location":"operations/upgrades/#v200-modules-upgrades","title":"v2.0.0 modules upgrades","text":"This only concerns custom modules not shipped with IRIS Web App. The IRIS module interface has been upgraded to v1.2.0. No breaking changes are associated. One need to change the iris_module_interface
dependency to 1.2.0 in the requirements and rebuild the module.
The client has been updated to reflect the latest changes of the API. It also integrates features that were missing previously, such as Datastore Management. Some methods have been deprecated and some other modified. The easiest way to upgrade is to increase the version in the requirements and test. Each deprecated method will produce a warning or raise an exception.
"},{"location":"operations/upgrades/#v145","title":"v1.4.5","text":"If you are coming from IRIS <= v1.3.1 please read this. Changes have been made to the NGINX docker to allow upload of big files for the datastore. It is hence necessary to also rebuild the NGINX docker this time.
docker-compose stop
docker-compose rm app worker
git checkout <last_tagged_version>
- eg git checkout v1.4.5
docker-compose build --no-cache app worker nginx
docker-compose up
This only applies if you are coming from IRIS <= v1.3.1.
This version brings breaking changes in the DB docker by adding a named volume instead of the default one. This implies that previous existing database is ignored as the new docker won't know which volume was previously used. To prevent this, please strictly follow the guide below. This will copy the data of the existing volume, to the new named one.
docker container list
. It should look like iris-web-db-x
name
field with the command below)docker inspect <iris_db> | grep -A5 \"Mounts\"\n# Example of output\n\"Mounts\": [\n{\n\"Type\": \"volume\",\n \"Name\": \"a90b9998a3233a68438c8e099bd0ba98d9f62c9734e40297b8067f9fdb921eb9\",\n \"Source\": \"/var/lib/docker/volumes/a90b9998a3233a68438c8e099bd0ba98d9f62c9734e40297b8067f9fdb921eb9/_data\",\n \"Destination\": \"/var/lib/postgresql/data\",\n
3. Stop all the IRIS dockers : docker-compose stop
4. Create a new empty volume : docker volume create --name iris-web_db_data
5. Run a volume copy via a dummy image : docker run --rm -it -v <previous_db_volume_id>:/from:ro -v iris-web_db_data:/to alpine ash -c \"cd /from ; cp -av . /to\"\n# With the example of 2., this gives \ndocker run --rm -it -v a90b9998a3233a68438c8e099bd0ba98d9f62c9734e40297b8067f9fdb921eb9:/from:ro -v iris-web_db_data:/to alpine ash -c \"cd /from ; cp -av . /to\"\n
6. Pull the last changes from the repository, checkout to v1.4.4
, build and run. git pull origin git checkout v1.4.4\ndocker-compose build docker-compose up
7. The data should be successfully transferred. Do not forget to clear out your browser cache, many JS files were changed.
"},{"location":"operations/upgrades/#v143","title":"v1.4.3","text":"A patch exists for this version. Please directly upgrade to v1.4.4
"},{"location":"operations/upgrades/#v142","title":"v1.4.2","text":"A patch exists for this version. Please directly upgrade to v1.4.4
"},{"location":"operations/upgrades/#v141","title":"v1.4.1","text":"A patch exists for this version. Please directly upgrade to v1.4.4
"},{"location":"operations/upgrades/#v140","title":"v1.4.0","text":"A patch exists for this version. Please directly upgrade to v1.4.4
"},{"location":"operations/access_control/","title":"Access control","text":"Introduced in v2.0.0
IRIS offers a granular access control for cases and management features. Two types of access control are available :
To ease the access control, users can be managed in :
A user can be in one or multiple groups. The effective case access control of a user is deducted from its groups membership and its own cases access control. The effective permissions are deducted from its groups membership.
"},{"location":"operations/access_control/#cases-access-control-overview","title":"Cases access control overview","text":"Cases access control offer three levels:
deny_all
: No access to the case. The user don't even see the case listed, read_only
: Read-only access to the case. The user can see everything related to the case but cannot edit,full_access
: Read-write access to the case. The user can see and edit everything related to the case, including closing and deleting the case. As mentioned above, cases access control can be applied to groups and users. It starts with the groups and ends with atomic user access control.
For example, the following configuration gives no access to the user since the atomic user access prevail upon the rest.
stateDiagram-v2\n DefaultPermission --> Group_prevail: FullAccess\n\n state join_groups <<join>>\n Group1 --> join_groups: ReadOnly\n Group2 --> join_groups: DenyAll\n join_groups --> Group_prevail: ReadOnly\n Group_prevail --> Effective_DenyAll: ReadOnly \n\n Effective_DenyAll: Resulting Deny All access for user\n User_Access --> Effective_DenyAll: DenyAll
In the next configuration, the user has Read Only access to the case because the atomic user access is not set, so the access is inherited from the group ownership.
stateDiagram-v2\n state join_groups <<join>>\n Group1 --> join_groups: ReadOnly\n Group2 --> join_groups: DenyAll\n Group3 --> join_groups: DenyAll\n join_groups --> Group_prevail: ReadOnly\n\n Effective_ReadOnly: Resulting Read Only access for user\n Group_prevail --> Effective_ReadOnly: ReadOnly \n\n User_Access --> Effective_ReadOnly: Unset
This notably allows to create groups which can join a set of people from different organisations to work on the same case.
"},{"location":"operations/access_control/#permissions-control","title":"Permissions control","text":"Permissions allow to control the access to specific management features on the platform (adding users, cases etc.). Two permissions are available:
standard_user
: which includes: creating cases, searching across cases, read activity feed and read DIM tasksserver_administrator
: which includes all the permissions of standard_user
as well as managing users, modules, customers, case objects, custom attributes, report templates and server settings. IRIS supports local and LDAP authentication. In both cases, users need to be declared in IRIS.
"},{"location":"operations/access_control/authentication/#local-authentication","title":"Local authentication","text":"Local authentication is the default setting. The password is validated against the local IRIS database. Passwords are stored salted and hashed, it is thus not possible to retrieve them in case they are lost. It is however possible to change them.
"},{"location":"operations/access_control/authentication/#changing-a-lost-password","title":"Changing a lost password","text":"If another administrative user exists : Being logged as this user, head to the Advanced
> Access Control
> Users
section, and change the administrator password.
If no other administrative user exists : the change cannot be done via IRIS and an access to the backend is needed.
Danger
Do not delete and recreate any users directly from the DB! This will create inconsistencies in the relations and certainly corrupt everything.
Generate the hash of the new password with Python BCrypt in Python prompt
import bcrypt\nprint(bcrypt.hashpw(<new_password>.encode('utf-8'), bcrypt.gensalt())\n
Connect to the DB docker then the Postgresql database iris_db
and update the password
docker exec -ti <db_docker_id> /bin/bash\n/ # su postgres\n/ # psql\npostgres=# \\c iris_db \npostgres=# UPDATE \"user\" SET password = '<hash>' WHERE \"user\".name = 'administrator';\n
LDAP authentication rely on a LDAP server to verify the password of a user. The user needs to be declared in IRIS.
graph LR\n A[User] -->|Authenticate| B(IRIS WebApp)\n B --> C{User exists in DB?}\n C -->|Yes| D{LDAP accepted password?}\n C -->|No| E[Authentication failed]\n D -->|Yes| F[Authentication succeeded]\n D -->|No| E[Authentication failed]
"},{"location":"operations/access_control/authentication/#settings","title":"Settings","text":"The LDAP settings are present in the .env
. Once the LDAP server information is set, reboot the Iris WebApp docker needs to be restarted.
docker-compose restart app\n
"},{"location":"operations/access_control/authentication/#setting-up-ldap-for-the-first-runtime-of-iris","title":"Setting up LDAP for the first runtime of IRIS","text":"To set up LDAP without having run IRIS priorly, and as the app needs the accounts to be created first before using LDAP, one have to set the IRIS_ADM_EMAIL
environment with the LDAP Email of the administrator user.
IRIS_AUTHENTICATION_TYPE=ldap\n\n## IP address or FQDN of the ldap server\nLDAP_SERVER=dc1.domain.local\n\n## Port of the LDAP server\nLDAP_PORT=636\n## LDAP Authentication type\nLDAP_AUTHENTICATION_TYPE=SIMPLE\n\n## Prefix to search the users within \nLDAP_USER_PREFIX=uid=\n## Suffix to search the users within\nLDAP_USER_SUFFIX=ou=people,dc=example,dc=com\n\n## Set to True to use LDAPS\nLDAP_USE_SSL=True\n\n## Set to True to verify the server certificate validity\nLDAP_VALIDATE_CERTIFICATE=True\n\n## TLS version to use LDAPS\nLDAP_TLS_VERSION=1.2\n\n## LDAP TLS configuration \nLDAP_CUSTOM_TLS_CONFIG=False\n\n# Set email address of the first user, that will be the admin \nIRIS_ADM_EMAIL=adm@example.com
"},{"location":"operations/access_control/authentication/#setting-up-for-active-directory","title":"Setting up for Active Directory","text":"To use LDAP with an Active Directory, the following settings can be used:
Example of LDAP configuration for first run with Active DirectoryIRIS_AUTHENTICATION_TYPE=ldap\n\n## IP address or FQDN of the ldap server\nLDAP_SERVER=dc1.domain.local\n\n## Port of the LDAP server\nLDAP_PORT=636\n## LDAP Authentication type\nLDAP_AUTHENTICATION_TYPE=SIMPLE\n\n## Prefix to search the users within\nLDAP_USER_PREFIX=DOMAIN\\\n## Suffix to search the users within\nLDAP_USER_SUFFIX=\n## Set to True to verify the server certificate validity\nLDAP_VALIDATE_CERTIFICATE=True\n\n## TLS version to use LDAPS\nLDAP_TLS_VERSION=1.2\n\n## LDAP TLS configuration \nLDAP_CUSTOM_TLS_CONFIG=False\n\n# Set email address of the first user, that will be the admin\nIRIS_ADM_EMAIL=adm@example.com
"},{"location":"operations/access_control/authentication/#setting-up-ldap-after-iris-already-ran","title":"Setting up LDAP after IRIS already ran","text":"To set up LDAP after IRIS was already run, one only needs to set up the settings described previously without # Set email address of admin IRIS_ADM_EMAIL=adm@example.com
and restart the docker.
Usernames in IRIS have to match the ones set in LDAP for the authentication to succeed.
"},{"location":"operations/access_control/authentication/#ldap-certificates","title":"LDAP certificates","text":"If the LDAP server uses a self-signed certificate, it is possible to add it to the trusted certificates of the IRIS WebApp docker.
certificates/ldap
folder of the IRIS root directory.LDAP_VALIDATE_CERTIFICATE
environment variable to True
in the .env
file.LDAP_CUSTOM_TLS_CONFIG
environment variable to False
in the .env
file.LDAP_CA_CERTIFICATE
environment variable certificate path used by the LDAP server in the .env
file.If the LDAP server requires a client certificate, it is possible to add it to the trusted certificates of the IRIS WebApp docker.
certificates/ldap
folder of the IRIS root directory.LDAP_VALIDATE_CERTIFICATE
environment variable to True
in the .env
file.LDAP_CUSTOM_TLS_CONFIG
environment variable to True
in the .env
file.LDAP_PRIVATE_KEY
environment to the file name of the key in the .env
file LDAP_PRIVATE_KEY_PASSWORD
environment variable to the password of the key in the .env
file - if needed Groups offer the possibility to set case access as well as permissions. By default two groups are created:
Administrator
: users in this group hold the server_administrator
permission, Analysts
: users in this group hold the standard_user
permission Both groups are set to give full cases access to the users.
"},{"location":"operations/access_control/groups/#setting-up-a-new-group","title":"Setting up a new group","text":"Head to the Access Control page and click Add group
.
Fill the form. All the fields can be change later on. The field Group name
has to be unique on the IRIS instance. Access control and members can be set once the group is created.
The group can be configured once created by clicking on it in the list.
"},{"location":"operations/access_control/groups/#users","title":"Users","text":""},{"location":"operations/access_control/groups/#adding-users-to-a-group","title":"Adding users to a group","text":"To add users to the group, go to the Members
tab and click Manage
.
The users manager should load and offers a list of users that can be added to the group. Select all the users you want to add to the group and press save.
Permissions computation
When a user is added/removed to a group, its effective cases access are recomputed. Depending on the amount of cases and users added/removed this can take some time. This process helps reducing the DB load when using IRIS during normal operation.
"},{"location":"operations/access_control/groups/#removing-users-from-a-group","title":"Removing users from a group","text":"To remove users from the group, go to the Members
tab and click Manage
.
The users manager should load and present a list of both users already in the group as well as the ones that can be added. To remove one or more users, un-tick them from the list and press Save
.
Alternatively, a user can be directly removed from withing the group manager. Click on the red trash next to the user to remove and confirm the deletion.
"},{"location":"operations/access_control/groups/#cases","title":"Cases","text":""},{"location":"operations/access_control/groups/#adding-cases-to-the-group","title":"Adding cases to the group","text":"Access to one or multiple existing cases can be granted to a group. From within the group manager
, go to the Cases access
tab and click Set case access
.
The cases access manager
loads and gives the possibility to set the access to one or more cases.
Three choices of access are offered:
deny_all
: No access at all to the case. The users won't even see the case listed, read_only
: Read-only access to the case. The users can see everything related to the case(s) but cannot change anything,full_access
: Read-Write access to the case. The users can see and change everything related to the case. Once the desired access is selected, press Set access
.
Permissions computation
As for the addition of users, when a case is added/removed to a group, all the users effective cases access are recomputed. Depending on the amount of cases added/removed and number of users this can take some time. This process helps reducing the DB load when using IRIS during normal operation.
"},{"location":"operations/access_control/groups/#removing-cases-from-the-group","title":"Removing cases from the group","text":"From within the group manager
, go to the Cases access
tab. Click on the red trash next to the case to remove and confirm the deletion.
A group can be deleted by clicking on its name in the list and then Delete
at the bottom of the Info
tab.
Authentication
Looking for authentication settings? It's here
Whatever the authentication mechanism used (Local or LDAP), the users have to be declared in IRIS. This is done in Advanced
> Access Control
> Users
.
Head to Advanced
> Access Control
> Users
and click Add user
. All fields of the form are required. All information can be changed after the creation.
Note
Permissions and groups can be set once the user is created.
Advanced
> Server settings
. The password also has to be set when using LDAP. It is however not used for the authentication. A random password can be set when using LDAP. No password is required when the user is set a service account. Service accounts
Service accounts users can use the API to perform any actions on the instance. They cannot login to the UI and they don't have a password.
"},{"location":"operations/access_control/users/#editing-a-user","title":"Editing a user","text":"
A user can be edited by clicking on its name or ID in Advanced
> Access Control
> Users
. A window opens and display the user's information. Tabs at the top allows to configure multiple settings related to the user.
Permissions of a user cannot be set directly. They are inherited from the groups membership. The tab Permissions
only displays the permissions the user has from its groups memberships. See Groups for more info.
Groups can be set by clicking on the Groups
tab of the user's window and then Manage
.
A new window appear with the possibility to select the groups the user should belong to.
After saving, the permissions of the user are updated. This can be verified in the Permissions
tab.
Cases access are usually set through groups membership. However for granularity they can be set per user. To set the access of a user on a case, click on the Cases access
tab of the user's window and then Set case access
.
As for the Groups, a selector appear and allows to select one or multiple cases and the access to associate.
Info
Application of a case access is immediate, even if the user is logged in and browsing the case.
Info
Granular case access can also be set from a case itself, in Summary
> Manage
> Access
.
A user can be deactivated, which has the following impact:
"},{"location":"operations/access_control/users/#deleting-a-user","title":"Deleting a user","text":"
It is usually not possible to delete a user. This is to keep consistency in the database. A user can be deleted if it has done absolutely no actions on the platform. If a user leaves the organisation, it is recommended to rename the user and deactivate it.
"},{"location":"operations/cases/case_operations/","title":"Case operations","text":""},{"location":"operations/cases/case_operations/#opening-a-case","title":"Opening a case","text":"
To open a case from the dashboard, press Add case
in the top right corner. Otherwise, go to the Manage Cases
tab and press Add case
in the top right corner.
A new window appears, requesting additional information. The following information are required:
The following information is optional:
Once Create
is clicked, the case is created and a popup ask whether to the get redirected to the case or to add a new one.
Each case has its own context. To switch between cases/context, either click on the name of the current case at the top left, or click on the switch button on the top right.
A popup appears and allows to select the case to switch to. By default the last 100 cases are displayed. To look further in the past, one can use the search bar. Press Save
to validate the switch. The page reloads with the new context.
A case metadata can be updated by going switching the case context and heading to Case
> Summary
. Clicking on Manage
brings up a new window with the case metadata. The right pencil button allows to edit the metadata.
The following information can be updated:
Open
. This defines the state of the case (open, closed, etc.). Unknown
. This defines if the case is a true positive, false positive, etc. Open
The access of a case can be updated by going to Case
> Summary
> Manage
, and from the popup, clicking on the Access
tab.
Changes of access are immediately applied.
"},{"location":"operations/cases/case_operations/#closing-a-case","title":"Closing a case","text":"A case can be closed by going to Case
> Summary
> Manage
, and from the popup, clicking on the Close case
button. Closing a case doesn't delete it and it can be reopened at any time. The case can also be modified after it has been closed.
The modifications history of a case can be retrieved by going to Case
> Summary
> Manage
, and from the popup, clicking on the history icon next to the case name.
IOCs are observables that were identified during the investigation, or that led to the case creation upon monitoring activities.
"},{"location":"operations/cases/iocs/#add-an-ioc","title":"Add an IoC","text":"An IoC object could be created by going to Case
> IOC
. Clicking on Add IOC
in the top right corner brings up a new window for the IoC creation.
A new window appears, requesting additional information. The following information is required:
Amber
by default)The following information is optional:
Once Save
is clicked, the ioc is created.
IoC object data can be updated by clicking on the IoC value in the Case
> IOC
table. A popup appears and allows to change required and non-required fields.
Once Update
is clicked, the IoC is updated.
IoC objects can be enriched in order to add valuable information to it.
"},{"location":"operations/cases/iocs/#comment-an-ioc","title":"Comment an IoC","text":"To comment an IoC, one can right click on it, in the Case
> IOC
menu, and select Comment
. A new pop-up appears and allows to leave comments. This is also achievable by clicking on the IoC value in the Case
> IOC
table, and by clicking on the Comment
button.
To have more information about modules, see the Modules section.
A set of modules can be launched to enrich IoCs. To do so, one can right click on the IoC , in the Case
> IOC
table, and select the module of choice. This is also achievable by clicking on the IoC value in the Case
> IOC
table, by clicking the Option
button, and selecting the desired module.
The results of the module will appear in newly created tabs, in the IoC details. To view the tabs, click on the the IoC value.
"},{"location":"operations/cases/iocs/#delete-an-ioc","title":"Delete an IoC","text":"This will permanently delete the IoC and its attributes
To delete an IoC, one could either right click on the IoC, and select Delete
, or click on the IoC value, and click on the Delete
button.
The IOC is only unlinked from the case if it references other cases
"},{"location":"operations/cases/notes/","title":"Notes","text":"IRIS allows analysts to add notes to cases. Notes can be added to a case from the Notes
tab. Notes are organized by groups. A note has to be within a group.
To add a group, click on the Add note group
button on the top right.
The title of the group note can be edited by clicking direclty on it and typing the new title. Validated with the green checkmark.
"},{"location":"operations/cases/notes/#adding-a-note","title":"Adding a note","text":"To add a note, click on the +
button on the top right of the target note group. A new note is added to the group. To edit it, click on the note and type the new content.
Tasks allow incident handlers to split the workload into unit tasks, and to assign them to the team members.
"},{"location":"operations/cases/tasks/#add-a-task","title":"Add a task","text":"A task can be created by going to Case
> Tasks
. Clicking on Add Task
in the top right corner brings up a new window for the task creation.
A new window appears, requesting additional information. The following information is required:
The following information is optional:
Once Save
is clicked, the task is created.
Task metadata can be updated by clicking on the task title in the Case
> Tasks
table. A popup appears and allows to change required and non-required fields.
Once Update
is clicked, the task is updated.
Tasks can be commented. This provide the ability for analysts to give more in-deepth information on the task execution (what did they do, how did they do, what are the results, etc.)
To comment a task, one can either right click on the task line and select Comment
, or click on the task title, and click on the Comment
button. A new windows appears and allows to add comments to the task.
Once Comment
is clicked, in the last window, a comment is added to the task. Comments are editable and removable.
New types of modules are introduced in IRIS v1.4.0
IRIS can be extended with modules. They can be split in two types:
Modules (or DIM - DFIR-IRIS Modules) are actually Python packages which must be installed in the Python environment of iris-webapp and the worker (see Quick Start). Once installed in the Python environment, modules can be managed in Advanced
> Modules
.
Info
This section is only available for users with the Admin role.
By default IRIS is shipped with multiple modules.
To add a module, the user can click on the \"+\" button:
Then the user must enter the name of the pre-installed module. The name of the pip package must be used.
If everything is ok, the module will appear on the list. It is currently disabled, and needs configuration before it can be enabled. To do so, the user can click on the module's name:
A new text box appears, showing information about the module, and a list of parameters to configure. Each mandatory parameter must be configured to enable the module.
After configuring all the mandatory parameters, the \"Enable button\" is revealed and the user can finally enable the module.
That's all! The user can confirm in the summary that the module is indeed enabled and ready to use.
Finally, the user can either disable or remove the module by clicking on the according buttons.
Now that the module is configured and enabled, let's see how we can use it!
NB: As a temporary fix, after adding and configuring a module, one must restart the IRIS services (dockers) else the worker won't have the module installed properly.
"},{"location":"operations/modules/mod_management/#how-to-use-the-module","title":"How to use the module","text":"As stated in the beginning, a module extends the capabilities of IRIS. For now, it allows importing evidences of your needs into what we call a pipeline, where data will be handled in the module (checking, parsing, ingestion...). In our provided module, IrisEVTXModule ingest EVTX files, parse them as JSON, and send the results to a Splunk instance using its HTTP event collector (HEC) endpoint.
In IRIS, the files are always imported in the context of a case. To import a file, the user can click on Manage cases
then Update
tab.
In Processing pipeline
, the user can pick a pipeline that will send the files to the wanted module. In our example, EVTX pipeline
refers to the IrisEVTXModule module. Below, the user can fill the arguments needed by the according pipeline. Arguments can be optional. Finally, the user can import one or several files and click Update
to start their processing by the module.
You can see in the picture below that the user will import four EVTX files.
The user can follow the upload of the different files with their respective progress bars.
Once uploaded, the status of the task can be observed on the DIM Tasks
page.
Clicking on a Task ID shows information on the task processing.
After the processing of the files by the module, the list of the imported files is stored in the Evidences
tab of the according case.
Introduced in IRIS v1.4.0
This module offers an interface with MISP and IRIS to automatically enrich IOCs with MISP insight.
The source code is available here. It is installed by default but needs to be configured to be enabled.
Note
The module is in its early stage and new features will be added over time.
"},{"location":"operations/modules/natives/IrisMISP/#features","title":"Features","text":"Two types of enrichement mecanism are proposed :
Get MISP insight
. This sends the targets IOCs to the module and insights will be fetched and applied. The following types of IOCs are handled by the module :
The insight request on an IOC not handled is simply ignored.
The insights take the form of attributes added to the IOC. They are based on configurable templates.
"},{"location":"operations/modules/natives/IrisMISP/#configuration","title":"Configuration","text":"The behavior of the module can be configured as needed. Head to the Advanced
> Modules
> IrisMISP
to change it.
At the time, only one MISP can be added. Future version might handled more than one MISP. The expected structure is the following :
{\n\"name\": \"Public_MISP\", \"type\":\"public\", \"url\":[\"https://testmisp\"],\n\"key\":[\"<apikey>\"], \"ssl\":[false]\n}\n
"},{"location":"operations/modules/natives/IrisVT/","title":"Module IRIS VT","text":"Introduced in IRIS v1.4.0
This module offers an interface with VirusTotal and IRIS to automatically enrich IOCs with VT insight. The source code is available here. It is installed by default but needs to be configured to be enabled.
"},{"location":"operations/modules/natives/IrisVT/#features","title":"Features","text":"Two types of enrichment mechanism are proposed :
Get VT insight
. This sends the targets IOCs to the module and insights will be fetched and applied. The following types of IOCs are handled by the module :
The insight request on an IOC not handled is simply ignored.
Two types of insights are proposed :
vt:malicious
or vt:suspicious
tag if the detection thresholds are met (configurable). For domains, an ASN tag can also be added. The behavior of the module can be configured as needed. Head to the Advanced
> Modules
> IrisVT
to change it.
vt:malicious
. To disable, add a value > 100. vt:suspicious
. To disable, add a value > 100. This module offers webhooks support for IRIS. It can be configured to send almost any events to to an external service supporting webhooks, such as Discord, Slack or Microsoft Teams. It can also be used with automation tools such as Tines and Shufle to further automate IRIS. The source code is available here.
"},{"location":"operations/modules/natives/IrisWebHooks/#features","title":"Features","text":"Slack Example
"},{"location":"operations/modules/natives/IrisWebHooks/#configuration","title":"Configuration","text":"The expected configuration is a JSON file, following the structure :
{ \"instance_url\": \"<IRIS_INSTANCE_URL>\",\n\"webhooks\": [\n{\n\"name\": \"Name of the webhook for internal reference only\",\n\"active\": false,\n\"trigger_on\": [<LIST OF HOOKS TO LISTEN TO>],\n\"request_url\": \"<URL OF THE WEBHOOK>\",\n\"use_rendering\": true,\n\"request_rendering\": \"<RENDERING TYPE OF THE MESSAGE>\", \"request_body\": {<BODY OF THE REQUET TO SEND>}\n},\n{\n\"name\": \"Another hook\",\n\"active\": false,\n\"use_rendering\": false,\n\"trigger_on\": [<LIST OF HOOKS TO LISTEN TO>],\n\"request_url\": \"<URL OF THE WEBHOOK 2>\",\n\"request_rendering\": \"<RENDERING TYPE OF THE MESSAGE>\", \"request_body\": {<BODY OF THE REQUEST TO SEND 2>}\n}\n]\n}\n
instance_url
: Base URL of IRIS. This is used to set the links in the messageswebhooks
: A list of JSON describing the webhooks For each webhook:name
: Internal name of the webhook, this can be anything active
: Optional - Set to false to disable the webhook trigger_on
: List of IRIS hooks for which the webhook should be triggered. Only the on_postload_XX
hooks are supported. To enable a set of hooks without writing them all, the following keywords can be used : all
: Includes all on_postload
hooks all_create
: Includes all on_postload_XX_create
hooksall_update
: Includes all on_postload_XX_update
hooksrequest_url
: The URL provided by the webhook receiver. For instance for Slack : see how to get onerequest_rendering
: URLs rendering may be specific from one receiver to another. The modules supports the following : markdown
: Format the message as markdown. This can be used with Discord for instance markdown_slack
: Format the message as markdown, with some specificities of Slack. html
: Format the message as HTML. request_body
: The request body to be sent to the webhook receiver. If use_rendering
is true, then two markups can be used to set the content of the webhook. The request has to be in JSON format and is sent as-is after replacements of the markups. %TITLE%
: Is replaced with name of the case and event title, e.g \"[#54 - Ransomware] IOC created\"%DESCRIPTION%
: Description of the event, e.g \"UserX created IOC mimi.exe in case #54\" If use_rendering
is false, then a raw json representation of the object related to the hook is available. See examples for more details. manual_trigger_name
: The name of the manual trigger in the UI. This should be set if the registered hook is of type on_manual_trigger
. This name is displayed as a new menu option in the UI for the target object. use_rendering
: Whether the data should be formated in Markdown or not. If set to false, then the request body field can use the raw data such as assets
. This will result in a request with the body containing the assets JSON representation related to the call of the hook. See examples for more details. Each time a webhook is added, the module subscribes to the specified hooks. After saving the configuration, one can check the registration was successful by filtering the Registered hooks table
(don't forget to refresh the table).
The following example is a combination of webhooks that can be used to further automate IRIS. It uses Tines as an example, but this is reproductible with any automation tool that can sent HTTP requests. A Tines story is created and is set up to receive a webhook, such as https://anothertest.tines.io/webhook/xxxx/xxxxx
. In this scenario, two IRIS webhooks are added:
on_manual_trigger_ioc_update
hook. on_postload_case_create
hook. We use the same Tines story and thus Tines webhook for both and dispatch the incoming request depending on its parameters.
"},{"location":"operations/modules/natives/IrisWebHooks/#todo","title":"TODO","text":""},{"location":"operations/modules/natives/IrisWebHooks/#examples-using-rendering","title":"Examples using rendering","text":"The following is an example of combined webhooks configuration. It can be directly imported in the module with the import feature. Please note that after import, the configuration should be opened and change to match your URL webhook receiver.
Download webhooks combined configuration example
"},{"location":"operations/modules/natives/IrisWebHooks/#discord","title":"Discord","text":"Discord webhook example - selection of events{ \"instance_url\": \"https://iris.local\",\n\"webhooks\": [\n{\n\"name\": \"Discord\",\n\"trigger_on\": [\n\"on_postload_ioc_create\",\n\"on_postload_ioc_update\",\n\"on_postload_note_create\",\n\"on_postload_note_update\"\n],\n\"request_url\": \"https://discord.com/api/webhooks/XXXX/XXXX\",\n\"request_rendering\": \"markdown\", \"request_body\": {\n\"embeds\": [{\n\"description\" : \"%DESCRIPTION%\",\n\"title\" : \"%TITLE%\"\n}]\n}\n}\n]\n}\n
"},{"location":"operations/modules/natives/IrisWebHooks/#slack","title":"Slack","text":"Slack webhook example - all events{ \"instance_url\": \"https://iris.local\",\n\"webhooks\": [\n{\n\"name\": \"Slack\",\n\"trigger_on\": [\n\"all\"\n],\n\"request_url\": \"https://hooks.slack.com/services/<XXX>/<XXX>/<XXX>\",\n\"request_rendering\": \"markdown_slack\",\n\"request_body\": {\n\"text\": \"%TITLE%\",\n\"blocks\": [\n{\n\"type\": \"section\",\n\"text\": {\n\"type\": \"mrkdwn\",\n\"text\": \"*%TITLE%*\"\n}\n},\n{\n\"type\": \"section\",\n\"block_id\": \"section567\",\n\"text\": {\n\"type\": \"mrkdwn\",\n\"text\": \"%DESCRIPTION%\"\n}\n}\n]\n}\n}\n]\n}\n
"},{"location":"operations/modules/natives/IrisWebHooks/#troubleshooting","title":"Troubleshooting","text":"Webhooks receivers are expecting specific message formatting to successfully process them. Please carefully read their documentations.
The module only handles JSON POST for the moment. If the target webhook receiver needs another type of request, please contact us so we can add it.
As any IRIS module, IrisWebhooks is logged into DIM Tasks. You can check the status of the requests made in these. Go to DIM Tasks
and then filter with webhooks
. You can then check details info by clicking in the Task ID. More info might be available in the Docker worker logs depending on the situation.
dfir_iris_client
offers a Python interface to communicate with IRIS.
It relies exclusively on the API, which means output of the methods are the same as specified in the API reference.
"},{"location":"python_client/#installation","title":"Installation","text":"It can be easily with pip : pip3 install dfir_iris_client
.
The source code of the project is available here.
"},{"location":"python_client/#versions","title":"Versions","text":"The Python client version follows the API versions (until the patch level). Meaning for API v1.0.1, one need to install dfir_iris_client-1.0.1
.
Examples of usage are available here.
"},{"location":"python_client/#documentation","title":"Documentation","text":"The documentation of the client is available on client.docs.dfir-iris.org.
"}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":"This documentation serves as a comprehensive guide to the IRIS web application operations, modules, and development.
If you're new to IRIS, we recommend starting with our Getting Started guide to learn the basics.
For those who want to try out IRIS easily, we offer a free demonstration instance of the rolling beta version here.
Our documentation is constantly evolving, so if you don't find what you're looking for, please contact us so we can add the missing piece.
"},{"location":"#what-is-iris","title":"What is IRIS?","text":"IRIS is a collaborative platform for incident response analysts that helps to share investigations at a technical level. It's a web application that can be installed on a fixed-server or on a laptop for roaming investigations where internet might not be available.
IRIS was born out of the struggle to share long and complex investigations among analysts.
The project is available on our Github organization.
Disclaimer
IRIS is still in its early stages. It can already be used in production, but please set backups of the database and DO NOT expose the interface on the Internet. We highly recommend the use of a private dedicated and secured network.
"},{"location":"changelog/","title":"Changelog","text":"For upgrades instructions, please see the Upgrades page.
"},{"location":"changelog/#v237-december-14-2023","title":"v2.3.7 December 14, 2023","text":""},{"location":"changelog/#whats-changed","title":"What's Changed","text":"Fixed
Improved
Full Changelog
"},{"location":"changelog/#v236-december-7-2023","title":"v2.3.6 December 7, 2023","text":""},{"location":"changelog/#whats-changed_1","title":"What's Changed","text":"Fixed
Full Changelog
"},{"location":"changelog/#v235-november-30-2023","title":"v2.3.5 November 30, 2023","text":""},{"location":"changelog/#whats-changed_2","title":"What's Changed","text":"Fixed
Full Changelog
"},{"location":"changelog/#v234-november-29-2023","title":"v2.3.4 November 29, 2023","text":"A severe bug has been introduced in this version. Please upgarde to v2.3.5.
"},{"location":"changelog/#whats-changed_3","title":"What's Changed","text":"Added
Fixed
\u2757 The layout of the reporting has slightly changed. Custom report templates might not work anymore. You can use https://<server>/case/export?cid=<case_id>
to get all the possible fields.
Full Changelog
"},{"location":"changelog/#v233-october-5-2023","title":"v2.3.3 October 5, 2023","text":""},{"location":"changelog/#whats-changed_4","title":"What's Changed","text":"Fixed
@Scriptception made their first contribution in 313
Full Changelog
"},{"location":"changelog/#v232-august-2-2023","title":"v2.3.2 August 2, 2023","text":""},{"location":"changelog/#whats-changed_5","title":"What's Changed","text":"Fixed
Patches a critical issue corrupting files uploaded to the DataStore via the IOC option or when a password is set
Full Changelog
"},{"location":"changelog/#v231-july-23-2023","title":"v2.3.1 July 23, 2023","text":""},{"location":"changelog/#whats-changed_6","title":"What's Changed","text":"Improved
Fixed
Full Changelog
"},{"location":"changelog/#v230-july-09-2023","title":"v2.3.0 July 09, 2023","text":""},{"location":"changelog/#whats-changed_7","title":"What's Changed","text":"Added
Fixed
Full Changelog
"},{"location":"changelog/#v223-june-19-2023","title":"v2.2.3 June 19, 2023","text":""},{"location":"changelog/#whats-changed_8","title":"What's Changed","text":"Added
Fixed
Full Changelog
"},{"location":"changelog/#v222-may-30-2023","title":"v2.2.2 May 30, 2023","text":""},{"location":"changelog/#whats-changed_9","title":"What's Changed","text":"Added
Fixed - Delete character escaping for passwords by @juadde in #253 - Case template of tasks without tags - Shortening of case names during updates - Bad handling of certain requests without CID - Deletion of assets related to alerts once merge into a case
Full Changelog
"},{"location":"changelog/#v221-may-24-2023","title":"v2.2.1 May 24, 2023","text":""},{"location":"changelog/#whats-changed_10","title":"What's Changed","text":"Note 1: This version contains a security fix for iris-web (see CVE-2023-30615) Note 2: on_postload_case_info_update
hook has been renamed to on_postload_case_update
Added
Fixed
Full Changelog
"},{"location":"changelog/#v220-may-22-2023","title":"v2.2.0 May 22, 2023","text":""},{"location":"changelog/#whats-changed_11","title":"What's Changed","text":"Added
Fixes
New Contributors
Full Changelog
"},{"location":"changelog/#v210-may-15-2023","title":"v2.1.0 May 15, 2023","text":"If you are using custom SSL certificates, please read the upgrade instructions when upgrading from previous versions.
"},{"location":"changelog/#whats-changed_12","title":"What's Changed","text":"Added
Fixed
New Contributors
Full Changelog
"},{"location":"changelog/#v202-april-18-2023","title":"v2.0.2 April 18, 2023","text":""},{"location":"changelog/#whats-changed_13","title":"What's Changed","text":"Fixed * Update case_notes_db.py for bug fix #200 by @LoneWolf-96 in #208 * Do not escape_filter_chars for NTLM username by @juadde in #212 * docker-compose cleanup by @juadde in #213 * Listening port not being propagated in nginx docker
New Contributors * @LoneWolf-96 made their first contribution in #208 * @juadde made their first contribution in #212
Full Changelog
"},{"location":"changelog/#v201-april-05-2023","title":"v2.0.1 April 05, 2023","text":""},{"location":"changelog/#whats-changed_14","title":"What's Changed","text":"Fixed [FIX] Additional table header in case management breaking proper loading of data in #206
Full Changelog
"},{"location":"changelog/#v200-march-26-2023","title":"v2.0.0 March 26, 2023","text":""},{"location":"changelog/#whats-changed_15","title":"What's Changed","text":"In addition to the features listed below, we are changing the way we are issuing releases. From now on, IRIS follows the Semantic Versioning 2.0 guidelines. The code ready for production is always tagged with a version number. alpha
and beta
versions are not production-ready.
Do not use the master
branch in production.
A website with the current development version is also provided and freely accessible. It serves as a beta before public releases.
Added
Improved
Fixed
Full Changelog
"},{"location":"changelog/#v145-june-9-2022","title":"v1.4.5 June 9, 2022","text":"What's Changed
Full Changelog
Notes: Changes are made to the Nginx docker to allow upload of big files, hence specific upgrades are needed and auto-updates is not yet supported for this version. Please see the upgrades instruction for more details.
"},{"location":"changelog/#v144-may-18-2022","title":"v1.4.4 May 18, 2022","text":"What's Changed
Full Changelog
"},{"location":"changelog/#v143-may-13-2022","title":"v1.4.3 May 13, 2022","text":"What's changed
Full Changelog
"},{"location":"changelog/#v142-april-22-2022","title":"v1.4.2 April 22, 2022","text":"Improvements :
Fixes :
A bug fixe exists for this version. See v1.4.3
You can directly contact us, should you need direct support, a demo, further information or anything else related to the project.
contact@dfir-iris.org
Discord
IRIS uses dozen of OS modules and this list is not exhaustive. If you developped something which is used in IRIS and does not figures here, you can contact us.
The most straightforward and recommended way to use IRIS is with Docker. This is presented here.
Disclaimer
IRIS is in its early stage. It can already be used in production, but please set backups of the database and DO NOT expose the interface on the Internet. We highly recommended the use of a private dedicated and secured network.
"},{"location":"getting_started/#pre-requisites","title":"Pre-requisites","text":""},{"location":"getting_started/#hardware","title":"Hardware","text":"IRIS does not require a lot of resources, and it can be run on a small laptop (4 cores, 8Gb of RAM). However, for large a organization and heavy usage, it will need to be significantly scaled up. We don't have benchmarks yet, but keep in mind that the database can grow rapidly and modules can require more resources depending on their purposes.
"},{"location":"getting_started/#docker","title":"Docker","text":"Docker and docker compose are needed to build and run the project. Depending on the OS you will find all the information to install them on the official website of Docker.
The platform is officially supported on most Linux and MacOS. While it should work on Windows, some path needed by the containers to store permanent files might need to be changed in the dockerfiles.
"},{"location":"getting_started/#versioning","title":"Versioning","text":"Starting from version 2.0.0, Iris is following the Semantic Versioning 2.0 guidelines. The code ready for production is always tagged with a version number. alpha
and beta
versions are not production-ready.
Do not use the master
branch in production.
To build and run IRIS, follow these steps:
Clone the iris-web
repository:
git clone https://github.com/dfir-iris/iris-web.git\ncd iris-web\n
Check out the latest non-beta tagged version:
git checkout v2.3.7\n
Copy the environment file
cp .env.model .env\n
Warning
The default configuration is suitable for testing only. To configure IRIS for production, see the configuration section.
Build the Docker containers:
docker-compose build\n
Start IRIS:
docker-compose up\n
IRIS should now be available on the host interface, port 443, using HTTPS protocol by default. You can access it by navigating to https://hostip in your web browser.
By default, an administrator account is created when IRIS is started for the first time. The password is printed in the console output. You can search for WARNING :: post_init :: create_safe_admin :: >>>
in the logs to find the password. Running docker compose logs app | grep 'admin'
should help to find it.
If you want to define an admin password at the first start, you can create and define the environment variable IRIS_ADM_PASSWORD
in the .env
. This has no effect once the administrator account is created.
Note that IRIS is split into five Docker services, each with a different role:
app
- iris_webapp: The core, including web server, database management, module management, etc.db
: A PostgreSQL databaseRabbitMQ
: A RabbitMQ engine to handle job queuing and processingworker
: A job handler relying on RabbitMQnginx
: A NGINX reverse proxyEach service can be built independently, which is useful when developing. In this QuickStart, all services are built at once.
"},{"location":"getting_started/#additional-configuration","title":"Additional configuration","text":"Please see configuration for more details.
"},{"location":"getting_started/#kubernetes","title":"Kubernetes","text":"For enterprises wishing to run their instance of IRIS, utilizing the projects official Helm charts and/or Kustomize manifests, allows them to significantly enhance their deployment and management, presenting a powerful solution to streamline their IRIS deployment and management processes, efficiently running across a cluster of machines, ensuring high availability and seamless scaling as demand fluctuates.
The deploy directory in the iris-web GitHub repository provides a practical starting point for deploying IRIS on their preferred managed k8s platform. We've created two variants: eks and gke, feel free to customize each with your own values.
For more details, please visit the deploy directory on GitHub: deploy
"},{"location":"resources/","title":"Resources","text":"This page is a collection of external resources to help you get started with IRIS and integrate it into your workflow. Please note that some of these resources may be outdated. If you want to add your own resources, please open a pull request or contact us.
"},{"location":"resources/#blog-posts","title":"Blog posts","text":"Note of 25-05-2023
IRIS is growing both in code and interestes. To future-proof the project, we have decided to pause the development of new features and focus on the core functionalities and stability for a while. This includes a major refactoring of the codebase. We will still fix bugs and security issues.
We do not provide a long-term roadmap to prevent any frustration and unmet expectations. It is constantly evolving with feedbacks we receive.
We've thus moved it within a Github project. You can check it here.
"},{"location":"security-advisories/","title":"Security Advisories","text":"This page lists all security advisories that have been published for the code released by DFIR-IRIS.
"},{"location":"security-advisories/#cve-2023-50712-dec-18-2023","title":"CVE-2023-50712 Dec 18, 2023","text":"CVE ID Github ID Severity Impacted product CVE-2023-30615 GHSA-593r-747g-p92p Moderate - CVSS3 4.6/10 iris-web"},{"location":"security-advisories/#description","title":"Description","text":"A stored Cross-Site Scripting (XSS) vulnerability has been identified in iris-web, affecting multiple locations in versions prior to v2.3.7. The vulnerability may allow an attacker to inject malicious scripts into the application, which could then be executed when a user visits the affected locations. This could lead to unauthorized access, data theft, or other related malicious activities.
An attacker need to be authenticated on the application to exploit this vulnerability.
"},{"location":"security-advisories/#affected-versions","title":"Affected versions","text":"iris-web
< 2.3.7
iris-web
>= 2.3.7
No workaround is available.
"},{"location":"security-advisories/#acknowledgment","title":"Acknowledgment","text":"Thanks to Leonard Rapp (G DATA Advanced Analytics GmbH) for the responsible disclosure.
"},{"location":"security-advisories/#cve-2023-30615-may-24-2023","title":"CVE-2023-30615 May 24, 2023","text":"CVE ID Github ID Severity Impacted product CVE-2023-30615 GHSA-gc6j-6276-2m49 Moderate - CVSS3 4.6/10 iris-web"},{"location":"security-advisories/#description_1","title":"Description","text":"A stored Cross-Site Scripting (XSS) vulnerability has been identified in iris-web
, affecting multiple locations in versions prior to v2.2.1
. The vulnerability allows an attacker to inject malicious scripts into the application, which are then executed when a user visits the affected locations. This can lead to unauthorized access, data theft, or other related malicious activities.
An attacker need to be authenticated on the application to exploit this vulnerability.
"},{"location":"security-advisories/#affected-versions_1","title":"Affected versions","text":"iris-web
< 2.2.1
iris-web
> 2.0.0
and < 2.2.1
while not using the alerting feature are not impacted.iris-web
>= 2.2.1
No workaround is available.
"},{"location":"support_us/","title":"Support us","text":"As a free and open source project, we rely on the support of our community to continue development and improve our platform. If you find the platform useful and would like to help us sustain and grow, you may consider supporting us financially through OpenCollective.
Support us
"},{"location":"zqa/","title":"Q & A","text":""},{"location":"zqa/#general-questions","title":"General questions","text":""},{"location":"zqa/#which-version-should-i-install","title":"Which version should I install?","text":"The master branch is stable as all the development is done under the develop branch and merged once ready. To ease the identification, each new version is tagged and a new release is published. We recommend using these. git checkout <tagged_version>
Yes, IRIS is under heavy development. We are adding more and more features, led by feedbacks from the community.
"},{"location":"zqa/#what-is-the-future-of-the-project","title":"What is the future of the project?","text":"We aim to make it evolve as much as possible with the help of the community. We have long term goals to integrate it seamlessly with project like MISP and other OS project, but we don't provide any commitment on how and when to avoid any unmet expectations. For a short term roadmap, you can head to the Github project.
"},{"location":"zqa/#how-can-i-contact-the-dfir-iris-team","title":"How can I contact the DFIR-IRIS team?","text":"You can reach us on discord, Twitter or by email.
"},{"location":"zqa/#cases","title":"Cases","text":""},{"location":"zqa/#can-i-recover-a-deleted-case","title":"Can I recover a deleted case?","text":"No. Cases are deleted from the database and changes are committed. There is no coming back unless you have made backups of the database (which we recommend).
"},{"location":"zqa/#can-i-recover-a-deleted-case-object","title":"Can I recover a deleted case object?","text":"No. Every object such as IOCs, assets, events, notes, etc are immediately deleted from the database and changes are committed.
"},{"location":"zqa/#can-i-add-a-new-asset-type","title":"Can I add a new asset type?","text":"Yes. With a user that have administrative rights, go to Advanced
> Case Objects
.
Yes. Starting from v1.3.0, IOC types can be manipulated. Head to Advanced
> Case Objects
Yes. Starting from v1.4.0, all case objects can be extended thanks to custom attributes. With a user that have administrative rights, go to Advanced
> Custom Attributes
.
Not for now. The searches in each case objects page are done client-side, and the attributes are not fetched. We will however implement a server side search in next releases.
"},{"location":"zqa/#can-i-create-two-cases-with-the-same-name-for-the-same-customer","title":"Can I create two cases with the same name for the same customer?","text":"Yes. Cases are identified with a unique number, so they can have the same name.
"},{"location":"zqa/#can-i-restrict-the-view-of-case-to-a-set-of-users","title":"Can I restrict the view of case to a set of users?","text":"Yes it is since v2.0.0. See Access control.
"},{"location":"zqa/#can-i-change-the-name-or-customer-of-an-existing-case","title":"Can I change the name or customer of an existing case?","text":"Yes it is since v2.0.0.
"},{"location":"zqa/#operations","title":"Operations","text":""},{"location":"zqa/#what-is-the-password-policy-can-it-be-changed","title":"What is the password policy? Can it be changed?","text":"Before v1.4.5, the password policy is hardcoded and cannot be changed. It should be 12 characters minimum and contains a capital letter and a number.
"},{"location":"zqa/#can-i-change-my-profile-picture","title":"Can I change my profile picture?","text":"No, not for now. This wasn't a priority for us, it will be released in future versions.
"},{"location":"zqa/#i-lost-the-administrator-password-can-i-recover-it","title":"I lost the administrator password, can I recover it?","text":"Passwords are hashed so they can't be recovered. But you can change it. Please see changing a lost password.
"},{"location":"zqa/#can-i-delete-a-user","title":"Can I delete a user?","text":"No. To keep consistencies in the database, users unfortunately cannot be deleted if they have done some activities. You can however disable them to prevent them appearing in the UI and connecting to the plafeform.
"},{"location":"zqa/#can-i-delete-a-customer","title":"Can I delete a customer?","text":"No. To keep consistencies in the database, customers unfortunately cannot be deleted if they are linked to cases.
"},{"location":"zqa/#can-i-prevent-backrefs-of-assets-and-iocs","title":"Can I prevent backrefs of assets and IOCs?","text":"No. It might be possible in future versions but for now it is better to spin up a new instance for restricted cases. The backref is however automatically disabled for performance reasons, for cases with more than 300 assets. We are working on a more efficient way to backref.
"},{"location":"zqa/#my-report-template-is-not-generated-and-generates-an-error","title":"My report template is not generated and generates an error","text":"Please triple check typos in tags as there is no fault tolerance. You can reach us in case of troubles.
"},{"location":"zqa/#integration","title":"Integration","text":""},{"location":"zqa/#can-i-enrich-iocs-with-external-sources","title":"Can I enrich IOCs with external sources?","text":"Starting from v1.4.0, it is now possible to easily develop module to enrich case objects. A module Iris VT and IRIS MISP are already provided.
"},{"location":"zqa/#is-there-an-api-client","title":"Is there an API client?","text":"Yes, you can find it on our Github.
"},{"location":"zqa/#security","title":"Security","text":""},{"location":"zqa/#can-i-restrict-cases","title":"Can I restrict cases?","text":"Yes it is since v2.0.0. See Access control.
"},{"location":"zqa/#can-i-expose-iris-on-the-internet","title":"Can I expose IRIS on the Internet?","text":"NO! Please don't. This platform should only be accessible in a restricted environment.
"},{"location":"zqa/#i-found-a-security-issue-can-i-have-a-bounty","title":"I found a security issue, can I have a bounty?","text":"No - IRIS is free and open source so there is no bounty. Please report it as soon as possible so we can fix it.
"},{"location":"zqa/#misc","title":"MISC","text":""},{"location":"zqa/#what-does-iris-stand-for","title":"What does IRIS stand for?","text":"Originally Incident Response Investigation System. But it can be whatever you want really.
"},{"location":"development/","title":"Development","text":"This section is under construction and more elements will be added over time
This documentation is not a detailed how-to develop IRIS. It gives some insights to help understand the basic code of the project and how to contribute.
"},{"location":"development/#general-repositories-conventions","title":"General repositories conventions","text":""},{"location":"development/#branches","title":"Branches","text":"We are using the Gitflow Workflow to manage our git branches. In a nutshell :
master
contains only \"production-ready\" codedevelop
contains the major development code. When ready it is tagged and merged into master
develop
contains either : new_feature
iXX_issue_title
, with XX being the issue number The commits convention is the following :
[action] Commit message
is used, with action
being a 3 letters action related to the commit, eg ADD
for additions, DEL
for deletions, IMP
for improvements, etc.[#issue_id][action] Commit message
The following sections are available in this documentation :
IRIS does not defines a separate API for users, meaning the HTML pages are actually using the API themselves. Routes don't need to handle the authentication and roles. These are handles by wrappers (see snippets below).
"},{"location":"development/code-tips/#page-route","title":"Page route","text":"A page returns an HTML content and should use the following code structure : Example of page route
@blueprint.route('/a/good/route', methods=['GET']) # (1)\n@login_required # (2)\ndef view_a_good_route(caseid, url_redir): # (3)\nif url_redir:\nreturn redirect(url_for('bluprintname.method_name', cid=caseid)) # (4)\n# route code \nreturn render_template(\"a_good_route.html\", variable_1=var_1, ...) # (5)\n
@login_required
is used for users page and @admin_login_required
is used for admin restricted pages. caseid
and url_redir
are variable provided by @login_required
and @admin_login_required
wraps. caseid
indicates which case ID the user tried to access the route with. url_redir
indicates the caseid provided wasn't valid and a redirection is needed. variable_1
is a value that can be accessed from within the template itself. More variables can be added, or not at all. An API route returns a JSON content. Two types are pre-defined and should be used : Standard API returns
response_success(msg=\"A success message\", data=<data associated with the success feedback>)\nresponse_error(msg=\"An error message\", data=<data associated with the error feedback>, status=<status code, by default 400>)\n
Below is an example of standard API route. Example of page route
@blueprint.route('/a/good/api_route', methods=['GET']) # (1)\n@api_login_required # (2)\ndef view_a_good_route(caseid): # (3)\n# API route code \nreturn response_success(\"ok\", data=my_data_object) # (4)\n
@api_login_required
is used for users API endpoints and @api_admin_required
is used for admin restricted endpoints. caseid
is provided @api_login_required
and @api_admin_required
wraps. It indicates which case ID the user tried to access the endpoint with. In case a DB migration is needed, you need to provide an alembic migration script.
Test your migration
Please try out your migration as this is an important piece of the upgrades. Spin up an old version of IRIS, input some data and then try to start your new version. Ensure that everything is migrated as expected.
In a terminal and from within the IRIS virtual env :
source
alembic -c app/alembic.ini revision -m \"A few words to describe your changes\"
This creates a new revision file source > app > alembic > versions
. It's a Python file that basically describes what needs to be updated DB-wise. You can take example from the ones we already have generated in the same folder.
Hint
During your tests you might face the issue that Alembic does not apply your changes after you executed it once. It's because it keeps tracks of the latest applied revision in a table alembic_version
. It doesn't know you changed the revision file. In that case the trick is to connect to the DB, and then delete the entry in the alembic_version. This will force it to reapply all revisions at startup. If you're using the DB docker you can use the following:
docker exec -it <db_container_id> /bin/sh
su postgres
psql
\\c iris_db;
DELETE FROM alembic_version;
It follows an issue raised on the Github of the project.
It recommends the use of a hybrid development environment, as most of the time only the web-app needs to be changed:
For the webapp configuration, a specific .ini
need to be created.
config.priv.ini
in source/app by copying the config.docker.ini
present in the same directory. PG_SERVER = db
to PG_SERVER = 127.0.0.1
or whatever IP is the Postgresql/docker running withThat's the only configuration change needed for the app to run outside docker. The docker.priv.ini
is already excluded in gitignore.
Then Pycharm need to be setup with a dedicated environment, by adding a new configuration:
source/run.py
source
To have pylint, right click on source
in the directory tree and mark directory as
> sources root
. The requirements then need to be installed. Pycharm should detect the requirements.txt and propose to install the dependencies. Otherwise they can be installed with the following command (issued in the virtual environment) : pip3 install -r source\\requirements.txt
docker-compose up db
IRIS can now be developed and debugged on the fly.
"},{"location":"development/environment/#tests-in-docker","title":"Tests in docker","text":"Once the code is working by running on Pycharm, we highly recommend testing it on Docker. To do so, the app docker need to be erased and rebuilt.
docker-compose rm app
docker-compose build app
docker-compose up db app
Development considerations If the development results in DB modification, please use Alembic and add a migration script so users don't loose their data when they upgrade.
"},{"location":"development/hooks/","title":"Hooks","text":"Introduced in IRIS v1.4.0
Hooks are a mean for modules to react on specific events that occurs on IRIS. By subscribing to a hook, a module is automatically notified when the associated event occurs. This offers a multitude of possibilities, from adding insight to IRIS objects, to pushing information to another platform or even changing how IRIS works.
"},{"location":"development/hooks/#types","title":"Types","text":"There are 3 types of hooks.
On preload
: Triggered before an object is processed and committed to database. It is triggered right after a request is received, and the data associated with the hook is usually the request content itself. In most of the cases, modules should not subscribe to these hooks. On postload
: Triggered after an object is processed and committed to database. It is triggered after IRIS processed the request and the data associated with the hook is usually a list of SqlAlchemy objects (such as IOC, Assets, etc). Manual
: Triggered by manual action of a user. When a module subscribes to these hooks, it needs to provide a \"menu option name\" which will be displayed to users. When they click this option, the associated hook is triggered for this module only. Multiple manual hooks can be registered for one module. Danger
on_preload
hooks must run synchronously, i.e not queued in RabbitMQ. This effectively blocks the current user request until the module finishes the processing. We highly recommend to only use on_postload
hooks for a better user experience. These hooks are transparent for users and rely on already verified and committed data. Handling on_preload
hooks implies the received data is unsafe - directly coming from remote clients - and the module needs to process the data as fast as possible.
Two methods are provided by IrisModuleInterface
to subscribe and unsubscribe to hooks.
def register_to_hook(module_id: int, \niris_hook_name: str, \nmanual_hook_name: str = None, \nrun_asynchronously: bool = True)\ndef deregister_from_hook(module_id: int, \niris_hook_name: str)\n
The registration method expects the following arguments:
module_id
: The ID of the calling module. This information is given by IRIS when the register_hooks
method is called. iris_hook_name
: The name of the hook to which subscribe. This must be one of the hook listed in the section below. manual_hook_name
: The name of the UI menu that is provided to users if the registration concerns a manual hook. If nothing is provided, IRIS will create a name composed as follows: <module_name>::<hook_name>
. This value is ignored if the signal is not manual. run_asynchronously
: Set to True (default) to run the module in a RabbitMQ task upon hook triggering. If set to False, the module is called immediately, which have for effect to effectively block the current user request until the module finishes. This is the behavior to use for on_preload
hooks. However, we strongly recommend the use of on_postload
hooks to prevent any unwanted (see warning section above). The deregistration method expects the following arguments:
module_id
: The ID of the calling module. This information is given by IRIS when the register_hooks
methods is called. iris_hook_name
: The name of the hook to which unsubscribe. If the module is not subscribed to the specified hook the function returns without errors. Please see the modules documentation for more details on how to implement these methods.
"},{"location":"development/hooks/#available-hooks","title":"Available hooks","text":"The following hooks are natively available for subscription.
Hook name Description on_preload_case_create Triggered on case creation, before commit in DB on_postload_case_create Triggered on case creation, after commit in DB on_preload_case_delete Triggered on case deletion, before commit in DB on_postload_case_delete Triggered on case deletion, after commit in DB on_postload_case_update Triggered on case update, before commit in DB on_manual_trigger_case Triggered upon user action on_postload_alert_create Triggered on alert creation, after commit in DB on_postload_alert_update Triggered on alert update, after commit in DB on_postload_alert_delete Triggered on alert deletion, after commit in DB on_postload_alert_escalate Triggered on alert escalation, after commit in DB on_postload_alert_merge Triggered on alert merge, after commit in DB on_postload_alert_unmerge Triggered on alert unmerge, after commit in DB on_preload_asset_create Triggered on asset creation, before commit in DB on_postload_asset_create Triggered on asset creation, after commit in DB on_preload_asset_update Triggered on asset update, before commit in DB on_postload_asset_update Triggered on asset update, after commit in DB on_preload_asset_delete Triggered on asset deletion, before commit in DB on_postload_asset_delete Triggered on asset deletion, after commit in DB on_manual_trigger_asset Triggered upon user action on_preload_note_create Triggered on note creation, before commit in DB on_postload_note_create Triggered on note creation, after commit in DB on_preload_note_update Triggered on note update, before commit in DB on_postload_note_update Triggered on note update, after commit in DB on_preload_note_delete Triggered on note deletion, before commit in DB on_postload_note_delete Triggered on note deletion, after commit in DB on_manual_trigger_note Triggered upon user action on_preload_ioc_create Triggered on ioc creation, before commit in DB on_postload_ioc_create Triggered on ioc creation, after commit in DB on_preload_ioc_update Triggered on ioc update, before commit in DB on_postload_ioc_update Triggered on ioc update, after commit in DB on_preload_ioc_delete Triggered on ioc deletion, before commit in DB on_postload_ioc_delete Triggered on ioc deletion, after commit in DB on_manual_trigger_ioc Triggered upon user action on_preload_event_create Triggered on event creation, before commit in DB on_preload_event_duplicate Triggered on event duplication, before commit in DB. This event only received the event ID which will be duplicated on_postload_event_create Triggered on event creation, after commit in DB on_preload_event_update Triggered on event update, before commit in DB on_postload_event_update Triggered on event update, after commit in DB on_preload_event_delete Triggered on event deletion, before commit in DB on_postload_event_delete Triggered on event deletion, after commit in DB on_manual_trigger_event Triggered upon user action on_preload_evidence_create Triggered on evidence creation, before commit in DB on_postload_evidence_create Triggered on evidence creation, after commit in DB on_preload_evidence_update Triggered on evidence update, before commit in DB on_postload_evidence_update Triggered on evidence update, after commit in DB on_preload_evidence_delete Triggered on evidence deletion, before commit in DB on_postload_evidence_delete Triggered on evidence deletion, after commit in DB on_manual_trigger_evidence Triggered upon user action on_preload_task_create Triggered on task creation, before commit in DB on_postload_task_create Triggered on task creation, after commit in DB on_preload_task_update Triggered on task update, before commit in DB on_postload_task_update Triggered on task update, after commit in DB on_preload_task_delete Triggered on task deletion, before commit in DB on_postload_task_delete Triggered on task deletion, after commit in DB on_manual_trigger_task Triggered upon user action on_preload_global_task_create Triggered on global task creation, before commit in DB on_postload_global_task_create Triggered on global task creation, after commit in DB on_preload_global_task_update Triggered on task update, before commit in DB on_postload_global_task_update Triggered on global task update, after commit in DB on_preload_global_task_delete Triggered on task deletion, before commit in DB on_postload_global_task_delete Triggered on global task deletion, after commit in DB on_manual_trigger_global_task Triggered upon user action on_preload_report_create Triggered on report creation, before generation in DB on_postload_report_create Triggered on report creation, before download of the document on_preload_activities_report_create Triggered on activities report creation, before generation in DB on_postload_activities_report_create Triggered on activities report creation, before download of the document on_postload_asset_commented Triggered on asset comment, after commit in DB on_postload_asset_comment_update Triggered on asset comment update, after commit in DB on_postload_asset_comment_delete Triggered on asset comment deletion, after commit in DB on_postload_evidence_commented Triggered on evidence comment, after commit in DB on_postload_evidence_comment_update Triggered on evidence comment update, after commit in DB on_postload_evidence_comment_delete Triggered on evidence comment deletion, after commit in DB on_postload_task_commented Triggered on task comment, after commit in DB on_postload_task_comment_update Triggered on task comment update, after commit in DB on_postload_task_comment_delete Triggered on task comment deletion, after commit in DB on_postload_ioc_commented Triggered on ioc comment, after commit in DB on_postload_ioc_comment_update Triggered on ioc comment update, after commit in DB on_postload_ioc_comment_delete Triggered on ioc comment deletion, after commit in DB on_postload_event_commented Triggered on event comment, after commit in DB on_postload_event_comment_update Triggered on event comment update, after commit in DB on_postload_event_comment_delete Triggered on event comment deletion, after commit in DB on_postload_note_commented Triggered on note comment, after commit in DB on_postload_note_comment_update Triggered on note comment update, after commit in DB on_postload_note_comment_delete Triggered on note comment deletion, after commit in DB on_postload_alert_commented Triggered on alert comment, after commit in DB on_postload_alert_comment_update Triggered on alert comment update, after commit in DB on_postload_alert_comment_delete Triggered on alert comment deletion, after commit in DB"},{"location":"development/structure/","title":"Structure overview","text":""},{"location":"development/structure/#flask","title":"Flask","text":"IRIS uses Flask for the web engine.
"},{"location":"development/structure/#routes-and-blueprints","title":"Routes and blueprints","text":"Each page and API endpoints (eg /login
, /dashboard
, /case/assets/list
, etc) refers to a route in the IRIS Flask app. They define what the application should do when Flask receives a request on an URI. To keep structure in the projects, these routes are grouped by Blueprints
. The Blueprints reflects the structure shown in the IRIS UI left menu. For instance there is a case
and an activities
Blueprint.
The Blueprints and thus routes are defined in source > app > blueprints
. All the blueprints are registered in source > app > views.py
.
IRIS uses dynamic page templating when an URI is visited. These Jinja2 templates are filled at runtime with the needed information and then returned to the client. Each route offering a page (i.e non-API endpoints) thus relies on a template. These are set in a folder named templates
in each Blueprint. For instance, for the dashboard template : source > app > blueprints > dashboard > templates > index.html
.
Static content is served from a common folder under source > app > static > assets
. It contains CSS, JS and images. These can be accessed by pages using \"/static/assets/<the-resource>\"
.
For the database management, the application uses SQLAlchemy with a PostgreSQL backend. There is - normally - no need to directly deal with PostgreSQL, everything goes through SQLAlchemy. It provides a Python overlay which allows to talk to the DB with objects.
"},{"location":"development/structure/#models","title":"Models","text":"Each table of the app is defined by a model. These are defined in source > app > model
. When IRIS starts, it looks for the already created tables and creates the missing ones if needed. If changes are done on a table or field, then a migration is needed. This is explained in Alembic migrations.
To help structuring the code, we are trying to move the DB code from the routes code. This is partially done and work in progress. If your route requests the DB, please put the DB code in source > app > datamgmt
.
To apply schema migration without the need to rebuild the DB, IRIS uses Alembic. It allows to define migration scheme and IRIS calls it when it starts so users can upgrade without too much hassles.
"},{"location":"development/structure/#hooks-modules-and-tasks","title":"Hooks, modules and tasks","text":"Modules are handled via tasks thanks to Celery and RabbitMQ. More info here and here.
"},{"location":"development/structure/#iris-startup","title":"IRIS startup","text":"When starting-up, IRIS initiates a bunch of DB objects, whether it is started for the first time or just restarted. Objects already created are not recreated, but the missing ones are applied. This ensure a smooth migration between versions. These are defined in source > app > post_init.py
. The scripts also contains the code that runs th DB migration with Alembic.
A DFIR-IRIS Module (DIM) is a Python package allowing to extend IRIS features. DIMs are not running constantly and are only called following specific actions done by users.
We distinct two types of modules:
Pipeline modules
: Allow uploading and processing of evidences through modular pipelines (eg: EVTX parsing and injection into a database or data visualiser). These are called when a user queries Update case
and select evidences to process. Processor modules
: Allow processing of IRIS data upon predefined actions / hooks. (eg: be notified when a new IOC is created and get VT/MISP insights for it). These are either called automatically upon specific events, or if a user manually triggers them. Except for some triggers for processor modules, all tasks provided by DIMs are run asynchronously in RabbitMQ tasks, so they don't impact the UI.
Both types of DIMs have the same structure, they only differ in their configurations and how they handle the data they receive. For that purpose, every DIM inherit from a common class named IrisModuleInterface
- available here - which provides the basic structure and methods of a module.
Hint
To quickly start writing a new module, one can follow these tutorials.
"},{"location":"development/modules/#overview","title":"Overview","text":"Modules are instantiated upon actions (hooks, triggers, user actions) and this occurs each time the said actions occur. It implies the initiation of a module has to be very quick. In most of the case, the __init__
method should not even be overwritten.
They can live either in the worker or the web-app, depending on their type and action they are handling. They can also live in both. This implies multiple instances of the same module can run at the same time.
The graph below shows two modules of different types running in the worker and interacting with external elements.
Modules don't have to handle the task creations or resource locks. This is handled by IRIS. They just need to process the data they received and return results in a predefined manner.
"},{"location":"development/modules/#common-structure","title":"Common structure","text":"The section below describes the common structure of modules.
"},{"location":"development/modules/#directory-structure","title":"Directory structure","text":"setup.py # Setup configuration to build the module \nREADME.md # README \niris_example_name # Name of the package \n __init__.py # Declaration of the package and main class\n IrisExampleConfig.py # Configuration of the module to help keep the main file clean \n IrisExampleInterface.py # Main class of the module \n module_helper # Sub module containing the helper functions of the module \n helper.py # for instance access to ext resource, manipulation of data \n helper2.py # etc. \n
"},{"location":"development/modules/#the-initpy-file","title":"The init.py file","text":"Iris loads the modules dynamically. To do so, it needs to know the name of the main class of the module and relies on __init__.py
to find this information.
__iris_module_interface = \"IrisEXAMPLEInterface\"\n
Where IrisEXAMPLEInterface
is the main class of the module and inherits of the base class IrisModuleInterface
.
Caution
Failing to provide the main class in __init__.py
or having the main class inherit from IrisModuleInterface
will make IRIS fail each time it attempts to load the module.
Iris needs to know what the module is doing and what services it is providing. This is done via the attributes of the main class (let's say IrisEXAMPLEInterface
). The attributes are :
_module_name
: string - \"human\" name presented to users. _interface_version
: float - version of IrisModuleInterface
used. If the version is not supported, the server will refuse to register the module. _module_version
: float - version of the module itself to help users keep tracks of evolutions. _module_type
: string - Type of module. The available modules types are listed in IrisModuleInterface.IrisModuleTypes
_pipeline_support
: bool - should be set to True if it implements a pipeline process (aka module of type pipeline_module
)._pipeline_info
: dict - contains the configuration of the pipeline. The following structure must be followed:pipeline_info = {\n# Name of the pipeline used for internal tracking. This \n# must be unique among all modules so pick something really unique \n\"pipeline_internal_name\": \"example_pipeline\",\n# The name of the pipeline presented to the user. Use something \n# that will help the users to identify the right pipeline\n\"pipeline_human_name\": \"Example Pipeline\",\n# Arguments presented to the users when they select the pipeline\n\"pipeline_args\": [\n['some_index', 'required'],\n['example_argument', 'optional']\n]\n}\n
_module_configuration
: A list of dict. The list contains each field needed by the module. This list is shown in the Iris webpage of the module configuration. Each field in an entry is mandatory. _module_configuration = [\n{\n\"param_name\": \"vt_api_key\",\n\"param_human_name\": \"VT API Key\",\n\"param_description\": \"Virus total API key\",\n\"default\": None,\n\"mandatory\": True,\n\"type\": \"sensitive_string\"\n},\n{\n\"param_name\": \"vt_key_is_premium\",\n\"param_human_name\": \"VT Key is premium\",\n\"param_description\": \"Set to True if the VT key is premium\",\n\"default\": False,\n\"mandatory\": True,\n\"type\": \"bool\"\n},\n{\n\"param_name\": \"vt_ip_assign_asn_as_tag\",\n\"param_human_name\": \"Assign ASN tag to IP\",\n\"param_description\": \"Assign a new tag to IOC IPs with the ASN fetched from VT\",\n\"default\": True,\n\"mandatory\": True,\n\"type\": \"bool\"\n}\n]\n
The above example results in the following.
"},{"location":"development/modules/quick_start/processor/","title":"Processor modules","text":"In this tutorial, we demonstrate the steps to write a basic processor module which subscribes to a hook, and log what it receives when the hook is triggered. We will also add a configuration setting to offer our users the ability disable this feature.
We'll call it IrisDummyModule
.
As described in the development module main page, the module should have the following structure.
setup.py # Setup configuration to build the module \nREADME.md # README \niris_dummy_module # Name of the package \n__init__.py # Declaration of the package and main class\nIrisDummyConfig.py # Configuration of the module to help keep the main file clean \nIrisDummyModule.py # Main class of the module \n
While the module could have only one main file IrisDummyModule.py
, we recommend splitting its configuration into a new configuration file (here IrisDummyConfig.py
) to keep the code clear.
There is no mandatory naming convention for the files or the class or the methods. We chose this one to keep things clear, and we recommend following the same. But it's up to you really.
We will walk over these files one by one during this tutorial.
"},{"location":"development/modules/quick_start/processor/#creating-the-interface","title":"Creating the interface","text":"The interface is the code that talks with IRIS. It implements methods that call and are called by the server. It needs to inherit IrisModuleInterface class from the IrisModuleInterface package. This module handles most of the methods needed by IRIS to recognize, set up and call the module. By inheriting this class in our interface, we avoid writing that part ourselves.
Let's write our basic interface class. The name of the file has to be the name of the main class, that's the only constraint. We'll see later on why.
iris_dummy_module/IrisDummyModule.py#!/usr/bin/env python3\n# Import the IrisInterface class\nfrom iris_interface.IrisModuleInterface import IrisModuleInterface\n# Create our module class\nclass IrisDummyModule(IrisModuleInterface):\npass \n
That's it! Actually this class is not doing anything right now. We'll need to add a few methods to register our hook later.
But first we need to indicate to IRIS what is our main interface class. Remember, there is no convention restriction, so IRIS has no way to know which class it should instantiate to call our module.
To do so, we need to set a specific variable in our __init__.py
.
# Set the __iris_module_interface variable to the name of our main class. \n# When IRIS instantiates the python module, it looks for \"module.__iris_module_interface\"\n# And then tries to instantiate the class \"__iris_module_interface.__iris_module_interface\", here 'IrisDummyModule.IrisDummyModule'. \n# That's why the python file must have the same name as the class. \n__iris_module_interface = \"IrisDummyModule\"\n
Our module is now recognizable by IRIS Pretty simple right?
"},{"location":"development/modules/quick_start/processor/#writing-the-configuration","title":"Writing the configuration","text":"The next step is to describe what the module is doing, its name, its configuration, etc. This is done by overwriting predefined variables of the IrisModuleInterface
class.
Let's create our Python configuration file and go through each variables.
iris_dummy_module/IrisDummyConfig.py# Import the module types list, so we can indicate the type of our module \nfrom iris_interface.IrisModuleInterface import IrisModuleTypes \n# Human name displayed in the GUI Manage > Modules. This can be anything, \n# but try to put something meaningful, so users recognize your module. \nmodule_name = \"IrisDummy\"\n# Description displayed when editing the module configuration in the UI. \n# This can be anything, \nmodule_description = \"Provides a dummy module that replies to one hook\"\n# Set the interface version used. This needs to be the version of \n# the IrisModuleInterface package. This version is check by the server to\n# to ensure our module can run on this specific server \ninterface_version = 1.1\n# The version of the module itself, it can be anything \nmodule_version = 1.0\n# The type of the module, here processor \nmodule_type = IrisModuleTypes.module_processor\n# Our module is a processor type, so it doesn't offer any pipeline \npipeline_support = False\n# Provide no pipeline information as our module don't implement any \npipeline_info = {}\n# The configuration of the module that will be displayed and configurable \n# by administrators on the UI. This describes every parameter that can \n# be set. \nmodule_configuration = [\n{\n\"param_name\": \"log_received_hook\",\n\"param_human_name\": \"Log received hook\",\n\"param_description\": \"Logs a message upon hook receiving if set to true. Otherwise do nothing.\",\n\"default\": True,\n\"mandatory\": True,\n\"type\": \"bool\"\n}\n]\n
The module configuration parameters are the following :
param_name
: The internal parameter name. This will be used by the module itself to fetch the value when needed.param_human_name
: The name displayed on the UI for this specific parameterparam_description
: A description explaining what this parameter is doing to help administratorsdefault
: The default value of our parameter. Here we set to True, so after install our module is already configured and ready to log the hook. mandatory
: Indicates whether the parameter is mandatory or not. If set to True and no value is provided (either by admin or by default), the module is automatically disabled by IRIStype
: The type of parameter. Here a boolean, which will be rendered under the form of a checkbox. A module can have as many parameters as it needs.
We now need to update our main class to set this configuration.
iris_dummy_module/IrisDummyModule.py#!/usr/bin/env python3\n# Import the IrisInterface class\nfrom iris_interface.IrisModuleInterface import IrisModuleInterface\n# Create our module class\nclass IrisDummyModule(IrisModuleInterface):\n# Set the configuration\n_module_name = interface_conf.module_name\n_module_description = interface_conf.module_description\n_interface_version = interface_conf.interface_version\n_module_version = interface_conf.module_version\n_pipeline_support = interface_conf.pipeline_support\n_pipeline_info = interface_conf.pipeline_info\n_module_configuration = interface_conf.module_configuration\n_module_type = interface_conf.module_type\npass \n
Done! The module is now providing enough information to IRIS, so it knows exactly what our module do and what needs to be called to run it.
However, our module is still doing nothing. Let's make it subscribe to an IRIS hook.
"},{"location":"development/modules/quick_start/processor/#subscribing-to-a-hook","title":"Subscribing to a hook","text":"Hooks allow to be notified by IRIS when a specific event occurs (IOC creation, deletion, etc). For a comprehensive description of hooks, please see the Hooks section of this documentation.
The registration (or subscription) to a hook occurs at two moments during the life of a module:
These registration/deregistration events are triggered by IRIS, and are propagated to modules through the IrisModuleInterface
method register_hooks
[ref].
To register to a hook, we need to override this method and register our hook within this method. To do so, IrisModuleInterface
offers us another method register_to_hook
[ref], which we can call for each hook we want to subscribe.
Here is a summary of the events:
register_hooks
of our module. This indicates it is time for us to register our hooks. register_to_hook
for each hook we want to subscribeLet's add this to our main class and register to the on_postload_ioc_create
. This will notify use each time a new IOC is created and committed to the database.
#!/usr/bin/env python3\n# Import the IrisInterface class\nfrom iris_interface.IrisModuleInterface import IrisModuleInterface\n# Create our module class\nclass IrisDummyModule(IrisModuleInterface):\n# Set the configuration\n_module_name = interface_conf.module_name\n_module_description = interface_conf.module_description\n_interface_version = interface_conf.interface_version\n_module_version = interface_conf.module_version\n_pipeline_support = interface_conf.pipeline_support\n_pipeline_info = interface_conf.pipeline_info\n_module_configuration = interface_conf.module_configuration\n_module_type = interface_conf.module_type\ndef register_hooks(self, module_id: int):\n\"\"\"\n Called by IRIS indicating it's time to register hooks. \n :param module_id: Module ID provided by IRIS.\n \"\"\"\n# Call the hook registration method. We need to pass the \n# the module_id to this method, otherwise IRIS won't know \n# to whom associate the hook. \n# The hook name needs to be a well known hook name by IRIS. \nstatus = self.register_to_hook(module_id, iris_hook_name='on_postload_ioc_create')\nif status.is_failure():\n# If we have a failure, log something out \nself.log.error(status.get_message())\nelse:\n# Log that we successfully registered to the hook \nself.log.info(f\"Successfully subscribed to on_postload_ioc_create hook\")\n
That's it! Our module has now officially subscribed to a hook and will be notified each time an IOC is created.
So how the module is notified? Once again this is done by a method named hooks_handler
[ref] that IrisModuleInterface
provides, and we need to overwrite.
This method is called each time one of the event associated to the hook we subscribed is triggered. It provides the name of the hook and as well as the data associated to it. By overwriting this method, we can process the hook and the data!
We will add a condition in this method, that is if the administrator sets the module parameter log_received_hook
to False, then the module won't log anything and simply return the data.
Hint
The current configuration of the module can be accessed with the attribute self._dict_conf
.
#!/usr/bin/env python3\n# Import the IrisInterface class\nfrom iris_interface.IrisModuleInterface import IrisModuleInterface\n# Create our module class\nclass IrisDummyModule(IrisModuleInterface):\n# Set the configuration\n_module_name = interface_conf.module_name\n_module_description = interface_conf.module_description\n_interface_version = interface_conf.interface_version\n_module_version = interface_conf.module_version\n_pipeline_support = interface_conf.pipeline_support\n_pipeline_info = interface_conf.pipeline_info\n_module_configuration = interface_conf.module_configuration\n_module_type = interface_conf.module_type\ndef register_hooks(self, module_id: int):\n\"\"\"\n Called by IRIS indicating it's time to register hooks. \n :param module_id: Module ID provided by IRIS.\n \"\"\"\n# Call the hook registration method. We need to pass the \n# the module_id to this method, otherwise IRIS won't know \n# to whom associate the hook. \n# The hook name needs to be a well known hook name by IRIS. \nstatus = self.register_to_hook(module_id, iris_hook_name='on_postload_ioc_create')\nif status.is_failure():\n# If we have a failure, log something out \nself.log.error(status.get_message())\nelse:\n# Log that we successfully registered to the hook \nself.log.info(f\"Successfully subscribed to on_postload_ioc_create hook\")\ndef hooks_handler(self, hook_name: str, data):\n\"\"\"\n Called by IRIS each time one of our hook is triggered. \n \"\"\"\n# read the current configuration and only log the call if \n# our parameter is set to true \nif self._dict_conf.get('log_received_hook') is True:\nself.log.info(f'Received {hook_name}')\nself.log.info(f'Received data of type {type(data)}')\n# Return a standardized message to IRIS saying that everything is ok. \n# logs=list(self.message_queue) is needed, so the users can see the logs \n# our module generated during its execution. \nreturn InterfaceStatus.I2Success(data=data, logs=list(self.message_queue))\n
We are done! Our module is now fully ready to register, subscribe to a hook and act when notified.
"},{"location":"development/modules/quick_start/processor/#installing-and-registering-the-module","title":"Installing and registering the module","text":"We need now need to build and install the module. To do so, you'll need this script and an IRIS docker instance running on the same machine.
The script will build the module, copy it to the docker instances, install it within them and restart the dockers.
chmox +x buildnpush2iris.sh
./buildnpush2iris.sh -a
The module should now be installed. We can register it in IRIS as explained here.
"},{"location":"operations/alerts/","title":"Alerts","text":"This section is under construction.
Introduced in IRIS v2.1.0
Alerts can be fed directly into IRIS using the Alerts API. Any source can inject alerts into IRIS, as long as it can send HTTP requests and respects the alert format.
A service account with the alert_read
and alert_write
permission can be used to send alerts to IRIS.
Warning
This section is only available for users with the alert_read
and alert_write
permissions.
Alerts can then be viewed in the Alerts
section of IRIS. Analysts can then triage the alerts and create cases from them.
Each alert can be expanded to show more details.
"},{"location":"operations/alerts/#alerts-assignment","title":"Alerts assignment","text":"Alerts can be assigned to analysts. This can be done directly from the alert view.
To self-assign an unassigned alert, click on the hand icon on the left.
Clicking again on the hand icon will prompt with a list of analysts to assign the alert to.
The right button Assign
, when hovering above an alert, can also be used to assign the alert to an analyst.
Alerts can be escalated/merged into a new case. When hovering above an alert, a Merge
button will appear.
Once clicked, a new window appears, requesting additional information. In this window, the analyst can:
Alerts can also be escalated/merged into an existing case. When hovering above an alert, a Merge
button will appear.
Once clicked, a new window appears requesting additional information. The button Merge into existing case
needs to be clicked. A new dropdown appears and allows to select the case to merge the alert into.
Similar to the case creation, the analyst can:
The selected IOCs and assets are then added to the selected case.
"},{"location":"operations/alerts/#unmerge-alerts-from-a-case","title":"Unmerge alerts from a case","text":"Alerts can be unmerged from a case.
Info
When unmerging an alert, the alert is not deleted. It is only removed from the case. The alert state is not changed. The IOCs and assets are not removed from the case.
When a case is merged, a new link appears on the alert and mentions the case it was merged into. Clicking on this link allows to browse the case or to unmerge the alert.
"},{"location":"operations/alerts/#alerts-relationships","title":"Alerts relationships","text":"Each alert have a Relastionships
section. This section shows the relationships between the alert and other objects in IRIS. This feature is in preview and might report false relationships.
The relationships are computed using the following logic:
By default the view limits the relationships to 100 nodes and looks back 7 days. This can be updated directly in the alert view.
"},{"location":"operations/api/","title":"API","text":"IRIS is meant to be plug-able and be integrated with the existing environments.
Through the REST API, one can do almost as much as it is possible to do through the web interface. Under the hood, the web interface is actually talking to the API.
Hint
A Python client is available here to ease the automation.
"},{"location":"operations/api/#api-keys","title":"API Keys","text":"The first step is to obtain an API key. Each user is automatically attributed one when it is created. It can be found on the left panel, under username and My Settings
.
Token exposure
In case the token is exposed and needs to be changed, a new one can be generated with the Renew
option. Renewing a token revokes the previous.
The API constantly evolves with IRIS and thus multiple versions exists. Use the references below to check which API version applies to your IRIS version. Starting from IRIS v1.4.0, the supported API version can also be checked in the UI Advanced
> Server settings
.
The API token is used as a Bearer and needs to be present in the header Authorization
when issuing requests. For example, to list all the cases:
curl --request GET \\\n--url http://localhost:8000/manage/cases/list?cid=1 \\\n--header 'Authorization: Bearer mWpCUVNzBMU5EnbIAK50jLPhYjKBTHZjobdogc_n_yixpJTmt9tzAf8WYDI7m5XgB9wCJnlaXlHIh9RZjtp2fA' \\\n--header 'Content-Type: application/json'\n
The only way to revoke a token is to renew the current one. Once done, the previous API token does not exist anymore in the database and it becomes ineffective.
"},{"location":"operations/case_templates/","title":"Case templates","text":"Introduced in IRIS v2.1.0
Case templates are a way to pre-configure a case with a set of predefined informations. The case templates can be managed in Advanced
> Case templates
.
Info
This section is only available for users with the administrator role.
Case templates are made of a set of informations that will be used to pre-fill the case creation form. The following elements can be set:
Looking for case templates?
We are providing a set of case templates in the IRIS Resources repository.
"},{"location":"operations/case_templates/#structure-of-templates","title":"Structure of templates","text":"The following defines the structure of a case template:
{\n\"name\": \"ransomware_infection\",\n\"display_name\": \"Ransomware Infection Template\",\n\"description\": \"This case template describes first-response tasks to handle information system compromised by a ransomware.\",\n\"author\": \"DFIR-IRIS\",\n\"classification\": \"malicious-code:ransomware\",\n\"title_prefix\": \"[RANS]\",\n\"summary\": \"# Context \\n\\n\\n# Contact \\n\\n\\n# Actions \\n\\n\\n\",\n\"tags\": [\"ransomware\",\"malware\"],\n\"tasks\": [\n{\n\"title\": \"Identify the perimeter\",\n\"description\": \"The perimeter of compromise must be identified\",\n\"tags\": [\"identify\"]\n},\n{\n\"title\": \"Collect compromised hosts\",\n\"description\": \"Deploy Velociraptor and start collecting evidence\",\n\"tags\": [\"collect\", \"velociraptor\"]\n},\n{\n\"title\": \"Containment\"\n}\n],\n\"note_groups\": [\n{\n\"title\": \"Identify\",\n\"notes\": [\n{\n\"title\": \"Identify the compromised accounts\",\n\"content\": \"# Observations\\n\\n\"\n}\n]\n},\n{\n\"title\": \"Collect\",\n\"notes\": [\n{\n\"title\": \"Velociraptor deployment\"\n},\n{\n\"title\": \"Assets collected\",\n\"content\": \"# Assets collected\\n\\n# Assets not collected\"\n}\n]\n}\n]\n}\n
"},{"location":"operations/case_templates/#using-case-templates","title":"Using case templates","text":"Case templates can be used when creating a new case. On the UI, when creating a case, select the case template to use in the Case template
dropdown. The case will then automatically use the informations defined in the template.
Since v2.0.0 the entire configuration is done through the .env
file at the root of the IRIS directory.
The default configuration is provided through a .env.model
file at the root of the IRIS directory. One need to copy this file to .env
and modify it if needed.
The default configuration is suitable for testing only. See the section below to configure IRIS for production.
"},{"location":"operations/configuration/#production-configuration","title":"Production configuration","text":""},{"location":"operations/configuration/#secrets","title":"Secrets","text":""},{"location":"operations/configuration/#required-changes","title":"Required changes","text":"The following secrets in the .env
need to be changed for production. We recommend using OpenSSL to generate different values from each secret: openssl rand -base64 64
POSTGRES_PASSWORD
: Password of the postgres userPOSTGRES_ADMIN_PASSWORD
: Password of the db admin user IRIS_SECRET_KEY
: Key used by Flask to secure the session cookiesIRIS_SECURITY_PASSWORD_SALT
: A salt used for password encryption in the DB Critical configuration
These settings are critical and need to be set properly otherwise authentication bypass may occur.
"},{"location":"operations/configuration/#optionnal-changes","title":"Optionnal changes","text":"To automate the provisionning of IRIS, one might need to set the default administrator API token and password. This can be achieve with the following environment variables. If those variables are not set, random ones are generated during the very first boot of the application.
Warning
The administrator password is printed in the logs. It is recommended to change it as soon as possible. The set of the following environment variables has no effect once the administrator account is created, i.e after the very first boot.
IRIS_ADM_PASSWORD
: Password of the administrator account. The password need to match the default password policy or the administrator won't be able to login, IRIS_ADM_API_KEY
: API key of the administrator. A random long string. No verification for the complexity is done. We recommend using openssl rand -base64 64
IRIS is configured to use a self-signed certificate by default. This is suitable for testing only. To use your own certificate, you need to set the following environment variables:
KEY_FILENAME
: The filename of the key file in the certificates/web_certificates
directory at the root of the IRIS directoryCERT_FILENAME
: The filename of the certificate file in the certificates/web_certificates
directory at the root of the IRIS directoryOnce the changes are done, nginx docker container need to be rebuilt with the following command:
docker-compose stop nginx\ndocker-compose build nginx --no-cache\ndocker-compose up
"},{"location":"operations/configuration/#authentication","title":"Authentication","text":""},{"location":"operations/configuration/#ldap","title":"LDAP","text":"IRIS can be configured to use LDAP authentication. See the Authentication section for more details.
"},{"location":"operations/configuration/#available-settings","title":"Available settings","text":"These environment variables are availabled to be set.
Key Section Opt DescriptionSERVER_NAME
Nginx No Passed to the server_name in NGINX configuration KEY_FILENAME
Nginx No SSL Cert key filename passed to the NGINX configuration CERT_FILENAME
Nginx No SSL Cert filename passed to the NGINX configuration INTERFACE_HTTPS_PORT
Nginx Yes Listening interface of IRIS POSTGRES_USER
DB No Name of the POSTGRES user POSTGRES_PASSWORD
DB No Password of the POSTGRES user POSTGRES_ADMIN_USER
DB No Name of the admin user POSTGRES_ADMIN_PASSWORD
DB No Password of the ADMIN user POSTGRES_DB
DB No Name of the DB used by IRIS POSTGRES_SERVER
DB No Hostname or IP of the DB POSTGRES_PORT
DB No Port of the DB server DOCKERIZED
IRIS Yes Set to 1
when using dockers (default) IRIS_SECRET_KEY
IRIS No Secret key used to secure sessions - needs to be random IRIS_SECURITY_PASSWORD_SALT
IRIS No Secret used to salt the passwords in DB - needs to be random IRIS_UPSTREAM_SERVER
IRIS No WebApp upstream server - used to configure nginx reverse proxy IRIS_UPSTREAM_PORT
IRIS No WebApp upstream server port - used to configure nginx reverse proxy IRIS_ORGANISATION_NAME
IRIS No Name of the company / organisation. Used on the UI IRIS_LOGIN_BANNER_TEXT
IRIS No Text displayed on the login page IRIS_LOGIN_PTFM_CONTACT
IRIS No Contact information displayed on the login page IRIS_UPLOADED_PATH
IRIS No Path to store uploaded data. IRIS_BACKUP_PATH
IRIS No Path to store backup files. IRIS_TEMPLATES_PATH
IRIS No Path of the templates IRIS_DATASTORE_PATH
IRIS No Path of the datastore files IRIS_DEMO_ENABLED
Demo No Set to True to switch IRIS to Demo mode IRIS_DEMO_DOMAIN
Demo No URL of the demonstration server IRIS_DEMO_USER_SEED
Demo No Random seed to generate demo users IRIS_DEMO_ADM_SEED
Demo No Random seed to generate admin users for demo CELERY_BROKER
Celery No Broker URL used to handle IRIS tasks IRIS_AUTHENTICATION_TYPE
Auth No IRIS auth mode : local
or ldap
IRIS_ADM_PASSWORD
Auth Yes Set to use as initial password of the administrator account. Only works for the very first run of IRIS. Needs to match the password policy IRIS_ADM_API_KEY
Auth Yes Set to use as initial API Key of the administrator IRIS_ADM_EMAIL
Auth Yes Set to use as initial email of the administrator IRIS_ADM_USERNAME
Auth Yes Set to use as initial username of the administrator LDAP_SERVER
Auth Yes LDAP server IP or domain LDAP_PORT
Auth Yes LDAP server port LDAP_USER_PREFIX
Auth Yes Prefix to search the users within LDAP_USER_SUFFIX
Auth Yes Suffix to search the users within LDAP_USE_SSL
Auth Yes Set to True to use LDAPS LDAP_VALIDATE_CERTIFICATE
Auth Yes Set to True to verify the server certificate validity LDAP_TLS_VERSION
Auth Yes TLS version to use LDAPS LDAP_SERVER_CERTIFICATE
Auth Yes Path of the LDAP server certificate LDAP_PRIVATE_KEY
Auth Yes Path of the LDAP private certificate LDAP_PRIVATE_KEY_PASSWORD
Auth Yes Password of the private key LDAP_AUTHENTICATION_TYPE
Auth Yes Simple, SASL or NTLM LDAP_CUSTOM_TLS_CONFIG
Auth Yes If set to true, the TLS configuration is not set by IRIS and taken from the defined environment. Default to False"},{"location":"operations/custom_attributes/","title":"Custom Attributes","text":"Introduced in IRIS v1.4.0
All the case objects can be extended with custom attributes. These attributes can be added by :
VT Report
attribute to each objects it analyses)Attributes offer the ability to :
This section only describes how an administrator can add or delete attributes to an object.
Tip
We have publish a detailed article of custom attributes with advanced usage on our blog.
"},{"location":"operations/custom_attributes/#management-page","title":"Management page","text":"Custom attributes can be changed in the Advanced
> Custom Attributes
section on the left panel.
The page lists the objects for which custom attributes can be added or modified.
Attributes are defined in JSON which describes tabs and fields that makes the attributes.
{\n\"Tab Name 1\": { // Defines a new tab \n\"Field 1\": { // Defines a new field within the Tab Name 1\n\"type\": \"input_string\", // Defines the type of field, here a standard string input\n\"mandatory\": true, // Indicates whether the field is mandatory upon saving\n\"value\": \"\" // Default value if any, else empty\n},\n\"Field 2\": { // Defines a second field within the tab Tab Name 1\n\"type\": \"input_checkbox\", // Defines an input checkbox\n\"mandatory\": false, // Indicates whether the field is mandatory upon saving\n\"value\": false // Default value - must be set for booleans\n}\n},\n\"VT report\": { // Defines a second tab named VT report\n\"Content\": { // Defines a new field Content within the VT Report\n\"type\": \"html\", // Defines an HTML interpreted content\n\"value\": \"\" // Default value if any, else empty\n}\n}\n}\n
The code above would be rendered as :
With :
Tab Name 1
VT report
The available fields type are available for rendering :
input_string
: Standard input textinput_textfield
: Standard input textfieldinput_checkbox
: Standard checkboxinput_date
: Standard date inputinput_datetime
: Standard date and time inputinput_select
: Standard select input. Need \"options\" tag to describe the available options, as a list of string. raw
: A static content rendered in raw text. HTML is not be interpreted.html
: A static content rendered as HTML. This is by nature prone to abuse, but at the same time allows adding custom JS scripts. When an attribute is updated, it will try to update all the existing objects with the new attributes. To prevent any data loss from previous attributes and attributes pushed by modules, the update is only made on attributes which don't have any values set or are type-compatibles (ie string to textfield).
The migration of an attribute can however be forced in two ways, both resulting in potential attributes data loss.
Good to know
Migrating or overwriting attributes never change the native information of an object. It only applies to custom attributes.
Partial overwrite
basically resets all the values of every target objects that matches the current attribute definition. All associated values are lost. This does not impact attributes pushed by modules or previous configuration.
Complete overwrite
resets all attributes of every target objects, including the ones created by modules, and then applies the current attributes. All associated values are lost.
Custom attributes can be more complex than what presented above. With the html
type, it is possible to build almost anything. Below is an example of the custom attributes used in the IrisVT module. The {{ }}
are used withing the module to generates the page with data received from VT.
Note : This example won't work as is, the value field is expanded here for reability.
IrisVT default custom attribute{\n\"VT report\": { \"Content\": { \"type\": \"html\", \"value\": \"<div class='row'>\n <div class='col-12'>\n <h3>Basic information</h3>\n <dl class='row'>\n {% if results.as_owner %}\n <dt class='col-sm-3'>AS owner</dt>\n <dd class='col-sm-9'>{{ results.as_owner }}</dd>\n {% endif %}\n {% if country %}\n <dt class='col-sm-3'>Country</dt>\n <dd class='col-sm-9'>{{ results.country }}</dd>\n {% endif %}\n </dl>\n </div>\n </div> \n {% if nb_detected_urls %}\n <div class='row'>\n <div class='col-12'>\n <h3>Detected URLS</h3>\n <dl class='row'>\n <dt class='col-sm-3'>Total detected URLs</dt>\n <dd class='col-sm-9'>{{ nb_detected_urls }}</dd>\n <dt class='col-sm-3'>Average detection ratio</dt>\n <dd class='col-sm-9'>{{ avg_urls_detect_ratio }}</dd>\n </dl>\n </div>\n </div> \n {% endif %}\n {% if nb_detected_samples %}\n <div class='row'>\n <div class='col-12'>\n <h3>Detected communicating samples</h3>\n <dl class='row'>\n <dt class='col-sm-3'>Total detected samples</dt>\n <dd class='col-sm-9'>{{ nb_detected_samples }}</dd>\n <dt class='col-sm-3'>Average detection ratio</dt>\n <dd class='col-sm-9'>{{ avg_samples_detect_ratio }}</dd>\n </dl>\n </div>\n </div> \n {% endif %}\n <div class='row'>\n <div class='col-12'>\n <div class='accordion'>\n <h3>Additional information</h3>\n {% if results.resolutions %}\n <div class='card'>\n <div class='card-header collapsed' id='drop_res' data-toggle='collapse' data-target='#drop_resolutions' aria-expanded='false' aria-controls='drop_resolutions' role='button'>\n <div class='span-icon'>\n <div class='flaticon-file'></div>\n </div>\n <div class='span-title'>\n Resolutions history\n </div>\n <div class='span-mode'></div>\n </div>\n <div id='drop_resolutions' class='collapse' aria-labelledby='drop_res' style=''>\n <div class='card-body'>\n <ul>\n {% for resolution in results.resolutions %} \n <li>{{ resolution.hostname }} ( Last resolved on {{resolution.last_resolved}} )</li>\n {% endfor %}\n </ul>\n </div>\n </div>\n </div>\n {% endif %}\n </div>\n </div>\n </div>\n <div class='row'>\n <div class='col-12'>\n <div class='accordion'>\n <h3>Raw report</h3>\n <div class='card'>\n <div class='card-header collapsed' id='drop_r' data-toggle='collapse' data-target='#drop_raw' aria-expanded='false' aria-controls='drop_raw' role='button'>\n <div class='span-icon'>\n <div class='flaticon-file'></div>\n </div>\n <div class='span-title'>\n Raw report\n </div>\n <div class='span-mode'></div>\n </div>\n <div id='drop_raw' class='collapse' aria-labelledby='drop_r' style=''>\n <div class='card-body'>\n <div id='vt_raw_ace'>{{ results| tojson(indent=4) }}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div> \n <script>\n var vt_in_raw = ace.edit('vt_raw_ace',\n {\n autoScrollEditorIntoView: true,\n minLines: 30,\n });\n vt_in_raw.setReadOnly(true);\n vt_in_raw.setTheme('ace/theme/tomorrow');\n vt_in_raw.session.setMode('ace/mode/json');\n vt_in_raw.renderer.setShowGutter(true);\n vt_in_raw.setOption('showLineNumbers', true);\n vt_in_raw.setOption('showPrintMargin', false);\n vt_in_raw.setOption('displayIndentGuides', true);\n vt_in_raw.setOption('maxLines', 'Infinity');\n vt_in_raw.session.setUseWrapMode(true);\n vt_in_raw.setOption('indentedSoftWrap', true);\n vt_in_raw.renderer.setScrollMargin(8, 5);\n </script>\" }\n}\n}\n
"},{"location":"operations/datastore/","title":"Datastore","text":"Introduced in IRIS v1.4.5
The datastore offers a way to store files directly in the context of a case. Documents, IOCs, evidences, etc, anything can be uploaded and managed through IRIS.
"},{"location":"operations/datastore/#accessing-the-datastore","title":"Accessing the datastore","text":"The datastore can be accessed from any page with the top-right shortcut.
A new pane will open, with a default folder tree created for the case.
"},{"location":"operations/datastore/#files","title":"Files","text":""},{"location":"operations/datastore/#adding-a-file","title":"Adding a file","text":"To add a file to a folder, press the +
button near a folder where you want to place it, and select Add file
.
A new window appear and allows to upload the file. The following fields are available :
infected
unless specified otherwise in this field,infected
unless specified otherwise in the password field. The file is also added to the IOC tab of the case, A file can be both IOC and Evidence, in which case it is handled as an IOC and also added to the Evidence tab of the case.
Depending on the file size, the upload might take some time. We are aware that the Nginx introduces a delay compared to a direct upload. We have not yet found a configuration that does not impact the speed upload.
Once uploaded, and depending on the options selected, the file appear in the target folder with specific icons. A mouse hover explains what each icon means.
"},{"location":"operations/datastore/#files-operations","title":"Files operations","text":"Once a file is added, a left-click on it shows a dropdown with multiple options.
Batch operations such as moving and deleting are also available by clicking on Select
at the top right, and then selecting the files.
Images can now be directly pasted in notes an summary. Only images are supported. Once an image is pasted, the file is automatically uploaded in the datastore in the folder Notes Upload
and a link to the file is inserted.
Example of pasted image
The image is by default sized to 40%
. Changing the end of the link =SIZE%xSIZE%
allows to resize the image.
The file is now available in the DS and can be replaced if needed. The ID of the file is the one provided in the link, which can help finding out when names are updated.
Note
Under certain conditions (browser, version, OS) the image copy/paste cannot be done directly. This is a known issue, not directly linked to IRIS but related to how browsers handle files in clipboards. If you face this issue, try to open the image and copy it from there instead of the file manager. Otherwise you need to upload it via the Datastore and then get a link from it.
"},{"location":"operations/datastore/#folders","title":"Folders","text":""},{"location":"operations/datastore/#adding-a-folder","title":"Adding a folder","text":"To add folder, press the +
button near a folder where you want to place it, and select Add subfolder
.
A new window appear requesting the name of the folder to create. Validate and the new folder appears in the folder tree. Files can then be added to it.
"},{"location":"operations/datastore/#moving-folders","title":"Moving folders","text":"Folders can be moved within other folders. When doing so, every files and subfolders are also moved, like we are used to with files managers.
Click on the +
near the folder to move, and then select Move
. The target folder is underlined in blue. Select then the target folder which should appear underline in orange. Then validate the move.
The folder and all its children are moved in the target directory.
"},{"location":"operations/datastore/#searching","title":"Searching","text":"When dozens of files are added, the filtering bar can be used to quickly find a file. The filtering mechanism is similar to the one in the timeline.
The query schema is : target_element:search_value AND target_element2:search_value2
. There is no OR condition and searching without target does not work.
The following target elements can be used to filter :
Here a are a few concepts to better understand how the datastore is working.
Folders represented on the datastore are virtual and do not represent the folders on the system. This is to ensure smooth files operations. The files are never touched again (unless overwritten or deleted) once uploaded. When a file or directory is moved or renamed, only its parent references are updated.
Files are saved by GUID instead of their real names on the system. They are saved under the mapped volume /home/iris/server_data/datastore
by default. Then three directories are created :
Evidences
IOCs
Regulars
Within each of these, a new subdirectory with the case ID is created when a file is uploaded. This is ensure IOC, which can be harmful, are formally identified on the server itself. Files can be found on the system by looking up the Storage UUID
of the file (eg: dsf-f86926ec-513d-4e47-88fa-02110e7fb412
) in these directories.
All components of IRIS offers by-default logging in the docker instances. Depending on the OS of the hosts, the location of these logs may differ.
For Debian-based distributions, the logs are usually in /var/lib/docker/containers/
. The usually interesting logs in IRIS are the following:
iriswebapp_app
: Contains the logs of core of IRIS, including major stack traces and access control output iriswebapp_worker
: Contains the logs of the worker and output of modulesiriswebapp_nginx
: Contains the logs of the reverse proxy. Every request to IRIS is logged there. Logs of IRIS can be forwarded to a SIEM for monitoring. Below is discussed how to setup Splunk forwarding. Other drivers are available and detailed on the docker website.
/etc/docker/daemon.json
and specify the following content: {\n\"log-driver\": \"splunk\",\n\"log-opts\": {\n\"splunk-insecureskipverify\": \"true\", \"splunk-index\": \"iris\",\n\"splunk-token\": \"YOUR HEC TOKEN\",\n\"splunk-url\": \"https://SPLUNK_SERVER:8088\"\n}\n}\n
systemctl reload docker
. The logs should appear in the Splunk instance.IRIS has the ability to generate reports based on the data of an investigation. The reports templates can be managed in Advanced
> Templates
.
Info
This section is only available for users with the Admin role.
There is two types of reports :
The following report formats are supported:
Reports templates are made of tags, which are then processed and filed by the template engine of IRIS. The templates can have any forms as soon as they respect the tags. We are providing two example of reports.
Info
The templates includes a few lines that describes how to handle styles. These should not be removed. They are be present in the generated reports and need to be removed manually.
"},{"location":"operations/reports/#available-tags","title":"Available tags","text":"The following tags are available. None are mandatory. If a tag is mistyped, the generation step will produce an error message.
Hint
Standard objects are accessible with {{ objectname }}
. List objects can be looped:
{% for object in object_list %}\n {{ object.attribute }}\n {% endfor %} \n
case.name
: Name of the casecase.description
: Description of the casecase.open_date
: Case open date case.close_date
: Case close date case.opened_by
: User that initially opened the case case.for_customer
: Customer linked to the case case.soc_id
: SOC ID number linked to the case evidences
: List of evidence objects (see below - given evidence
as loop variable)
evidence.filename
: File name of the evidence evidence.date_added
: Date of registration evidence.file_hash
: Hash of the evidence evidence.added_by
: User who added the evidenceevidence.custom_attributes
: Custom attributes of the evidenceiocs
: List of IOCs objects (see below - given ioc
as loop variable)
ioc.ioc_value
: Value of the IOC ioc.ioc_description
: Description of the IOCioc.ioc_type
: Type of IOC ioc.ioc_tags
: Tags linked to the IOC ioc.custom_attributes
: Custom attributes of the IOCnotes
: List of notes objects (see below - given note
as loop variable)
note.note_title
: Title of the note note.note_content
: Content of the note note.note_creationdate
: Creation date of the note note.note_lastupdate
: Date of last update note.custom_attributes
: Custom attributes of the notetasks
: List of tasks objects (see below - given task
as loop variable)
task.task_title
: Title of the task task.task_description
: Description of the task task.task_open_date
: Open date of the task task.task_last_update
: Last update of the task task.task_close_date
: Date of closure task.task_status
: Status of the task task.task_tags
: Task for the tags task.custom_attributes
: Custom attributes of the tasktimeline
: List of events objects (see below - given event
as loop variable)
event.event_title
: Title of the event event.event_content
: Content of the event event.event_raw
: Raw content of the event event.event_date
: Date when the event happened event.event_source
: Source of the event event.category
: Category of the event event.event_tags
: Tags of the events event.last_edited_by
: User who last edited the event event.assets
: List of assets names linked to the eventevent.custom_attributes
: Custom attributes of the eventWe are providing two example of full reports.
The following snippets aimed to be placed directly in the DOCX documents.
"},{"location":"operations/reports/#loops-and-tables","title":"Loops and tables","text":""},{"location":"operations/reports/#standard-loops","title":"Standard loops","text":"A loop needs to be used for list objects. Loop on IOC example
The IOCs of this case are : \n\n{% for ioc in case.iocs %}\n - {{ ioc.ioc_value }}\n - {{ ioc.ioc_description }}\n{% endfor %}\n
"},{"location":"operations/reports/#table-loops","title":"Table loops","text":"To use a loop in a table, a tr
tag needs to be added to the loop and the loop directly integrated in the table. Loop on IOC table example
The IOCs of this case are in the following table : \n\n{%tr for ioc in case.iocs %}\n {{ ioc.ioc_value }}\n {{ ioc.type_name }}\n {{ ioc.ioc_description }}\n{%tr endfor %}\n
Such as : "},{"location":"operations/reports/#nested-loops","title":"Nested loops","text":"Loops can be nested. Don't forget to close each loop.
Nested loop{%for ioc in case.iocs %}\n\n Custom attributes of {{ ioc.ioc_value }} :\n\n {% for attribute in ioc.custom_attributes %}\n\n - {{ attribute }}\n\n {% endfor %}\n\n{% endfor %}\n
"},{"location":"operations/reports/#conditions","title":"Conditions","text":""},{"location":"operations/reports/#standard","title":"Standard","text":"Check if asset is compromised{% for asset in assets %} \n\n {% if asset.compromised %}\n Asset {{ asset.asset_name }} is compromised\n {% endif %}\n\n{% endfor %}\n
"},{"location":"operations/reports/#list-is-not-empty","title":"List is not empty","text":"To check if a list of objects is not empty, use the processor tag count
.
{% if assets|count %} \n The case has assets\n{% endif %}\n
"},{"location":"operations/reports/#markdown-handling","title":"Markdown handling","text":"The case summary and notes are in markdown. A processor tag should thus be added |markdown
. Summary as markdown
This is an example of summary : \n\n{{ case.description|markdown }}\n
Loop over notesThis is an example of recursive notes : \n\n{% for note in case.notes %}\n\n My note named {{ note.note_title }} : \n {{ note.note_content|markdown }}\n\n{% endfor %}\n
"},{"location":"operations/reports/#troubleshoot","title":"Troubleshoot","text":"Most of the time an error of generation is due to misspelled tag or a missing closing tag ({% endfor %}
, {% endif %}
, etc). In case you cannot figure out what is going wrong, don't hesitate to reach us on Discord.
Some basic settings can be set in the section Advanced
> Server settings
.
Behavior :
Prevent post-init step to register default modules again during boot
: By default if a module is deleted and the server is restart, the module will be registered again. Setting this will prevent this behavior. Prevent post-init step to register default case objects again during boot
: By default if case objects are deleted and the server is restart, the case objects will be registered again. Setting this will prevent this behavior. Password policy : the password policy can be changed and is applied for the new users and next changes of users password
The tutorials have been discared as we now provide a free demonstration instance on v200.beta.dfir-iris.org. Should you need more information or assistance to use IRIS, you can contact us here.
"},{"location":"operations/upgrades/","title":"Upgrades","text":"Most of the time, Iris handles upgrades of the database automatically when a new version is started, thus no specific actions are required. However, some breaking changes might need manual intervention. Please use the selectors below to assess if a manual action is required.
Your current version: --Please choose current version-- v1.2.1 v1.3.0 v1.3.1 v1.4.0 v1.4.1 v1.4.2 v1.4.3 v1.4.4 v1.4.5 v2.0.0 v2.0.1 v2.0.2 v2.1.0 v2.2.0 v2.2.1 v2.2.2 v2.2.3 v2.3.0 v2.3.1 v2.3.2 v2.3.3 v2.3.4 v2.3.5 v2.3.6 v2.3.7
Upgrading to: --Please choose target version-- v1.2.1 v1.3.0 v1.3.1 v1.4.0 v1.4.1 v1.4.2 v1.4.3 v1.4.4 v1.4.5 v2.0.0 v2.0.1 v2.0.2 v2.1.0 v2.2.0 v2.2.1 v2.2.2 v2.2.3 v2.3.0 v2.3.1 v2.3.2 v2.3.3 v2.3.4 v2.3.5 v2.3.6 v2.3.7
Check upgrades conditions
For production environments, it is highly recommended to make backups of the DB in case any issues occur during upgrades.
"},{"location":"operations/upgrades/#backing-up-db","title":"Backing-up DB","text":"Only if you run in production and/or data is critical.
docker container list
docker exec <container> pg_dump -U postgres iris_db | \\ \ngzip > ../iris_db_backup.gz\n
zcat ../iris_db_backup.gz | less
"},{"location":"operations/upgrades/#upgrading","title":"Upgrading","text":"Stop the dockers
docker-compose stop\n
Remove the application dockers
docker-compose rm app worker\n
Get the last version of Iris
git checkout <last_tagged_version>\n
eg git checkout v2.3.6
Build the new versions
docker-compose build --no-cache app worker\n
Run IRIS again. The app will handle the DB migration automatically.
docker-compose up\n
In case something went wrong, you can rollback to your previous version and restore data.
docker-compose down db --volumes
docker-compose build --no-cache
docker-compose up db
docker container list
zcat ../iris_db_backup.gz | docker exec -i <container> psql -U postgres -d iris_db
docker-compose up
\u2757 The layout of the reporting has slightly changed. Custom report templates might not work anymore. You can use https://<server>/case/export?cid=<case_id>
to get all the possible fields.
No other impact is to be expected.
"},{"location":"operations/upgrades/#v210","title":"v2.1.0","text":"The default location of the SSL certificates have been changed from dockers/nginx/dev_certs
to certificates/nginx/web_certificates
. The docker-compose.yml
has thus been updated to mount this volume on the nginx Docker.
Except these changes, users in v2.0.x can upgrade to v2.1.0 without any manual intervention. Users in v1.4.x need to follow the v2.0.0 upgrade instructions before upgrading to v2.1.0.
"},{"location":"operations/upgrades/#v200","title":"v2.0.0","text":""},{"location":"operations/upgrades/#breaking-changes","title":"Breaking changes","text":"This version brings breaking changes on the following:
.env
configurationWarning
Custom made modules need to be upgraded to IRIS Module Interface v1.2.0. Please see modules upgrade for v2.0.0
"},{"location":"operations/upgrades/#instance-migration","title":"Instance migration","text":"To migrate an instance from v1.4.5, one can use the script in upgrades/upgrade_to_2.0.0.py
located in the repository. These commands needs to be run from the root of the repository (pwd
should return something like /iris-web
):
# Pull the lastest version \ngit pull # Checkout to iris v2.0.0\ngit checkout v2.0.0 # Check if upgrades possible\npython3 upgrades/upgrade_to_2.0.0.py --check\n\n# Run the upgrade\npython3 upgrades/upgrade_to_2.0.0.py --install\n
The script will take care of migrating the environment variables to reflect the changes in v2.0.0. Please review the .env
file afterward.
The port have been changed 443. The script asks if the previous port should be kept or migrated to the new one.
Once validated, one can proceed with the usual upgrade methodology.
docker-compose stop \ndocker-compose build --no-cache \ndocker-compose up -d\n
"},{"location":"operations/upgrades/#v200-modules-upgrades","title":"v2.0.0 modules upgrades","text":"This only concerns custom modules not shipped with IRIS Web App. The IRIS module interface has been upgraded to v1.2.0. No breaking changes are associated. One need to change the iris_module_interface
dependency to 1.2.0 in the requirements and rebuild the module.
The client has been updated to reflect the latest changes of the API. It also integrates features that were missing previously, such as Datastore Management. Some methods have been deprecated and some other modified. The easiest way to upgrade is to increase the version in the requirements and test. Each deprecated method will produce a warning or raise an exception.
"},{"location":"operations/upgrades/#v145","title":"v1.4.5","text":"If you are coming from IRIS <= v1.3.1 please read this. Changes have been made to the NGINX docker to allow upload of big files for the datastore. It is hence necessary to also rebuild the NGINX docker this time.
docker-compose stop
docker-compose rm app worker
git checkout <last_tagged_version>
- eg git checkout v1.4.5
docker-compose build --no-cache app worker nginx
docker-compose up
This only applies if you are coming from IRIS <= v1.3.1.
This version brings breaking changes in the DB docker by adding a named volume instead of the default one. This implies that previous existing database is ignored as the new docker won't know which volume was previously used. To prevent this, please strictly follow the guide below. This will copy the data of the existing volume, to the new named one.
docker container list
. It should look like iris-web-db-x
name
field with the command below)docker inspect <iris_db> | grep -A5 \"Mounts\"\n# Example of output\n\"Mounts\": [\n{\n\"Type\": \"volume\",\n \"Name\": \"a90b9998a3233a68438c8e099bd0ba98d9f62c9734e40297b8067f9fdb921eb9\",\n \"Source\": \"/var/lib/docker/volumes/a90b9998a3233a68438c8e099bd0ba98d9f62c9734e40297b8067f9fdb921eb9/_data\",\n \"Destination\": \"/var/lib/postgresql/data\",\n
3. Stop all the IRIS dockers : docker-compose stop
4. Create a new empty volume : docker volume create --name iris-web_db_data
5. Run a volume copy via a dummy image : docker run --rm -it -v <previous_db_volume_id>:/from:ro -v iris-web_db_data:/to alpine ash -c \"cd /from ; cp -av . /to\"\n# With the example of 2., this gives \ndocker run --rm -it -v a90b9998a3233a68438c8e099bd0ba98d9f62c9734e40297b8067f9fdb921eb9:/from:ro -v iris-web_db_data:/to alpine ash -c \"cd /from ; cp -av . /to\"\n
6. Pull the last changes from the repository, checkout to v1.4.4
, build and run. git pull origin git checkout v1.4.4\ndocker-compose build docker-compose up
7. The data should be successfully transferred. Do not forget to clear out your browser cache, many JS files were changed.
"},{"location":"operations/upgrades/#v143","title":"v1.4.3","text":"A patch exists for this version. Please directly upgrade to v1.4.4
"},{"location":"operations/upgrades/#v142","title":"v1.4.2","text":"A patch exists for this version. Please directly upgrade to v1.4.4
"},{"location":"operations/upgrades/#v141","title":"v1.4.1","text":"A patch exists for this version. Please directly upgrade to v1.4.4
"},{"location":"operations/upgrades/#v140","title":"v1.4.0","text":"A patch exists for this version. Please directly upgrade to v1.4.4
"},{"location":"operations/access_control/","title":"Access control","text":"Introduced in v2.0.0
IRIS offers a granular access control for cases and management features. Two types of access control are available :
To ease the access control, users can be managed in :
A user can be in one or multiple groups. The effective case access control of a user is deduced from its groups membership and its own cases access control. The effective permissions are deduced from its groups membership.
"},{"location":"operations/access_control/#cases-access-control-overview","title":"Cases access control overview","text":"Cases access control offer three levels:
deny_all
: No access to the case. The user don't even see the case listed, read_only
: Read-only access to the case. The user can see everything related to the case but cannot edit,full_access
: Read-write access to the case. The user can see and edit everything related to the case, including closing and deleting the case. As mentioned above, cases access control can be applied to groups and users. It starts with the groups and ends with atomic user access control.
For example, the following configuration gives no access to the user since the atomic user access prevail upon the rest.
stateDiagram-v2\n DefaultPermission --> Group_prevail: FullAccess\n\n state join_groups <<join>>\n Group1 --> join_groups: ReadOnly\n Group2 --> join_groups: DenyAll\n join_groups --> Group_prevail: ReadOnly\n Group_prevail --> Effective_DenyAll: ReadOnly \n\n Effective_DenyAll: Resulting Deny All access for user\n User_Access --> Effective_DenyAll: DenyAll
In the next configuration, the user has Read Only access to the case because the atomic user access is not set, so the access is inherited from the group ownership.
stateDiagram-v2\n state join_groups <<join>>\n Group1 --> join_groups: ReadOnly\n Group2 --> join_groups: DenyAll\n Group3 --> join_groups: DenyAll\n join_groups --> Group_prevail: ReadOnly\n\n Effective_ReadOnly: Resulting Read Only access for user\n Group_prevail --> Effective_ReadOnly: ReadOnly \n\n User_Access --> Effective_ReadOnly: Unset
This notably allows to create groups which can join a set of people from different organisations to work on the same case.
"},{"location":"operations/access_control/#permissions-control","title":"Permissions control","text":"Permissions allow to control the access to specific management features on the platform (adding users, cases etc.). Two permissions are available:
standard_user
: which includes: creating cases, searching across cases, read activity feed and read DIM tasksserver_administrator
: which includes all the permissions of standard_user
as well as managing users, modules, customers, case objects, custom attributes, report templates and server settings. IRIS supports local and LDAP authentication. In both cases, users need to be declared in IRIS.
"},{"location":"operations/access_control/authentication/#local-authentication","title":"Local authentication","text":"Local authentication is the default setting. The password is validated against the local IRIS database. Passwords are stored salted and hashed, it is thus not possible to retrieve them in case they are lost. It is however possible to change them.
"},{"location":"operations/access_control/authentication/#changing-a-lost-password","title":"Changing a lost password","text":"If another administrative user exists : Being logged as this user, head to the Advanced
> Access Control
> Users
section, and change the administrator password.
If no other administrative user exists : the change cannot be done via IRIS and an access to the backend is needed.
Danger
Do not delete and recreate any users directly from the DB! This will create inconsistencies in the relations and certainly corrupt everything.
Generate the hash of the new password with Python BCrypt in Python prompt
import bcrypt\nprint(bcrypt.hashpw('<new_password>'.encode('utf-8'), bcrypt.gensalt()))\n
Connect to the DB docker then the Postgresql database iris_db
and update the password
docker exec -ti <db_docker_id> /bin/bash\n/ # su postgres\n/ # psql\npostgres=# \\c iris_db \npostgres=# UPDATE \"user\" SET password = '<hash>' WHERE \"user\".name = 'administrator';\npostgres=# \\q\nexit\nexit\n
LDAP authentication rely on a LDAP server to verify the password of a user. The user needs to be declared in IRIS.
graph LR\n A[User] -->|Authenticate| B(IRIS WebApp)\n B --> C{User exists in DB?}\n C -->|Yes| D{LDAP accepted password?}\n C -->|No| E[Authentication failed]\n D -->|Yes| F[Authentication succeeded]\n D -->|No| E[Authentication failed]
"},{"location":"operations/access_control/authentication/#settings","title":"Settings","text":"The LDAP settings are present in the .env
. Once the LDAP server information is set, reboot the Iris WebApp docker needs to be restarted.
docker-compose restart app\n
"},{"location":"operations/access_control/authentication/#setting-up-ldap-for-the-first-runtime-of-iris","title":"Setting up LDAP for the first runtime of IRIS","text":"To set up LDAP without having run IRIS priorly, and as the app needs the accounts to be created first before using LDAP, one has to set the IRIS_ADM_EMAIL
environment with the LDAP Email of the administrator user.
IRIS_AUTHENTICATION_TYPE=ldap\n\n## IP address or FQDN of the ldap server\nLDAP_SERVER=dc1.domain.local\n\n## Port of the LDAP server\nLDAP_PORT=636\n## LDAP Authentication type\nLDAP_AUTHENTICATION_TYPE=SIMPLE\n\n## Prefix to search the users within \nLDAP_USER_PREFIX=uid=\n## Suffix to search the users within\nLDAP_USER_SUFFIX=ou=people,dc=example,dc=com\n\n## Set to True to use LDAPS\nLDAP_USE_SSL=True\n\n## Set to True to verify the server certificate validity\nLDAP_VALIDATE_CERTIFICATE=True\n\n## TLS version to use LDAPS\nLDAP_TLS_VERSION=1.2\n\n## LDAP TLS configuration \nLDAP_CUSTOM_TLS_CONFIG=False\n\n# Set email address of the first user, that will be the admin \nIRIS_ADM_EMAIL=adm@example.com
"},{"location":"operations/access_control/authentication/#setting-up-for-active-directory","title":"Setting up for Active Directory","text":"To use LDAP with an Active Directory, the following settings can be used:
Example of LDAP configuration for first run with Active DirectoryIRIS_AUTHENTICATION_TYPE=ldap\n\n## IP address or FQDN of the ldap server\nLDAP_SERVER=dc1.domain.local\n\n## Port of the LDAP server\nLDAP_PORT=636\n## LDAP Authentication type\nLDAP_AUTHENTICATION_TYPE=SIMPLE\n\n## Prefix to search the users within\nLDAP_USER_PREFIX=DOMAIN\\\n## Suffix to search the users within\nLDAP_USER_SUFFIX=\n## Set to True to verify the server certificate validity\nLDAP_VALIDATE_CERTIFICATE=True\n\n## TLS version to use LDAPS\nLDAP_TLS_VERSION=1.2\n\n## LDAP TLS configuration \nLDAP_CUSTOM_TLS_CONFIG=False\n\n# Set email address of the first user, that will be the admin\nIRIS_ADM_EMAIL=adm@example.com
"},{"location":"operations/access_control/authentication/#setting-up-ldap-after-iris-already-ran","title":"Setting up LDAP after IRIS already ran","text":"To set up LDAP after IRIS was already run, one only needs to set up the settings described previously without # Set email address of admin IRIS_ADM_EMAIL=adm@example.com
and restart the docker.
Usernames in IRIS have to match the ones set in LDAP for the authentication to succeed.
"},{"location":"operations/access_control/authentication/#ldap-certificates","title":"LDAP certificates","text":"If the LDAP server uses a self-signed certificate, it is possible to add it to the trusted certificates of the IRIS WebApp docker.
certificates/ldap
folder of the IRIS root directory.LDAP_VALIDATE_CERTIFICATE
environment variable to True
in the .env
file.LDAP_CUSTOM_TLS_CONFIG
environment variable to False
in the .env
file.LDAP_CA_CERTIFICATE
environment variable certificate path used by the LDAP server in the .env
file.If the LDAP server requires a client certificate, it is possible to add it to the trusted certificates of the IRIS WebApp docker.
certificates/ldap
folder of the IRIS root directory.LDAP_VALIDATE_CERTIFICATE
environment variable to True
in the .env
file.LDAP_CUSTOM_TLS_CONFIG
environment variable to True
in the .env
file.LDAP_PRIVATE_KEY
environment to the file name of the key in the .env
file LDAP_PRIVATE_KEY_PASSWORD
environment variable to the password of the key in the .env
file - if needed Groups offer the possibility to set case access as well as permissions. By default two groups are created:
Administrator
: users in this group hold the server_administrator
permission, Analysts
: users in this group hold the standard_user
permission Both groups are set to give full cases access to the users.
"},{"location":"operations/access_control/groups/#setting-up-a-new-group","title":"Setting up a new group","text":"Head to the Access Control page and click Add group
.
Fill the form. All the fields can be changed later on. The field Group name
has to be unique on the IRIS instance. Access control and members can be set once the group is created.
The group can be configured once created by clicking on it in the list.
"},{"location":"operations/access_control/groups/#users","title":"Users","text":""},{"location":"operations/access_control/groups/#adding-users-to-a-group","title":"Adding users to a group","text":"To add users to the group, go to the Members
tab and click Manage
.
The User Manager
should load and offers a list of users that can be added to the group. Select all the users you want to add to the group and press Save
.
Permissions computation
When a user is added/removed to a group, its effective cases access are recomputed. Depending on the number of cases and users added/removed this can take some time. This process helps reducing the DB load when using IRIS during normal operation.
"},{"location":"operations/access_control/groups/#removing-users-from-a-group","title":"Removing users from a group","text":"To remove users from the group, go to the Members
tab and click Manage
.
The User Manager
should load and present a list of both users already in the group as well as the ones that can be added. To remove one or more users, un-tick them from the list and press Save
.
Alternatively, a user can be directly removed from within the group manager. Click on the red trash next to the user to remove and confirm the deletion.
"},{"location":"operations/access_control/groups/#cases","title":"Cases","text":""},{"location":"operations/access_control/groups/#adding-cases-to-the-group","title":"Adding cases to the group","text":"Access to one or multiple existing cases can be granted to a group. From within the Group Manager
, go to the Cases Access
tab and click Set case access
.
The Cases Access Manager
loads and gives the possibility to set the access to one or more cases.
Three choices of access are offered:
deny_all
: No access at all to the case. The users won't even see the case listed, read_only
: Read-only access to the case. The users can see everything related to the case(s) but cannot change anything,full_access
: Read-Write access to the case. The users can see and change everything related to the case. Once the desired access is selected, press Set access
.
Permissions computation
As for the addition of users, when a case is added/removed to a group, all the users' effective cases access are recomputed. Depending on the amount of cases added/removed and number of users this can take some time. This process helps reducing the DB load when using IRIS during normal operation.
"},{"location":"operations/access_control/groups/#removing-cases-from-the-group","title":"Removing cases from the group","text":"From within the Group Manager
, go to the Cases Access
tab. Click on the red trash next to the case to remove and confirm the deletion.
A group can be deleted by clicking on its name in the list and then Delete
at the bottom of the Info
tab.
Authentication
Looking for authentication settings? It's here
Whatever the authentication mechanism used (Local or LDAP), the users have to be declared in IRIS. This is done in Advanced
> Access Control
> Users
.
Head to Advanced
> Access Control
> Users
and click Add user
. All fields of the form are required. All information can be changed after the creation.
Note
Permissions and groups can be set once the user is created.
Advanced
> Server settings
. The password also has to be set when using LDAP, it is then however not used for the authentication. A random password can be set when using LDAP. No password is required when the user is set as a service account. Service accounts
Service accounts users can use the API to perform any actions on the instance. They cannot login to the UI and they don't have a password.
"},{"location":"operations/access_control/users/#editing-a-user","title":"Editing a user","text":"
A user can be edited by clicking on its name or ID in Advanced
> Access Control
> Users
. A window opens and display the user's information. Tabs at the top allow to configure multiple settings related to the user.
Permissions of a user cannot be set directly. They are inherited from the groups membership. The tab Permissions
only displays the permissions the user has from its groups memberships. See Groups for more info.
Groups can be set by clicking on the Groups
tab of the user's window and then Manage
.
A new window appears with the possibility to select the groups the user should belong to.
After saving, the permissions of the user are updated. This can be verified in the Permissions
tab.
Cases access are usually set through groups membership. However for granularity they can be set per user. To set the access of a user on a case, click on the Cases access
tab of the user's window and then Set case access
.
As for the Groups, a selector appears and allows to select one or multiple cases and the access to associate.
Info
Application of a case access is immediate, even if the user is logged in and browsing the case.
Info
Granular case access can also be set from a case itself, in Summary
> Manage
> Access
.
A user can be deactivated, which has the following impact:
"},{"location":"operations/access_control/users/#deleting-a-user","title":"Deleting a user","text":"
It is usually not possible to delete a user. This is to keep consistency in the database. A user can be deleted if it has done absolutely no actions on the platform. If a user leaves the organisation, it is recommended to rename the user and deactivate it.
"},{"location":"operations/cases/case_operations/","title":"Case operations","text":""},{"location":"operations/cases/case_operations/#opening-a-case","title":"Opening a case","text":"
To open a case from the dashboard, press Add case
in the top right corner. Otherwise, go to the Manage Cases
tab and press Add case
in the top right corner.
A new window appears, requesting additional information. The following information are required:
The following information is optional:
Once Create
is clicked, the case is created and a popup ask whether to the get redirected to the case or to add a new one.
Each case has its own context. To switch between cases/context, either click on the name of the current case at the top left, or click on the switch button on the top right.
A popup appears and allows to select the case to switch to. By default the last 100 cases are displayed. To look further in the past, one can use the search bar. Press Save
to validate the switch. The page reloads with the new context.
A case metadata can be updated by going switching the case context and heading to Case
> Summary
. Clicking on Manage
brings up a new window with the case metadata. The right pencil button allows to edit the metadata.
The following information can be updated:
Open
. This defines the state of the case (open, closed, etc.). Unknown
. This defines if the case is a true positive, false positive, etc. Open
The access of a case can be updated by going to Case
> Summary
> Manage
, and from the popup, clicking on the Access
tab.
Changes of access are immediately applied.
"},{"location":"operations/cases/case_operations/#closing-a-case","title":"Closing a case","text":"A case can be closed by going to Case
> Summary
> Manage
, and from the popup, clicking on the Close case
button. Closing a case doesn't delete it and it can be reopened at any time. The case can also be modified after it has been closed.
The modifications history of a case can be retrieved by going to Case
> Summary
> Manage
, and from the popup, clicking on the history icon next to the case name.
IOCs are observables that were identified during the investigation, or that led to the case creation upon monitoring activities.
"},{"location":"operations/cases/iocs/#add-an-ioc","title":"Add an IoC","text":"An IoC object could be created by going to Case
> IOC
. Clicking on Add IOC
in the top right corner brings up a new window for the IoC creation.
A new window appears, requesting additional information. The following information is required:
Amber
by default)The following information is optional:
Once Save
is clicked, the ioc is created.
IoC object data can be updated by clicking on the IoC value in the Case
> IOC
table. A popup appears and allows to change required and non-required fields.
Once Update
is clicked, the IoC is updated.
IoC objects can be enriched in order to add valuable information to it.
"},{"location":"operations/cases/iocs/#comment-an-ioc","title":"Comment an IoC","text":"To comment an IoC, one can right click on it, in the Case
> IOC
menu, and select Comment
. A new pop-up appears and allows to leave comments. This is also achievable by clicking on the IoC value in the Case
> IOC
table, and by clicking on the Comment
button.
To have more information about modules, see the Modules section.
A set of modules can be launched to enrich IoCs. To do so, one can right click on the IoC , in the Case
> IOC
table, and select the module of choice. This is also achievable by clicking on the IoC value in the Case
> IOC
table, by clicking the Option
button, and selecting the desired module.
The results of the module will appear in newly created tabs, in the IoC details. To view the tabs, click on the the IoC value.
"},{"location":"operations/cases/iocs/#delete-an-ioc","title":"Delete an IoC","text":"This will permanently delete the IoC and its attributes
To delete an IoC, one could either right click on the IoC, and select Delete
, or click on the IoC value, and click on the Delete
button.
The IOC is only unlinked from the case if it references other cases
"},{"location":"operations/cases/notes/","title":"Notes","text":"IRIS allows analysts to add notes to cases. Notes can be added to a case from the Notes
tab. Notes are organized by groups. A note has to be within a group.
To add a group, click on the Add note group
button on the top right.
The title of the group note can be edited by clicking direclty on it and typing the new title. Validated with the green checkmark.
"},{"location":"operations/cases/notes/#adding-a-note","title":"Adding a note","text":"To add a note, click on the +
button on the top right of the target note group. A new note is added to the group. To edit it, click on the note and type the new content.
Tasks allow incident handlers to split the workload into unit tasks, and to assign them to the team members.
"},{"location":"operations/cases/tasks/#add-a-task","title":"Add a task","text":"A task can be created by going to Case
> Tasks
. Clicking on Add Task
in the top right corner brings up a new window for the task creation.
A new window appears, requesting additional information. The following information is required:
The following information is optional:
Once Save
is clicked, the task is created.
Task metadata can be updated by clicking on the task title in the Case
> Tasks
table. A popup appears and allows to change required and non-required fields.
Once Update
is clicked, the task is updated.
Tasks can be commented. This provide the ability for analysts to give more in-deepth information on the task execution (what did they do, how did they do, what are the results, etc.)
To comment a task, one can either right click on the task line and select Comment
, or click on the task title, and click on the Comment
button. A new windows appears and allows to add comments to the task.
Once Comment
is clicked, in the last window, a comment is added to the task. Comments are editable and removable.
New types of modules are introduced in IRIS v1.4.0
IRIS can be extended with modules. They can be split in two types:
Modules (or DIM - DFIR-IRIS Modules) are actually Python packages which must be installed in the Python environment of iris-webapp and the worker (see Quick Start). Once installed in the Python environment, modules can be managed in Advanced
> Modules
.
Info
This section is only available for users with the Admin role.
By default IRIS is shipped with multiple modules.
To add a module, the user can click on the \"+\" button:
Then the user must enter the name of the pre-installed module. The name of the pip package must be used.
If everything is ok, the module will appear on the list. It is currently disabled, and needs configuration before it can be enabled. To do so, the user can click on the module's name:
A new text box appears, showing information about the module, and a list of parameters to configure. Each mandatory parameter must be configured to enable the module.
After configuring all the mandatory parameters, the \"Enable button\" is revealed and the user can finally enable the module.
That's all! The user can confirm in the summary that the module is indeed enabled and ready to use.
Finally, the user can either disable or remove the module by clicking on the according buttons.
Now that the module is configured and enabled, let's see how we can use it!
NB: As a temporary fix, after adding and configuring a module, one must restart the IRIS services (dockers) else the worker won't have the module installed properly.
"},{"location":"operations/modules/mod_management/#how-to-use-the-module","title":"How to use the module","text":"As stated in the beginning, a module extends the capabilities of IRIS. For now, it allows importing evidences of your needs into what we call a pipeline, where data will be handled in the module (checking, parsing, ingestion...). In our provided module, IrisEVTXModule ingest EVTX files, parse them as JSON, and send the results to a Splunk instance using its HTTP event collector (HEC) endpoint.
In IRIS, the files are always imported in the context of a case. To import a file, the user can click on Manage cases
then Update
tab.
In Processing pipeline
, the user can pick a pipeline that will send the files to the wanted module. In our example, EVTX pipeline
refers to the IrisEVTXModule module. Below, the user can fill the arguments needed by the according pipeline. Arguments can be optional. Finally, the user can import one or several files and click Update
to start their processing by the module.
You can see in the picture below that the user will import four EVTX files.
The user can follow the upload of the different files with their respective progress bars.
Once uploaded, the status of the task can be observed on the DIM Tasks
page.
Clicking on a Task ID shows information on the task processing.
After the processing of the files by the module, the list of the imported files is stored in the Evidences
tab of the according case.
Introduced in IRIS v1.4.0
This module offers an interface with MISP and IRIS to automatically enrich IOCs with MISP insight.
The source code is available here. It is installed by default but needs to be configured to be enabled.
Note
The module is in its early stage and new features will be added over time.
"},{"location":"operations/modules/natives/IrisMISP/#features","title":"Features","text":"Two types of enrichement mecanism are proposed :
Get MISP insight
. This sends the targets IOCs to the module and insights will be fetched and applied. The following types of IOCs are handled by the module :
The insight request on an IOC not handled is simply ignored.
The insights take the form of attributes added to the IOC. They are based on configurable templates.
"},{"location":"operations/modules/natives/IrisMISP/#configuration","title":"Configuration","text":"The behavior of the module can be configured as needed. Head to the Advanced
> Modules
> IrisMISP
to change it.
At the time, only one MISP can be added. Future version might handled more than one MISP. The expected structure is the following :
{\n\"name\": \"Public_MISP\", \"type\":\"public\", \"url\":[\"https://testmisp\"],\n\"key\":[\"<apikey>\"], \"ssl\":[false]\n}\n
"},{"location":"operations/modules/natives/IrisVT/","title":"Module IRIS VT","text":"Introduced in IRIS v1.4.0
This module offers an interface with VirusTotal and IRIS to automatically enrich IOCs with VT insight. The source code is available here. It is installed by default but needs to be configured to be enabled.
"},{"location":"operations/modules/natives/IrisVT/#features","title":"Features","text":"Two types of enrichment mechanism are proposed :
Get VT insight
. This sends the targets IOCs to the module and insights will be fetched and applied. The following types of IOCs are handled by the module :
The insight request on an IOC not handled is simply ignored.
Two types of insights are proposed :
vt:malicious
or vt:suspicious
tag if the detection thresholds are met (configurable). For domains, an ASN tag can also be added. The behavior of the module can be configured as needed. Head to the Advanced
> Modules
> IrisVT
to change it.
vt:malicious
. To disable, add a value > 100. vt:suspicious
. To disable, add a value > 100. This module offers webhooks support for IRIS. It can be configured to send almost any events to to an external service supporting webhooks, such as Discord, Slack or Microsoft Teams. It can also be used with automation tools such as Tines and Shufle to further automate IRIS. The source code is available here.
"},{"location":"operations/modules/natives/IrisWebHooks/#features","title":"Features","text":"Slack Example
"},{"location":"operations/modules/natives/IrisWebHooks/#configuration","title":"Configuration","text":"The expected configuration is a JSON file, following the structure :
{ \"instance_url\": \"<IRIS_INSTANCE_URL>\",\n\"webhooks\": [\n{\n\"name\": \"Name of the webhook for internal reference only\",\n\"active\": false,\n\"trigger_on\": [<LIST OF HOOKS TO LISTEN TO>],\n\"request_url\": \"<URL OF THE WEBHOOK>\",\n\"use_rendering\": true,\n\"request_rendering\": \"<RENDERING TYPE OF THE MESSAGE>\", \"request_body\": {<BODY OF THE REQUET TO SEND>}\n},\n{\n\"name\": \"Another hook\",\n\"active\": false,\n\"use_rendering\": false,\n\"trigger_on\": [<LIST OF HOOKS TO LISTEN TO>],\n\"request_url\": \"<URL OF THE WEBHOOK 2>\",\n\"request_rendering\": \"<RENDERING TYPE OF THE MESSAGE>\", \"request_body\": {<BODY OF THE REQUEST TO SEND 2>}\n}\n]\n}\n
instance_url
: Base URL of IRIS. This is used to set the links in the messageswebhooks
: A list of JSON describing the webhooks For each webhook:name
: Internal name of the webhook, this can be anything active
: Optional - Set to false to disable the webhook trigger_on
: List of IRIS hooks for which the webhook should be triggered. Only the on_postload_XX
hooks are supported. To enable a set of hooks without writing them all, the following keywords can be used : all
: Includes all on_postload
hooks all_create
: Includes all on_postload_XX_create
hooksall_update
: Includes all on_postload_XX_update
hooksrequest_url
: The URL provided by the webhook receiver. For instance for Slack : see how to get onerequest_rendering
: URLs rendering may be specific from one receiver to another. The modules supports the following : markdown
: Format the message as markdown. This can be used with Discord for instance markdown_slack
: Format the message as markdown, with some specificities of Slack. html
: Format the message as HTML. request_body
: The request body to be sent to the webhook receiver. If use_rendering
is true, then two markups can be used to set the content of the webhook. The request has to be in JSON format and is sent as-is after replacements of the markups. %TITLE%
: Is replaced with name of the case and event title, e.g \"[#54 - Ransomware] IOC created\"%DESCRIPTION%
: Description of the event, e.g \"UserX created IOC mimi.exe in case #54\" If use_rendering
is false, then a raw json representation of the object related to the hook is available. See examples for more details. manual_trigger_name
: The name of the manual trigger in the UI. This should be set if the registered hook is of type on_manual_trigger
. This name is displayed as a new menu option in the UI for the target object. use_rendering
: Whether the data should be formated in Markdown or not. If set to false, then the request body field can use the raw data such as assets
. This will result in a request with the body containing the assets JSON representation related to the call of the hook. See examples for more details. Each time a webhook is added, the module subscribes to the specified hooks. After saving the configuration, one can check the registration was successful by filtering the Registered hooks table
(don't forget to refresh the table).
The following example is a combination of webhooks that can be used to further automate IRIS. It uses Tines as an example, but this is reproductible with any automation tool that can sent HTTP requests. A Tines story is created and is set up to receive a webhook, such as https://anothertest.tines.io/webhook/xxxx/xxxxx
. In this scenario, two IRIS webhooks are added:
on_manual_trigger_ioc_update
hook. on_postload_case_create
hook. We use the same Tines story and thus Tines webhook for both and dispatch the incoming request depending on its parameters.
"},{"location":"operations/modules/natives/IrisWebHooks/#todo","title":"TODO","text":""},{"location":"operations/modules/natives/IrisWebHooks/#examples-using-rendering","title":"Examples using rendering","text":"The following is an example of combined webhooks configuration. It can be directly imported in the module with the import feature. Please note that after import, the configuration should be opened and change to match your URL webhook receiver.
Download webhooks combined configuration example
"},{"location":"operations/modules/natives/IrisWebHooks/#discord","title":"Discord","text":"Discord webhook example - selection of events{ \"instance_url\": \"https://iris.local\",\n\"webhooks\": [\n{\n\"name\": \"Discord\",\n\"trigger_on\": [\n\"on_postload_ioc_create\",\n\"on_postload_ioc_update\",\n\"on_postload_note_create\",\n\"on_postload_note_update\"\n],\n\"request_url\": \"https://discord.com/api/webhooks/XXXX/XXXX\",\n\"request_rendering\": \"markdown\", \"request_body\": {\n\"embeds\": [{\n\"description\" : \"%DESCRIPTION%\",\n\"title\" : \"%TITLE%\"\n}]\n}\n}\n]\n}\n
"},{"location":"operations/modules/natives/IrisWebHooks/#slack","title":"Slack","text":"Slack webhook example - all events{ \"instance_url\": \"https://iris.local\",\n\"webhooks\": [\n{\n\"name\": \"Slack\",\n\"trigger_on\": [\n\"all\"\n],\n\"request_url\": \"https://hooks.slack.com/services/<XXX>/<XXX>/<XXX>\",\n\"request_rendering\": \"markdown_slack\",\n\"request_body\": {\n\"text\": \"%TITLE%\",\n\"blocks\": [\n{\n\"type\": \"section\",\n\"text\": {\n\"type\": \"mrkdwn\",\n\"text\": \"*%TITLE%*\"\n}\n},\n{\n\"type\": \"section\",\n\"block_id\": \"section567\",\n\"text\": {\n\"type\": \"mrkdwn\",\n\"text\": \"%DESCRIPTION%\"\n}\n}\n]\n}\n}\n]\n}\n
"},{"location":"operations/modules/natives/IrisWebHooks/#troubleshooting","title":"Troubleshooting","text":"Webhooks receivers are expecting specific message formatting to successfully process them. Please carefully read their documentations.
The module only handles JSON POST for the moment. If the target webhook receiver needs another type of request, please contact us so we can add it.
As any IRIS module, IrisWebhooks is logged into DIM Tasks. You can check the status of the requests made in these. Go to DIM Tasks
and then filter with webhooks
. You can then check details info by clicking in the Task ID. More info might be available in the Docker worker logs depending on the situation.
dfir_iris_client
offers a Python interface to communicate with IRIS.
It relies exclusively on the API, which means output of the methods are the same as specified in the API reference.
"},{"location":"python_client/#installation","title":"Installation","text":"It can be easily with pip : pip3 install dfir_iris_client
.
The source code of the project is available here.
"},{"location":"python_client/#versions","title":"Versions","text":"The Python client version follows the API versions (until the patch level). Meaning for API v1.0.1, one need to install dfir_iris_client-1.0.1
.
Examples of usage are available here.
"},{"location":"python_client/#documentation","title":"Documentation","text":"The documentation of the client is available on client.docs.dfir-iris.org.
"}]} \ No newline at end of file diff --git a/security-advisories/index.html b/security-advisories/index.html index 8a1f356..07b8885 100644 --- a/security-advisories/index.html +++ b/security-advisories/index.html @@ -1541,7 +1541,7 @@