hacktricks/mobile-pentesting/ios-pentesting/ios-serialisation-and-encoding.md

8.7 KiB

### NSCoding e NSSecureCoding

O iOS vem com dois protocolos para **serialização** de objetos para Objective-C ou `NSObject`s: **`NSCoding`** e **`NSSecureCoding`**. Quando uma **classe está em conformidade** com qualquer um dos protocolos, os dados são serializados para **`NSData`**: um invólucro para **buffers de bytes**. Note que `Data` em Swift é o mesmo que `NSData` ou seu equivalente mutável: `NSMutableData`. O protocolo `NSCoding` declara os dois métodos que devem ser implementados para codificar/deodificar suas variáveis de instância. **Uma classe que usa `NSCoding` precisa implementar `NSObject` ou ser anotada como uma classe @objc**. O protocolo `NSCoding` exige a implementação de encode e init conforme mostrado abaixo.
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.
}

O problema com NSCoding é que o objeto muitas vezes já está construído e inserido antes de você poder avaliar o tipo de classe. Isso permite que um atacante injete facilmente todos os tipos de dados. Portanto, o protocolo NSSecureCoding foi introduzido. Ao conformar-se com NSSecureCoding, você precisa incluir:

static var supportsSecureCoding: Bool {
return true
}

quando init(coder:) faz parte da classe. Em seguida, ao decodificar o objeto, deve ser feita uma verificação, por exemplo:

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

A conformidade com NSSecureCoding garante que os objetos sendo instanciados são de fato aqueles que eram esperados. No entanto, não são realizadas verificações adicionais de integridade sobre os dados e os dados não são criptografados. Portanto, qualquer dado secreto precisa de criptografia adicional e dados cuja integridade deve ser protegida devem receber um HMAC adicional.

Arquivamento de Objetos com NSKeyedArchiver

NSKeyedArchiver é uma subclasse concreta de NSCoder e oferece uma maneira de codificar objetos e armazená-los em um arquivo. O NSKeyedUnarchiver decodifica os dados e recria os dados originais. Vamos pegar o exemplo da seção NSCoding e agora arquivar e desarquivar eles:

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

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

Você também pode salvar as informações no plist primário 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

É uma combinação dos protocolos Decodable e Encodable. Uma String, Int, Double, Date, Data e URL são naturalmente Codable: o que significa que podem ser codificados e decodificados facilmente sem nenhum trabalho adicional. Vamos ver o seguinte exemplo:

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

Adicionando Codable à lista de herança para o CustomPointStruct no exemplo, os métodos init(from:) e encode(to:) são automaticamente suportados. Para mais detalhes sobre o funcionamento do Codable, consulte a Documentação do Desenvolvedor Apple.

Você também pode usar codable para salvar os dados na lista de propriedades primária 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)
}

Codificação JSON

Existem muitas bibliotecas de terceiros para codificar dados em JSON (como exposto aqui). No entanto, a Apple oferece suporte para codificação/decodificação de JSON diretamente combinando Codable com um JSONEncoder e um 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

Existem várias maneiras de fazer codificação XML. Semelhante ao parsing de JSON, existem várias bibliotecas de terceiros, como: Fuzi, Ono, AEXML, RaptureXML, SwiftyXMLParser, SWXMLHash

Elas variam em termos de velocidade, uso de memória, persistência de objetos e mais importante: diferem na forma como lidam com entidades externas XML. Veja XXE no visualizador de Office da Apple iOS como exemplo. Portanto, é fundamental desativar o parsing de entidades externas, se possível. Veja a folha de dicas de prevenção de XXE da OWASP para mais detalhes. Além das bibliotecas, você pode usar a classe XMLParser da Apple

Quando não estiver usando bibliotecas de terceiros, mas sim o XMLParser da Apple, certifique-se de que shouldResolveExternalEntities retorne false.

{% hint style="danger" %} Todas essas formas de serializar/codificar dados podem ser usadas para armazenar dados no sistema de arquivos. Nesses cenários, verifique se os dados armazenados contêm qualquer tipo de informação sensível.
Além disso, em alguns casos, você pode ser capaz de abusar de alguns dados serializados (capturando-os via MitM ou modificando-os dentro do sistema de arquivos) desserializando dados arbitrários e fazendo com que o aplicativo execute ações inesperadas (veja a página de Deserialization). Nestes casos, é recomendado enviar/salvar os dados serializados criptografados e assinados. {% endhint %}

Referências

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

Aprenda hacking no AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Outras maneiras de apoiar o HackTricks: