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.
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