gophish-ruby 0.2.0 → 0.4.0

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.
@@ -105,6 +105,51 @@ else
105
105
  end
106
106
  ```
107
107
 
108
+ ### 5. Create Your First Landing Page
109
+
110
+ Landing pages are what users see when they click phishing links:
111
+
112
+ ```ruby
113
+ # Create a basic landing page
114
+ page = Gophish::Page.new(
115
+ name: "Microsoft Login Page",
116
+ html: <<~HTML
117
+ <!DOCTYPE html>
118
+ <html>
119
+ <head>
120
+ <title>Microsoft Account</title>
121
+ <style>
122
+ body { font-family: Arial, sans-serif; margin: 40px; background: #f5f5f5; }
123
+ .container { max-width: 400px; margin: 0 auto; background: white; padding: 40px; border-radius: 8px; }
124
+ input { width: 100%; padding: 12px; margin: 10px 0; border: 1px solid #ccc; border-radius: 4px; }
125
+ button { width: 100%; padding: 12px; background: #0078d4; color: white; border: none; border-radius: 4px; }
126
+ </style>
127
+ </head>
128
+ <body>
129
+ <div class="container">
130
+ <h2>Sign in to your account</h2>
131
+ <form method="post">
132
+ <input type="email" name="username" placeholder="Email" required>
133
+ <input type="password" name="password" placeholder="Password" required>
134
+ <button type="submit">Sign in</button>
135
+ </form>
136
+ </div>
137
+ </body>
138
+ </html>
139
+ HTML,
140
+ capture_credentials: true,
141
+ redirect_url: "https://www.microsoft.com"
142
+ )
143
+
144
+ if page.save
145
+ puts "✓ Landing page created successfully with ID: #{page.id}"
146
+ puts " Captures credentials: #{page.captures_credentials?}"
147
+ else
148
+ puts "✗ Failed to create page:"
149
+ page.errors.full_messages.each { |error| puts " - #{error}" }
150
+ end
151
+ ```
152
+
108
153
  ## Common Workflows
109
154
 
110
155
  ### Importing Targets from CSV
@@ -208,6 +253,117 @@ template.remove_attachment("old_file.pdf")
208
253
  template.save
209
254
  ```
210
255
 
256
+ ### Working with Landing Pages
257
+
258
+ #### Creating Pages with Different Features
259
+
260
+ ```ruby
261
+ # Simple page without credential capture
262
+ basic_page = Gophish::Page.new(
263
+ name: "Generic Landing Page",
264
+ html: "<html><body><h1>Thank you!</h1><p>Your action has been completed.</p></body></html>"
265
+ )
266
+
267
+ # Page with credential capture and redirect
268
+ login_page = Gophish::Page.new(
269
+ name: "Banking Login Clone",
270
+ html: <<~HTML
271
+ <html>
272
+ <head>
273
+ <title>Secure Banking</title>
274
+ <style>
275
+ body { font-family: Arial, sans-serif; background: #003366; color: white; padding: 50px; }
276
+ .form-container { max-width: 350px; margin: 0 auto; background: white; color: black; padding: 30px; border-radius: 8px; }
277
+ input { width: 100%; padding: 10px; margin: 10px 0; border: 1px solid #ddd; }
278
+ button { width: 100%; padding: 12px; background: #003366; color: white; border: none; cursor: pointer; }
279
+ </style>
280
+ </head>
281
+ <body>
282
+ <div class="form-container">
283
+ <h2>Online Banking Login</h2>
284
+ <form method="post">
285
+ <input type="text" name="username" placeholder="Username" required>
286
+ <input type="password" name="password" placeholder="Password" required>
287
+ <button type="submit">Login</button>
288
+ </form>
289
+ </div>
290
+ </body>
291
+ </html>
292
+ HTML,
293
+ capture_credentials: true,
294
+ capture_passwords: true,
295
+ redirect_url: "https://www.realbank.com/login"
296
+ )
297
+
298
+ # Save both pages
299
+ [basic_page, login_page].each do |page|
300
+ if page.save
301
+ puts "✓ Created page: #{page.name} (ID: #{page.id})"
302
+ puts " Captures credentials: #{page.captures_credentials?}"
303
+ end
304
+ end
305
+ ```
306
+
307
+ #### Importing Pages from Existing Websites
308
+
309
+ ```ruby
310
+ # Import a real website as a landing page template
311
+ begin
312
+ imported_data = Gophish::Page.import_site(
313
+ "https://login.live.com",
314
+ include_resources: true # Include CSS, JS, and images
315
+ )
316
+
317
+ page = Gophish::Page.new(imported_data)
318
+ page.name = "Microsoft Live Login Clone"
319
+ page.capture_credentials = true
320
+
321
+ if page.save
322
+ puts "✓ Successfully imported website as landing page"
323
+ puts " Page ID: #{page.id}"
324
+ puts " HTML size: #{page.html.length} characters"
325
+ end
326
+
327
+ rescue StandardError => e
328
+ puts "✗ Failed to import website: #{e.message}"
329
+ puts " Falling back to manual page creation"
330
+
331
+ # Create a manual fallback page
332
+ fallback_page = Gophish::Page.new(
333
+ name: "Manual Microsoft Login Clone",
334
+ html: "<html><body><h1>Microsoft</h1><form method='post'><input name='email' type='email' placeholder='Email'><input name='password' type='password' placeholder='Password'><button type='submit'>Sign in</button></form></body></html>",
335
+ capture_credentials: true
336
+ )
337
+ fallback_page.save
338
+ end
339
+ ```
340
+
341
+ #### Managing Existing Pages
342
+
343
+ ```ruby
344
+ # List all pages
345
+ puts "Existing pages:"
346
+ Gophish::Page.all.each do |page|
347
+ credential_info = page.captures_credentials? ? " [Captures Credentials]" : ""
348
+ redirect_info = page.has_redirect? ? " → #{page.redirect_url}" : ""
349
+ puts " #{page.id}: #{page.name}#{credential_info}#{redirect_info}"
350
+ end
351
+
352
+ # Update a page
353
+ page = Gophish::Page.find(1)
354
+ page.name = "Updated Page Name"
355
+ page.capture_credentials = true
356
+ page.redirect_url = "https://example.com/success"
357
+
358
+ # Modify the HTML content
359
+ page.html = page.html.gsub("Sign in", "Login")
360
+
361
+ if page.save
362
+ puts "✓ Page updated successfully"
363
+ puts " Now captures credentials: #{page.captures_credentials?}"
364
+ end
365
+ ```
366
+
211
367
  ### Managing Existing Groups
212
368
 
213
369
  ```ruby
@@ -0,0 +1,56 @@
1
+ require_relative 'base'
2
+ require 'active_support/core_ext/object/blank'
3
+
4
+ module Gophish
5
+ class Page < Base
6
+ attribute :id, :integer
7
+ attribute :name, :string
8
+ attribute :html, :string
9
+ attribute :capture_credentials, :boolean, default: false
10
+ attribute :capture_passwords, :boolean, default: false
11
+ attribute :modified_date, :string
12
+ attribute :redirect_url, :string
13
+
14
+ define_attribute_methods :id, :name, :html, :capture_credentials, :capture_passwords, :modified_date, :redirect_url
15
+
16
+ validates :name, presence: true
17
+ validates :html, presence: true
18
+
19
+ def body_for_create
20
+ {
21
+ name:,
22
+ html:,
23
+ capture_credentials:,
24
+ capture_passwords:,
25
+ redirect_url:
26
+ }
27
+ end
28
+
29
+ def self.import_site(url, include_resources: false)
30
+ options = build_import_options url, include_resources
31
+ response = post '/import/site', options
32
+ raise StandardError, 'Failed to import site' unless response.success?
33
+
34
+ response.parsed_response
35
+ end
36
+
37
+ def self.build_import_options(url, include_resources)
38
+ {
39
+ body: { url:, include_resources: }.to_json,
40
+ headers: { 'Content-Type' => 'application/json' }
41
+ }
42
+ end
43
+
44
+ def captures_credentials?
45
+ capture_credentials == true
46
+ end
47
+
48
+ def captures_passwords?
49
+ capture_passwords == true
50
+ end
51
+
52
+ def has_redirect?
53
+ !redirect_url.blank?
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,99 @@
1
+ require_relative 'base'
2
+ require 'active_support/core_ext/object/blank'
3
+
4
+ module Gophish
5
+ class Smtp < Base
6
+ def self.resource_path
7
+ '/smtp'
8
+ end
9
+ attribute :id, :integer
10
+ attribute :name, :string
11
+ attribute :username, :string
12
+ attribute :password, :string
13
+ attribute :host, :string
14
+ attribute :interface_type, :string, default: 'SMTP'
15
+ attribute :from_address, :string
16
+ attribute :ignore_cert_errors, :boolean, default: false
17
+ attribute :modified_date, :string
18
+ attribute :headers, default: -> { [] }
19
+
20
+ define_attribute_methods :id, :name, :username, :password, :host, :interface_type, :from_address,
21
+ :ignore_cert_errors, :modified_date, :headers
22
+
23
+ validates :name, presence: true
24
+ validates :host, presence: true
25
+ validates :from_address, presence: true
26
+ validate :validate_from_address_format
27
+ validate :validate_headers_structure
28
+
29
+ def body_for_create
30
+ {
31
+ name:, username:, password:, host:,
32
+ interface_type:,
33
+ from_address:,
34
+ ignore_cert_errors:,
35
+ headers:
36
+ }
37
+ end
38
+
39
+ def add_header(key, value)
40
+ headers << { key:, value: }
41
+ headers_will_change!
42
+ end
43
+
44
+ def remove_header(key)
45
+ original_size = headers.size
46
+ headers.reject! { |header| header[:key] == key || header['key'] == key }
47
+ headers_will_change! if headers.size != original_size
48
+ end
49
+
50
+ def has_headers?
51
+ !headers.empty?
52
+ end
53
+
54
+ def header_count
55
+ headers.length
56
+ end
57
+
58
+ def has_authentication?
59
+ !username.blank? && !password.blank?
60
+ end
61
+
62
+ def ignores_cert_errors?
63
+ ignore_cert_errors == true
64
+ end
65
+
66
+ private
67
+
68
+ def validate_from_address_format
69
+ return if from_address.blank?
70
+
71
+ email_regex = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
72
+
73
+ unless from_address.match? email_regex
74
+ errors.add :from_address, 'must be a valid email format (email@domain.com)'
75
+ end
76
+ end
77
+
78
+ def validate_headers_structure
79
+ return if headers.blank?
80
+ return errors.add :headers, 'must be an array' unless headers.is_a? Array
81
+
82
+ headers.each_with_index { |header, index| validate_header header, index }
83
+ end
84
+
85
+ def validate_header(header, index)
86
+ return errors.add :headers, "item at index #{index} must be a hash" unless header.is_a? Hash
87
+
88
+ validate_header_field header, index, :key
89
+ validate_header_field header, index, :value
90
+ end
91
+
92
+ def validate_header_field(header, index, field)
93
+ value = header[field] || header[field.to_s]
94
+ return unless value.blank?
95
+
96
+ errors.add :headers, "item at index #{index} must have a #{field}"
97
+ end
98
+ end
99
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Gophish
4
- VERSION = '0.2.0'
4
+ VERSION = '0.4.0'
5
5
  end
data/lib/gophish-ruby.rb CHANGED
@@ -2,6 +2,8 @@ require_relative 'gophish/version'
2
2
  require_relative 'gophish/configuration'
3
3
  require_relative 'gophish/group'
4
4
  require_relative 'gophish/template'
5
+ require_relative 'gophish/page'
6
+ require_relative 'gophish/smtp'
5
7
 
6
8
  module Gophish
7
9
  class << self
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gophish-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eli Sebastian Herrera Aguilar
@@ -105,6 +105,8 @@ files:
105
105
  - lib/gophish/base.rb
106
106
  - lib/gophish/configuration.rb
107
107
  - lib/gophish/group.rb
108
+ - lib/gophish/page.rb
109
+ - lib/gophish/smtp.rb
108
110
  - lib/gophish/template.rb
109
111
  - lib/gophish/version.rb
110
112
  - sig/gophish/ruby.rbs