Class: WPScan::Controller::PasswordAttack

Inherits:
CMSScanner::Controller::Base
  • Object
show all
Defined in:
app/controllers/password_attack.rb

Overview

Password Attack Controller

Instance Method Summary collapse

Instance Method Details

#attack_optsObject



28
29
30
31
32
33
# File 'app/controllers/password_attack.rb', line 28

def attack_opts
  @attack_opts ||= {
    show_progression: user_interaction?,
    multicall_max_passwords: ParsedCli.multicall_max_passwords
  }
end

#attackerCMSScanner::Finders::Finder

Returns The finder used to perform the attack.

Returns:

  • (CMSScanner::Finders::Finder)

    The finder used to perform the attack



60
61
62
# File 'app/controllers/password_attack.rb', line 60

def attacker
  @attacker ||= attacker_from_cli_options || attacker_from_automatic_detection
end

#attacker_from_automatic_detectionCMSScanner::Finders::Finder

Returns:

  • (CMSScanner::Finders::Finder)


103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'app/controllers/password_attack.rb', line 103

def attacker_from_automatic_detection
  if xmlrpc_get_users_blogs_enabled?
    wp_version = target.wp_version

    if wp_version && wp_version < '4.4'
      Finders::Passwords::XMLRPCMulticall.new(xmlrpc)
    else
      Finders::Passwords::XMLRPC.new(xmlrpc)
    end
  elsif target.
    Finders::Passwords::WpLogin.new(target)
  else
    raise Error::NoLoginInterfaceDetected
  end
end

#attacker_from_cli_optionsCMSScanner::Finders::Finder

Returns:

  • (CMSScanner::Finders::Finder)


70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'app/controllers/password_attack.rb', line 70

def attacker_from_cli_options
  return unless ParsedCli.password_attack

  case ParsedCli.password_attack
  when :wp_login
    raise Error::NoLoginInterfaceDetected unless target.

    Finders::Passwords::WpLogin.new(target)
  when :xmlrpc
    raise Error::XMLRPCNotDetected unless xmlrpc

    Finders::Passwords::XMLRPC.new(xmlrpc)
  when :xmlrpc_multicall
    raise Error::XMLRPCNotDetected unless xmlrpc

    Finders::Passwords::XMLRPCMulticall.new(xmlrpc)
  end
end

#cli_optionsObject



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'app/controllers/password_attack.rb', line 7

def cli_options
  [
    OptFilePath.new(
      ['--passwords FILE-PATH', '-P',
       'List of passwords to use during the password attack.',
       'If no --username/s option supplied, user enumeration will be run.'],
      exists: true
    ),
    OptSmartList.new(['--usernames LIST', '-U', 'List of usernames to use during the password attack.']),
    OptInteger.new(['--multicall-max-passwords MAX_PWD',
                    'Maximum number of passwords to send by request with XMLRPC multicall'],
                   default: 500),
    OptChoice.new(['--password-attack ATTACK',
                   'Force the supplied attack to be used rather than automatically determining one.',
                   'Multicall will only work against WP < 4.4'],
                  choices: %w[wp-login xmlrpc xmlrpc-multicall],
                  normalize: %i[downcase underscore to_sym]),
    OptString.new(['--login-uri URI', 'The URI of the login page if different from /wp-login.php'])
  ]
end

#runObject



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'app/controllers/password_attack.rb', line 35

def run
  return unless ParsedCli.passwords

  begin
    found = []

    if user_interaction?
      output('@info',
             msg: "Performing password attack on #{attacker.titleize} against #{users.size} user/s")
    end

    attacker.attack(users, ParsedCli.passwords, attack_opts) do |user|
      found << user

      attacker.progress_bar.log("[SUCCESS] - #{user.username} / #{user.password}")
    end
  rescue Error::NoLoginInterfaceDetected => e
    # TODO: Maybe output that in JSON as well.
    output('@notice', msg: e.to_s) if user_interaction?
  ensure
    output('users', users: found)
  end
end

#usersArray<Users>

Returns The users to brute force.

Returns:

  • (Array<Users>)

    The users to brute force



120
121
122
123
124
125
126
# File 'app/controllers/password_attack.rb', line 120

def users
  return target.users unless ParsedCli.usernames

  ParsedCli.usernames.reduce([]) do |acc, elem|
    acc << Model::User.new(elem.chomp)
  end
end

#xmlrpcModel::XMLRPC

Returns:



65
66
67
# File 'app/controllers/password_attack.rb', line 65

def xmlrpc
  @xmlrpc ||= target.xmlrpc
end

#xmlrpc_get_users_blogs_enabled?Boolean

Returns:

  • (Boolean)


90
91
92
93
94
95
96
97
98
99
100
# File 'app/controllers/password_attack.rb', line 90

def xmlrpc_get_users_blogs_enabled?
  if xmlrpc&.enabled? &&
     xmlrpc.available_methods.include?('wp.getUsersBlogs') &&
     !xmlrpc.method_call('wp.getUsersBlogs', [SecureRandom.hex[0, 6], SecureRandom.hex[0, 4]])
            .run.body.match?(/>\s*405\s*</)

    true
  else
    false
  end
end