29 July 2015

The Android Stagefright vulnerability frightens the actors and the audience!

Android's BroadAnywhere threat was mistaken by many, and NRecursions clarified it for them. Now there's a surprising new bug called "Stagefright". Here are the details that matter:

Why is Stagefright dangerous?
Normally, if your computer or phone gets infected or hacked, it happens when you perform an action. Inserting an infected pen drive or clicking on a phishing link or visiting malicious websites.
Stagefright on the other hand, needs you to do nothing. A hacker just has to know your mobile number, and they can send you an MMS which will deliver a media file to your phone, which will also contain a mechanism through which the hacker can execute software code remotely. All this happens automatically. Even without you touching your phone. The hacker can then compromise your phone's security (allowing them to cause integer overflows, underflows, access files in the phone's external storage, access the phone's camera, audio and even execute the hackers code) and then delete the MMS. So you won't even know that your phone has been hacked.

Could your phone have been attacked already?
Unlikely. The bug was found by Joshua J Drake, a person at Zimperium mobile security, while he was searching for potential bugs. Zimperium informed Google about the bug and gave Google patches too.
News about the bug was made public on 21st July 2015, and now that people know of the bug, it's better to protect your phone ASAP. Full details of the bug will be revealed only on August 5th and 7th 2015 at the US Computer Security Conference and Defcon respectively.
Android versions 2.2 to version 5.1.1 are vulnerable. Cyanogen too.
Zimperium zLabs VP of Platform Research and Exploitation - See more at: http://blog.zimperium.com/experts-found-a-unicorn-in-the-heart-of-android/#sthash.5SWieNLS.dpuf
Zimperium zLabs VP of Platform Research and Exploitation - See more at: http://blog.zimperium.com/experts-found-a-unicorn-in-the-heart-of-android/#sthash.5SWieNLS.dpuf
Zimperium zLabs VP of Platform Research and Exploitation - See more at: http://blog.zimperium.com/experts-found-a-unicorn-in-the-heart-of-android/#sthash.5SWieNLS.dpuf
Zimperium zLabs VP of Platform Research and Exploitation - See more at: http://blog.zimperium.com/experts-found-a-unicorn-in-the-heart-of-android/#sthash.5SWieNLS.dpuf

How to protect your phone?
Open up your messaging service (the one you send SMS'es with), go to the Settings, scroll to the "Multimedia message (MMS) settings" and un-select the Auto-retrieve option and the Roaming auto-retrieve option. Do the same for Google Hangouts.
This will prevent the malicious MMS from getting automatically retrieved and loaded into Android's Stagefright module.
Of course the other way to protect yourself is to receive Google's Android updates.

Why is it called Stagefright?
Ever since version 2.2 (Froyo), Android has a native media playback engine called "Stagefright". The bug happens to exist in a few places in this software module.
This is a screenshot from Stagefright's page:



For programmers: 
Would you have been able to spot such a bug?
See the patches for Stagefright below:

The integer underflow patch:
     if (streamDependenceFlag) {
+        if (size < 2)
+            return ERROR_MALFORMED;
         offset += 2;
         size -= 2;
     }
@@ -145,11 +147,15 @@
             return ERROR_MALFORMED;
         }
         unsigned URLlength = mData[offset];
+        if (URLlength >= size)
+            return ERROR_MALFORMED;
         offset += URLlength + 1;
         size -= URLlength + 1;
     }
 
     if (OCRstreamFlag) {
+        if (size < 2)
+            return ERROR_MALFORMED;
         offset += 2;
         size -= 2;
 
The integer overflow patch:
     mTimeToSampleCount = U32_AT(&header[4]);
-    uint64_t allocSize = mTimeToSampleCount * 2 * sizeof(uint32_t);
+    uint64_t allocSize = mTimeToSampleCount * 2 * (uint64_t)sizeof(uint32_t);
     if (allocSize > SIZE_MAX) {
         return ERROR_OUT_OF_RANGE;
     }
@@ -376,7 +376,7 @@
     }
 
     mNumCompositionTimeDeltaEntries = numEntries;
-    uint64_t allocSize = numEntries * 2 * sizeof(uint32_t);
+    uint64_t allocSize = numEntries * 2 * (uint64_t)sizeof(uint32_t);
     if (allocSize > SIZE_MAX) {
         return ERROR_OUT_OF_RANGE;
     }
@@ -426,7 +426,7 @@
         ALOGV("Table of sync samples is empty or has only a single entry!");
     }
 
-    uint64_t allocSize = mNumSyncSamples * sizeof(uint32_t);
+    uint64_t allocSize = mNumSyncSamples * (uint64_t)sizeof(uint32_t);
     if (allocSize > SIZE_MAX) {
         return ERROR_OUT_OF_RANGE;
     }
  
 

Bounty

