gophish-ruby 0.1.0 → 0.3.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.
@@ -84,6 +84,72 @@ else
84
84
  end
85
85
  ```
86
86
 
87
+ ### 4. Create Your First Template
88
+
89
+ Templates define the email content for your phishing campaigns:
90
+
91
+ ```ruby
92
+ # Create a basic email template
93
+ template = Gophish::Template.new(
94
+ name: "Security Awareness Test",
95
+ subject: "Important Security Update Required",
96
+ html: "<h1>Security Update</h1><p>Please click <a href='{{.URL}}'>here</a> to update your password.</p>",
97
+ text: "Security Update\n\nPlease visit {{.URL}} to update your password."
98
+ )
99
+
100
+ if template.save
101
+ puts "✓ Template created successfully with ID: #{template.id}"
102
+ else
103
+ puts "✗ Failed to create template:"
104
+ template.errors.full_messages.each { |error| puts " - #{error}" }
105
+ end
106
+ ```
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
+
87
153
  ## Common Workflows
88
154
 
89
155
  ### Importing Targets from CSV
@@ -125,6 +191,179 @@ else
125
191
  end
126
192
  ```
127
193
 
194
+ ### Working with Templates
195
+
196
+ #### Creating Templates with Attachments
197
+
198
+ ```ruby
199
+ # Create template with file attachments
200
+ template = Gophish::Template.new(
201
+ name: "Invoice Template",
202
+ subject: "Your Invoice #{{.RId}}",
203
+ html: "<p>Dear {{.FirstName}},</p><p>Please find your invoice attached.</p>"
204
+ )
205
+
206
+ # Add PDF attachment
207
+ pdf_content = File.read("sample_invoice.pdf")
208
+ template.add_attachment(pdf_content, "application/pdf", "invoice.pdf")
209
+
210
+ # Check attachments
211
+ puts "Template has #{template.attachment_count} attachments" if template.has_attachments?
212
+
213
+ template.save
214
+ ```
215
+
216
+ #### Importing Email Templates
217
+
218
+ ```ruby
219
+ # Import an existing email (.eml file)
220
+ email_content = File.read("phishing_template.eml")
221
+
222
+ imported_data = Gophish::Template.import_email(
223
+ email_content,
224
+ convert_links: true # Convert links for Gophish tracking
225
+ )
226
+
227
+ template = Gophish::Template.new(imported_data)
228
+ template.name = "Imported Email Template"
229
+ template.save
230
+
231
+ puts "Imported template: #{template.name}"
232
+ ```
233
+
234
+ #### Managing Existing Templates
235
+
236
+ ```ruby
237
+ # List all templates
238
+ puts "Existing templates:"
239
+ Gophish::Template.all.each do |template|
240
+ attachment_info = template.has_attachments? ? " (#{template.attachment_count} attachments)" : ""
241
+ puts " #{template.id}: #{template.name}#{attachment_info}"
242
+ end
243
+
244
+ # Update a template
245
+ template = Gophish::Template.find(1)
246
+ template.subject = "Updated Subject Line"
247
+ template.html = "<h1>Updated Content</h1><p>New message content here.</p>"
248
+
249
+ # Add or remove attachments
250
+ template.add_attachment(File.read("new_file.pdf"), "application/pdf", "new_file.pdf")
251
+ template.remove_attachment("old_file.pdf")
252
+
253
+ template.save
254
+ ```
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
+
128
367
  ### Managing Existing Groups
129
368
 
