Android Privilege Elevation Vulnerability CVE-2014-7920 & CVE-2014-7921 Analysis
This is Android mediaserver Elevation of Privilege Vulnerability, the use of CVE-2014-7920 and CVE-2014-7921 to achieve Elevation of Privilege, from 0 permission mentioned media permissions, where the CVE-2014-7921 affects Android 4.0.3 and later versions, CVE-2014-7920 affects Android 2.2 and later versions. Google did not fix these two vulnerabilities until Android5.1. The disclosure process for this vulnerability [1] is as follows:
In January 24, 2016, the vulnerability author published the vulnerability analysis and exploit [2]. After obtaining the exploit, they failed to run on several Android versions. Therefore, they analyzed the cause and learned how to exploit the vulnerability. The record is as follows. You are welcome to exchange and learn.
If you are not familiar with Android Binder, search for learning materials online and analyze the vulnerabilities below.
0x1 vulnerability causes
The preceding two vulnerabilities are detected in mediaserver. mediaserver is implemented in main_mediaserver.cpp [3], and its main () function initializes two services:
One is AudioFlinger [4], the service name is "media. audio_flinger", the other is AudioPolicyService [5], and the service name is "media. audio_policy ".
1.1 memory Write Vulnerability
The memory Write Vulnerability occurs in media. in audio_policy, the interface class is IAudioPolicyService [6] [7]. The startOutput () interface has the increasing memory Write vulnerability, and the stopOutput () interface has the decreasing memory Write Vulnerability.
The startOutput () interface is defined:
StartOutput (audio_io_handle_t output, audio_stream_type_t stream, int session = 0)
The stopOutput () interface is defined:
StopOutput (audio_io_handle_t output, audio_stream_type_t stream, int session = 0)
1) incremental Write Vulnerability of startOutput
Anyone familiar with Android Binder knows that the native class of this interface is BnAudioPolicyService [8]. When the client requests the "START_OUTPUT" code, that is, the startOutput () interface, BnAudioPolicyService: onTransact () after receiving the request, run the following code:
Continue calling AudioPolicyService: startOutput () [9] method:
MpAudioPolicy-> start_output (mpAudioPolicy, output, stream, session );
MpAudioPolicy is of the audio_policy type, and audio_policy: start_output () is defined as ap_start_output () in audio_policy_hal.cpp. This method is called:
Lap-> apm-> startOutput () is implemented by the AudioPolicyManagerBase: startOutput () method, which calls:
Let's take a look at the code for the AudioOutputDescriptor: changeRefCount () [10] method:
When both of the if statements are false, the mRefCount [stream] + = delta; statement is executed.
In this case, if the index stream can be controlled, the relative offset memory of the mRefCount memory can be modified to add delta. It happens that stream is one of the interface parameters and has not been verified. The delta input in AudioPolicyManagerBase: startOutput () is 1, that is, there is a memory Write vulnerability with an increment of 1. The memory Write vulnerability must meet the following conditions:
LisDuplicated () is False: Fortunately, most outputs are not duplicated by default.
L (delta + (int) mRefCount [stream]) <0: the value of mRefCount [stream] <0x7FFFFFFF is False only because of this judgment condition, this limits the memory Write Vulnerability and can only increase the memory value smaller than 0x7FFFFFFF.
2) stopOutput's dedecreasing Write Vulnerability
The stopOutput () interface is similar to the startOutput () interface. Let's look at the AudioPolicyManagerBase: stopOutput () method, which calls:
OutputDesc-> changeRefCount (stream,-1 );
Similar to the startOutput () interface, there are also the same write restrictions. The difference is that the memory Write Vulnerability of decreasing 1.
1.2 memory read Vulnerability
The memory Write vulnerability is also generated in "media. audio_policy". The problem lies in the isStreamActive () interface. This interface is defined:
Bool isStreamActive (audio_stream_type_t stream, uint32_t inPastMs)
Similar to the startOutput () interface, this interface calls the AudioPolicyManagerBase: isStreamActive () method, which calls:
That is, the AudioOutputDescriptor: isStreamActive () method. The code of this method is:
If the value of mRefCount [stream] is 0 based on the returned value of isStreamActive (), two conditions must be met:
LmRefCount [stream]! = 0;
Lns2ms (sysTime-mStopTime [stream])> inPastMs:
SysTime-mStopTime [stream] is a time difference value and a positive value. When inPastMs> = 0x80000000, this inequality is true.
Therefore, you can control the values of stream and inPastMs to determine whether the relative memory offset of mRefCount is 0.
0x2 exploitation of the previous 2.1 exploitation techniques Summary 1) use memory read to fuzzy match the audio_hw_device object
The audio_hw_device structure contains the audio hardware device function pointer, which is called in the interfaces provided by the "media. audio_policy" and "media. audio_flinger" services, which is conducive to writing exploit. The audio_hw_device structure is defined as follows:
[Source image]
Analysis shows that the reserved and function_ptrs-> get_supported_devices in the audio_hw_device object are 0, and other fields are not 0. The structure can be expressed in the form of 0 and non-0:
As mentioned above, the isStreamActive () interface can be used to determine whether the memory value is non-0. In this way, the feature is searched in memory based on the audio_hw_device structure, and the audio_hw_device object can be searched in the Upper/lower memory area of mRefCount.
2) use memory write to leak the memory address
"Media. audio_flinger" provides the getInputBufferSize () interface [11], which is defined:
The server code is:
When the client calls the getInputBufferSize () interface, the server finally calls the get_input_buffer_size () function, that is, audio_h1_cpp: adev_get_input_buffer_size (), and returns the size. According to the features of the arm command, in the disassembly of the get_input_buffer_size (dev, & config) function, the dev pointer is passed through R0, that is, the audio_hw_device object. After the function is executed, the return value is returned through R0. If you modify the get_input_buffer_size function pointer and point it to "bx lr", you can get the memory address of the audio_hw_device object.
The get_input_buffer_size () function pointer is also stored in the audio_hw_device object. The memory Write vulnerability allows audio_hw_device. get_input_buffer_size to point to a "bx lr" address to obtain the audio_hw_device object address.
2.2.
I have crash multiple times when debugging exploit. I put the updated exploit in runtime 4.3 _ r2.1, And the runtime environment is AVD 4.3 (4.3 _ r2.1 ).
1) mediaserver crash when searching for the audio_hw_device object
When you run exploit, you can search for the audio_hw_device object. However, mediaserver crash may be caused by a large memory range. You can narrow the search range. For example, set MAX_OFFSET to-3000.
2) The adev_open_output_stream () address is always not obtained.
In AVD 4.3, the author used the original gadget read_r0_offset_108 to always fail to get the pointer to the adev_open_output_stream function, and then found a gadget (thumb) in camera. goldfish. so ):
3) crash on android 4.4.2
At the beginning, the execution of exploit in AVD 4.4.2 was always unsuccessful. debugging found that the value of audio_hw_device. get_input_buffer_size was set to 0, for example:
Because the audio. primary. goldfish. so the base address is greater than 0x7FFFFFFF, that is, mRefCount [offset_get_input_buffer_size]> 0x7FFFFFFF, that is, a negative number. When incrementing by 1/decimal 1, changeRefCount () method, if) mRefCount [stream]) <0, set mRefCount [stream] to 0. In 4.4.2, it is difficult to use it successfully.
0x3 Vulnerability Analysis 3.1 search audio_hw_device object relative offset
The features in the audio_hw_device structure are used to search for matched objects in the mediaserver process as described in section 2.1-1. The memory read vulnerability of isStreamActive () is used to read the memory ing of 0/non-0 in the memory area of the mRefCount attachment, and then matches the audio_hw_device structure features to calculate the relative offset of the audio_hw_device object to the mRefCount.
3.2 Bypass ASLR
2.1-2 mentioned the use of the memory Write vulnerability to obtain the memory address. Next we will analyze how exploit uses the memory Write Vulnerability to bypass ASLR.
1) obtain the base address of audio. primary. goldfish. so.
First, modify the value of the audio_hw_device. get_input_buffer_size pointer. The original value of get_input_buffer_size is directed to bytes, and the value of get_input_buffer_size is changed to camera. goldfish. so: 0:
Library_offset indicates the lib base address used and audio. primary. goldfish. so library base address offset, gadget_offset is relative to the lib library base address offset; RELATIVE_ADDRESS_OF_GET_INPUT_BUFFER_SIZE is adev_get_input_buffer_size function address in audio. primary. goldfish. so offset; The modify_value () function is the encapsulation of the memory increment 1/decrease 1 operation. Gadget1 is:
Then, call AudioFlinger: getInputBufferSize () to jump to gadget1.
Uint32_t read_function_pointer_address = af-> getInputBufferSize (0, (audio_format_t) 0, (audio_channel_mask_t) 0 );
When executing the gadget1 command, R0 is dev, that is, the audio_hw_device object. For details, refer to the audio_hw_device structure. R0 + 0x64 is the value of open_output_stream, that is, adev_open_output_stream, Which is returned.
Then subtract the offset READ_FUNCTION_POINTER_OFFSET_FROM_BASE_ADDRESS of adev_open_output_stream in audio. primary. goldfish. so to get the base address of audio. primary. goldfish. so.
2) obtain the audio_hw_device object address
Modify audio_hw_device. get_input_buffer_size pointer to point it to libcamera_client.so: 0x208FC + 1, that is, gadget2:
When gadget2 is running, dev (R0) value is directly returned, that is, the address of the audio_hw_device object.
3) set write gadget
Modify audio_hw_device. get_input_buffer_size to libcamera_client.so: 0x208f0 + 1, and mark it as gadget3. The Code is as follows:
Let's take a look at the AudioFlinger: getInputBufferSize () method, where:
Read gadget3, write data call interface getInputBufferSize (address, 0, value) (this interface is defined as getInputBufferSize (bytes, audio_format_t format, bytes), go to get_input_buffer_size (dev, config, r0 is dev, R1 is & config, and gadget3 is executed as follows:
So far we direct audio_hw_device. get_input_buffer_size to gadget3, and then call getInputBufferSize (address, 0, value) to write value to the address-0xC memory.
3.3 layout of the Gadget Buffer
Write the system () function address and parameters to audio_hw_device.reserved, modify audio_hw_device.get_input_buffer_size to point to a call gadget, and call the gadget when get_input_buffer_size () is called again.
1) Write system () function parameters
View exploitation code:
The write32 () function writes data in two steps:
A) set the data offset.
Call modify_value (aps, g_primary_device_offset + 1, offset-g_current_write_offset); Modify dev. the content of version. This field is used as the array offset when data is written. dev. version is decreased from 0x200 for the first time until 0xC:
B) Write Data
Call af-> getInputBufferSize (g_primary_device_address + sizeof (uint32_t) + 12, (audio_format_t) 0, (audio_channel_mask_t) value) to trigger the execution of the gadget3 command:
In this case, R0 points to dev, and R1 is & config:
View [R1] memory. R1 [0] Is config. sampleRate is the address, which is the address to be written. address is & dev [0] + 4 + 12, and R1 [1] is config. channelMask is the value and the value is "/dat ":
The pseudocode of gadget3 is roughly as follows:
The data has been written into the audio_hw_device object after the gadget (a) is run:
2) write the system () function address
Calculate the address of the system () function based on the base address of audio. primary. goldfish. so, and call gadget3 to write it to dev + 36:
3) Write call gadget
Call gadget3 to modify the value of the audio_hw_device. get_input_buffer_size pointer. Use the pointer: libstagefright. so: 0x5EF88 + 1, which is recorded as gadget4.
Gadget4:
3.4 trigger Code Execution
The Code is as follows:
Af-> getInputBufferSize (0, (audio_format_t) 0, (audio_channel_mask_t) 0 );
When getInputBufferSize () is called, The gadget4 execution is triggered:
Register value:
Call the system () function to load the command/data/local/tmp/. The author wrote a remote shell named a, which is the shell obtained after running successfully and is the "media" permission:
0x4 reference
[1] http://bits-please.blogspot.com/2016/01/android-privilege-escalation-to.html
[2] https://github.com/laginimaineb/cve-2014-7920-7921
[3] http://androidxref.com/4.3_r2.1/xref/frameworks/av/media/mediaserver/main_mediaserver.cpp#40
[4] http://androidxref.com/4.3_r2.1/xref/frameworks/av/services/audioflinger/AudioFlinger.cpp
[5] http://androidxref.com/4.3_r2.1/xref/frameworks/av/services/audioflinger/AudioPolicyService.cpp
[6] http://androidxref.com/4.3_r2.1/xref/frameworks/av/include/media/IAudioPolicyService.h
[7] http://androidxref.com/4.3_r2.1/xref/frameworks/av/media/libmedia/IAudioPolicyService.cpp
Http://androidxref.com/4.3_r2.1/xref/frameworks/av/media/libmedia/IAudioPolicyService.cpp#384
Http://androidxref.com/4.3_r2.1/xref/frameworks/av/services/audioflinger/AudioPolicyService.cpp#234 [9]
Http://androidxref.com/4.3_r2.1/xref/hardware/libhardware_legacy/audio/AudioPolicyManagerBase.cpp#3083 [10]
[11] http://androidxref.com/4.3_r2.1/xref/frameworks/av/media/libmedia/IAudioFlinger.cpp#346
[12] https://github.com/Vinc3nt4H/cve-2014-7920-7921_update