The Future Going Back in Time to Abuse Android's
Total Page:16
File Type:pdf, Size:1020Kb
Back To The Future Going Back In Time To Abuse Android’s JIT !1 $ whoami • Benjamin Watson • Director of Security Research @VerSprite Security • Android • @rotlogix !2 Agenda • Inspiration and Overview • Android 4.4.4 JIT Internals & Abuse • Android 7.1.1 JIT Internals & Abuse • Android Oreo • Tools • Future Challenges • Conclusion !3 Back To The Future Going Back In Time To Abuse Android’s JIT !4 Making Android Malware Great The First Time !5 On The Shoulders Of Giants !6 On the Shoulders of Giants @mattifestation @rwincey !7 Shellcode Execution in .NET using MSIL- Based JIT Overwrite • @mattifestation discovered the CPBLK opcode, which is effectively the MSIL equivalent to memcpy • He used to this opcode to overwrite a JIT’ed .NET method with shellcode • https://www.exploit-monday.com/2013/04/ MSILbasedShellcodeExec.html !8 Java Shellcode Execution • @rwincey uses the Unsafe API to overwrite a JIT’ed Java method with shellcode • https://www.slideshare.net/RyanWincey/java- shellcodeoffice !9 On the Shoulders of Giants • After absorbing Matt and Ryan’s research, I was left with one question … “ Is this also possible in Android? “ … !10 Motivation • These techniques discussed today are post-exploitation in nature • We already have installed a malicious application or gain code execution in Java through an arbitrary application • Our goal is to execute shellcode in memory entirely through Java without loading additional shared-libraries, or utilizing JNI !11 Motivation • This means that a simple “application” can have a self- contained solution for loading shellcode from memory into memory • This only relies on having access to a runtime and nothing more • The technique results in zero disk presence, which eliminates the need packaging up shared-libraries or other executable payloads !12 AD Network Malicious PNG Download Shellcode from PNG Extract shellcode from PNG Embedded Shellcode and execute it in memory via JIT technique Google Play Malware Installation !13 Time Travel !14 Time Travel • Unfortunately the existence of a JIT compiler is not in every major version of Android • Up until KitKat, Android utilized Dalvik, which is a JIT based VM • When the Android Runtime (ART) was introduced in Lollipop it replaced the Dalvik VM • The Android Runtime itself relies on Ahead-of-Time (AOT) compilation !15 Time Travel • Things change in Android Nougat, when a new JIT compiler is introduced with code profiling • This is meant to improve the performance of applications when running in the ART !16 !17 Overview • This research predominantly focuses on Android KitKat and Android Nougat • We are going to deep dive the internals of Dalvik’s JIT implementation and the JIT compiler running in version 7.1.1 • Our main goal is to abuse each implementation in order to execute shellcode directly from memory • This research was performed on a Nexus 5 running 4.4.4 and a Nexus 5X running 7.1.1 !18 Dalvik JIT Internals !19 Dalvik JIT Internals • The first question we need to answer is the following • How does a Dalvik method in its bytecode form, become Just-in-Time compiled? • For this research I care less about the “why” and more about the “how” !20 Dalvik JIT Internals Java Source Code Java Bytecode Dalvik Bytecode Dalvik Executable Dalvik VM !21 Dalvik JIT Internals - Put in Work • When a Dalvik method has been selected for JIT compilation, the compiler spins up and goes to work • Once the JIT compiler has finished its compilation operations for the given Dalvik method, it calls the dvmJitSetCodeAddr function • The compiler passes the target Dalvik method’s bytecode pointer and the JIT code address as arguments to dvmJitSetCodeAddr !22 {} Bytecode and JIT Code Pointers !23 Dalvik JIT Internals - Lookup & Add • dvmJitSetCodeAddr is then responsible for calling and passing the dPC to lookupAndAdd • lookupAndAdd handles the “Lookup & Add” JIT operations {} dPC -> Dalvik Method Bytecode !24 Dalvik JIT Internals - Lookup & Add • lookupAndAdd is responsible for finding available “JitEntry” slots in the “JitTable” • pJitEntryTable - Hash table structure that contains one or more JitEntry structures • JitEntry - Structure that contains a pointer to the Dalvik method’s bytecode and a pointer to its translated JIT code !25 Dalvik JIT Internals - Lookup & Add An available JitEntry slot within the pJitEntryTable hash table is updated with the hash of the target dPC The JitEntry is populated with the code address for the target Dalvik method The translated address member is initialized and the JitEntry is returned !26 Dalvik JIT Internals - Lookup & Add • Within the dvmSetCodeAddr function the translated address is then used to fill out the JitEntry’s codeAddress member {} !27 Dalvik JIT Internals - JIT Entries • The pJitEntryTable is a member of a global structure called DvmJitGlobals which is defined as gDvmJit • The JitEntry structure is compromised a few different things including the JitEntryInfoUnion union and JitEntryInfo structure !28 {} Pointers of Interest !29 Dalvik JIT Internals - JIT Cache • JIT code is memory mapped and a pointer to the beginning of the memory map is stored in the gDvmJit global structure’s codeCache member The translated JIT code pointer within a JitEntry will point somewhere in the JIT Code Cache !30 Dalvik JIT Internals - JIT Execution •There are a lot of moving parts in the Dalvik VM when a method is invoked •Our main focus will be within Dalvik’s interpreter dvmInvokeMethod dvmCallMethodV dvmCallMethod dvmInterpret dvmMterpStd dvmMterpStdRun !31 Dalvik JIT Internals - JIT Entries • dvmMterpStdRun is implemented entire in assembly • This function is responsible for loading the current PC and executing it • If that method is JIT’ed the PC will be pointing to the translated code within a thread structure passed in as an argument to the function • There are multiple global entry points back into the interpreter from JIT code !32 Abusing Dalvik(s) JIT • My goal hopefully has become relatively clear at this point • I want the ability to hijack a JitEntry for a target method with a pointer to shellcode !33 Abusing Dalvik(s) JIT • The biggest challenge was finding a way to read, write and map memory from Java • My first idea was to explore the Unsafe API included within Android • This did not prove to be fruitful • After digging through Android’s internals, I stumbled on the libcore package !34 Abusing Dalvik(s) JIT • I was pleasantly surprised when I found that libcore contain a class called libcore.io.Posix • This class implements the libcore.io.OS interface, and contains the methods mmap and munmap ! • Basically all of the posix methods in the libcore.io.Posix class are JNI methods that call their native counterpart. • These methods can easily be accessed via Java reflection! • Now we have a mechanism for mapping RWX shellcode !35 Abusing Dalvik(s) JIT • I also discovered the libcore.io.Memory and libcore.io.MemoryBlock classes, which provides methods for reading and writing to memory through different forms • peekInt, pokeInt, and pokeByteArray specifically were used to read from and write to our shellcode and other memory in the virtual machine !36 Abusing Dalvik(s) JIT !37 Abusing Dalvik(s) JIT • The JIT Code Cache isn’t writeable, but this doesn’t matter! • We can use our read and write primitives for controlling entries in the writeable global JitTable • In order to do this we need to locate the gDvmJit global structure in memory • gDvmJIT actually has a symbol reference to it! • We wrote a basic process maps parser for getting the base address of libdvm, then simply added the offset !38 {} How do we deal with this? !39 Abusing Dalvik(s) JIT • We attempted to honor the tableLock and whether or not it was being held by checking the pthread_mutex_t structure’s state member • If the tableLock was not held, we would acquire the lock by setting the state to “locked” in Java through our memory write primitive • However, we found that the tableLock is rarely held when first checked • Initially we thought racing against the JIT compiler in order to write to the JitTable would be required • We also found this wasn’t typically the case !40 Abusing Dalvik(s) JIT • Attacking the JIT Entry Table requires overwriting the codeAddress field of a JitEntry for a method or a partial method • We were unsuccessful with being able to force a given method to be JIT’ed in a consistent way • So we focused on scanning the entire JIT Entry Table for populated JIT Entries !41 Abusing Dalvik(s) JIT • In order to attack a target JitEntry, we first needed to know which classes had already been loaded in the VM • For each method that was extracted from the loaded class, we need to determine if the pointer to that method’s Dalvik PC matches the Dalvik PC within the JitEntry • Knowing the target method also allows us to invoke this from Java after we have overwritten the codeAddress via reflection !42 {} The VM globals contains a hash table for all of the loaded classes {} Each class contains of a vtable of Method structures representing its methods !43 {} The Method structure contains the insns field, which points to the method’s Dalvik bytecode If the method has been JIT’ed, the dPC field in the JitEntry should match !44 JitEntry JIT Code Cache Shellcode dPC* codeAddress* dPC* codeAddress* •In Java we mmap memory and write our shellcode to that allocation •Through libcore, we can access our shellcode’s base address •We save off the original JitEntry structure in order to restore it later •We use the pointer to our shellcode to replace the codeAddress