require "XSpear/version" require "XSpear/banner" require "XSpear/log" require "XSpear/XSpearRepoter" require 'net/http' require 'uri' require 'optparse' require 'colorize' require "selenium-webdriver" module XSpear class Error < StandardError; end end class XspearScan def initialize(url, data, headers, params, thread, output, verbose) @url = url @data = data @headers = headers if params.nil? @params = params else @params = params.split(",") end @thread = thread @output = output @verbose = verbose @report = XspearRepoter.new @url, Time.now end class ScanCallbackFunc def initialize(url, method, query, response) @url = url @method = method @query = query @response = response # self.run end def run # Override callback function.. # return type: Array(state, message) # + state: i(INFO), v(VULN), s(SYSTEM) # + message: your message # e.g # return "v", "reflected xss with #{query}" end end class CallbackStringMatch < ScanCallbackFunc def run if @response.body.include? @query [true, "reflected #{@query}"] else [false, "not reflected #{@query}"] end end end class CallbackErrorPatternMatch < ScanCallbackFunc def run info = "Found" if @response.body.to_s.match(/(SQL syntax.*MySQL|Warning.*mysql_.*|MySqlException \(0x|valid MySQL result|check the manual that corresponds to your (MySQL|MariaDB) server version|MySqlClient\.|com\.mysql\.jdbc\.exceptions)/i) info = info + "MYSQL " end if @response.body.to_s.match(/(Driver.* SQL[\-\_\ ]*Server|OLE DB.* SQL Server|\bSQL Server.*Driver|Warning.*mssql_.*|\bSQL Server.*[0-9a-fA-F]{8}|[\s\S]Exception.*\WSystem\.Data\.SqlClient\.|[\s\S]Exception.*\WRoadhouse\.Cms\.|Microsoft SQL Native Client.*[0-9a-fA-F]{8})/i) info = info + "MSSQL " end if @response.body.to_s.match(/(\bORA-\d{5}|Oracle error|Oracle.*Driver|Warning.*\Woci_.*|Warning.*\Wora_.*)/i) info = info + "Oracle " end if @response.body.to_s.match(/(PostgreSQL.*ERROR|Warning.*\Wpg_.*|valid PostgreSQL result|Npgsql\.|PG::SyntaxError:|org\.postgresql\.util\.PSQLException|ERROR:\s\ssyntax error at or near)/i) info = info + "Postgres " end if @response.body.to_s.match(/(Microsoft Access (\d+ )?Driver|JET Database Engine|Access Database Engine|ODBC Microsoft Access)/i) info = info + "MSAccess " end if @response.body.to_s.match(/(SQLite\/JDBCDriver|SQLite.Exception|System.Data.SQLite.SQLiteException|Warning.*sqlite_.*|Warning.*SQLite3::|\[SQLITE_ERROR\])/i) info = info + "SQLite " end if @response.body.to_s.match(/(Warning.*sybase.*|Sybase message|Sybase.*Server message.*|SybSQLException|com\.sybase\.jdbc)/i) info = info + "SyBase " end if @response.body.to_s.match(/(Warning.*ingres_|Ingres SQLSTATE|Ingres\W.*Driver)/i) info = info + "Ingress " end if info.length > 5 [true, "#{@info}"] else [false, "#{@info}"] end end end class CallbackXSSSelenium < ScanCallbackFunc def run begin options = Selenium::WebDriver::Firefox::Options.new(args: ['-headless']) driver = Selenium::WebDriver.for(:firefox, options: options) if @method == "GET" begin driver.get(@url+"?"+@query) alert = driver.switch_to().alert() if alert.text.to_s == "45" driver.quit return [true, "found alert/prompt/confirm (45) in selenium!! #{@query}\n => "] else driver.quit return [true, "found alert/prompt/confirm event in selenium #{@query}\n =>"] end rescue Selenium::WebDriver::Error::UnexpectedAlertOpenError => e driver.quit return [true, "found alert/prompt/confirm error base in selenium #{@query}\n =>"] rescue => e driver.quit return [false, "not found alert/prompt/confirm event #{@query}\n =>"] end end rescue => e log('s', "Error Selenium : #{e}") end end end def run r = [] event_handler = [ 'onAbort', 'onActivate', 'onAfterPrint', 'onAfterUpdate', 'onBeforeActivate', 'onBeforeCopy', 'onBeforeCut', 'onBeforeDeactivate', 'onBeforeEditFocus', 'onBeforePaste', 'onBeforePrint', 'onBeforeUnload', 'onBeforeUpdate', 'onBegin', 'onBlur', 'onBounce', 'onCellChange', 'onChange', 'onClick', 'onContextMenu', 'onControlSelect', 'onCopy', 'onCut', 'onDataAvailable', 'onDataSetChanged', 'onDataSetComplete', 'onDblClick', 'onDeactivate', 'onDrag', 'onDragEnd', 'onDragLeave', 'onDragEnter', 'onDragOver', 'onDragDrop', 'onDragStart', 'onDrop', 'onEnd', 'onError', 'onErrorUpdate', 'onFilterChange', 'onFinish', 'onFocus', 'onFocusIn', 'onFocusOut', 'onHashChange', 'onHelp', 'onInput', 'onKeyDown', 'onKeyPress', 'onKeyUp', 'onLayoutComplete', 'onLoad', 'onLoseCapture', 'onMediaComplete', 'onMediaError', 'onMessage', 'onMouseDown', 'onMouseEnter', 'onMouseLeave', 'onMouseMove', 'onMouseOut', 'onMouseOver', 'onMouseUp', 'onMouseWheel', 'onMove', 'onMoveEnd', 'onMoveStart', 'onOffline', 'onOnline', 'onOutOfSync', 'onPaste', 'onPause', 'onPopState', 'onProgress', 'onPropertyChange', 'onReadyStateChange', 'onRedo', 'onRepeat', 'onReset', 'onResize', 'onResizeEnd', 'onResizeStart', 'onResume', 'onReverse', 'onRowsEnter', 'onRowExit', 'onRowDelete', 'onRowInserted', 'onScroll', 'onSeek', 'onSelect', 'onSelectionChange', 'onSelectStart', 'onStart', 'onStop', 'onStorage', 'onSyncRestored', 'onSubmit', 'onTimeError', 'onTrackChange', 'onUndo', 'onUnload', 'onURLFlip' ] log('s', 'creating a test query.') r.push makeQueryPattern('d', 'XsPeaR"', 'XsPeaR"', 'i', "Found SQL Error Pattern", CallbackErrorPatternMatch) r.push makeQueryPattern('r', 'rEfe6', 'rEfe6', 'i', 'reflected parameter', CallbackStringMatch) # Check Special Chat r.push makeQueryPattern('f', 'XsPeaR>', 'XsPeaR>', 'i', "not filtered "+">".blue, CallbackStringMatch) r.push makeQueryPattern('f', '', 'onhwul=64', 'i', "not filtered event handler "+"on{any} pattern".blue, CallbackStringMatch) event_handler.each do |ev| r.push makeQueryPattern('f', "\"", "#{ev}=64", 'i', "not filtered event handler "+"#{ev}=64".blue, CallbackStringMatch) end r.push makeQueryPattern('x', '">', '', 'h', "reflected "+"XSS Code".red, CallbackStringMatch) r.push makeQueryPattern('x', '', '', 'h', "reflected "+"XSS Code".red, CallbackStringMatch) r.push makeQueryPattern('x', '', '', 'h', "reflected "+"XSS Code".red, CallbackStringMatch) r.push makeQueryPattern('x', '">