Author: Shen Di
Please indicate the source of Reprint: http://blogs.360.cn/360mobile
Bluebox announced on July 30 that Android has had an APK signature problem since 2010 [1], and will release details on this year's blackhat.
By using this vulnerability, the authority can be enhanced and the sandbox limit can be broken. I have successfully exploited this vulnerability before the details are disclosed, and here I share some details of vulnerability exploitation.
1、 About APK signature
Android app needs to sign before it is released. The signature information is placed in the / meta-inf directory of the APK compressed package. This signature is usually verified for the following purposes:
– verify the integrity of the file data in the APK before installation.
– identify APK. If an APK has a system signature, it will have higher permissions; if the signatures of the two apks are consistent, the two applications can share data. In some specific scenarios, some applications will verify whether other applications have a specific signature. For example, WebKit will verify whether a plug-in is checked out by adobe.
Before the application is published, it can satisfy the requirement to sign APK with a self signed certificate. Use the "keytool – printcert – file cert.rsa" command to view the details of the certificate. The following is a normal self signed certificate. You can see that all people and signers are the same.
However, the signature file format conforms to the specifications of rfc2315 [2] and rfc2459 [3], so this certificate can also be issued by ca. If you use a CA issued certificate to issue the APK program, a certificate chain may exist in cert.rsa in the meta-inf directory, including the root certificate and the sub certificate.
On Android, we can use the following code to obtain all certificates in the certificate chain in the application:
sig=packageManager.getPackageInfo(pkgName,PackageManager.GET_PERMISSIONS| PackageManager.GET_SIGNATURES);
for(Signature sig : pkginfo.signatures)
Log.d("TEST", sig.toCharsString()+"n");
2、 The problem
The Google bug ID of this vulnerability is 13678484. From the repair code on AOSP, it can be found that the problem occurs in the jarverifier class of signature verification [4]. The key code is to add the option of chaincheck in jarutils, which can verify all certificates.
- private static X509Certificate findCert(Principal issuer, X509Certificate[] candidates) {
+ private static X509Certificate findCert(Principal issuer, X509Certificate[] candidates,
+ X509Certificate subjectCert, boolean chainCheck) {
for (int i = 0; i < candidates.length; i++) {
if (issuer.equals(candidates[i].getSubjectDN())) {
+ if (chainCheck) {
+ try {
+ subjectCert.verify(candidates[i].getPublicKey());
+ } catch (Exception e) {
+ continue;
+ }
+ }
return candidates[i];
}
}
What would have happened without this code? We can construct a malicious certificate as follows:
- A root certificate (marked as CA) is generated on the development machine and used to issue a sub Certificate (marked as sign)
- Then use this sub certificate to sign the APK we are going to release. At this time, the. RSA file in the APK will contain two certificates, one is sign and the other is ca. And all APK files can be verified with this RSA file
- Tampering with this RSA file only modifies the contents of the CA certificate (the CA after replacement is recorded as fakeca), does not modify the signerinfo part of the certificate, and does not affect packagemanger's use of signerinfo.encrypteddigest to verify the data integrity of the APK package before installation
- This APK can be successfully installed and contains two certificates, one is sign and the other is fakeca
If you don't know the pkcs7 signature file format as I did before, you will encounter some problems when trying to maliciously modify RSA files. I recommend using the open source project pyasn1 to modify RSA files. Because this format essentially uses der encoding, and uses ASN1 for serialization, pyasn1 allows us to quickly familiarize ourselves with this file format, and quickly start to tamper with signatures.
I put the file modification code on my GitHub [5]. Note that this is not a tool to automatically tamper with the certificate. It contains some hard coding of path and format. If you want to use this code, you need to understand the meaning of the code and make some modifications.
I use my own tools to tamper with a certificate issued by Adobe:
3、 How to use
As mentioned above, packagemanger does not verify the validity of all certificates in the verification certificate chain when installing the APK, as long as the specified sign can verify the validity of all files in the APK.
But another function of signing certificate, authentication, is affected by this vulnerability. Many places in the system use getpackageinfo to obtain the installation package certificate. If multiple certificates are obtained, it is generally considered that only one certificate can be trusted. For example, the logic of WebKit plug-in authentication of Adobe Flash player plug-in is as follows [6]
225 private static boolean containsPluginPermissionAndSignatures(PackageInfo pkgInfo) {
226
227
228 String permissions[] = pkgInfo.requestedPermissions;
229 if (permissions == null) {
230 return false;
231 }
232 boolean permissionOk = false;
233 for (String permit : permissions) {
234 if (PLUGIN_PERMISSION.equals(permit)) {
235 permissionOk = true;
236 break;
237 }
238 }
239 if (!permissionOk) {
240 return false;
241 }
242
243
244 Signature signatures[] = pkgInfo.signatures;
245 if (signatures == null) {
246 return false;
247 }
248 if (SystemProperties.getBoolean("ro.secure", false)) {
249 boolean signatureMatch = false;
250 for (Signature signature : signatures) {
251 for (int i = 0; i < SIGNATURES.length; i++) {
252 if (SIGNATURES[i].equals(signature)) {
253 signatureMatch = true;
254 break;
255 }
256 }
257 }
258 if (!signatureMatch) {
259 return false;
260 }
261 }
262
263 return true;
264 }
265
From this code and other codes of pluginmanager.java, we can see that WebKit certifies whether an APK is an adobe flashplayer plug-in as follows:
– the APK certificate contains Adobe's signature certificate, and the certificate data is written in the code (pluginmanager. Signature? 1), which is the signature used by adobe
– APK applied for android.webkit.permission.plugin permission
– APK declares a service. Intent is android.webkit.plugin, and a meta information is type. The value of type must be native
In the above requirements, the only mandatory limitation is certificate verification. Using fakeid, we have bypassed this limitation, and other verifications are naturally not a problem.
Before 4.4, any page that uses WebView and accesses the flash request page (such as Sina homepage) will be injected by our APK program. The following figure shows the injection of 2345 mobile browser:
Here you can see that com.example.noperm and com.adobe.flashplayer are both considered trusted plug-ins and injected successfully.
But just the injection was successful, and our code didn't get the chance to execute. We succeeded in the injection because WebKit called pluginmanager.getpluginclass to load the service that we registered to receive android.webkit.plugin before. But our code will not be called back. We need to further understand the specification of WebKit plug-in development.
291
292 Class<?> getPluginClass(String packageName, String className)
293 throws NameNotFoundException, ClassNotFoundException {
294 Context pluginContext = mContext.createPackageContext(packageName,
295 Context.CONTEXT_INCLUDE_CODE |
296 Context.CONTEXT_IGNORE_SECURITY);
297 ClassLoader pluginCL = pluginContext.getClassLoader();
298 return pluginCL.loadClass(className);
299 }
4、 Understand WebKit plugin and break through sandbox restrictions to execute code
As we have seen above, WebKit calls getpluginclass to load our APK into the virtual machine, but no code is triggered.
In fact, the core of WebKit plugin is native program, and the Java code in APK is only called by JNI. In order to trigger code execution, we also need to put a conforming so file in our APK for WebKit to call
AOSP happens to have a browser plug-in code [7], so it's easy for us to understand the plug-in writing specification.
First we need to export four interfaces.
extern "C" {
EXPORT NPError NP_Initialize(NPNetscapeFuncs* browserFuncs, NPPluginFuncs* pluginFuncs, void *java_env);
EXPORT NPError NP_GetValue(NPP instance, NPPVariable variable, void *value);
EXPORT const char* NP_GetMIMEDescription(void);
EXPORT void NP_Shutdown(void);
};
In the callback function, it tells the browser that we are a flash plug-in, so when WebKit encounters the flash request of the page, it will load the so file under the LIBS directory of all plug-ins and ask what kind of plug-in it is
All we have to do is tell the browser: I am a flash processing plug-in.
const char *NP_GetMIMEDescription(void)
{
return"application/x-shockwave-flash:swf:ShockwaveFlash;application/futuresplash:spl:Futu
reSplash Player";
}
In this way, our so is also loaded, and the code has broken through the sandbox to execute:
I put some key code and compiled POC on GitHub:
https://github.com/retme7/FakeID_poc_by_retme_bug_13678484/
Related links:
[1] http://bluebox.com/blog/technical/android-fake-id-vulnerability/
[2] https://www.ietf.org/rfc/rfc2315.txt
[3] https://www.ietf.org/rfc/rfc2459
[4] https://android.googlesource.com/platform/libcore/+/android-cts-4.1_r4%5E%21/
[5]https://github.com/retme7/FakeID_poc_by_retme_bug_13678484/
[6]AOSP/frameworks/base/core/java/android/webkit/PluginManager.java
[7]AOSP/frameworks/base/tests/BrowserTestPlugin/