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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +41 -0
- data/README.md +342 -2
- data/Rakefile +1 -1
- data/docs/API_REFERENCE.md +572 -4
- data/docs/EXAMPLES.md +984 -0
- data/docs/GETTING_STARTED.md +239 -0
- data/lib/gophish/base.rb +6 -24
- data/lib/gophish/group.rb +2 -0
- data/lib/gophish/page.rb +56 -0
- data/lib/gophish/template.rb +94 -0
- data/lib/gophish/version.rb +1 -1
- data/lib/gophish-ruby.rb +2 -1
- metadata +3 -1
data/docs/EXAMPLES.md
CHANGED
@@ -6,6 +6,8 @@ This document contains practical examples for common use cases with the Gophish
|
|
6
6
|
|
7
7
|
- [Basic Operations](#basic-operations)
|
8
8
|
- [CSV Operations](#csv-operations)
|
9
|
+
- [Template Operations](#template-operations)
|
10
|
+
- [Page Operations](#page-operations)
|
9
11
|
- [Error Handling](#error-handling)
|
10
12
|
- [Advanced Scenarios](#advanced-scenarios)
|
11
13
|
- [Production Examples](#production-examples)
|
@@ -252,6 +254,988 @@ end
|
|
252
254
|
group = import_large_csv("large_employee_list.csv", "All Company Employees")
|
253
255
|
```
|
254
256
|
|
257
|
+
## Template Operations
|
258
|
+
|
259
|
+
### Basic Template Creation
|
260
|
+
|
261
|
+
```ruby
|
262
|
+
# Simple text and HTML template
|
263
|
+
template = Gophish::Template.new(
|
264
|
+
name: "Security Awareness Training",
|
265
|
+
subject: "Important Security Update - Action Required",
|
266
|
+
html: <<~HTML,
|
267
|
+
<h1>Security Update Required</h1>
|
268
|
+
<p>Dear {{.FirstName}},</p>
|
269
|
+
<p>We have detected suspicious activity on your account. Please click <a href="{{.URL}}">here</a> to verify your account immediately.</p>
|
270
|
+
<p>This link will expire in 24 hours.</p>
|
271
|
+
<p>Best regards,<br>IT Security Team</p>
|
272
|
+
HTML
|
273
|
+
text: <<~TEXT
|
274
|
+
Security Update Required
|
275
|
+
|
276
|
+
Dear {{.FirstName}},
|
277
|
+
|
278
|
+
We have detected suspicious activity on your account. Please visit {{.URL}} to verify your account immediately.
|
279
|
+
|
280
|
+
This link will expire in 24 hours.
|
281
|
+
|
282
|
+
Best regards,
|
283
|
+
IT Security Team
|
284
|
+
TEXT
|
285
|
+
)
|
286
|
+
|
287
|
+
if template.save
|
288
|
+
puts "✓ Template '#{template.name}' created with ID: #{template.id}"
|
289
|
+
else
|
290
|
+
puts "✗ Failed to create template: #{template.errors.full_messages}"
|
291
|
+
end
|
292
|
+
```
|
293
|
+
|
294
|
+
### Template with Attachments
|
295
|
+
|
296
|
+
```ruby
|
297
|
+
# Create template with multiple attachments
|
298
|
+
template = Gophish::Template.new(
|
299
|
+
name: "Invoice Phishing Template",
|
300
|
+
subject: "Invoice #{{.RId}} - Payment Due",
|
301
|
+
html: "<h1>Invoice Attached</h1><p>Dear {{.FirstName}},</p><p>Please find your invoice attached for immediate payment.</p>"
|
302
|
+
)
|
303
|
+
|
304
|
+
# Add PDF invoice attachment
|
305
|
+
pdf_content = File.read("sample_invoice.pdf")
|
306
|
+
template.add_attachment(pdf_content, "application/pdf", "invoice_#{Time.now.strftime('%Y%m%d')}.pdf")
|
307
|
+
|
308
|
+
# Add company logo
|
309
|
+
logo_content = File.read("company_logo.png")
|
310
|
+
template.add_attachment(logo_content, "image/png", "logo.png")
|
311
|
+
|
312
|
+
# Add fake Excel spreadsheet
|
313
|
+
excel_content = File.read("expense_report.xlsx")
|
314
|
+
template.add_attachment(excel_content, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "Q4_expenses.xlsx")
|
315
|
+
|
316
|
+
puts "Template has #{template.attachment_count} attachments"
|
317
|
+
|
318
|
+
if template.save
|
319
|
+
puts "✓ Template with attachments created successfully"
|
320
|
+
end
|
321
|
+
```
|
322
|
+
|
323
|
+
### Email Import from .EML Files
|
324
|
+
|
325
|
+
```ruby
|
326
|
+
# Import existing phishing email
|
327
|
+
def import_phishing_template(eml_file_path, template_name)
|
328
|
+
unless File.exist?(eml_file_path)
|
329
|
+
puts "✗ EML file not found: #{eml_file_path}"
|
330
|
+
return nil
|
331
|
+
end
|
332
|
+
|
333
|
+
email_content = File.read(eml_file_path)
|
334
|
+
|
335
|
+
# Import with link conversion for tracking
|
336
|
+
begin
|
337
|
+
imported_data = Gophish::Template.import_email(email_content, convert_links: true)
|
338
|
+
rescue StandardError => e
|
339
|
+
puts "✗ Email import failed: #{e.message}"
|
340
|
+
return nil
|
341
|
+
end
|
342
|
+
|
343
|
+
# Create template from imported data
|
344
|
+
template = Gophish::Template.new(imported_data)
|
345
|
+
template.name = template_name
|
346
|
+
|
347
|
+
if template.valid?
|
348
|
+
if template.save
|
349
|
+
puts "✓ Successfully imported template '#{template.name}'"
|
350
|
+
puts " Subject: #{template.subject}"
|
351
|
+
puts " Has HTML: #{!template.html.nil?}"
|
352
|
+
puts " Has Text: #{!template.text.nil?}"
|
353
|
+
puts " Attachments: #{template.attachment_count}"
|
354
|
+
return template
|
355
|
+
else
|
356
|
+
puts "✗ Save failed: #{template.errors.full_messages}"
|
357
|
+
end
|
358
|
+
else
|
359
|
+
puts "✗ Validation failed: #{template.errors.full_messages}"
|
360
|
+
end
|
361
|
+
|
362
|
+
nil
|
363
|
+
end
|
364
|
+
|
365
|
+
# Usage
|
366
|
+
template = import_phishing_template("phishing_email.eml", "Imported Phishing Template")
|
367
|
+
```
|
368
|
+
|
369
|
+
### Template Management Operations
|
370
|
+
|
371
|
+
```ruby
|
372
|
+
# List all existing templates
|
373
|
+
def list_templates
|
374
|
+
templates = Gophish::Template.all
|
375
|
+
puts "Found #{templates.length} templates:"
|
376
|
+
|
377
|
+
templates.each do |template|
|
378
|
+
attachment_info = template.has_attachments? ? " (#{template.attachment_count} attachments)" : ""
|
379
|
+
puts " #{template.id}: #{template.name}#{attachment_info}"
|
380
|
+
puts " Subject: #{template.subject}" if template.subject
|
381
|
+
puts " Modified: #{template.modified_date}" if template.modified_date
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
# Update existing template
|
386
|
+
def update_template(template_id, new_subject = nil, new_html = nil)
|
387
|
+
begin
|
388
|
+
template = Gophish::Template.find(template_id)
|
389
|
+
rescue StandardError
|
390
|
+
puts "✗ Template #{template_id} not found"
|
391
|
+
return false
|
392
|
+
end
|
393
|
+
|
394
|
+
puts "Updating template '#{template.name}'"
|
395
|
+
|
396
|
+
template.subject = new_subject if new_subject
|
397
|
+
template.html = new_html if new_html
|
398
|
+
|
399
|
+
if template.save
|
400
|
+
puts "✓ Template updated successfully"
|
401
|
+
true
|
402
|
+
else
|
403
|
+
puts "✗ Update failed: #{template.errors.full_messages}"
|
404
|
+
false
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
# Clone template with modifications
|
409
|
+
def clone_template(original_id, new_name, modifications = {})
|
410
|
+
begin
|
411
|
+
original = Gophish::Template.find(original_id)
|
412
|
+
rescue StandardError
|
413
|
+
puts "✗ Original template #{original_id} not found"
|
414
|
+
return nil
|
415
|
+
end
|
416
|
+
|
417
|
+
# Create new template with same content
|
418
|
+
new_template = Gophish::Template.new(
|
419
|
+
name: new_name,
|
420
|
+
subject: original.subject,
|
421
|
+
html: original.html,
|
422
|
+
text: original.text
|
423
|
+
)
|
424
|
+
|
425
|
+
# Apply modifications
|
426
|
+
modifications.each do |field, value|
|
427
|
+
new_template.send("#{field}=", value) if new_template.respond_to?("#{field}=")
|
428
|
+
end
|
429
|
+
|
430
|
+
# Copy attachments
|
431
|
+
if original.has_attachments?
|
432
|
+
original.attachments.each do |attachment|
|
433
|
+
new_template.attachments << attachment.dup
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
if new_template.save
|
438
|
+
puts "✓ Template cloned as '#{new_name}' (ID: #{new_template.id})"
|
439
|
+
new_template
|
440
|
+
else
|
441
|
+
puts "✗ Clone failed: #{new_template.errors.full_messages}"
|
442
|
+
nil
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
# Usage examples
|
447
|
+
list_templates
|
448
|
+
update_template(1, "Updated Subject Line", "<h1>Updated HTML content</h1>")
|
449
|
+
clone_template(1, "Modified Version", { subject: "Modified Subject" })
|
450
|
+
```
|
451
|
+
|
452
|
+
### Template Validation and Error Handling
|
453
|
+
|
454
|
+
```ruby
|
455
|
+
# Comprehensive template validation
|
456
|
+
def validate_template_thoroughly(template)
|
457
|
+
puts "Validating template '#{template.name}'"
|
458
|
+
|
459
|
+
# Basic validation
|
460
|
+
unless template.valid?
|
461
|
+
puts "✗ Basic validation failed:"
|
462
|
+
template.errors.full_messages.each { |error| puts " - #{error}" }
|
463
|
+
return false
|
464
|
+
end
|
465
|
+
|
466
|
+
# Content validation
|
467
|
+
has_html = !template.html.nil? && !template.html.strip.empty?
|
468
|
+
has_text = !template.text.nil? && !template.text.strip.empty?
|
469
|
+
|
470
|
+
unless has_html || has_text
|
471
|
+
puts "✗ Template has no content (neither HTML nor text)"
|
472
|
+
return false
|
473
|
+
end
|
474
|
+
|
475
|
+
# Gophish template variable validation
|
476
|
+
content = "#{template.html} #{template.text} #{template.subject}"
|
477
|
+
|
478
|
+
# Check for common Gophish template variables
|
479
|
+
variables_found = content.scan(/\{\{\.(\w+)\}\}/).flatten.uniq
|
480
|
+
puts " Found template variables: #{variables_found.join(', ')}" if variables_found.any?
|
481
|
+
|
482
|
+
# Warn about missing tracking URL
|
483
|
+
unless content.include?('{{.URL}}')
|
484
|
+
puts " ⚠ Warning: No {{.URL}} tracking variable found"
|
485
|
+
end
|
486
|
+
|
487
|
+
# Attachment validation
|
488
|
+
if template.has_attachments?
|
489
|
+
puts " Validating #{template.attachment_count} attachments:"
|
490
|
+
template.attachments.each_with_index do |attachment, index|
|
491
|
+
name = attachment[:name] || attachment['name']
|
492
|
+
type = attachment[:type] || attachment['type']
|
493
|
+
content = attachment[:content] || attachment['content']
|
494
|
+
|
495
|
+
puts " #{index + 1}. #{name} (#{type})"
|
496
|
+
|
497
|
+
if content.nil? || content.empty?
|
498
|
+
puts " ✗ Missing content"
|
499
|
+
return false
|
500
|
+
end
|
501
|
+
|
502
|
+
# Validate Base64 encoding
|
503
|
+
begin
|
504
|
+
Base64.strict_decode64(content)
|
505
|
+
puts " ✓ Valid Base64 encoding"
|
506
|
+
rescue ArgumentError
|
507
|
+
puts " ✗ Invalid Base64 encoding"
|
508
|
+
return false
|
509
|
+
end
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
puts "✓ Template validation passed"
|
514
|
+
true
|
515
|
+
end
|
516
|
+
|
517
|
+
# Test various template scenarios
|
518
|
+
def test_template_scenarios
|
519
|
+
# Valid template
|
520
|
+
valid_template = Gophish::Template.new(
|
521
|
+
name: "Valid Template",
|
522
|
+
subject: "Test {{.FirstName}}",
|
523
|
+
html: "<p>Click {{.URL}} to continue</p>",
|
524
|
+
text: "Visit {{.URL}} to continue"
|
525
|
+
)
|
526
|
+
validate_template_thoroughly(valid_template)
|
527
|
+
|
528
|
+
# Invalid template (no content)
|
529
|
+
invalid_template = Gophish::Template.new(
|
530
|
+
name: "Invalid Template",
|
531
|
+
subject: "Test"
|
532
|
+
)
|
533
|
+
validate_template_thoroughly(invalid_template)
|
534
|
+
|
535
|
+
# Template with attachment
|
536
|
+
template_with_attachment = Gophish::Template.new(
|
537
|
+
name: "Attachment Template",
|
538
|
+
html: "<p>See attachment</p>"
|
539
|
+
)
|
540
|
+
template_with_attachment.add_attachment("Hello World", "text/plain", "test.txt")
|
541
|
+
validate_template_thoroughly(template_with_attachment)
|
542
|
+
end
|
543
|
+
|
544
|
+
test_template_scenarios
|
545
|
+
```
|
546
|
+
|
547
|
+
### Bulk Template Operations
|
548
|
+
|
549
|
+
```ruby
|
550
|
+
# Create multiple templates from a configuration
|
551
|
+
def create_template_suite(campaign_name)
|
552
|
+
templates = [
|
553
|
+
{
|
554
|
+
name: "#{campaign_name} - Initial Email",
|
555
|
+
subject: "Important Account Update Required",
|
556
|
+
html: "<h1>Account Update</h1><p>Dear {{.FirstName}}, please update your account by clicking <a href='{{.URL}}'>here</a>.</p>",
|
557
|
+
text: "Dear {{.FirstName}}, please update your account by visiting {{.URL}}"
|
558
|
+
},
|
559
|
+
{
|
560
|
+
name: "#{campaign_name} - Follow-up",
|
561
|
+
subject: "URGENT: Account Suspension Notice",
|
562
|
+
html: "<h1 style='color: red;'>URGENT</h1><p>Your account will be suspended in 24 hours. Verify immediately: {{.URL}}</p>",
|
563
|
+
text: "URGENT: Your account will be suspended in 24 hours. Verify at {{.URL}}"
|
564
|
+
},
|
565
|
+
{
|
566
|
+
name: "#{campaign_name} - Final Warning",
|
567
|
+
subject: "Final Warning - Account Closure",
|
568
|
+
html: "<h1>Final Warning</h1><p>This is your last chance to save your account: {{.URL}}</p>",
|
569
|
+
text: "Final Warning: Last chance to save your account at {{.URL}}"
|
570
|
+
}
|
571
|
+
]
|
572
|
+
|
573
|
+
created_templates = []
|
574
|
+
|
575
|
+
templates.each_with_index do |template_data, index|
|
576
|
+
puts "Creating template #{index + 1}/#{templates.length}: #{template_data[:name]}"
|
577
|
+
|
578
|
+
template = Gophish::Template.new(template_data)
|
579
|
+
|
580
|
+
if template.save
|
581
|
+
puts " ✓ Created with ID: #{template.id}"
|
582
|
+
created_templates << template
|
583
|
+
else
|
584
|
+
puts " ✗ Failed: #{template.errors.full_messages}"
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
puts "\nTemplate suite '#{campaign_name}' creation completed"
|
589
|
+
puts "Successfully created: #{created_templates.length}/#{templates.length} templates"
|
590
|
+
|
591
|
+
created_templates
|
592
|
+
end
|
593
|
+
|
594
|
+
# Usage
|
595
|
+
suite = create_template_suite("Q4 Security Training")
|
596
|
+
```
|
597
|
+
|
598
|
+
## Page Operations
|
599
|
+
|
600
|
+
### Basic Landing Page Creation
|
601
|
+
|
602
|
+
```ruby
|
603
|
+
# Simple landing page without credential capture
|
604
|
+
simple_page = Gophish::Page.new(
|
605
|
+
name: "Generic Thank You Page",
|
606
|
+
html: <<~HTML
|
607
|
+
<!DOCTYPE html>
|
608
|
+
<html>
|
609
|
+
<head>
|
610
|
+
<title>Thank You</title>
|
611
|
+
<style>
|
612
|
+
body {
|
613
|
+
font-family: Arial, sans-serif;
|
614
|
+
text-align: center;
|
615
|
+
background: #f0f0f0;
|
616
|
+
padding: 50px;
|
617
|
+
}
|
618
|
+
.container {
|
619
|
+
max-width: 500px;
|
620
|
+
margin: 0 auto;
|
621
|
+
background: white;
|
622
|
+
padding: 40px;
|
623
|
+
border-radius: 10px;
|
624
|
+
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
625
|
+
}
|
626
|
+
</style>
|
627
|
+
</head>
|
628
|
+
<body>
|
629
|
+
<div class="container">
|
630
|
+
<h1>Thank You!</h1>
|
631
|
+
<p>Your request has been processed successfully.</p>
|
632
|
+
<p>You will receive a confirmation email shortly.</p>
|
633
|
+
</div>
|
634
|
+
</body>
|
635
|
+
</html>
|
636
|
+
HTML
|
637
|
+
)
|
638
|
+
|
639
|
+
if simple_page.save
|
640
|
+
puts "✓ Simple page created: #{simple_page.id}"
|
641
|
+
end
|
642
|
+
```
|
643
|
+
|
644
|
+
### Landing Page with Credential Capture
|
645
|
+
|
646
|
+
```ruby
|
647
|
+
# Microsoft-style login page with credential capture
|
648
|
+
microsoft_page = Gophish::Page.new(
|
649
|
+
name: "Microsoft Office 365 Login Clone",
|
650
|
+
html: <<~HTML,
|
651
|
+
<!DOCTYPE html>
|
652
|
+
<html>
|
653
|
+
<head>
|
654
|
+
<title>Microsoft Office</title>
|
655
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
656
|
+
<style>
|
657
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
658
|
+
body {
|
659
|
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
660
|
+
background: #f5f5f5;
|
661
|
+
display: flex;
|
662
|
+
justify-content: center;
|
663
|
+
align-items: center;
|
664
|
+
min-height: 100vh;
|
665
|
+
}
|
666
|
+
.login-container {
|
667
|
+
background: white;
|
668
|
+
padding: 40px;
|
669
|
+
border-radius: 8px;
|
670
|
+
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
671
|
+
max-width: 400px;
|
672
|
+
width: 100%;
|
673
|
+
}
|
674
|
+
.logo {
|
675
|
+
text-align: center;
|
676
|
+
margin-bottom: 30px;
|
677
|
+
color: #737373;
|
678
|
+
font-size: 24px;
|
679
|
+
font-weight: 300;
|
680
|
+
}
|
681
|
+
h1 {
|
682
|
+
color: #1B1B1B;
|
683
|
+
font-size: 24px;
|
684
|
+
font-weight: 600;
|
685
|
+
margin-bottom: 20px;
|
686
|
+
}
|
687
|
+
.form-group {
|
688
|
+
margin-bottom: 20px;
|
689
|
+
}
|
690
|
+
input[type="email"], input[type="password"] {
|
691
|
+
width: 100%;
|
692
|
+
padding: 12px;
|
693
|
+
border: 1px solid #CCCCCC;
|
694
|
+
border-radius: 4px;
|
695
|
+
font-size: 14px;
|
696
|
+
transition: border-color 0.3s;
|
697
|
+
}
|
698
|
+
input[type="email"]:focus, input[type="password"]:focus {
|
699
|
+
outline: none;
|
700
|
+
border-color: #0078D4;
|
701
|
+
}
|
702
|
+
.signin-button {
|
703
|
+
width: 100%;
|
704
|
+
padding: 12px;
|
705
|
+
background: #0078D4;
|
706
|
+
color: white;
|
707
|
+
border: none;
|
708
|
+
border-radius: 4px;
|
709
|
+
font-size: 14px;
|
710
|
+
font-weight: 600;
|
711
|
+
cursor: pointer;
|
712
|
+
transition: background-color 0.3s;
|
713
|
+
}
|
714
|
+
.signin-button:hover {
|
715
|
+
background: #106EBE;
|
716
|
+
}
|
717
|
+
.footer-links {
|
718
|
+
text-align: center;
|
719
|
+
margin-top: 20px;
|
720
|
+
font-size: 12px;
|
721
|
+
}
|
722
|
+
.footer-links a {
|
723
|
+
color: #0078D4;
|
724
|
+
text-decoration: none;
|
725
|
+
margin: 0 10px;
|
726
|
+
}
|
727
|
+
</style>
|
728
|
+
</head>
|
729
|
+
<body>
|
730
|
+
<div class="login-container">
|
731
|
+
<div class="logo">Microsoft</div>
|
732
|
+
<h1>Sign in</h1>
|
733
|
+
<form method="post" action="">
|
734
|
+
<div class="form-group">
|
735
|
+
<input type="email" name="username" placeholder="Email, phone, or Skype" required>
|
736
|
+
</div>
|
737
|
+
<div class="form-group">
|
738
|
+
<input type="password" name="password" placeholder="Password" required>
|
739
|
+
</div>
|
740
|
+
<button type="submit" class="signin-button">Sign in</button>
|
741
|
+
</form>
|
742
|
+
<div class="footer-links">
|
743
|
+
<a href="#">Can't access your account?</a>
|
744
|
+
<a href="#">Sign-in options</a>
|
745
|
+
</div>
|
746
|
+
</div>
|
747
|
+
</body>
|
748
|
+
</html>
|
749
|
+
HTML
|
750
|
+
capture_credentials: true,
|
751
|
+
capture_passwords: true,
|
752
|
+
redirect_url: "https://office.com"
|
753
|
+
)
|
754
|
+
|
755
|
+
if microsoft_page.save
|
756
|
+
puts "✓ Microsoft login page created: #{microsoft_page.id}"
|
757
|
+
puts " Captures credentials: #{microsoft_page.captures_credentials?}"
|
758
|
+
puts " Captures passwords: #{microsoft_page.captures_passwords?}"
|
759
|
+
puts " Redirects to: #{microsoft_page.redirect_url}"
|
760
|
+
end
|
761
|
+
```
|
762
|
+
|
763
|
+
### Banking Login Page with Enhanced Security Theater
|
764
|
+
|
765
|
+
```ruby
|
766
|
+
# Realistic banking login page
|
767
|
+
banking_page = Gophish::Page.new(
|
768
|
+
name: "SecureBank Online Banking Portal",
|
769
|
+
html: <<~HTML,
|
770
|
+
<!DOCTYPE html>
|
771
|
+
<html>
|
772
|
+
<head>
|
773
|
+
<title>SecureBank - Online Banking</title>
|
774
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
775
|
+
<style>
|
776
|
+
body {
|
777
|
+
font-family: Arial, sans-serif;
|
778
|
+
margin: 0;
|
779
|
+
background: linear-gradient(135deg, #003366, #004499);
|
780
|
+
min-height: 100vh;
|
781
|
+
}
|
782
|
+
.header {
|
783
|
+
background: white;
|
784
|
+
padding: 15px 0;
|
785
|
+
border-bottom: 3px solid #003366;
|
786
|
+
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
787
|
+
}
|
788
|
+
.header-content {
|
789
|
+
max-width: 1200px;
|
790
|
+
margin: 0 auto;
|
791
|
+
padding: 0 20px;
|
792
|
+
display: flex;
|
793
|
+
align-items: center;
|
794
|
+
}
|
795
|
+
.logo {
|
796
|
+
font-size: 24px;
|
797
|
+
font-weight: bold;
|
798
|
+
color: #003366;
|
799
|
+
}
|
800
|
+
.security-badge {
|
801
|
+
margin-left: auto;
|
802
|
+
color: #28a745;
|
803
|
+
font-size: 12px;
|
804
|
+
display: flex;
|
805
|
+
align-items: center;
|
806
|
+
}
|
807
|
+
.security-badge::before {
|
808
|
+
content: "🔒";
|
809
|
+
margin-right: 5px;
|
810
|
+
}
|
811
|
+
.main-content {
|
812
|
+
display: flex;
|
813
|
+
justify-content: center;
|
814
|
+
align-items: center;
|
815
|
+
min-height: calc(100vh - 80px);
|
816
|
+
padding: 40px 20px;
|
817
|
+
}
|
818
|
+
.login-form {
|
819
|
+
background: white;
|
820
|
+
padding: 40px;
|
821
|
+
border-radius: 12px;
|
822
|
+
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
|
823
|
+
max-width: 400px;
|
824
|
+
width: 100%;
|
825
|
+
}
|
826
|
+
.form-title {
|
827
|
+
text-align: center;
|
828
|
+
color: #003366;
|
829
|
+
font-size: 28px;
|
830
|
+
margin-bottom: 30px;
|
831
|
+
font-weight: bold;
|
832
|
+
}
|
833
|
+
.security-notice {
|
834
|
+
background: #E8F4FD;
|
835
|
+
border: 1px solid #B8DAFF;
|
836
|
+
padding: 15px;
|
837
|
+
border-radius: 6px;
|
838
|
+
margin-bottom: 25px;
|
839
|
+
font-size: 14px;
|
840
|
+
color: #0C5460;
|
841
|
+
}
|
842
|
+
.form-group {
|
843
|
+
margin-bottom: 20px;
|
844
|
+
}
|
845
|
+
label {
|
846
|
+
display: block;
|
847
|
+
margin-bottom: 8px;
|
848
|
+
font-weight: bold;
|
849
|
+
color: #003366;
|
850
|
+
}
|
851
|
+
input[type="text"], input[type="password"] {
|
852
|
+
width: 100%;
|
853
|
+
padding: 15px;
|
854
|
+
border: 2px solid #CCC;
|
855
|
+
border-radius: 6px;
|
856
|
+
font-size: 16px;
|
857
|
+
transition: border-color 0.3s;
|
858
|
+
}
|
859
|
+
input[type="text"]:focus, input[type="password"]:focus {
|
860
|
+
outline: none;
|
861
|
+
border-color: #003366;
|
862
|
+
}
|
863
|
+
.login-button {
|
864
|
+
width: 100%;
|
865
|
+
padding: 15px;
|
866
|
+
background: #003366;
|
867
|
+
color: white;
|
868
|
+
border: none;
|
869
|
+
border-radius: 6px;
|
870
|
+
font-size: 18px;
|
871
|
+
font-weight: bold;
|
872
|
+
cursor: pointer;
|
873
|
+
transition: background-color 0.3s;
|
874
|
+
}
|
875
|
+
.login-button:hover {
|
876
|
+
background: #004499;
|
877
|
+
}
|
878
|
+
.footer-text {
|
879
|
+
text-align: center;
|
880
|
+
margin-top: 20px;
|
881
|
+
font-size: 12px;
|
882
|
+
color: #666;
|
883
|
+
}
|
884
|
+
</style>
|
885
|
+
</head>
|
886
|
+
<body>
|
887
|
+
<div class="header">
|
888
|
+
<div class="header-content">
|
889
|
+
<div class="logo">🏛️ SecureBank</div>
|
890
|
+
<div class="security-badge">256-bit SSL Encryption</div>
|
891
|
+
</div>
|
892
|
+
</div>
|
893
|
+
|
894
|
+
<div class="main-content">
|
895
|
+
<div class="login-form">
|
896
|
+
<h1 class="form-title">Secure Login</h1>
|
897
|
+
|
898
|
+
<div class="security-notice">
|
899
|
+
<strong>🔐 Security Notice:</strong> For your protection, please ensure you are on our secure website before entering your credentials.
|
900
|
+
</div>
|
901
|
+
|
902
|
+
<form method="post" action="">
|
903
|
+
<div class="form-group">
|
904
|
+
<label for="username">User ID:</label>
|
905
|
+
<input type="text" id="username" name="username" required autocomplete="username">
|
906
|
+
</div>
|
907
|
+
|
908
|
+
<div class="form-group">
|
909
|
+
<label for="password">Password:</label>
|
910
|
+
<input type="password" id="password" name="password" required autocomplete="current-password">
|
911
|
+
</div>
|
912
|
+
|
913
|
+
<button type="submit" class="login-button">Access My Account</button>
|
914
|
+
</form>
|
915
|
+
|
916
|
+
<div class="footer-text">
|
917
|
+
Your connection is secured with 256-bit encryption<br>
|
918
|
+
© 2024 SecureBank. All rights reserved.
|
919
|
+
</div>
|
920
|
+
</div>
|
921
|
+
</div>
|
922
|
+
</body>
|
923
|
+
</html>
|
924
|
+
HTML
|
925
|
+
capture_credentials: true,
|
926
|
+
capture_passwords: true,
|
927
|
+
redirect_url: "https://www.securebank.com/login-success"
|
928
|
+
)
|
929
|
+
|
930
|
+
if banking_page.save
|
931
|
+
puts "✓ Banking page created: #{banking_page.id}"
|
932
|
+
end
|
933
|
+
```
|
934
|
+
|
935
|
+
### Website Import Examples
|
936
|
+
|
937
|
+
```ruby
|
938
|
+
# Import real websites as landing pages
|
939
|
+
def import_website_examples
|
940
|
+
websites_to_import = [
|
941
|
+
{
|
942
|
+
url: "https://accounts.google.com/signin",
|
943
|
+
name: "Google Login Clone",
|
944
|
+
include_resources: true
|
945
|
+
},
|
946
|
+
{
|
947
|
+
url: "https://login.microsoftonline.com",
|
948
|
+
name: "Microsoft Azure Login Clone",
|
949
|
+
include_resources: false # Faster import, basic styling only
|
950
|
+
},
|
951
|
+
{
|
952
|
+
url: "https://www.paypal.com/signin",
|
953
|
+
name: "PayPal Login Clone",
|
954
|
+
include_resources: true
|
955
|
+
}
|
956
|
+
]
|
957
|
+
|
958
|
+
websites_to_import.each do |site_config|
|
959
|
+
puts "Importing #{site_config[:url]}"
|
960
|
+
|
961
|
+
begin
|
962
|
+
# Import the website
|
963
|
+
imported_data = Gophish::Page.import_site(
|
964
|
+
site_config[:url],
|
965
|
+
include_resources: site_config[:include_resources]
|
966
|
+
)
|
967
|
+
|
968
|
+
# Create page from imported data
|
969
|
+
page = Gophish::Page.new(imported_data)
|
970
|
+
page.name = site_config[:name]
|
971
|
+
page.capture_credentials = true
|
972
|
+
|
973
|
+
if page.save
|
974
|
+
puts " ✓ Successfully imported: #{page.name} (ID: #{page.id})"
|
975
|
+
puts " HTML size: #{page.html.length} characters"
|
976
|
+
puts " Captures credentials: #{page.captures_credentials?}"
|
977
|
+
else
|
978
|
+
puts " ✗ Failed to save: #{page.errors.full_messages.join(', ')}"
|
979
|
+
end
|
980
|
+
|
981
|
+
rescue StandardError => e
|
982
|
+
puts " ✗ Import failed: #{e.message}"
|
983
|
+
|
984
|
+
# Create fallback manual page
|
985
|
+
fallback_page = create_fallback_page(site_config[:name], site_config[:url])
|
986
|
+
if fallback_page
|
987
|
+
puts " ✓ Created fallback page: #{fallback_page.id}"
|
988
|
+
end
|
989
|
+
end
|
990
|
+
|
991
|
+
puts
|
992
|
+
end
|
993
|
+
end
|
994
|
+
|
995
|
+
def create_fallback_page(name, original_url)
|
996
|
+
# Extract domain name for styling
|
997
|
+
domain = URI.parse(original_url).host.gsub('www.', '')
|
998
|
+
|
999
|
+
fallback_page = Gophish::Page.new(
|
1000
|
+
name: "#{name} (Manual Fallback)",
|
1001
|
+
html: <<~HTML,
|
1002
|
+
<html>
|
1003
|
+
<head>
|
1004
|
+
<title>#{domain.capitalize}</title>
|
1005
|
+
<style>
|
1006
|
+
body { font-family: Arial, sans-serif; max-width: 400px; margin: 100px auto; padding: 40px; }
|
1007
|
+
.logo { font-size: 24px; margin-bottom: 30px; text-align: center; color: #333; }
|
1008
|
+
input { width: 100%; padding: 12px; margin: 10px 0; border: 1px solid #ddd; border-radius: 4px; }
|
1009
|
+
button { width: 100%; padding: 12px; background: #1a73e8; color: white; border: none; border-radius: 4px; cursor: pointer; }
|
1010
|
+
button:hover { background: #1557b0; }
|
1011
|
+
</style>
|
1012
|
+
</head>
|
1013
|
+
<body>
|
1014
|
+
<div class="logo">#{domain.capitalize}</div>
|
1015
|
+
<form method="post">
|
1016
|
+
<input type="email" name="username" placeholder="Email" required>
|
1017
|
+
<input type="password" name="password" placeholder="Password" required>
|
1018
|
+
<button type="submit">Sign in</button>
|
1019
|
+
</form>
|
1020
|
+
</body>
|
1021
|
+
</html>
|
1022
|
+
HTML
|
1023
|
+
capture_credentials: true
|
1024
|
+
)
|
1025
|
+
|
1026
|
+
fallback_page.save ? fallback_page : nil
|
1027
|
+
end
|
1028
|
+
|
1029
|
+
# Run the import
|
1030
|
+
import_website_examples
|
1031
|
+
```
|
1032
|
+
|
1033
|
+
### Page Management and Updates
|
1034
|
+
|
1035
|
+
```ruby
|
1036
|
+
# Comprehensive page management
|
1037
|
+
class PageManager
|
1038
|
+
def self.list_all_pages
|
1039
|
+
pages = Gophish::Page.all
|
1040
|
+
puts "Found #{pages.length} landing pages:"
|
1041
|
+
|
1042
|
+
pages.each do |page|
|
1043
|
+
features = []
|
1044
|
+
features << "🔑 Captures Credentials" if page.captures_credentials?
|
1045
|
+
features << "🔒 Captures Passwords" if page.captures_passwords?
|
1046
|
+
features << "🔄 Has Redirect" if page.has_redirect?
|
1047
|
+
|
1048
|
+
feature_text = features.any? ? " [#{features.join(', ')}]" : ""
|
1049
|
+
puts " #{page.id}: #{page.name}#{feature_text}"
|
1050
|
+
|
1051
|
+
if page.has_redirect?
|
1052
|
+
puts " → Redirects to: #{page.redirect_url}"
|
1053
|
+
end
|
1054
|
+
|
1055
|
+
puts " HTML size: #{page.html.length} characters"
|
1056
|
+
puts
|
1057
|
+
end
|
1058
|
+
end
|
1059
|
+
|
1060
|
+
def self.update_page_security(page_id, enable_credential_capture: false, redirect_to: nil)
|
1061
|
+
begin
|
1062
|
+
page = Gophish::Page.find(page_id)
|
1063
|
+
rescue StandardError
|
1064
|
+
puts "✗ Page #{page_id} not found"
|
1065
|
+
return false
|
1066
|
+
end
|
1067
|
+
|
1068
|
+
puts "Updating security settings for '#{page.name}'"
|
1069
|
+
|
1070
|
+
# Update credential capture settings
|
1071
|
+
if enable_credential_capture
|
1072
|
+
page.capture_credentials = true
|
1073
|
+
page.capture_passwords = true
|
1074
|
+
puts " ✓ Enabled credential capture"
|
1075
|
+
else
|
1076
|
+
page.capture_credentials = false
|
1077
|
+
page.capture_passwords = false
|
1078
|
+
puts " ✓ Disabled credential capture"
|
1079
|
+
end
|
1080
|
+
|
1081
|
+
# Update redirect URL
|
1082
|
+
if redirect_to
|
1083
|
+
page.redirect_url = redirect_to
|
1084
|
+
puts " ✓ Set redirect URL: #{redirect_to}"
|
1085
|
+
end
|
1086
|
+
|
1087
|
+
if page.save
|
1088
|
+
puts " ✓ Page updated successfully"
|
1089
|
+
true
|
1090
|
+
else
|
1091
|
+
puts " ✗ Update failed: #{page.errors.full_messages.join(', ')}"
|
1092
|
+
false
|
1093
|
+
end
|
1094
|
+
end
|
1095
|
+
|
1096
|
+
def self.clone_page(original_id, new_name, modifications = {})
|
1097
|
+
begin
|
1098
|
+
original = Gophish::Page.find(original_id)
|
1099
|
+
rescue StandardError
|
1100
|
+
puts "✗ Original page #{original_id} not found"
|
1101
|
+
return nil
|
1102
|
+
end
|
1103
|
+
|
1104
|
+
# Create clone
|
1105
|
+
cloned_page = Gophish::Page.new(
|
1106
|
+
name: new_name,
|
1107
|
+
html: original.html,
|
1108
|
+
capture_credentials: original.capture_credentials,
|
1109
|
+
capture_passwords: original.capture_passwords,
|
1110
|
+
redirect_url: original.redirect_url
|
1111
|
+
)
|
1112
|
+
|
1113
|
+
# Apply modifications
|
1114
|
+
modifications.each do |field, value|
|
1115
|
+
cloned_page.send("#{field}=", value) if cloned_page.respond_to?("#{field}=")
|
1116
|
+
end
|
1117
|
+
|
1118
|
+
if cloned_page.save
|
1119
|
+
puts "✓ Page cloned successfully: '#{new_name}' (ID: #{cloned_page.id})"
|
1120
|
+
cloned_page
|
1121
|
+
else
|
1122
|
+
puts "✗ Clone failed: #{cloned_page.errors.full_messages.join(', ')}"
|
1123
|
+
nil
|
1124
|
+
end
|
1125
|
+
end
|
1126
|
+
end
|
1127
|
+
|
1128
|
+
# Usage examples
|
1129
|
+
PageManager.list_all_pages
|
1130
|
+
|
1131
|
+
# Enable security features on an existing page
|
1132
|
+
PageManager.update_page_security(
|
1133
|
+
1,
|
1134
|
+
enable_credential_capture: true,
|
1135
|
+
redirect_to: "https://legitimate-site.com"
|
1136
|
+
)
|
1137
|
+
|
1138
|
+
# Clone a page with modifications
|
1139
|
+
PageManager.clone_page(
|
1140
|
+
1,
|
1141
|
+
"Modified Banking Page",
|
1142
|
+
{
|
1143
|
+
capture_passwords: false,
|
1144
|
+
redirect_url: "https://different-redirect.com"
|
1145
|
+
}
|
1146
|
+
)
|
1147
|
+
```
|
1148
|
+
|
1149
|
+
### A/B Testing with Multiple Page Variants
|
1150
|
+
|
1151
|
+
```ruby
|
1152
|
+
# Create multiple variants of the same phishing page for testing
|
1153
|
+
def create_page_variants(base_name, base_html, variants)
|
1154
|
+
created_pages = []
|
1155
|
+
|
1156
|
+
variants.each_with_index do |variant, index|
|
1157
|
+
variant_name = "#{base_name} - #{variant[:name]}"
|
1158
|
+
|
1159
|
+
# Start with base HTML
|
1160
|
+
modified_html = base_html.dup
|
1161
|
+
|
1162
|
+
# Apply modifications
|
1163
|
+
variant[:modifications].each do |search, replace|
|
1164
|
+
modified_html.gsub!(search, replace)
|
1165
|
+
end
|
1166
|
+
|
1167
|
+
page = Gophish::Page.new(
|
1168
|
+
name: variant_name,
|
1169
|
+
html: modified_html,
|
1170
|
+
capture_credentials: variant[:capture_credentials] || true,
|
1171
|
+
capture_passwords: variant[:capture_passwords] || true,
|
1172
|
+
redirect_url: variant[:redirect_url]
|
1173
|
+
)
|
1174
|
+
|
1175
|
+
if page.save
|
1176
|
+
puts "✓ Created variant #{index + 1}: #{variant_name} (ID: #{page.id})"
|
1177
|
+
created_pages << page
|
1178
|
+
else
|
1179
|
+
puts "✗ Failed to create variant #{index + 1}: #{page.errors.full_messages.join(', ')}"
|
1180
|
+
end
|
1181
|
+
end
|
1182
|
+
|
1183
|
+
created_pages
|
1184
|
+
end
|
1185
|
+
|
1186
|
+
# Example: Create different urgency levels for the same login page
|
1187
|
+
base_html = <<~HTML
|
1188
|
+
<html>
|
1189
|
+
<head><title>Account Security</title></head>
|
1190
|
+
<body>
|
1191
|
+
<h1>URGENCY_LEVEL</h1>
|
1192
|
+
<p>MESSAGE_TEXT</p>
|
1193
|
+
<form method="post">
|
1194
|
+
<input type="email" name="username" placeholder="Email" required>
|
1195
|
+
<input type="password" name="password" placeholder="Password" required>
|
1196
|
+
<button type="submit" style="background: BUTTON_COLOR;">BUTTON_TEXT</button>
|
1197
|
+
</form>
|
1198
|
+
</body>
|
1199
|
+
</html>
|
1200
|
+
HTML
|
1201
|
+
|
1202
|
+
variants = [
|
1203
|
+
{
|
1204
|
+
name: "Low Urgency",
|
1205
|
+
modifications: {
|
1206
|
+
"URGENCY_LEVEL" => "Account Update Available",
|
1207
|
+
"MESSAGE_TEXT" => "Please update your account information at your convenience.",
|
1208
|
+
"BUTTON_COLOR" => "#0078d4",
|
1209
|
+
"BUTTON_TEXT" => "Update Account"
|
1210
|
+
},
|
1211
|
+
redirect_url: "https://microsoft.com"
|
1212
|
+
},
|
1213
|
+
{
|
1214
|
+
name: "Medium Urgency",
|
1215
|
+
modifications: {
|
1216
|
+
"URGENCY_LEVEL" => "Security Alert",
|
1217
|
+
"MESSAGE_TEXT" => "We detected unusual activity. Please verify your account.",
|
1218
|
+
"BUTTON_COLOR" => "#ff8c00",
|
1219
|
+
"BUTTON_TEXT" => "Verify Now"
|
1220
|
+
},
|
1221
|
+
redirect_url: "https://microsoft.com/security"
|
1222
|
+
},
|
1223
|
+
{
|
1224
|
+
name: "High Urgency",
|
1225
|
+
modifications: {
|
1226
|
+
"URGENCY_LEVEL" => "IMMEDIATE ACTION REQUIRED",
|
1227
|
+
"MESSAGE_TEXT" => "Your account will be suspended in 24 hours! Login immediately.",
|
1228
|
+
"BUTTON_COLOR" => "#dc3545",
|
1229
|
+
"BUTTON_TEXT" => "SAVE MY ACCOUNT"
|
1230
|
+
},
|
1231
|
+
redirect_url: "https://microsoft.com/urgent"
|
1232
|
+
}
|
1233
|
+
]
|
1234
|
+
|
1235
|
+
pages = create_page_variants("Microsoft Security Alert", base_html, variants)
|
1236
|
+
puts "\nCreated #{pages.length} page variants for A/B testing"
|
1237
|
+
```
|
1238
|
+
|
255
1239
|
## Error Handling
|
256
1240
|
|
257
1241
|
### Comprehensive Error Handling
|