Ce code permet de convertir le répertoire désigné (et donc tout son contenu) en fichier image ISO compressé et directement utilisable en mounting.
Les malwares les plus avancés (types engineers) utilisent ce processus pour la simple et bonne raison qu'il n'est nullement contrôlé, ni par le système d'exploitation ni par les systèmes de sécurité opérationnelle. Le fait de transférer (de la machine infectée au master server) un unique fichier compressé plutôt que plusieurs petits fichiers à la suite réduit le nombre de trames TCP émises et, in extenso, réduit l'activité du contrôle ICMP ; chose qui permet généralement de tromper la vigilance des IDS/IPS lorsque ceux-ci ne sont pas configurés en haute sensibilité.
La rapidité de la conversion dépend de la taille du répertoire, mais ce code est optimisé (réduction des descripteurs de contrôle de validité), donc relativement rapide ; lors du transfert vers l'extérieur (stealing), l'encapsulation prend en contre-partie un certain temps afin que TCP vérifie l'intégrité des offset d'en-tête, mais une fois terminée, l'opération est relativement rapide.
En extrapôlant, et afin d'optimiser le spreading du malware, ce-dernier pourrait se dupliquer dans le répertoire désigné, et ensuite lancer l'opération de conversion en ISO, il serait donc intégré à l'image disque et pourrait infecter n'importe quelle machine par l'intermédiaire du lecteur sur laquelle l'image serait ensuite montée ; les antivirus ne vérifiant pas la décompression et l'exécution des ISO, c'est extrêmement efficace.
A titre d'exemple, ce module est embarqué par des malware type Flame ou Stuxnet dont le but, en plus de détourner les systèmes SCADA iraniens, était de voler tous les fichiers techniques concernant les installations nucléaires ciblées, répertoires faisant très souvent plusieurs gigaoctets et passant donc très bien en ISO.
NB : j'ai essayé de commenter au maximum.
-----------------------------------------------------------------------
-----------------------------------------------------------------------
[Veille technologique]
- Activer le hash millimétrique sur l'IDS/IPS, c'est à dire le découpage des paquets à raison d'une séparation tripartite plutôt que bipartite, oblige TCP à générer des checksum de contrôle et des numéros de séquence surnuméraires ; ceci ne pose aucun problème lors du transfert de fichiers classiques, mais les fichiers ISO sont des fichiers lourds, et donc entraîne une perte de cohérence hautement significative, ce qui aboutit dans 98% des cas à une corruption de l'ISO avant même qu'il arrive sur le master server.
- Utiliser un système de protection opérationnelle intégrant un contrôleur de conversion standard fichier->ISO, bloquant les tentatives effectuées par toute application non-autorisée manuellement.
Les malwares les plus avancés (types engineers) utilisent ce processus pour la simple et bonne raison qu'il n'est nullement contrôlé, ni par le système d'exploitation ni par les systèmes de sécurité opérationnelle. Le fait de transférer (de la machine infectée au master server) un unique fichier compressé plutôt que plusieurs petits fichiers à la suite réduit le nombre de trames TCP émises et, in extenso, réduit l'activité du contrôle ICMP ; chose qui permet généralement de tromper la vigilance des IDS/IPS lorsque ceux-ci ne sont pas configurés en haute sensibilité.
La rapidité de la conversion dépend de la taille du répertoire, mais ce code est optimisé (réduction des descripteurs de contrôle de validité), donc relativement rapide ; lors du transfert vers l'extérieur (stealing), l'encapsulation prend en contre-partie un certain temps afin que TCP vérifie l'intégrité des offset d'en-tête, mais une fois terminée, l'opération est relativement rapide.
En extrapôlant, et afin d'optimiser le spreading du malware, ce-dernier pourrait se dupliquer dans le répertoire désigné, et ensuite lancer l'opération de conversion en ISO, il serait donc intégré à l'image disque et pourrait infecter n'importe quelle machine par l'intermédiaire du lecteur sur laquelle l'image serait ensuite montée ; les antivirus ne vérifiant pas la décompression et l'exécution des ISO, c'est extrêmement efficace.
A titre d'exemple, ce module est embarqué par des malware type Flame ou Stuxnet dont le but, en plus de détourner les systèmes SCADA iraniens, était de voler tous les fichiers techniques concernant les installations nucléaires ciblées, répertoires faisant très souvent plusieurs gigaoctets et passant donc très bien en ISO.
NB : j'ai essayé de commenter au maximum.
-----------------------------------------------------------------------
Code:
void Folder2Iso(LPCTSTR lpszDir, LPCTSTR lpszIso, LPCTSTR lpVolumeName, LPCTSTR lpPublisher, const SYSTEMTIME* creationTime, BOOL bJoliet, BOOL* bStop, PPROGRESS_ROUTINE pProgressRoutine) { HANDLE hThread; BYTE OneSector[SECTOR_SIZE]; // création d'une dimension virtuelle DWORD d; list<Folder*>::const_iterator i; PROGRESS_STRUCT ps; // contrôleur de conversion ps.bFinished = FALSE; ps.progressRoutine = pProgressRoutine; HANDLE hIsoFile = CreateFile(lpszIso, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); // création d'un objet 'file' destiné à recevoir l'ISO ZeroMemory(OneSector, sizeof OneSector); // attribution d'une zone mémoire liée à la dimension virtuelle int currentSector = bJoliet ? 20 : 19; // secteur désigné dans l'arbre itératif for(int j = 0; j < currentSector; j++) WriteFile(hIsoFile, OneSector, sizeof OneSector, &d, 0); // procédure d'écriture de l'ISO TCHAR szDir[MAX_PATH]; LPTSTR lpEnd = bnstrcpy(szDir, lpszDir); // EOF LPTSTR lpIdent = lpEnd - 1; list<Folder*> dirList; // arbre contenant les différents secteurs Folder root(0, dirList, szDir, lpIdent, lpEnd, 0, bJoliet); // racine d'indexation root.SetNumber(1); // premier index selectionné if(*bStop) goto closeFile; root.SetExtent(¤tSector, bJoliet); int extent = 0; ps.bFinished = FALSE; ps.current = &extent; ps.max = currentSector; DWORD dwThreadID; hThread = CreateThread(0, 0, ProgressRoutine, &ps, 0, &dwThreadID); // création du main thread if(*bStop) goto closeFile; root.Write(hIsoFile, bJoliet, bStop, &extent); if(*bStop) goto closeFile; PathTableCompare cmp; dirList.sort(cmp); // tri des entrées de l'arbre itératif if(*bStop) goto closeFile; int pathTableSize1 = 10, pathTableSize2 = 10; // première entrée égale à 10 i = dirList.begin(); int currentNumber = 2; while(i != dirList.end()) { (*i)->SetNumber(currentNumber++); // parcours de l'arbre pathTableSize1 += sizeof(PATH_TABLE_RECORD) + (*i)->m_shortLength - 1; // 1ère entrée if(pathTableSize1 & 1) pathTableSize1++; if(bJoliet) { pathTableSize2 += sizeof(PATH_TABLE_RECORD) + 2 * (*i)->m_length - 1; // 2ème entrée if(pathTableSize2 & 1) pathTableSize2++; } else pathTableSize2 = pathTableSize1; i++; } if(*bStop) return; // création de la table d'écriture contenant l'index, l'entrée de l'arbre et la taille du buffer LPBYTE lpMem = (LPBYTE)Alloc(pathTableSize2 & 0x7FF ? (pathTableSize2 & ~0x7FF) + 0x800 : pathTableSize2), ptr; // enregistrement de la table en mémoire temporaire LPPATH_TABLE_RECORD pathTableRec = (LPPATH_TABLE_RECORD)lpMem; pathTableRec->Length = 2; pathTableRec->ParentNumber = 1; DWORD LPathTable1, MPathTable1, LPathTable2, MPathTable2; // écriture de la première table pathTableRec = (LPPATH_TABLE_RECORD)lpMem; ptr = lpMem + 10; LPathTable1 = currentSector; pathTableRec->ExtentLocation = root.GetExtent1(); i = dirList.begin(); while(i != dirList.end()) { if(*bStop) goto releaseMem; pathTableRec = (LPPATH_TABLE_RECORD)ptr; (*i)->FillLPathTableRec(pathTableRec); // remplissage de la table ptr += sizeof(PATH_TABLE_RECORD) - 1 + pathTableRec->Length; // on se déplace dans la table afin d'écrire uniformément if((LPARAM)ptr & 1) ptr++; i++; } DWORD size = (DWORD)(ptr - lpMem); if(size & 0x7FF) { size &= ~0x7FF; size += 0x800; } currentSector += size / 0x800; // on change de secteur WriteFile(hIsoFile, lpMem, size, &d, 0); // écriture de la table dans le fichier de destination // écriture de la deuxième table (même procédure) pathTableRec = (LPPATH_TABLE_RECORD)lpMem; ptr = lpMem + 10; MPathTable1 = currentSector; pathTableRec->ExtentLocation = INVERT32(root.GetExtent1()); i = dirList.begin(); while(i != dirList.end()) { if(*bStop) goto releaseMem; pathTableRec = (LPPATH_TABLE_RECORD)ptr; (*i)->FillMPathTableRec(pathTableRec); ptr += sizeof(PATH_TABLE_RECORD) - 1 + pathTableRec->Length; if((LPARAM)ptr & 1) ptr++; i++; } size = (DWORD)(ptr - lpMem); if(size & 0x7FF) { int x = 0x800 - (size & 0x7FF); ZeroMemory(lpMem + (size & 0x7FF), x); size &= ~0x7FF; size += 0x800; } currentSector += size / 0x800; WriteFile(hIsoFile, lpMem, size, &d, 0); if(bJoliet) { // écriture des tables dans le secteur désigné pathTableRec = (LPPATH_TABLE_RECORD)lpMem; ptr = lpMem + 10; LPathTable2 = currentSector; pathTableRec->ExtentLocation = root.GetExtent2(); i = dirList.begin(); while(i != dirList.end()) { if(*bStop) goto releaseMem; pathTableRec = (LPPATH_TABLE_RECORD)ptr; (*i)->FillLPathTableRec2(pathTableRec); ptr += sizeof(PATH_TABLE_RECORD) - 1 + pathTableRec->Length; if((LPARAM)ptr & 1) ptr++; i++; } size = (DWORD)(ptr - lpMem); if(size & 0x7FF) { int x = 0x800 - (size & 0x7FF); ZeroMemory(lpMem + (size & 0x7FF), x); size &= ~0x7FF; size += 0x800; } currentSector += size / 0x800; WriteFile(hIsoFile, lpMem, size, &d, 0); // écriture de la n+1 table pathTableRec = (LPPATH_TABLE_RECORD)lpMem; ptr = lpMem + 10; MPathTable2 = currentSector; pathTableRec->ExtentLocation = INVERT32(root.GetExtent2()); i = dirList.begin(); while(i != dirList.end()) { if(*bStop) goto releaseMem; pathTableRec = (LPPATH_TABLE_RECORD)ptr; (*i)->FillMPathTableRec2(pathTableRec); ptr += sizeof(PATH_TABLE_RECORD) - 1 + pathTableRec->Length; if((LPARAM)ptr & 1) ptr++; i++; } size = (DWORD)(ptr - lpMem); if(size & 0x7FF) { int x = 0x800 - (size & 0x7FF); ZeroMemory(lpMem + (size & 0x7FF), x); size &= ~0x7FF; size += 0x800; } currentSector += size / 0x800; WriteFile(hIsoFile, lpMem, size, &d, 0); } // libération mémoire releaseMem: Free(lpMem); if(*bStop) goto closeFile; SetFilePointer(hIsoFile, 16 * SECTOR_SIZE, 0, FILE_BEGIN); // on se replace au début de l'ISO PRIMARY_VOLUME_DESCRIPTOR desc; // descripteur principal ZeroMemory(&desc, sizeof desc); // récupération des informations du volume courant desc.VolumeDescType = 1; desc.VolumeDescVersion = 1; memcpy(desc.StandardIdentifier, "CD001", 5); strcpy(desc.system_id, "Win32"); TCharTo9660(desc.volume_id, lpVolumeName); SET32(desc.VolumeSpaceSize, currentSector); desc.VolumeSetSize = 0; desc.VolumeSequenceNumber = 0x01000001; desc.LogicalBlockSize = 0x00080800; SET32(desc.PathTableSize, pathTableSize1); desc.TypeLPathTable = LPathTable1; desc.TypeMPathTable = INVERT32(MPathTable1); FillRecord(&desc.RootDirRecord, &root.m_time, root.GetSize1(), 0, root.GetExtent1(), 34, 2); TCharTo9660(desc.publisher_id, lpPublisher); TCharTo9660(desc.preparer_id, lpPublisher); // écriture de la date de conversion afin de valider la nouvelle image char date[20]; _itoa(creationTime->wYear, date, 10); date[4] = creationTime->wMonth / 10 + '0'; date[5] = creationTime->wMonth % 10 + '0'; date[6] = creationTime->wDay / 10 + '0'; date[7] = creationTime->wDay % 10 + '0'; date[8] = creationTime->wHour / 10 + '0'; date[9] = creationTime->wHour % 10 + '0'; date[10] = creationTime->wMinute / 10 + '0'; date[11] = creationTime->wMinute % 10 + '0'; date[12] = creationTime->wSecond / 10 + '0'; date[13] = creationTime->wSecond % 10 + '0'; date[14] = 0; // copie complète du buffer (tables n....n+1) dans le fichier ISO memcpy(desc.CreationDate, date, 14); memcpy(desc.ModificationDate, date, 14); desc.FileStructureVersion = 1; WriteFile(hIsoFile, &desc, sizeof desc, &d, 0); if(bJoliet) { // ouverture d'un deuxième descripteur pour contrôle de fin d'écriture desc.VolumeDescType = 2; TCharToJoliet(desc.volume_id, lpVolumeName); TCharToJoliet(desc.system_id, TEXT("Win32")); FillRecord(&desc.RootDirRecord, &root.m_time, root.GetSize2(), 0, root.GetExtent2(), 34, 2); TCharToJoliet(desc.publisher_id, lpPublisher); TCharToJoliet(desc.preparer_id, lpPublisher); *(DWORD*)desc.reserved3 = '\0E/%'; SET32(desc.PathTableSize, pathTableSize2); desc.TypeLPathTable = LPathTable2; desc.TypeMPathTable = INVERT32(MPathTable2); WriteFile(hIsoFile, &desc, sizeof desc, &d, 0); } // fermeture de session + EOF ZeroMemory((LPBYTE)&desc + 6, sizeof desc - 6); desc.VolumeDescType = 255; WriteFile(hIsoFile, &desc, sizeof desc, &d, 0); closeFile: CloseHandle(hIsoFile); if(hThread) { ps.bFinished = 1; WaitForSingleObject(hThread, INFINITE); } }
[Veille technologique]
- Activer le hash millimétrique sur l'IDS/IPS, c'est à dire le découpage des paquets à raison d'une séparation tripartite plutôt que bipartite, oblige TCP à générer des checksum de contrôle et des numéros de séquence surnuméraires ; ceci ne pose aucun problème lors du transfert de fichiers classiques, mais les fichiers ISO sont des fichiers lourds, et donc entraîne une perte de cohérence hautement significative, ce qui aboutit dans 98% des cas à une corruption de l'ISO avant même qu'il arrive sur le master server.
- Utiliser un système de protection opérationnelle intégrant un contrôleur de conversion standard fichier->ISO, bloquant les tentatives effectuées par toute application non-autorisée manuellement.
Commentaire