Port d'attache

Bap M., Correia M., Shong geu M.

← Port d'attache · Sillage / Le Hollandais Volant / SeaGate / BoatNet

L'équipe de réponse à incident a pu capturer un dump mémoire complet d'un processus .NET suspect exécuté sur le serveur SCADA compromis.

Explorez le fichier fourni pour extraire des informations sur l'identité des attaquants, leur mode opératoire et leurs intentions.

À la suite d'une intrusion, les autorités portuaires ont constaté un comportement anormal des portes maritimes. L'analyse du serveur SCADA a révélé l'injection d'un programme C# malveillant chargé de lire les messages AIS.

L'équipe de réponse à incident vous a mis à disposition un dump mémoire .NET du processus malveillant.

Votre mission consiste à analyser le dump mémoire post-mortem fourni afin de déterminer l'intention des attaquants derrière cette compromission.

Cote 46 pts

Indices

Avec l'utilisation de `dotnet-dump`, vérifier la légitimité des structures.
Inspecter les objets de la classe FluentModbusConfig pour déterminer le contenu des objets instanciés.

Faire son rapport

MMSI:COMMANDE

Résolution


Méthode de résolution

L'objectif de cet exercice est de comprendre comment les attaquants ont réussi à exécuter des commandes sur le serveur SCADA grâce au programme C# injecté.

Étant donné que le code source n'est pas fourni, nous allons principalement utiliser des outils d'analyse mémoire adaptés comme dotnet-dump.

Identification des structures

L'investigation est de type post-mortem, il faut donc cibler les objets statiques persistants qui définissent le mode opératoire de l'attaquant.

En utilisant la commande dumpheap -stat, nous pouvons lister les classes présentes dans la mémoire du processus .NET.

dotnet-dump analyze ScadaCommWkr.dmp

> dumpheap -stat
Statistics:
          MT Count TotalSize Class Name
789348bc9b50     1        24 System.Collections.Generic.GenericEqualityComparer<System.String>
789348bcb5f8     1        24 System.OrdinalCaseSensitiveComparer
...
789348c4bdb0     1        80 System.Collections.Generic.Dictionary<System.Int32, System.Threading.CancellationTokenSource>
78934943db70     2        80 FluentModbusConfig+ModbusEvent[]
7893496e3108     1        80 System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.Threading.Tasks.VoidTaskResult>+AsyncStateMachineBox<Program+<>c__DisplayClass10_2+<<Main>b__1>d>
789349083f70     3        88 System.Reflection.ParameterInfo[]
789348bf5d68     3        96 System.LazyHelper
...
7893496e4ac8     4        96 FluentModbus.ModbusClient+<>c__DisplayClass19_0
7893496e4c38     1        96 System.Collections.Generic.Dictionary<System.Int32, System.Threading.CancellationTokenSource>+Entry[]
7893496e0958     1        96 Program+<>c__DisplayClass10_2+<<Main>b__1>d
78934926e088     1       104 System.IO.StreamWriter
78934906e3f0     2       112 System.RuntimeType+RuntimeTypeCache+MemberInfoCache<System.Reflection.RuntimePropertyInfo>
78934922ea38     3       120 System.Threading.ThreadPoolWorkQueue+WorkStealingQueue
7893496e2668     3       120 System.Threading.TimerQueue[]
789348b2f608     1       128 System.OutOfMemoryException
789348b2f708     1       128 System.StackOverflowException
789348b2f808     1       128 System.ExecutionEngineException
789348bf1c90     2       128 System.Func<System.String>
7893490845c0     2       128 System.Reflection.InvokerEmitUtil+InvokeFunc_RefArgs
7893490858b8     4       128 System.Diagnostics.Tracing.EventSourceAttribute[]
789349225d40     4       128 Microsoft.Win32.SafeHandles.SafeWaitHandle

En examinant les classes listées, nous pouvons repérer plusieurs structures dont System, FluentModbus, Microsoft, mais celui qui nous intéresse particulièrement est FluentModbusConfig, qui n'est pas une classe standard de la bibliothèque FluentModbus.

En utilisant la commande dumpheap -type FluentModbusConfig, nous pouvons localiser les instances de cette classe.

> dumpheap -type FluentModbusConfig
         Address               MT           Size
    78533a8e0ac0     789348c48df0             32 
    78533a8e0ae0     78934943db70             24 
    78533a8e0af8     789348c44e10             48 
    78533a8e0ba0     78934943db70             56 
    78533a8e0bd8     789348c44e10             48 
    78533a8e0c80     789348c44e10             48 

Statistics:
          MT Count TotalSize Class Name
789348c48df0     1        32 System.Collections.Generic.List<FluentModbusConfig+ModbusEvent>
78934943db70     2        80 FluentModbusConfig+ModbusEvent[]
789348c44e10     3       144 FluentModbusConfig+ModbusEvent
Total 6 objects, 256 bytes

