Creating an Android Forensics Lab

Introduction

Before I start this tutorial/blog/walkthrough on how to build your own Android environment for digital forensics purposes, I have to give some context on how I started this amateur adventure.

uwu

First of all, I am a challenge creator for my university’s cybersecurity committee and there was a CTF competition held there on the 2nd of September 2023. So, I took it upon myself to make something unique for the participants and myself… And oh boy, was it unique and difficult to do for someone like me. The full code can be found here.

The code is a bit janky, but adjust where necessary.

So, credit where credit is due. I got the inspiration and most of the materials from Sam Browne, an amazing lecturer (not mine tho :P). He has plenty of courses for free on YouTube but the videos and content are more organised on his website.


Installing Android Studio

Ok this first part is pretty easy to do, but since I have something up my sleeves I’m gonna teach you what to do.

  1. First go download the installer for Android Studio here.

It will be easier if you have a MacBook or a Linux Distro for scripting purposes

Android Studio website

  1. Go through the normal setup stuff according to the documentation.

  2. Once you’re done installing Android Studio, let’s make a new project!

Android Studio Dashboard

Click on New Project


Selecting a Template

For the template pick anything (I choose EmptyViews Activity because you can choose the option to program in Java if you want a custom app)


Selecting an API

Don’t worry about the name it’s just an empty project but you must select “API 29 (“Q”;Android 10) depending on the architecture of your host”


  1. Once you make a new project, click on Create Device

Create a New Device


  1. Select the Pixel 2 as your device

Selecting the Virtual Hardware

From testing this works best, correct me if I’m wrong


  1. Lastly, select the system image “Q” from Google’s API (make sure to download and install the image first)

Selecting an Android Image

Again, from testing, Android 10 works best


Selecting an Android Image

If you’re using a virtual machine make sure Virtualize Intel VT-X/EPT or AMD-V/RVI is enabled to support nested virtualisation


Rooting Your Device

So, you have a virtual Android phone, but it won’t be much if you’re not root or Administrator for Windows people. For that reason, let’s do some Red Teaming by escalating our privileges.

Android device created

There is already an exploit we can use, so let’s just download it from here.

  1. You can use git to clone the repository
git clone https://github.com/newbit1/rootAVD.git

Cloning the repo


  1. Before we root the device, let’s export some variables! (This is very important generally and for scripting purposes)
# For MacOS
export PATH=$PATH:~/Library/Android/sdk/platform-tools/
export EMULATOR=~/Library/Android/sdk/emulator/

# For Linux
export PATH=$PATH:/home/$USER/Android/Sdk/platform-tools/
export EMULATOR=/home/$USER/Android/Sdk/emulator/ 

  1. Let’s turn on our device and run rootAVD.sh

Cloning the repo


Launching rootAVD.sh

When we run it without any arguments it will prompt us to give an argument of where the ramdisk of the Android device is. So let’s do that!


Launching rootAVD.sh

However, remember that we downloaded API 29 and not the one in the suggestions. The location of the ramdisk that we want is usually in ~/Android/Sdk/system-images/android-29/ramdisk.img for Linux


Running rootAVD.sh


Running rootAVD.sh with Arguments

So the exploit will just do its thing to root the Android device, but it’ll ask you for the version of Magisk (an agent to root the device). Choose 3 for canary


  1. Restart the device

At this point we need to press the off button on our Android device, essentially forcing it to turn off. Then, we turn it on again


  1. Install Magisk

Install Magisk 1

You would notice in your app tray that there’s an app called “Magisk”.


Install Magisk 2

Once you’re in Magisk just click OK.


Install Magisk 3

Select Direct Install and click on Let’s Go.


Install Magisk 3

Magisk will root the device and once it’s done, click on Reboot.


  1. Now that the device has been rebooted, enter into a shell with adb shell. Then we can escalate our privileges with su.

Spawning a Shell


Escalating Privileges

Once Grant is activated click it and you will gain root!


Instructions

Automating Root Access

Now that you know how to get root access, let’s try to automate that! First, let’s get our emulator’s name or ID. You can do that with adb devices.

Listing devices

Note that this is the default device most of the time but it may change so watch out. Now on to the scripting part.


