Need to reverse engineer or exploit a file upload vulnerability in an
embedded web server? I added a multipart/form-data
class to Bowcaster
to help with that. You can have a look here:
https://github.com/zcutlip/bowcaster/blob/master/src/bowcaster/clients/http.py
Here’s some background:
I’ve been reverse engineering how the Netgear R6200 web server parses a
new firmware image when you use the firmware update facility in the web
interface. Manually browsing to the router’s web interface, then to the
firmware update form, then browsing to a firmware file on disk, then
clicking “upload” gets really tedious after a few times.
I wanted to use Bowcaster’s
HttpClient
class to do this programmatically. Unfortunately, it lacked
the ability to generate a multipart/form-data
POST body, so I added a
class to help with that. I know; there are 3rd party Python modules to
do this already, but I don’t want Bowcaster to have any dependencies
other than Python, itself.
Anyway, below is an example program that uses the class to upload a firmware image, which you can get here from Bowcaster’s example code. In order to identify the fields that need to be populated, I manually uploaded a file through a web browser, and analyzed the POST request in Burp Suite.
#!/usr/bin/env python
"""
Example code to upload a firmware file to the Netgear R6200 firmware update form.
"""
import sys
import os
from bowcaster.common import Logging
from bowcaster.clients import HttpClient
from bowcaster.clients import HTTPError
from bowcaster.clients import MultipartForm
def send_fw(url,fw_file):
logger=Logging(max_level=Logging.DEBUG)
logger.LOG_INFO("Sending %s" % fw_file)
logger.LOG_INFO("to %s" % url)
fw_file_basename=os.path.basename(fw_file)
logger.LOG_INFO("Creating headers.")
headers={"Accept":
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"}
headers["Accept-Language"]="en-US,en;q=0.5"
headers["Accept-Encoding"]="gzip, deflate"
headers["Referer"]="http://192.168.127.141/UPG_upgrade.htm"
#admin:password
headers["Authorization"]="Basic YWRtaW46cGFzc3dvcmQ="
headers["Connection"]="keep-alive"
logger.LOG_INFO("Creating post data")
mf=MultipartForm()
mf.add_field("buttonHit","Upgrade")
mf.add_field("buttonValue","Upload")
mf.add_field("IS_check_upgrade","0")
mf.add_field("ver_check_enable","1")
mf.add_file("mtenFWUpload",fw_file)
mf.add_field("upfile",fw_file_basename)
mf.add_field("Upgrade","Upload")
mf.add_field("progress","")
post_data=str(mf)
headers["Content-Length"]=("%s" % len(post_data))
headers["Content-Type"]=mf.get_content_type()
client=HttpClient()
logger.LOG_INFO("Sending request.")
resp=client.send(url,headers=headers,post_data=post_data,logger=logger)
return resp
def main(fw_file,host=None):
if not host:
host="192.168.127.141"
url="http://%s/upgrade_check.cgi" % host
resp=send_fw(url,fw_file)
print resp
if __name__ == "__main__":
if(len(sys.argv) == 2):
main(sys.argv[1])
elif len(sys.argv) == 3:
main(sys.argv[1],host=sys.argv[2])
else:
print("Specify at least firmware file.")
sys.exit(1)