Solve the iGoat Exercise

This tutorial will show you how Ocular can help you solve the exercises and challenges presented in the iGoat. Per OAWSP, iGoat is a "learning tool for iOS app pentesting and security."

Prerequisites

For this tutorial, we will assume that you're running iGoat on an iOS Simulator, which will allow you to try out the following examples. Therefore, you should have an iOS simulator installed and ready to use.

Downloading and Configuring iGoat

To fetch the source files and open the project in Xcode, run:

git clone https://github.com/OWASP/igoat.git
cd igoat/iGoat/
open iGoat.xcodeproj/

Change your build settings to enable Link Time Optimization before you build iGoat (this gets you the LLVM bitcode that you can then load into Ocular).

You will also need to add -flto to Other C Flags and Other Linker Flags. You should have something like this in the end:

LTO Build Settings

Finally, to simplify setup, go to File > Project Settings and change Derived Data to Project-Relative Location:

Project Settings

At this point, build the app and run it using any iOS simulator.

Loading iGoat into Ocular

Once you build the iGoat application, you can load its LLVM bitcode into Ocular. To do this, you'll need to

  1. Navigate to the object directory and obtain the build files
  2. Convert the build artifacts into a Code Property Graph (CPG)

You can run the following in your Terminal to navigate to the object directory:

cd DerivedData/iGoat/Build/Intermediates.noindex/iGoat.build/Debug-iphonesimulator/iGoat.build/Objects-normal/x86_64

There are several object files, each of which is an LLVM bitcode file:

$ file BrokenCryptographyExerciseViewController.o
BrokenCryptographyExerciseViewController.o: LLVM bitcode, wrapper x86_64

You can convert these files into a CPG using the following:

<path-to-Ocular -installation>/llvm2cpg.sh --output=/tmp/iGoat.cpg.bin.zip *.o

After processing, you will see something like this:

[2020-03-27 11:55:27.097] [llvm2cpg] [info] Serializing CPG
[2020-03-27 11:55:27.907] [llvm2cpg] [info] Saving CPG on disk
[2020-03-27 11:55:42.170] [llvm2cpg] [info] CPG is successfully saved on disk: /tmp/iGoat.cpg.bin.zip
[2020-03-27 11:55:44.992] [llvm2cpg] [info] Shutting down

Now, run Ocular:

sl ocular

Load the CPG:

ocular> importCpg("/tmp/iGoat.cpg.bin.zip")

Yap Storage

Let's start with the Yap Storage exercise.

In the iGoat app running in the simulator, go to Data Protection (Rest) -> Yap Storage and click Start.

Yap Storage Exercise

This challenge is to log in using a username/password combination. Try a few combinations to ensure that the authentication mechanism is in place.

At this point, you can assume that the Yap database is somehow involved here since the challenge is called Yap Storage. To validate (or invalidate) this assumption, check if the YapDatabase class exists in the CPG:

ocular> cpg.typeDecl.nameExact("YapDatabase").p
List[String] = List(
"(TYPE_DECL,2842): AST_PARENT_FULL_NAME: <global>, AST_PARENT_TYPE: NAMESPACE_BLOCK, FULL_NAME: YapDatabase, INHERITS_FROM_TYPE_FULL_NAME: NSObject, IS_EXTERNAL: false, NAME: YapDatabase, ORDER: 0"
)

You can see from the results that YapDatabase exists.

The next thing to do is to find a valid username/password combination that you can use to log in. Assume that the credentials are hardcoded and written to the database during the app lifecycle.

Run the following to look at the available Yap database classes:

ocular> cpg.typeDecl.name("YapDatabase[A-Za-z]+").name.toList.sorted
List[String] = List(
"YapDatabaseCloudKit",
"YapDatabaseCloudKitConnection",
"YapDatabaseCloudKitOptions",
"YapDatabaseCloudKitRecordHandler",
"YapDatabaseCloudKitTransaction",
"YapDatabaseConnection",
...
"YapDatabaseViewState",
"YapDatabaseViewTransaction"
)

Note: You could have used .*, but doing so includes all types, including function pointers, ObjC metaclasses, etc., which may make the results difficult to parse.

Ocular returns are a few classes, but the one you would be interested in is YapDatabaseReadWriteTransaction. Let's look at the available methods:

ocular> cpg.typeDecl.nameExact("YapDatabaseReadWriteTransaction").method.name.toList.sorted
List[String] = List(
".cxx_destruct",
"addRegisteredExtensionTransaction:withName:",
"removeAllObjectsInAllCollections",
"removeAllObjectsInCollection:",
"removeAllValuesForExtension:",
"removeObjectForKey:inCollection:",
"removeObjectForKey:inCollection:withRowid:",
"removeObjectsForKeys:inCollection:",
"removeRegisteredExtensionTransactionWithName:",
"removeValueForKey:extension:",
"replaceMetadata:forKey:inCollection:",
"replaceMetadata:forKey:inCollection:withRowid:serializedMetadata:",
"replaceMetadata:forKey:inCollection:withSerializedMetadata:",
"replaceObject:forKey:inCollection:",
"replaceObject:forKey:inCollection:withRowid:serializedObject:",
"replaceObject:forKey:inCollection:withSerializedObject:",
"rollback",
"setBoolValue:forKey:extension:",
"setDataValue:forKey:extension:",
"setDoubleValue:forKey:extension:",
"setIntValue:forKey:extension:",
"setObject:forKey:inCollection:",
"setObject:forKey:inCollection:withMetadata:",
"setObject:forKey:inCollection:withMetadata:serializedObject:serializedMetadata:",
"setStringValue:forKey:extension:",
"setYapDatabaseModifiedNotificationCustomObject:",
"touchMetadataForKey:inCollection:",
"touchObjectForKey:inCollection:",
"yapDatabaseModifiedNotificationCustomObject"
)

As you can see, there are a few methods related to data storage. These three should be of particular interest to you:

"setObject:forKey:inCollection:",
"setObject:forKey:inCollection:withMetadata:",
"setObject:forKey:inCollection:withMetadata:serializedObject:serializedMetadata:"

Let's see if the application calls any of these methods:

ocular> cpg.call.name("setObject:forKey:inCollection:.*").name.p
List[String] = List(
"setObject:forKey:inCollection:",
"setObject:forKey:inCollection:",
"setObject:forKey:inCollection:withMetadata:serializedObject:serializedMetadata:",
"setObject:forKey:inCollection:withMetadata:serializedObject:serializedMetadata:"
)

Ocular shows that there are several calls to these methods, so let's narrow the results of interest by checking to see where they're called from:

> cpg.call.name("setObject:forKey:inCollection:.*").map(c => c.start.file.name.head + " -> " + c.name).p
List[String] = List(
"/tmp/igoat/iGoat/iGoat/YAP Data Storage/YAPExcersizeViewController.m -> setObject:forKey:inCollection:",
"/tmp/igoat/iGoat/iGoat/YAP Data Storage/YAPExcersizeViewController.m -> setObject:forKey:inCollection:",
"/tmp/igoat/iGoat/iGoat/ThirdParty/YapDatabase/YapDatabaseTransaction.m -> setObject:forKey:inCollection:withMetadata:serializedObject:serializedMetadata:",
"/tmp/igoat/iGoat/iGoat/ThirdParty/YapDatabase/YapDatabaseTransaction.m -> setObject:forKey:inCollection:withMetadata:serializedObject:serializedMetadata:"
)

From the results, you can see that setObject:forKey:inCollection:withMetadata:serializedObject:serializedMetadata:, which is called by YapDatabaseTransaction.m and must be some type of Yap database internals. Furthermore, setObject:forKey:inCollection: which is called from YAPExcersizeViewController.m seems to be what we're looking for as well.

Let's see if there any string arguments passed to the setObject:forKey:inCollection:

ocular> cpg.call.name("setObject:forKey:inCollection:").argument.isLiteral.map(l => l.order + ":" + l.code).p
List[String] = List(
"2:setObject:forKey:inCollection:",
"3:TheUnknown",
"4:YapKeyPassword",
"5:iGoat",
"2:setObject:forKey:inCollection:",
"3:JohnDoe@yap.com",
"4:YapKeyEmail",
"5:iGoat"
)

Bingo! At this point, you can map the following arguments to the method parameters:

  • Argument 2 is the implicit method name (ObjC selector)
  • Arguments 3, 4, and 5 map to the key, object, and collection, respectively.

Let's make the output easier to read:

ocular> cpg.call.name("setObject:forKey:inCollection:").map(c => c.start.argument.order(4).code.head + " = " + c.start.argument.order(3).code.head).p
List[String] = List("YapKeyPassword = TheUnknown", "YapKeyEmail = JohnDoe@yap.com")

Now, you can check to see if this email/password combination works. Return to the iOS simulator and provide the credentials.

Yap Storage Exercise Success

Yes, it works!