Google offers a bounty of upto $20000 (which is less, in my opinion) to people who find and report vulnerabilities in Google's codebase.

___________________________________________


Update: In response to the comment below, I had created some code that I uploaded on Coliru Viewer. Am making that code available below just in case Coliru later decides to remove content that's too old.

#include <stdint.h>
#include <iostream>
int main()
{
    uint64_t v = 2*sizeof(uint32_t);
    uint64_t vv = 2*(uint64_t)sizeof(uint32_t);
  
    if (v == vv) {std::cout<<"v and vv are equal\n";} else {std::cout<<"v and vv are not equal\n";}
  
    std::cout<<"v = "<<v<<" vv = "<<vv<<"\n";
  
    uint64_t lli = 18446744073709551615;
    uint32_t li =  4294967295;
    uint32_t li2 =  4294967295*2;
    uint64_t li2_64 =  4294967295*2;
    std::cout<<"long long int = "<<lli<<"\n";
    std::cout<<"long int = "<<li<<"\n";
    std::cout<<"long int * 2 = "<<li2<<"\n";
    std::cout<<"long int * 2 in long long int = "<<li2_64<<"\n";
  
    uint64_t li3_64 = li * 2 * 4;
    uint64_t li4_64 = li * 2;
    std::cout<<"li = "<<li<<"\n";
    std::cout<<"li4_64 when at li*2 = "<<li4_64<<"\n";
    li4_64 = li4_64 * 4;
    std::cout<<"li4_64 when at li*2*4 = "<<li4_64<<"\n";
    uint64_t li5_64 = li * 2 * (uint64_t)4;
    std::cout<< "li3_64 = " << li3_64 << " li4_64 = "<<li4_64<<"\n";
    if (li3_64 == li4_64) {std::cout<<"they are equal\n";} else {std::cout<<"they are not equal\n";}
    std::cout<<"li5 = "<<li5_64<<"\n";
}

2 comments:

Anonymous said...

That integer overflow patch:
- uint64_t allocSize = mTimeToSampleCount * 2 * sizeof(uint32_t);
+ uint64_t allocSize = mTimeToSampleCount * 2 * (uint64_t)sizeof(uint32_t);
if (allocSize > SIZE_MAX) {
return ERROR_OUT_OF_RANGE;
}

I'm not sure that's going to fix the problem, although honestly I haven't thought about it much. C++ order-of-operation rules state left-to-right evaluation for *, and integer promotion takes place for each pair of operands (there's no concept AFIK of the compiler looking at three operands and deciding to promote two of them based on a third) so it's going to evaluate(mTimeToSampleCount * 2) as a 32-bit op (potentially overflowing), and then convert to that result to 64-bit and multiply by 4 (the "(uint64_t)sizeof" part). Maybe I'm missing something, but I think a more correct fix would be:
+ uint64_t allocSize = mTimeToSampleCount * (uint64_t)(2 * sizeof(uint32_t));

(not the only possible fix, but this also takes into account that the last two operands are constants at compile time so the system can optimize it to just a const 8LL before doing the 64-bit multiply with mTimeToSampleCount).


uint64_t allocSize = mTimeToSampleCount * (uint64_t)(2 * sizeof(uint32_t));

(

Nav said...

Who are you Anonymous? I'd like to get to know you better. It's rare that people with good technical expertise write on NRecursions.

After reading your reply, I also checked up the standards, operator precedence rules and even the actual code of stagefright:
https://android.googlesource.com/platform/frameworks/av/+/ae6965ae7664aaea489a8d58358035610075c9af/media/libstagefright/SampleTable.cpp
https://github.com/pfalcon/android-platform-headers/blob/master/android-4.0.4_r1/frameworks/base/media/libstagefright/include/SampleTable.h

...and felt that you were right after all.

True, the multiplication operators are evaluated left to right, but this paragraph on MSDN is worth looking at: https://msdn.microsoft.com/en-us/library/2bxt6kc4.aspx
It says:
"The direction of evaluation does not affect the results of expressions that include more than one multiplication (*), addition (+), or binary-bitwise (& | ^) operator at the same level. Order of operations is not defined by the language. The compiler is free to evaluate such expressions in any order, if the compiler can guarantee a consistent result."

Try running this program I created: http://coliru.stacked-crooked.com/a/194920eedc9077d7
You'll see that for
uint32_t li = 4294967295;
uint64_t li3_64 = li * 2 * 4;
uint64_t li4_64 = li * 2;
li4_64 = li4_64 * 4;
uint64_t li5_64 = li * 2 * (uint64_t)4;

The output is:
li = 4294967295
li3_64 = 4294967288
li4_64 = 17179869176
li5 = 17179869176

So you see my friend; although you are right about the possibility of an overflow, it actually does not happen because the compiler sees the what comes after the second "*" too before evaluating the expression.