send_keystrokes(){
  adb -s $emulator shell input keyevent $1
}

adb -s $emulator shell su &	
send_keystrokes 22 
sleep 1 
send_keystrokes 22 
sleep 1
send_keystrokes 22 
send_keystrokes 66 
  1. We will invoke the su command by using adb with the switch s to identify the emulator being used.

  2. The shell argument will spawn a shell and pass in the su command and it will run the root shell in the background with &.

  3. The send_keystrokes function will use the same method to connect to the emulator in Step 1.

  4. It will then send a keyevent as an input and an argument of the key.

  5. In our case, the 22 key will be pressed, indicating a right-arrow key input.

You can refer to this for more keyevents.

  1. The sleep command is used so that the input won’t be executed immediately.

  2. So, with that said, when the su command is used, adb will sent 3 right-arrow keys delayed by 1 second each.

  3. Then, a keystroke of 66 indicating an enter key will click on Grant.



Connecting to the Android Device

exec 3<>/dev/tcp/$host/$port

echo "auth <AUTH CODE>" >&3
  1. This command will simply make a connection with our Android Device using the file descriptor 3 allowing us to interact with our Android device through a shell different from the adb shell with extra commands.

File descriptor 3 (often referred to as “fd3”) is like a special label that a computer program or script can use to manage an extra resource, like a file, network connection, or something else. It’s not built into the system like the standard input (0), output (1), or error (2) streams, but it can be created and used when needed. For example, a program might set up fd3 to read data from a specific file, allowing it to work with that file separately from standard input or output.


  1. However, before we execute any commands, we need to authenticate ourselves

Even though we’re running this locally… security am I right? lol


  1. We can do that by making a connection to give us a prompt of where to get our authentication token

# port 5554 is the default port but check your emulator name
# 
# jigsaw@jigsaw:-$ adb devices
# 
# List of devices attached
# emulator-5554 device
#
# The number at the end is usually the port number you want to connect to

jigsaw@jigsaw:-$ nc 127.0.0.1 5554 

Finding Auth Token


  1. So let’s type cat out the authentication token.

Get Auth Token


  1. Now let’s pass that token into our connection!

Now, just now we were able to nc into our Android device, why didn’t we just execute commands there? Well, we did it this way so that we can secure a connection in our script and pass commands through the script instead of typing it one by one.

echo "auth <AUTH TOKEN>" >&3

Setting the Time

With the connections established, we can dive into creating our evidence! However, for BAT CTF 2023 we needed the evidence to be at a specific time and I found a way to do that! (Be sure to change the time according to when you want the evidence to be created)

set_time(){
  adb -s $emulator shell "su -c 'toybox date $1'"
}
  1. The function above will identify the emulator used with the s switch.

  2. A shell will be spawned through adb and will use su to run the following command with the c switch.

  3. Using toolbox with the argument date and a second argument with the date format MMDDHHMMYYYY.SS

Don’t ask me why the date format is weird because I thought so too


Installing Applications

If you were paying attention to the logs when we were escalating our privileges, you would have realised that it installed Magisk. How? Let me show you.

# <APP> is in the form of an APK file

adb -s $emulator install <APP>

With the install argument, we can install any app we want!


Setting the Android Device’s Phone Number

Yea just a warning, do this in another terminal with the same exports we did previously. The phone will then launch after this

$EMULATOR/emulator -avd Pixel_2_API_29 -phone-number 601163282118 -read-only
  1. The emulator binary will use the emulated device we have with the avd switch

  2. We can then change the device’s phone number through the phone-number switch

  3. The read-only switch is inserted so that that number cannot be edited on run time


Sending Messages

I just want to note, through my research I have not seen any command that can send messages but to script it out manually

  1. First, you know those irritating text suggestions that pop up when we text? Let’s remove that.
# Referring to the send_keystrokes() function
# Update: After debugging this particular script only works if you rooted the device

adb -s $emulator shell am start -n com.google.android.apps.messaging/.ui.ConversationListActivity

send_keystrokes 61 
send_keystrokes 61 
send_keystrokes 61 
send_keystrokes 61 
send_keystrokes 61 
send_keystrokes 61 
send_keystrokes 61  
send_keystrokes 66

