├── 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 |
--------------------------------------------------------------------------------