130
369
  ```ruby
data/lib/gophish/base.rb CHANGED
@@ -13,12 +13,13 @@ module Gophish
13
13
  include ActiveModel::Model
14
14
  include ActiveModel::Attributes
15
15
  include ActiveModel::Validations
16
+ include ActiveModel::Dirty
16
17
  include ActiveRecord::Callbacks
17
18
 
18
19
  def initialize(attributes = {})
19
20
  @persisted = false
20
- @changed_attributes = {}
21
21
  super(attributes)
22
+ clear_changes_information
22
23
  end
23
24
 
24
25
  def self.configuration
@@ -125,7 +126,7 @@ module Gophish
125
126
  send "#{key}=", value if respond_to? "#{key}="
126
127
  end
127
128
  @persisted = true
128
- @changed_attributes.clear
129
+ clear_changes_information
129
130
  end
130
131
 
131
132
  def handle_error_response(response)
@@ -145,9 +146,9 @@ module Gophish
145
146
 
146
147
  def update_record
147
148
  return false if id.nil?
148
- return true if @changed_attributes.empty?
149
+ return true unless changed?
149
150
 
150
- response = self.class.put "#{self.class.resource_path}/#{id}/", request_options(body_for_update)
151
+ response = self.class.put "#{self.class.resource_path}/#{id}", request_options(body_for_update)
151
152
  return handle_error_response response unless response.success?
152
153
 
153
154
  update_attributes_from_response response.parsed_response
@@ -177,29 +178,10 @@ module Gophish
177
178
  end
178
179
 
179
180
  def body_for_update
180
- body_for_create
181
- end
182
-
183
- def attribute_changed?(attribute)
184
- @changed_attributes.key? attribute.to_s
185
- end
186
-
187
- def changed_attributes
188
- @changed_attributes.keys
189
- end
190
-
191
- def attribute_was(attribute)
192
- @changed_attributes[attribute.to_s]
181
+ body_for_create.merge id:
193
182
  end
194
183
 
195
184
  def []=(attribute, value)
196
- attribute_str = attribute.to_s
197
- current_value = send attribute if respond_to? attribute
198
-
199
- unless current_value == value
200
- @changed_attributes[attribute_str] = current_value
201
- end
202
-
203
185
  send "#{attribute}=", value if respond_to? "#{attribute}="
204
186
  end
205
187
  end
data/lib/gophish/group.rb CHANGED
@@ -9,6 +9,8 @@ module Gophish
9
9
  attribute :modified_date, :string
10
10
  attribute :targets
11
11
 
12
+ define_attribute_methods :id, :name, :modified_date, :targets
13
+
12
14
  validates :name, presence: true
13
15
  validates :targets, presence: true
14
16
  validate :validate_targets_structure
@@ -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,94 @@
1
+ require_relative 'base'
2
+ require 'active_support/core_ext/object/blank'
3
+ require 'base64'
4
+
5
+ module Gophish
6
+ class Template < Base
7
+ attribute :id, :integer
8
+ attribute :name, :string
9
+ attribute :subject, :string
10
+ attribute :text, :string
11
+ attribute :html, :string
12
+ attribute :modified_date, :string
13
+ attribute :attachments, default: -> { [] }
14
+
15
+ define_attribute_methods :id, :name, :subject, :text, :html, :modified_date, :attachments
16
+
17
+ validates :name, presence: true
18
+ validate :validate_content_presence
19
+ validate :validate_attachments_structure
20
+
21
+ def body_for_create
22
+ { name:, subject:, text:, html:, attachments: }
23
+ end
24
+
25
+ def self.import_email(content, convert_links: false)
26
+ options = build_import_options content, convert_links
27
+ response = post '/import/email', options
28
+ raise StandardError, 'Failed to import email' unless response.success?
29
+
30
+ response.parsed_response
31
+ end
32
+
33
+ def self.build_import_options(content, convert_links)
34
+ {
35
+ body: { content:, convert_links: }.to_json,
36
+ headers: { 'Content-Type' => 'application/json' }
37
+ }
38
+ end
39
+
40
+ def add_attachment(content, type, name)
41
+ encoded_content = encode_content content
42
+ attachments << { content: encoded_content, type:, name: }
43
+ attachments_will_change!
44
+ end
45
+
46
+ def remove_attachment(name)
47
+ original_size = attachments.size
48
+ attachments.reject! { |attachment| attachment[:name] == name || attachment['name'] == name }
49
+ attachments_will_change! if attachments.size != original_size
50
+ end
51
+
52
+ def has_attachments?
53
+ !attachments.empty?
54
+ end
55
+
56
+ def attachment_count
57
+ attachments.length
58
+ end
59
+
60
+ private
61
+
62
+ def encode_content(content)
63
+ content.is_a?(String) ? Base64.strict_encode64(content) : content
64
+ end
65
+
66
+ def validate_content_presence
67
+ return unless text.blank? && html.blank?
68
+
69
+ errors.add :base, 'Need to specify at least plaintext or HTML content'
70
+ end
71
+
72
+ def validate_attachments_structure
73
+ return if attachments.blank?
74
+ return errors.add :attachments, 'must be an array' unless attachments.is_a? Array
75
+
76
+ attachments.each_with_index { |attachment, index| validate_attachment attachment, index }
77
+ end
78
+
79
+ def validate_attachment(attachment, index)
80
+ return errors.add :attachments, "item at index #{index} must be a hash" unless attachment.is_a? Hash
81
+
82
+ validate_attachment_field attachment, index, :content
83
+ validate_attachment_field attachment, index, :type
84
+ validate_attachment_field attachment, index, :name
85
+ end
86
+
87
+ def validate_attachment_field(attachment, index, field)
88
+ value = attachment[field] || attachment[field.to_s]
89
+ return unless value.blank?
90
+
91
+ errors.add :attachments, "item at index #{index} must have a #{field}"
92
+ end
93
+ end
94
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Gophish
4
- VERSION = '0.1.0'
4
+ VERSION = '0.3.0'
5
5
  end
data/lib/gophish-ruby.rb CHANGED
@@ -1,7 +1,8 @@
1
1
  require_relative 'gophish/version'
2
2
  require_relative 'gophish/configuration'
3
- require_relative 'gophish/base'
4
3
  require_relative 'gophish/group'
4
+ require_relative 'gophish/template'
5
+ require_relative 'gophish/page'
5
6
 
6
7
  module Gophish
7
8
  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.1.0
4
+ version: 0.3.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/template.rb
108
110
  - lib/gophish/version.rb
109
111
  - sig/gophish/ruby.rbs
110
112
  homepage: https://github.com/EliSebastian/gopish-ruby