Plusieurs objets FluentModbusConfig+ModbusEvent sont présents en mémoire. En examinant le contenu de chacun d'eux, nous pouvons remarquer des structures intéressantes.

> do 78533a8e0af8                                                                                                                                                                      
Name:        FluentModbusConfig+ModbusEvent
MethodTable: 0000789348c44e10
EEClass:     0000789348d3d898
Tracked Type: false
Size:        48(0x30) bytes
File:        /home/scada/scada-wkr/ScadaCommWkr/bin/Debug/net8.0/ScadaCommWkr.dll
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
0000789348c43c00  4000018        8        System.Byte[]  0 instance 000078533a8e0b28 <Field>k__BackingField
0000789348b2d7c8  4000019       10        System.String  0 instance 0000789346a00560 <Tag>k__BackingField
0000789348bca3d0  400001a       18 ...Private.CoreLib]]  1 instance 000078533a8e0b10 <DL>k__BackingField
0000789348bca3d0  400001b       20 ...Private.CoreLib]]  1 instance 000078533a8e0b18 <DT>k__BackingField

L'analyse révèle des objets contenant :
- Un tableau de bytes (Field)
- Une chaîne de caractères (Tag)
- Deux autres champs (DL et DT)

En inspectant Field avec la commande dumparray, nous pouvons obtenir des informations intéressantes comme la taille et le contenu du tableau de bytes.

> dumparray 000078533a8e0b28                                                                                                                                                           
Name:        System.Byte[]
MethodTable: 0000789348c43c00
EEClass:     0000789348c43b90
Size:        56(0x38) bytes
Array:       Rank 1, Number of elements 32, Type Byte
Element Methodtable: 0000789348ad1a58
[0] 000078533a8e0b38
[1] 000078533a8e0b39
...
[30] 000078533a8e0b56
[31] 000078533a8e0b57

Le tableau de bytes contient 32 éléments. En examinant le contenu du tableau, nous pouvons observer une structure de bytes qui semble être un hash sha256 : 09d16a8e07e8c2580de60a5a76f95d2172c32e9c3fce8d7c889ad5f62aad9f15.

> db 000078533a8e0b38 -c 32                                                                                                                                                            
000078533a8e0b38: 09 d1 6a 8e 07 e8 c2 58 0d e6 0a 5a 76 f9 5d 21  ..j....X...Zv.]!
000078533a8e0b48: 72 c3 2e 9c 3f ce 8d 7c 88 9a d5 f6 2a ad 9f 15  r...?..|....*...

De plus, nous observons un objet de type String Tag. En l'examinant avec do, nous pouvons lire son contenu et découvrir qu'il s'agit d'une commande : EMERGENCY_OPEN.

> do 0000789346a00560                                                                                                                                                                  
Name:        System.String
MethodTable: 0000789348b2d7c8
EEClass:     0000789348b15ca8
Tracked Type: false
Size:        50(0x32) bytes
File:        /usr/lib/dotnet/shared/Microsoft.NETCore.App/8.0.21/System.Private.CoreLib.dll
String:      EMERGENCY_OPEN
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
0000789348ab0980  40002d1        8         System.Int32  1 instance               14 _stringLength
0000789348abad30  40002d2        c          System.Char  1 instance               45 _firstChar
0000789348b2d7c8  40002d0       b0        System.String  0   static 0000789346a00008 Empty

En répétant cette analyse pour les autres objets FluentModbusConfig+ModbusEvent, on peut extraire d'autres paires Hash/Tag :

Hash Tag
09 d1 6a 8e 07 e8 c2 58 0d e6 0a 5a 76 f9 5d 21 72 c3 2e 9c 3f ce 8d 7c 88 9a d5 f6 2a ad 9f 15 EMERGENCY_OPEN
82 4f 57 50 31 b3 ac 37 55 e0 e1 b1 a8 3e df 4f bb a7 89 d0 0a 7c b5 7b 91 96 7c d3 aa bc 1f 60 EMERGENCY_CLOSE
ad 37 af 3b cc 33 85 a5 f4 6e 50 82 d7 70 c0 e0 38 0d 29 c1 4f 72 d2 e7 b6 c3 f5 5a bc 55 11 c1 SCHEDULE_OPERATION

Déchiffrement des MMSI

Une fois les paires Hash/Tag extraites, nous pouvons tenter de retrouver les secrets statiques utilisés par les attaquants pour générer ces hash. En utilisant des outils comme Hashcat, nous pouvons essayer de retrouver les valeurs originales.

