5

When I was reversing some well known Android chat applications (I can not disclose which specific ones, but all of them where owned by companies with 1 billion+ capital and have hundreds of millions of accounts), I saw an interesting feature in C++ code.

These applications read uninitialized data and sent it to their web service.

In pseudocode, they do the following job:

static constexpr const size_t uninitialized_data_size = 1024;
auto uninitialized_data = malloc(uninitialized_data_size);
HttpPost("http://my-url.com", uninitialized_data, uninitialized_data_size);

Of course, we can think that it is just a bug... But when multiple huge companies do the same thing, I'm starting to think: why?

Does anyone have idea what information uninitialized data can supply to security team? Is it just repeated error made by accident, or they extract some useful information from uninitialized data that they send to their server on regular basis?

  • Interesting question. Is it on a .so file or these applications all written in C++? – de6f Aug 5 at 18:10
  • 1
    It is SO file that implements Java native methods via JNI in typical Android application. I suspect that it may be any security algorithm. At least on application startup, uninitialized data has more or less predictable values; as longer application runs, as more random data is here. However, it is question if malloc can return memory used by Java before that; I have no idea how does Java handles memory in low level. Most of code runs via Java, so Java memory is less predictable than C++ one in such application. – Vitalii Aug 6 at 6:37
  • Are you sure you have an actual malloc() call and not something else, e.g. calloc()? – Igor Skochinsky Aug 7 at 6:48
  • Yes, sure, malloc. – Vitalii Aug 7 at 6:54
1

There were some cases where uninitialized memory was used as an entropy source, e.g. OpenSSL, but I doubt this is happening here. Possibly there is something not present in the snippet you posted, or it could indeed be a genuine bug.

  • But why to send entropy from Android device to server? I suspect they somehow detect emulators in this way; for example, may be emulator has different base address range, etc. But I have no specific idea. – Vitalii Aug 8 at 6:14
  • 1
    maybe emulator always has memory filled with a specific pattern? – Igor Skochinsky Aug 8 at 8:17
1

I have not 100% idea how do they use this information, but what I suspect from information supplied by other participants of this conversation and also information from Internet:

  • In Android JNI application, Java doesn't use malloc to allocate Java object. Instead, Java has its own memory manager. So, Java code has a very little influence on what malloc return.
  • malloc prefers to use the same arena for the same thread if possible; so, in most cases other JNI calls made from other threads will not influence what does malloc returns.
  • It means, that malloc will often return data allocated by the same thread. In JNI, we usually don't create long living C/C++ objects and prefer to use Java as memory manager, because it is problematic to free C++ memory in Java. finalize doesn't provide any guarantees! So, even if we will try to free C++ data associated with Java object in finalize, we can never know for sure that we'll not get memory leak, because OS will never call finalize. So, we may expect that 99% of malloc made during JNI call will call free during the same call.
  • So, we can use uninitialized data to detect (with some probability) that our shared library is loaded in some unusual environment.

Lets imagine following code:

void MarkHeap()
{
    static const char *ones = "11....1"; // String that contains 1024 ones
    auto some_data = malloc(1024);
    memcpy(some_data, ones, 1024);
    free(some_data);
}

size_t CheckMarkHeap()
{
    auto some_data = malloc(100);
    size_t ones_count = 0;
    for(size_t i = 0; i < 100; ++i)
        if(some_data[i] == '1')
            ++ones_count;
    free(some_data);
    return ones_count;
}

MarkHeap();
auto ones_count = CheckMarkHeap()

Here we can expect that very often ones_count will equal to 100! We can now use this strategy to check (with some probability) if CheckMarkHeap is called soon after MarkHeap.

In situation where CheckMarkHeap calculates any kind of security token, we can afraid that anyone will try to use our own shared library to bypass our protection; in case of Android, we can extract shared library from APK and try to interpret it with Android emulator or any embedded ARM emulation library like unicorn. If we implemented MarkHeap in another shared library that doesn't draw attacker attention and somehow call it before CheckMarkHeap, we have a good chance to detect that our security library is loaded from unusual context.

Of course, we can not ban immediately for that, because any random events may affect ones_count. However, if ones_count is not 100 in more than 60% of calls, we can make any soft penalty to suspicious account (for example, ask for phone verification, SafetyNet verification, show CAPTCHA more often, send account to human moderation, etc).

