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

155 lines
7.4 KiB
Markdown
Raw Normal View History

2021-05-21 16:38:18 +00:00
# iOS Serialisation and Encoding
#### NSCoding and NSSecureCoding
iOS comes with two protocols for object **serialisation** for Objective-C or `NSObject`s: **`NSCoding`** and **`NSSecureCoding`**. When a **class conforms** to either of the protocols, the data is serialized to **`NSData`**: a wrapper for **byte buffers**. Note that `Data` in Swift is the same as `NSData` or its mutable counterpart: `NSMutableData`. The `NSCoding` protocol declares the two methods that must be implemented in order to encode/decode its instance-variables. **A class using `NSCoding` needs to implement `NSObject` or be annotated as an @objc class**. The `NSCoding` protocol requires to implement encode and init as shown below.
```swift
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.
}
```
The issue with `NSCoding` is that the object is often already **constructed and inserted before you can evaluate** the class-type. This **allows an attacker to easily inject all sorts of data**. Therefore, the **`NSSecureCoding`** protocol has been introduced. When conforming to [`NSSecureCoding`](https://developer.apple.com/documentation/foundation/NSSecureCoding) you need to include:
```swift
static var supportsSecureCoding: Bool {
return true
}
```
when `init(coder:)` is part of the class. Next, when decoding the object, a check should be made, e.g.:
```swift
let obj = decoder.decodeObject(of:MyClass.self, forKey: "myKey")
```
The conformance to `NSSecureCoding` ensures that objects being instantiated are indeed the ones that were expected. However, there are **no additional integrity checks done** over the data and the data is not encrypted. Therefore, any secret data needs additional **encryption** and data of which the integrity must be protected, should get an additional HMAC.
#### Object Archiving with NSKeyedArchiver
`NSKeyedArchiver` is a concrete subclass of `NSCoder` and provides a way to encode objects and store them in a file. The `NSKeyedUnarchiver` decodes the data and recreates the original data. Let's take the example of the `NSCoding` section and now archive and unarchive them:
```swift
// archiving:
NSKeyedArchiver.archiveRootObject(customPoint, toFile: "/path/to/archive")
// unarchiving:
guard let customPoint = NSKeyedUnarchiver.unarchiveObjectWithFile("/path/to/archive") as?
CustomPoint else { return nil }
```
You can also save the info in primary plist `NSUserDefaults`:
```swift
// 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
It is a combination of the `Decodable` and `Encodable` protocols. A `String`, `Int`, `Double`, `Date`, `Data` and `URL` are `Codable` by nature: meaning they can easily be encoded and decoded without any additional work. Let's take the following example:
```swift
struct CustomPointStruct:Codable {
var x: Double
var name: String
}
```
By adding `Codable` to the inheritance list for the `CustomPointStruct` in the example, the methods `init(from:)` and `encode(to:)` are automatically supported. Fore more details about the workings of `Codable` check [the Apple Developer Documentation](https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types).
You can also use codable to save the data in the primary property list `NSUserDefaults`:
```swift
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)
}
```
#### JSON Encoding
There are a lot of thrid party libraries to encode data in JSON \(like exposed [here](https://mobile-security.gitbook.io/mobile-security-testing-guide/ios-testing-guide/0x06h-testing-platform-interaction#json-and-codable)\). However, Apple provides support for JSON encoding/decoding directly by combining `Codable` together with a `JSONEncoder` and a `JSONDecoder`:
```swift
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
There are multiple ways to do XML encoding. Similar to JSON parsing, there are various third party libraries, such as: [Fuzi](https://github.com/cezheng/Fuzi), [Ono](https://github.com/mattt/Ono), [AEXML](https://github.com/tadija/AEXML), [RaptureXML](https://github.com/ZaBlanc/RaptureXML), [SwiftyXMLParser](https://github.com/yahoojapan/SwiftyXMLParser), [SWXMLHash](https://github.com/drmohundro/SWXMLHash)
They vary in terms of speed, memory usage, object persistence and more important: differ in how they handle XML external entities. See [XXE in the Apple iOS Office viewer](https://nvd.nist.gov/vuln/detail/CVE-2015-3784) as an example. Therefore, it is key to disable external entity parsing if possible. See the [OWASP XXE prevention cheatsheet](https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html) for more details. Next to the libraries, you can make use of Apple's [`XMLParser` class](https://developer.apple.com/documentation/foundation/xmlparser)
When not using third party libraries, but Apple's `XMLParser`, be sure to let `shouldResolveExternalEntities` return `false`.
{% hint style="danger" %}
All these ways of serialising/encoding data can be **used to store data in the file system**. In those scenarios, check if the stored data contains any kind of **sensitive information**.
2021-05-21 17:13:19 +00:00
Moreover, in some cases you may be able to **abuse some serialised** data \(capturing it via MitM or modifying it inside the filesystem\) deserializing arbitrary data and **making the application perform unexpected actions** \(see [Deserialization page](../../pentesting-web/deserialization/)\). In these cases, it's recommended to send/save the serialised data encrypted and signed.
2021-05-21 16:38:18 +00:00
{% endhint %}
### References
{% embed url="https://mobile-security.gitbook.io/mobile-security-testing-guide/ios-testing-guide/0x06h-testing-platform-interaction\#testing-object-persistence-mstg-platform-8" %}