Insecure Dynamic Library Loading
Chargement non sécurisé de bibliothèque dynamique
Description
Le chargement dynamique de bibliothèques, s'il n'est pas implémenté de manière sécurisée, peut introduire une série de vulnérabilités importantes qui posent de graves risques pour la sécurité et l'intégrité d'une application :
Chemin de chargement non sécurisé : Cette vulnérabilité survient lorsqu'une application charge des bibliothèques à partir d'emplacements non approuvés ou manipulés. Si une application charge involontairement des bibliothèques provenant de sources malveillantes, elle ouvre la porte à des attaques potentielles. Les attaquants peuvent exploiter cette faiblesse en plaçant des bibliothèques malveillantes dans des emplacements non fiables, entraînant l'exécution de code avec des privilèges élevés ou un accès non autorisé à des ressources sensibles.
Détournement de bibliothèque (Library Hijacking) : Le détournement de bibliothèque se produit lorsqu'un attaquant substitue une bibliothèque légitime par une bibliothèque malveillante. En altérant le processus de chargement de la bibliothèque, l'attaquant peut tromper l'application pour qu'elle charge la bibliothèque malveillante à la place. Cela peut permettre un large éventail d'attaques, y compris l'injection de code, l'escalade de privilèges et l'accès non autorisé à des données sensibles.
Ces vulnérabilités, si elles ne sont pas traitées, peuvent avoir des conséquences graves. Les attaquants peuvent injecter du code malveillant dans une application, compromettre son intégrité, obtenir un accès non autorisé à des ressources critiques et potentiellement exploiter d'autres vulnérabilités de sécurité.
Exemples d'implémentations vulnérables :
void loadDependency(String unsanitized_user_input) {
// Dynamically load the library without sanitizing the input
final dylib = DynamicLibrary.open(unsanitized_user_input);
// Resolve and call a function from the loaded library that contains malicious code
final libraryMethod =
dylib.lookupFunction<Pointer<Utf8> Function(), Pointer<Utf8> Function()>(
'getSensitiveData');
final result = libraryMethod();
}
func loadDynamicLibrary(unsanitized_user_input: String) {
// Attempt to dynamically load the library without sanitizing user input
if let libraryHandle = dlopen(unsanitized_user_input, RTLD_NOW) {
// Library loaded successfully, resolve a function that could contain malicious code
if let method = dlsym(libraryHandle, "libraryMethod") {
typealias FunctionType = @convention(c) () -> String
let function = unsafeBitCast(libraryMethod, to: FunctionType.self)
let result = function()
}
dlclose(libraryHandle)
}
}
fun loadDependency(unsanitized_user_input : String) {
// Load the library dynamically without sanitizing the input
System.load(unsanitized_user_input)
// Perform some operation using the dynamically loaded library
val result = libraryMethod()
}
Recommandation
Lors du traitement du chargement de bibliothèques dynamiques, un développeur doit :
- Mettre en œuvre des techniques robustes de validation et de nettoyage des entrées pour empêcher les attaques de traversée de répertoires (directory traversal) et d'injection.
- Charger uniquement des bibliothèques provenant de sources fiables et vérifiées, en garantissant l'intégrité et l'authenticité des bibliothèques.
- Appliquer des contrôles d'accès stricts pour empêcher le chargement non autorisé de bibliothèques.
- Mettre à jour et corriger régulièrement les bibliothèques pour traiter les vulnérabilités connues.
- Employer des mécanismes de signature de code (code signing) et de vérification d'intégrité forts pour s'assurer de l'intégrité des bibliothèques chargées dynamiquement.
Exemples d'implémentations sécurisées :
void validation(String input){
// Verify library file integrity
Uint8List fileBytes = libraryFile.readAsBytesSync();
Digest fileDigest = SHA256Digest().process(fileBytes);
Uint8List expectedChecksum = Uint8List.fromList([...]);
if (fileDigest.bytes != expectedChecksum) {
return false; // Invalid file checksum
}
if (/* checks for .. patterns */ )
return false; // Invalid path
// Add additional validation checks specific to your implementation
return true;
}
void loadDependency(String unsanitized_user_input) {
// sanitize user input
if (validation(unsanitized_user_input) == false)
throw FormatException("invalid input");
// Dynamically load the library with a validated input
final dylib = DynamicLibrary.open(unsanitized_user_input);
// Resolve and call a function from the loaded library
final libraryMethod =
dylib.lookupFunction<Pointer<Utf8> Function(), Pointer<Utf8> Function()>(
'getSensitiveData');
final result = libraryMethod();
}
func validation(unsanitized_user_input: String){
guard let fileData = fileManager.contents(atPath: libraryURL.path) else {
return false // Failed to read file data
}
let fileDigest = SHA256.hash(data: fileData)
// Compare the calculated checksum with the expected checksum
let expectedChecksum: [UInt8] = [ /* Replace with your expected checksum */ ]
if fileDigest != expectedChecksum {
return false // Invalid file checksum
if (/* checks for .. patterns */ )
return false; // Invalid path
// Add additional validation checks specific to your implementation
return true;
}
}
func loadDynamicLibrary(unsanitized_user_input: String) {
// Validate your user input
if !validation(unsanitized_user_input){
throw CustomValidationError.invalidInput
}
// Attempt to dynamically load the library with validated user input
if let libraryHandle = dlopen(unsanitized_user_input, RTLD_NOW) {
// Library loaded successfully, resolve a function that could contain malicious code
if let method = dlsym(libraryHandle, "libraryMethod") {
typealias FunctionType = @convention(c) () -> String
let function = unsafeBitCast(libraryMethod, to: FunctionType.self)
let result = function()
}
dlclose(libraryHandle)
}
}
fun validation(input : String){
// Verify library file integrity
val fileBytes = Files.readAllBytes(libraryFile.toPath())
val digest = MessageDigest.getInstance("SHA-256")
val fileDigest = digest.digest(fileBytes)
val expectedChecksum = byteArrayOf(...)
if (!fileDigest.contentEquals(expectedChecksum)) {
return false
}
if (/* checks for .. patterns */ )
return false // Invalid path
// Add additional validation checks specific to your implementation
return true
}
fun loadDependency(unsanitized_user_input : String) {
// validate your uses input
if (!validation(unsanitized_user_input))
throw IllegalArgumentException("Invalid input")
// Load the library dynamically with the validated input
System.load(unsanitized_user_input)
// Perform some operation using the dynamically loaded library
val result = libraryMethod()
}
Liens
Normes
- OWASP_MASVS_L1:
- MSTG_PLATFORM_3
- OWASP_MASVS_L2:
- MSTG_PLATFORM_3
- PCI_STANDARDS:
- REQ_6_2
- REQ_6_3
- REQ_11_3
- OWASP_MASVS_v2_1:
- MASVS_CODE_4
- SOC2_CONTROLS:
- CC_2_1
- CC_4_1
- CC_7_1
- CC_7_2
- CC_7_4
- CC_7_5
- HIPAA_CONTROLS:
- SECURITY212
- SECURITY213
- SECURITY255