0

The exact behavior would depend on the implementation details of the memory manager behind malloc(). But it is a common case that memory managers would reuse memory blocks previously released with free(). If malloc() recycles a memory block which is not zeroed (for example using memset()) before being released, the block will generally contain buffer content remaining from previously executed functions inside the process.

Given that the memory manager does not initialize blocks upon allocation and release, the implications of this pseudocode is that it will repeatedly take random memory samples (of 1k size) within the process' memory address space, which will occasionally catch some data remained from the workings of previously executed code.

  • 1
    It is obvious that malloc contains data used previously; the question is how may they use this information? – Vitalii Aug 6 at 14:34
  • Kinda important that this is within the same process. Because that limits the utility of such an attack, methinks ... – 0xC0000022L Aug 13 at 20:55
-1

Maybe to steal data?

When you request memory via malloc you will get a block of memory that was used by other applications or the os before. I think, it should be possible to extract valuable information (like hashes that were not properly cleaned up by password-managers and other credentials) from there, in respect to the large user-base and th computational power these companies have available.

Let's do some calculations (unfortunately in code tags, becuase RE-Ex does not have MathJax):

Let...

  • n be the number of memory from which malloc can choose
  • m be the size of the window we can search in
  • l be the size of the searched sequence

The amount of different positions the window can have is given by n - m + 1.

The amount of positions, which will fully include a sequence of size l is m - l + 1.

Therefore the probability, that a random chosen search window will include our sequence is

p = (m - l + 1)/(n - m + 1)

We will assume,

  • that the average available memory in the smartphone is n = 4GB
  • the size of our search window is m = 1024
  • that the length of the sequence is l = 16 (the size of an MD5-Hash)

This gives us a probability of p = (1024 - 16 + 1)/(4e9 - 1024 + 1) ~= 2.5225e-7.

Let's do some more assumptions: Suppose the messenger has 1.5 billion users (like WhatsApp has). Let's also suppose the code sends the data twice a day and one thousandth of the users have an unreserved MD5 hash somewhere in memory.

That gives us a total size of 1.5e9 / 1000 * 2 = 3e6 samples to evaluate each day.

We will approximate the expected numbers of useful hashes with the normal distribution:

sigma = sqrt(3e9*2.5225e-7*(1-2.5225e-7)) ~= 27.50909 (so the approx. should yield good results)

my = 3e9*2.5525e-7 ~= 756.75

So the company will get with an certainty of 99.73% between 701 and 812 (my-2*sigma, my+2*sigma) usable hashes each day.

Disclaimer: I am not certain how java handles memory and how plausible (and effective) this scenario is. Nor do I have used any wellfounded values - I just plugged in some pseudo-logic numbers. I give also no guarantee for the corecctness of my calculations (I never really liked statistics).

Nontheless: Feel free to play around with the values and correct me if I did some miscalculations.

  • The premise of your idea is false in practice. Of course if unallocated memory contained data used by the system or other applications, that would be a huge security flaw. Which is why it doesn't - sane operating systems don't hand dirty pages to a process. Any data that is there belonged to this process or its ancestor. And the common ancestor of it all, zygote, is presumably careful about such things, in part by being too early and undifferentiated to contain much of interest in the way of user data. – Chris Stratton Aug 14 at 4:23
  • @ChrisStratton Could you please explain why do you think that a "sane OS would not hand over dirty pages"? I did some research myself and could not find anything regarding the OS doing any cleanup on it's memory (it would be very ineffeicient too), nor regarding allocating the proccess'es heap in some specific place. Until now I did not had the time to try my thesis out, but I will probably be able to run some tests this evening (GMT). But I would be very grateful if you could point me to some paper explaining this more - I am always eager to learn more about low-level memory management :) – Anonymous Anonymous Aug 14 at 11:33
  • If you look you'll find debates over zeroing released pages in a background kernel thread vs doing it on assigning them to a process. Everyone agrees it has to be done, the only question is between doing so in advance vs upon need. Seems the trend is now towards doing it on assignment as it doesn't take long. – Chris Stratton Aug 14 at 13:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.