Class: Arachni::Session

Inherits:
Object show all
Includes:
UI::Output, Utilities
Defined in:
lib/arachni/session.rb

Overview

Session management class.

Handles logins, provided log-out detection, stores and executes login sequences and provided general webapp session related helpers.

Author:

  • Tasos “Zapotek” Laskos <tasos.laskos@arachni-scanner.com>

Defined Under Namespace

Classes: Error

Constant Summary collapse

LOGIN_TRIES =
5
LOGIN_RETRY_WAIT =
5

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Utilities

#available_port, available_port_mutex, #bytes_to_kilobytes, #bytes_to_megabytes, #caller_name, #caller_path, #cookie_decode, #cookie_encode, #cookies_from_file, #cookies_from_parser, #cookies_from_response, #exception_jail, #exclude_path?, #follow_protocol?, #form_decode, #form_encode, #forms_from_parser, #forms_from_response, #full_and_absolute_url?, #generate_token, #get_path, #hms_to_seconds, #html_decode, #html_encode, #include_path?, #links_from_parser, #links_from_response, #normalize_url, #page_from_response, #page_from_url, #parse_set_cookie, #path_in_domain?, #path_too_deep?, #port_available?, #rand_port, #random_seed, #redundant_path?, #regexp_array_match, #remove_constants, #request_parse_body, #seconds_to_hms, #skip_page?, #skip_path?, #skip_resource?, #skip_response?, #to_absolute, #uri_decode, #uri_encode, #uri_parse, #uri_parse_query, #uri_parser, #uri_rewrite

Methods included from UI::Output

#debug?, #debug_level_1?, #debug_level_2?, #debug_level_3?, #debug_level_4?, #debug_off, #debug_on, #disable_only_positives, #included, #mute, #muted?, #only_positives, #only_positives?, #print_bad, #print_debug, #print_debug_backtrace, #print_debug_level_1, #print_debug_level_2, #print_debug_level_3, #print_debug_level_4, #print_error, #print_error_backtrace, #print_exception, #print_info, #print_line, #print_ok, #print_status, #print_verbose, #reroute_to_file, #reroute_to_file?, reset_output_options, #unmute, #verbose?, #verbose_on

Constructor Details

#initializeSession

Returns a new instance of Session.



65
66
67
# File 'lib/arachni/session.rb', line 65

def initialize
    @check_options = {}
end

Instance Attribute Details

#browserBrowser (readonly)

Returns:



56
57
58
# File 'lib/arachni/session.rb', line 56

def browser
  @browser
end

#check_optionsHash

Returns HTTP::Client#request options for #logged_in?.

Returns:



63
64
65
# File 'lib/arachni/session.rb', line 63

def check_options
  @check_options
end

#login_sequenceBlock (readonly)

Returns:

  • (Block)


59
60
61
# File 'lib/arachni/session.rb', line 59

def 
  @login_sequence
end

Instance Method Details

#can_login?Bool

Returns `true` if there is log-in capability, `false` otherwise.

Returns:

  • (Bool)

    `true` if there is log-in capability, `false` otherwise.



189
190
191
# File 'lib/arachni/session.rb', line 189

def can_login?
    configured? && 
end

#clean_upObject



69
70
71
72
# File 'lib/arachni/session.rb', line 69

def clean_up
    configuration.clear
    shutdown_browser
end

#configurationObject



109
110
111
# File 'lib/arachni/session.rb', line 109

def configuration
    Data.session.configuration
end

#configure(options) ⇒ Object

Parameters:

Options Hash (options):

  • :url (String)

    URL containing the login form.

  • :inputs (Hash{String=>String})

    Hash containing inputs with which to locate and fill-in the form.



104
105
106
107
# File 'lib/arachni/session.rb', line 104

def configure( options )
    configuration.clear
    configuration.merge! options
end

#configured?Bool

Returns `true` if configured, `false` otherwise.

Returns:

  • (Bool)

    `true` if configured, `false` otherwise.



115
116
117
# File 'lib/arachni/session.rb', line 115

def configured?
    !!@login_sequence || configuration.any?
end

Tries to find the main session (login/ID) cookie.

Parameters:

  • block (Block)

    Block to be passed the cookie.

Raises:



87
88
89
90
91
92
93
94
95
96
97
# File 'lib/arachni/session.rb', line 87

def cookie( &block )
    return block.call( @session_cookie ) if @session_cookie
    fail Error::NoLoginCheck, 'No login-check has been configured.' if !

    cookies.each do |cookie|
        logged_in?( cookies: { cookie.name => '' } ) do |bool|
            next if bool
            block.call( @session_cookie = cookie )
        end
    end
end

#cookiesArray<Element::Cookie>

Returns Session cookies.

Returns:



76
77
78
# File 'lib/arachni/session.rb', line 76

def cookies
    http.cookies.select(&:session?)
end

#ensure_logged_inBool?

Returns `true` if logged-in, `false` otherwise, `nil` if there's no log-in capability.

Returns:

  • (Bool, nil)

    `true` if logged-in, `false` otherwise, `nil` if there's no log-in capability.



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/arachni/session.rb', line 196

