moby.rb 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +89 -0
- data/lib/Moby/VERSION.rb +1 -1
- data/{moby.gemspec → moby.rb.gemspec} +4 -4
- data/test/Moby/VERSION_test.rb +2 -2
- metadata +5 -13
- data/CHANGES.txt +0 -32
- data/lib/Moby/Backends/MechanizeBackend.rb +0 -157
- data/lib/Moby/Backends/SeleniumBackend.rb +0 -236
- data/lib/Moby/Configuration.rb +0 -113
- data/lib/Moby/RandomInputGenerator.rb +0 -49
- data/lib/Selenium/WebDriver/Driver/Attempt/attempt.rb +0 -18
- data/lib/Selenium/WebDriver/SearchContext/ElementPresentQ/element_presentQ.rb +0 -13
- data/lib/Thoran/Selenium/WebDriver/Driver/Attempt/attempt.rb +0 -53
- data/lib/Thoran/Selenium/WebDriver/SearchContext/ElementPresentQ/element_presentQ.rb +0 -39
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8a52161eceecdfce20494ae2ff6b8e8fa8aefceb1d6911db3c2fd3bac475896f
|
|
4
|
+
data.tar.gz: 6242e8451d3d66b70b9452dc305329c121e97a8e1d868cb6744228208d2e8da6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 40ccfc56a276bb2c2ef6ad12e903dd2791f5be79f857ad4f6718c846b848ddee3ee62974f0a60e7a56e2cd8560f6761ccd53a5aecad3919fb6b4e9bc0274d752
|
|
7
|
+
data.tar.gz: dd7eba42a50b08e714407079b8191348f8f18d0ee389881dfce367af78d185c6b0a16570fa484d57362df8a23ec1278986ebb970c3815db897432e11b51b3f7f
|
data/CHANGELOG
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# CHANGELOG
|
|
2
|
+
|
|
3
|
+
## 20260807
|
|
4
|
+
|
|
5
|
+
1.0.1: Add a CHANGELOG in the form changelogger writes, and retire CHANGES.txt.
|
|
6
|
+
|
|
7
|
+
1. + CHANGELOG, in the form changelogger writes: a dated heading per version, the version and its summary on the first line of the body, then the numbered items, newest first — the form the other repositories already use. Versions 0.8.0 to 1.0.0 are carried across from CHANGES.txt; 0.7.0 to 0.7.4 are recovered from the git-importall revisions, each of which carries a version and a date of its own. Nothing before 0.7.0 is recorded: the 2006 series stamps every version with the one creation date, and the revisions between it and 0.7.0 carry a date but no version, so neither a reliable date nor a reliable version can be had for them.
|
|
8
|
+
2. - CHANGES.txt. The CHANGELOG holds that record now, and keeping both invites them to disagree. Its changelog_uri and file list in moby.rb.gemspec are pointed at CHANGELOG in its place.
|
|
9
|
+
3. ~ lib/Moby/VERSION.rb: VERSION: /1.0.0/1.0.1/
|
|
10
|
+
4. ~ test/Moby/VERSION_test.rb: /1.0.0/1.0.1/
|
|
11
|
+
|
|
12
|
+
## 20260108
|
|
13
|
+
|
|
14
|
+
1.0.0: Ensure that tests are in place and that version handling is working properly.
|
|
15
|
+
|
|
16
|
+
1. + tests
|
|
17
|
+
2. + moby.rb.gemspec
|
|
18
|
+
3. ~ lib/moby.rb: - Moby::VERSION_STRING
|
|
19
|
+
4. ~ lib/moby.rb: Fix finding username and password fields by name. It was returning the name of the field and not the field object.
|
|
20
|
+
5. + lib/Moby/VERSION.rb
|
|
21
|
+
6. ~ bin/moby: + --version flag
|
|
22
|
+
7. ~ README.md: Updated installation and usage.
|
|
23
|
+
8. ~ CHANGES.txt: Reorganised in descending order and added dates.
|
|
24
|
+
9. ~ LICENSE.txt: Updated the years to include the year this was started: /2026/2006-2026/
|
|
25
|
+
|
|
26
|
+
## 20260105
|
|
27
|
+
|
|
28
|
+
0.9.1: Fix syntax errors in README.md.
|
|
29
|
+
|
|
30
|
+
1. ~ README.md: Fix syntax errors.
|
|
31
|
+
2. ~ lib/Moby.rb: VERSION_STRING: /0.9.0/0.9.1/
|
|
32
|
+
|
|
33
|
+
## 20240605
|
|
34
|
+
|
|
35
|
+
0.9.0: Merge of 2019's 0.8.0 and 2023's 0.8.1.
|
|
36
|
+
|
|
37
|
+
## 20231206
|
|
38
|
+
|
|
39
|
+
0.8.1: Fix when username or password field number is specified or nothing is specified.
|
|
40
|
+
|
|
41
|
+
1. ~ lib/Whale: ~ set_username_field(): Fix when username field number is specified or nothing is specified.
|
|
42
|
+
2. ~ lib/Whale: ~ set_password_field(): Fix when password field number is specified or nothing is specified.
|
|
43
|
+
|
|
44
|
+
## 20231119
|
|
45
|
+
|
|
46
|
+
0.8.0: An accidental fork of 0.7.0 in 2023.
|
|
47
|
+
|
|
48
|
+
1. + README.md
|
|
49
|
+
2. + CHANGES.txt
|
|
50
|
+
3. + Gemfile
|
|
51
|
+
4. ~ bin/whale: ~ main(): - args (unnecessary)
|
|
52
|
+
5. ~ bin/whale VERSION_STRING --> lib/Whale::VERSION_STRING
|
|
53
|
+
6. ~ bin/whale: ~ switches(): use Whale::VERSION_STRING
|
|
54
|
+
7. ~ lib/Whale.rb: general tidying
|
|
55
|
+
8. + lib/File/self.collect.rb
|
|
56
|
+
|
|
57
|
+
## 20190820
|
|
58
|
+
|
|
59
|
+
0.7.4: Rescue Net::OpenTimeout in the submission loop.
|
|
60
|
+
|
|
61
|
+
1. ~ counter_phish(): + rescue Net::OpenTimeout.
|
|
62
|
+
|
|
63
|
+
## 20190819
|
|
64
|
+
|
|
65
|
+
0.7.3: Rescue Net::HTTP::Persistent::Error in place of EOFError.
|
|
66
|
+
|
|
67
|
+
1. ~ counter_phish(): rescue Net::HTTP::Persistent::Error in place of EOFError.
|
|
68
|
+
|
|
69
|
+
## 20190819
|
|
70
|
+
|
|
71
|
+
0.7.2: Set field values on the field objects rather than through the setter methods.
|
|
72
|
+
|
|
73
|
+
1. ~ counter_phish(): set the user agent and field values inline — mechanize.user_agent_alias, username_field.value, password_field.value — in place of set_user_agent()/set_username_field()/set_password_field().
|
|
74
|
+
2. ~ username(): use the username_is_email_address? predicate, its default nil in place of false.
|
|
75
|
+
3. ~ user_agent: attr_writer in place of attr_accessor.
|
|
76
|
+
4. ~ counter_phish(): verbose reports username_field.value:password_field.value.
|
|
77
|
+
|
|
78
|
+
## 20190819
|
|
79
|
+
|
|
80
|
+
0.7.1: Assign to the field's value rather than replacing the field object.
|
|
81
|
+
|
|
82
|
+
1. ~ set_username_field()/set_password_field(): set the field's value in place of replacing the field object at form.fields[n].
|
|
83
|
+
2. ~ counter_phish(): + rescue EOFError; + pp page in debug.
|
|
84
|
+
|
|
85
|
+
## 20190815
|
|
86
|
+
|
|
87
|
+
0.7.0: The earliest recoverable version.
|
|
88
|
+
|
|
89
|
+
1. whale floods a phishing form with fake credentials: counter_phish() loads the page, then loops submitting a generated username and password, each built from random dictionary words and TLDs. Recorded as an inventory of the first cleanly-versioned revision; the revision before it carries no version and a different structure, and is not reconstructed here.
|
data/lib/Moby/VERSION.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# moby.gemspec
|
|
1
|
+
# moby.rb.gemspec
|
|
2
2
|
|
|
3
3
|
require_relative './lib/Moby/VERSION'
|
|
4
4
|
|
|
@@ -30,10 +30,10 @@ Gem::Specification.new do |spec|
|
|
|
30
30
|
'bin/*',
|
|
31
31
|
'lib/**/*.rb',
|
|
32
32
|
'test/**/*.rb',
|
|
33
|
-
'
|
|
33
|
+
'CHANGELOG',
|
|
34
34
|
'Gemfile',
|
|
35
35
|
'LICENSE.txt',
|
|
36
|
-
'moby.gemspec',
|
|
36
|
+
'moby.rb.gemspec',
|
|
37
37
|
'Rakefile',
|
|
38
38
|
'README.md',
|
|
39
39
|
]
|
|
@@ -44,7 +44,7 @@ Gem::Specification.new do |spec|
|
|
|
44
44
|
|
|
45
45
|
spec.metadata = {
|
|
46
46
|
"bug_tracker_uri" => "https://github.com/thoran/moby/issues",
|
|
47
|
-
"changelog_uri" => "https://github.com/thoran/moby/blob/main/
|
|
47
|
+
"changelog_uri" => "https://github.com/thoran/moby/blob/main/CHANGELOG",
|
|
48
48
|
"source_code_uri" => "https://github.com/thoran/moby",
|
|
49
49
|
"documentation_uri" => "https://github.com/thoran/moby/blob/main/README.md"
|
|
50
50
|
}
|
data/test/Moby/VERSION_test.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: moby.rb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- thoran
|
|
@@ -102,7 +102,7 @@ executables:
|
|
|
102
102
|
extensions: []
|
|
103
103
|
extra_rdoc_files: []
|
|
104
104
|
files:
|
|
105
|
-
-
|
|
105
|
+
- CHANGELOG
|
|
106
106
|
- Gemfile
|
|
107
107
|
- LICENSE.txt
|
|
108
108
|
- README.md
|
|
@@ -110,17 +110,9 @@ files:
|
|
|
110
110
|
- bin/moby
|
|
111
111
|
- lib/File/self.collect.rb
|
|
112
112
|
- lib/Moby.rb
|
|
113
|
-
- lib/Moby/Backends/MechanizeBackend.rb
|
|
114
|
-
- lib/Moby/Backends/SeleniumBackend.rb
|
|
115
|
-
- lib/Moby/Configuration.rb
|
|
116
|
-
- lib/Moby/RandomInputGenerator.rb
|
|
117
113
|
- lib/Moby/VERSION.rb
|
|
118
|
-
- lib/Selenium/WebDriver/Driver/Attempt/attempt.rb
|
|
119
|
-
- lib/Selenium/WebDriver/SearchContext/ElementPresentQ/element_presentQ.rb
|
|
120
114
|
- lib/Thoran/File/SelfCollect/self.collect.rb
|
|
121
|
-
-
|
|
122
|
-
- lib/Thoran/Selenium/WebDriver/SearchContext/ElementPresentQ/element_presentQ.rb
|
|
123
|
-
- moby.gemspec
|
|
115
|
+
- moby.rb.gemspec
|
|
124
116
|
- test/Moby/VERSION_test.rb
|
|
125
117
|
- test/moby_test.rb
|
|
126
118
|
- test/test_helper.rb
|
|
@@ -129,7 +121,7 @@ licenses:
|
|
|
129
121
|
- MIT
|
|
130
122
|
metadata:
|
|
131
123
|
bug_tracker_uri: https://github.com/thoran/moby/issues
|
|
132
|
-
changelog_uri: https://github.com/thoran/moby/blob/main/
|
|
124
|
+
changelog_uri: https://github.com/thoran/moby/blob/main/CHANGELOG
|
|
133
125
|
source_code_uri: https://github.com/thoran/moby
|
|
134
126
|
documentation_uri: https://github.com/thoran/moby/blob/main/README.md
|
|
135
127
|
rdoc_options: []
|
|
@@ -146,7 +138,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
146
138
|
- !ruby/object:Gem::Version
|
|
147
139
|
version: '0'
|
|
148
140
|
requirements: []
|
|
149
|
-
rubygems_version: 4.0.
|
|
141
|
+
rubygems_version: 4.0.17
|
|
150
142
|
specification_version: 4
|
|
151
143
|
summary: Moby is a credentials poisoning tool which floods phishing forms with fake
|
|
152
144
|
credentials.
|
data/CHANGES.txt
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
1.0.0 - 2026-01-08
|
|
2
|
-
Ensure that tests are in place and that version handling is working properly.
|
|
3
|
-
+ tests
|
|
4
|
-
+ moby.gemspec
|
|
5
|
-
~ lib/moby.rb: - Moby::VERSION_STRING
|
|
6
|
-
~ lib/moby.rb: Fix finding username and password fields by name. It was returning the name of the field and not the field object.
|
|
7
|
-
+ lib/Moby/VERSION.rb
|
|
8
|
-
~ bin/moby: + --version flag
|
|
9
|
-
~ README.md: Updated installation and usage.
|
|
10
|
-
~ CHANGES.txt: Reorganised in descending order and added dates.
|
|
11
|
-
~ LICENSE.txt: Updated the years to include the year this was started: /2026/2006-2026/
|
|
12
|
-
|
|
13
|
-
0.9.1 - 2026-01-05
|
|
14
|
-
~ README.md: Fix syntax errors.
|
|
15
|
-
~ lib/Moby.rb: VERSION_STRING: /0.9.0/0.9.1/
|
|
16
|
-
|
|
17
|
-
0.9.0 - 2024-06-05
|
|
18
|
-
Merged the 2019 0.8.0 version of moby with the 2023 0.8.1 version of whale.
|
|
19
|
-
|
|
20
|
-
0.8.1 - 2023-12-06
|
|
21
|
-
~ lib/Whale: ~ set_username_field(): Fix when username field number is specified or nothing is specified.
|
|
22
|
-
~ lib/Whale: ~ set_password_field(): Fix when password field number is specified or nothing is specified.
|
|
23
|
-
|
|
24
|
-
0.8.0 - 2023-11-19
|
|
25
|
-
+ README.md
|
|
26
|
-
+ CHANGES.txt
|
|
27
|
-
+ Gemfile
|
|
28
|
-
~ bin/whale: ~ main(): - args (unnecessary)
|
|
29
|
-
~ bin/whale VERSION_STRING --> lib/Whale::VERSION_STRING
|
|
30
|
-
~ bin/whale: ~ switches(): use Whale::VERSION_STRING
|
|
31
|
-
~ lib/Whale.rb: general tidying
|
|
32
|
-
+ lib/File/self.collect.rb
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
# lib/moby/backends/mechanize_backend.rb
|
|
2
|
-
# Moby::Backends::MechanizeBackend
|
|
3
|
-
|
|
4
|
-
require 'mechanize'
|
|
5
|
-
require 'pp'
|
|
6
|
-
|
|
7
|
-
class Moby
|
|
8
|
-
module Backends
|
|
9
|
-
class MechanizeBackend
|
|
10
|
-
def counter_phish
|
|
11
|
-
set_up_agent
|
|
12
|
-
load_page
|
|
13
|
-
repeatedly_fill_and_submit_login_form
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
private
|
|
17
|
-
|
|
18
|
-
def initialize(config:, random_input_generator:)
|
|
19
|
-
@config = config
|
|
20
|
-
@random_input_generator = random_input_generator
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def agent
|
|
24
|
-
@agent ||= (
|
|
25
|
-
puts "Setting up Mechanize agent..." if @config.verbose?
|
|
26
|
-
agent = Mechanize.new
|
|
27
|
-
puts "Agent setup complete." if @config.verbose?
|
|
28
|
-
agent
|
|
29
|
-
)
|
|
30
|
-
end
|
|
31
|
-
alias_method :set_up_agent, :agent
|
|
32
|
-
|
|
33
|
-
def page
|
|
34
|
-
@page ||= (
|
|
35
|
-
puts "Navigating to #{@config.url}..." if @config.verbose?
|
|
36
|
-
page = agent.get(@config.url)
|
|
37
|
-
puts "Page loaded successfully." if @config.verbose?
|
|
38
|
-
page
|
|
39
|
-
)
|
|
40
|
-
end
|
|
41
|
-
alias_method :load_page, :page
|
|
42
|
-
|
|
43
|
-
def form
|
|
44
|
-
@form ||= (
|
|
45
|
-
if @config.using_form_name?
|
|
46
|
-
find_form_by_name
|
|
47
|
-
else
|
|
48
|
-
find_form_by_number
|
|
49
|
-
end
|
|
50
|
-
)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def repeatedly_fill_and_submit_login_form
|
|
54
|
-
start_time = Time.now
|
|
55
|
-
puts "moby session begun #{start_time}."
|
|
56
|
-
submission_count = 0
|
|
57
|
-
begin
|
|
58
|
-
if @config.fixed_number_of_submissions?
|
|
59
|
-
@config.iterations.times do |iteration|
|
|
60
|
-
fill_and_submit_login_form
|
|
61
|
-
submission_count += 1
|
|
62
|
-
puts "Iteration #{submission_count} of #{@config.iterations}." if @config.verbose?
|
|
63
|
-
end
|
|
64
|
-
puts "Successfully completed #{@config.iterations} submissions." if @config.verbose?
|
|
65
|
-
else
|
|
66
|
-
loop do
|
|
67
|
-
fill_and_submit_login_form
|
|
68
|
-
submission_count += 1
|
|
69
|
-
puts "#{submission_count} #{username}:#{password}" if @config.verbose?
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
ensure
|
|
73
|
-
finish_time = Time.now
|
|
74
|
-
time_delta_in_minutes = (finish_time - start_time) / 60
|
|
75
|
-
submissions_per_minute = submission_count / time_delta_in_minutes
|
|
76
|
-
puts "moby session terminated #{finish_time} with #{submission_count} counter-phishes served in #{time_delta_in_minutes} minutes for an average of #{submissions_per_minute} submissions per minute."
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def fill_and_submit_login_form
|
|
81
|
-
puts "Filling with: #{username}:#{password}" if @config.verbose?
|
|
82
|
-
agent.user_agent_alias = @config.user_agent
|
|
83
|
-
username_field.value = username
|
|
84
|
-
password_field.value = password
|
|
85
|
-
pp page if @config.debug?
|
|
86
|
-
result = agent.submit(form)
|
|
87
|
-
pp result if @config.debug?
|
|
88
|
-
sleep @config.delay if @config.delay > 0
|
|
89
|
-
rescue Net::HTTP::Persistent::Error
|
|
90
|
-
puts "\n\nNet::HTTP::Persistent::Error rescued.\n\n" if @config.verbose?
|
|
91
|
-
rescue Net::OpenTimeout
|
|
92
|
-
puts "\n\nNet::OpenTimeout rescued.\n\n" if @config.verbose?
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def find_form_by_name
|
|
96
|
-
page.form_with(name: @config.form_name) ||
|
|
97
|
-
page.form_with(id: @config.form_name) ||
|
|
98
|
-
raise("Could not find a form with the name or id of #{@config.form_name}.")
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def find_form_by_number
|
|
102
|
-
page.forms[@config.form_number] ||
|
|
103
|
-
raise("Could not find a form at index #{@config.form_number}.")
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def username
|
|
107
|
-
@random_input_generator.random_username
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def password
|
|
111
|
-
@random_input_generator.random_password
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def username_field
|
|
115
|
-
@username_field ||=(
|
|
116
|
-
if @config.using_username_field_name?
|
|
117
|
-
find_username_field_by_name
|
|
118
|
-
else
|
|
119
|
-
find_username_field_by_number
|
|
120
|
-
end
|
|
121
|
-
)
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def password_field
|
|
125
|
-
@password_field ||= (
|
|
126
|
-
if @config.using_password_field_name?
|
|
127
|
-
find_password_field_by_name
|
|
128
|
-
else
|
|
129
|
-
find_password_field_by_number
|
|
130
|
-
end
|
|
131
|
-
)
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
def find_username_field_by_name
|
|
135
|
-
form.field_with(name: @config.username_field_name) ||
|
|
136
|
-
form.field_with(id: @config.username_field_name) ||
|
|
137
|
-
raise("Could not find username field: #{@config.username_field_name}")
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
def find_username_field_by_number
|
|
141
|
-
form.fields[@config.username_field_number] ||
|
|
142
|
-
raise("Could not find username field at index #{@config.username_field_number}")
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
def find_password_field_by_name
|
|
146
|
-
form.field_with(name: @config.password_field_name) ||
|
|
147
|
-
form.field_with(id: @config.password_field_name) ||
|
|
148
|
-
raise("Could not find password field: #{@config.password_field_name}.")
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
def find_password_field_by_number
|
|
152
|
-
form.fields[@config.password_field_number] ||
|
|
153
|
-
raise("Could not find password field at index: #{@config.password_field_number}.")
|
|
154
|
-
end
|
|
155
|
-
end
|
|
156
|
-
end
|
|
157
|
-
end
|
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
# lib/Moby/Backends/SeleniumBackend.rb
|
|
2
|
-
# Moby::Backends::SeleniumBackend
|
|
3
|
-
|
|
4
|
-
require 'selenium-webdriver'
|
|
5
|
-
|
|
6
|
-
require_relative '../Selenium/WebDriver/Driver/Attempt/attempt'
|
|
7
|
-
require_relative '../Selenium/WebDriver/SearchContext/ElementPresentQ/element_presentQ'
|
|
8
|
-
|
|
9
|
-
class Moby
|
|
10
|
-
module Backends
|
|
11
|
-
class SeleniumBackend
|
|
12
|
-
def counter_phish
|
|
13
|
-
set_up_driver
|
|
14
|
-
navigate_to_target
|
|
15
|
-
repeatedly_fill_and_submit_login_form
|
|
16
|
-
ensure
|
|
17
|
-
cleanup_driver unless @config.debug?
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
private
|
|
21
|
-
|
|
22
|
-
def initialize(config:, random_input_generator:)
|
|
23
|
-
@config = config
|
|
24
|
-
@random_input_generator = random_input_generator
|
|
25
|
-
@driver = nil
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def driver
|
|
29
|
-
puts "Setting up #{@config.browser} driver..." if @config.verbose
|
|
30
|
-
case @config.browser.to_sym
|
|
31
|
-
when :chrome
|
|
32
|
-
options = Selenium::WebDriver::Chrome::Options.new
|
|
33
|
-
options.add_argument('--headless') unless @config.debug
|
|
34
|
-
options.add_argument('--disable-gpu')
|
|
35
|
-
options.add_argument('--no-sandbox')
|
|
36
|
-
options.add_argument("user-agent=#{@config.user_agent}") if @config.user_agent
|
|
37
|
-
@driver = Selenium::WebDriver.for(:chrome, options: options)
|
|
38
|
-
when :firefox
|
|
39
|
-
options = Selenium::WebDriver::Firefox::Options.new
|
|
40
|
-
options.add_argument('--headless') unless @config.debug
|
|
41
|
-
options.add_preference('general.useragent.override', @config.user_agent) if @config.user_agent
|
|
42
|
-
@driver = Selenium::WebDriver.for(:firefox, options: options)
|
|
43
|
-
else
|
|
44
|
-
@driver = Selenium::WebDriver.for(@config.browser)
|
|
45
|
-
end
|
|
46
|
-
puts "Driver setup complete." if @config.verbose?
|
|
47
|
-
end
|
|
48
|
-
alias_method :set_up_driver, :driver
|
|
49
|
-
|
|
50
|
-
def navigate_to_target
|
|
51
|
-
puts "Navigating to #{@config.url}..." if @config.verbose?
|
|
52
|
-
@driver.attempt do
|
|
53
|
-
@driver.get(@config.url)
|
|
54
|
-
wait_for_page_load
|
|
55
|
-
end
|
|
56
|
-
puts "Page loaded successfully." if @config.verbose?
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def wait_for_page_load
|
|
60
|
-
wait = Selenium::WebDriver::Wait.new(timeout: 10)
|
|
61
|
-
wait.until do
|
|
62
|
-
@driver.execute_script('return document.readyState') == 'complete'
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def form
|
|
67
|
-
@form ||= (
|
|
68
|
-
if @config.using_form_name?
|
|
69
|
-
find_form_by_name
|
|
70
|
-
else
|
|
71
|
-
find_form_by_number
|
|
72
|
-
end
|
|
73
|
-
)
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def repeatedly_fill_and_submit_login_form
|
|
77
|
-
start_time = Time.now
|
|
78
|
-
puts "moby session begun #{start_time}."
|
|
79
|
-
submission_count = 0
|
|
80
|
-
begin
|
|
81
|
-
if @config.fixed_number_of_submissions?
|
|
82
|
-
@config.iterations.times do |iteration|
|
|
83
|
-
fill_and_submit_login_form
|
|
84
|
-
submission_count += 1
|
|
85
|
-
puts "Submission #{submission_count} of #{@config.iterations}." if @config.verbose?
|
|
86
|
-
end
|
|
87
|
-
puts "Successfully completed #{@config.iterations} submissions." if @config.verbose?
|
|
88
|
-
else
|
|
89
|
-
loop do
|
|
90
|
-
fill_and_submit_login_form
|
|
91
|
-
submission_count += 1
|
|
92
|
-
puts "#{submission_count} #{username}:#{password}" if @config.verbose?
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
ensure
|
|
96
|
-
finish_time = Time.now
|
|
97
|
-
time_delta_in_minutes = (finish_time - start_time) / 60
|
|
98
|
-
submissions_per_minute = submission_count / time_delta_in_minutes
|
|
99
|
-
puts "moby session terminated #{finish_time} with #{submission_count} counter-phishes served in #{time_delta_in_minutes} minutes for an average of #{submissions_per_minute} submissions per minute."
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
def fill_and_submit_login_form
|
|
104
|
-
puts "Filling with: #{username}:#{password}" if @config.verbose?
|
|
105
|
-
driver.navigate.to(@config.url)
|
|
106
|
-
wait_for_page_load
|
|
107
|
-
driver.attempt do
|
|
108
|
-
username_field.clear
|
|
109
|
-
username_field.send_keys(username)
|
|
110
|
-
password_field.clear
|
|
111
|
-
password_field.send_keys(password)
|
|
112
|
-
submit_form(form)
|
|
113
|
-
end
|
|
114
|
-
sleep @config.delay if @config.delay > 0
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
def find_form_by_name
|
|
118
|
-
name = @config.form_name
|
|
119
|
-
selectors = [
|
|
120
|
-
[:name, name],
|
|
121
|
-
[:id, name],
|
|
122
|
-
[:css, "form[name='#{name}']"],
|
|
123
|
-
[:css, "form##{name}"]
|
|
124
|
-
]
|
|
125
|
-
selectors.each do |type, selector|
|
|
126
|
-
if @driver.element_present?(type, selector)
|
|
127
|
-
return @driver.find_element(type, selector)
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
raise("Could not find a form with the name or id of #{@config.form_name}.")
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
def find_form_by_number
|
|
134
|
-
index = @config.form_number
|
|
135
|
-
forms = @driver.find_elements(:tag_name, 'form')
|
|
136
|
-
return forms[index] if forms[index]
|
|
137
|
-
raise("Could not find a form at index #{@config.form_number}.")
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
def username
|
|
141
|
-
@random_input_generator.random_username
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
def password
|
|
145
|
-
@random_input_generator.random_password
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
def username_field
|
|
149
|
-
@username_field ||= (
|
|
150
|
-
if @config.using_username_field_name?
|
|
151
|
-
find_username_field_by_name
|
|
152
|
-
else
|
|
153
|
-
find_username_field_by_number
|
|
154
|
-
end
|
|
155
|
-
)
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
def password_field
|
|
159
|
-
@password_field ||= (
|
|
160
|
-
if @config.using_password_field_name?
|
|
161
|
-
find_password_field_by_name
|
|
162
|
-
else
|
|
163
|
-
find_password_field_by_number
|
|
164
|
-
end
|
|
165
|
-
)
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
def find_username_field_by_name
|
|
169
|
-
name = @config.username_field_name
|
|
170
|
-
selectors = [
|
|
171
|
-
[:name, name],
|
|
172
|
-
[:id, name],
|
|
173
|
-
[:css, "input[name='#{name}']"],
|
|
174
|
-
[:css, "input##{name}"]
|
|
175
|
-
]
|
|
176
|
-
selectors.each do |type, selector|
|
|
177
|
-
begin
|
|
178
|
-
element = form.find_element(type, selector)
|
|
179
|
-
return element if element
|
|
180
|
-
rescue Selenium::WebDriver::Error::NoSuchElementError
|
|
181
|
-
next
|
|
182
|
-
end
|
|
183
|
-
end
|
|
184
|
-
raise("Could not find username field: #{@config.username_field_name}")
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
def find_username_field_by_number
|
|
188
|
-
index = @config.username_field_number
|
|
189
|
-
inputs = form.find_elements(:tag_name, 'input')
|
|
190
|
-
inputs[index] ||
|
|
191
|
-
raise("Could not find username field at index #{index}")
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
def find_password_field_by_name
|
|
195
|
-
name = @config.password_field_name
|
|
196
|
-
selectors = [
|
|
197
|
-
[:name, name],
|
|
198
|
-
[:id, name],
|
|
199
|
-
[:css, "input[name='#{name}']"],
|
|
200
|
-
[:css, "input##{name}"]
|
|
201
|
-
]
|
|
202
|
-
selectors.each do |type, selector|
|
|
203
|
-
begin
|
|
204
|
-
element = form.find_element(type, selector)
|
|
205
|
-
return element if element
|
|
206
|
-
rescue Selenium::WebDriver::Error::NoSuchElementError
|
|
207
|
-
next
|
|
208
|
-
end
|
|
209
|
-
end
|
|
210
|
-
raise("Could not find password field: #{@config.password_field_name}")
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
def find_password_field_by_number
|
|
214
|
-
index = @config.password_field_number
|
|
215
|
-
inputs = form.find_elements(:tag_name, 'input')
|
|
216
|
-
inputs[index] ||
|
|
217
|
-
raise("Could not find password field at index #{index}")
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
def submit_form(form)
|
|
221
|
-
begin
|
|
222
|
-
submit_button = form.find_element(:css, "input[type='submit'], button[type='submit'], button")
|
|
223
|
-
submit_button.click
|
|
224
|
-
rescue
|
|
225
|
-
form.submit
|
|
226
|
-
end
|
|
227
|
-
sleep 0.5
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
def cleanup_driver
|
|
231
|
-
@driver.quit if @driver
|
|
232
|
-
puts "Driver closed." if @config.verbose?
|
|
233
|
-
end
|
|
234
|
-
end
|
|
235
|
-
end
|
|
236
|
-
end
|
data/lib/Moby/Configuration.rb
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
# lib/Moby/Configuration.rb
|
|
2
|
-
# Moby::Configuration
|
|
3
|
-
|
|
4
|
-
require 'mechanize'
|
|
5
|
-
|
|
6
|
-
class Moby
|
|
7
|
-
class Configuration
|
|
8
|
-
AVAILABLE_USER_AGENTS = (Mechanize::AGENT_ALIASES.keys - ['Mechanize']).freeze
|
|
9
|
-
|
|
10
|
-
attr_accessor(
|
|
11
|
-
:url,
|
|
12
|
-
:backend,
|
|
13
|
-
:browser,
|
|
14
|
-
:debug,
|
|
15
|
-
:verbose,
|
|
16
|
-
:form_name,
|
|
17
|
-
:form_number,
|
|
18
|
-
:username_field_name,
|
|
19
|
-
:username_field_number,
|
|
20
|
-
:password_field_name,
|
|
21
|
-
:password_field_number,
|
|
22
|
-
:username_hostname,
|
|
23
|
-
:username_is_email_address,
|
|
24
|
-
:iterations,
|
|
25
|
-
:delay,
|
|
26
|
-
:word_list_path
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
attr_writer(
|
|
30
|
-
:user_agent,
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
def user_agent
|
|
34
|
-
@user_agent || random_user_agent
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def using_form_name?
|
|
38
|
-
!@form_name.nil?
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def using_username_field_name?
|
|
42
|
-
!@username_field_name.nil?
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def using_password_field_name?
|
|
46
|
-
!@password_field_name.nil?
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def mechanize?
|
|
50
|
-
@backend == :mechanize
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def selenium?
|
|
54
|
-
@backend == :selenium
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def username_is_email_address?
|
|
58
|
-
@username_is_email_address || @username_hostname || @username_field_name == 'email'
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def debug?
|
|
62
|
-
@debug
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def verbose?
|
|
66
|
-
@verbose
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def fixed_number_of_submissions?
|
|
70
|
-
!!@iterations
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
private
|
|
74
|
-
|
|
75
|
-
# @param url [String] The target URL
|
|
76
|
-
# @param backend [Symbol] :mechanize or :selenium (default: :selenium)
|
|
77
|
-
# @param browser [Symbol] :chrome or :firefox (default: :chrome)
|
|
78
|
-
|
|
79
|
-
def initialize(url:, backend: :selenium, browser: :chrome, **args)
|
|
80
|
-
@url = url
|
|
81
|
-
@backend = backend.to_sym
|
|
82
|
-
|
|
83
|
-
# Browser configuration (for Selenium)
|
|
84
|
-
@browser = browser.to_sym
|
|
85
|
-
@user_agent = args[:user_agent]
|
|
86
|
-
|
|
87
|
-
# Form identification
|
|
88
|
-
@form_name = args[:form_name]
|
|
89
|
-
@form_number = args[:form_number] || 0
|
|
90
|
-
|
|
91
|
-
# Field identification
|
|
92
|
-
@username_field_name = args[:username_field_name]
|
|
93
|
-
@username_field_number = args[:username_field_number] || 0
|
|
94
|
-
@password_field_name = args[:password_field_name]
|
|
95
|
-
@password_field_number = args[:password_field_number] || 1
|
|
96
|
-
|
|
97
|
-
# Username configuration
|
|
98
|
-
@username_hostname = args[:username_hostname]
|
|
99
|
-
@username_is_email_address = args[:username_is_email_address] || false
|
|
100
|
-
|
|
101
|
-
# Runtime configuration
|
|
102
|
-
@debug = args[:debug] || false
|
|
103
|
-
@verbose = args[:verbose] || false
|
|
104
|
-
@iterations = args[:iterations] || 100
|
|
105
|
-
@delay = args[:delay] || 0
|
|
106
|
-
@word_list_path = args[:word_list_path] || '/usr/share/dict/words'
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
def random_user_agent
|
|
110
|
-
AVAILABLE_USER_AGENTS.sample
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
end
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
# lib/Moby/RandomInputGenerator.rb
|
|
2
|
-
# Moby::RandomInputGenerator
|
|
3
|
-
|
|
4
|
-
class Moby
|
|
5
|
-
class RandomInputGenerator
|
|
6
|
-
TLD = %w{com net org edu int mil gov arpa biz aero name coop info pro museum}
|
|
7
|
-
|
|
8
|
-
def random_username
|
|
9
|
-
if username_is_email_address?
|
|
10
|
-
if @username_hostname
|
|
11
|
-
"#{random_word}@#{@username_hostname}"
|
|
12
|
-
else
|
|
13
|
-
"#{random_word}@#{random_word}.#{random_TLD}"
|
|
14
|
-
end
|
|
15
|
-
else
|
|
16
|
-
random_word
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def random_password
|
|
21
|
-
random_word
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
private
|
|
25
|
-
|
|
26
|
-
def initialize(word_list_path:, username_hostname: nil, username_is_email_address: false)
|
|
27
|
-
@word_list_path = word_list_path
|
|
28
|
-
@username_hostname = username_hostname
|
|
29
|
-
@username_is_email_address = username_is_email_address
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def random_word
|
|
33
|
-
words.sample
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def words
|
|
37
|
-
@words ||= File.readlines(@word_list_path).map(&:chomp).reject(&:empty?)
|
|
38
|
-
end
|
|
39
|
-
alias_method :load_words, :words
|
|
40
|
-
|
|
41
|
-
def random_TLD
|
|
42
|
-
TLD.sample
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def username_is_email_address?
|
|
46
|
-
@username_is_email_address
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
end
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# Selenium/WebDriver/Driver/Attempt/attempt.rb
|
|
2
|
-
# Selenium::WebDriver::Driver::Attempt.attempt
|
|
3
|
-
|
|
4
|
-
# 20200418
|
|
5
|
-
# 0.3.0
|
|
6
|
-
|
|
7
|
-
# Examples:
|
|
8
|
-
# 1. driver.attempt do |driver|
|
|
9
|
-
# driver.get('https://example.com/users/sign_in')
|
|
10
|
-
# enter_username
|
|
11
|
-
# enter_password
|
|
12
|
-
# sign_in
|
|
13
|
-
# end
|
|
14
|
-
|
|
15
|
-
# Changes:
|
|
16
|
-
# 1. Moved all the logic into the Thoran namespace.
|
|
17
|
-
|
|
18
|
-
require 'Thoran/Selenium/WebDriver/Driver/Attempt/attempt'
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
# Selenium/WebDriver/SearchContext/ElementPresentQ/element_presentQ.rb
|
|
2
|
-
# Selenium::WebDriver::SearchContext::ElementPresentQ.element_present?
|
|
3
|
-
|
|
4
|
-
# 20200418
|
|
5
|
-
# 0.3.0
|
|
6
|
-
|
|
7
|
-
# Examples:
|
|
8
|
-
# 1. driver.element_present?(:xpath, '//a[@id="submit"]')
|
|
9
|
-
|
|
10
|
-
# Changes:
|
|
11
|
-
# 1. Moved all the logic into the Thoran namespace.
|
|
12
|
-
|
|
13
|
-
require 'Thoran/Selenium/WebDriver/SearchContext/ElementPresentQ/element_presentQ'
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
# Thoran/Selenium/WebDriver/Attempt/attempt.rb
|
|
2
|
-
# Thoran::Selenium::WebDriver::Attempt.attempt
|
|
3
|
-
|
|
4
|
-
# 20200419
|
|
5
|
-
# 0.0.1
|
|
6
|
-
|
|
7
|
-
# Examples:
|
|
8
|
-
# 1. driver.attempt do |driver|
|
|
9
|
-
# driver.get('https://example.com/users/sign_in')
|
|
10
|
-
# enter_username
|
|
11
|
-
# enter_password
|
|
12
|
-
# sign_in
|
|
13
|
-
# end
|
|
14
|
-
|
|
15
|
-
# Changes:
|
|
16
|
-
# 1. + Thoran namespace.
|
|
17
|
-
|
|
18
|
-
module Thoran
|
|
19
|
-
module Selenium
|
|
20
|
-
module WebDriver
|
|
21
|
-
module Driver
|
|
22
|
-
module Attempt
|
|
23
|
-
|
|
24
|
-
def attempt(max_attempts = 3, &block)
|
|
25
|
-
attempts = 0
|
|
26
|
-
loop do
|
|
27
|
-
begin
|
|
28
|
-
yield
|
|
29
|
-
break
|
|
30
|
-
rescue Timeout::Error, ::Selenium::WebDriver::Error::UnknownError, ::Selenium::WebDriver::Error::NoSuchElementError => e
|
|
31
|
-
attempts += 1
|
|
32
|
-
if attempts >= max_attempts
|
|
33
|
-
@driver.quit
|
|
34
|
-
puts "Giving up after #{max_attempts} attempts to #{__method__} because #{e}."
|
|
35
|
-
exit
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
module Selenium
|
|
48
|
-
module WebDriver
|
|
49
|
-
class Driver
|
|
50
|
-
include Thoran::Selenium::WebDriver::Driver::Attempt
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
# Thoran/Selenium/WebDriver/SearchContext/ElementPresentQ/element_presentQ.rb
|
|
2
|
-
# Thoran::Selenium::WebDriver::SearchContext::ElementPresentQ.element_present?
|
|
3
|
-
|
|
4
|
-
# 20200418
|
|
5
|
-
# 0.0.0
|
|
6
|
-
|
|
7
|
-
# Examples:
|
|
8
|
-
# 1. driver.element_present?(:xpath, '//a[@id="submit"]')
|
|
9
|
-
# 2. driver.element_present?(:id, @logout_button_id)
|
|
10
|
-
|
|
11
|
-
# Changes:
|
|
12
|
-
# 1. + Thoran namespace.
|
|
13
|
-
|
|
14
|
-
module Thoran
|
|
15
|
-
module Selenium
|
|
16
|
-
module WebDriver
|
|
17
|
-
module SearchContext
|
|
18
|
-
module ElementPresentQ
|
|
19
|
-
|
|
20
|
-
def element_present?(selector_type, selector)
|
|
21
|
-
bridge.find_element_by(selector_type, selector)
|
|
22
|
-
true
|
|
23
|
-
rescue ::Selenium::WebDriver::Error::NoSuchElementError
|
|
24
|
-
false
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
module Selenium
|
|
34
|
-
module WebDriver
|
|
35
|
-
class Driver
|
|
36
|
-
include Thoran::Selenium::WebDriver::SearchContext::ElementPresentQ
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|