├── .bowerrc ├── .env.example ├── .env.travis ├── .gitattributes ├── .gitignore ├── .styleci.yml ├── .travis.yml ├── LICENSE ├── README.md ├── VERSION ├── app ├── ActivityLog.php ├── Campaign.php ├── Console │ ├── Commands │ │ ├── CatchMissed.php │ │ ├── CheckMail.php │ │ ├── CreateUser.php │ │ ├── Disable2FA.php │ │ └── LogPull.php │ └── Kernel.php ├── Email.php ├── EmailTemplate.php ├── Exceptions │ └── Handler.php ├── Http │ ├── Controllers │ │ ├── AjaxController.php │ │ ├── Auth │ │ │ ├── ForgotPasswordController.php │ │ │ ├── LoginController.php │ │ │ ├── RegisterController.php │ │ │ └── ResetPasswordController.php │ │ ├── CampaignController.php │ │ ├── Controller.php │ │ ├── DashboardController.php │ │ ├── EmailController.php │ │ ├── Google2FAController.php │ │ ├── LogController.php │ │ ├── SettingsController.php │ │ └── TargetsController.php │ ├── Kernel.php │ ├── Middleware │ │ ├── EncryptCookies.php │ │ ├── RedirectIfAuthenticated.php │ │ ├── TrimStrings.php │ │ └── VerifyCsrfToken.php │ └── Requests │ │ └── ValidateSecretRequest.php ├── Jobs │ ├── AddToList.php │ ├── ImportTargets.php │ ├── Job.php │ ├── SendEmail.php │ └── StartCampaign.php ├── Libraries │ ├── CacheHelper.php │ ├── DateHelper.php │ └── DomainTools.php ├── LogAggregate.php ├── Providers │ ├── AppServiceProvider.php │ ├── AuthServiceProvider.php │ ├── BroadcastServiceProvider.php │ ├── EventServiceProvider.php │ └── RouteServiceProvider.php ├── ReceivedMail.php ├── ReceivedMailAttachment.php ├── TargetList.php ├── TargetUser.php └── User.php ├── artisan ├── bootstrap ├── app.php ├── autoload.php └── cache │ └── .gitignore ├── bower.json ├── composer.json ├── composer.lock ├── config ├── app.php ├── auth.php ├── broadcasting.php ├── cache.php ├── database.php ├── datatables.php ├── fiercephish.php ├── filesystems.php ├── mail.php ├── queue.php ├── services.php ├── session.php └── view.php ├── database ├── .gitignore ├── factories │ └── ModelFactory.php ├── migrations │ ├── .gitkeep │ ├── 2014_10_12_000000_create_users_table.php │ ├── 2014_10_12_100000_create_password_resets_table.php │ ├── 2017_01_20_111748_create_target_users_table.php │ ├── 2017_01_20_111930_create_target_lists_table.php │ ├── 2017_01_20_111959_create_targetlists_targetusers_table.php │ ├── 2017_01_20_112029_create_email_templates_table.php │ ├── 2017_01_20_112053_create_campaigns_table.php │ ├── 2017_01_20_112740_create_emails_table.php │ ├── 2017_01_20_112808_create_activity_logs_table.php │ ├── 2017_01_20_113538_create_received_mails_table.php │ ├── 2017_01_20_113647_create_received_mail_attachments_table.php │ ├── 2017_01_20_113715_create_log_aggregates_table.php │ ├── 2017_01_20_125015_create_jobs_table.php │ └── 2017_01_20_125024_create_failed_jobs_table.php └── seeds │ ├── .gitkeep │ └── DatabaseSeeder.php ├── install.sh ├── package.json ├── php.ini ├── phpunit.xml ├── public ├── .htaccess ├── css │ ├── fiercephish.css │ └── login_page.css ├── favicon.ico ├── images │ ├── ajax-loader.gif │ ├── fiercephish_logo.png │ ├── loading.gif │ ├── login_logo.png │ ├── mail_attachment.png │ └── outlook_person.png ├── index.php ├── js │ ├── custom_ckeditor.js │ └── ta.js ├── robots.txt └── web.config ├── resources ├── lang │ └── en │ │ ├── auth.php │ │ ├── pagination.php │ │ ├── passwords.php │ │ └── validation.php └── views │ ├── auth │ ├── 2fa.blade.php │ └── login.blade.php │ ├── campaigns │ ├── create.blade.php │ ├── details.blade.php │ └── index.blade.php │ ├── common │ ├── errors.blade.php │ ├── notifications.blade.php │ ├── successes.blade.php │ └── warnings.blade.php │ ├── dashboard │ └── index.blade.php │ ├── emails │ ├── check_settings.blade.php │ ├── email_log.blade.php │ ├── email_log_details.blade.php │ ├── inbox.blade.php │ ├── send_simple.blade.php │ └── template_index.blade.php │ ├── errors │ ├── 404.blade.php │ └── 503.blade.php │ ├── layouts │ ├── app.blade.php │ ├── email_html.blade.php │ ├── email_plaintext.blade.php │ └── login.blade.php │ ├── logs │ └── index.blade.php │ ├── settings │ ├── configs │ │ ├── config.blade.php │ │ └── import_export.blade.php │ └── usermanagement │ │ ├── editprofile.blade.php │ │ └── index.blade.php │ └── targets │ ├── assign.blade.php │ ├── index.blade.php │ ├── list_details.blade.php │ └── lists.blade.php ├── routes ├── api.php ├── channels.php ├── console.php └── web.php ├── server.php ├── storage ├── app │ ├── .gitignore │ └── public │ │ └── .gitignore ├── framework │ ├── .gitignore │ ├── cache │ │ └── .gitignore │ ├── sessions │ │ └── .gitignore │ └── views │ │ └── .gitignore └── logs │ └── .gitignore ├── tests ├── CreatesApplication.php ├── Feature │ └── ExampleTest.php ├── TestCase.php └── Unit │ └── ExampleTest.php ├── update.sh └── webpack.mix.js /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "public/vendor" 3 | } 4 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | ### If you edit this file, it is imperative that you run: 2 | ### php artisan config:cache 3 | 4 | 5 | ############################ 6 | ####### App Settings ####### 7 | ############################ 8 | 9 | ## Application environment (default: master) 10 | APP_ENV=master 11 | 12 | ## Debugging mode (default: false) 13 | APP_DEBUG=false 14 | 15 | ## Application Logging level 16 | APP_LOG_LEVEL=debug 17 | 18 | ## Application Timezone (default: America/Chicago) 19 | APP_TIMEZONE=America/Chicago 20 | 21 | ## Application encryption key 22 | ## Generate this with "php artisan key:generate" 23 | ## Then run: "php artisan config:cache" 24 | APP_KEY=SomeRandomString 25 | 26 | ## Application URL (default: http://localhost) 27 | APP_URL=http://localhost 28 | 29 | ## Application Name, no need to change this (default: FiercePhish) 30 | APP_NAME=FiercePhish 31 | 32 | #------------------------------------------------------------------------------- 33 | 34 | ############################## 35 | ####### Proxy Settings ####### 36 | ############################## 37 | 38 | ## Root URL prefix for the proxy. (default: null) 39 | ## This is useful if FiercePhish is behind a proxy or loadbalancer 40 | ## Example: https://firephish.example.com/ 41 | PROXY_URL=null 42 | 43 | ## Schema to use for the proxy. (default null) 44 | ## This is useful if FiercePhish is behind a HTTPS proxy or loadbalancer 45 | ## Options: null https http 46 | PROXY_SCHEMA=null 47 | 48 | #------------------------------------------------------------------------------- 49 | 50 | ################################# 51 | ####### Database Settings ####### 52 | ################################# 53 | 54 | ## Database type (default: mysql) 55 | DB_CONNECTION=mysql 56 | 57 | ## Database host (default: 127.0.0.1) 58 | DB_HOST=127.0.0.1 59 | 60 | ## Database port (default: 3306) 61 | DB_PORT=3306 62 | 63 | ## Database username (default: root) 64 | DB_USERNAME=root 65 | 66 | ## Database password 67 | DB_PASSWORD=secret 68 | 69 | ## Database name (default: fiercephish) 70 | DB_DATABASE=fiercephish 71 | 72 | #------------------------------------------------------------------------------- 73 | 74 | ############################## 75 | ###### Service settings ###### 76 | ############################## 77 | 78 | ## Cache driver (this shouldn't really change) (default: file) 79 | CACHE_DRIVER=file 80 | 81 | ## Session driver (this shouldn't really change) (default: file) 82 | SESSION_DRIVER=file 83 | 84 | ## Broadcast driver (this shouldn't really change) (default: log) 85 | BROADCAST_DRIVER=log 86 | 87 | ## Queue drvier (this shouldn't really change) (default: database) 88 | QUEUE_DRIVER=database 89 | 90 | ## Redis hostname (this is unused currently) (default: 127.0.0.1) 91 | REDIS_HOST=127.0.0.1 92 | 93 | ## Redis password (this is unused currently) (default: null) 94 | REDIS_PASSWORD=null 95 | 96 | ## Redis port (this is unused currently) (default: 6379) 97 | REDIS_PORT=6379 98 | 99 | ## Pusher App ID (this is unused currently) (default: null) 100 | PUSHER_APP_ID=null 101 | 102 | ## Pusher key (this is unused currently) (default: null) 103 | PUSHER_APP_KEY=null 104 | 105 | ## Pusher secret (this is unused currently) (default: null) 106 | PUSHER_APP_SECRET=null 107 | 108 | #------------------------------------------------------------------------------- 109 | 110 | ############################# 111 | ####### Mail settings ####### 112 | ############################# 113 | 114 | ## Mail driver to use (default: smtp) 115 | ## Options: stmp mailgun 116 | MAIL_DRIVER=smtp 117 | 118 | ## SMTP hostname (default: 127.0.0.1) 119 | MAIL_HOST=127.0.0.1 120 | 121 | ## SMTP port (default: 25) 122 | MAIL_PORT=25 123 | 124 | ## SMTP username (default: null) 125 | MAIL_USERNAME=null 126 | 127 | ## SMTP password (default: null) 128 | MAIL_PASSWORD=null 129 | 130 | ## SMTP connection encryption (default: null) 131 | ## Options: null tls 132 | MAIL_ENCRYPTION=null 133 | 134 | ## Mailgun domain (default: null) 135 | MAILGUN_DOMAIN=null 136 | 137 | ## Mailgun secret key (default: null) 138 | MAILGUN_SECRET=null 139 | 140 | #------------------------------------------------------------------------------- 141 | 142 | #################################### 143 | ####### FiercePhish Settings ####### 144 | #################################### 145 | 146 | ## Prefix for FiercePhish instance (default: null) 147 | ## Example: if this is "test/this", access FiercePhish by browsing 148 | ## to http://IP/test/this 149 | URI_PREFIX=null 150 | 151 | ## Test mode for email sending (default: false) 152 | ## If this is "true", emails will not actually be sent 153 | TEST_EMAIL_JOB=false 154 | 155 | ## Imap host for inbox feature (default: null) 156 | IMAP_HOST=null 157 | 158 | ## Imap port for inbox feature (default: 143) 159 | IMAP_PORT=143 160 | 161 | ## Imap username for inbox feature (default: fiercephish) 162 | ## Currently unused 163 | IMAP_USERNAME=fiercephish 164 | 165 | ## Imap password for inbox feature (default: null) 166 | ## Currently unused 167 | IMAP_PASSWORD=null 168 | 169 | ## Email to BCC on all emails sent out (default: null) 170 | ## This is helpful if you want to see all emails actually 171 | ## being sent and want a log of it. 172 | MAIL_BCC_ALL=null 173 | 174 | #------------------------------------------------------------------------------- 175 | 176 | ################################# 177 | ####### Advanced Settings ####### 178 | ################################# 179 | 180 | ## This enables Analytics to see how much FiercePhish is actually being used. By 181 | ## being able to see how much this project is being used, I can dedicate time to 182 | ## specific features and it encourages development. All information is anonymized 183 | ## and no sensitive data is ever sent. However, if you would like to disable this, 184 | ## change below to "false". It is much appreciated to keep it enabled, however. 185 | ## (default: true) 186 | ANALYTICS=true 187 | -------------------------------------------------------------------------------- /.env.travis: -------------------------------------------------------------------------------- 1 | ### If you edit this file, it is imperative that you run: 2 | ### php artisan config:cache 3 | 4 | 5 | ############################ 6 | ####### App Settings ####### 7 | ############################ 8 | 9 | ## Application environment (default: master) 10 | APP_ENV=staging 11 | 12 | ## Debugging mode (default: false) 13 | APP_DEBUG=false 14 | 15 | ## Application Logging level 16 | APP_LOG_LEVEL=debug 17 | 18 | ## Application Timezone (default: America/Chicago) 19 | APP_TIMEZONE=America/Chicago 20 | 21 | ## Application encryption key 22 | ## Generate this with "php artisan key:generate" 23 | ## Then run: "php artisan config:cache" 24 | APP_KEY=SomeRandomString 25 | 26 | ## Application URL (default: http://localhost) 27 | APP_URL=http://localhost 28 | 29 | ## Application Name, no need to change this (default: FiercePhish) 30 | APP_NAME=FiercePhish 31 | 32 | #------------------------------------------------------------------------------- 33 | 34 | ############################## 35 | ####### Proxy Settings ####### 36 | ############################## 37 | 38 | ## Root URL prefix for the proxy. (default: null) 39 | ## This is useful if FiercePhish is behind a proxy or loadbalancer 40 | ## Example: https://firephish.example.com/ 41 | PROXY_URL=null 42 | 43 | ## Schema to use for the proxy. (default null) 44 | ## This is useful if FiercePhish is behind a HTTPS proxy or loadbalancer 45 | ## Options: null https http 46 | PROXY_SCHEMA=null 47 | 48 | #------------------------------------------------------------------------------- 49 | 50 | ################################# 51 | ####### Database Settings ####### 52 | ################################# 53 | 54 | ## Database type (default: mysql) 55 | DB_CONNECTION=mysql 56 | 57 | ## Database host (default: 127.0.0.1) 58 | DB_HOST=127.0.0.1 59 | 60 | ## Database port (default: 3306) 61 | DB_PORT=3306 62 | 63 | ## Database username (default: root) 64 | DB_USERNAME=travis 65 | 66 | ## Database password 67 | DB_PASSWORD= 68 | 69 | ## Database name (default: fiercephish) 70 | DB_DATABASE=fiercephish 71 | 72 | #------------------------------------------------------------------------------- 73 | 74 | ############################## 75 | ###### Service settings ###### 76 | ############################## 77 | 78 | ## Cache driver (this shouldn't really change) (default: file) 79 | CACHE_DRIVER=file 80 | 81 | ## Session driver (this shouldn't really change) (default: file) 82 | SESSION_DRIVER=file 83 | 84 | ## Broadcast driver (this shouldn't really change) (default: log) 85 | BROADCAST_DRIVER=log 86 | 87 | ## Queue drvier (this shouldn't really change) (default: database) 88 | QUEUE_DRIVER=sync 89 | 90 | ## Redis hostname (this is unused currently) (default: 127.0.0.1) 91 | REDIS_HOST=127.0.0.1 92 | 93 | ## Redis password (this is unused currently) (default: null) 94 | REDIS_PASSWORD=null 95 | 96 | ## Redis port (this is unused currently) (default: 6379) 97 | REDIS_PORT=6379 98 | 99 | ## Pusher App ID (this is unused currently) (default: null) 100 | PUSHER_APP_ID=null 101 | 102 | ## Pusher key (this is unused currently) (default: null) 103 | PUSHER_APP_KEY=null 104 | 105 | ## Pusher secret (this is unused currently) (default: null) 106 | PUSHER_APP_SECRET=null 107 | 108 | #------------------------------------------------------------------------------- 109 | 110 | ############################# 111 | ####### Mail settings ####### 112 | ############################# 113 | 114 | ## Mail driver to use (default: smtp) 115 | ## Options: stmp mailgun 116 | MAIL_DRIVER=smtp 117 | 118 | ## SMTP hostname (default: 127.0.0.1) 119 | MAIL_HOST=127.0.0.1 120 | 121 | ## SMTP port (default: 25) 122 | MAIL_PORT=25 123 | 124 | ## SMTP username (default: null) 125 | MAIL_USERNAME=null 126 | 127 | ## SMTP password (default: null) 128 | MAIL_PASSWORD=null 129 | 130 | ## SMTP connection encryption (default: null) 131 | ## Options: null tls 132 | MAIL_ENCRYPTION=null 133 | 134 | ## Mailgun domain (default: null) 135 | MAILGUN_DOMAIN=null 136 | 137 | ## Mailgun secret key (default: null) 138 | MAILGUN_SECRET=null 139 | 140 | #------------------------------------------------------------------------------- 141 | 142 | #################################### 143 | ####### FiercePhish Settings ####### 144 | #################################### 145 | 146 | ## Prefix for FiercePhish instance (default: null) 147 | ## Example: if this is "test/this", access FiercePhish by browsing 148 | ## to http://IP/test/this 149 | URI_PREFIX=null 150 | 151 | ## Test mode for email sending (default: false) 152 | ## If this is "true", emails will not actually be sent 153 | TEST_EMAIL_JOB=true 154 | 155 | ## Imap host for inbox feature (default: null) 156 | IMAP_HOST=null 157 | 158 | ## Imap port for inbox feature (default: 143) 159 | IMAP_PORT=143 160 | 161 | ## Imap username for inbox feature (default: fiercephish) 162 | ## Currently unused 163 | IMAP_USERNAME=fiercephish 164 | 165 | ## Imap password for inbox feature (default: null) 166 | ## Currently unused 167 | IMAP_PASSWORD=null 168 | 169 | ## Email to BCC on all emails sent out (default: null) 170 | ## This is helpful if you want to see all emails actually 171 | ## being sent and want a log of it. 172 | MAIL_BCC_ALL=null 173 | 174 | #------------------------------------------------------------------------------- 175 | 176 | ################################# 177 | ####### Advanced Settings ####### 178 | ################################# 179 | 180 | ## This enables Analytics to see how much FiercePhish is actually being used. By 181 | ## being able to see how much this project is being used, I can dedicate time to 182 | ## specific features and it encourages development. All information is anonymized 183 | ## and no sensitive data is ever sent. However, if you would like to disable this, 184 | ## change below to "false". It is much appreciated to keep it enabled, however. 185 | ## (default: true) 186 | ANALYTICS=false 187 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.css linguist-vendored 3 | *.scss linguist-vendored 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /public/storage 3 | /public/hot 4 | /public/vendor 5 | /storage/*.key 6 | /vendor 7 | /.idea 8 | Homestead.json 9 | Homestead.yaml 10 | .env 11 | php_errors.log 12 | php.ini 13 | /.vagrant 14 | Vagrantfile -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: laravel 2 | 3 | risky: false 4 | 5 | enabled: 6 | - no_empty_comment 7 | 8 | 9 | finder: 10 | exclude: 11 | - "tests" 12 | name: 13 | - "*.php" -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | services: 4 | - mysql 5 | 6 | php: 7 | - 5.6.4 8 | - 7.0 9 | 10 | before_script: 11 | - cp .env.travis .env 12 | - travis_retry composer self-update 13 | - travis_retry composer install --no-interaction 14 | - travis_retry npm install -g bower 15 | - travis_retry bower install --allow-root 16 | - mysql -e 'create database fiercephish;' 17 | - php artisan key:generate 18 | - php artisan migrate 19 | 20 | script: 21 | - vendor/bin/phpunit -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![FiercePhish](http://i.imgur.com/5WyejWU.png) 2 | 3 | # FiercePhish 4 | 5 | 6 | FiercePhish is a full-fledged phishing framework to manage all phishing engagements. It allows you to track separate phishing campaigns, schedule sending of emails, and much more. The features will continue to be expanded and will include website spoofing, click tracking, and extensive notification options. 7 | 8 | **Note: As of 1/6/2017, FirePhish has been renamed FiercePhish. Screenshots may still show FirePhish logo** 9 | 10 | # All Information is on the Wiki Pages 11 | 12 | [ChangeLog](https://github.com/Raikia/FiercePhish/wiki/Changelog) 13 | 14 | [Click here to go to the Wiki Pages](https://github.com/Raikia/FiercePhish/wiki) 15 | 16 | # Disclaimer 17 | 18 | This project is my own and is not a representation of my employer's views. It is my own side project and released by me alone. 19 | 20 | # Screenshot 21 | 22 | ![Screenshot](http://i.imgur.com/v852BbM.png) 23 | 24 | More screenshots are available in the ["Features" wiki pages](https://github.com/Raikia/FiercePhish/wiki/Features-Overview) 25 | 26 | # Quick Automated Install 27 | 28 | For more information (like a manual installation method), see the [wiki pages](https://github.com/Raikia/FiercePhish/wiki) 29 | 30 | This is the preferred method of installing FiercePhish + SMTP + IMAP services. 31 | 32 | ### Supported Operating Systems 33 | * Ubuntu 16.04 34 | * Ubuntu 16.10 35 | 36 | (Fresh installs are expected, but the installer should work on a used OS with no problems) 37 | 38 | (Ubuntu 14.04 support has been removed. To install FiercePhish on 14.04, [read these instructions](https://github.com/Raikia/FiercePhish/wiki/Ubuntu-14.04-Installation-Guide)) 39 | 40 | _If you would like a different OS distribution supported, create a [Github issue](https://github.com/Raikia/FiercePhish/issues)_ 41 | 42 | 43 | ### Recommended Prerequisites 44 | * Purchase a domain name to send emails from 45 | 46 | This isn't required, but it is heavily suggested. Phishing campaigns where you spoof an active domain you don't own are extremely susceptible to being spam filtered (unless the domain's SPF record is improperly configured). The best way to perform a phishing campaign is by buying a generic domain that can fool someone ("yourfilehost.com") or a domain that is very similar to a real domain ("microsoft-secure.com"). 47 | 48 | ### Installation Method #1 (remote curl download) 49 | 50 | This method is probably the easiest way to install/configure everything. It is a fully unattended installation (aside from the beginning). 51 | 52 | 1. You must run the installer as root: 53 | 54 | ```sudo su``` 55 | 56 | 2. Generate the configuration file: 57 | 58 | ```curl https://raw.githubusercontent.com/Raikia/FiercePhish/master/install.sh | bash``` 59 | 60 | 3. This will create a configuration file located at "~/fiercephish.config". You must edit this file before moving on! 61 | 62 | [Click here for a detailed description of the configuration variables](https://github.com/Raikia/FiercePhish/wiki/Installation-Configuration-File) 63 | 64 | 4. Once "CONFIGURED=true" is set in the configuration file, re-run the install script: 65 | 66 | ```curl https://raw.githubusercontent.com/Raikia/FiercePhish/master/install.sh | bash``` 67 | 68 | 5. Sit and wait. The installation could take anywhere from 5-15 minutes depending on your server's download speed. 69 | 70 | 6. Once the installation completes, follow the instructions it prints out. It will tell you what [DNS entries](https://github.com/Raikia/FiercePhish/wiki/DNS-Configurations) to set. 71 | 72 | 73 | ### Installation Method #2 (local installation run) 74 | 75 | This method is just as easy as method #1, but the install will prompt you as it runs for the information it requires (as opposed to using a configuration file like method #1). 76 | 77 | 1. You must run the installer as root: 78 | 79 | ```sudo su``` 80 | 81 | 2. Download the configuration file: 82 | 83 | ```wget https://raw.githubusercontent.com/Raikia/FiercePhish/master/install.sh``` 84 | 85 | 3. Set the installer as executable: 86 | 87 | ```chmod +x install.sh``` 88 | 89 | 4. Run the installer: 90 | 91 | ``` ./install.sh ``` 92 | 93 | The installer will prompt you for the same information as is described in [the configuration file for method #1](https://github.com/Raikia/FiercePhish/wiki/Installation-Configuration-File). See that wiki page for information on what to provide. 94 | 95 | 5. Sit and wait. The installation could take anywhere from 5-15 minutes depending on your server's download speed. 96 | 97 | 6. Once the installation completes, follow the instructions it prints out. It will tell you what [DNS entries](https://github.com/Raikia/FiercePhish/wiki/DNS-Configurations) to set. 98 | 99 | 100 | ### Updating 101 | 102 | As of FiercePhish v1.2.0, an update script is included. Versions prior to 1.2.0 are **not** compatible with 1.2.0 and later, so you'll have to do a fresh install (or read the wiki). 103 | 104 | To update FiercePhish, simply run: 105 | ``` 106 | sudo ./update.sh 107 | ``` 108 | ### Troubleshooting 109 | 110 | If you have errors with the installation script, you can safely rerun the script without messing anything up (even if you provide it different information). If you continue to have problems, set "VERBOSE=true" (for method #1) or run ```./install.sh -v``` (for method #2) to see the full log of everything running. If you still have problems, [submit a bug report](https://github.com/Raikia/FiercePhish/wiki/Reporting-Bugs). -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 1.2.4 -------------------------------------------------------------------------------- /app/ActivityLog.php: -------------------------------------------------------------------------------- 1 | log = $msg; 18 | $a->type = $type; 19 | $a->is_error = $error; 20 | if (Auth::check()) 21 | $a->user = Auth::user()->name; 22 | else 23 | $a->user = null; 24 | $a->save(); 25 | return $a; 26 | } 27 | 28 | public static function fetch() 29 | { 30 | return ActivityLog::orderby('id', 'desc')->get(); 31 | } 32 | 33 | public function set_ref_id($id) 34 | { 35 | $this->ref_id = $id; 36 | $this->save(); 37 | return $this; 38 | } 39 | 40 | public function set_ref_text($text) 41 | { 42 | $this->ref_text = $text; 43 | $this->save(); 44 | return $this; 45 | } 46 | 47 | public function set_error($bool) 48 | { 49 | $this->is_error = $bool; 50 | $this->save(); 51 | return $this; 52 | } 53 | 54 | public function read() 55 | { 56 | $ret_text = ''; 57 | $ret_text = '['.\App\Libraries\DateHelper::format($this->created_at, 'm/d/Y - H:i:s').'] '; 58 | if ($this->is_error) 59 | $ret_text .= '!!!! ERROR !!!! - '; 60 | $ret_text .= '{'.$this->type.'} '; 61 | $ret_text .= $this->log; 62 | $username = ''; 63 | if ($this->user != null) 64 | { 65 | $username = ' ('.$this->user.')'; 66 | } 67 | $ret_text .= $username; 68 | return $ret_text; 69 | } 70 | 71 | public static function getJobList() 72 | { 73 | $all_jobs = \DB::table('jobs')->orderby('available_at', 'asc')->where('queue', '!=', 'campaign_email')->get(); 74 | $all_strs = ['html' => '']; 75 | foreach ($all_jobs as $raw_job) 76 | { 77 | $j = unserialize(json_decode($raw_job->payload)->data->command); 78 | $desc = ''; 79 | if ($j->description != '') 80 | $desc = '
'.e($j->description).'
'; 81 | $all_strs['html'] .= '
  • 82 | 83 | 84 | 85 | '.e($j->title).' 86 | '.\App\Libraries\DateHelper::relative(Carbon::createFromTimestamp($raw_job->available_at)).' 87 | 88 | 89 | '.$desc.' 90 |
    91 |
    92 | '.$j->getProgress().'% 93 |
    94 |
    95 |
    96 |
    97 |
  • '; 98 | } 99 | if ($all_strs['html'] == '') 100 | $all_strs['html'] = '
  • No running jobs
  • '; 101 | $all_strs['num'] = count($all_jobs); 102 | return $all_strs; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /app/Campaign.php: -------------------------------------------------------------------------------- 1 | belongsTo('App\EmailTemplate'); 22 | } 23 | 24 | public function target_list() 25 | { 26 | return $this->belongsTo('App\TargetList'); 27 | } 28 | 29 | public function emails() 30 | { 31 | return $this->hasMany('App\Email'); 32 | } 33 | 34 | public function getStatus() 35 | { 36 | switch ($this->status) 37 | { 38 | case Campaign::NOT_STARTED: 39 | return "Not started"; 40 | case Campaign::SENDING: 41 | return "Sending emails"; 42 | case Campaign::WAITING: 43 | return "Running"; 44 | case Campaign::FINISHED: 45 | return "Completed"; 46 | case Campaign::CANCELLED: 47 | return "Cancelled"; 48 | default: 49 | return "Unknown status"; 50 | } 51 | } 52 | 53 | public function cancel() 54 | { 55 | if ($this->status != Campaign::FINISHED && $this->status != Campaign::CANCELLED) 56 | { 57 | $this->status = Campaign::CANCELLED; 58 | $this->save(); 59 | } 60 | $this->emails()->where('status', '!=', Email::SENT)->where('status', '!=', Email::CANCELLED)->where('status', '!=', Email::FAILED)->update(['status' => Email::CANCELLED]); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /app/Console/Commands/CatchMissed.php: -------------------------------------------------------------------------------- 1 | subMinutes(5)))->where('status', Email::NOT_SENT)->get(); 43 | foreach ($emails as $email) 44 | { 45 | $this->info("Queueing email " . $email->id); 46 | $email->send(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/Console/Commands/CreateUser.php: -------------------------------------------------------------------------------- 1 | argument('username'); 47 | if ($username == null) 48 | $username = $this->ask('Enter a username'); 49 | $email = $this->argument('email'); 50 | if ($email == null) 51 | $email = $this->ask('Enter an email address'); 52 | $password = $this->argument('password'); 53 | if ($password == null) 54 | $password = $this->secret('Enter a password'); 55 | if (!$this->option('confirm')) 56 | if (!$this->confirm("Are you sure you want to create this account? ")) 57 | return; 58 | if (User::where('name', $username)->count() != 0) 59 | { 60 | $this->error("User already exists!"); 61 | return; 62 | } 63 | $newUser = new User([ 64 | 'name' => $username, 65 | 'email' => $email, 66 | 'password' => bcrypt($password), 67 | ]); 68 | $newUser->save(); 69 | ActivityLog::log("Added a new user named \"".$newUser->name."\" (via artisan)", "Settings"); 70 | $this->info("User created successfully!"); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/Console/Commands/Disable2FA.php: -------------------------------------------------------------------------------- 1 | argument('username'); 45 | if ($username == null) 46 | $username = $this->ask('Enter a username'); 47 | $user = User::where('name', $username)->first(); 48 | if ($user === null) 49 | { 50 | $this->error('User "'.$username.'" was not found'); 51 | return; 52 | } 53 | if ($user->google2fa_secret == null) 54 | { 55 | $this->error('User "'.$username.'" does not have 2FA enabled'); 56 | return; 57 | } 58 | if (!$this->option('confirm')) 59 | if (!$this->confirm("Are you sure you want to disable the 2FA for this account? ")) 60 | return; 61 | $user->google2fa_secret = null; 62 | $user->save(); 63 | ActivityLog::log("Disabled 2FA for user named \"".$user->name."\" (via artisan)", "Settings"); 64 | $this->info("2FA disabled successfully!"); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app/Console/Commands/LogPull.php: -------------------------------------------------------------------------------- 1 | '/var/log/mail.log', 'imap' => '/var/log/dovecot.log']; 29 | 30 | /** 31 | * Create a new command instance. 32 | * 33 | * @return void 34 | */ 35 | public function __construct() 36 | { 37 | parent::__construct(); 38 | } 39 | 40 | /** 41 | * Execute the console command. 42 | * 43 | * @return mixed 44 | */ 45 | public function handle() 46 | { 47 | $timezone = system("date +%Z"); 48 | $this->info("Running log aggregation"); 49 | foreach ($this->files_to_log as $type => $file) 50 | { 51 | $latest = LogAggregate::where('log_type', $type)->orderby('log_time', 'desc')->orderby('id','asc')->first(); 52 | $latest_hash = ''; 53 | if ($latest !== null) 54 | { 55 | $latest_hash = $latest->hash; 56 | } 57 | if (is_readable($file)) 58 | { 59 | $fp = fopen($file, 'r'); 60 | 61 | $pos = -2; 62 | $currentLine = ''; 63 | while (-1 !== fseek($fp, $pos, SEEK_END)) { 64 | $char = fgetc($fp); 65 | if (PHP_EOL != $char) 66 | { 67 | $currentLine = $char . $currentLine; 68 | 69 | } 70 | else 71 | { 72 | $words = preg_split("/\s+/", $currentLine); 73 | if (count($words) < 3) 74 | continue; // This means the log was being written to while pulling 75 | $strtime = $words[0] . " " . $words[1] . " " . $words[2]; 76 | //$time = strtotime($strtime.' '.$timezone); 77 | $time = Carbon::parse($strtime.' '.$timezone)->timezone("UTC"); 78 | //$datetime = date("Y-m-d H:i:s", $time); 79 | $words_arr = explode(": ", $currentLine, 2); 80 | if (count($words_arr) < 2) 81 | continue; // This means the log was being written to while pulling 82 | $words = $words_arr[1]; 83 | $newlog = new LogAggregate(); 84 | $newlog->log_time = $time; 85 | $newlog->log_type = $type; 86 | $newlog->data = $words; 87 | $newlog->hash = LogAggregate::hash($newlog); 88 | //echo $newlog."\n"; 89 | if ($newlog->hash == $latest_hash) 90 | { 91 | break; 92 | } 93 | try 94 | { 95 | $newlog->save(); 96 | } 97 | catch (\Exception $e) 98 | { 99 | } 100 | $currentLine = ''; 101 | } 102 | $pos--; 103 | } 104 | fclose($fp); 105 | } 106 | } 107 | $this->info("Completed log aggregation."); 108 | $this->info("Searching for logs for emails"); 109 | $after_date = Carbon::now()->subMinutes(5); 110 | $before_date = Carbon::now()->addSeconds(30); 111 | $emails = Email::where('status', Email::SENT)->where('sent_time', '<=', $before_date)->where('sent_time', '>=', $after_date)->get(); 112 | foreach ($emails as $email) 113 | { 114 | $logs = LogAggregate::getSurroundingLogs($email->sent_time, 2, 5, 'smtp'); 115 | $total_str = ''; 116 | foreach ($logs as $log) 117 | { 118 | $total_str .= $log->log_time."\t".$log->data."\n"; 119 | } 120 | if (strlen($total_str) > strlen($email->related_logs)) 121 | { 122 | $email->related_logs = $total_str; 123 | $email->save(); 124 | } 125 | } 126 | $this->info("Purging logs 20 minutes old"); 127 | $before_date = Carbon::now()->subMinutes(20); 128 | LogAggregate::where('log_time', '<=', $before_date)->delete(); 129 | $this->info("Completed log aggregation."); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /app/Console/Kernel.php: -------------------------------------------------------------------------------- 1 | command('fp:checkmail')->everyMinute(); 32 | $schedule->command('fp:logpull')->everyMinute(); 33 | $schedule->command('fp:catchmissedmail')->everyMinute(); 34 | } 35 | 36 | /** 37 | * Register the Closure based commands for the application. 38 | * 39 | * @return void 40 | */ 41 | protected function commands() 42 | { 43 | require base_path('routes/console.php'); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/Email.php: -------------------------------------------------------------------------------- 1 | belongsTo('App\Campaign'); 29 | } 30 | 31 | public function send($delay=-1, $queue="email") 32 | { 33 | if ($delay === -1) 34 | $delay = Carbon::now()->addSeconds(1); 35 | if ($this->status == Email::SENT) 36 | $this->status = Email::PENDING_RESEND; 37 | else 38 | $this->status = Email::NOT_SENT; 39 | $this->planned_time = $delay; 40 | $this->save(); 41 | $job = (new SendEmail(['title' => 'Send Email', 'description' => 'To: '.$this->receiver_name, 'icon' => 'envelope'], $this))->onQueue($queue)->delay($delay); 42 | dispatch($job); 43 | } 44 | 45 | public function targetuser() 46 | { 47 | return $this->belongsTo('App\TargetUser', 'target_user_id'); 48 | } 49 | 50 | public function getStatus() 51 | { 52 | switch ($this->status) 53 | { 54 | case Email::NOT_SENT: 55 | return "Not sent"; 56 | case Email::SENDING: 57 | return "Sending"; 58 | case Email::SENT: 59 | return "Sent"; 60 | case Email::PENDING_RESEND: 61 | return "Pending resend"; 62 | case Email::CANCELLED: 63 | return "Cancelled"; 64 | case Email::FAILED: 65 | return "Failed sending"; 66 | default: 67 | return "Unknown status"; 68 | } 69 | } 70 | 71 | public function cancel() 72 | { 73 | if ($this->status != Email::SENT && $this->status != Email::CANCELLED && $this->status != Email::FAILED) 74 | { 75 | $this->status = Email::CANCELLED; 76 | $this->save(); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /app/EmailTemplate.php: -------------------------------------------------------------------------------- 1 | 'The name of the person (example: "John Doe")', 14 | '[first_name]' => 'The first name of the person (example: "John")', 15 | '[last_name]' => 'The last name of the person (example: "Doe")', 16 | '[username]' => 'Username of the person (example: In "john.doe@domain.com", it would be "john.doe")', 17 | '[email]' => 'Email of the target person (example: "john.doe@domain.com")', 18 | '[uid]' => 'A unique identifier for this user. This is useful in links to identify who clicked on a specific link', 19 | '[from_name]' => 'Name of the person sending the email', 20 | '[from_email]' => 'Email of the person sending the email', 21 | '[extra]' => 'Extra data a campaign might like to customize (such as a signature with titles) - UNUSED CURRENTLY', 22 | ]; 23 | 24 | 25 | public static $DEFAULTS = [ 26 | '[name]' => 'John Doe', 27 | '[first_name]' => 'John', 28 | '[last_name]' => 'Doe', 29 | '[username]' => 'john.doe', 30 | '[email]' => 'john.doe@domain.com', 31 | '[uid]' => '0696f64d67415a89782075f1d990b2deb449d5e5', 32 | '[from_name]' => 'Bill Smith', 33 | '[from_email]' => 'bsmith@malicious.com', 34 | '[extra]' => 'Director of IT Security', 35 | ]; 36 | 37 | private function parse_variables($text, $campaign, $targetUser) 38 | { 39 | $text = str_replace('[name]', $targetUser->full_name(), $text); 40 | $text = str_replace('[first_name]', $targetUser->first_name, $text); 41 | $text = str_replace('[last_name]', $targetUser->last_name, $text); 42 | $text = str_replace('[username]', explode("@",$targetUser->email)[0], $text); 43 | $text = str_replace('[email]', $targetUser->email, $text); 44 | $text = str_replace('[uid]', $targetUser->uuid($campaign), $text); 45 | $text = str_replace('[from_name]', $campaign->from_name, $text); 46 | $text = str_replace('[from_email]', $campaign->from_email, $text); 47 | $text = str_replace('[extra]', '', $text); 48 | return $text; 49 | } 50 | 51 | public function craft_email($campaign, $targetUser) 52 | { 53 | $email = new Email(); 54 | $email->sender_name = $campaign->from_name; 55 | $email->sender_email = $campaign->from_email; 56 | $email->target_user_id = $targetUser->id; 57 | $email->campaign_id = $campaign->id; 58 | $email->subject = $this->parse_variables($this->subject, $campaign, $targetUser); 59 | $email->message = $this->parse_variables($this->template, $campaign, $targetUser); 60 | $email->tls = true; // Maybe change this to be editable in the campaign 61 | $email->has_attachment = false; // Maybe change this to be editable in the campaign 62 | $email->status = Email::NOT_SENT; 63 | $email->uuid = $targetUser->uuid($campaign); 64 | $email->save(); 65 | return $email; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/Exceptions/Handler.php: -------------------------------------------------------------------------------- 1 | expectsJson()) { 60 | return response()->json(['error' => 'Unauthenticated.'], 401); 61 | } 62 | 63 | return redirect()->guest(action('Auth\LoginController@showLoginForm')); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/ForgotPasswordController.php: -------------------------------------------------------------------------------- 1 | middleware('guest'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/LoginController.php: -------------------------------------------------------------------------------- 1 | redirectTo = action('DashboardController@index'); 43 | $this->redirectAfterLogout = action('Auth\LoginController@showLoginForm'); 44 | $this->middleware('guest', ['except' => 'logout']); 45 | } 46 | 47 | public function username() 48 | { 49 | return 'name'; 50 | } 51 | 52 | /** 53 | * Send the post-authentication response. 54 | * 55 | * @param \Illuminate\Http\Request $request 56 | * @param \Illuminate\Contracts\Auth\Authenticatable $user 57 | * @return \Illuminate\Http\Response 58 | */ 59 | private function authenticated(Request $request, Authenticatable $user) 60 | { 61 | if ($user->google2fa_secret) { 62 | Auth::logout(); 63 | $request->session()->put('2fa:user:id', $user->id); 64 | return redirect()->action('Auth\LoginController@getValidateToken'); 65 | } 66 | return redirect()->intended($this->redirectTo); 67 | } 68 | /** 69 | * 70 | * @return \Illuminate\Http\Response 71 | */ 72 | public function getValidateToken() 73 | { 74 | if (session('2fa:user:id')) { 75 | return view('auth.2fa'); 76 | } 77 | return redirect()->action('Auth\LoginController@showLoginForm'); 78 | } 79 | /** 80 | * 81 | * @param App\Http\Requests\ValidateSecretRequest $request 82 | * @return \Illuminate\Http\Response 83 | */ 84 | public function postValidateToken(ValidateSecretRequest $request) 85 | { 86 | //get user id and create cache key 87 | $userId = $request->session()->pull('2fa:user:id'); 88 | $key = $userId . ':' . $request->totp; 89 | //use cache to store token to blacklist 90 | Cache::add($key, true, 4); 91 | //login and redirect user 92 | Auth::loginUsingId($userId); 93 | return redirect()->intended($this->redirectTo); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/RegisterController.php: -------------------------------------------------------------------------------- 1 | middleware('guest'); 40 | } 41 | 42 | /** 43 | * Get a validator for an incoming registration request. 44 | * 45 | * @param array $data 46 | * @return \Illuminate\Contracts\Validation\Validator 47 | */ 48 | protected function validator(array $data) 49 | { 50 | return Validator::make($data, [ 51 | 'name' => 'required|max:255', 52 | 'email' => 'required|email|max:255|unique:users', 53 | 'password' => 'required|min:6|confirmed', 54 | ]); 55 | } 56 | 57 | /** 58 | * Create a new user instance after a valid registration. 59 | * 60 | * @param array $data 61 | * @return User 62 | */ 63 | protected function create(array $data) 64 | { 65 | return User::create([ 66 | 'name' => $data['name'], 67 | 'email' => $data['email'], 68 | 'password' => bcrypt($data['password']), 69 | ]); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/Http/Controllers/Auth/ResetPasswordController.php: -------------------------------------------------------------------------------- 1 | middleware('guest'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Http/Controllers/CampaignController.php: -------------------------------------------------------------------------------- 1 | middleware('auth'); 21 | } 22 | 23 | public function index() 24 | { 25 | $all_campaigns = Campaign::all(); 26 | return view('campaigns.index')->with('all_campaigns', $all_campaigns); 27 | } 28 | 29 | public function create() 30 | { 31 | $all_templates = EmailTemplate::orderby('name', 'asc')->get(); 32 | $all_lists = TargetList::orderby('name', 'asc')->get(); 33 | return view('campaigns.create')->with('templates', $all_templates)->with('lists', $all_lists); 34 | } 35 | 36 | public function create_post(Request $request) 37 | { 38 | 39 | $this->validate($request, [ 40 | 'campaign_name' => 'required', 41 | 'campaign_description' => 'required', 42 | 'email_template' => 'required|integer', 43 | 'target_list' => 'required|integer', 44 | 'sender_name' => 'required', 45 | 'sender_email' => 'required|email', 46 | ]); 47 | 48 | $template = EmailTemplate::findOrFail($request->input('email_template')); 49 | $list = TargetList::findOrFail($request->input('target_list')); 50 | $campaign = new Campaign(); 51 | $campaign->name = $request->input('campaign_name'); 52 | $campaign->from_name = $request->input('sender_name'); 53 | $campaign->from_email = $request->input('sender_email'); 54 | $campaign->description = $request->input('campaign_description'); 55 | $campaign->status = Campaign::NOT_STARTED; 56 | $campaign->target_list_id = $request->input('target_list'); 57 | $campaign->email_template_id = $request->input('email_template'); 58 | $campaign->save(); 59 | $start_date = $request->input('starting_date') ?: \App\Libraries\DateHelper::now()->format('m/d/Y'); 60 | $start_time = $request->input('starting_time') ?: \App\Libraries\DateHelper::now()->format('g:ia'); 61 | $start_date = Carbon::parse($start_date . ' ' . $start_time, config('fiercephish.APP_TIMEZONE'))->addSeconds(1)->timezone('UTC'); 62 | $nowTime = Carbon::now()->timezone('UTC'); 63 | if ($start_date < $nowTime) { 64 | $start_date = $nowTime; 65 | } 66 | $send_num_emails = min((int)$request->input('send_num'),1000); 67 | $send_every_minutes = min((int)$request->input('send_every_x_minutes'), 1000); 68 | if ($request->input('sending_schedule') == 'all' || empty($request->input('send_num')) || empty($request->input('send_every_x_minutes'))) 69 | { 70 | $send_num_emails = -1; // Send all emails at once 71 | } 72 | $job = (new StartCampaign(['title' => 'Create campaign', 'description' => 'Campaign: "' . $campaign->name.'"', 'icon' => 'play'], $campaign, $list, $template, $send_num_emails, $send_every_minutes, $start_date))->onQueue('operation')->delay(1); 73 | $this->dispatch($job); 74 | ActivityLog::log("Created a create campaign job named \"".$campaign->name."\" to queue ".$list->users()->count()." emails for sending", "Campaign"); 75 | return redirect()->action('CampaignController@campaign_details', ['id' => $campaign->id])->with('success', 'Job to create campaign has been launched successfully'); 76 | } 77 | 78 | 79 | public function campaign_details($id) 80 | { 81 | $campaign = Campaign::findOrFail($id); 82 | return view('campaigns.details')->with('campaign', $campaign); 83 | } 84 | 85 | public function campaign_cancel($id) 86 | { 87 | $campaign = Campaign::findOrFail($id); 88 | $campaign->cancel(); 89 | ActivityLog::log("Cancelled the \"".$campaign->name."\" campaign", "Campaign"); 90 | return back()->with('success', 'Campaign was cancelled successfully'); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /app/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | middleware('auth'); 20 | } 21 | public function index() 22 | { 23 | // Get sent email stats 24 | $rawSendEmailData = DB::table('emails')->select(DB::raw('DATE(CONVERT_TZ(sent_time, "+00:00", "'.\App\Libraries\DateHelper::getOffset().'")) as date'), DB::raw('count(*) as numEmails'))->groupBy('date')->where('status', Email::SENT)->get(); 25 | // Fill in the gaps of dates where no emails were sent 26 | $sendEmailData = []; 27 | if (count($rawSendEmailData) > 0) 28 | { 29 | $sendEmailData = [$rawSendEmailData[0]]; 30 | for ($x=1; $xdate)->addDay(1)->format('Y-m-d'); 33 | while ($curDate != $rawSendEmailData[$x]->date) 34 | { 35 | $obj = new \stdClass(); 36 | $obj->date = $curDate; 37 | $obj->numEmails = "0"; 38 | $sendEmailData[] = $obj; 39 | $curDate = Carbon::parse($curDate)->addDay(1)->format('Y-m-d'); 40 | } 41 | $sendEmailData[] = $rawSendEmailData[$x]; 42 | } 43 | } 44 | 45 | // Get error emails 46 | $errorEmailData = DB::table('emails')->select(DB::raw('DATE(CONVERT_TZ(updated_at, "+00:00", "'.\App\Libraries\DateHelper::getOffset().'")) as date'), DB::raw('count(*) as numEmails'))->groupBy('date')->where('status', Email::CANCELLED)->get(); 47 | 48 | // Get Statistics 49 | $statistics = [ 50 | 'numSent' => Email::where('status', Email::SENT)->count(), 51 | 'numCancelled' => Email::where('status', Email::CANCELLED)->count(), 52 | 'numPending' => Email::where('status', Email::NOT_SENT)->orWhere('status', Email::SENDING)->orWhere('status', Email::PENDING_RESEND)->count(), 53 | ]; 54 | 55 | return view('dashboard.index')->with('activitylog', ActivityLog::fetch())->with('sendEmailData', $sendEmailData)->with('errorEmailData', $errorEmailData)->with('emailStats', $statistics)->with('allActiveCampaigns', Campaign::where('status', Campaign::NOT_STARTED)->orWhere('status', Campaign::SENDING)->orWhere('status', Campaign::WAITING)->get()); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/Http/Controllers/Google2FAController.php: -------------------------------------------------------------------------------- 1 | middleware(['web', 'auth']); 20 | } 21 | 22 | public function enableTwoFactor(Request $request) 23 | { 24 | $secret = $this->generateSecret(); 25 | $user = $request->user(); 26 | $user->google2fa_secret = Crypt::encrypt($secret); 27 | $user->save(); 28 | 29 | return redirect()->action('SettingsController@get_editprofile')->with('success', 'Enabled Google 2FA'); 30 | } 31 | 32 | public function disableTwoFactor(Request $request) 33 | { 34 | $user = $request->user(); 35 | $user->google2fa_secret = null; 36 | $user->save(); 37 | return redirect()->action('SettingsController@get_editprofile')->with('success', 'Google 2FA has been disabled'); 38 | } 39 | 40 | private function generateSecret() 41 | { 42 | $randomBytes = random_bytes(10); 43 | return Base32::encodeUpper($randomBytes); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/Http/Controllers/LogController.php: -------------------------------------------------------------------------------- 1 | middleware('auth'); 21 | $this->logs_to_check = [ 22 | 'apache-access' => '/var/log/apache2/access_fiercephish.log', 23 | 'apache-error' => '/var/log/apache2/error_fiercephish.log', 24 | 'mail' => '/var/log/mail.log', 25 | 'dovecot' => '/var/log/dovecot.log', 26 | 'laravel' => base_path('storage/logs/laravel.log'), 27 | ]; 28 | } 29 | 30 | public function index() 31 | { 32 | $logs = ActivityLog::orderby('id', 'desc')->take(200)->get(); 33 | $activitylog_arr = []; 34 | foreach ($logs as $log) 35 | { 36 | $activitylog_arr[] = $log->read(); 37 | } 38 | $activitylog = implode("\n", $activitylog_arr); 39 | return view('logs.index')->with('logs', $this->logs_to_check)->with('activitylog', $activitylog); 40 | } 41 | 42 | public function download($type) 43 | { 44 | $file_to_download = ''; 45 | if (array_key_exists($type, $this->logs_to_check)) 46 | { 47 | if (!is_readable($this->logs_to_check[$type])) 48 | return back()->withErrors('"'.$this->logs_to_check[$type].'" does not exist or has invalid permissions'); 49 | return Response::download($this->logs_to_check[$type], $type.'.log'); 50 | } 51 | elseif ($type == 'all') 52 | { 53 | $zip = new ZipArchive(); 54 | $file = '/tmp/log_download.zip'; 55 | if ($zip->open($file, ZipArchive::CREATE) != true) 56 | { 57 | return back()->withErrors("Unable to create ZIP file"); 58 | } 59 | foreach ($this->logs_to_check as $name => $log) 60 | { 61 | if (is_readable($log)) 62 | $zip->addFile($log, $name.'.log'); 63 | } 64 | // ActivityLog 65 | $logs = ActivityLog::orderby('id', 'desc')->get(); 66 | $activitylog_arr = []; 67 | foreach ($logs as $log) 68 | { 69 | $activitylog_arr[] = $log->read(); 70 | } 71 | $activitylog = implode("\n", $activitylog_arr); 72 | $zip->addFromString('activitylog.log', $activitylog); 73 | $zip->close(); 74 | $resp = Response::make(file_get_contents($file), '200', [ 75 | 'Content-Type' => 'application/zip', 76 | 'Content-Disposition' => 'attachment; filename="fiercephish_logs.bundle.zip"', 77 | 'Content-Length' => filesize($file), 78 | ]); 79 | unlink($file); 80 | return $resp; 81 | } 82 | elseif ($type == 'activitylog') 83 | { 84 | $logs = ActivityLog::orderby('id', 'desc')->get(); 85 | $activitylog_arr = []; 86 | foreach ($logs as $log) 87 | { 88 | $activitylog_arr[] = $log->read(); 89 | } 90 | $activitylog = implode("\n", $activitylog_arr); 91 | return Response::make($activitylog, '200', [ 92 | 'Content-Type' => 'application/octet-stream', 93 | 'Content-Disposition' => 'attachment; filename="activitylog.log"', 94 | ]); 95 | } 96 | else 97 | { 98 | return abort(404); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /app/Http/Kernel.php: -------------------------------------------------------------------------------- 1 | [ 30 | \App\Http\Middleware\EncryptCookies::class, 31 | \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, 32 | \Illuminate\Session\Middleware\StartSession::class, 33 | \Illuminate\View\Middleware\ShareErrorsFromSession::class, 34 | \App\Http\Middleware\VerifyCsrfToken::class, 35 | \Illuminate\Routing\Middleware\SubstituteBindings::class, 36 | ], 37 | 38 | 'api' => [ 39 | 'throttle:60,1', 40 | 'bindings', 41 | ], 42 | ]; 43 | 44 | /** 45 | * The application's route middleware. 46 | * 47 | * These middleware may be assigned to groups or used individually. 48 | * 49 | * @var array 50 | */ 51 | protected $routeMiddleware = [ 52 | 'auth' => \Illuminate\Auth\Middleware\Authenticate::class, 53 | 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 54 | 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, 55 | 'can' => \Illuminate\Auth\Middleware\Authorize::class, 56 | 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 57 | 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 58 | ]; 59 | } 60 | -------------------------------------------------------------------------------- /app/Http/Middleware/EncryptCookies.php: -------------------------------------------------------------------------------- 1 | check()) { 21 | return redirect()->action('DashboardController@index'); 22 | } 23 | 24 | return $next($request); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/Http/Middleware/TrimStrings.php: -------------------------------------------------------------------------------- 1 | extend( 25 | 'valid_token', 26 | function ($attribute, $value, $parameters, $validator) { 27 | $secret = Crypt::decrypt($this->user->google2fa_secret); 28 | return Google2FA::verifyKey($secret, $value); 29 | }, 30 | 'Not a valid token' 31 | ); 32 | $factory->extend( 33 | 'used_token', 34 | function ($attribute, $value, $parameters, $validator) { 35 | $key = $this->user->id . ':' . $value; 36 | return !Cache::has($key); 37 | }, 38 | 'Cannot reuse token' 39 | ); 40 | } 41 | /** 42 | * Determine if the user is authorized to make this request. 43 | * 44 | * @return bool 45 | */ 46 | public function authorize() 47 | { 48 | try { 49 | $this->user = User::findOrFail( 50 | session('2fa:user:id') 51 | ); 52 | } catch (Exception $exc) { 53 | return false; 54 | } 55 | return true; 56 | } 57 | /** 58 | * Get the validation rules that apply to the request. 59 | * 60 | * @return array 61 | */ 62 | public function rules() 63 | { 64 | return [ 65 | 'totp' => 'bail|required|digits:6|valid_token|used_token', 66 | ]; 67 | } 68 | } -------------------------------------------------------------------------------- /app/Jobs/AddToList.php: -------------------------------------------------------------------------------- 1 | targetlist = $targetlist; 31 | $this->num_to_add = $num_to_add; 32 | $this->only_unassigned = $only_unassigned; 33 | parent::__construct($meta); 34 | } 35 | 36 | /** 37 | * Execute the job. 38 | * 39 | * @return void 40 | */ 41 | public function handle() 42 | { 43 | // Add all to list 44 | if ($this->num_to_add < 0) 45 | { 46 | $list = $this->targetlist; 47 | $query = $this->targetlist->availableUsers(); 48 | $count = 0; 49 | if ($this->only_unassigned) 50 | $query = TargetUser::doesntHave('lists'); 51 | $totalNum = $query->count(); 52 | $chunkSize = 1000; 53 | $batch = $query->take($chunkSize)->get(); 54 | while (count($batch) > 0) { 55 | $list->users()->syncWithoutDetaching($batch->pluck('id')->toArray()); 56 | $count += count($batch); 57 | $this->setProgress(round(($count/$totalNum)*100)); 58 | $batch = $query->take($chunkSize)->get(); 59 | } 60 | ActivityLog::log("Added All Target Users to the Target List \"".$list->name."\" job completed", "Target List"); 61 | } 62 | else 63 | { 64 | $list = $this->targetlist; 65 | $count = 0; 66 | $num_left = $this->num_to_add; 67 | while ($num_left > 0) 68 | { 69 | $chunk = min($num_left, 1000); 70 | $query = $list->availableUsers(); 71 | if ($this->only_unassigned) 72 | $query = TargetUser::doesntHave('lists'); 73 | $list->users()->syncWithoutDetaching($query->inRandomOrder()->take($chunk)->pluck('id')->toArray()); 74 | $num_left -= $chunk; 75 | $count += $chunk; 76 | $this->setProgress(round(($count/$this->num_to_add)*100)); 77 | } 78 | ActivityLog::log("Added Random Target Users to the Target List \"".$list->name."\" job completed", "Target List"); 79 | } 80 | $this->cleanup(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /app/Jobs/ImportTargets.php: -------------------------------------------------------------------------------- 1 | process_path = $path; 28 | parent::__construct($meta); 29 | } 30 | 31 | 32 | 33 | /** 34 | * Execute the job. 35 | * 36 | * @return void 37 | */ 38 | public function handle() 39 | { 40 | if (!file_exists($this->process_path)) 41 | { 42 | $this->cleanup(); 43 | return; 44 | } 45 | $lines = explode("\n", file_get_contents($this->process_path)); 46 | $total = count($lines); 47 | $processed = 0; 48 | $errors = []; 49 | foreach ($lines as $line) 50 | { 51 | 52 | $line = trim($line); 53 | if ($line == "") 54 | continue; 55 | $parts = str_getcsv($line, ",", '"'); 56 | for ($x=0; $xfirst_name = $parts[0]; 66 | $t->last_name = $parts[1]; 67 | $t->email = $parts[2]; 68 | if (count($parts) > 3) 69 | $t->notes = $parts[3]; 70 | try 71 | { 72 | $t->save(); 73 | } 74 | catch (\Illuminate\Database\QueryException $e) 75 | { 76 | $t = TargetUser::where('first_name', $parts[0])->where('last_name', $parts[1])->where('email', $parts[2])->first(); 77 | if ($t !== null && $t->hidden) 78 | { 79 | $t->hidden = false; 80 | $t->save(); 81 | } 82 | } 83 | ++$processed; 84 | $new_progress = round(($processed/$total)*100); 85 | if ($this->getProgress() != $new_progress) 86 | { 87 | $this->setProgress($new_progress); 88 | } 89 | } 90 | } 91 | ActivityLog::log("Target User import job completed (".$this->description.")", "Target User"); 92 | unlink($this->process_path); 93 | $this->cleanup(); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /app/Jobs/Job.php: -------------------------------------------------------------------------------- 1 | title = $meta['title']; 24 | $this->description = $meta['description']; 25 | $this->icon = $meta['icon']; 26 | $this->uid = 'job_'.rand(); 27 | Cache::forever($this->uid, 0); 28 | } 29 | 30 | public function setProgress($num) 31 | { 32 | Cache::forever($this->uid, $num); 33 | } 34 | 35 | public function getProgress() 36 | { 37 | if (Cache::has($this->uid)) 38 | return Cache::get($this->uid); 39 | Cache::forever($this->uid, 0); 40 | return 0; 41 | } 42 | 43 | public function cleanup() 44 | { 45 | Cache::forget($this->uid); 46 | } 47 | } -------------------------------------------------------------------------------- /app/Jobs/SendEmail.php: -------------------------------------------------------------------------------- 1 | email = $email; 32 | parent::__construct($meta); 33 | } 34 | 35 | /** 36 | * Execute the job. 37 | * 38 | * @return void 39 | */ 40 | public function handle() 41 | { 42 | if ($this->email->campaign != null && $this->email->campaign->status == Campaign::CANCELLED) 43 | { 44 | $this->email->status = Email::CANCELLED; 45 | $this->email->save(); 46 | $this->cleanup(); 47 | return; 48 | } 49 | if ($this->email->status == Email::CANCELLED || $this->email->status == Email::SENDING || $this->email->status == Email::SENT || $this->email->status == Email::FAILED) 50 | { 51 | $this->cleanup(); 52 | return; 53 | } 54 | if ($this->email->campaign != null) 55 | { 56 | $this->email->campaign->status = Campaign::SENDING; 57 | $this->email->campaign->save(); 58 | } 59 | $this->email->status = Email::SENDING; 60 | $this->email->save(); 61 | if (config('fiercephish.TEST_EMAIL_JOB') === false) 62 | { 63 | try 64 | { 65 | Mail::send(['layouts.email_html', 'layouts.email_plaintext'], ['data' => $this->email->message], function ($message) { 66 | $message->from($this->email->sender_email, $this->email->sender_name); 67 | $message->to($this->email->targetuser->email, $this->email->targetuser->full_name()); 68 | $message->subject($this->email->subject); 69 | if (strstr(config('fiercephish.APP_URL'), '.') !== false) 70 | { 71 | $id = explode('@',$message->getSwiftMessage()->getId()); 72 | $domain = explode(':', str_replace(['http://','https://'],'', config('fiercephish.APP_URL')))[0]; 73 | $message->getSwiftMessage()->setId($id[0].'@'.$domain); 74 | // $message->getSwiftMessage()->getHeaders()->addTextHeader('List-Unsubscribe', ''); 75 | } 76 | if ($this->email->has_attachment) 77 | { 78 | $message->attachData(base64_decode($this->email->attachment), $this->email->attachment_name, ['mime' => $this->email->attachment_mime]); 79 | } 80 | if (strpos(config('fiercephish.MAIL_BCC_ALL'), '@') !== false) 81 | $message->bcc(config('fiercephish.MAIL_BCC_ALL')); 82 | }); 83 | } 84 | catch (\Exception $e) 85 | { 86 | $this->email->status = Email::FAILED; 87 | $this->email->save(); 88 | Log::error($e); 89 | echo 'Error: '.$e->getMessage()."\n"; 90 | if ($this->email->campaign != null) 91 | { 92 | ActivityLog::log("Failed to send an email to \"".$this->email->targetuser->email."\" for campaign \"".$this->email->campaign->name."\" (email ID ".$this->email->id.") (try #".$this->attempts().')', "SendEmail", true); 93 | } 94 | else 95 | { 96 | ActivityLog::log("Failed to send an email (simple send) to \"".$this->email->targetuser->email."\" (email ID ".$this->email->id.") (try #".$this->attempts().')', "SendEmail", true); 97 | } 98 | ActivityLog::log("Cancelling email due to failed sending attempt. Check the log for the errors!", "SendEmail"); 99 | $this->checkCampaign(); 100 | $this->delete(); 101 | return; 102 | } 103 | } 104 | $this->email->sent_time = Carbon::now(); 105 | $this->email->status = Email::SENT; 106 | $this->email->save(); 107 | if ($this->email->campaign != null) 108 | { 109 | ActivityLog::log("Sent an email to \"".$this->email->targetuser->email."\" for campaign \"".$this->email->campaign->name."\" (email ID ".$this->email->id.")", "SendEmail"); 110 | } 111 | else 112 | { 113 | ActivityLog::log("Sent an email (simple send) to \"".$this->email->targetuser->email."\" (email ID ".$this->email->id.")", "SendEmail"); 114 | } 115 | 116 | 117 | $this->checkCampaign(); 118 | 119 | $this->cleanup(); 120 | } 121 | 122 | public function failed(Exception $exception) 123 | { 124 | $this->email->status = Email::FAILED; 125 | $this->email->save(); 126 | Log::error($e); 127 | echo $exception->getMessage(); 128 | $this->cleanup(); 129 | } 130 | 131 | 132 | public function checkCampaign() 133 | { 134 | if ($this->email->campaign != null) 135 | { 136 | if ($this->email->campaign->emails()->where('status', Email::NOT_SENT)->count() == 0) 137 | { 138 | $this->email->campaign->status = Campaign::FINISHED; 139 | } 140 | else 141 | { 142 | $this->email->campaign->status = Campaign::WAITING; 143 | } 144 | $this->email->campaign->save(); 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /app/Jobs/StartCampaign.php: -------------------------------------------------------------------------------- 1 | campaign = $campaign; 33 | $this->list = $list; 34 | $this->template = $template; 35 | $this->send_num_emails = $send_num_emails; 36 | $this->send_every_minutes = $send_every_minutes; 37 | $this->start_date = $start_date; 38 | parent::__construct($meta); 39 | } 40 | 41 | /** 42 | * Execute the job. 43 | * 44 | * @return void 45 | */ 46 | public function handle() 47 | { 48 | $list = $this->list; 49 | $campaign = $this->campaign; 50 | $template = $this->template; 51 | $send_num_emails = $this->send_num_emails; 52 | $send_every_minutes = $this->send_every_minutes; 53 | $start_date = $this->start_date; 54 | $send_all_immediately = false; 55 | if ($send_num_emails < 0) 56 | $send_all_immediately = true; 57 | $original_send_num_emails = $send_num_emails; 58 | $numUsers = $list->users()->count(); 59 | $numSent = 0; 60 | $list->users()->chunk(1000, function($users) use($send_all_immediately, &$send_num_emails, $original_send_num_emails, &$counter, &$numSent, $campaign, $template, &$start_date, $send_every_minutes, $numUsers) { 61 | foreach ($users as $user) 62 | { 63 | $new_email = $template->craft_email($campaign, $user); 64 | if ($send_all_immediately) 65 | { 66 | $new_email->send($start_date, 'campaign_email'); 67 | } 68 | else 69 | { 70 | $new_email->send($start_date, 'campaign_email'); 71 | --$send_num_emails; 72 | if ($send_num_emails == 0) 73 | { 74 | $start_date = $start_date->addMinutes($send_every_minutes); 75 | $send_num_emails = $original_send_num_emails; 76 | } 77 | } 78 | ++$numSent; 79 | $oldProgress = $this->getProgress(); 80 | $newRate = round(($numSent/$numUsers)*100); 81 | if ($oldProgress != $newRate) 82 | { 83 | $this->setProgress($newRate); 84 | } 85 | } 86 | }); 87 | ActivityLog::log("Completed campaign job named \"".$campaign->name."\" to queue ".$list->users()->count()." emails for sending", "Campaign"); 88 | $this->cleanup(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /app/Libraries/CacheHelper.php: -------------------------------------------------------------------------------- 1 | timezone(config('fiercephish.APP_TIMEZONE'))->toDateTimeString(); 13 | } 14 | 15 | public static function readable($date) 16 | { 17 | if (DateHelper::isNull($date)) 18 | return "N/A"; 19 | return $date->timezone(config('fiercephish.APP_TIMEZONE'))->format('M j, Y @ g:i:s a'); 20 | } 21 | 22 | public static function relative($date) 23 | { 24 | if (DateHelper::isNull($date)) 25 | return "N/A"; 26 | return $date->timezone(config('fiercephish.APP_TIMEZONE'))->diffForHumans(); 27 | } 28 | 29 | public static function format($date, $format) 30 | { 31 | if (DateHelper::isNull($date)) 32 | return "N/A"; 33 | return $date->timezone(config('fiercephish.APP_TIMEZONE'))->format($format); 34 | } 35 | 36 | public static function isNull($date) 37 | { 38 | return $date===null || property_exists($date, 'year') || $date->year < 5; 39 | } 40 | 41 | public static function getOffset($tz='') 42 | { 43 | if ($tz === '') 44 | $tz = config('fiercephish.APP_TIMEZONE'); 45 | $target_time_zone = new \DateTimeZone($tz); 46 | $date_time = new \DateTime('now', $target_time_zone); 47 | return $date_time->format('P'); 48 | } 49 | 50 | public static function now() 51 | { 52 | return Carbon::now()->timezone(config('fiercephish.APP_TIMEZONE')); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/LogAggregate.php: -------------------------------------------------------------------------------- 1 | log_time.'-'.$log->log_type.'-'.$log->data); 16 | } 17 | 18 | 19 | 20 | public static function getSurroundingLogs($carbon_date, $before_num_secs='20', $after_num_secs='20', $type = null) 21 | { 22 | $adate = $carbon_date->copy()->subSeconds($before_num_secs); 23 | $bdate = $carbon_date->copy()->addSeconds($after_num_secs); 24 | $query = LogAggregate::where('log_time', '>=', $adate)->where('log_time', '<=', $bdate); 25 | if ($type !== null) 26 | $query = $query->where('log_type', $type); 27 | $query = $query->orderby('log_time', 'asc')->orderby('id', 'desc'); 28 | return $query->get(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | composer('layouts.app', function ($view) { 18 | $latest_version = \App\Libraries\CacheHelper::getLatestVersion(); 19 | $current_version = \App\Libraries\CacheHelper::getCurrentVersion(); 20 | $current_jobs = \App\ActivityLog::getJobList(); 21 | $view->with('layout_all_active_campaigns', \App\Campaign::where('status', \App\Campaign::WAITING)->orWhere('status', \App\Campaign::SENDING)->get()) 22 | ->with('latest_fiercephish_version', $latest_version) 23 | ->with('current_fiercephish_version', $current_version) 24 | ->with('current_jobs', $current_jobs); 25 | }); 26 | } 27 | 28 | /** 29 | * Register any application services. 30 | * 31 | * @return void 32 | */ 33 | public function register() 34 | { 35 | // 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/Providers/AuthServiceProvider.php: -------------------------------------------------------------------------------- 1 | 'App\Policies\ModelPolicy', 17 | ]; 18 | 19 | /** 20 | * Register any authentication / authorization services. 21 | * 22 | * @return void 23 | */ 24 | public function boot() 25 | { 26 | $this->registerPolicies(); 27 | 28 | // 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/Providers/BroadcastServiceProvider.php: -------------------------------------------------------------------------------- 1 | [ 17 | 'App\Listeners\EventListener', 18 | ], 19 | ]; 20 | 21 | /** 22 | * Register any events for your application. 23 | * 24 | * @return void 25 | */ 26 | public function boot() 27 | { 28 | parent::boot(); 29 | 30 | // 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/Providers/RouteServiceProvider.php: -------------------------------------------------------------------------------- 1 | mapApiRoutes(); 39 | 40 | $this->mapWebRoutes(); 41 | 42 | // 43 | } 44 | 45 | /** 46 | * Define the "web" routes for the application. 47 | * 48 | * These routes all receive session state, CSRF protection, etc. 49 | * 50 | * @return void 51 | */ 52 | protected function mapWebRoutes() 53 | { 54 | Route::middleware('web') 55 | ->namespace($this->namespace) 56 | ->group(base_path('routes/web.php')); 57 | } 58 | 59 | /** 60 | * Define the "api" routes for the application. 61 | * 62 | * These routes are typically stateless. 63 | * 64 | * @return void 65 | */ 66 | protected function mapApiRoutes() 67 | { 68 | Route::prefix('api') 69 | ->middleware('api') 70 | ->namespace($this->namespace) 71 | ->group(base_path('routes/api.php')); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/ReceivedMail.php: -------------------------------------------------------------------------------- 1 | hasMany('App\ReceivedMailAttachment'); 21 | } 22 | 23 | public function attachment_count() 24 | { 25 | return $this->attachments()->selectRaw('received_mail_id, count(*) as aggregate')->groupBy('received_mail_id'); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/ReceivedMailAttachment.php: -------------------------------------------------------------------------------- 1 | belongsTo('App\ReceivedMail'); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/TargetList.php: -------------------------------------------------------------------------------- 1 | belongsToMany('App\TargetUser'); 16 | } 17 | 18 | 19 | public function availableUsers() 20 | { 21 | return TargetUser::where('hidden', false)->whereDoesntHave('lists', function ($q) { 22 | $q->where('target_list_id', '=', $this->id); 23 | }); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /app/TargetUser.php: -------------------------------------------------------------------------------- 1 | belongsToMany('App\TargetList'); 15 | } 16 | 17 | public function uuid($campaign) 18 | { 19 | return sha1(sha1('xG!1jBdn?/y]n=~07DRp'.$this->first_name.'.M{5>gDe'.$this->last_name.'`lWcv'.$this->email.'q=N8{?iW1V[,15^B*IRC').($campaign->id)); 20 | } 21 | 22 | public function emails() 23 | { 24 | return $this->hasMany('App\Email'); 25 | } 26 | 27 | public function full_name() 28 | { 29 | $str = $this->first_name; 30 | if ($this->last_name !== '') 31 | $str .= ' '.$this->last_name; 32 | return $str; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/User.php: -------------------------------------------------------------------------------- 1 | make(Illuminate\Contracts\Console\Kernel::class); 32 | 33 | $status = $kernel->handle( 34 | $input = new Symfony\Component\Console\Input\ArgvInput, 35 | new Symfony\Component\Console\Output\ConsoleOutput 36 | ); 37 | 38 | /* 39 | |-------------------------------------------------------------------------- 40 | | Shutdown The Application 41 | |-------------------------------------------------------------------------- 42 | | 43 | | Once Artisan has finished running. We will fire off the shutdown events 44 | | so that any final work may be done by the application before we shut 45 | | down the process. This is the last thing to happen to the request. 46 | | 47 | */ 48 | 49 | $kernel->terminate($input, $status); 50 | 51 | exit($status); 52 | -------------------------------------------------------------------------------- /bootstrap/app.php: -------------------------------------------------------------------------------- 1 | singleton( 30 | Illuminate\Contracts\Http\Kernel::class, 31 | App\Http\Kernel::class 32 | ); 33 | 34 | $app->singleton( 35 | Illuminate\Contracts\Console\Kernel::class, 36 | App\Console\Kernel::class 37 | ); 38 | 39 | $app->singleton( 40 | Illuminate\Contracts\Debug\ExceptionHandler::class, 41 | App\Exceptions\Handler::class 42 | ); 43 | 44 | /* 45 | |-------------------------------------------------------------------------- 46 | | Return The Application 47 | |-------------------------------------------------------------------------- 48 | | 49 | | This script returns the application instance. The instance is given to 50 | | the calling script so we can separate the building of the instances 51 | | from the actual running of the application and sending responses. 52 | | 53 | */ 54 | 55 | return $app; 56 | -------------------------------------------------------------------------------- /bootstrap/autoload.php: -------------------------------------------------------------------------------- 1 | " 7 | ], 8 | "license": "GPL-3.0", 9 | "homepage": "https://fiercephish.com", 10 | "ignore": [ 11 | "**/.*", 12 | "node_modules", 13 | "bower_components", 14 | "public/vendor", 15 | "test", 16 | "tests" 17 | ], 18 | "dependencies": { 19 | "jquery": "1.x", 20 | "font-awesome": "^4.7.0", 21 | "bootstrap": "3.x", 22 | "fastclick": "^1.0.6", 23 | "nprogress": "^0.2.0", 24 | "bootstrap-progressbar": "^0.9.0", 25 | "jquery.inputmask": "^3.3.3", 26 | "x-editable": "^1.5.1", 27 | "select2": "select2-ng#^4.0.3", 28 | "ckeditor": "^4.5.11", 29 | "blockUI": "blockui#*", 30 | "bootstrap-datepicker": "^1.6.4", 31 | "datatables.net": "^1.10.12", 32 | "datatables.net-bs": "^1.10.12", 33 | "datatables.net-select-bs": "^1.2.0", 34 | "jt.timepicker": "jquery-timepicker-jt#^1.11.8", 35 | "bootbox.js": "bootbox#^4.4.0", 36 | "Flot": "flot#^0.8.3", 37 | "gentelella": "^1.3.0", 38 | "tooltipster": "^4.1.6", 39 | "DateJS": "^1.0.0-rc3" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "raikia/fiercephish", 3 | "description": "FiercePhish Phishing Framework", 4 | "keywords": ["security", "phishing", "netsec", "hacking", "email"], 5 | "license": "GPL-3.0", 6 | "type": "project", 7 | "require": { 8 | "php": ">=5.6.4", 9 | "bacon/bacon-qr-code": "^1.0", 10 | "doctrine/dbal": "^2.5", 11 | "guzzlehttp/guzzle": "^6.2", 12 | "laravel/framework": "5.4.*", 13 | "laravel/tinker": "~1.0", 14 | "paragonie/constant_time_encoding": "^1", 15 | "pragmarx/google2fa": "^1.0", 16 | "yajra/laravel-datatables-oracle": "^7.0", 17 | "soundasleep/html2text": "~0.5" 18 | }, 19 | "require-dev": { 20 | "fzaninotto/faker": "~1.4", 21 | "mockery/mockery": "0.9.*", 22 | "phpunit/phpunit": "~5.0" 23 | }, 24 | "autoload": { 25 | "classmap": [ 26 | "database" 27 | ], 28 | "psr-4": { 29 | "App\\": "app/" 30 | } 31 | }, 32 | "autoload-dev": { 33 | "psr-4": { 34 | "Tests\\": "tests/" 35 | } 36 | }, 37 | "scripts": { 38 | "post-root-package-install": [ 39 | "php -r \"file_exists('.env') || copy('.env.example', '.env');\"" 40 | ], 41 | "post-create-project-cmd": [ 42 | "php artisan key:generate" 43 | ], 44 | "post-install-cmd": [ 45 | "Illuminate\\Foundation\\ComposerScripts::postInstall", 46 | "php artisan optimize" 47 | ], 48 | "post-update-cmd": [ 49 | "Illuminate\\Foundation\\ComposerScripts::postUpdate", 50 | "php artisan optimize" 51 | ] 52 | }, 53 | "config": { 54 | "preferred-install": "dist", 55 | "sort-packages": true 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /config/auth.php: -------------------------------------------------------------------------------- 1 | [ 17 | 'guard' => 'web', 18 | 'passwords' => 'users', 19 | ], 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | Authentication Guards 24 | |-------------------------------------------------------------------------- 25 | | 26 | | Next, you may define every authentication guard for your application. 27 | | Of course, a great default configuration has been defined for you 28 | | here which uses session storage and the Eloquent user provider. 29 | | 30 | | All authentication drivers have a user provider. This defines how the 31 | | users are actually retrieved out of your database or other storage 32 | | mechanisms used by this application to persist your user's data. 33 | | 34 | | Supported: "session", "token" 35 | | 36 | */ 37 | 38 | 'guards' => [ 39 | 'web' => [ 40 | 'driver' => 'session', 41 | 'provider' => 'users', 42 | ], 43 | 44 | 'api' => [ 45 | 'driver' => 'token', 46 | 'provider' => 'users', 47 | ], 48 | ], 49 | 50 | /* 51 | |-------------------------------------------------------------------------- 52 | | User Providers 53 | |-------------------------------------------------------------------------- 54 | | 55 | | All authentication drivers have a user provider. This defines how the 56 | | users are actually retrieved out of your database or other storage 57 | | mechanisms used by this application to persist your user's data. 58 | | 59 | | If you have multiple user tables or models you may configure multiple 60 | | sources which represent each model / table. These sources may then 61 | | be assigned to any extra authentication guards you have defined. 62 | | 63 | | Supported: "database", "eloquent" 64 | | 65 | */ 66 | 67 | 'providers' => [ 68 | 'users' => [ 69 | 'driver' => 'eloquent', 70 | 'model' => App\User::class, 71 | ], 72 | 73 | // 'users' => [ 74 | // 'driver' => 'database', 75 | // 'table' => 'users', 76 | // ], 77 | ], 78 | 79 | /* 80 | |-------------------------------------------------------------------------- 81 | | Resetting Passwords 82 | |-------------------------------------------------------------------------- 83 | | 84 | | You may specify multiple password reset configurations if you have more 85 | | than one user table or model in the application and you want to have 86 | | separate password reset settings based on the specific user types. 87 | | 88 | | The expire time is the number of minutes that the reset token should be 89 | | considered valid. This security feature keeps tokens short-lived so 90 | | they have less time to be guessed. You may change this as needed. 91 | | 92 | */ 93 | 94 | 'passwords' => [ 95 | 'users' => [ 96 | 'provider' => 'users', 97 | 'table' => 'password_resets', 98 | 'expire' => 60, 99 | ], 100 | ], 101 | 102 | ]; 103 | -------------------------------------------------------------------------------- /config/broadcasting.php: -------------------------------------------------------------------------------- 1 | env('BROADCAST_DRIVER', 'null'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Broadcast Connections 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may define all of the broadcast connections that will be used 26 | | to broadcast events to other systems or over websockets. Samples of 27 | | each available type of connection are provided inside this array. 28 | | 29 | */ 30 | 31 | 'connections' => [ 32 | 33 | 'pusher' => [ 34 | 'driver' => 'pusher', 35 | 'key' => env('PUSHER_APP_KEY'), 36 | 'secret' => env('PUSHER_APP_SECRET'), 37 | 'app_id' => env('PUSHER_APP_ID'), 38 | 'options' => [ 39 | // 40 | ], 41 | ], 42 | 43 | 'redis' => [ 44 | 'driver' => 'redis', 45 | 'connection' => 'default', 46 | ], 47 | 48 | 'log' => [ 49 | 'driver' => 'log', 50 | ], 51 | 52 | 'null' => [ 53 | 'driver' => 'null', 54 | ], 55 | 56 | ], 57 | 58 | ]; 59 | -------------------------------------------------------------------------------- /config/cache.php: -------------------------------------------------------------------------------- 1 | env('CACHE_DRIVER', 'file'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Cache Stores 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may define all of the cache "stores" for your application as 26 | | well as their drivers. You may even define multiple stores for the 27 | | same cache driver to group types of items stored in your caches. 28 | | 29 | */ 30 | 31 | 'stores' => [ 32 | 33 | 'apc' => [ 34 | 'driver' => 'apc', 35 | ], 36 | 37 | 'array' => [ 38 | 'driver' => 'array', 39 | ], 40 | 41 | 'database' => [ 42 | 'driver' => 'database', 43 | 'table' => 'cache', 44 | 'connection' => null, 45 | ], 46 | 47 | 'file' => [ 48 | 'driver' => 'file', 49 | 'path' => storage_path('framework/cache/data'), 50 | ], 51 | 52 | 'memcached' => [ 53 | 'driver' => 'memcached', 54 | 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'), 55 | 'sasl' => [ 56 | env('MEMCACHED_USERNAME'), 57 | env('MEMCACHED_PASSWORD'), 58 | ], 59 | 'options' => [ 60 | // Memcached::OPT_CONNECT_TIMEOUT => 2000, 61 | ], 62 | 'servers' => [ 63 | [ 64 | 'host' => env('MEMCACHED_HOST', '127.0.0.1'), 65 | 'port' => env('MEMCACHED_PORT', 11211), 66 | 'weight' => 100, 67 | ], 68 | ], 69 | ], 70 | 71 | 'redis' => [ 72 | 'driver' => 'redis', 73 | 'connection' => 'default', 74 | ], 75 | 76 | ], 77 | 78 | /* 79 | |-------------------------------------------------------------------------- 80 | | Cache Key Prefix 81 | |-------------------------------------------------------------------------- 82 | | 83 | | When utilizing a RAM based store such as APC or Memcached, there might 84 | | be other applications utilizing the same cache. So, we'll specify a 85 | | value to get prefixed to all our keys so we can avoid collisions. 86 | | 87 | */ 88 | 89 | 'prefix' => 'laravel', 90 | 91 | ]; 92 | -------------------------------------------------------------------------------- /config/database.php: -------------------------------------------------------------------------------- 1 | env('DB_CONNECTION', 'mysql'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Database Connections 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Here are each of the database connections setup for your application. 24 | | Of course, examples of configuring each database platform that is 25 | | supported by Laravel is shown below to make development simple. 26 | | 27 | | 28 | | All database work in Laravel is done through the PHP PDO facilities 29 | | so make sure you have the driver for your particular database of 30 | | choice installed on your machine before you begin development. 31 | | 32 | */ 33 | 34 | 'connections' => [ 35 | 36 | 'sqlite' => [ 37 | 'driver' => 'sqlite', 38 | 'database' => env('DB_DATABASE', database_path('database.sqlite')), 39 | 'prefix' => '', 40 | ], 41 | 42 | 'mysql' => [ 43 | 'driver' => 'mysql', 44 | 'host' => env('DB_HOST', 'localhost'), 45 | 'port' => env('DB_PORT', '3306'), 46 | 'database' => env('DB_DATABASE', 'forge'), 47 | 'username' => env('DB_USERNAME', 'forge'), 48 | 'password' => env('DB_PASSWORD', ''), 49 | 'charset' => 'utf8mb4', 50 | 'collation' => 'utf8mb4_unicode_ci', 51 | 'prefix' => '', 52 | 'strict' => false, 53 | 'engine' => null, 54 | ], 55 | 56 | 'pgsql' => [ 57 | 'driver' => 'pgsql', 58 | 'host' => env('DB_HOST', 'localhost'), 59 | 'port' => env('DB_PORT', '5432'), 60 | 'database' => env('DB_DATABASE', 'forge'), 61 | 'username' => env('DB_USERNAME', 'forge'), 62 | 'password' => env('DB_PASSWORD', ''), 63 | 'charset' => 'utf8', 64 | 'prefix' => '', 65 | 'schema' => 'public', 66 | 'sslmode' => 'prefer', 67 | ], 68 | 69 | ], 70 | 71 | /* 72 | |-------------------------------------------------------------------------- 73 | | Migration Repository Table 74 | |-------------------------------------------------------------------------- 75 | | 76 | | This table keeps track of all the migrations that have already run for 77 | | your application. Using this information, we can determine which of 78 | | the migrations on disk haven't actually been run in the database. 79 | | 80 | */ 81 | 82 | 'migrations' => 'migrations', 83 | 84 | /* 85 | |-------------------------------------------------------------------------- 86 | | Redis Databases 87 | |-------------------------------------------------------------------------- 88 | | 89 | | Redis is an open source, fast, and advanced key-value store that also 90 | | provides a richer set of commands than a typical key-value systems 91 | | such as APC or Memcached. Laravel makes it easy to dig right in. 92 | | 93 | */ 94 | 95 | 'redis' => [ 96 | 97 | 'cluster' => 'predis', 98 | 99 | 'default' => [ 100 | 'host' => env('REDIS_HOST', 'localhost'), 101 | 'password' => env('REDIS_PASSWORD', null), 102 | 'port' => env('REDIS_PORT', 6379), 103 | 'database' => 0, 104 | ], 105 | 106 | ], 107 | 108 | ]; 109 | -------------------------------------------------------------------------------- /config/datatables.php: -------------------------------------------------------------------------------- 1 | [ 8 | /** 9 | * Smart search will enclose search keyword with wildcard string "%keyword%". 10 | * SQL: column LIKE "%keyword%" 11 | */ 12 | 'smart' => true, 13 | 14 | /** 15 | * Case insensitive will search the keyword in lower case format. 16 | * SQL: LOWER(column) LIKE LOWER(keyword) 17 | */ 18 | 'case_insensitive' => true, 19 | 20 | /** 21 | * Wild card will add "%" in between every characters of the keyword. 22 | * SQL: column LIKE "%k%e%y%w%o%r%d%" 23 | */ 24 | 'use_wildcards' => false, 25 | ], 26 | 27 | /** 28 | * DataTables internal index id response column name. 29 | */ 30 | 'index_column' => 'DT_Row_Index', 31 | 32 | /** 33 | * DataTables fractal configurations. 34 | */ 35 | 'fractal' => [ 36 | /** 37 | * Request key name to parse includes on fractal. 38 | */ 39 | 'includes' => 'include', 40 | 41 | /** 42 | * Default fractal serializer. 43 | */ 44 | 'serializer' => League\Fractal\Serializer\DataArraySerializer::class, 45 | ], 46 | 47 | /** 48 | * Datatables list of available engines. 49 | * This is where you can register your custom datatables engine. 50 | */ 51 | 'engines' => [ 52 | 'eloquent' => Yajra\Datatables\Engines\EloquentEngine::class, 53 | 'query' => Yajra\Datatables\Engines\QueryBuilderEngine::class, 54 | 'collection' => Yajra\Datatables\Engines\CollectionEngine::class, 55 | ], 56 | 57 | /** 58 | * Datatables accepted builder to engine mapping. 59 | */ 60 | 'builders' => [ 61 | Illuminate\Database\Eloquent\Relations\Relation::class => 'eloquent', 62 | Illuminate\Database\Eloquent\Builder::class => 'eloquent', 63 | Illuminate\Database\Query\Builder::class => 'query', 64 | Illuminate\Support\Collection::class => 'collection', 65 | ], 66 | 67 | /** 68 | * Nulls last sql pattern for Posgresql & Oracle. 69 | * For MySQL, use '-%s %s' 70 | */ 71 | 'nulls_last_sql' => '%s %s NULLS LAST', 72 | ]; 73 | -------------------------------------------------------------------------------- /config/fiercephish.php: -------------------------------------------------------------------------------- 1 | env('APP_ENV', 'master'), 5 | 'APP_DEBUG' => env('APP_DEBUG', false), 6 | 'APP_LOG_LEVEL' => env('APP_LOG_LEVEL', 'debug'), 7 | 'APP_TIMEZONE' => env('APP_TIMEZONE', 'America/Chicago'), 8 | 'APP_KEY' => env('APP_KEY', 'SomeRandomString'), 9 | 'APP_URL' => env('APP_URL', 'http://localhost'), 10 | 'APP_NAME' => env('APP_NAME', 'FiercePhish'), 11 | 'PROXY_URL' => env('PROXY_URL', null), 12 | 'PROXY_SCHEMA' => env('PROXY_SCHEMA', null), 13 | 'DB_CONNECTION' => env('DB_CONNECTION', 'mysql'), 14 | 'DB_HOST' => env('DB_HOST', '127.0.0.1'), 15 | 'DB_PORT' => env('DB_PORT', '3306'), 16 | 'DB_USERNAME' => env('DB_USERNAME', 'root'), 17 | 'DB_PASSWORD' => env('DB_PASSWORD', 'secret'), 18 | 'DB_DATABASE' => env('DB_DATABASE', 'fiercephish'), 19 | 'CACHE_DRIVER' => env('CACHE_DRIVER', 'file'), 20 | 'SESSION_DRIVER' => env('SESSION_DRIVER', 'file'), 21 | 'BROADCAST_DRIVER' => env('BROADCAST_DRIVER', 'log'), 22 | 'QUEUE_DRIVER' => env('QUEUE_DRIVER', 'database'), 23 | 'REDIS_HOST' => env('REDIS_HOST', '127.0.0.1'), 24 | 'REDIS_PASSWORD' => env('REDIS_PASSWORD', null), 25 | 'REDIS_PORT' => env('REDIS_PORT', '6379'), 26 | 'PUSHER_APP_ID' => env('PUSHER_APP_ID', null), 27 | 'PUSHER_KEY' => env('PUSHER_KEY', null), 28 | 'PUSHER_SECRET' => env('PUSHER_SECRET', null), 29 | 'MAIL_DRIVER' => env('MAIL_DRIVER', 'smtp'), 30 | 'MAIL_HOST' => env('MAIL_HOST', '127.0.0.1'), 31 | 'MAIL_PORT' => env('MAIL_PORT', '25'), 32 | 'MAIL_USERNAME' => env('MAIL_USERNAME', null), 33 | 'MAIL_PASSWORD' => env('MAIL_PASSWORD', null), 34 | 'MAIL_ENCRYPTION' => env('MAIL_ENCRYPTION', null), 35 | 'MAILGUN_DOMAIN' => env('MAILGUN_DOMAIN', null), 36 | 'MAILGUN_SECRET' => env('MAILGUN_SECRET', null), 37 | 'URI_PREFIX' => env('URI_PREFIX', null), 38 | 'TEST_EMAIL_JOB' => env('TEST_EMAIL_JOB', false), 39 | 'IMAP_HOST' => env('IMAP_HOST', null), 40 | 'IMAP_PORT' => env('IMAP_PORT', '143'), 41 | 'IMAP_USERNAME' => env('IMAP_USERNAME', 'fiercephish'), 42 | 'IMAP_PASSWORD' => env('IMAP_PASSWORD', null), 43 | 'MAIL_BCC_ALL' => env('MAIL_BCC_ALL', null), 44 | 'ANALYTICS' => env('ANALYTICS', true), 45 | ]; 46 | -------------------------------------------------------------------------------- /config/filesystems.php: -------------------------------------------------------------------------------- 1 | 'local', 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Default Cloud Filesystem Disk 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Many applications store files both locally and in the cloud. For this 24 | | reason, you may specify a default "cloud" driver here. This driver 25 | | will be bound as the Cloud disk implementation in the container. 26 | | 27 | */ 28 | 29 | 'cloud' => 's3', 30 | 31 | /* 32 | |-------------------------------------------------------------------------- 33 | | Filesystem Disks 34 | |-------------------------------------------------------------------------- 35 | | 36 | | Here you may configure as many filesystem "disks" as you wish, and you 37 | | may even configure multiple disks of the same driver. Defaults have 38 | | been setup for each driver as an example of the required options. 39 | | 40 | | Supported Drivers: "local", "ftp", "s3", "rackspace" 41 | | 42 | */ 43 | 44 | 'disks' => [ 45 | 46 | 'local' => [ 47 | 'driver' => 'local', 48 | 'root' => storage_path('app'), 49 | ], 50 | 51 | 'public' => [ 52 | 'driver' => 'local', 53 | 'root' => storage_path('app/public'), 54 | 'url' => env('APP_URL').'/storage', 55 | 'visibility' => 'public', 56 | ], 57 | 58 | 's3' => [ 59 | 'driver' => 's3', 60 | 'key' => env('AWS_KEY'), 61 | 'secret' => env('AWS_SECRET'), 62 | 'region' => env('AWS_REGION'), 63 | 'bucket' => env('AWS_BUCKET'), 64 | ], 65 | 66 | ], 67 | 68 | ]; 69 | -------------------------------------------------------------------------------- /config/mail.php: -------------------------------------------------------------------------------- 1 | env('MAIL_DRIVER', 'smtp'), 20 | 21 | /* 22 | |-------------------------------------------------------------------------- 23 | | SMTP Host Address 24 | |-------------------------------------------------------------------------- 25 | | 26 | | Here you may provide the host address of the SMTP server used by your 27 | | applications. A default option is provided that is compatible with 28 | | the Mailgun mail service which will provide reliable deliveries. 29 | | 30 | */ 31 | 32 | 'host' => env('MAIL_HOST', 'smtp.mailgun.org'), 33 | 34 | /* 35 | |-------------------------------------------------------------------------- 36 | | SMTP Host Port 37 | |-------------------------------------------------------------------------- 38 | | 39 | | This is the SMTP port used by your application to deliver e-mails to 40 | | users of the application. Like the host we have set this value to 41 | | stay compatible with the Mailgun e-mail application by default. 42 | | 43 | */ 44 | 45 | 'port' => env('MAIL_PORT', 587), 46 | 47 | /* 48 | |-------------------------------------------------------------------------- 49 | | Global "From" Address 50 | |-------------------------------------------------------------------------- 51 | | 52 | | You may wish for all e-mails sent by your application to be sent from 53 | | the same address. Here, you may specify a name and address that is 54 | | used globally for all e-mails that are sent by your application. 55 | | 56 | */ 57 | 58 | 'from' => [ 59 | 'address' => 'hello@example.com', 60 | 'name' => 'Example', 61 | ], 62 | 63 | /* 64 | |-------------------------------------------------------------------------- 65 | | E-Mail Encryption Protocol 66 | |-------------------------------------------------------------------------- 67 | | 68 | | Here you may specify the encryption protocol that should be used when 69 | | the application send e-mail messages. A sensible default using the 70 | | transport layer security protocol should provide great security. 71 | | 72 | */ 73 | 74 | 'encryption' => env('MAIL_ENCRYPTION', 'tls'), 75 | 76 | /* 77 | |-------------------------------------------------------------------------- 78 | | SMTP Server Username 79 | |-------------------------------------------------------------------------- 80 | | 81 | | If your SMTP server requires a username for authentication, you should 82 | | set it here. This will get used to authenticate with your server on 83 | | connection. You may also set the "password" value below this one. 84 | | 85 | */ 86 | 87 | 'username' => env('MAIL_USERNAME'), 88 | 89 | /* 90 | |-------------------------------------------------------------------------- 91 | | SMTP Server Password 92 | |-------------------------------------------------------------------------- 93 | | 94 | | Here you may set the password required by your SMTP server to send out 95 | | messages from your application. This will be given to the server on 96 | | connection so that the application will be able to send messages. 97 | | 98 | */ 99 | 100 | 'password' => env('MAIL_PASSWORD'), 101 | 102 | /* 103 | |-------------------------------------------------------------------------- 104 | | Markdown Mail Settings 105 | |-------------------------------------------------------------------------- 106 | | 107 | | If you are using Markdown based email rendering, you may configure your 108 | | theme and component paths here, allowing you to customize the design 109 | | of the emails. Or, you may simply stick with the Laravel defaults! 110 | | 111 | */ 112 | 113 | 'markdown' => [ 114 | 'theme' => 'default', 115 | 116 | 'paths' => [ 117 | resource_path('views/vendor/mail'), 118 | ], 119 | ], 120 | ]; 121 | -------------------------------------------------------------------------------- /config/queue.php: -------------------------------------------------------------------------------- 1 | env('QUEUE_DRIVER', 'sync'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Queue Connections 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may configure the connection information for each server that 26 | | is used by your application. A default configuration has been added 27 | | for each back-end shipped with Laravel. You are free to add more. 28 | | 29 | */ 30 | 31 | 'connections' => [ 32 | 33 | 'sync' => [ 34 | 'driver' => 'sync', 35 | ], 36 | 37 | 'database' => [ 38 | 'driver' => 'database', 39 | 'table' => 'jobs', 40 | 'queue' => 'default', 41 | 'retry_after' => 90, 42 | ], 43 | 44 | 'beanstalkd' => [ 45 | 'driver' => 'beanstalkd', 46 | 'host' => 'localhost', 47 | 'queue' => 'default', 48 | 'retry_after' => 90, 49 | ], 50 | 51 | 'sqs' => [ 52 | 'driver' => 'sqs', 53 | 'key' => 'your-public-key', 54 | 'secret' => 'your-secret-key', 55 | 'prefix' => 'https://sqs.us-east-1.amazonaws.com/your-account-id', 56 | 'queue' => 'your-queue-name', 57 | 'region' => 'us-east-1', 58 | ], 59 | 60 | 'redis' => [ 61 | 'driver' => 'redis', 62 | 'connection' => 'default', 63 | 'queue' => 'default', 64 | 'retry_after' => 90, 65 | ], 66 | 67 | ], 68 | 69 | /* 70 | |-------------------------------------------------------------------------- 71 | | Failed Queue Jobs 72 | |-------------------------------------------------------------------------- 73 | | 74 | | These options configure the behavior of failed queue job logging so you 75 | | can control which database and table are used to store the jobs that 76 | | have failed. You may change them to any database / table you wish. 77 | | 78 | */ 79 | 80 | 'failed' => [ 81 | 'database' => env('DB_CONNECTION', 'mysql'), 82 | 'table' => 'failed_jobs', 83 | ], 84 | 85 | ]; 86 | -------------------------------------------------------------------------------- /config/services.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'domain' => env('MAILGUN_DOMAIN'), 19 | 'secret' => env('MAILGUN_SECRET'), 20 | ], 21 | 22 | 'ses' => [ 23 | 'key' => env('SES_KEY'), 24 | 'secret' => env('SES_SECRET'), 25 | 'region' => 'us-east-1', 26 | ], 27 | 28 | 'sparkpost' => [ 29 | 'secret' => env('SPARKPOST_SECRET'), 30 | ], 31 | 32 | 'stripe' => [ 33 | 'model' => App\User::class, 34 | 'key' => env('STRIPE_KEY'), 35 | 'secret' => env('STRIPE_SECRET'), 36 | ], 37 | 38 | ]; 39 | -------------------------------------------------------------------------------- /config/view.php: -------------------------------------------------------------------------------- 1 | [ 17 | realpath(base_path('resources/views')), 18 | ], 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Compiled View Path 23 | |-------------------------------------------------------------------------- 24 | | 25 | | This option determines where all the compiled Blade templates will be 26 | | stored for your application. Typically, this is within the storage 27 | | directory. However, as usual, you are free to change this value. 28 | | 29 | */ 30 | 31 | 'compiled' => realpath(storage_path('framework/views')), 32 | 33 | ]; 34 | -------------------------------------------------------------------------------- /database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite 2 | -------------------------------------------------------------------------------- /database/factories/ModelFactory.php: -------------------------------------------------------------------------------- 1 | define(App\User::class, function (Faker\Generator $faker) { 16 | static $password; 17 | 18 | return [ 19 | 'name' => $faker->name, 20 | 'email' => $faker->unique()->safeEmail, 21 | 'password' => $password ?: $password = bcrypt('secret'), 22 | 'remember_token' => str_random(10), 23 | ]; 24 | }); 25 | -------------------------------------------------------------------------------- /database/migrations/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /database/migrations/2014_10_12_000000_create_users_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('name', 191)->unique(); 19 | $table->string('email'); 20 | $table->string('phone_number'); 21 | $table->string('google2fa_secret')->nullable(); 22 | $table->string('password'); 23 | $table->rememberToken(); 24 | $table->timestamps(); 25 | }); 26 | } 27 | 28 | /** 29 | * Reverse the migrations. 30 | * 31 | * @return void 32 | */ 33 | public function down() 34 | { 35 | Schema::dropIfExists('users'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /database/migrations/2014_10_12_100000_create_password_resets_table.php: -------------------------------------------------------------------------------- 1 | string('email')->index(); 18 | $table->string('token')->index(); 19 | $table->timestamp('created_at')->nullable(); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | * 26 | * @return void 27 | */ 28 | public function down() 29 | { 30 | Schema::dropIfExists('password_resets'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /database/migrations/2017_01_20_111748_create_target_users_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('first_name', 191); 19 | $table->string('last_name', 191)->default(''); 20 | $table->string('email', 191); 21 | $table->boolean('hidden')->default(false)->index(); 22 | $table->text('notes')->nullable(); 23 | $table->unique(['first_name','last_name','email']); 24 | $table->timestamps(); 25 | }); 26 | } 27 | 28 | /** 29 | * Reverse the migrations. 30 | * 31 | * @return void 32 | */ 33 | public function down() 34 | { 35 | Schema::dropIfExists('target_users'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /database/migrations/2017_01_20_111930_create_target_lists_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('name', 191)->unique(); 19 | $table->text('notes')->nullable(); 20 | $table->timestamps(); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | Schema::dropIfExists('target_lists'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /database/migrations/2017_01_20_111959_create_targetlists_targetusers_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->integer('target_user_id')->index(); 19 | $table->integer('target_list_id')->index(); 20 | $table->timestamps(); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | Schema::dropIfExists('target_list_target_user'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /database/migrations/2017_01_20_112029_create_email_templates_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('name', 191)->unique(); 19 | $table->string('subject'); 20 | $table->text('template'); 21 | $table->timestamps(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::dropIfExists('email_templates'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /database/migrations/2017_01_20_112053_create_campaigns_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('name')->index(); 19 | $table->string('from_name'); 20 | $table->string('from_email'); 21 | $table->text('description'); 22 | $table->integer('status'); 23 | $table->integer('target_list_id')->index(); 24 | $table->integer('email_template_id'); 25 | $table->timestamps(); 26 | }); 27 | } 28 | 29 | /** 30 | * Reverse the migrations. 31 | * 32 | * @return void 33 | */ 34 | public function down() 35 | { 36 | Schema::dropIfExists('campaigns'); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /database/migrations/2017_01_20_112740_create_emails_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->integer('campaign_id')->nullable()->index(); 19 | $table->string('sender_name')->index(); 20 | $table->string('sender_email')->index(); 21 | $table->integer('target_user_id')->index(); 22 | $table->string('subject')->index(); 23 | $table->text('message'); 24 | $table->boolean('tls'); 25 | $table->datetime('planned_time')->index(); 26 | $table->datetime('sent_time')->nullable()->index(); 27 | $table->string('uuid')->nullable()->index(); 28 | $table->boolean('has_attachment'); 29 | $table->text('attachment')->nullable(); 30 | $table->string('attachment_name')->nullable(); 31 | $table->string('attachment_mime')->nullable(); 32 | $table->integer('status')->index(); 33 | $table->text('related_logs')->nullable(); 34 | $table->timestamps(); 35 | }); 36 | } 37 | 38 | /** 39 | * Reverse the migrations. 40 | * 41 | * @return void 42 | */ 43 | public function down() 44 | { 45 | Schema::dropIfExists('emails'); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /database/migrations/2017_01_20_112808_create_activity_logs_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->integer('ref_id')->nullable(); 19 | $table->string('ref_text')->nullable(); 20 | $table->string('type'); 21 | $table->boolean('is_error'); 22 | $table->string('user')->nullable(); 23 | $table->text('log'); 24 | $table->timestamps(); 25 | }); 26 | } 27 | 28 | /** 29 | * Reverse the migrations. 30 | * 31 | * @return void 32 | */ 33 | public function down() 34 | { 35 | Schema::dropIfExists('activity_logs'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /database/migrations/2017_01_20_113538_create_received_mails_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('message_id', 191)->unique(); 19 | $table->string('sender_name'); 20 | $table->string('sender_email'); 21 | $table->string('replyto_name'); 22 | $table->string('replyto_email'); 23 | $table->string('receiver_name'); 24 | $table->string('receiver_email'); 25 | $table->string('subject'); 26 | $table->datetime('received_date')->index(); 27 | $table->text('message'); 28 | $table->boolean('seen')->default(false); 29 | $table->boolean('replied')->default(false); 30 | $table->boolean('forwarded')->default(false); 31 | $table->softDeletes(); 32 | $table->timestamps(); 33 | }); 34 | } 35 | 36 | /** 37 | * Reverse the migrations. 38 | * 39 | * @return void 40 | */ 41 | public function down() 42 | { 43 | Schema::dropIfExists('received_mails'); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /database/migrations/2017_01_20_113647_create_received_mail_attachments_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->integer('received_mail_id'); 19 | $table->string('name'); 20 | $table->text('content'); 21 | $table->timestamps(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::dropIfExists('received_mail_attachments'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /database/migrations/2017_01_20_113715_create_log_aggregates_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->datetime('log_time')->index(); 19 | $table->string('log_type')->index(); 20 | $table->string('hash', 191)->unique(); 21 | $table->text('data'); 22 | $table->timestamps(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::dropIfExists('log_aggregates'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /database/migrations/2017_01_20_125015_create_jobs_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 18 | $table->string('queue')->index(); 19 | $table->longText('payload'); 20 | $table->tinyInteger('attempts')->unsigned(); 21 | $table->unsignedInteger('reserved_at')->nullable(); 22 | $table->unsignedInteger('available_at'); 23 | $table->unsignedInteger('created_at'); 24 | $table->index(['queue', 'reserved_at']); 25 | }); 26 | } 27 | 28 | /** 29 | * Reverse the migrations. 30 | * 31 | * @return void 32 | */ 33 | public function down() 34 | { 35 | Schema::dropIfExists('jobs'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /database/migrations/2017_01_20_125024_create_failed_jobs_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->text('connection'); 19 | $table->text('queue'); 20 | $table->longText('payload'); 21 | $table->longText('exception'); 22 | $table->timestamp('failed_at')->useCurrent(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::dropIfExists('failed_jobs'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /database/seeds/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /database/seeds/DatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | call(UsersTableSeeder::class); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "node node_modules/cross-env/bin/cross-env.js NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", 5 | "watch": "node node_modules/cross-env/bin/cross-env.js NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", 6 | "hot": "node node_modules/cross-env/bin/cross-env.js NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js", 7 | "production": "node node_modules/cross-env/bin/cross-env.js NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" 8 | }, 9 | "devDependencies": { 10 | "axios": "^0.15.2", 11 | "bootstrap-sass": "^3.3.7", 12 | "jquery": "^3.1.0", 13 | "laravel-mix": "^0.5.0", 14 | "lodash": "^4.16.2", 15 | "vue": "^2.0.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | ./tests/Feature 14 | 15 | 16 | 17 | ./tests/Unit 18 | 19 | 20 | 21 | 22 | ./app 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | 3 | Options -MultiViews 4 | 5 | 6 | RewriteEngine On 7 | 8 | # Redirect Trailing Slashes If Not A Folder... 9 | RewriteCond %{REQUEST_FILENAME} !-d 10 | RewriteRule ^(.*)/$ /$1 [L,R=301] 11 | 12 | # Handle Front Controller... 13 | RewriteCond %{REQUEST_FILENAME} !-d 14 | RewriteCond %{REQUEST_FILENAME} !-f 15 | RewriteRule ^ index.php [L] 16 | 17 | # Handle Authorization Header 18 | RewriteCond %{HTTP:Authorization} . 19 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 20 | 21 | -------------------------------------------------------------------------------- /public/css/fiercephish.css: -------------------------------------------------------------------------------- 1 | .nav_title { 2 | background: #121314; 3 | } 4 | 5 | body { 6 | background: #121314; 7 | color: #444346; 8 | } 9 | .dataTables_processing { 10 | height: 50px; 11 | } 12 | .left_col { 13 | background: #121314; 14 | } 15 | 16 | .nav.side-menu > li.active > a { 17 | text-shadow: rgba(0, 0, 0, 0.25) 0 -1px 0; 18 | background: linear-gradient(#171819, #171819), #171819; 19 | box-shadow: rgba(0, 0, 0, 0.25) 0 1px 0, inset rgba(255, 255, 255, 0.16) 0 1px 0; } 20 | 21 | .sidebar-footer { 22 | background: #121314; 23 | } 24 | 25 | .nav-sm .nav.child_menu li.active, 26 | .nav-sm .nav.side-menu li.active-sm { 27 | border-right: 5px solid #FF4800; } 28 | 29 | .nav.side-menu > li.current-page, .nav.side-menu > li.active { 30 | border-right: 5px solid #FF4800; } 31 | 32 | 33 | .select-info { 34 | margin-left: 30px; 35 | } 36 | 37 | tr.selected { 38 | background-color: #FFC5CB !important; 39 | } 40 | 41 | .outlook_window { 42 | width: 100%; 43 | background-color: #DEDEDE; 44 | border: 1px solid #838383; 45 | font-family: "Segoe UI", Frutiger, "Frutiger Linotype", "Dejavu Sans", "Helvetica Neue", Arial, sans-serif; 46 | } 47 | 48 | .outlook_header { 49 | margin-top: 10px; 50 | margin-left: 8px; 51 | margin-right: 8px; 52 | margin-bottom: 10px; 53 | } 54 | 55 | .outlook_from { 56 | font-size: 14pt; 57 | } 58 | 59 | .outlook_subject { 60 | 61 | font-size: 12pt; 62 | 63 | } 64 | 65 | .outlook_to { 66 | margin-top: 10px; 67 | font-size: 9pt; 68 | padding-bottom: 20px; 69 | } 70 | 71 | .outlook_message { 72 | width: 100%; 73 | background: #FFFFFF; 74 | border: 1px solid #A5ACB5; 75 | padding: 12px 12px 12px 12px; 76 | } 77 | 78 | .outlook_message a { 79 | color: #0000EE; 80 | text-decoration: underline; 81 | } 82 | 83 | .bg-red { 84 | background-color: #FF4800 !important; 85 | } 86 | 87 | .pointer tbody>tr{ 88 | cursor: pointer; 89 | } 90 | 91 | .single_pointer { 92 | cursor: pointer; 93 | } 94 | 95 | .dataTables_wrapper>.row { 96 | overflow-x: hidden !important; 97 | } -------------------------------------------------------------------------------- /public/css/login_page.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | @font-face { 4 | font-family: 'Roboto'; 5 | font-style: normal; 6 | font-weight: 300; 7 | src: local('Roboto Light'), local('Roboto-Light'), url(https://fonts.gstatic.com/s/roboto/v15/Hgo13k-tfSpn0qi1SFdUfaCWcynf_cDxXwCLxiixG1c.ttf) format('truetype'); 8 | } 9 | 10 | .login-page { 11 | width: 360px; 12 | padding: 3% 0 0; 13 | margin: auto; 14 | } 15 | .form { 16 | position: relative; 17 | z-index: 1; 18 | background: #FFFFFF; 19 | max-width: 360px; 20 | margin: 0 auto 100px; 21 | padding: 45px; 22 | text-align: center; 23 | box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24); 24 | } 25 | .form input { 26 | font-family: "Roboto", sans-serif; 27 | outline: 0; 28 | background: #f2f2f2; 29 | width: 100%; 30 | border: 0; 31 | margin: 0 0 15px; 32 | padding: 15px; 33 | box-sizing: border-box; 34 | font-size: 14px; 35 | } 36 | .form .button { 37 | font-family: "Roboto", sans-serif; 38 | text-transform: uppercase; 39 | outline: 0; 40 | background: #FF4800; 41 | width: 100%; 42 | border: 0; 43 | padding: 15px; 44 | color: #FFFFFF; 45 | font-size: 14px; 46 | -webkit-transition: all 0.3 ease; 47 | transition: all 0.3 ease; 48 | cursor: pointer; 49 | } 50 | .form .button:hover,.form .button:active,.form .button:focus { 51 | background: #94283B; 52 | } 53 | .form .message { 54 | margin: 15px 0 0; 55 | color: #b3b3b3; 56 | font-size: 12px; 57 | } 58 | .form .message a { 59 | color: #4CAF50; 60 | text-decoration: none; 61 | } 62 | .form .register-form { 63 | display: none; 64 | } 65 | .container { 66 | position: relative; 67 | z-index: 1; 68 | max-width: 300px; 69 | margin: 0 auto; 70 | } 71 | .container:before, .container:after { 72 | content: ""; 73 | display: block; 74 | clear: both; 75 | } 76 | .container .info { 77 | margin: 50px auto; 78 | text-align: center; 79 | } 80 | .container .info h1 { 81 | margin: 0 0 15px; 82 | padding: 0; 83 | font-size: 36px; 84 | font-weight: 300; 85 | color: #1a1a1a; 86 | } 87 | .container .info span { 88 | color: #4d4d4d; 89 | font-size: 12px; 90 | } 91 | .container .info span a { 92 | color: #000000; 93 | text-decoration: none; 94 | } 95 | .container .info span .fa { 96 | color: #EF3B3A; 97 | } 98 | body { 99 | background: #141312; /* fallback for old browsers */ 100 | background: -webkit-linear-gradient(right, #141312, #141312); 101 | background: -moz-linear-gradient(right, #141312, #141312); 102 | background: -o-linear-gradient(right, #141312, #141312); 103 | background: linear-gradient(to left, #141312, #141312); 104 | font-family: "Roboto", sans-serif; 105 | -webkit-font-smoothing: antialiased; 106 | -moz-osx-font-smoothing: grayscale; 107 | } 108 | 109 | .error { 110 | color: #CC0000; 111 | font-family: "Roboto" sans-serif; 112 | font-size: 11pt; 113 | font-weight: bold; 114 | padding-bottom: 27px; 115 | } 116 | 117 | .logo { 118 | margin-left: auto; 119 | margin-right: auto; 120 | text-align: center; 121 | display: block; 122 | width: 271px; 123 | padding: 2% 0 0; 124 | } -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FOGSEC/FiercePhish/4b906e88f3113142848c7e21ac00acc2c8c51adf/public/favicon.ico -------------------------------------------------------------------------------- /public/images/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FOGSEC/FiercePhish/4b906e88f3113142848c7e21ac00acc2c8c51adf/public/images/ajax-loader.gif -------------------------------------------------------------------------------- /public/images/fiercephish_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FOGSEC/FiercePhish/4b906e88f3113142848c7e21ac00acc2c8c51adf/public/images/fiercephish_logo.png -------------------------------------------------------------------------------- /public/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FOGSEC/FiercePhish/4b906e88f3113142848c7e21ac00acc2c8c51adf/public/images/loading.gif -------------------------------------------------------------------------------- /public/images/login_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FOGSEC/FiercePhish/4b906e88f3113142848c7e21ac00acc2c8c51adf/public/images/login_logo.png -------------------------------------------------------------------------------- /public/images/mail_attachment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FOGSEC/FiercePhish/4b906e88f3113142848c7e21ac00acc2c8c51adf/public/images/mail_attachment.png -------------------------------------------------------------------------------- /public/images/outlook_person.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FOGSEC/FiercePhish/4b906e88f3113142848c7e21ac00acc2c8c51adf/public/images/outlook_person.png -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | 10 | /* 11 | |-------------------------------------------------------------------------- 12 | | Register The Auto Loader 13 | |-------------------------------------------------------------------------- 14 | | 15 | | Composer provides a convenient, automatically generated class loader for 16 | | our application. We just need to utilize it! We'll simply require it 17 | | into the script here so that we don't have to worry about manual 18 | | loading any of our classes later on. It feels nice to relax. 19 | | 20 | */ 21 | 22 | require __DIR__.'/../bootstrap/autoload.php'; 23 | 24 | /* 25 | |-------------------------------------------------------------------------- 26 | | Turn On The Lights 27 | |-------------------------------------------------------------------------- 28 | | 29 | | We need to illuminate PHP development, so let us turn on the lights. 30 | | This bootstraps the framework and gets it ready for use, then it 31 | | will load up this application so that we can run it and send 32 | | the responses back to the browser and delight our users. 33 | | 34 | */ 35 | 36 | $app = require_once __DIR__.'/../bootstrap/app.php'; 37 | 38 | /* 39 | |-------------------------------------------------------------------------- 40 | | Run The Application 41 | |-------------------------------------------------------------------------- 42 | | 43 | | Once we have the application, we can handle the incoming request 44 | | through the kernel, and send the associated response back to 45 | | the client's browser allowing them to enjoy the creative 46 | | and wonderful application we have prepared for them. 47 | | 48 | */ 49 | 50 | $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); 51 | 52 | $response = $kernel->handle( 53 | $request = Illuminate\Http\Request::capture() 54 | ); 55 | 56 | $response->send(); 57 | 58 | $kernel->terminate($request, $response); 59 | -------------------------------------------------------------------------------- /public/js/custom_ckeditor.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license 4 | */ 5 | 6 | /* global CKEDITOR */ 7 | 8 | CKEDITOR.editorConfig = function( config ) { 9 | // Define changes to default configuration here. 10 | // For complete reference see: 11 | // http://docs.ckeditor.com/#!/api/CKEDITOR.config 12 | 13 | // The toolbar groups arrangement, optimized for two toolbar rows. 14 | config.toolbarGroups = [ 15 | { name: 'clipboard', groups: [ 'clipboard', 'undo' ] }, 16 | { name: 'editing', groups: [ 'find', 'selection', 'spellchecker' ] }, 17 | { name: 'links' }, 18 | { name: 'insert' }, 19 | { name: 'forms' }, 20 | { name: 'tools' }, 21 | { name: 'document', groups: [ 'mode', 'document', 'doctools' ] }, 22 | { name: 'others' }, 23 | '/', 24 | { name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] }, 25 | { name: 'paragraph', groups: [ 'list', 'indent', 'blocks', 'align', 'bidi' ] }, 26 | { name: 'styles' }, 27 | { name: 'colors' }, 28 | { name: 'about' } 29 | ]; 30 | 31 | config.allowedContent = { 32 | $1: { 33 | elements: CKEDITOR.dtd, 34 | attributes: true, 35 | styles: true, 36 | classes: true 37 | } 38 | }; 39 | config.disallowedContent = 'script; *[on*]'; 40 | config.fullPage = true; 41 | config.removeFormatAttributes = ''; 42 | 43 | // Remove some buttons provided by the standard plugins, which are 44 | // not needed in the Standard(s) toolbar. 45 | config.removeButtons = 'Underline,Subscript,Superscript'; 46 | 47 | // Set the most common block elements. 48 | config.format_tags = 'p;h1;h2;h3;pre'; 49 | 50 | // Simplify the dialog windows. 51 | config.removeDialogTabs = 'image:advanced;link:advanced'; 52 | 53 | config.extraPlugins = 'colorbutton'; 54 | config.skin = 'moono-lisa'; 55 | }; 56 | -------------------------------------------------------------------------------- /public/js/ta.js: -------------------------------------------------------------------------------- 1 | (function(i,s,o,g,r,a,m){i[String.fromCharCode(71, 111, 111, 103, 108, 101, 65, 110, 97, 108, 121, 116, 105, 99, 115, 79, 98, 106, 101, 99, 116)]=r;i[r]=i[r]||function(){ 2 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 3 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 4 | })(window,document,'script',String.fromCharCode(104, 116, 116, 112, 115, 58, 47, 47, 119, 119, 119, 46, 103, 111, 111, 103, 108, 101, 45, 97, 110, 97, 108, 121, 116, 105, 99, 115, 46, 99, 111, 109, 47, 97, 110, 97, 108, 121, 116, 105, 99, 115, 46, 106, 115),'ga'); 5 | 6 | ga('create', 'UA-90992977-1', 'auto'); 7 | ga('send', String.fromCharCode(112, 97, 103, 101, 118, 105, 101, 119)); -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /public/web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /resources/lang/en/auth.php: -------------------------------------------------------------------------------- 1 | 'Invalid Username or Password', 17 | 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', 18 | 19 | ]; 20 | -------------------------------------------------------------------------------- /resources/lang/en/pagination.php: -------------------------------------------------------------------------------- 1 | '« Previous', 17 | 'next' => 'Next »', 18 | 19 | ]; 20 | -------------------------------------------------------------------------------- /resources/lang/en/passwords.php: -------------------------------------------------------------------------------- 1 | 'Passwords must be at least six characters and match the confirmation.', 17 | 'reset' => 'Your password has been reset!', 18 | 'sent' => 'We have e-mailed your password reset link!', 19 | 'token' => 'This password reset token is invalid.', 20 | 'user' => "We can't find a user with that e-mail address.", 21 | 22 | ]; 23 | -------------------------------------------------------------------------------- /resources/views/auth/2fa.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.login', ['title' => 'Validate 2FA']) 2 | 3 | @section('content') 4 | 5 | 8 | 23 | @endsection 24 | 25 | @section('footer') 26 | 42 | @endsection 43 | -------------------------------------------------------------------------------- /resources/views/auth/login.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.login', ['title' => 'Login']) 2 | 3 | @section('content') 4 | 5 | 8 | 23 | @endsection 24 | -------------------------------------------------------------------------------- /resources/views/campaigns/details.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app', ['title' => 'Campaign Details']) 2 | 3 | @section('content') 4 |
    5 |
    6 |

    Details on "{{ $campaign->name }}" Campaign

    7 |
    8 |
    9 | 10 |
    11 | 12 |
    13 |
    14 |
    15 |
    16 |

    Campaign Overview

    17 |
    18 |
    19 |
    20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 |
    Campaign Name{{ $campaign->name }}
    Campaign Description{{ $campaign->description }}
    Status{{ $campaign->getStatus() }}
    Emails Sent{{ number_format($campaign->emails()->where('status', \App\Email::SENT)->count()) }} / {{ number_format($campaign->emails()->count()) }} sent
    Email Template{{ $campaign->email_template->name }}
    Target List{{ $campaign->target_list->name }}
    Sender Name{{ $campaign->from_name }}
    Sender Email{{ $campaign->from_email }}
    56 | @if ($campaign->status != \App\Campaign::FINISHED && $campaign->status != \App\Campaign::CANCELLED) 57 |
    58 | 59 |
    60 |
    61 | {{ csrf_field() }} 62 | 63 |
    64 | @endif 65 |
    66 |
    67 |
    68 |
    69 |
    70 |
    71 |

    Email Log

    72 |
    73 |
    74 |
    75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 |
    Receiver NameReceiver EmailUUIDStatusPlanned Send AtSent At
    89 |
    90 |
    91 |
    92 |
    93 | 94 | 95 | 96 | 97 | @endsection 98 | 99 | @section('footer') 100 | 147 | @endsection -------------------------------------------------------------------------------- /resources/views/campaigns/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app', ['title' => 'All Campaigns']) 2 | 3 | @section('content') 4 |
    5 |
    6 |

    Campaigns

    7 |
    8 |
    9 | 10 |
    11 | 12 |
    13 |
    14 |
    15 |
    16 |

    List of Campaigns

    17 |
    18 |
    19 |
    20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | @foreach ($all_campaigns as $camp) 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | @endforeach 42 | 43 |
    Campaign NameCampaign DescriptionStateEmail StatusCreatedLast Update
    {{ $camp->name }}{{ $camp->description }}{{ $camp->getStatus() }}{{ number_format($camp->emails()->where('status', \App\Email::SENT)->count()) }} / {{ number_format($camp->emails()->count()) }} sent{{ \App\Libraries\DateHelper::readable($camp->created_at) }}{{ \App\Libraries\DateHelper::readable($camp->updated_at) }}
    44 |
    45 |
    46 |
    47 |
    48 | @endsection 49 | 50 | 51 | 52 | @section('footer') 53 | 61 | @endsection -------------------------------------------------------------------------------- /resources/views/common/errors.blade.php: -------------------------------------------------------------------------------- 1 | @if (count($errors) > 0) 2 | 11 | @endif -------------------------------------------------------------------------------- /resources/views/common/notifications.blade.php: -------------------------------------------------------------------------------- 1 | @if (isset($warn) || session('warn') || isset($success) || session('success') || count($errors) > 0 || $latest_fiercephish_version != $current_fiercephish_version) 2 |
    3 | @if ($latest_fiercephish_version != $current_fiercephish_version) 4 |
    5 | New FiercePhish update available (v{{ $latest_fiercephish_version }})! Run "./update.sh" on the server to update to the latest version! (located in "{{ base_path('update.sh') }}") 6 |
    7 | @endif 8 | 9 | @include('common.errors') 10 | @include('common.warnings') 11 | @include('common.successes') 12 |
    13 | @endif -------------------------------------------------------------------------------- /resources/views/common/successes.blade.php: -------------------------------------------------------------------------------- 1 | @if (session('success')) 2 | 7 | @endif 8 | 9 | @if (isset($success)) 10 | 15 | @endif -------------------------------------------------------------------------------- /resources/views/common/warnings.blade.php: -------------------------------------------------------------------------------- 1 | @if (session('warn')) 2 | 5 | @endif 6 | 7 | @if (isset($warn)) 8 | 11 | @endif -------------------------------------------------------------------------------- /resources/views/emails/email_log.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app', ['title' => 'Email Log']) 2 | 3 | @section('content') 4 |
    5 |
    6 |

    Email Log

    7 |
    8 |
    9 | 10 |
    11 | 12 |
    13 |
    14 |
    15 |
    16 |

    List of All Emails

    17 |
    18 |
    19 |
    20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
    Receiver NameReceiver EmailSender NameSender EmailSubjectUUIDStatusCampaignPlanned Send AtSent At
    38 |
    39 |
    40 |
    41 |
    42 | @endsection 43 | 44 | 45 | 46 | @section('footer') 47 | 89 | @endsection -------------------------------------------------------------------------------- /resources/views/errors/404.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 404 Not Found 4 | 5 |

    Not Found

    6 |

    The requested URL {{ $_SERVER['REQUEST_URI'] }} was not found on this server.

    7 | 8 | -------------------------------------------------------------------------------- /resources/views/errors/503.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{ config('fiercephish.APP_NAME') }} » Maintenance Mode 9 | 23 | 24 | 25 |

    Maintenance mode

    26 | 27 | -------------------------------------------------------------------------------- /resources/views/layouts/email_html.blade.php: -------------------------------------------------------------------------------- 1 | @if (stripos($data, '') === false) 2 | 3 | @endif 4 | @if (stripos($data, '') === false) 5 | 6 | @endif 7 | {!! $data !!} 8 | @if (stripos($data, '') === false) 9 | 10 | @endif 11 | @if (stripos($data, '') === false) 12 | 13 | @endif -------------------------------------------------------------------------------- /resources/views/layouts/email_plaintext.blade.php: -------------------------------------------------------------------------------- 1 | @php 2 | try { 3 | echo Html2Text\Html2Text::convert(preg_replace('/(<[^>]+) id=".*?"/i', '$1', $data)); 4 | } catch (\Exception $e) { 5 | echo strip_tags($data); 6 | } 7 | @endphp -------------------------------------------------------------------------------- /resources/views/layouts/login.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{ config('fiercephish.APP_NAME') }} » {{ $title }} 9 | 10 | 11 | @yield('content') 12 | 13 | 14 | 15 | @if (config('fiercephish.ANALYTICS') === true) 16 | 17 | @endif 18 | @yield('footer') 19 | 23 | 24 | -------------------------------------------------------------------------------- /resources/views/logs/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app', ['title' => 'Log Viewer']) 2 | 3 | @section('content') 4 | 5 |
    6 |
    7 |

    Raw System Logs

    8 |
    9 |
    10 | 11 |
    12 | 13 |
    14 |
    15 |
    16 |
    17 |

    Log Information

    18 |
    19 |
    20 |
    21 | To view the logs, you must set the proper permissions on the log files. "{{ exec('whoami') }}" must have "read" access to the log files.
    22 | To set permissions, run: "chown {{ exec('whoami') }}:{{ exec('whoami') }} <log_file>"

    23 | Logs this will show: 24 |
    25 |
      26 | @foreach ($logs as $name => $log) 27 |
    • 28 | {{ $log }} 29 | @if (!is_readable($log)) 30 |   (Invalid file permissions or file does not exist!) 31 | @endif 32 |
    • 33 | @endforeach 34 |
    • FiercePhish Activity Log
    • 35 |
    36 |
    37 |
    38 |
    39 |
    40 |
    41 |
    42 |

    Download Logs

    43 |
    44 |
    45 |
    46 | 47 | 48 | @foreach ($logs as $name => $loc) 49 | @if (is_readable($loc)) 50 | 51 | 52 | 53 | 54 | @endif 55 | @endforeach 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 |
    "{{ $loc }}"Download log
    Activity LogDownload log
    Download All Logs
    65 |
    66 |
    67 |
    68 |
    69 | 70 | @foreach ($logs as $name => $loc) 71 | @if (is_readable($loc)) 72 |
    73 |
    74 |
    75 |
    76 |

    Last 200 lines of {{ $loc }}

    77 |
    78 |
    79 |
    80 | @php 81 | $retArr = []; 82 | @endphp 83 | @if (exec('tail -n 200 '.$loc, $retArr)) 84 |
    {{ implode("\n", $retArr) }}
    85 | @else 86 |
    Empty log file
    87 | @endif 88 |
    89 |
    90 |
    91 |
    92 | @endif 93 | @endforeach 94 |
    95 |
    96 |
    97 |
    98 |

    Last 200 lines of Activity Log

    99 |
    100 |
    101 |
    102 |
    {{ $activitylog }}
    103 |
    104 |
    105 |
    106 |
    107 | 108 | @endsection 109 | 110 | @section('footer') 111 | 116 | @endsection -------------------------------------------------------------------------------- /resources/views/settings/configs/import_export.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app', ['title' => 'Import/Export Configurations']) 2 | 3 | @section('content') 4 | 5 |
    6 |
    7 |

    Application Configuration Settings

    8 |
    9 |
    10 | 11 |
    12 | 13 |
    14 |
    15 |
    16 |
    17 |

    Data Export

    18 |
    19 |
    20 |
    21 |
    22 | {{ csrf_field() }} 23 |
    24 | 25 |
    26 | 27 |
    28 |
    29 |

    You can export the database here for importing into a new FiercePhish instance

    30 |
    31 |
    32 |
    33 |
    34 |
    35 |
    36 |
    37 |

    Data Import

    38 |
    39 |
    40 |
    41 |
    42 | {{ csrf_field() }} 43 |
    44 | 45 |
    46 | 50 | 51 |
    52 |
    53 |
    54 |
    55 |
    56 | 57 |
    58 |
    59 |

    You can import a previous FiercePhish instance here. It will overwrite the current data and configurations, so its suggested to only do this on a new FiercePhish install.

    60 |
    61 |
    62 |
    63 |
    64 |
    65 | 66 | 67 | @endsection 68 | 69 | @section('footer') 70 | 83 | @endsection -------------------------------------------------------------------------------- /resources/views/targets/lists.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app', ['title' => 'All Target Lists']) 2 | 3 | @section('content') 4 | 5 |
    6 |
    7 |

    List of Lists

    8 |
    9 |
    10 | 11 |
    12 | 13 |
    14 |
    15 |
    16 |
    17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | @if (count($targetLists) > 0) 27 | @foreach ($targetLists as $list) 28 | 29 | 30 | 31 | 32 | 33 | @endforeach 34 | @else 35 | 36 | 37 | 38 | @endif 39 | 40 |
    Name# of UsersNotes
    {{ str_limit($list->name,200) }}{{ number_format($list->users()->count()) }}{{ $list->notes }}
    No Lists Yet
    41 |
    42 |
    43 |
    44 |
    45 | 46 | 47 | 48 | 49 |
    50 |
    51 |
    52 |
    53 |

    Add a List

    54 |
    55 |
    56 |
    57 | 58 |
    59 | {{ csrf_field() }} 60 |
    61 | 63 |
    64 | 65 |
    66 |
    67 | 68 |
    69 |
    70 |
    71 | 72 |
    73 |
    74 | 75 |
    76 | 77 |
    78 |
    79 |
    80 | 81 | 106 | 119 | 136 |
    137 | 138 | @endsection 139 | 140 | @section('footer') 141 | 155 | @endsection -------------------------------------------------------------------------------- /routes/api.php: -------------------------------------------------------------------------------- 1 | get('/user', function (Request $request) { 17 | return $request->user(); 18 | }); 19 | -------------------------------------------------------------------------------- /routes/channels.php: -------------------------------------------------------------------------------- 1 | id === (int) $id; 16 | }); 17 | -------------------------------------------------------------------------------- /routes/console.php: -------------------------------------------------------------------------------- 1 | comment(Inspiring::quote()); 18 | })->describe('Display an inspiring quote'); 19 | -------------------------------------------------------------------------------- /routes/web.php: -------------------------------------------------------------------------------- 1 | config('fiercephish.URI_PREFIX')], function() { 22 | // Authentication Routes... 23 | Route::get('login', 'Auth\LoginController@showLoginForm'); 24 | Route::post('login', 'Auth\LoginController@login'); 25 | Route::get('logout', 'Auth\LoginController@logout'); 26 | Route::get('2fa/validate', 'Auth\LoginController@getValidateToken'); 27 | Route::post('2fa/validate', ['middleware' => 'throttle:5', 'uses' => 'Auth\LoginController@postValidateToken']); 28 | 29 | 30 | 31 | // TargetsController Routes... 32 | Route::get('targets', 'TargetsController@index'); 33 | Route::post('targets/add', 'TargetsController@addTarget'); 34 | Route::post('targets/import', 'TargetsController@importTargets'); 35 | Route::get('targets/lists', 'TargetsController@targetlists_index'); 36 | Route::get('targets/list/{id}', 'TargetsController@targetlists_details'); 37 | Route::post('targets/lists/add', 'TargetsController@addList'); 38 | Route::post('targets/list/{id}/clear', 'TargetsController@clearList'); 39 | Route::post('targets/list/{id}/addall', 'TargetsController@addAlltoList'); 40 | Route::post('targets/list/{id}/addrandom', 'TargetsController@addRandomtoList'); 41 | Route::post('targets/list/removeuser/{id?}/{user_id?}', 'TargetsController@removeUser'); 42 | Route::get('targets/assign/{id}', 'TargetsController@assign_index'); 43 | Route::post('targets/assign/set', 'TargetsController@assignToLists'); 44 | 45 | 46 | 47 | // CampaignController Routes... 48 | Route::get('campaigns', 'CampaignController@index'); 49 | Route::get('campaigns/create', 'CampaignController@create'); 50 | Route::post('campaigns/create', 'CampaignController@create_post'); 51 | Route::get('campaigns/{id?}', 'CampaignController@campaign_details'); 52 | Route::post('campaigns/{id}/cancel', 'CampaignController@campaign_cancel'); 53 | 54 | 55 | 56 | // SettingsController Routes... 57 | Route::get('settings/users', 'SettingsController@index'); 58 | Route::post('settings/users/add', 'SettingsController@addUser'); 59 | Route::post('settings/users/delete', 'SettingsController@deleteUser'); 60 | Route::get('settings/profile/{id?}', 'SettingsController@get_editprofile'); 61 | Route::post('settings/profile', 'SettingsController@post_editprofile'); 62 | Route::post('2fa/enable', 'Google2FAController@enableTwoFactor'); 63 | Route::post('2fa/disable', 'Google2FAController@disableTwoFactor'); 64 | Route::get('settings/config', 'SettingsController@get_config'); 65 | Route::post('settings/config/save', 'SettingsController@post_config'); 66 | Route::get('settings/export', 'SettingsController@get_import_export'); 67 | Route::post('settings/export/download', 'SettingsController@post_export_data'); 68 | Route::get('settings/export/download', 'SettingsController@post_export_data'); 69 | Route::post('settings/export/import', 'SettingsController@post_import_data'); 70 | 71 | // LogController Routes... 72 | Route::get('logs', 'LogController@index'); 73 | Route::get('logs/download/{type}', 'LogController@download'); 74 | 75 | // EmailController Routes... 76 | Route::get('emails/templates/{id?}', 'EmailController@template_index'); 77 | Route::post('emails/templates/add', 'EmailController@addTemplate'); 78 | Route::post('emails/templates/edit', 'EmailController@editTemplate'); 79 | Route::post('emails/templates/delete', 'EmailController@deleteTemplate'); 80 | Route::get('emails/check', 'EmailController@check_settings_index'); 81 | Route::get('emails/simple/{id?}/{fwd?}', 'EmailController@send_simple_index'); 82 | Route::post('emails/simple/send', 'EmailController@send_simple_post'); 83 | Route::get('emails/log', 'EmailController@email_log'); 84 | Route::get('emails/log/{id?}', 'EmailController@email_log_details'); 85 | Route::post('emails/log/{id}/resend', 'EmailController@email_resend'); 86 | Route::post('emails/log/{id}/cancel', 'EmailController@email_cancel'); 87 | Route::get('inbox', 'EmailController@inbox_get'); 88 | Route::get('inbox/download/{id?}', 'EmailController@inbox_download_attachment'); 89 | 90 | 91 | // Dashboard Routes... 92 | Route::get('home', 'DashboardController@index'); 93 | Route::get('/', 'DashboardController@index'); 94 | 95 | 96 | 97 | // Ajax Routes... 98 | Route::post('ajax/targetuser/note', 'AjaxController@edit_targetuser_notes'); 99 | Route::post('ajax/targetlist/note', 'AjaxController@edit_targetlist_notes'); 100 | Route::post('ajax/targetuser/list/{id?}', 'AjaxController@targetuser_list'); 101 | Route::post('ajax/targetlist/membership/{id}', 'AjaxController@targetuser_membership'); 102 | Route::get('ajax/targetlist/membership/{id}', 'AjaxController@targetuser_membership'); 103 | Route::get('ajax/emails/template/{id?}', 'AjaxController@get_emailtemplate_info'); 104 | Route::get('ajax/emails/check/{command?}/{domain?}', 'AjaxController@email_check_commands'); 105 | Route::post('ajax/email/log', 'AjaxController@email_log'); 106 | Route::get('ajax/log/{id?}', 'AjaxController@get_activitylog'); 107 | Route::get('ajax/jobs', 'AjaxController@get_jobs'); 108 | Route::post('ajax/campaign/{id}', 'AjaxController@campaign_emails_get'); 109 | Route::get('ajax/inbox/new', 'AjaxController@get_num_new_messages'); 110 | Route::get('ajax/inbox/delete/{id?}', 'AjaxController@delete_inbox_message'); 111 | Route::get('ajax/inbox/{id?}', 'AjaxController@get_inbox_messages'); 112 | }); -------------------------------------------------------------------------------- /server.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | 10 | $uri = urldecode( 11 | parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) 12 | ); 13 | 14 | // This file allows us to emulate Apache's "mod_rewrite" functionality from the 15 | // built-in PHP web server. This provides a convenient way to test a Laravel 16 | // application without having installed a "real" web server software here. 17 | if ($uri !== '/' && file_exists(__DIR__.'/public'.$uri)) { 18 | return false; 19 | } 20 | 21 | require_once __DIR__.'/public/index.php'; 22 | -------------------------------------------------------------------------------- /storage/app/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !public/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /storage/app/public/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/.gitignore: -------------------------------------------------------------------------------- 1 | config.php 2 | routes.php 3 | schedule-* 4 | compiled.php 5 | services.json 6 | events.scanned.php 7 | routes.scanned.php 8 | down 9 | -------------------------------------------------------------------------------- /storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /tests/CreatesApplication.php: -------------------------------------------------------------------------------- 1 | make(Kernel::class)->bootstrap(); 19 | 20 | return $app; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/Feature/ExampleTest.php: -------------------------------------------------------------------------------- 1 | get('/login'); 20 | 21 | $response->assertStatus(200); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | assertTrue(true); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /webpack.mix.js: -------------------------------------------------------------------------------- 1 | const { mix } = require('laravel-mix'); 2 | 3 | /* 4 | |-------------------------------------------------------------------------- 5 | | Mix Asset Management 6 | |-------------------------------------------------------------------------- 7 | | 8 | | Mix provides a clean, fluent API for defining some Webpack build steps 9 | | for your Laravel application. By default, we are compiling the Sass 10 | | file for the application as well as bundling up all the JS files. 11 | | 12 | */ 13 | 14 | mix.js('resources/assets/js/app.js', 'public/js') 15 | .sass('resources/assets/sass/app.scss', 'public/css'); 16 | --------------------------------------------------------------------------------