Xcode 7 Bitcode workflow and Security Evaluation
With the release of Xcode 7, Apple added a new feature Bitcode [1] For Xcode:
New features often mean new attack surfaces. This article first introduces Bitcode and Bitcode-related workflows. After familiarizing yourself with the Bitcode workflow, we will evaluate Bitcode-related attacks, finally, the test methods and current test results for each attack are introduced.
What is Bitcode
Simply put, Bitcode is a binary representation of a LLVM-IR on a disk. For details about Bitcode, refer to [2]. Here we will use examples to give you a perceptual knowledge of Bitcode.
Write a simple C program to calculate the sum of two numbers. The Code is as follows:
int add(int a, int b){ int c = a + b; return c;}
Save the above program as add. c, and then compile the source program into Bitcode:
clang -emit-llvm -c add.c -o add.bc
Execute the above command to generate add. bc. We use the binary editor to open the generated file and view the file content:
Because Bitcode is a binary representation of the LLVM-IR, for example, it is basically unreadable without understanding the encoding method. Here we convert Bitcode into text:
llvm-dis add.bc -o add.ll
Open add. ll in a text editor and you can see that the LLVM-IR of the add function is as follows:
; ModuleID = 'add. bc 'target datalayout = "e-m: o-i64: 64-f80: 128-n8: 16: 32: 64-S128" target triple = "x86_64-apple-macosx10.11.0"; Function Attrs: nounwind ssp uwtable; the following is the LLVM-IR corresponding to add (); you can note that this representation will apply for a lot of variables,; interested students can understand the Static Single Assignment (SSA) define i32 @ add (i32% a, i32 % B) #0 {% 1 = alloca i32, align 4; variable bytes space, subsequently, this parameter is used to store a % 2 = alloca i32, align 4, variable 2 and 4 bytes, and variable B % c = alloca i32, align 4; variable c, and 4 bytes, it is subsequently used to store the results c store i32 % a, i32 * % 1, align 4; save a to the variable 1 store i32 % B, i32 * % 2, align 4; save B to variable 2% 3 = load i32, i32 * % 1, align 4; save number 1 to variable 3% 4 = load i32, i32 * % 2, align 4; Save the count now 2 to variable 4% 5 = add nsw i32 % 3, % 4; save the sum of variable 3 and variable 4 to store i32 % 5, i32 * % c, align 4 in variable 5; save variable 5 to result c % 6 = load i32, i32 * % c, align 4; save Result c to ret i32 % 6 in variable 6; return variable 6}
Comparing the source code with the added () function has been commented on the LLVM-IR said, we should have a perceptual knowledge of the LLVM-IR, below we take a look at the Bitcode workflow.
Workflow
Apple's workflow description:
“ When you archive for submission to the App Store, Xcode compiles your app into an intermediate representation. The App Store then compiles the bitcode down into the 64- or 32-bit executables as necessary.”
The preceding workflow can be divided into two phases:
1. When uploading an application to the AppStore, Xcode uploads the corresponding Bitcode together. 2. AppStore recompiles Bitcode into executable programs for users to download.
The complete workflow related to Bitcode is divided into the following issues or sub-processes and described separately:
1. Where is the Bitcode? 2. Method for embedding Bitcode 3. method for generating executable programs from Bitcode
Where is the Bitcode?
According to Apple's description, Bitcode is generated only in Archive, so a test project is created:
Run Archive and view the generated package structure:
After analysis, the Bitcode is not directly found in the above directory, and then check the generated MachO. Use MachOView to load the generated MachO. The result is as follows:
The final executable program contains segments and sections related to LLVM. Continue to view the corresponding Section information:
For example, Section _ bundle stores an xar document, extracts the xar document, and then uses the following command to unbind the document:
Unlock: xar-x-f XXX. xar
After unzipping, you can see the Bitcode file.
Summary: The Bitcode corresponding to the program is packaged into an xar document by Xcode and embedded into the MachO file.
The following describes how to embed Bitcode in MachO.
Embedding Bitcode
??Method 1
?? By comparing the Archive and non-Archive compilation parameters, we can find that by adding the compilation parameter-fembed-bitcode at the position shown in, Xcode can also embed Bitcode in MachO during normal Compilation:
??Method 2
?? Method 1 is very convenient, but IDE does a lot of work, so it is not easy to understand the specific process. Next we compile the executable file by ourselves. The executable program generated from the source code is mainly divided into two processes: Compilation and linking. to control these two processes, the following describes the configuration of Makefile and the parameters used in these two processes.
When using Makefile to compile iOS programs, some general configurations are as follows for your reference:
SDK_iOS := $(shell xcodebuild -version -sdk iphoneos Path)CC_iOS := $(shell xcrun --sdk iphoneos --find clang)LD_iOS := $(CC_iOS)SYS_ROOT = -isysroot $(SDK_iOS)SDK_SETTINGS_iOS = $(SYS_ROOT) -I$(SDK_iOS)/usr/include -I$(SDK_iOS)/usr/local/includeMIN_VER_iOS = -miphoneos-version-min=8.0ARCH_iOS = -arch arm64
Take main. m as an example to describe the required parameters for compilation:
CC_FLAGS_COMMON = -fblocks -std=gnu99 -fobjc-arc -g -fembed-bitcodeCC_FLAGS=-x objective-c $(ARCH_iOS) $(CC_FLAGS_COMMON)COMPILE_iOS_OBJ=$(CC_iOS) $(MIN_VER_iOS) $(SDK_SETTINGS_iOS) $(CC_FLAGS) $(COMPILE_iOS_OBJ) -c main.m -o main.o
Link main. o, AppDelegate. o, and ViewController. o to executable program parameters:
LDFLAGS=$(SYS_ROOT) \ -dead_strip \ -fembed-bitcode \ -fobjc-arc -fobjc-link-runtimeLINK_iOS_BIN=$(LD_iOS) $(ARCH_iOS) $(MIN_VER_iOS) $(LDFLAGS)LDFLAGS_CUSTOM=-framework Foundation -framework UIKit$(LINK_iOS_BIN) $(LDFLAGS_CUSTOM) AppDelegate.o ViewController.o main.o -o XBCTest
You can use the make command to embed Bitcode into an executable program by slightly modifying the Makefile snippet.
??Method 3
?? In this method, we will further break down the above steps. The specific process is as follows:
Source code → Bitcode → xar → executable program
Source code → Bitcode
In this process, we compile the source code of the iOS application into Bitcode. The following uses main. m as an example to describe the parameters used:
CC_FLAGS_COMMON_BC = $(CC_FLAGS_COMMON)COMPILE_iOS_32_BC = $(CC_iOS) -cc1 -x objective-c $(CC_FLAGS_COMMON_BC) -triple thumbv7-apple-ios8.0.0 -disable-llvm-optzns -target-abi apcs-gnu -mfloat-abi soft $(SYS_ROOT)COMPILE_iOS_64_BC = $(CC_iOS) -cc1 -x objective-c $(CC_FLAGS_COMMON_BC) -triple arm64-apple-ios8.0.0 -disable-llvm-optzns -target-abi darwinpcs $(SYS_ROOT) $(COMPILE_iOS_64_BC) -emit-llvm-bc main.m -o main.bc
After completing this process, we can get three Bitcode files:
1. main. bc
2. AppDelegate. bc
3. ViewController. bc
Bitcode → xar
In this step, we will package the three Bitcode files obtained above into an xar document. There is nothing special about packaging. It should be noted that it must be compatible with the xar generated by Xcode. The specific parameters are as follows:
Generation: xar-toc-cksum none-c-f BC. xar main. bc AppDelegate. bc ViewController. bc
Xar → executable program
To simplify the process, we will jump out of Makefile and use Xcode to clear the following compilation parameters:
Copy the generated BC. xar file to the root directory of the test project:
Edit the Other Linker Flags of the Project Settings and add:-Wl,-sectcreate ,__ LLVM ,__ bundle, $ (SRCROOT)/BC. xar, for example:
Compile the program and view the generated MachO file. You can see that Bitcode has been added to MachO.
As mentioned above, we have introduced the method of embedding Bitcode in MachO, which corresponds to the first process: "When uploading an application to AppStore, Xcode uploads the corresponding Bitcode together ", the second process is described below.
Method for generating executable programs from Bitcode
The second process is:"AppStore recompiles Bitcode into executable programs for users to download.". The second process is performed on Apple's Server. We cannot directly obtain the details, but it should all be based on the same tool chain. We can simulate this process.
Extract Bitcode from MachO
After the AppStore obtains the uploaded IPA, it first needs to extract the xar file containing Bitcode from the MachO file in the IPA. In Xcode's tool chain, segedit can be used to extract sections from MachO. The specific parameters for extracting xar are as follows:
segedit ./XBCTest -extract "__LLVM" "__bundle" Embedded-BC.xar
After the xar is extracted, unlock the xar:
Unlock: xar-x-f Embedded-BC.xar
Obtain the following Bitcode files:
You can also use the llvm-dis tool to process the above file into a readable form to understand the content of each file.
Generate executable programs
After Bitcode is available, you need to compile Bitcode into an executable program. There are two steps: Compile Bitcode into an Object file and link the Object file to the executable program.
????Compile Bitcode into an Object file
?? The Makefile snippets are as follows:
COMPILE_iOS_BC_2_OBJ=$(CC_iOS) $(MIN_VER_iOS) $(SYS_ROOT) $(ARCH_iOS) $(COMPILE_iOS_BC_2_OBJ) -c 1 -o 1.o$(COMPILE_iOS_BC_2_OBJ) -c 2 -o 2.o$(COMPILE_iOS_BC_2_OBJ) -c 3 -o 3.o$(COMPILE_iOS_BC_2_OBJ) -c 4 -o 4.o
??
Link an Object file to an executable program
??
The Makefile snippets are as follows:
LDFLAGS=$(SYS_ROOT) \ -dead_strip \ -fobjc-arc -fobjc-link-runtimeLINK_iOS_BIN=$(LD_iOS) $(ARCH_iOS) $(MIN_VER_iOS) $(LDFLAGS)LDFLAGS_CUSTOM=-framework Foundation -framework UIKit$(LINK_iOS_BIN) $(LDFLAGS_CUSTOM) 1.o 2.o 3.o 4.o -o XBCTest
As shown above, we have re-generated the executable program XBCTest from Bitcode.
Attack Surface
Let's first review Bitcode's local workflow: Xcode uploads the MachO embedded with Bitcode to the AppStore. Through analysis, we can find that there are two problems:
1. Consistency between MachO and the embedded Bitcode. That is, whether the Bitcode of program B can be embedded into program.
2. Whether the AppStore trusts Xcode without checking for consistency issues, thus allowing Malformed MachO to be uploaded to the AppStore.
After analyzing the possible problems, we think that if the Bitcode process and function are defective, the two goals can be threatened: common users and apple.
Common User
Bitcode is transparent to common users, so users cannot be directly attacked through its vulnerabilities. However, consistency may pose A threat to common users. Imagine: If program A submitted for AppStore review embeds Bitcode containing malicious code, normal users may download programs containing malicious code from the AppStore.
For this attack method, we call it Bitcode Injection. The following describes the implementation method of this attack and our test results.
Apple
If Malformed MachO can be uploaded to an Apple Server, the apple server requires two additional operations: unbind xar and compile Bitcode. If these two processes have problems, the DoS can be generated on the Apple Server, and the DoS can cause arbitrary code execution on the Apple Server.
In addition, Bitcode was originally a kind of serialization form of LLVM-IR, and LLVM-IR is a kind of intermediate form, has not been directly exposed before, and is now fully open, and is a binary format, this is very prone to problems. The process of generating executable files from Bitcode consists of the following sub-processes:
1. platform-independent IR-based code optimization.
2. The platform of IR is related and legal.
3. Optimization and code generation related to the platform.
These are the internal processes of the compiler. For various reasons, the traditional tests on the compiler mainly focus on the front-end Parser and Lexer, now some intermediate or back-end processes with Bitcode are also exposed. If the above process has a problem, the worst result is that the compiler's instruction generation can be controlled.
The above is an analysis of the attack. Later, we will introduce the ideas for testing xar and Bitcode, as well as the problems found.
Bitcode Injection
The method for implementing Bitcode Injection has been introduced in the previous section, but the method mentioned above is not concise enough. Here we will introduce a simpler method, the main idea is to use Xcode to the maximum extent. The specific implementation steps of this method are as follows:
1. Use Xcode to build the project XBCTest:
2. Copy the project XBCTest to obtain the project XBCTest2:
3. Modify the source code of the XBCTest2 project and embed malicious code:
4. Archive Project XBCTest2:
5. Obtain MachO and use segedit to extract xar containing Bitcode from MachO:
Extract xar: segedit./XBCTest-extract "_ LLVM" "_ bundle" BC. xar
6. Modify the link tag of the Project XBCTest and embed the extracted xar: BC. xar into the MachO file of the Project XBCTest.
7. Disable the Bitcode feature of the project XBCTest, Archive and upload it to the AppStore:
During the test, we did not embed malicious code. Instead, we found two completely different applications on the internet, Embedded One Bitcode into another MachO, and submitted it to the AppStore.
When you submit an application to the AppStore, you can perform two checks: static analysis is performed by Xcode locally; after the application is submitted, the Apple Server checks the application. However, applications constructed using Bitcode Injection can perform the following two checks:
After a long review, our application was rejected because our application and description were inconsistent. In the description, our application should look like the following:
However, after Apple's reviewers install the program, it looks like this:
There are at least three problems:
1. The Bitcode Injection method we use is correct. 2. Apple's reviewers reviewed the program compiled from Bitcode. 3. Consistency depends on human flesh. If the embedded malicious code does not affect the UI, it is possible to bypass the review.
Test xar
Ideas
Perform a fuzzy test on xar. The data generation method is based on the standard xar document for variation.
Test Results
Currently, some null pointers are used to solve the reference issue in Fuzz.
Test clang
Ideas
Perform a fuzzy test on the Bitcode-to-Object function in clang, and generate test data by means of variation.
Test Results
Through Fuzz of clang, we found some problems related to heap damage.
Summary
1. The Xcode 7 bitcode feature has opened a huge attacking surface, Apple shoshould do something to narrow it, for example: checking the bitcode is identical to the related MachO file.
2. in the above section, we have introduced in detail the attack surface and testing ideas for each attack. We hope these will be helpful for you to study the attack surface and security related to Bitcode.
References
[1] What's New in Xcode
[2] LLVM Bitcode File Format