├── ps-sys-updates ├── ps4-updatefeature.html ├── ps5-updatefeature.html ├── ps3-updatelist.txt ├── ps5-updatelist.xml ├── ps4-updatelist.xml └── vita-updatelist.xml ├── nginx ├── general.conf ├── error.conf ├── vhosts │ ├── ps-net-tests │ ├── ps-sys-updates │ ├── ROOT_DOMAIN │ └── hijacked-landing-pages └── nginx.conf ├── healthcheck.sh ├── PUPs └── README.md ├── LICENSE ├── .github └── workflows │ ├── pull-request.yml │ ├── release-tag.yml │ └── on-demand.yml ├── Dockerfile ├── README.md └── entrypoint.sh /ps-sys-updates/ps4-updatefeature.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ps-sys-updates/ps5-updatefeature.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nginx/general.conf: -------------------------------------------------------------------------------- 1 | location = /favicon.ico { 2 | log_not_found off; 3 | access_log off; 4 | } 5 | 6 | location = /robots.txt { 7 | log_not_found off; 8 | access_log off; 9 | } 10 | 11 | location = /sitemap.xml { 12 | log_not_found off; 13 | access_log off; 14 | } 15 | -------------------------------------------------------------------------------- /ps-sys-updates/ps3-updatelist.txt: -------------------------------------------------------------------------------- 1 | # {{UPPER_COUNTRY}} 2 | Dest=84;CompatibleSystemSoftwareVersion=4.9000-; 3 | Dest=84;IncrementalUpdateVersion=00010aad-00010aad;ImageVersion=00010b14;SystemSoftwareVersion=4.9000;CDN=http://d{{COUNTRY}}01.ps3.update.playstation.net/update/ps3/image/{{COUNTRY}}/2023_0228_05fe32f5dc8c78acbcd84d36ee7fdc5b/PS3PATCH.PUP;CDN_Timeout=30; 4 | Dest=84;ImageVersion=00010b14;SystemSoftwareVersion=4.9000;CDN=http://d{{COUNTRY}}01.ps3.update.playstation.net/update/ps3/image/{{COUNTRY}}/2023_0228_05fe32f5dc8c78acbcd84d36ee7fdc5b/PS3UPDAT.PUP;CDN_Timeout=30; 5 | -------------------------------------------------------------------------------- /nginx/error.conf: -------------------------------------------------------------------------------- 1 | ## 2 | # Custom HTML Error Pages 3 | ## 4 | 5 | location @error_html { 6 | more_set_headers "Date: "; 7 | more_set_headers "Content-Type: text/html"; 8 | 9 | return 0 "${status} | ${status_text}Error: ${status}: ${status_text}"; 10 | } 11 | 12 | ## 13 | # Custom JSON Error Pages 14 | ## 15 | 16 | location @error_json { 17 | more_set_headers "Date: "; 18 | more_set_headers "Content-Type: application/json"; 19 | 20 | return 0 "{\"error\":true,\"code\":\"${status}\",\"message\":\"${status_text}\"}"; 21 | } 22 | -------------------------------------------------------------------------------- /healthcheck.sh: -------------------------------------------------------------------------------- 1 | #!/bin/ash 2 | # shellcheck shell=dash 3 | 4 | # Check Nintendo Landing Pages 5 | 6 | # Check PlayStation Landing Pages 7 | 8 | # Check PlayStation Network Tests 9 | 10 | # Check Generic PlayStation Update 11 | 12 | # Check PS3 Update PUP CDNs 13 | 14 | # Check PS4 Update PUP CDNs 15 | 16 | # Check PS5 Update PUP CDNs 17 | 18 | # Check PS Vita Update PUP CDNs 19 | 20 | # Check PS3 Update Lists 21 | 22 | # Check PS4 Update Lists 23 | 24 | # Check PS5 Update Lists 25 | 26 | # Check PS Vita Update Lists 27 | 28 | # Check PS4 Update Features 29 | 30 | # Check PS5 Update Features 31 | 32 | # Check Blocked Pages 33 | 34 | exit 0 35 | -------------------------------------------------------------------------------- /ps-sys-updates/ps5-updatelist.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | http://d{{COUNTRY}}01.ps5.update.playstation.net/update/ps5/official/{{UNKNOWN}}/image/2020_0101/sys_0000000000000000000000000000000000000000000000000000000000000000/PS5UPDATE.PUP?dest={{COUNTRY}} 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /ps-sys-updates/ps4-updatelist.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | http://d{{COUNTRY}}01.ps4.update.playstation.net/update/ps4/image/2018_0118/sys_f86d4f9d2c049547bd61f942151ffb55/PS4UPDATE.PUP?dest={{COUNTRY}} 7 | 8 | 9 | 10 | 11 | http://d{{COUNTRY}}01.ps4.update.playstation.net/update/ps4/image/2018_0118/rec_6c28dbf66f63b7d3953491cc656f4e2d/PS4UPDATE.PUP?dest={{COUNTRY}} 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /PUPs/README.md: -------------------------------------------------------------------------------- 1 | # PUP File Hosting 2 | 3 | To actually serve update files (PUPs) themselves take a look at the format of the updatelist files contained in `ps-sys-updates`. Notice the hash provided in the URL? 4 | 5 | ## PS3 6 | 7 | `/update/ps3/image/{{COUNTRY}}/0000_0000_$MD5/PS3PATCH.PUP` or 8 | `/update/ps3/image/{{COUNTRY}}/0000_0000_$MD5/PS3UPDAT.PUP` 9 | will be served from 10 | `PS3PATCH_$MD5.PUP` and `PS3UPDAT_$MD5.PUP` from within this folder 11 | 12 | ## Vita 13 | 14 | `/update/psp2/image/0000_0000/(rel|sd|pre)_$MD5/PSP2UPDAT.PUP` 15 | will be served from 16 | `PSP2UPDAT_$md5.PUP` within this folder 17 | 18 | ## PS4 19 | 20 | `/update/ps4/image/0000_0000/(sys|rec)_$MD5/PS4UPDATE.PUP` 21 | will be served from 22 | `PS4UPDATE_$MD5.PUP` within this folder 23 | 24 | ## PS5 25 | 26 | `/update/ps5/official/00000000000000000000000000000000/image/\0000_00000/(sys|rec)_$SHA256/PS5UPDATE.PUP` 27 | will be served from 28 | `PS5UPDATE_$SHA256.PUP` within this folder 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020-2024 Al Azif, https://github.com/Al-Azif/exploit-host-http 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /ps-sys-updates/vita-updatelist.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | http://d{{COUNTRY}}01.psp2.update.playstation.net/update/psp2/image/2016_0323/rel_cd80b3da55771c99557db552542dff2b/PSP2UPDAT.PUP?dest={{COUNTRY}} 9 | 10 | 11 | 12 | http://d{{COUNTRY}}01.psp2.update.playstation.net/update/psp2/image/2016_0323/sd_4190a24e21086c240221cc009cde1ef2/PSP2UPDAT.PUP?dest={{COUNTRY}} 13 | 14 | 15 | http://d{{COUNTRY}}01.psp2.update.playstation.net/update/psp2/image/2016_0323/pre_c1ffba04357e27e337e599977f642230/PSP2UPDAT.PUP?dest={{COUNTRY}} 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /.github/workflows/pull-request.yml: -------------------------------------------------------------------------------- 1 | name: pull-request 2 | 3 | on: 4 | pull_request: 5 | 6 | jobs: 7 | static-analysis: 8 | name: Static Analysis 9 | runs-on: ubuntu-latest 10 | steps: 11 | - 12 | name: Checkout 13 | uses: actions/checkout@v4.2.2 14 | - 15 | name: Run ShellCheck 16 | uses: ludeeus/action-shellcheck@2.0.0 17 | - 18 | name: Run Hadolint 19 | uses: hadolint/hadolint-action@v3.1.0 20 | with: 21 | dockerfile: Dockerfile 22 | docker: 23 | name: Docker Build 24 | needs: static-analysis 25 | runs-on: ubuntu-latest 26 | steps: 27 | - 28 | name: Checkout 29 | uses: actions/checkout@v4.2.2 30 | with: 31 | submodules: recursive 32 | - 33 | name: Set up QEMU 34 | uses: docker/setup-qemu-action@v3.2.0 35 | - 36 | name: Set up Docker Buildx 37 | uses: docker/setup-buildx-action@v3.8.0 38 | - 39 | name: Build 40 | uses: docker/build-push-action@v6.10.0 41 | with: 42 | context: . 43 | platforms: linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/s390x 44 | # No `linux/ppc64le` support due to missing `lua-resty-core` Alpine package 45 | push: false 46 | -------------------------------------------------------------------------------- /nginx/vhosts/ps-net-tests: -------------------------------------------------------------------------------- 1 | # PlayStation Network Test Servers 2 | # Does not support TLS 3 | # Domains: 4 | # (get|post|ena).net.playstation.net 5 | 6 | server { 7 | charset utf-8; 8 | chunked_transfer_encoding on; 9 | 10 | include error.conf; 11 | error_page 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 421 422 423 424 426 428 429 431 451 500 501 502 503 504 505 506 507 508 510 511 @error_html; 12 | 13 | listen 0.0.0.0:80; 14 | listen 0.0.0.0:443 ssl http2; 15 | #{{IPV6}} listen [::]:80; 16 | #{{IPV6}} listen [::]:443 ssl http2; 17 | server_name "~^(get|post|ena)\.net\.playstation\.net$"; 18 | 19 | ssl_certificate /etc/nginx/certs/snakeoil.crt; 20 | ssl_certificate_key /etc/nginx/certs/private/snakeoil.key; 21 | 22 | # 2M Get Network Test 23 | location = /networktest/get_2m { 24 | gzip off; 25 | more_set_headers "Content-Type: text/plain"; 26 | charset off; 27 | 28 | alias /var/www/ps-net-tests/get_2m; 29 | } 30 | 31 | # 6M Get Network Test 32 | location = /networktest/get_6m { 33 | gzip off; 34 | more_set_headers "Content-Type: text/plain"; 35 | charset off; 36 | 37 | alias /var/www/ps-net-tests/get_6m; 38 | } 39 | 40 | # PS5 Ping Test 41 | location = /netstart/icst { 42 | gzip off; 43 | more_set_headers "Content-Type: text/plain"; 44 | charset off; 45 | 46 | return 200 "Success"; 47 | } 48 | 49 | # 128 Post Network Test 50 | location ~* "^/networktest/post_128m?$" { 51 | gzip off; 52 | more_set_headers "Content-Type: text/plain"; 53 | charset off; 54 | 55 | return 200; 56 | } 57 | 58 | location / { 59 | return 444; 60 | } 61 | 62 | include general.conf; 63 | } 64 | -------------------------------------------------------------------------------- /.github/workflows/release-tag.yml: -------------------------------------------------------------------------------- 1 | name: release-tag 2 | 3 | on: 4 | push: 5 | tags: 6 | - v*.*.* 7 | 8 | jobs: 9 | docker: 10 | name: Docker Build 11 | runs-on: ubuntu-latest 12 | steps: 13 | - 14 | name: Checkout 15 | uses: actions/checkout@v4.2.2 16 | with: 17 | submodules: recursive 18 | - 19 | name: Docker metadata 20 | id: meta 21 | uses: docker/metadata-action@v5.6.1 22 | with: 23 | images: alazif/exploit-host-http 24 | tags: | 25 | type=semver,pattern={{version}} 26 | type=semver,pattern={{major}}.{{minor}} 27 | type=semver,pattern={{major}} 28 | - 29 | name: Set up QEMU 30 | uses: docker/setup-qemu-action@v3.2.0 31 | - 32 | name: Set up Docker Buildx 33 | uses: docker/setup-buildx-action@v3.8.0 34 | - 35 | name: Login to DockerHub 36 | uses: docker/login-action@v3.3.0 37 | with: 38 | username: ${{ secrets.DOCKERHUB_USERNAME }} 39 | password: ${{ secrets.DOCKERHUB_TOKEN }} 40 | - 41 | name: Build and push 42 | uses: docker/build-push-action@v6.10.0 43 | with: 44 | context: . 45 | platforms: linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/s390x 46 | # No `linux/ppc64le` support due to missing `lua-resty-core` Alpine package 47 | push: true 48 | tags: ${{ steps.meta.outputs.tags }} 49 | labels: ${{ steps.meta.outputs.labels }} 50 | - 51 | name: Update repo description 52 | uses: peter-evans/dockerhub-description@v4.0.0 53 | with: 54 | username: ${{ secrets.DOCKERHUB_USERNAME }} 55 | password: ${{ secrets.DOCKERHUB_TOKEN }} 56 | repository: alazif/exploit-host-http 57 | -------------------------------------------------------------------------------- /.github/workflows/on-demand.yml: -------------------------------------------------------------------------------- 1 | name: on-demand 2 | 3 | on: 4 | workflow_dispatch: 5 | branches: 6 | - 'main' 7 | 8 | jobs: 9 | static-analysis: 10 | name: Static Analysis 11 | runs-on: ubuntu-latest 12 | steps: 13 | - 14 | name: Checkout 15 | uses: actions/checkout@v4.2.2 16 | - 17 | name: Run ShellCheck 18 | uses: ludeeus/action-shellcheck@2.0.0 19 | - 20 | name: Run Hadolint 21 | uses: hadolint/hadolint-action@v3.1.0 22 | with: 23 | dockerfile: Dockerfile 24 | docker: 25 | name: Docker Build 26 | needs: static-analysis 27 | runs-on: ubuntu-latest 28 | steps: 29 | - 30 | name: Checkout 31 | uses: actions/checkout@v4.2.2 32 | with: 33 | submodules: recursive 34 | - 35 | name: Set up QEMU 36 | uses: docker/setup-qemu-action@v3.2.0 37 | - 38 | name: Set up Docker Buildx 39 | uses: docker/setup-buildx-action@v3.8.0 40 | - 41 | name: Login to DockerHub 42 | uses: docker/login-action@v3.3.0 43 | with: 44 | username: ${{ secrets.DOCKERHUB_USERNAME }} 45 | password: ${{ secrets.DOCKERHUB_TOKEN }} 46 | - 47 | name: Build and push 48 | uses: docker/build-push-action@v6.10.0 49 | with: 50 | context: . 51 | platforms: linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/s390x 52 | # No `linux/ppc64le` support due to missing `lua-resty-core` Alpine package 53 | push: true 54 | tags: alazif/exploit-host-http:latest 55 | - 56 | name: Update repo description 57 | uses: peter-evans/dockerhub-description@v4.0.0 58 | with: 59 | username: ${{ secrets.DOCKERHUB_USERNAME }} 60 | password: ${{ secrets.DOCKERHUB_TOKEN }} 61 | repository: alazif/exploit-host-http 62 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.22.2 2 | 3 | RUN \ 4 | # Update and install system applications 5 | apk add --update --no-cache \ 6 | bind-tools=9.20.16-r0 \ 7 | certbot=4.0.0-r0 \ 8 | curl=8.14.1-r2 \ 9 | libcap=2.76-r0 \ 10 | libfaketime=0.9.10-r3 \ 11 | lua-resty-core=0.1.31-r0 \ 12 | nginx=1.28.0-r3 \ 13 | nginx-mod-http-fancyindex=1.28.0-r3 \ 14 | nginx-mod-http-headers-more=1.28.0-r3 \ 15 | nginx-mod-http-lua=1.28.0-r3 \ 16 | openssl=3.5.4-r0 \ 17 | shadow=4.17.3-r0 \ 18 | tini=0.19.0-r3 && \ 19 | # Remove default NGINX vHosts and websites 20 | rm -f /etc/nginx/sites-enabled/default && \ 21 | rm -f /etc/nginx/sites-available/default && \ 22 | rm -rf /var/www/* && \ 23 | mkdir -p /var/www && \ 24 | # Setup templates directory 25 | mkdir -p /etc/nginx/templates/sites-available && \ 26 | chmod 755 /etc/nginx/templates && \ 27 | chmod 755 /etc/nginx/templates/sites-available && \ 28 | # Setup enabled vHost directory 29 | mkdir -p /etc/nginx/sites-enabled && \ 30 | chmod 755 /etc/nginx/sites-enabled && \ 31 | # Setup enabled modules directory 32 | mkdir -p /etc/nginx/modules-enabled && \ 33 | chmod 755 /etc/nginx/modules-enabled && \ 34 | # Setup folders for certs 35 | mkdir -p /etc/nginx/certs/private && \ 36 | chmod 755 /etc/nginx/certs && \ 37 | chmod 710 /etc/nginx/certs/private && \ 38 | # Setup logging directory 39 | mkdir -p /var/log/nginx && \ 40 | # Allown nginx to use privileged ports without root 41 | setcap 'cap_net_bind_service=+ep' /usr/sbin/nginx && \ 42 | # Change nginx user's uid/gid 43 | groupmod -g 10001 nginx && \ 44 | usermod -u 10000 nginx 45 | 46 | # Copy LICENSE to container 47 | COPY LICENSE /LICENSE 48 | 49 | # Copy NGINX global settings to container 50 | COPY nginx/nginx.conf /etc/nginx/templates/ 51 | COPY nginx/general.conf /etc/nginx/templates/ 52 | COPY nginx/error.conf /etc/nginx/templates/ 53 | 54 | # Copy NGINX vHosts to container 55 | COPY nginx/vhosts* /etc/nginx/templates/sites-available/ 56 | 57 | # Copy entrypoint script to container 58 | COPY entrypoint.sh /entrypoint.sh 59 | 60 | # Copy HEALTHCHECK script to container 61 | COPY healthcheck.sh /healthcheck.sh 62 | 63 | # Copy system update metadata files 64 | COPY ps-sys-updates /srv/ps-sys-updates 65 | 66 | # Copy system update files (PUPs) 67 | COPY PUPs /srv/PUPs 68 | 69 | # Set permissions on copied files 70 | RUN \ 71 | mkdir -p \ 72 | /var/www/cache \ 73 | /var/www/exploits \ 74 | /var/www/themes/default && \ 75 | echo "Exploit Landing Page" > /var/www/themes/default/index.html && \ 76 | chmod -R 644 \ 77 | /etc/nginx/templates/nginx.conf \ 78 | /etc/nginx/templates/general.conf \ 79 | /etc/nginx/templates/error.conf \ 80 | /etc/nginx/templates/sites-available/* && \ 81 | chmod +x \ 82 | /entrypoint.sh \ 83 | /healthcheck.sh 84 | 85 | # Open HTTP(S) ports 86 | EXPOSE 80/tcp 443/tcp 87 | 88 | # Start entrypoint script 89 | ENTRYPOINT ["/sbin/tini", "--", "/entrypoint.sh", "/usr/sbin/nginx"] 90 | 91 | # Add HEALTHCHECK directive 92 | HEALTHCHECK CMD [ "/healthcheck.sh" ] 93 | 94 | # Set default command for container 95 | CMD ["-e", "/dev/stderr", "-g", "daemon off;"] 96 | -------------------------------------------------------------------------------- /nginx/vhosts/ps-sys-updates: -------------------------------------------------------------------------------- 1 | # PlayStation System Update Servers 2 | # Does not support TLS 3 | # Domains: 4 | # update.playstation.net 5 | # (d|f|h)[a-z]{2}01.(ps3|ps4|ps5|psp2).update.playstation.net 6 | 7 | server { 8 | charset utf-8; 9 | chunked_transfer_encoding on; 10 | 11 | include error.conf; 12 | error_page 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 421 422 423 424 426 428 429 431 451 500 501 502 503 504 505 506 507 508 510 511 @error_html; 13 | 14 | listen 0.0.0.0:80; 15 | listen 0.0.0.0:443 ssl http2; 16 | #{{IPV6}} listen [::]:80; 17 | #{{IPV6}} listen [::]:443 ssl http2; 18 | server_name update.playstation.net "~^(d|f|h)[a-z]{2}01\.(ps3|ps4|ps5|psp2)\.update\.playstation\.net$"; 19 | 20 | ssl_certificate /etc/nginx/certs/snakeoil.crt; 21 | ssl_certificate_key /etc/nginx/certs/private/snakeoil.key; 22 | 23 | location ~* "^/update/ps3/image/\D{2}/\d{4}_\d{4}_(?[a-fA-F0-9]{32})/PS3PATCH\.PUP$" { 24 | alias /var/www/PUPs/PS3PATCH_$md5.PUP; 25 | } 26 | location ~* "^/update/ps3/image/\D{2}/\d{4}_\d{4}_(?[a-fA-F0-9]{32}S)/PS3UPDAT\.PUP$" { 27 | alias /var/www/PUPs/PS3UPDAT_$md5.PUP; 28 | } 29 | 30 | location ~* "^/update/ps4/image/\d{4}_\d{4}/(sys|rec)_(?[a-fA-F0-9]{32})/PS4UPDATE\.PUP$" { 31 | alias /var/www/PUPs/PS4UPDATE_$md5.PUP; 32 | } 33 | 34 | location ~* "^/update/ps5/official/(?[a-zA-Z0-9]{32})/image/\d{4}_\d{4}/(sys|rec)_(?[a-fA-F0-9]{64})/PS5UPDATE\.PUP$" { 35 | alias /var/www/PUPs/PS5UPDATE_$sha256.PUP; 36 | } 37 | 38 | location ~* "^/update/psp2/image/\d{4}_\d{4}/(rel|sd|pre)_(?[a-fA-F0-9]{32})/PSP2UPDAT\.PUP$" { 39 | alias /var/www/PUPs/PSP2UPDAT_$md5.PUP; 40 | } 41 | 42 | # The pages here are built in NGINX to avoid needing to install PHP as the pages are built with simple substitution 43 | location ~* "^/update/ps3/list/(?[a-z]{2})/ps3-updatelist\.txt$" { 44 | set_by_lua_block $upper_country { 45 | return string.upper(ngx.var.country) 46 | } 47 | more_set_headers "Content-Type: text/plain"; 48 | sub_filter_types *; 49 | alias /var/www/ps-sys-updates/ps3-updatelist.txt; 50 | sub_filter "{{UPPER_COUNTRY}}" "${upper_country}"; 51 | sub_filter "{{COUNTRY}}" "${country}"; 52 | sub_filter_once off; 53 | } 54 | 55 | location ~* "^/update/ps4/list/(?[a-z]{2})/ps4-updatelist\.xml$" { 56 | more_set_headers "Content-Type: application/xml"; 57 | alias /var/www/ps-sys-updates/ps4-updatelist.xml; 58 | sub_filter_types *; 59 | sub_filter "{{COUNTRY}}" "${country}"; 60 | sub_filter_once off; 61 | } 62 | 63 | location ~* "^/update/ps5/official/(?[a-zA-Z0-9]{32})/list/(?[a-z]{2})/updatelist\.xml$" { 64 | more_set_headers "Content-Type: application/xml"; 65 | alias /var/www/ps-sys-updates/ps5-updatelist.xml; 66 | sub_filter_types *; 67 | sub_filter "{{COUNTRY}}" "${country}"; 68 | sub_filter "{{UNKNOWN}}" "${unknown}"; 69 | sub_filter_once off; 70 | } 71 | 72 | location ~* "^/update/psp2/list/(?[a-z]{2})/psp2-updatelist\.xml$" { 73 | more_set_headers "Content-Type: application/xml"; 74 | alias /var/www/ps-sys-updates/vita-updatelist.xml; 75 | sub_filter_types *; 76 | sub_filter "{{COUNTRY}}" "${country}"; 77 | sub_filter_once off; 78 | } 79 | 80 | location ~* "^/update/ps4/html/(?[a-z]{2})/(?[a-z]{2})/ps4-updatefeature\.html$" { 81 | alias /var/www/ps-sys-updates/ps4-updatefeature.html; 82 | } 83 | 84 | location ~* "^/update/ps5/official/(?[a-zA-Z0-9]{32})/html/feature/(?[a-z]{2})/(?[a-z]{2})/updatefeature\.html$" { 85 | alias /var/www/ps-sys-updates/ps5-updatefeature.html; 86 | } 87 | 88 | location / { 89 | return 444; 90 | } 91 | 92 | include general.conf; 93 | } 94 | -------------------------------------------------------------------------------- /nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | user nginx; 2 | pid /run/nginx.pid; 3 | worker_processes auto; 4 | worker_rlimit_nofile 65535; 5 | include /etc/nginx/modules-enabled/*.conf; 6 | 7 | pcre_jit on; 8 | 9 | # Needed to read environmental variable in hijacked-landing-pages 10 | env REDIRECT_TYPE; 11 | env ROOT_DOMAIN_PATH; 12 | 13 | events { 14 | multi_accept on; 15 | worker_connections 65535; 16 | use epoll; 17 | } 18 | 19 | http { 20 | ## 21 | # Get User's Real IP from Cloudflare 22 | ## 23 | 24 | #{{CF_IPV4}} 25 | #{{CF_IPV6}} 26 | #{{CF_IP}} 27 | 28 | ## 29 | # Basic Settings 30 | ## 31 | 32 | sendfile on; 33 | tcp_nopush on; 34 | tcp_nodelay on; 35 | 36 | client_body_buffer_size 16K; 37 | client_header_buffer_size 1k; 38 | types_hash_max_size 2048; 39 | client_max_body_size 128m; 40 | large_client_header_buffers 4 18k; 41 | 42 | client_body_timeout 12; 43 | client_header_timeout 12; 44 | keepalive_timeout 15; 45 | send_timeout 10; 46 | reset_timedout_connection on; 47 | 48 | # The server_names_hash_bucket_size is 32 on some systems by default... This 49 | # won't work with *our* default setup (Valid values should be a power of two) 50 | #{{SERVER_NAME_HASH_BUCKET_SIZE}} 51 | 52 | # Depreciated 53 | # lua_load_resty_core off; 54 | 55 | ## 56 | # Shhhhhh 57 | ## 58 | 59 | server_tokens off; 60 | more_set_headers "Server: "; 61 | more_set_headers "X-Powered-By: "; 62 | etag off; 63 | 64 | ## 65 | # Content Type and MIME info 66 | ## 67 | 68 | charset utf-8; 69 | charset_types text/plain text/css text/xml text/cache-manifest text/vnd.wap.wml application/javascript application/json application/xml application/rss+xml; 70 | include /etc/nginx/mime.types; 71 | types { 72 | text/cache-manifest appcache manifest; 73 | } 74 | default_type application/octet-stream; 75 | 76 | ## 77 | # Logging Settings 78 | ## 79 | 80 | access_log off; # /var/log/nginx/access.log; 81 | error_log /dev/null; # /var/log/nginx/error.log {{NGINX_ERROR_LOG_LEVEL}}; 82 | 83 | ## 84 | # Compression Settings 85 | ## 86 | 87 | gzip on; 88 | gzip_comp_level 6; 89 | gzip_min_length 1024; 90 | gzip_proxied expired no-cache no-store private auth; 91 | gzip_types text/css text/javascript text/xml text/plain text/x-component application/javascript application/x-javascript application/json application/xml application/rss+xml application/atom+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml; 92 | 93 | ## 94 | # Error Map 95 | ## 96 | 97 | map $status $status_text { 98 | 400 "Bad Request"; 99 | 401 "Unauthorized"; 100 | 402 "Payment Required"; 101 | 403 "Forbidden"; 102 | 404 "Not Found"; 103 | 405 "Method Not Allowed"; 104 | 406 "Not Acceptable"; 105 | 407 "Proxy Authentication Required"; 106 | 408 "Request Timeout"; 107 | 409 "Conflict"; 108 | 410 "Gone"; 109 | 411 "Length Required"; 110 | 412 "Precondition Failed"; 111 | 413 "Payload Too Large"; 112 | 414 "URI Too Long"; 113 | 415 "Unsupported Media Type"; 114 | 416 "Range Not Satisfiable"; 115 | 417 "Expectation Failed"; 116 | 418 "I'm a teapot"; 117 | 421 "Misdirected Request"; 118 | 422 "Unprocessable Entity"; 119 | 423 "Locked"; 120 | 424 "Failed Dependency"; 121 | 426 "Upgrade Required"; 122 | 428 "Precondition Required"; 123 | 429 "Too Many Requests"; 124 | 431 "Request Header Fields Too Large"; 125 | 444 "Connection Closed Without Response"; 126 | 451 "Unavailable For Legal Reasons"; 127 | 500 "Internal Server Error"; 128 | 501 "Not Implemented"; 129 | 502 "Bad Gateway"; 130 | 503 "Service Unavailable"; 131 | 504 "Gateway Timeout"; 132 | 505 "HTTP Version Not Supported"; 133 | 506 "Variant Also Negotiates"; 134 | 507 "Insufficient Storage"; 135 | 508 "Loop Detected"; 136 | 510 "Not Extended"; 137 | 511 "Network Authentication Required"; 138 | default "Something is wrong..."; 139 | } 140 | 141 | ## 142 | # SSL 143 | ## 144 | 145 | ssl_session_timeout 1d; 146 | ssl_session_cache shared:SSL:10m; 147 | ssl_session_tickets off; 148 | 149 | ## 150 | # Virtual Host Configs 151 | ## 152 | 153 | include /etc/nginx/conf.d/*.conf; 154 | include /etc/nginx/sites-enabled/*; 155 | } 156 | -------------------------------------------------------------------------------- /nginx/vhosts/ROOT_DOMAIN: -------------------------------------------------------------------------------- 1 | # Main Site Server Block 2 | # Support TLS in various ways 3 | # Domains: 4 | # the.gate 5 | # or the root domain set in the config 6 | 7 | # Check user input 8 | map $cookie_theme $theme { 9 | "Default" "default"; 10 | "Open Orbis" "openorbis"; 11 | default "default"; 12 | } 13 | 14 | server { 15 | charset utf-8; 16 | chunked_transfer_encoding on; 17 | 18 | include error.conf; 19 | error_page 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 421 422 423 424 426 428 429 431 451 500 501 502 503 504 505 506 507 508 510 511 @error_html; 20 | 21 | listen 0.0.0.0:80; 22 | listen 0.0.0.0:443 ssl http2; 23 | #{{IPV6}} listen [::]:80; 24 | #{{IPV6}} listen [::]:443 ssl http2; 25 | server_name {{ROOT_DOMAIN}}; 26 | 27 | # TLS Config 28 | #{{LETSENCRYPT}} ssl_certificate /etc/letsencrypt/live/{{ROOT_DOMAIN}}/fullchain.pem; 29 | #{{LETSENCRYPT}} ssl_certificate_key /etc/letsencrypt/live/{{ROOT_DOMAIN}}/privkey.pem; 30 | #{{LETSENCRYPT}} ssl_trusted_certificate /etc/letsencrypt/live/{{ROOT_DOMAIN}}/chain.pem; 31 | 32 | #{{SELF}} ssl_certificate /etc/nginx/certs/snakeoil.crt; 33 | #{{SELF}} ssl_certificate_key /etc/nginx/certs/private/snakeoil.key; 34 | 35 | #{{MOUNT}} ssl_certificate /etc/nginx/certs/fullchain.pem; 36 | #{{MOUNT}} ssl_certificate_key /etc/nginx/certs/private/privkey.pem; 37 | #{{MOUNT}} ssl_trusted_certificate /etc/nginx/certs/chain.pem; 38 | 39 | #{{CF_STRICT}} ssl_client_certificate /etc/nginx/certs/origin-pull-ca.pem; 40 | #{{CF_STRICT}} ssl_verify_client on; 41 | 42 | ## 43 | # OCSP Stapling 44 | ## 45 | 46 | #{{OCSP_STAPLING}} ssl_stapling on; 47 | #{{OCSP_STAPLING}} ssl_stapling_verify on; 48 | #{{OCSP_STAPLING}} resolver 1.1.1.1 1.0.0.1 [2606:4700:4700::1111] [2606:4700:4700::1001] valid=60s; 49 | #{{OCSP_STAPLING}} resolver_timeout 2s; 50 | 51 | root /var/www; 52 | 53 | location = /api/themes { 54 | error_page 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 421 422 423 424 426 428 429 431 451 500 501 502 503 504 505 506 507 508 510 511 @error_json; 55 | more_set_headers "Content-Type application/json"; 56 | return 200 "{\"themes\":[\"Default\"]}"; 57 | } 58 | 59 | location = /api/menu { 60 | error_page 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 421 422 423 424 426 428 429 431 451 500 501 502 503 504 505 506 507 508 510 511 @error_json; 61 | more_set_headers "Content-Type application/json"; 62 | alias /var/www/menu.json; 63 | } 64 | 65 | location = /api/news { 66 | error_page 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 421 422 423 424 426 428 429 431 451 500 501 502 503 504 505 506 507 508 510 511 @error_json; 67 | more_set_headers "Content-Type application/json"; 68 | alias /var/www/news.json; 69 | } 70 | 71 | location = /exploits { 72 | rewrite ^ /exploits/ permanent; 73 | } 74 | 75 | location ^~ "/exploits/" { 76 | autoindex on; 77 | autoindex_exact_size on; 78 | autoindex_format html; 79 | autoindex_localtime off; 80 | index hidden.html; 81 | 82 | try_files $uri $uri/ =404; 83 | } 84 | 85 | location ^~ "/cache/" { 86 | try_files $uri $uri/ =404; 87 | } 88 | 89 | location ~* "\.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$" { 90 | root /var/www/themes/$theme; 91 | expires 1M; 92 | add_header Cache-Control "public"; 93 | } 94 | 95 | location ~* "\.(?:css|js)$" { 96 | root /var/www/themes/$theme; 97 | expires 1y; 98 | add_header Cache-Control "public"; 99 | } 100 | 101 | location / { 102 | # add_header Cache-Control no-store; 103 | root /var/www/themes/$theme; 104 | autoindex off; 105 | index index.html; 106 | 107 | try_files $uri $uri/ =404; 108 | } 109 | 110 | include general.conf; 111 | } 112 | 113 | # Dump any requests that are not accounted for 114 | server { 115 | listen 0.0.0.0:80 default_server; 116 | listen 0.0.0.0:443 ssl http2 default_server; 117 | #{{IPV6}} listen [::]:80 default_server; 118 | #{{IPV6}} listen [::]:443 ssl http2 default_server; 119 | 120 | ssl_certificate /etc/nginx/certs/snakeoil.crt; 121 | ssl_certificate_key /etc/nginx/certs/private/snakeoil.key; 122 | 123 | access_log off; 124 | error_log /dev/null; 125 | 126 | return 444; 127 | } 128 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Exploit Host HTTP 2 | 3 | Purpose made HTTP Docker file setup for hosting exploits for the web browser for Sony PlayStation devices and the Nintendo Wii/WiiU/Switch. This essentially has to be used with the [Exploit Host DNS](https://github.com/Al-Azif/exploit-host-DNS) component. It's possible to use it "standalone", but will require something to make the browser send the correct `Host` header with it's HTTP(S) requests. 4 | 5 | ## Features 6 | 7 | When used in conjunction with [Exploit Host DNS](https://github.com/Al-Azif/exploit-host-DNS) following features are available: 8 | 9 | - Enables internet speed tests 10 | - Enables serving custom system updates 11 | - Hijacks system update feature pages 12 | - Hijacks default browser landing pages (Connection Tests, User's Manuals, and Browser Homepages) 13 | - Redirect is cached (It is not cached on PS5 as it becomes permanent) 14 | - Prepackaged with the latest Exploit Host website 15 | - Can redirect to an external page, to a self hosted site, or to the included Exploit Host website 16 | 17 | ## Usage 18 | 19 | This is setup to work right out of the box with [Exploit Host DNS](https://github.com/Al-Azif/exploit-host-DNS). There are a lot of options for your individual hosting wants/needs; however, I'll only show the basic usage here. 20 | 21 | ### Command Line 22 | 23 | This command will always pull the latest image from Docker Hub, run on the main Docker bridge network, and it will restart if it's not running until you explicitly tell it to stop. 24 | 25 | `docker run -d --network bridge -p 80:80/tcp -p 443:443/tcp --restart unless-stopped --pull always alazif/exploit-host-http:latest` 26 | 27 | ### Composer 28 | 29 | This composer file will do the same as the command above. 30 | 31 | ```yml 32 | --- 33 | version: "3.8" 34 | 35 | services: 36 | exploit-host-http: 37 | image: alazif/exploit-host-http:latest 38 | network_mode: bridge 39 | ports: 40 | - 80:80/tcp 41 | - 443:443/udp 42 | pull_policy: always 43 | restart: unless-stopped 44 | ``` 45 | 46 | Start the compose file by calling `docker compose up -d` from the same location as the composer file. 47 | 48 | ## Options (Environment Variables) 49 | 50 | | Option | Default | Type | Info | 51 | |:--------------------------------|:--------------|:---------------|:---------| 52 | | DEBUG | `false` | boolean | Show debug output for `entrypoint.sh` in the Docker log. | 53 | | REDIRECT_TYPE | `http` | string | The protocol that is used for the hijacked landing page redirect. Valid values are `http` and `https`. | 54 | | ROOT_DOMAIN | `the.gate` | string | The root domain that is used for hijacked landing page redirect. This is **ONLY** the domain itself. | 55 | | ROOT_DOMAIN_PATH | none | string | Additional path to append to root domain for redirect. If needed you can add an alternative port here as well. | 56 | | HIJACK_URL | none | string | Rather than hosting the hijacked landing page just redirect the request to another domain hosted elsewhere. If this is set, `ROOT_DOMAIN` and `ROOT_DOMAIN_PATH` are ignored. | 57 | | NGINX_ACCESS_LOG | `false` | boolean | Enables the NGINX access log, located at `/var/log/nginx/access.log` | 58 | | NGINX_ERROR_LOG | `false` | boolean | Enables the NGINX error log, located at `/var/log/nginx/error.log` | 59 | | NGINX_ERROR_LOG_LEVEL | `warn` | string | The error log level for the NGINX error log. Valid values are `debug`, `info`, `notice`, `warn`, `error`, `crit`, `alert`, `emerg`. Ignored if `NGINX_ERROR_LOG` is `false`. | 60 | | TLS | `self` | string | Valid values are `self`, `letsencrypt`, and `mount`. | 61 | | CF_IP_CORRECTION | `false` | boolean | Automatically correct CloudFlare IP addresses to the real IP address for logging. | 62 | | CF_STRICT | `false` | boolean | | 63 | | OCSP_STAPLING | `false` | boolean | | 64 | | SEVER_HASH_BUCKET_SIZE_OVERRIDE | `false` | boolean | Overrides the `server_names_hash_bucket_size` option in NGINX to be `64`. Some systems have `32` as the default and that is not enough for our usage. | 65 | 66 | ## TODO 67 | 68 | - [ ] Verify `TLS` options work as expected, I believe certbot for letsencrypt has changed. 69 | - [ ] Make healthcheck.sh 70 | - [ ] Verify `CF_STRICT` still works as expected and hasn't changed. 71 | -------------------------------------------------------------------------------- /nginx/vhosts/hijacked-landing-pages: -------------------------------------------------------------------------------- 1 | # Landing Pages 2 | # Does not support TLS (Correctly anyway...) 3 | # Domains: 4 | # www.playstation.com 5 | # manuals.playstation.net 6 | # oss.dl.playstation.net 7 | # status.playstation.com 8 | # ctest.cdn.nintendo.net 9 | # conntest.nintendowifi.net 10 | # nasc.nintendowifi.net 11 | # cfh.wapp.wii.com 12 | 13 | server { 14 | charset utf-8; 15 | chunked_transfer_encoding on; 16 | 17 | include error.conf; 18 | error_page 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 421 422 423 424 426 428 429 431 451 500 501 502 503 504 505 506 507 508 510 511 @error_html; 19 | 20 | listen 0.0.0.0:80; 21 | listen 0.0.0.0:443 ssl http2; 22 | #{{IPV6}} listen [::]:80; 23 | #{{IPV6}} listen [::]:443 ssl http2; 24 | server_name www.playstation.com manuals.playstation.net oss.dl.playstation.net status.playstation.com ctest.cdn.nintendo.net "~^(conntest|nasc)\.nintendowifi\.net$"; 25 | 26 | ssl_certificate /etc/nginx/certs/snakeoil.crt; 27 | ssl_certificate_key /etc/nginx/certs/private/snakeoil.key; 28 | 29 | set_by_lua_block $REDIRECT_TYPE { 30 | return os.getenv("REDIRECT_TYPE"); 31 | } 32 | 33 | set_by_lua_block $ROOT_DOMAIN_PATH { 34 | return os.getenv("ROOT_DOMAIN_PATH"); 35 | } 36 | 37 | set $REDIRECT_URL "$REDIRECT_TYPE://{{ROOT_DOMAIN}}$ROOT_DOMAIN_PATH"; 38 | 39 | set $REDIRECT ""; 40 | set $REDIRECT "${REDIRECT}"; 41 | set $REDIRECT "${REDIRECT}"; 42 | set $REDIRECT "${REDIRECT}"; 43 | set $REDIRECT "${REDIRECT}Redirector"; 44 | set $REDIRECT "${REDIRECT}"; 45 | set $REDIRECT "${REDIRECT}"; 46 | set $REDIRECT "${REDIRECT}"; 49 | set $REDIRECT "${REDIRECT}"; 50 | set $REDIRECT "${REDIRECT}"; 51 | 52 | # Using `return 200 $REDIRECT;` causes it to replace the User's Manual... FOREVER!!! 53 | set $IS_PS5 0; 54 | if ($http_user_agent ~ ".*PlayStation 5.*") { 55 | set $IS_PS5 1; 56 | } 57 | 58 | location = /redirect.manifest { 59 | more_set_headers "Content-Type: text/cache-manifest"; 60 | 61 | return 200 "CACHE MANIFEST\n\nCACHE:\n/\n/index.html\n\nNETWORK:\n*\n\nSETTINGS:\nprefer-online\n\n# Date: 2020-01-01T00:00:00Z"; 62 | } 63 | 64 | # www.playstation.com 65 | location ~* "^/?(index.html)?" { 66 | if ($IS_PS5) { 67 | return 302 $REDIRECT_URL/; 68 | } 69 | 70 | more_set_headers "Content-Type: text/html"; 71 | 72 | return 200 $REDIRECT; 73 | } 74 | 75 | # manuals.playstation.net 76 | location ~* "^/document/[a-z]{2}/ps3/current/?(index.html)?" { 77 | more_set_headers "Content-Type: text/html"; 78 | 79 | return 200 $REDIRECT; 80 | } 81 | 82 | # manuals.playstation.net 83 | location ~* "^/document/[a-z]{2}/(ps4|ps5|psvita)/?(index.html)?" { 84 | if ($IS_PS5) { 85 | return 302 $REDIRECT_URL/; 86 | } 87 | 88 | more_set_headers "Content-Type: text/html"; 89 | 90 | return 200 $REDIRECT; 91 | } 92 | 93 | # oss.dl.playstation.net 94 | location ~* "^/ps4/[a-zA-Z]{2}(-[a-zA-Z]{2})?/?(index.html)?" { 95 | if ($IS_PS5) { 96 | return 302 $REDIRECT_URL/; 97 | } 98 | 99 | more_set_headers "Content-Type: text/html"; 100 | 101 | return 200 $REDIRECT; 102 | } 103 | 104 | # status.playstation.com 105 | location ~* "^/[a-z]{2}(-[a-z]{3,4})?(-[a-z]{2})?/?(index.html)?" { 106 | if ($IS_PS5) { 107 | return 302 $REDIRECT_URL/; 108 | } 109 | 110 | more_set_headers "Content-Type: text/html"; 111 | 112 | return 200 $REDIRECT; 113 | } 114 | 115 | location / { 116 | return 444; 117 | } 118 | 119 | include general.conf; 120 | } 121 | 122 | server { 123 | charset utf-8; 124 | chunked_transfer_encoding on; 125 | 126 | include error.conf; 127 | error_page 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 421 422 423 424 426 428 429 431 451 500 501 502 503 504 505 506 507 508 510 511 @error_html; 128 | 129 | listen 0.0.0.0:80; 130 | listen 0.0.0.0:443 ssl http2; 131 | #{{IPV6}} listen [::]:80; 132 | #{{IPV6}} listen [::]:443 ssl http2; 133 | server_name cfh.wapp.wii.com; 134 | 135 | ssl_certificate /etc/nginx/certs/snakeoil.crt; 136 | ssl_certificate_key /etc/nginx/certs/private/snakeoil.key; 137 | 138 | set_by_lua_block $REDIRECT_TYPE { 139 | return os.getenv("REDIRECT_TYPE"); 140 | } 141 | 142 | set_by_lua_block $ROOT_DOMAIN_PATH { 143 | return os.getenv("ROOT_DOMAIN_PATH"); 144 | } 145 | 146 | set $REDIRECT_URL "$REDIRECT_TYPE://{{ROOT_DOMAIN}}$ROOT_DOMAIN_PATH"; 147 | 148 | location ~* "^/eula/[0-9]{3}/[a-z]{2}\.html" { 149 | return 302 $REDIRECT_URL/; 150 | } 151 | 152 | location / { 153 | return 444; 154 | } 155 | 156 | include general.conf; 157 | } 158 | -------------------------------------------------------------------------------- /entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/ash 2 | # shellcheck shell=dash 3 | set -e 4 | 5 | # Input defaults and text to lower case 6 | DEBUG=${DEBUG:-"false"} && DEBUG=$(echo "$DEBUG" | tr "[:upper:]" "[:lower:]") 7 | REDIRECT_TYPE=${REDIRECT_TYPE:-"http"} && REDIRECT_TYPE=$(echo "$REDIRECT_TYPE" | tr "[:upper:]" "[:lower:]") 8 | ROOT_DOMAIN=${ROOT_DOMAIN:-"the.gate"} && ROOT_DOMAIN=$(echo "$ROOT_DOMAIN" | tr "[:upper:]" "[:lower:]") 9 | ROOT_DOMAIN_PATH=${ROOT_DOMAIN_PATH:-""} 10 | NGINX_ACCESS_LOG=${NGINX_ACCESS_LOG:-"false"} && NGINX_ACCESS_LOG=$(echo "$NGINX_ACCESS_LOG" | tr "[:upper:]" "[:lower:]") 11 | NGINX_ERROR_LOG=${NGINX_ERROR_LOG:-"false"} && NGINX_ERROR_LOG=$(echo "$NGINX_ERROR_LOG" | tr "[:upper:]" "[:lower:]") 12 | NGINX_ERROR_LOG_LEVEL=${NGINX_ERROR_LOG_LEVEL:-"warn"} && NGINX_ERROR_LOG_LEVEL=$(echo "$NGINX_ERROR_LOG_LEVEL" | tr "[:upper:]" "[:lower:]") 13 | TLS=${TLS:-"self"} && TLS=$(echo "$TLS" | tr "[:upper:]" "[:lower:]") 14 | CF_IP_CORRECTION=${CF_IP_CORRECTION:-"false"} && CF_IP_CORRECTION=$(echo "$CF_IP_CORRECTION" | tr "[:upper:]" "[:lower:]") 15 | CF_STRICT=${CF_STRICT:-"false"} && CF_STRICT=$(echo "$CF_STRICT" | tr "[:upper:]" "[:lower:]") 16 | OCSP_STAPLING=${OCSP_STAPLING:-"false"} && OCSP_STAPLING=$(echo "$OCSP_STAPLING" | tr "[:upper:]" "[:lower:]") 17 | SEVER_HASH_BUCKET_SIZE_OVERRIDE=${SEVER_HASH_BUCKET_SIZE_OVERRIDE:-"false"} && SEVER_HASH_BUCKET_SIZE_OVERRIDE=$(echo "$SEVER_HASH_BUCKET_SIZE_OVERRIDE" | tr "[:upper:]" "[:lower:]") 18 | 19 | # Input validation 20 | if [ "$DEBUG" != "true" ] && [ "$DEBUG" != "false" ]; then 21 | echo "[!] Invalid option for DEBUG, expected \"true\" or \"false\"" 22 | exit 1 23 | fi 24 | 25 | if ! echo "$ROOT_DOMAIN" | grep -E "^[a-z0-9-]+(\.[a-z0-9]+)+$" > /dev/null 2>&1; then 26 | echo "[!] Invalid option for ROOT_DOMAIN, invalid domain name used" 27 | exit 1 28 | fi 29 | 30 | if [ "$NGINX_ACCESS_LOG" != "true" ] && [ "$NGINX_ACCESS_LOG" != "false" ]; then 31 | echo "[!] Invalid option for NGINX_ACCESS_LOG, expected \"true\" or \"false\"" 32 | exit 1 33 | fi 34 | 35 | if [ "$NGINX_ERROR_LOG" != "true" ] && [ "$NGINX_ERROR_LOG" != "false" ]; then 36 | echo "[!] Invalid option for NGINX_ERROR_LOG, expected \"true\" or \"false\"" 37 | exit 1 38 | fi 39 | 40 | if [ "$NGINX_ERROR_LOG_LEVEL" != "debug" ] && [ "$NGINX_ERROR_LOG_LEVEL" != "info" ] && [ "$NGINX_ERROR_LOG_LEVEL" != "notice" ] && [ "$NGINX_ERROR_LOG_LEVEL" != "warn" ] && [ "$NGINX_ERROR_LOG_LEVEL" != "error" ] && [ "$NGINX_ERROR_LOG_LEVEL" != "crit" ] && [ "$NGINX_ERROR_LOG_LEVEL" != "alert" ] && [ "$NGINX_ERROR_LOG_LEVEL" != "emerg" ]; then 41 | echo "[!] Invalid option for NGINX_ERROR_LOG_LEVEL, expected \"debug\", \"info\", \"notice\", \"warn\", \"error\", \"crit\", \"alert\", or \"emerg\"" 42 | exit 1 43 | fi 44 | 45 | if [ "$SEVER_HASH_BUCKET_SIZE_OVERRIDE" != "true" ] && [ "$SEVER_HASH_BUCKET_SIZE_OVERRIDE" != "false" ]; then 46 | echo "[!] Invalid option for SEVER_HASH_BUCKET_SIZE_OVERRIDE, expected \"true\" or \"false\"" 47 | exit 1 48 | fi 49 | 50 | if [ "$REDIRECT_TYPE" != "http" ] && [ "$REDIRECT_TYPE" != "https" ]; then 51 | echo "[!] Invalid option for REDIRECT_TYPE, expected \"http\" or \"https\"" 52 | exit 1 53 | fi 54 | 55 | if [ "$TLS" != "self" ] && [ "$TLS" != "letsencrypt" ] && [ "$TLS" != "mount" ]; then 56 | echo "[!] Invalid option for TLS, expected \"self\", \"letsencrypt\", \"mount\"" 57 | exit 1 58 | fi 59 | 60 | if [ "$TLS" = "letsencrypt" ]; then 61 | if [ -z "$CERTBOT_EMAIL" ]; then 62 | echo "[!] CERTBOT_EMAIL not set for Let's Encrypt certificates" 63 | exit 1 64 | elif ! echo "$CERTBOT_EMAIL" | grep -E "^.+@.+\..+$" > /dev/null 2>&1; then 65 | echo "[!] Invalid option for CERTBOT_EMAIL, invalid eMail address used" 66 | exit 1 67 | fi 68 | fi 69 | 70 | if [ "$CF_IP_CORRECTION" != "true" ] && [ "$CF_IP_CORRECTION" != "false" ]; then 71 | echo "[!] Invalid option for CF_IP_CORRECTION, expected \"true\" or \"false\"" 72 | exit 1 73 | fi 74 | 75 | if [ "$CF_STRICT" != "true" ] && [ "$CF_STRICT" != "false" ]; then 76 | echo "[!] Invalid option for CF_STRICT, expected \"true\" or \"false\"" 77 | exit 1 78 | fi 79 | 80 | if [ "$OCSP_STAPLING" != "true" ] && [ "$OCSP_STAPLING" != "false" ]; then 81 | echo "[!] Invalid option for OCSP_STAPLING, expected \"true\" or \"false\"" 82 | exit 1 83 | fi 84 | 85 | if [ "$DEBUG" = "true" ]; then 86 | echo "=== DEBUG =====================================================" 87 | echo "REDIRECT_TYPE » $REDIRECT_TYPE" 88 | echo "ROOT_DOMAIN » $ROOT_DOMAIN" 89 | echo "ROOT_DOMAIN_PATH » $ROOT_DOMAIN_PATH" 90 | echo "NGINX_ACCESS_LOG » $NGINX_ACCESS_LOG" 91 | echo "NGINX_ERROR_LOG » $NGINX_ERROR_LOG" 92 | echo "NGINX_ERROR_LOG_LEVEL » $NGINX_ERROR_LOG_LEVEL" 93 | echo "SEVER_HASH_BUCKET_SIZE_OVERRIDE » $SEVER_HASH_BUCKET_SIZE_OVERRIDE" 94 | echo "TLS » $TLS" 95 | if [ -n "$CERTBOT_EMAIL" ]; then 96 | echo "CERTBOT_EMAIL » $CERTBOT_EMAIL" 97 | fi 98 | echo "CF_IP_CORRECTION » $CF_IP_CORRECTION" 99 | echo "CF_STRICT » $CF_STRICT" 100 | echo "OCSP_STAPLING » $OCSP_STAPLING" 101 | echo "===============================================================" 102 | fi 103 | 104 | # Set environmental variables 105 | if [ -n "$REDIRECT_TYPE" ]; then 106 | export REDIRECT_TYPE=$REDIRECT_TYPE 107 | fi 108 | 109 | if [ -n "$ROOT_DOMAIN_PATH" ]; then 110 | export ROOT_DOMAIN_PATH=$ROOT_DOMAIN_PATH 111 | fi 112 | 113 | # Delete all files in sites-available and sites-enabled. In case this isn't fresh instance 114 | rm -rf /etc/nginx/sites-available/* 2> /dev/null || true 115 | rm -rf /etc/nginx/sites-enabled/* 2> /dev/null || true 116 | 117 | # Copy (while overwriting files) from /etc/nginx/templates into /etc/nginx/ 118 | cp -rf /etc/nginx/templates/* /etc/nginx 119 | 120 | echo "[-] Using \"$REDIRECT_TYPE://$ROOT_DOMAIN$ROOT_DOMAIN_PATH\" as host" 121 | 122 | # Setup Cloudflare IP correction 123 | if [ "$CF_IP_CORRECTION" = "false" ]; then 124 | echo "[-] Skipping Cloudflare IP correction" 125 | else 126 | echo "[-] Enabling Cloudflare IP correction" 127 | CF_IPV4="" 128 | # TODO: Throw errors for and halt execution for CURL issues 129 | for i in $(curl -s https://www.cloudflare.com/ips-v4); do 130 | CF_IPV4="$CF_IPV4 set_real_ip_from $i;\n" 131 | done 132 | sed -i "s,#{{CF_IPV4}},$CF_IPV4,g" /etc/nginx/nginx.conf 133 | 134 | CF_IPV6="" 135 | # TODO: Throw errors for and halt execution for CURL issues 136 | for i in $(curl -s https://www.cloudflare.com/ips-v6); do 137 | CF_IPV6="$CF_IPV6 set_real_ip_from $i;\n" 138 | done 139 | sed -i "s,#{{CF_IPV6}},$CF_IPV6,g" /etc/nginx/nginx.conf 140 | 141 | sed -i "s/#{{CF_IP}}/real_ip_header CF-Connecting-IP;/g" /etc/nginx/nginx.conf 142 | fi 143 | 144 | # Rename ROOT_DOMAIN 145 | if [ -f /etc/nginx/sites-available/ROOT_DOMAIN ]; then 146 | mv /etc/nginx/sites-available/ROOT_DOMAIN "/etc/nginx/sites-available/$ROOT_DOMAIN" 147 | fi 148 | 149 | # Setup the logging options in nginx.conf 150 | echo "[-] Configuring logging settings..." 151 | if [ "$NGINX_ACCESS_LOG" = "true" ]; then 152 | sed -i "s/access_log off; #/access_log/g" "/etc/nginx/nginx.conf" 153 | fi 154 | 155 | if [ "$NGINX_ERROR_LOG" = "true" ]; then 156 | sed -i "s/error_log \/dev\/null; #/error_log/g" "/etc/nginx/nginx.conf" 157 | fi 158 | 159 | sed -i "s/{{NGINX_ERROR_LOG_LEVEL}}/$NGINX_ERROR_LOG_LEVEL/g" "/etc/nginx/nginx.conf" 160 | 161 | # Toggle server_names_hash_bucket_size override 162 | if [ "$SEVER_HASH_BUCKET_SIZE_OVERRIDE" = "true" ]; then 163 | echo "[-] Enabling server_names_hash_bucket_size override..." 164 | sed -i "s/#{{SERVER_NAME_HASH_BUCKET_SIZE}}/server_names_hash_bucket_size 64;/g" "/etc/nginx/nginx.conf" 165 | fi 166 | 167 | # Enable all modules currently in modules 168 | echo "[-} Enabling all modules in /etc/nginx/modules directory..." 169 | for file in /etc/nginx/modules/*; do 170 | ln -sf "$file" "/etc/nginx/modules-enabled/" 171 | done 172 | 173 | # Replace variables in the files in the `/etc/nginx/sites-available/` directory 174 | echo "[-] Replacing variables in vHost files..." 175 | for file in /etc/nginx/sites-available/*; do 176 | sed -i "s/{{ROOT_DOMAIN}}/$ROOT_DOMAIN/g" "$file" 177 | 178 | # TODO: Bind to IPv4 interface if it's available 179 | 180 | # Bind to IPv6 interface if it's available 181 | if [ "$(ip -6 addr)" != "" ]; then 182 | sed -i "s/#{{IPV6}} //g" "$file" 183 | fi 184 | 185 | if [ "$file" = "/etc/nginx/sites-available/$ROOT_DOMAIN" ]; then 186 | # Strict TLS settings for Cloudflare 187 | if [ "$CF_STRICT" = "false" ]; then 188 | echo "[-] Skipping strict Cloudflare TLS" 189 | else 190 | echo "[-] Enabling strict Cloudflare TLS" 191 | sed -i "s/#CF_STRICT //g" "$file" 192 | # TODO: Throw errors for and halt execution for CURL issues 193 | curl -L https://developers.cloudflare.com/ssl/static/authenticated_origin_pull_ca.pem -o /etc/nginx/certs/origin-pull-ca.pem 194 | chmod 644 /etc/nginx/certs/origin-pull-ca.pem 195 | fi 196 | 197 | # Use Let's Encrypt TLS certificates 198 | if [ "$TLS" = "letsencrypt" ]; then 199 | echo "[-] Using Let's Encrypt for TLS certificates" 200 | 201 | echo "[-] Running certbot to get TLS certificates for $ROOT_DOMAIN" 202 | nginx 203 | certbot certonly -n --agree-tos --no-eff-email --email "$CERTBOT_EMAIL" --webroot --webroot-path /var/www/themes/default -d "$ROOT_DOMAIN" --post-hook "nginx -s reload" 204 | nginx -s stop 205 | 206 | sed -i "s/#{{LETSENCRYPT}} //g" "$file" 207 | fi 208 | 209 | # Use self signed TLS certificates 210 | if [ "$TLS" = "self" ]; then 211 | echo "[-] Using self signed TLS certificates" 212 | sed -i "s/#{{SELF}} //g" "$file" 213 | fi 214 | 215 | # Use mounted TLS certificates 216 | if [ "$TLS" = "mount" ]; then 217 | echo "[-] Expecting Docker mounts for TLS. Map your certificates to the following locations:" 218 | echo "/etc/nginx/certs/fullchain.pem" 219 | echo "/etc/nginx/certs/private/privkey.pem" 220 | echo "/etc/nginx/certs/chain.pem" 221 | 222 | sed -i "s/#{{MOUNT}} //g" "$file" 223 | fi 224 | 225 | # OCSP Stapling 226 | if [ "$OCSP_STAPLING" = "false" ]; then 227 | echo "[-] Skipping OCSP Stapling" 228 | else 229 | echo "[-] Enabling OCSP Stapling" 230 | sed -i "s/#{{OCSP_STAPLING}} //g" "$file" 231 | fi 232 | fi 233 | done 234 | 235 | # Generate snakeoil certs for domains found in hijacked domains (Up to 8 sections for the domain) 236 | LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1 FAKETIME="@1969-12-31 23:59:59" openssl req -nodes -x509 -newkey rsa:4096 -keyout /etc/nginx/certs/private/snakeoil.key -out /etc/nginx/certs/snakeoil.crt -days 36500 -subj '/CN=*/CN=*.*/CN=*.*.*/CN=*.*.*.*/CN=*.*.*.*.*/CN=*.*.*.*.*.*/CN=*.*.*.*.*.*.*/CN=*.*.*.*.*.*.*.*' > /dev/null 2>&1 237 | chmod 644 /etc/nginx/certs/snakeoil.crt 238 | chmod 640 /etc/nginx/certs/private/snakeoil.key 239 | 240 | # Enable all vHosts in site-available 241 | echo "[-} Enabling all vHosts in /etc/nginx/sites-available directory..." 242 | for file in /etc/nginx/sites-available/*; do 243 | ln -sf "$file" "/etc/nginx/sites-enabled/" 244 | done 245 | 246 | # Make network test files if they don't exist, fails silently 247 | mkdir -p /var/www/ps-net-tests || true 248 | if [ ! -f /var/www/ps-net-tests/get_2m ] || [ ! -f /var/www/ps-net-tests/get_6m ]; then 249 | echo "[-] Generating binary files for network tests..." 250 | truncate -s 2M /var/www/ps-net-tests/get_2m || true 251 | truncate -s 6M /var/www/ps-net-tests/get_6m || true 252 | fi 253 | 254 | # Move system update meta files if they don't exist in the correct location, fails silently 255 | mkdir -p /var/www/ps-sys-updates || true 256 | if [ -z "$(ls -A /var/www/ps-sys-updates)" ]; then 257 | cp -r /srv/ps-sys-updates /var/www || true 258 | fi 259 | 260 | # Make PUP file directory if it doesn't exist, fails silently 261 | mkdir -p /var/www/PUPs || true 262 | if [ -z "$(ls -A /var/www/PUPs)" ]; then 263 | cp -r /srv/PUPs /var/www || true 264 | fi 265 | 266 | echo "[-] Starting NGINX..." 267 | exec "$@" 268 | --------------------------------------------------------------------------------