def ensure_logged_in
    return if !can_login?
    return true if logged_in?

    print_bad 'The scanner has been logged out.'
    print_info 'Trying to re-login...'

    LOGIN_TRIES.times do |i|
        self.

        if self.logged_in?
            print_ok 'Logged-in successfully.'
            return true
        end

        print_bad "Login attempt #{i+1} failed, retrying after " <<
                      "#{LOGIN_RETRY_WAIT} seconds..."
        sleep LOGIN_RETRY_WAIT
    end

    print_bad 'Could not re-login.'
    false
end

#find_login_form(opts = {}, &block) ⇒ Object

Finds a login forms based on supplied location, collection and criteria.

Parameters:

  • opts (Hash) (defaults to: {})
  • block (Block)

    If a block and a :url are given, the request will run async and the block will be called with the result of this method.

Options Hash (opts):

  • :requires_password (Bool)

    Does the login form include a password field? (Defaults to `true`)

  • :action (Array, Regexp)

    Regexp to match or String to compare against the form action.

  • :inputs (String, Array, Hash, Symbol)

    Inputs that the form must contain.

  • :forms (Array<Element::Form>)

    Collection of forms to look through.

  • :pages (Page, Array<Page>)

    Pages to look through.

  • :url (String)

    URL to fetch and look for forms.



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/arachni/session.rb', line 138

def ( opts = {}, &block )
    async = block_given?

    requires_password = (opts[:requires_password].nil? ? true : opts[:requires_password])

    find = proc do |cforms|
        cforms.select do |f|
            next if requires_password && !f.requires_password?

            oks = []

            if action = opts[:action]
                oks << !!(action.is_a?( Regexp ) ? f.action =~ action : f.action == action)
            end

            if inputs = opts[:inputs]
                oks << f.has_inputs?( inputs )
            end

            oks.count( true ) == oks.size
        end.first
    end

    forms = if opts[:pages]
                [opts[:pages]].flatten.map { |p| p.forms }.flatten
            elsif opts[:forms]
                opts[:forms]
            elsif (url = opts[:url])
                http_opts = {
                    update_cookies:  true,
                    follow_location: true,
                    performer:       self
                }

                if async
                    http.get( url, http_opts ) do |r|
                        block.call find.call( forms_from_response( r, true ) )
                    end
                else
                    forms_from_response(
                        http.get( url, http_opts.merge( mode: :sync ) ),
                        true
                    )
                end
            end

    find.call( forms || [] ) if !async
end

#has_browser?Boolean

Returns:

  • (Boolean)


314
315
316
# File 'lib/arachni/session.rb', line 314

def has_browser?
    Browser.has_executable? && Options.scope.dom_depth_limit > 0
end

#has_login_check?Bool

Returns `true` if a login check exists, `false` otherwise.

Returns:

  • (Bool)

    `true` if a login check exists, `false` otherwise.



305
306
307
# File 'lib/arachni/session.rb', line 305

def 
    !!@login_check || !!(Options.session.check_url && Options.session.check_pattern)
end

#httpHTTP::Client

Returns:



310
311
312
# File 'lib/arachni/session.rb', line 310

def http
    HTTP::Client
end

#logged_in?(http_options = {}, &block) ⇒ Bool?

Returns `true` if we're logged-in, `false` otherwise.

Parameters:

  • http_options (Hash) (defaults to: {})

    HTTP options to use for the check.

  • block (Block)

    If a block has been provided the check will be async and the result will be passed to it, otherwise the method will return the result.

Returns:

  • (Bool, nil)

    `true` if we're logged-in, `false` otherwise.

Raises:



274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
# File 'lib/arachni/session.rb', line 274

def logged_in?( http_options = {}, &block )
    fail Error::NoLoginCheck if !

    http_options = http_options.merge(
        method:          :get,
        mode:            block_given? ? :async : :sync,
        follow_location: true,
        performer:       self
    )
    http_options.merge!( @check_options )

    print_debug 'Performing login check.'

    bool = nil
    http.request( Options.session.check_url, http_options ) do |response|
        bool = !!response.body.match( Options.session.check_pattern )

        print_debug "Login check done: #{bool}"

        if !bool
            print_debug "\n#{response.request}#{response}"
        end

        block.call( bool ) if block
    end

    bool
end

#login(raise = false) ⇒ Page?

Uses the information provided by #configure or #login_sequence to login.

Returns:

  • (Page, nil)

    Page if the login was successful, `nil` otherwise.

Raises:



238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/arachni/session.rb', line 238

def ( raise = false )
    fail Error::NotConfigured, 'Please configure the session first.' if !configured?

    refresh_browser

    page = nil
    exception_jail raise do
        page = @login_sequence ?  : 
    end

    if has_browser?
        http.update_cookies browser.cookies
    end

    page
ensure
    shutdown_browser
end

#record_login_sequence(&block) ⇒ Object

Parameters:

  • block (Block)

    Login sequence. Must return the resulting Page.

    If a #browser is available it will be passed to the block.



225
226
227
# File 'lib/arachni/session.rb', line 225

def ( &block )
    @login_sequence = block
end

#with_browser(*args, &block) ⇒ Object

Parameters:

  • block (Block)

    Block to be passed the #browser.



259
260
261
# File 'lib/arachni/session.rb', line 259

def with_browser( *args, &block )
    block.call browser, *args
end