cat <<EOF >> hashs.txt
09d16a8e07e8c2580de60a5a76f95d2172c32e9c3fce8d7c889ad5f62aad9f15
824f575031b3ac3755e0e1b1a83edf4fbba789d00a7cb57b91967cd3aabc1f60
ad37af3bcc3385a5f46e5082d770c0e0380d29c14f72d2e7b6c3f55abc5511c1
EOF

hashcat --potfile-disable -m 1400 -a 3 hashs.txt ?d?d?d?d?d?d?d?d?d --status -o found.txt
cat found.txt

Ce script utilise Hashcat pour effectuer une attaque par force brute sur les hash extraits, en supposant que les MMSI sont composés de 9 chiffres (0-9).

Cela permet d'identifier les MMSI ayant déclenché les actions spécifiques :
| Tag | MMSI |
|------------------|-------------|
| EMERGENCY_OPEN | 338123456 |
| EMERGENCY_CLOSE | 366654321 |
| SCHEDULE_OPERATION | 367000999 |

Découverte de l'Intention

Enfin, pour déterminer les paramètres de l'opération planifiée (délai d'ouverture et durée de fermeture), on peut examiner les champs DL et DT de l'objet FluentModbusConfig+ModbusEvent correspondant à SCHEDULE_OPERATION, toujours avec la commande do.

> do 78533a8e0c80                                                                                                                                                                      
Name:        FluentModbusConfig+ModbusEvent
MethodTable: 0000789348c44e10
EEClass:     0000789348d3d898
Tracked Type: false
Size:        48(0x30) bytes
File:        /home/scada/scada-wkr/ScadaCommWkr/bin/Debug/net8.0/ScadaCommWkr.dll
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
0000789348c43c00  4000018        8        System.Byte[]  0 instance 000078533a8e0cb0 <Field>k__BackingField
0000789348b2d7c8  4000019       10        System.String  0 instance 0000789346a005d0 <Tag>k__BackingField
0000789348bca3d0  400001a       18 ...Private.CoreLib]]  1 instance 000078533a8e0c98 <DL>k__BackingField
0000789348bca3d0  400001b       20 ...Private.CoreLib]]  1 instance 000078533a8e0ca0 <DT>k__BackingField

En examinant le contenu de DL et DT avec la commande dd, nous pouvons lire les valeurs brutes stockées dans ces champs en ignorant les 4 premiers octets qui correspondent au drapeau nullable qui se rajoute lorsqu'on utilise un objet Nullable en C#.

# Examiner le champ DL
> dd 000078533a8e0c98                                                                                                                                                                  
000078533a8e0c98: 00000001 00000006 00000001 00000001 00000000 00000000 48c43c00 00007893
000078533a8e0cb8: 00000020 00000000 3baf37ad a58533cc 82506ef4 e0c070d7 c1290d38 e7d2724f
000078533a8e0cd8: 5af5c3b6 c11155bc 00000000 00000000 4943dce0 00007893 00000000 00000000
000078533a8e0cf8: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
000078533a8e0d18: 4943da68 00007893 00000000 00000000 48c454b0 00007893 3a8e0d48 00007853
000078533a8e0d38: 3a8dff78 00007853 00000000 00000000 48c45ae8 00007893 00000000 00000000
000078533a8e0d58: 00000000 00000000 3a8e11d0 00007853 00000000 00000000 00000000 00000000
000078533a8e0d78: 48c4ae40 00007893 3a8e0d28 00007853 00000000 00000000 48c09188 00007893

# Examiner le champ DT
> dd 000078533a8e0ca0                                                                                                                                                                  
000078533a8e0ca0: 00000001 00000001 00000000 00000000 48c43c00 00007893 00000020 00000000
000078533a8e0cc0: 3baf37ad a58533cc 82506ef4 e0c070d7 c1290d38 e7d2724f 5af5c3b6 c11155bc
000078533a8e0ce0: 00000000 00000000 4943dce0 00007893 00000000 00000000 00000000 00000000
000078533a8e0d00: 00000000 00000000 00000000 00000000 00000000 00000000 4943da68 00007893
000078533a8e0d20: 00000000 00000000 48c454b0 00007893 3a8e0d48 00007853 3a8dff78 00007853
000078533a8e0d40: 00000000 00000000 48c45ae8 00007893 00000000 00000000 00000000 00000000
000078533a8e0d60: 3a8e11d0 00007853 00000000 00000000 00000000 00000000 48c4ae40 00007893
000078533a8e0d80: 3a8e0d28 00007853 00000000 00000000 48c09188 00007893 00000000 00000000

En analysant les valeurs hexadécimales, nous pouvons en déduire que :
- DL : 6
- DT : 1

Ainsi, l'opération planifiée consiste à ouvrir une porte maritime après un délai de 6 heures pour une durée de 1 heure avant de la refermer automatiquement.