hacktricks/mobile-pentesting/ios-pentesting/ios-serialisation-and-encoding.md
2023-06-03 01:46:23 +00:00

9.4 KiB

NSCoding y NSSecureCoding

iOS cuenta con dos protocolos para la serialización de objetos para Objective-C o NSObjects: NSCoding y NSSecureCoding. Cuando una clase se ajusta a cualquiera de los protocolos, los datos se serializan a NSData: un envoltorio para buffers de bytes. Tenga en cuenta que Data en Swift es lo mismo que NSData o su contraparte mutable: NSMutableData. El protocolo NSCoding declara los dos métodos que deben implementarse para codificar/decodificar sus variables de instancia. Una clase que utiliza NSCoding necesita implementar NSObject o estar anotada como una clase @objc. El protocolo NSCoding requiere implementar encode e init como se muestra a continuación.

class CustomPoint: NSObject, NSCoding {

    //required by NSCoding:
    func encode(with aCoder: NSCoder) {
        aCoder.encode(x, forKey: "x")
        aCoder.encode(name, forKey: "name")
    }

    var x: Double = 0.0
    var name: String = ""

    init(x: Double, name: String) {
            self.x = x
            self.name = name
    }

    // required by NSCoding: initialize members using a decoder.
    required convenience init?(coder aDecoder: NSCoder) {
            guard let name = aDecoder.decodeObject(forKey: "name") as? String
                    else {return nil}
            self.init(x:aDecoder.decodeDouble(forKey:"x"),
                                name:name)
    }

    //getters/setters/etc.
}

El problema con NSCoding es que el objeto a menudo ya está construido e insertado antes de que puedas evaluar el tipo de clase. Esto permite a un atacante inyectar fácilmente todo tipo de datos. Por lo tanto, se ha introducido el protocolo NSSecureCoding. Al conformarse con NSSecureCoding, es necesario incluir:

static var supportsSecureCoding: Bool {
        return true
}

Cuando init(coder:) es parte de la clase. A continuación, al decodificar el objeto, se debe realizar una comprobación, por ejemplo:

let obj = decoder.decodeObject(of:MyClass.self, forKey: "myKey")

La conformidad con NSSecureCoding asegura que los objetos que se están instanciando son realmente los que se esperaban. Sin embargo, no se realizan controles adicionales de integridad sobre los datos y los datos no están cifrados. Por lo tanto, cualquier dato secreto necesita cifrado adicional y los datos cuya integridad deba ser protegida, deben tener un HMAC adicional.

Archivado de objetos con NSKeyedArchiver

NSKeyedArchiver es una subclase concreta de NSCoder y proporciona una forma de codificar objetos y almacenarlos en un archivo. El NSKeyedUnarchiver decodifica los datos y recrea los datos originales. Tomemos el ejemplo de la sección NSCoding y ahora los archivamos y desarchivamos:

// archiving:
NSKeyedArchiver.archiveRootObject(customPoint, toFile: "/path/to/archive")

// unarchiving:
guard let customPoint = NSKeyedUnarchiver.unarchiveObjectWithFile("/path/to/archive") as?
    CustomPoint else { return nil }

También puedes guardar la información en el plist primario NSUserDefaults:

// archiving:
let data = NSKeyedArchiver.archivedDataWithRootObject(customPoint)
NSUserDefaults.standardUserDefaults().setObject(data, forKey: "customPoint")

// unarchiving:
if let data = NSUserDefaults.standardUserDefaults().objectForKey("customPoint") as? NSData {
    let customPoint = NSKeyedUnarchiver.unarchiveObjectWithData(data)
}

Codable

Es una combinación de los protocolos Decodable y Encodable. Un String, Int, Double, Date, Data y URL son Codable por naturaleza: lo que significa que pueden ser codificados y decodificados fácilmente sin ningún trabajo adicional. Tomemos el siguiente ejemplo:

struct CustomPointStruct:Codable {
    var x: Double
    var name: String
}

Al agregar Codable a la lista de herencia de CustomPointStruct en el ejemplo, los métodos init(from:) y encode(to:) son compatibles automáticamente. Para obtener más detalles sobre el funcionamiento de Codable, consulte la documentación de desarrolladores de Apple.

También puede usar codable para guardar los datos en la lista de propiedades primarias NSUserDefaults:

struct CustomPointStruct: Codable {
        var point: Double
        var name: String
    }

    var points: [CustomPointStruct] = [
        CustomPointStruct(point: 1, name: "test"),
        CustomPointStruct(point: 2, name: "test"),
        CustomPointStruct(point: 3, name: "test"),
    ]

    UserDefaults.standard.set(try? PropertyListEncoder().encode(points), forKey: "points")
    if let data = UserDefaults.standard.value(forKey: "points") as? Data {
        let points2 = try? PropertyListDecoder().decode([CustomPointStruct].self, from: data)
    }

Codificación JSON

Existen muchas bibliotecas de terceros para codificar datos en JSON (como se explica aquí). Sin embargo, Apple proporciona soporte para la codificación/decodificación de JSON directamente combinando Codable junto con un JSONEncoder y un JSONDecoder:

struct CustomPointStruct: Codable {
    var point: Double
    var name: String
}

let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted

let test = CustomPointStruct(point: 10, name: "test")
let data = try encoder.encode(test)
let stringData = String(data: data, encoding: .utf8)

// stringData = Optional ({
// "point" : 10,
// "name" : "test"
// })

XML

Existen múltiples formas de codificar XML. Al igual que con el análisis de JSON, existen varias bibliotecas de terceros, como: Fuzi, Ono, AEXML, RaptureXML, SwiftyXMLParser, SWXMLHash

Varían en términos de velocidad, uso de memoria, persistencia de objetos y lo más importante: difieren en cómo manejan las entidades XML externas. Vea XXE en el visor de Office de Apple iOS como ejemplo. Por lo tanto, es clave deshabilitar el análisis de entidades externas XML si es posible. Consulte la hoja de trucos de prevención de XXE de OWASP para obtener más detalles. Además de las bibliotecas, se puede hacer uso de la clase XMLParser de Apple

Cuando no se utilizan bibliotecas de terceros, sino la clase XMLParser de Apple, asegúrese de que shouldResolveExternalEntities devuelva false.

{% hint style="danger" %} Todas estas formas de serializar/codificar datos se pueden utilizar para almacenar datos en el sistema de archivos. En esos escenarios, verifique si los datos almacenados contienen algún tipo de información sensible.
Además, en algunos casos puede ser posible abusar de algunos datos serializados (capturándolos a través de MitM o modificándolos dentro del sistema de archivos) deserializando datos arbitrarios y haciendo que la aplicación realice acciones inesperadas (ver la página de deserialización). En estos casos, se recomienda enviar/guardar los datos serializados cifrados y firmados. {% endhint %}

Referencias

{% embed url="https://mobile-security.gitbook.io/mobile-security-testing-guide/ios-testing-guide/0x06h-testing-platform-interaction#testing-object-persistence-mstg-platform-8" %}

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