send_keystrokes 20
send_keystrokes 20
send_keystrokes 20
send_keystrokes 20
send_keystrokes 66

send_keystrokes 20
send_keystrokes 20
send_keystrokes 20
send_keystrokes 20
send_keystrokes 66


send_keystrokes 66
send_keystrokes 20
send_keystrokes 20
send_keystrokes 66
send_keystrokes 20
send_keystrokes 20
send_keystrokes 66

  1. Through adb, we will start a shell and am or the Activity Manager we will start the Messaging application

The messaging app by default will be in com.google.android.apps.messaging/.ui.ConversationListActivity


  1. Then keystrokes will be sent to individually click the buttons in the Messaging app

  1. Now let’s send the messages.
send_message(){
      adb -s $emulator shell am start -a android.intent.action.SENDTO -d sms:$1 --es sms_body \"$2\"
      sleep 1
      adb -s $emulator shell input keyevent 22
      sleep 1
      adb -s $emulator shell input keyevent 66
}

I’ll be honest, I don’t really know what the -d and the -es switch does but I do know it builds out the message we’re going to send (so it’s like sending an email through telnet).


  1. Through the Activity Manager again, we’ll be calling a function to send a message.

  2. The d switch will be used with the argument sms: and the recipient’s number must be typed after that.

  3. Then, the message will be specified here through the es switch with the argument sms_body and a string after that will 2 quotations with escape characters.

  4. Through adb again, we will be automatically clicking the right-arrow key and an enter key after that.


Receiving Messages

receive_message(){
  echo "sms send $1 $2" >&3
}

Don’t worry, the part to receive messages is much more simple (calm down XD)


  1. Remember the connection we have just now? Now, we’re just echoing a special command, sms with the argument to send a message as the first parameter and the phone number the Android device is going to be receiving the message from.

  2. We will then pass it to the connection have running in the background.


Sending Calls

make_call(){
  adb -s $emulator shell am start -a android.intent.action.CALL -d tel:$1
  sleep 10
  adb -s $emulator shell input keyevent 6
}

  1. Through an adb shell and as well as the Activity Manager, we’ll be calling a function to call a number through the d switch and tel: followed by a number as an argument.

  2. However, once you make a call, it will be calling forever.

  3. To end the call, send a keystroke with 6 as the argument.

The keystroke 6 is the key to end a call


Receiving Calls

  sleep 5
  echo "gsm call $1" >&3
  sleep 10
  echo "gsm cancel $1" >&3

  1. Through the connection we have, we can send another special command to receive calls.

  2. Just send the command, gsm with the call argument followed by a phone number to the connection.

  3. However, same thing here, the call will go on forever.

  4. To end it, just send another gsm command with the cancel argument to the connection.


Transferring Files

adb -s $emulator push $1 <LOCATION>
  1. Through adb again (gotta love that word lol), we’ll push the file that we want as the argument followed by the location we want the file to end up in the Android device.

Extracting Evidence

# Disable line buffering to read input immediately
  stty -echo
  stty cbreak
  
  # Read a single character from the user
  read -n 1 key
  
  # Enable line buffering and echoing
  stty echo
  stty -cbreak
  
  # Perform an action based on the input received
  echo  # Move to a new line
  1. So before we actually extract the evidence out of the Android device, let’s pause the script so that if we have anything we want to do manually we have to do.

adb -s $emulator shell "su -c 'tar --exclude=android_image.tar.gz -cvzf /sdcard/Download/android_image.tar.gz /data'" 
adb pull /sdcard/Download/android_image.tar.gz
  1. When we’re ready, we’re just passing a command with root privileges through the adb shell.

  2. It will then use tar to archive the data directory, excluding the archive file with the exclude switch.

  3. The file will be located within the virtual SD card.

  4. For the last time, we will use adb to pull the archive file.


Conclusion

In conclusion, is this cool? Yes. Is it realistic? Well… I can’t say. However, it is very good for making CTF challenges and I will be using it in the future. Also, there is a con of making challenges this way. Through ALeapp, we can identify the host machine. With a MacBook, users will also be able to see information about your MacBook through the ._MACOSX and .DS_STORE folders. So, bottom point being, make sure your laptop is clean. *wink*

jigsaw's blog

i’m zach and I build labs and create ctf challenges