assemblyline 4.7.2.dev47__tar.gz → 4.7.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {assemblyline-4.7.2.dev47/assemblyline.egg-info → assemblyline-4.7.2.2}/PKG-INFO +8 -2
- assemblyline-4.7.2.2/assemblyline/VERSION +1 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/digests.py +16 -11
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/entropy.py +40 -2
- assemblyline-4.7.2.2/assemblyline/common/frequency.pyx +39 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/logformat.py +1 -1
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/net.py +93 -1
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/postprocess.py +288 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/config.py +1 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/submission.py +1 -1
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2/assemblyline.egg-info}/PKG-INFO +8 -2
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline.egg-info/SOURCES.txt +1 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline.egg-info/requires.txt +7 -1
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/setup.py +16 -3
- assemblyline-4.7.2.dev47/assemblyline/VERSION +0 -1
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/LICENCE.md +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/MANIFEST.in +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/README.md +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/__init__.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/cachestore/__init__.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/__init__.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/archiving.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/attack_map.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/backupmanager.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/banner.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/bundling.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/caching.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/chunk.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/classification.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/classification.yml +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/cleanup_filestore.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/codec.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/comms.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/constants.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/custom.magic +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/custom.yara +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/dict_utils.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/dispatcher.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/exceptions.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/file.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/forge.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/heuristics.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/hexdump.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/identify.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/identify_defaults.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/importing.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/iprange.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/isotime.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/log.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/lucene.lark +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/memory_zip.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/metrics.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/net_static.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/null.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/path.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/random_user.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/security.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/signaturing.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/str_utils.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/tag_safelist.yml +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/tagging.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/threading.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/uid.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/common/version.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/datasource/__init__.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/datasource/al.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/datasource/alert.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/datasource/common.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/datastore/__init__.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/datastore/bulk.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/datastore/collection.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/datastore/exceptions.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/datastore/helper.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/datastore/store.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/datastore/support/__init__.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/datastore/support/build.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/datastore/support/schemas.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/filestore/__init__.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/filestore/transport/__init__.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/filestore/transport/azure.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/filestore/transport/base.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/filestore/transport/ftp.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/filestore/transport/http.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/filestore/transport/local.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/filestore/transport/s3.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/filestore/transport/sftp.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/__init__.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/base.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/common.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/messages/__init__.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/messages/alert.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/messages/alerter_heartbeat.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/messages/archive_heartbeat.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/messages/changes.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/messages/dispatcher_heartbeat.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/messages/dispatching.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/messages/elastic_heartbeat.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/messages/expiry_heartbeat.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/messages/ingest_heartbeat.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/messages/metrics.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/messages/retrohunt_heartbeat.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/messages/scaler_heartbeat.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/messages/scaler_status_heartbeat.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/messages/service_heartbeat.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/messages/service_timing_heartbeat.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/messages/submission.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/messages/task.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/messages/vacuum_heartbeat.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/__init__.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/actions.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/alert.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/apikey.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/badlist.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/cached_file.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/emptyresult.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/error.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/file.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/filescore.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/heuristic.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/ontology/__init__.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/ontology/file.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/ontology/filetypes/__init__.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/ontology/filetypes/pe.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/ontology/ontology.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/ontology/results/__init__.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/ontology/results/antivirus.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/ontology/results/http.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/ontology/results/malware_config.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/ontology/results/network.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/ontology/results/process.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/ontology/results/sandbox.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/ontology/results/signature.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/replay.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/result.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/retrohunt.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/safelist.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/service.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/service_delta.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/signature.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/statistics.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/submission_summary.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/submission_tree.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/tagging.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/user.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/user_favorites.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/user_settings.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/models/workflow.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/random_data/__init__.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/random_data/create_test_data.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/random_data/sample_rules.yar +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/random_data/sample_suricata.rules +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/odm/randomizer.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/py.typed +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/remote/__init__.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/remote/datatypes/__init__.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/remote/datatypes/cache.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/remote/datatypes/counters.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/remote/datatypes/daily_quota_tracker.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/remote/datatypes/events.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/remote/datatypes/exporting_counter.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/remote/datatypes/hash.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/remote/datatypes/lock.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/remote/datatypes/queues/__init__.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/remote/datatypes/queues/comms.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/remote/datatypes/queues/multi.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/remote/datatypes/queues/named.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/remote/datatypes/queues/priority.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/remote/datatypes/set.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/remote/datatypes/user_quota_tracker.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/run/__init__.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/run/cli.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/run/pubsub_reader.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/run/suricata_importer.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline/run/yara_importer.py +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline.egg-info/dependency_links.txt +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline.egg-info/entry_points.txt +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/assemblyline.egg-info/top_level.txt +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/pyproject.toml +0 -0
- {assemblyline-4.7.2.dev47 → assemblyline-4.7.2.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: assemblyline
|
|
3
|
-
Version: 4.7.2.
|
|
3
|
+
Version: 4.7.2.2
|
|
4
4
|
Summary: Assemblyline 4 - Automated malware analysis framework
|
|
5
5
|
Home-page: https://github.com/CybercentreCanada/assemblyline-base
|
|
6
6
|
Author: CCCS Assemblyline development team
|
|
@@ -24,20 +24,25 @@ Requires-Dist: urllib3>=2.6.0
|
|
|
24
24
|
Requires-Dist: python-baseconv
|
|
25
25
|
Requires-Dist: boto3
|
|
26
26
|
Requires-Dist: pysftp
|
|
27
|
+
Requires-Dist: netifaces
|
|
28
|
+
Requires-Dist: pyroute2.core
|
|
27
29
|
Requires-Dist: redis
|
|
28
30
|
Requires-Dist: requests[socks]
|
|
29
31
|
Requires-Dist: elasticsearch<9.0.0,>=8.0.0
|
|
30
32
|
Requires-Dist: python-datemath!=3.0.2
|
|
31
33
|
Requires-Dist: packaging
|
|
34
|
+
Requires-Dist: tabulate
|
|
32
35
|
Requires-Dist: PyYAML
|
|
36
|
+
Requires-Dist: easydict
|
|
33
37
|
Requires-Dist: bcrypt
|
|
34
38
|
Requires-Dist: cart
|
|
35
|
-
Requires-Dist:
|
|
39
|
+
Requires-Dist: cccs-ssdeep
|
|
36
40
|
Requires-Dist: python-magic
|
|
37
41
|
Requires-Dist: pytz
|
|
38
42
|
Requires-Dist: apscheduler
|
|
39
43
|
Requires-Dist: websocket_client<1.0.0
|
|
40
44
|
Requires-Dist: elastic-apm[flask]>=6.13.0
|
|
45
|
+
Requires-Dist: cython
|
|
41
46
|
Requires-Dist: docker
|
|
42
47
|
Requires-Dist: kubernetes>18
|
|
43
48
|
Requires-Dist: notifications-python-client
|
|
@@ -47,6 +52,7 @@ Requires-Dist: azure-identity
|
|
|
47
52
|
Requires-Dist: msoffcrypto-tool
|
|
48
53
|
Requires-Dist: chardet<6
|
|
49
54
|
Requires-Dist: yara-python
|
|
55
|
+
Requires-Dist: python-tlsh
|
|
50
56
|
Requires-Dist: hauntedhouse==0.1.10
|
|
51
57
|
Requires-Dist: magika
|
|
52
58
|
Requires-Dist: paramiko<4
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
4.7.2.2
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import hashlib
|
|
2
|
+
import ssdeep
|
|
3
|
+
import tlsh
|
|
2
4
|
from typing import Dict
|
|
3
5
|
|
|
4
6
|
from assemblyline.common import entropy
|
|
5
|
-
|
|
7
|
+
|
|
6
8
|
DEFAULT_BLOCKSIZE = 65536
|
|
7
9
|
|
|
8
10
|
|
|
@@ -12,7 +14,10 @@ def get_digests_for_file(path: str, blocksize: int = DEFAULT_BLOCKSIZE, calculat
|
|
|
12
14
|
""" Generate digests for file reading only 'blocksize bytes at a time."""
|
|
13
15
|
bc = None
|
|
14
16
|
if calculate_entropy:
|
|
15
|
-
|
|
17
|
+
try:
|
|
18
|
+
bc = entropy.BufferedCalculator()
|
|
19
|
+
except Exception:
|
|
20
|
+
pass
|
|
16
21
|
|
|
17
22
|
result = {}
|
|
18
23
|
|
|
@@ -20,9 +25,7 @@ def get_digests_for_file(path: str, blocksize: int = DEFAULT_BLOCKSIZE, calculat
|
|
|
20
25
|
sha1 = hashlib.sha1()
|
|
21
26
|
sha256 = hashlib.sha256()
|
|
22
27
|
if not skip_fuzzy_hashes:
|
|
23
|
-
th =
|
|
24
|
-
ssdeep = SsdeepHasher()
|
|
25
|
-
|
|
28
|
+
th = tlsh.Tlsh()
|
|
26
29
|
size = 0
|
|
27
30
|
|
|
28
31
|
with open(path, 'rb') as f:
|
|
@@ -34,13 +37,12 @@ def get_digests_for_file(path: str, blocksize: int = DEFAULT_BLOCKSIZE, calculat
|
|
|
34
37
|
|
|
35
38
|
while length > 0:
|
|
36
39
|
if bc is not None:
|
|
37
|
-
bc.update(data)
|
|
40
|
+
bc.update(data, length)
|
|
38
41
|
md5.update(data)
|
|
39
42
|
sha1.update(data)
|
|
40
43
|
sha256.update(data)
|
|
41
44
|
if not skip_fuzzy_hashes:
|
|
42
45
|
th.update(data)
|
|
43
|
-
ssdeep.update(data)
|
|
44
46
|
size += length
|
|
45
47
|
|
|
46
48
|
data = f.read(blocksize)
|
|
@@ -56,10 +58,13 @@ def get_digests_for_file(path: str, blocksize: int = DEFAULT_BLOCKSIZE, calculat
|
|
|
56
58
|
result['size'] = size
|
|
57
59
|
|
|
58
60
|
if not skip_fuzzy_hashes:
|
|
59
|
-
result["ssdeep"] = ssdeep.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
result["ssdeep"] = ssdeep.hash_from_file(path)
|
|
62
|
+
# Try to finalise the TLSH Hash and add it to the results
|
|
63
|
+
try:
|
|
64
|
+
th.final()
|
|
65
|
+
result['tlsh'] = th.hexdigest()
|
|
66
|
+
except Exception:
|
|
67
|
+
pass
|
|
63
68
|
|
|
64
69
|
return result
|
|
65
70
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import io
|
|
2
|
-
from typing import Tuple, List, BinaryIO
|
|
3
2
|
|
|
4
|
-
from
|
|
3
|
+
from math import log
|
|
4
|
+
from typing import Tuple, List, BinaryIO, AnyStr
|
|
5
5
|
|
|
6
|
+
frequency = None
|
|
6
7
|
|
|
7
8
|
# The minimum partition size should be 256 bytes as the keyspace
|
|
8
9
|
# for a char is 256 bytes
|
|
@@ -51,3 +52,40 @@ def calculate_partition_entropy(fin: BinaryIO, num_partitions: int = 50) -> Tupl
|
|
|
51
52
|
full_entropy_calculator.update(partition)
|
|
52
53
|
return full_entropy_calculator.entropy(), p_entropies
|
|
53
54
|
|
|
55
|
+
|
|
56
|
+
class BufferedCalculator(object):
|
|
57
|
+
def __init__(self):
|
|
58
|
+
global frequency
|
|
59
|
+
import pyximport
|
|
60
|
+
pyximport.install()
|
|
61
|
+
# noinspection PyUnresolvedReferences
|
|
62
|
+
from assemblyline.common import frequency
|
|
63
|
+
|
|
64
|
+
self.c = {}
|
|
65
|
+
self.length = 0
|
|
66
|
+
|
|
67
|
+
def entropy(self) -> float:
|
|
68
|
+
if self.length == 0:
|
|
69
|
+
return 0.0
|
|
70
|
+
|
|
71
|
+
length = float(self.length)
|
|
72
|
+
|
|
73
|
+
entropy = 0.0
|
|
74
|
+
for v in self.c.values():
|
|
75
|
+
prob = float(v) / length
|
|
76
|
+
entropy += prob * log(prob, 2)
|
|
77
|
+
|
|
78
|
+
entropy *= -1
|
|
79
|
+
|
|
80
|
+
# Make sure we don't return -0.0.
|
|
81
|
+
if not entropy:
|
|
82
|
+
entropy = 0.0
|
|
83
|
+
|
|
84
|
+
return entropy
|
|
85
|
+
|
|
86
|
+
def update(self, data: AnyStr, length: int = 0):
|
|
87
|
+
if not length:
|
|
88
|
+
length = len(data)
|
|
89
|
+
|
|
90
|
+
self.length += length
|
|
91
|
+
self.c = frequency.counts(data, length, self.c)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# cython: language_level=3
|
|
2
|
+
|
|
3
|
+
# noinspection PyUnresolvedReferences
|
|
4
|
+
from libc.string cimport memset
|
|
5
|
+
|
|
6
|
+
def counts(b, c, d=None):
|
|
7
|
+
if d is None:
|
|
8
|
+
d = {}
|
|
9
|
+
cdef long long t[256]
|
|
10
|
+
cdef unsigned char* s = b
|
|
11
|
+
cdef int l = c
|
|
12
|
+
cdef int i = 0
|
|
13
|
+
|
|
14
|
+
memset(t, 0, 256 * sizeof(long long))
|
|
15
|
+
|
|
16
|
+
for k, v in d.iteritems():
|
|
17
|
+
t[k] = v
|
|
18
|
+
|
|
19
|
+
while i < l:
|
|
20
|
+
t[s[i]] += 1
|
|
21
|
+
i += 1
|
|
22
|
+
|
|
23
|
+
return {i: t[i] for i in range(256) if t[i]}
|
|
24
|
+
|
|
25
|
+
def counts_old(s, d=None):
|
|
26
|
+
if d is None:
|
|
27
|
+
d = {}
|
|
28
|
+
cdef int i
|
|
29
|
+
cdef int t[256]
|
|
30
|
+
|
|
31
|
+
memset(t, 0, 256 * sizeof(int))
|
|
32
|
+
|
|
33
|
+
for k, v in d.iteritems():
|
|
34
|
+
t[k] = v
|
|
35
|
+
|
|
36
|
+
for c in s:
|
|
37
|
+
t[ord(c)] += 1
|
|
38
|
+
|
|
39
|
+
return {i: t[i] for i in range(256) if t[i]}
|
|
@@ -10,7 +10,7 @@ except Exception: # pylint:disable=W0702
|
|
|
10
10
|
ip = 'x.x.x.x'
|
|
11
11
|
# noinspection PyBroadException
|
|
12
12
|
try:
|
|
13
|
-
from
|
|
13
|
+
from assemblyline.common.net import get_hostip
|
|
14
14
|
ip = get_hostip()
|
|
15
15
|
except Exception: # pylint:disable=W0702
|
|
16
16
|
pass
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
from ipaddress import ip_address, IPv4Network
|
|
2
2
|
import socket
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
3
5
|
import os
|
|
6
|
+
import uuid
|
|
4
7
|
import functools
|
|
8
|
+
from random import randint
|
|
5
9
|
|
|
10
|
+
import netifaces as nif
|
|
11
|
+
import pr2modules.iproute as iproute
|
|
6
12
|
|
|
7
13
|
from assemblyline.common.net_static import TLDS_ALPHA_BY_DOMAIN, TLDS_SPECIAL_BY_DOMAIN
|
|
8
14
|
SYSTEM_LOCAL_TLD = os.getenv('SYSTEM_LOCAL_TLD', '')
|
|
@@ -25,8 +31,8 @@ def find_top_level_domains() -> set[str]:
|
|
|
25
31
|
local_tlds = {
|
|
26
32
|
tld.strip().strip('.').upper()
|
|
27
33
|
for tld in SYSTEM_LOCAL_TLD.split(";")
|
|
28
|
-
if tld.strip().strip('.')
|
|
29
34
|
}
|
|
35
|
+
local_tlds.discard('')
|
|
30
36
|
return TLDS_ALPHA_BY_DOMAIN | single_label_special | local_tlds
|
|
31
37
|
|
|
32
38
|
|
|
@@ -129,3 +135,89 @@ def is_valid_email(email: str) -> bool:
|
|
|
129
135
|
|
|
130
136
|
def get_hostname() -> str:
|
|
131
137
|
return socket.gethostname()
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def get_mac_address() -> str:
|
|
141
|
+
return "".join(["{0:02x}".format((uuid.getnode() >> i) & 0xff) for i in range(0, 8 * 6, 8)][::-1]).upper()
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def get_mac_for_ip(ip: str) -> str:
|
|
145
|
+
for i in nif.interfaces():
|
|
146
|
+
addrs = nif.ifaddresses(i)
|
|
147
|
+
try:
|
|
148
|
+
if_mac = addrs[nif.AF_LINK][0]['addr']
|
|
149
|
+
if_ip = addrs[nif.AF_INET][0]['addr']
|
|
150
|
+
except (IndexError, KeyError):
|
|
151
|
+
if_mac = if_ip = None
|
|
152
|
+
|
|
153
|
+
if if_mac and if_ip == ip:
|
|
154
|
+
return if_mac.replace(':', '').upper()
|
|
155
|
+
|
|
156
|
+
# If we couldn't match on IP just use the old uuid based approach.
|
|
157
|
+
return get_mac_address()
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def get_random_mac(separator: str = ':') -> str:
|
|
161
|
+
oui = [0x52, 0x54, 0x00]
|
|
162
|
+
mac = oui + [randint(0, 0xff), randint(0, 0xff), randint(0, 0xff)]
|
|
163
|
+
return separator.join("%02x" % x for x in mac).upper()
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def get_route_to(dst: str) -> str:
|
|
167
|
+
ret_val = None
|
|
168
|
+
try:
|
|
169
|
+
with iproute.IPRoute() as ipr:
|
|
170
|
+
for k, v in ipr.route('get', dst=dst)[0]['attrs']:
|
|
171
|
+
if k == "RTA_PREFSRC":
|
|
172
|
+
ret_val = v
|
|
173
|
+
break
|
|
174
|
+
except (ImportError, KeyError, ValueError):
|
|
175
|
+
if sys.platform.startswith('linux'):
|
|
176
|
+
cmdline = 'ip route get to {dst} | sed -e "s/.*src //" | head -n 1 | sed -e "s/ .*//"'.format(dst=dst)
|
|
177
|
+
p = subprocess.Popen(cmdline, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
|
|
178
|
+
stdout, stderr = p.communicate()
|
|
179
|
+
if stdout:
|
|
180
|
+
ret_val = stdout.strip()
|
|
181
|
+
finally:
|
|
182
|
+
return ret_val
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def get_hostip() -> str:
|
|
186
|
+
ip = None
|
|
187
|
+
try:
|
|
188
|
+
from assemblyline.common import forge
|
|
189
|
+
config = forge.get_config()
|
|
190
|
+
ip = get_route_to(config.datastore.hosts[0])
|
|
191
|
+
except Exception:
|
|
192
|
+
pass
|
|
193
|
+
|
|
194
|
+
return ip or get_default_gateway_ip()
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def get_default_gateway_ip() -> str:
|
|
198
|
+
# fetch the nic serving up the default gateway
|
|
199
|
+
if_default = nif.gateways().get('default')
|
|
200
|
+
(ip, nic) = if_default.get(nif.AF_INET)
|
|
201
|
+
# Fetch the IP of that nic
|
|
202
|
+
try:
|
|
203
|
+
ip = nif.ifaddresses(nic).get(nif.AF_INET)[0].get('addr')
|
|
204
|
+
except (IndexError, KeyError):
|
|
205
|
+
subnet = ip.split(".")[0]
|
|
206
|
+
if sys.platform.startswith('win'):
|
|
207
|
+
proc = subprocess.Popen('ipconfig', stdout=subprocess.PIPE, text=True)
|
|
208
|
+
output = proc.stdout.read()
|
|
209
|
+
for line in output.split('\n'):
|
|
210
|
+
if "IP Address" in line and ": %s" % subnet in line:
|
|
211
|
+
ip = line.split(": ")[1].replace('\r', '')
|
|
212
|
+
break
|
|
213
|
+
|
|
214
|
+
else:
|
|
215
|
+
proc = subprocess.Popen('ifconfig', stdout=subprocess.PIPE, text=True)
|
|
216
|
+
output = proc.stdout.read()
|
|
217
|
+
|
|
218
|
+
for line in output.split('\n'):
|
|
219
|
+
if "addr:%s" % subnet in line:
|
|
220
|
+
ip = line.split("addr:")[1].split(" ")[0]
|
|
221
|
+
break
|
|
222
|
+
|
|
223
|
+
return ip
|
|
@@ -570,3 +570,291 @@ class SubmissionFilter:
|
|
|
570
570
|
return '<SubmissionFilter ' + str(self.expression) + '>'
|
|
571
571
|
|
|
572
572
|
|
|
573
|
+
def should_resubmit(score: float, shift: float = 500) -> bool:
|
|
574
|
+
|
|
575
|
+
# Resubmit:
|
|
576
|
+
#
|
|
577
|
+
# 100% with a score above 400.
|
|
578
|
+
# 10% with a score of 301 to 400.
|
|
579
|
+
# 1% with a score of 201 to 300.
|
|
580
|
+
# 0.1% with a score of 101 to 200.
|
|
581
|
+
# 0.01% with a score of 1 to 100.
|
|
582
|
+
# 0.001% with a score of 0.
|
|
583
|
+
# 0% with a score below 0.
|
|
584
|
+
|
|
585
|
+
if score < 0:
|
|
586
|
+
return False
|
|
587
|
+
|
|
588
|
+
if score >= shift:
|
|
589
|
+
return True
|
|
590
|
+
|
|
591
|
+
resubmit_probability = 1.0 / 10 ** ((shift - score) / 100)
|
|
592
|
+
|
|
593
|
+
return random.random() < resubmit_probability
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
class ActionWorker:
|
|
597
|
+
def __init__(self, cache: bool, config, datastore, redis_persist) -> None:
|
|
598
|
+
# Store parameters
|
|
599
|
+
self.running_cache_tasks = cache
|
|
600
|
+
|
|
601
|
+
# Setup dependencies
|
|
602
|
+
self.config = config
|
|
603
|
+
self.datastore = datastore
|
|
604
|
+
|
|
605
|
+
# Submissions that should have alerts generated
|
|
606
|
+
self.alert_queue: NamedQueue[dict] = NamedQueue(ALERT_QUEUE_NAME, redis_persist)
|
|
607
|
+
self.unique_queue: PriorityQueue[dict] = PriorityQueue('m-unique', redis_persist)
|
|
608
|
+
self.config_hash: Hash[str] = Hash(CONFIG_HASH, redis_persist)
|
|
609
|
+
|
|
610
|
+
# Archive manager (late import due to circular import)
|
|
611
|
+
from assemblyline.common.archiving import ArchiveManager
|
|
612
|
+
self.archive_manager: ArchiveManager = ArchiveManager(self.config, self.datastore)
|
|
613
|
+
|
|
614
|
+
# Load actions
|
|
615
|
+
self.actions: dict[str, tuple[SubmissionFilter, PostprocessAction]] = {}
|
|
616
|
+
self._load_actions()
|
|
617
|
+
|
|
618
|
+
# Make sure we load any changed actions
|
|
619
|
+
self.reload_watcher: EventWatcher[str] = EventWatcher()
|
|
620
|
+
self.reload_watcher.register('system.postprocess', self._load_actions)
|
|
621
|
+
self.reload_watcher.start()
|
|
622
|
+
|
|
623
|
+
# Create an event loop to handle highly parallel webhook calls
|
|
624
|
+
self.loop = asyncio.new_event_loop()
|
|
625
|
+
threading.Thread(target=self.loop.run_forever, name='webhook_caller', daemon=True).start()
|
|
626
|
+
|
|
627
|
+
def stop(self):
|
|
628
|
+
self.reload_watcher.stop()
|
|
629
|
+
while self.loop.is_running():
|
|
630
|
+
if len(asyncio.all_tasks(self.loop)) == 0:
|
|
631
|
+
break
|
|
632
|
+
time.sleep(0.1)
|
|
633
|
+
self.loop.call_soon_threadsafe(self.loop.stop)
|
|
634
|
+
|
|
635
|
+
def _load_actions(self, _path: Optional[str] = None):
|
|
636
|
+
# Load the action data from redis
|
|
637
|
+
data = self.config_hash.get(POST_PROCESS_CONFIG_KEY)
|
|
638
|
+
|
|
639
|
+
# If nothing is in redis, fall back to legacy storage
|
|
640
|
+
if data is None:
|
|
641
|
+
try:
|
|
642
|
+
with CacheStore('system', config=self.config, datastore=self.datastore) as cache:
|
|
643
|
+
byte_data = cache.get('postprocess_actions')
|
|
644
|
+
if byte_data:
|
|
645
|
+
data = byte_data.decode()
|
|
646
|
+
except Exception:
|
|
647
|
+
logger.warn("Couldn't access system files")
|
|
648
|
+
|
|
649
|
+
# Decode data
|
|
650
|
+
objects = DEFAULT_POSTPROCESS_ACTIONS
|
|
651
|
+
if data:
|
|
652
|
+
try:
|
|
653
|
+
raw: dict[str, Any] = yaml.safe_load(data)
|
|
654
|
+
objects = {
|
|
655
|
+
key: PostprocessAction(data)
|
|
656
|
+
for key, data in raw.items()
|
|
657
|
+
}
|
|
658
|
+
except Exception:
|
|
659
|
+
logger.exception("Couldn't load stored actions")
|
|
660
|
+
|
|
661
|
+
# Check which ones can be active
|
|
662
|
+
ready_objects: dict[str, tuple[SubmissionFilter, PostprocessAction]] = {}
|
|
663
|
+
for key, action in objects.items():
|
|
664
|
+
if not action.enabled:
|
|
665
|
+
continue
|
|
666
|
+
|
|
667
|
+
try:
|
|
668
|
+
fltr = SubmissionFilter(action.filter)
|
|
669
|
+
except Exception:
|
|
670
|
+
logger.exception("Failed to load submission filter")
|
|
671
|
+
continue
|
|
672
|
+
|
|
673
|
+
if self.running_cache_tasks and action.run_on_cache:
|
|
674
|
+
if not fltr.cache_safe:
|
|
675
|
+
logger.error("Tried to apply non-cache-safe filter to cached submissions.")
|
|
676
|
+
continue
|
|
677
|
+
ready_objects[key] = fltr, action
|
|
678
|
+
|
|
679
|
+
if not self.running_cache_tasks and action.run_on_completed:
|
|
680
|
+
ready_objects[key] = fltr, action
|
|
681
|
+
|
|
682
|
+
# Swap in the new actions
|
|
683
|
+
self.actions = ready_objects
|
|
684
|
+
|
|
685
|
+
def process_submission(self, submission: Submission, tags: list[dict[str, Any]]) -> bool:
|
|
686
|
+
return self.process(submission=submission, tags=tags, score=submission.max_score)
|
|
687
|
+
|
|
688
|
+
def process_cachehit(self, submission: SubmissionMessage, score: float) -> bool:
|
|
689
|
+
return self.process(submission=submission, tags=None, score=score)
|
|
690
|
+
|
|
691
|
+
def process(self, submission: Union[Submission, SubmissionMessage],
|
|
692
|
+
score: float, tags: Optional[list[dict[str, Any]]]) -> bool:
|
|
693
|
+
""" Handle any postprocessing events for a submission.
|
|
694
|
+
|
|
695
|
+
Return bool indicating if a resubmission action has happened.
|
|
696
|
+
"""
|
|
697
|
+
archive_submission = submission.params.auto_archive
|
|
698
|
+
use_archive_alternate_dtl = submission.params.use_archive_alternate_dtl
|
|
699
|
+
create_alert = False
|
|
700
|
+
resubmit: Optional[set[str]] = None
|
|
701
|
+
webhooks = []
|
|
702
|
+
|
|
703
|
+
for fltr, action in self.actions.values():
|
|
704
|
+
if not fltr.test(submission, score=score, tags=tags):
|
|
705
|
+
continue
|
|
706
|
+
|
|
707
|
+
# Check if we need to launch an alert
|
|
708
|
+
create_alert |= action.raise_alert
|
|
709
|
+
|
|
710
|
+
# Check if we need to archive the submission
|
|
711
|
+
archive_submission |= action.archive_submission
|
|
712
|
+
use_archive_alternate_dtl |= action.use_archive_alternate_dtl
|
|
713
|
+
|
|
714
|
+
# Accumulate resubmit services
|
|
715
|
+
if action.resubmit is not None:
|
|
716
|
+
do_resubmit = True
|
|
717
|
+
if action.resubmit.random_below is not None:
|
|
718
|
+
do_resubmit = should_resubmit(score, action.resubmit.random_below)
|
|
719
|
+
|
|
720
|
+
if do_resubmit:
|
|
721
|
+
if resubmit is None:
|
|
722
|
+
resubmit = set()
|
|
723
|
+
resubmit.update(set(action.resubmit.additional_services))
|
|
724
|
+
|
|
725
|
+
# Accumulate hooks
|
|
726
|
+
if action.webhook is not None and action.webhook not in webhooks:
|
|
727
|
+
webhooks.append(action.webhook)
|
|
728
|
+
|
|
729
|
+
# Bail early if nothing is to be done
|
|
730
|
+
if resubmit is None and not create_alert and not webhooks and not archive_submission:
|
|
731
|
+
return False
|
|
732
|
+
|
|
733
|
+
# Prepare a message formatted submission
|
|
734
|
+
if isinstance(submission, Submission):
|
|
735
|
+
submission_msg = from_datastore_submission(submission)
|
|
736
|
+
else:
|
|
737
|
+
submission_msg = submission
|
|
738
|
+
|
|
739
|
+
# Default values
|
|
740
|
+
extended_scan = 'skipped'
|
|
741
|
+
did_resubmit = False
|
|
742
|
+
submit_to = []
|
|
743
|
+
|
|
744
|
+
# Check if we resubmit
|
|
745
|
+
if resubmit is not None:
|
|
746
|
+
selected = set(submission.params.services.selected)
|
|
747
|
+
resubmit_to = set(submission.params.services.resubmit) | resubmit
|
|
748
|
+
|
|
749
|
+
if not selected.issuperset(resubmit_to):
|
|
750
|
+
submit_to = sorted(selected | resubmit_to)
|
|
751
|
+
extended_scan = 'submitted'
|
|
752
|
+
|
|
753
|
+
# Raise alert
|
|
754
|
+
if submission.params.generate_alert and create_alert:
|
|
755
|
+
logger.info(f"[{submission_msg.sid} :: {submission_msg.files[0].sha256}] Notifying alerter to "
|
|
756
|
+
"create or update an alert")
|
|
757
|
+
|
|
758
|
+
self.alert_queue.push(dict(
|
|
759
|
+
submission=submission_msg.as_primitives(),
|
|
760
|
+
score=score,
|
|
761
|
+
extended_scan=extended_scan,
|
|
762
|
+
ingest_id=submission_msg.metadata.get('ingest_id', None)
|
|
763
|
+
))
|
|
764
|
+
|
|
765
|
+
if submit_to:
|
|
766
|
+
logger.info(f"[{submission.sid} :: {submission.files[0].sha256}] Resubmitted for extended analysis")
|
|
767
|
+
resubmission = SubmissionMessage(submission_msg.as_primitives())
|
|
768
|
+
resubmission.params.psid = submission.sid
|
|
769
|
+
resubmission.sid = get_random_id()
|
|
770
|
+
resubmission.scan_key = None
|
|
771
|
+
resubmission.params.services.resubmit = []
|
|
772
|
+
resubmission.params.services.selected = submit_to
|
|
773
|
+
|
|
774
|
+
self.unique_queue.push(submission.params.priority, dict(
|
|
775
|
+
score=score,
|
|
776
|
+
extended_scan=extended_scan,
|
|
777
|
+
ingest_id=submission.metadata.get('ingest_id', None),
|
|
778
|
+
submission=resubmission.as_primitives(),
|
|
779
|
+
))
|
|
780
|
+
did_resubmit = True
|
|
781
|
+
|
|
782
|
+
# Archive the submission
|
|
783
|
+
if archive_submission:
|
|
784
|
+
if self.config.datastore.archive.enabled:
|
|
785
|
+
logger.info(f"[{submission_msg.sid} :: {submission_msg.files[0].sha256}] Evaluating if the file can"
|
|
786
|
+
" be moved to the malware archive")
|
|
787
|
+
|
|
788
|
+
if self.archive_manager.archive_submission(
|
|
789
|
+
submission_msg.as_primitives(),
|
|
790
|
+
submission_msg.params.delete_after_archive,
|
|
791
|
+
use_alternate_dtl=use_archive_alternate_dtl)['action'] == "archive":
|
|
792
|
+
logger.info(f"[{submission_msg.sid} :: {submission_msg.files[0].sha256}] Archiver was notified "
|
|
793
|
+
"to copy the file in the malware archive")
|
|
794
|
+
else:
|
|
795
|
+
logger.info(f"[{submission_msg.sid} :: {submission_msg.files[0].sha256}] The file was "
|
|
796
|
+
"re-submitted for analysis because it does not meet the minimum service requirement")
|
|
797
|
+
|
|
798
|
+
else:
|
|
799
|
+
logger.warning(f"[{submission_msg.sid} :: {submission_msg.files[0].sha256}] Trying to archive a "
|
|
800
|
+
"submission on a system where archiving is disabled")
|
|
801
|
+
|
|
802
|
+
# Trigger webhooks
|
|
803
|
+
for hook in webhooks:
|
|
804
|
+
asyncio.run_coroutine_threadsafe(self._process_hook(hook, submission, score), self.loop)
|
|
805
|
+
|
|
806
|
+
return did_resubmit
|
|
807
|
+
|
|
808
|
+
async def _process_hook(self, hook: Webhook, submission: Union[Submission, SubmissionMessage], score: float):
|
|
809
|
+
backoff = 0.0
|
|
810
|
+
cafile = None
|
|
811
|
+
|
|
812
|
+
try:
|
|
813
|
+
is_cache = isinstance(submission, SubmissionMessage)
|
|
814
|
+
payload = json.dumps({
|
|
815
|
+
'is_cache': is_cache,
|
|
816
|
+
'score': score,
|
|
817
|
+
'submission': submission.as_primitives()
|
|
818
|
+
})
|
|
819
|
+
|
|
820
|
+
# Setup auth headers and other headers
|
|
821
|
+
auth = None
|
|
822
|
+
if hook.username and hook.password:
|
|
823
|
+
auth = aiohttp.BasicAuth(login=hook.username, password=hook.password)
|
|
824
|
+
headers = {head.name: head.value for head in hook.headers}
|
|
825
|
+
headers.setdefault('Content-Type', 'application/json')
|
|
826
|
+
|
|
827
|
+
# Setup ssl details
|
|
828
|
+
sslcontext: Union[None, bool, ssl.SSLContext] = None
|
|
829
|
+
if hook.ssl_ignore_errors:
|
|
830
|
+
sslcontext = False
|
|
831
|
+
if hook.ca_cert:
|
|
832
|
+
cafile = tempfile.NamedTemporaryFile()
|
|
833
|
+
cafile.write(hook.ca_cert.encode())
|
|
834
|
+
cafile.flush()
|
|
835
|
+
sslcontext = ssl.create_default_context(cafile=cafile.name)
|
|
836
|
+
|
|
837
|
+
# Setup setup http query details
|
|
838
|
+
async with aiohttp.ClientSession(auth=auth, headers=headers) as session:
|
|
839
|
+
# Loop up to retry limit
|
|
840
|
+
for _ in range(hook.retries):
|
|
841
|
+
# Wait before retrying, 0 first time, so we can have this before the post
|
|
842
|
+
# and not wait after the final failure
|
|
843
|
+
await asyncio.sleep(backoff)
|
|
844
|
+
backoff = min(RETRY_MAX_BACKOFF, backoff * 2) + 0.1
|
|
845
|
+
|
|
846
|
+
# Try posting to the webhook once. If it succeeds return and let
|
|
847
|
+
# the withs and finallys finish all the cleanup
|
|
848
|
+
try:
|
|
849
|
+
resp = await session.request(hook.method, hook.uri, data=payload,
|
|
850
|
+
ssl=sslcontext, proxy=hook.proxy)
|
|
851
|
+
resp.raise_for_status()
|
|
852
|
+
return
|
|
853
|
+
except Exception:
|
|
854
|
+
logger.exception(f"Error pushing to webhook: {hook}")
|
|
855
|
+
|
|
856
|
+
except Exception:
|
|
857
|
+
logger.exception(f"Error reading webhook configuration: {hook}")
|
|
858
|
+
finally:
|
|
859
|
+
if cafile is not None:
|
|
860
|
+
cafile.close()
|
|
@@ -113,6 +113,7 @@ class LDAP(odm.Model):
|
|
|
113
113
|
enabled: bool = odm.Boolean(description="Should LDAP be enabled or not?")
|
|
114
114
|
ip_filter: List[str] = odm.Optional(odm.List(odm.ValidatedKeyword(CIDR_REGEX)),
|
|
115
115
|
description="List of CIDRs allowed to access internal authentication")
|
|
116
|
+
admin_dn: str = odm.Optional(odm.Keyword(), description="DN of the group or the user who will get admin privileges")
|
|
116
117
|
bind_user: str = odm.Optional(odm.Keyword(), description="User use to query the LDAP server")
|
|
117
118
|
bind_pass: str = odm.Optional(odm.Keyword(), description="Password used to query the LDAP server")
|
|
118
119
|
auto_create: bool = odm.Boolean(description="Auto-create users if they are missing")
|
|
@@ -49,7 +49,7 @@ class SubmissionParams(odm.Model):
|
|
|
49
49
|
description="Original classification of the submission.")
|
|
50
50
|
deep_scan = odm.Boolean(default=False, description="Select to perform a deep scan.")
|
|
51
51
|
description = odm.Text(store=True, copyto="__text__", description="User-supplied information applied to Submission Details.")
|
|
52
|
-
filetype_override = odm.Optional(odm.
|
|
52
|
+
filetype_override = odm.Optional(odm.Keyword(),
|
|
53
53
|
description="Override the system's identification of the submitted file")
|
|
54
54
|
generate_alert = odm.Boolean(default=False, description="Generate alert upon completion of analysis.")
|
|
55
55
|
groups = odm.List(odm.Keyword(), default=[], description="List relevant group or organization related to this scan.")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: assemblyline
|
|
3
|
-
Version: 4.7.2.
|
|
3
|
+
Version: 4.7.2.2
|
|
4
4
|
Summary: Assemblyline 4 - Automated malware analysis framework
|
|
5
5
|
Home-page: https://github.com/CybercentreCanada/assemblyline-base
|
|
6
6
|
Author: CCCS Assemblyline development team
|
|
@@ -24,20 +24,25 @@ Requires-Dist: urllib3>=2.6.0
|
|
|
24
24
|
Requires-Dist: python-baseconv
|
|
25
25
|
Requires-Dist: boto3
|
|
26
26
|
Requires-Dist: pysftp
|
|
27
|
+
Requires-Dist: netifaces
|
|
28
|
+
Requires-Dist: pyroute2.core
|
|
27
29
|
Requires-Dist: redis
|
|
28
30
|
Requires-Dist: requests[socks]
|
|
29
31
|
Requires-Dist: elasticsearch<9.0.0,>=8.0.0
|
|
30
32
|
Requires-Dist: python-datemath!=3.0.2
|
|
31
33
|
Requires-Dist: packaging
|
|
34
|
+
Requires-Dist: tabulate
|
|
32
35
|
Requires-Dist: PyYAML
|
|
36
|
+
Requires-Dist: easydict
|
|
33
37
|
Requires-Dist: bcrypt
|
|
34
38
|
Requires-Dist: cart
|
|
35
|
-
Requires-Dist:
|
|
39
|
+
Requires-Dist: cccs-ssdeep
|
|
36
40
|
Requires-Dist: python-magic
|
|
37
41
|
Requires-Dist: pytz
|
|
38
42
|
Requires-Dist: apscheduler
|
|
39
43
|
Requires-Dist: websocket_client<1.0.0
|
|
40
44
|
Requires-Dist: elastic-apm[flask]>=6.13.0
|
|
45
|
+
Requires-Dist: cython
|
|
41
46
|
Requires-Dist: docker
|
|
42
47
|
Requires-Dist: kubernetes>18
|
|
43
48
|
Requires-Dist: notifications-python-client
|
|
@@ -47,6 +52,7 @@ Requires-Dist: azure-identity
|
|
|
47
52
|
Requires-Dist: msoffcrypto-tool
|
|
48
53
|
Requires-Dist: chardet<6
|
|
49
54
|
Requires-Dist: yara-python
|
|
55
|
+
Requires-Dist: python-tlsh
|
|
50
56
|
Requires-Dist: hauntedhouse==0.1.10
|
|
51
57
|
Requires-Dist: magika
|
|
52
58
|
Requires-Dist: paramiko<4
|