Add EndGame version 2

This commit is contained in:
Onion Limited 2021-03-28 19:42:45 +00:00
parent 770d6da100
commit 4a766ef0ac
No known key found for this signature in database
GPG Key ID: E4B6CAC49B242A44
30 changed files with 2191 additions and 705 deletions

8
.idea/endgame.iml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/endgame.iml" filepath="$PROJECT_DIR$/.idea/endgame.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

39
.idea/workspace.xml Normal file
View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="29df5c5c-3c59-4700-af86-2362f0062d00" name="Default Changelist" comment="">
<change beforePath="$PROJECT_DIR$/site.conf" beforeDir="false" afterPath="$PROJECT_DIR$/site.conf" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="GOROOT" path="$USER_HOME$/.gvm/gos/go1.14" />
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="ProjectId" id="1l8K8ywGwL2SwuuTElCHEoZyLHy" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
<property name="go.import.settings.migrated" value="true" />
<property name="go.sdk.automatically.set" value="true" />
<property name="last_opened_file_path" value="$PROJECT_DIR$/../docker-tor-hidden-service" />
<property name="settings.editor.selected.configurable" value="ide.date.format" />
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="WindowStateProjectService">
<state x="473" y="214" key="FileChooserDialogImpl" timestamp="1607015731915">
<screen x="0" y="0" width="1368" height="768" />
</state>
<state x="473" y="214" key="FileChooserDialogImpl/0.0.1368.768@0.0.1368.768" timestamp="1607015731915" />
<state x="380" y="251" width="616" height="512" key="find.popup" timestamp="1606975273843">
<screen x="0" y="0" width="1368" height="768" />
</state>
<state x="380" y="251" width="616" height="512" key="find.popup/0.0.1368.768@0.0.1368.768" timestamp="1606975273843" />
</component>
</project>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,24 @@
Onionbalance is the core of your scalability system. This is what makes all your seperate front onions work as a unified front!
However it has it's limits. Onion Services have a limit to the amount of introduction points within a specific descriptor they can have and how large the descriptor can be.
These limits lead to some pretty big problems.
1) There is a limit to the amount of fronts a single onion can have.
2) Each front can only have a certain amount of introduction points themselves.
3) Under attack descriptors can become spent before they can be replaced.
"spent" descriptors are when all the introduction points to the onion service are no longer accepting clients. Introduction points have a certain amount of introduction cell requests it will accept before not accepting anymore. These requests are the Tor's main DDOS problem. By specifically requesting tons of introduction cells, which are computationally cheap to do, an attacker can overload an onion service. The only way to protect against this right now is by scaling out.
First read the README.MD from the main directory and install onionbalance to a 2CPU 2GB RAM server seperate from your cluster of fronts.
After you have installed onionbalance you will need to cd into the directory go to /onionbalance/hs_v3/params.py and change
N_INTROS_PER_INSTANCE = 2 -> N_INTROS_PER_INSTANCE = 1
Save the params.py file and go back to the main onionbalance directory. Run
python3 setup.py install
and then you can setup the configuration
**When setting up your onionbalance configuration limit the amount of fronts to 18! Setting it higher you can get descriptor or introduction issues which will cause your onion's descriptor to not be correctly pushed.**

View File

@ -24,7 +24,7 @@ service tor stop
rm /etc/tor/torrc rm /etc/tor/torrc
mv torrc /etc/tor/torrc mv torrc /etc/tor/torrc
git clone https://gitlab.torproject.org/asn/onionbalance.git git clone https://github.com/zscole/onionbalance.git
cd onionbalance cd onionbalance
python3 setup.py install python3 setup.py install

View File

@ -1,27 +1,47 @@
# EndGame - Onion Service DDOS Prevention Front System # EndGame V2 - Onion Service DDOS Prevention Front System
Provided by [Dread](http://dreadytofatroptsdj6io7l3xptbet6onoyno2yv7jicoxknyazubrad.onion/) and [White House Market](http://dreadytofatroptsdj6io7l3xptbet6onoyno2yv7jicoxknyazubrad.onion/d/WhiteHouseMarket). With help from [Big Blue Market](http://dreadytofatroptsdj6io7l3xptbet6onoyno2yv7jicoxknyazubrad.onion/d/BigBlueMarket) and [Empire Market](dreadytofatroptsdj6io7l3xptbet6onoyno2yv7jicoxknyazubrad.onion/d/EmpireMarket). V2 Provided by [Dread](http://dreadytofatroptsdj6io7l3xptbet6onoyno2yv7jicoxknyazubrad.onion/) and [White House Market](http://dreadytofatroptsdj6io7l3xptbet6onoyno2yv7jicoxknyazubrad.onion/d/WhiteHouseMarket).
**Should be used with this [onionbalance](https://github.com/zscole/onionbalance) process for distinct descriptors. Use one onion for everything.**
EndGame is EndGame is
- a front system designed to protect the core application servers on an onion service in a safe and private way. - a front system designed to protect the core application servers on an onion service in a safe and private way.
- locally complied and locally run (no trusted or middle party). - locally complied and locally run (no trusted or middle party).
- a combination of multiple different technologies working together in harmony (listed below). - a combination of multiple different technologies working together in harmony (listed below).
- FREE FOR ALL TO USE! - FREE FOR ALL TO USE!
- *arguably* magic ㄟ( ▔, ▔ )ㄏ - *arguably* magic ㄟ( ▔, ▔ )ㄏ
# Main Features # Main Features
- Fully scripted and easily deploy-able (for mass scaling!) on blank Debian 10 systems. - Fully scripted and easily deploy-able (for mass scaling!) on blank Debian 10 systems.
- Full featured NGINX LUA script to filter packets and provide a captcha directly using the NGINX layer. - Full featured NGINX LUA script to filter packets and provide a captcha directly using the NGINX layer.
- Rate limiting via Tor's V3 onion service circuit ID system with secondary rate limiting based on a testcookie like system. - Rate limiting via Tor's V3 onion service circuit ID system with secondary rate limiting based on a testcookie like system.
- Easy Configuration for both local and remote (over Tor) front systems. - Easy Configuration for both local and remote (over Tor) front systems.
- Easily configurable and change-able to meet an onion service's needs. - Easily configurable and change-able to meet an onion service's needs.
It can also: It can also:
- Cause you to grow a bigger dick than the asshole DDOSER (true *figurally*, lies *probably*) - Cause you to grow a bigger dick than the asshole DDOSER (true *figurally*, lies *probably*)
- Save you millions of dollars do to DDOSER's downing your site for ransom or for their extorting fees. - Save you millions of dollars do to DDOSER's downing your site for ransom or for their extorting fees.
- Make it look like you know what the fuck you are doing. - Make it look like you know what the fuck you are doing.
# V2 Updates
V2 EndGame has updates to the broken captcha generation process using a clock facing captcha. It includes extra features like
- updated documentation
- load balanced Tor socks processes for more stable socks_passes
- unix listening instead of ports for performance, stability, and security
- true randomization for captcha and cookie generation
- simple queue system (time based, read below)
- various theme configuration options right on the setup file
- dependency script to get all the dependencies only once. Effectively snapshotting all dependencies preventing future dependency repo exploits in the VERY unlikely case a repo was to get compromised. Paranoia mode.
- bug fixes and various performance tunings
### Notes About Queue System
V2 introduces a queue system which effectively prevents CPU exhaustion from mass get attacks. The clock captcha generation is computationally intensive and specifically vulnerable to this kind of attack. By limiting the amount of connections and amount of captcha tries it greatly reduces the CPU cycles to handle the attack.
In this version there is a simple time on line 110 of the `lua/cap.lua` file which gets checked on line 143. It is recommended to variate this value by attaching a sliding scale time circumstance base on front CPU load. Exponential functions based on the "/proc/stat" value. If you do that, keep the curve private because there is always an "ideal" attack value.
When you set set the time value update the `queue.html` file via a script to rewrite the meta refresh variable.
### Tech Overview ### Tech Overview
@ -51,7 +71,7 @@ Libraries:
* [LUA Resty Cookie](https://github.com/cloudflare/lua-resty-cookie) - Provides cookie manipulation * [LUA Resty Cookie](https://github.com/cloudflare/lua-resty-cookie) - Provides cookie manipulation
* [LUA Resty Session](https://github.com/bungle/lua-resty-session) - Provides session manipulation * [LUA Resty Session](https://github.com/bungle/lua-resty-session) - Provides session manipulation
* [LUA Resty AES](https://github.com/c64bob/lua-resty-aes/raw/master/lib/resty/aes_functions.lua) - AES Functions file for LUA. Used for shared session cookies. * [LUA Resty AES](https://github.com/c64bob/lua-resty-aes/raw/master/lib/resty/aes_functions.lua) - AES Functions file for LUA. Used for shared session cookies.
* [LUA GD](https://github.com/ittner/lua-gd/) - GD image generation bindings For LUA * [LUA Resty Random](https://github.com/bungle/lua-resty-random) - A *true* random number library for OpenResty.
### Configuration ### Configuration
@ -64,17 +84,23 @@ There are options. Such as:
* TORAUTHPASSWORD - Password which is used for your Tor Control Port Authentication with NGINX. Alphanumeric without spaces (example: passwordIcanremembertyping) * TORAUTHPASSWORD - Password which is used for your Tor Control Port Authentication with NGINX. Alphanumeric without spaces (example: passwordIcanremembertyping)
* KEY - Alphanumeric Key for the shared front session key. Random between 64-128 would do fine. (example: isthis64charactorsalreadyicantbelieveitwowsocoolwaitnotyetohdarn) * KEY - Alphanumeric Key for the shared front session key. Random between 64-128 would do fine. (example: isthis64charactorsalreadyicantbelieveitwowsocoolwaitnotyetohdarn)
* SALT - 8 character salt used with the key. 8 random alphanumeric characters (example: saltsalt) * SALT - 8 character salt used with the key. 8 random alphanumeric characters (example: saltsalt)
* SESSION_LENGTH - In seconds the amount of time until cookie timeout. Set it high as you can. (example: 3600 [aka 1 hour])
* HEXCOLOR - HEX color put into the css file to be not purple but your main site's color. Any CSS hex will work. (example: #9b59b6) * HEXCOLOR - HEX color put into the css file to be not purple but your main site's color. Any CSS hex will work. (example: #9b59b6)
* SITENAME - Site name automatically put in the captcha html file. (example: dread)
* LOCALPROXY - If true will set proxy_pass url to the PROXYPASSURL and disable load balanced Tor processes. If enabled will take the BACKENDONIONURL and configure load balanced socks_pass. It's highly recommended to proxy locally if possible.
* PROXYPASSURL - The local url used to proxy_pass all good connections. Not used if LOCALPROXY set to false.
* BACKENDONIONURL - The remote onion service endpoint. This onion is not public and should have no rate limiting or filtering on it. Generally the "core" server onion. Not used if LOCALPROXY set to true.
There is also some editing you need to do in the `caphtml_d.lua`, `naxsi_whitelist.rules`, `site.conf`, and `torrc` files. There is also some editing you need to do in the `caphtml_d.lua`, `naxsi_whitelist.rules`, `site.conf`, and `torrc` files.
- `resty/caphtml_d.lua` - Two Base64 Images. The favicon (line 143) and main logo (line 162). You can use [this](https://base64.guru/converter/encode/image/ico) for the favicon and [this](https://base64.guru/converter/encode/image) for the main logo. - `resty/caphtml_d.lua` - Two Base64 Images. The favicon (line 143) and main logo (line 162). You can use [this](https://base64.guru/converter/encode/image/ico) for the favicon and [this](https://base64.guru/converter/encode/image) for the main logo.
- `queue.html` - Two base64 images. Search for <link href=" for the favicon and .logobgimg for the main logo. Update accordingly.
- `naxsi_whitelist.rules` - NAXSI's Whitelist Rules with some internal rules [see this](https://github.com/nbs-system/naxsi/wiki/internal-rules). To be configured for your specific application's use case. - `naxsi_whitelist.rules` - NAXSI's Whitelist Rules with some internal rules [see this](https://github.com/nbs-system/naxsi/wiki/internal-rules). To be configured for your specific application's use case.
- `site.conf` - Line 110 and 111 has the two rate limiting EndGame does. One by the circuit ID and one by the cookie. Depending on how your site calls files you may need to change these values. - `site.conf` - Line 114 and 115 has the two rate limiting EndGame does. One by the circuit ID and one by the cookie. Depending on how your site calls files you may need to change these values.
- Defaulty set to 3 consistent on line 110 and 111. 110 for circuit. 111 for cookie. - Defaulty set to 6 consistent on line 114 and 115. 114 for circuit. 115 for cookie.
- Line 245 has a nodelay burst of 6 for the circuit. Line 251 has a nodelay burst of 6 for the cookie. - Line 263 has a nodelay burst of 10 for the circuit. Line 269 has a nodelay burst of 10 for the cookie.
- Line 268-272 socks proxy_pass system. If you want EndGame to pass the filtered request over Tor you uncomment the socks_* lines and change the exampleprivatev3onion.onion to your core webserver's private v3 address. - Line 288-293 socks proxy_pass system. If you want EndGame to pass the filtered request over Tor you uncomment the socks_* lines and change the BACKENDONIONURL variable in the setup.sh file to your core webserver's private v3 address. If you do this you will need to comment the proxy_pass.
- Line 273 regular proxy_pass. If you have a secure local connection you want to use the regular proxy_pass for reliability and latency improvements. Just change it to your core webserver's private IP. - Line 273 regular proxy_pass. If you have a secure local connection you want to use the regular proxy_pass for reliability and latency improvements. Just change it to your core webserver's private IP. This is set as the default for performance reasons.
- `torrc` - Depending on what you set your burst as change the HiddenServiceMaxStreams value to that plus 2. - `torrc` - Depending on what you set your burst as change the HiddenServiceMaxStreams value to that plus 2.
### Setup Process ### Setup Process
@ -97,7 +123,7 @@ After the files are transferred make the setup.sh file executable and run it wit
##### STEP 4: ##### STEP 4:
Scale out. Without scaling out you are bring a knife to a gun fight. At minimum you need 3 fronts. Onionbalance v3 doesn't have distinct descriptors which means if you go past about 6-9 fronts there might be some descriptor sizing issues on the default onionbalance setup. Endgame does make it much harder to take you down but you need to scale to keep up. Otherwise your front's Tor will get overloaded and you will go down. It's a dick measuring contest between you and the attacker. By scaling out you are effectively adding more length to your dick. Scale out. Without scaling out you are bring a knife to a gun fight. At minimum you need 3 fronts. Onionbalance v3 does have distinct descriptors now. You can scale to the moon and back. Endgame does make it much harder to take you down but you need to scale to keep up. Otherwise your front's Tor will get overloaded and you will go down. It's a dick measuring contest between you and the attacker. By scaling out you are effectively adding more length to your dick.
##### STEP 5: ##### STEP 5:
@ -107,4 +133,4 @@ After scaling out with multiple fronts add all their onion addresses to onionbal
EndGame isn't perfect. It can't protect against introduction cell type attacks (the Tor project will need to add POW at the introduction points to fix that). But it does provide good protection and scaling which makes it much harder to take you down overall for whatever people throw at you. EndGame isn't perfect. It can't protect against introduction cell type attacks (the Tor project will need to add POW at the introduction points to fix that). But it does provide good protection and scaling which makes it much harder to take you down overall for whatever people throw at you.
This all is a major step forward for the darknet community. Never give in to the extorting DDOSERS. You are only paying to be attacked with more power in the future. Instead stand together and say "NO". As a united front we will reach heights never seen before. This all is a major step forward for the darknet community. Never give in to the extorting DDOSERS. You are only paying to be attacked with more power in the future. Instead stand together and say "NO". As a united front we will reach heights never seen before.

321
cap_d.css
View File

@ -1,3 +1,320 @@
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}strong{font-weight:bold}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:'';content:none}table{border-collapse:collapse;border-spacing:0}*:focus{outline:0}input,select,textarea{border:0;box-shadow:0}html{height:100%}body{height:100%;line-height:1;background:#1A1E23;font-family:roboto, helvetica, sans-serif, arial, verdana, tahoma;font-size:16px;color:#fff}.container{width:100%;margin:0 auto;min-height:100%;position:relative;max-height:100vh;overflow:hidden}.container>.inner{position:absolute;top:50%;left:0;right:0;margin:0 auto;text-align:center;-webkit-transform:translateY(-50%);-moz-transform:translateY(-50%);transform:translateY(-50%);}.container>.inner>.logo{display:inline-block;vertical-align:middle;text-decoration:none;margin-bottom:10px}.container>.inner>.logo>.square{display:inline-block;vertical-align:middle;width:40px;height:40px;background-color:HEXCOLOR;background-size:24px 24px;background-position:center center;background-repeat:no-repeat;margin-right:10px}.container>.inner>.logo>.text{display:inline-block;vertical-align:middle;font-size:30px;color:#fff;font-weight:700}.container>.inner>.date{display:block;text-align:center;font-size:42px}.container>.inner>.date>.day{color:HEXCOLOR;font-weight:bold}.signed{display:block;width:400px;height:150px;margin-top:20px;margin:20px auto 0 auto;}.signed>textarea{margin:0 auto;width:400px;height:150px;min-width:400px;max-width:400px;display:block;padding:15px;background:#fff;border:1px solid HEXCOLOR;min-height:150px;max-height:150px;}p{margin:0 auto 20px auto;max-width:300px;}form.ddos_form .captcha-input{margin:0 auto 20px auto;display: block;width:300px;font-size:0;}form.ddos_form .captcha-input input{display:inline-block;vertical-align:top;height:50px;width:calc(100% - 150px);outline:0;border:none;font-size:16px;color:#000;padding:0 15px;line-height:50px;}form.ddos_form .captcha-input img{display:inline-block;vertical-align:top;}form.ddos_form button{border-radius:3px;display:block;width:300px;margin:0 auto;background:HEXCOLOR;cursor:pointer;color:#fff;font-size:16px;text-transform:uppercase;text-align:center;height:40px;line-height:40px;outline:0;border:none;} html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}
.captchav2 {text-align: center;width: 100%;}.captchav2 > .imgWrap {display: inline-block;vertical-align: middle;width: 150px;height: 150px;background-size: cover;background-position: center center;}.captchav2 > .inputWrap {display: inline-block;vertical-align: middle;width: 120px;height: 120px;position: relative;margin-left: 15px;}.captchav2 > .inputWrap > div {position: absolute;width: 30px;height: 30px;cursor: pointer;border-radius: 100%;border: 2px solid #fff;} .captchav2 > .inputWrap > div.c1 { top: 0; left: 0; }.captchav2 > .inputWrap > div.c2 { top: 0; left: 0; right: 0; margin: 0 auto; }.captchav2 > .inputWrap > div.c3 { top: 0; right: 0; } .captchav2 > .inputWrap > div.c4 { top: 50%; left: 0; margin-top: -15px; }.captchav2 > .inputWrap > div.c5 { top: 50%; left: 0; right: 0; margin: -15px auto 0 auto; }.captchav2 > .inputWrap > div.c6 { top: 50%; right: 0; margin-top: -15px; } .captchav2 > .inputWrap > div.c7 { bottom: 0; left: 0; }.captchav2 > .inputWrap > div.c8 { bottom: 0; left: 0; right: 0; margin: 0 auto; }.captchav2 > .inputWrap > div.c9 { bottom: 0; right: 0; } .captchav2 > .inputWrap > input[type="checkbox"] {opacity: 0;margin: 0;padding: 0;cursor: pointer;position: absolute;width: 30px;height: 30px;z-index: 5;}.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(1) { top: 0; left: 0; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(1):checked ~ .c1 { background: #fff; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(2) { top: 0; left: 0; right: 0; margin: 0 auto; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(2):checked ~ .c2 { background: #fff; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(3) { top: 0; right: 0; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(3):checked ~ .c3 { background: #fff; } .captchav2 > .inputWrap > input[type="checkbox"]:nth-child(4) { top: 50%; left: 0; margin-top: -15px; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(4):checked ~ .c4 { background: #fff; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(5) { top: 50%; left: 0; right: 0; margin: -15px auto 0 auto; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(5):checked ~ .c5 { background: #fff; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(6) { top: 50%; right: 0; margin-top: -15px; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(6):checked ~ .c6 { background: #fff; } .captchav2 > .inputWrap > input[type="checkbox"]:nth-child(7) { bottom: 0; left: 0; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(7):checked ~ .c7 { background: #fff; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(8) { bottom: 0; left: 0; right: 0; margin: 0 auto; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(8):checked ~ .c8 { background: #fff; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(9) { bottom: 0; right: 0; }.captchav2 > .inputWrap > input[type="checkbox"]:nth-child(9):checked ~ .c9 { background: #fff; } strong{font-weight:bold}
body{line-height:1}ol,ul{list-style:none}
blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:'';content:none}
table{border-collapse:collapse;border-spacing:0}*:focus{outline:0}input,select,textarea{border:0;box-shadow:0}
html{height:100%}body{height:100%;line-height:1;background:#1A1E23;
font-family:roboto, helvetica, sans-serif, arial, verdana, tahoma;font-size:16px;color:#fff}
.container {width:100%;margin:0 auto;min-height:100%;position:relative;max-height:100vh;overflow:hidden; }
.container>.inner{position:absolute;top:50%;left:0;right:0;margin:0 auto;text-align:center;-webkit-transform:translateY(-50%);-moz-transform:translateY(-50%); transform:translateY(-50%);}
.container>.inner>.logo{display:inline-block;vertical-align:middle;text-decoration:none;margin-bottom:10px}
.container>.inner>.logo>.square{display:inline-block;vertical-align:middle;width:40px;height:40px;background-color:#9b59b6;background-size:24px 24px;background-position:center center;background-repeat:no-repeat;margin-right:10px}
.container>.inner>.logo>.text{display:inline-block;vertical-align:middle;font-size:30px;color:#fff;font-weight:700}.container>.inner>.date{display:block;text-align:center;font-size:42px}
.container>.inner>.date>.day{color:#9b59b6;font-weight:bold}.signed{display:block;width:400px;height:150px;margin-top:20px;margin:20px auto 0 auto;}
.signed>textarea {margin:0 auto;width:400px;height:150px;min-width:400px;max-width:400px;display:block;padding:15px;background:#fff;border:1px solid #9b59b6;min-height:150px;max-height:150px;}p{margin:0 auto 20px auto;max-width:300px;}form.ddos_form .captcha-input{margin:0 auto 20px auto;display: block;width:300px;font-size:0;}form.ddos_form .captcha-input input{display:inline-block;vertical-align:top;height:50px;width:calc(100% - 150px);outline:0;border:none;font-size:16px;color:#000;padding:0 15px;line-height:50px;}form.ddos_form .captcha-input img{display:inline-block;vertical-align:top;}form.ddos_form button{border-radius:3px;display:block;width:300px;margin:0 auto;background:#9b59b6;cursor:pointer;color:#fff;font-size:16px;text-transform:uppercase;text-align:center;height:40px;line-height:40px;outline:0;border:none;}
.captchav2 {text-align: center;width: 100%;}
.captchav2 > .imgWrap {
display: inline-block;
text-align: center;
vertical-align: middle;
width: 195px;
height: 195px;
z-index: 4;
background-size: cover;
background-position: center center;
transition: transform .5s ease;
}
.captchav2 > .inputWrap {text-align: center; display:inline-block;vertical-align: middle;width: 184px;height: 184px;position: relative;margin-left: 0px; transition: transform .5s ease; }
.captchav2 > .inputWrap > div {position: absolute;width: 60px;height: 60px;cursor: pointer;border-radius: 100%;border: 2px solid #fff;}
.expire {
display: inline-block;
vertical-align: top;
width: 100px;
height: 50px;
position: relative;
background: #888;
animation: timer-warning 1s 1;
animation-fill-mode: forwards;
animation-delay: 50s;
margin-bottom: 6px;
border-radius: 3px;
}
.expire>.timer {
position: absolute;
top: 0;
left: 0;
width: 100px;
overflow: hidden;
}
.expire>.timer>.time-part-wrapper {
display: inline-block;
vertical-align: top;
height: 50px;
line-height: 50px;
color: #fff;
}
.expire>.timer>.time-part-wrapper:first-child:after {
content: ':';
display: inline-block;
vertical-align: top;
width: 15px;
height: 50px;
line-height: 50px;
text-align: center;
font-size: 22px;
}
.expire>.timer>.time-part-wrapper>.time-part {
display: inline-block;
vertical-align: top;
width: 15px;
position: relative;
animation: timer-expire;
animation-fill-mode: forwards;
animation-delay: 60s;
}
.expire>.timer>.time-part-wrapper>.time-part>.digit-wrapper {
position: absolute;
top: 0;
left: 0;
width: 15px;
text-align: center;
}
.expire>.timer>.time-part-wrapper>.time-part>.digit-wrapper>.digit {
display: block;
width: 100%;
text-align: center;
height: 50px;
line-height: 50px;
font-size: 22px;
}
.expire>.timer>.time-part-wrapper>.time-part.seconds.tens>.digit-wrapper {
top: -50px;
animation: timer-seconds-tens 50s 1;
animation-fill-mode: forwards;
animation-delay: 1s;
}
.expire>.timer>.time-part-wrapper>.time-part.seconds.ones>.digit-wrapper {
animation: timer-seconds-ones 10s 6;
}
.expire>.timer>.time-part-wrapper>.time-part.hundredths.tens>.digit-wrapper {
animation: timer-seconds-ones 1s 60;
}
.expire>.timer>.time-part-wrapper>.time-part.hundredths.ones>.digit-wrapper {
animation: timer-seconds-ones 500ms 120;
}
@-webkit-keyframes timer-seconds-tens {
0% {
top: -50px;
}
19% {
top: -50px;
}
20% {
top: -100px;
}
39% {
top: -100px;
}
40% {
top: -150px;
}
59% {
top: -150px;
}
60% {
top: -200px;
}
79% {
top: -200px;
}
80% {
top: -250px;
}
99% {
top: -250px;
}
100% {
top: -300px;
}
}
@-webkit-keyframes timer-seconds-ones {
0% {
top: 0;
}
9% {
top: 0;
}
10% {
top: -50px;
}
19% {
top: -50px;
}
20% {
top: -100px;
}
29% {
top: -100px;
}
30% {
top: -150px;
}
39% {
top: -150px;
}
40% {
top: -200px;
}
49% {
top: -200px;
}
50% {
top: -250px;
}
59% {
top: -250px;
}
60% {
top: -300px;
}
69% {
top: -300px;
}
70% {
top: -350px;
}
79% {
top: -350px;
}
80% {
top: -400px;
}
89% {
top: -400px;
}
90% {
top: -450px;
}
99% {
top: -450px;
}
100% {
top: -500px;
}
}
@-webkit-keyframes timer-warning {
from {
background: #1A1E23;
}
to {
background: #E7943C;
}
}
@-webkit-keyframes timer-expire {
from {
color: #000;
}
to {
color: #e7643c;
}
}
@-webkit-keyframes button-expired {
from {
visibility: hidden;
}
to {
visibility: visible;
}
}
@-webkit-keyframes button-before {
from {
visibility: visible;
}
to {
visibility: hidden;
}
}
select {
font-size: 18px;
border: 1px solid #ccc;
padding: 0 10px;
height: 30px;
line-height: 40px;
color: #ffffe9;
background: #1a1e23;
}
.center {
text-align: center;
}
form.ddos_form button.before {
animation: button-before;
animation-fill-mode: forwards;
animation-delay: 60s;
}
form.ddos_form button.expired {
visibility: hidden;
background: #E74C3C;
animation: button-expired;
animation-fill-mode: forwards;
animation-delay: 60s;
margin-top: -40px;
}

BIN
font.ttf

Binary file not shown.

View File

@ -1,60 +0,0 @@
#!/usr/bin/python3 -u
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
import random
import os
def generate_background():
random.seed()
unicode_chars = (
"\u2605",
"\u2606",
"\u2663",
"\u2667",
"\u2660",
"\u2664",
"\u2662",
"\u2666",
"\u263a",
"\u263b",
"\u26aa",
"\u26ab",
"\u2b53",
"\u2b54",
"\u2b00",
"\u2b08",
"\u2780",
"\u278a",
"\u267c",
"\u267d",
"\u25b2",
"\u25b3",
)
unicode_max = len(unicode_chars)
try:
for i in range(0, 25):
im_cropped = Image.new('RGB', (150, 150),
(random.randrange(120, 255), random.randrange(120, 255), random.randrange(120, 255)))
origwidth, origheight = im_cropped.size
watermark = Image.new("RGBA", im_cropped.size)
waterdraw = ImageDraw.ImageDraw(watermark, "RGBA")
number_of_shapes = random.randrange(10, 15)
for step in range(0, number_of_shapes):
fillcolor = (
random.randrange(0, 255), random.randrange(0, 255), random.randrange(0, 255),
random.randrange(240, 255))
u_char = unicode_chars[random.randrange(0, unicode_max)]
font = ImageFont.truetype("/etc/nginx/font.ttf", random.randrange(25, 30))
waterdraw.text((random.randrange(-10, 130), random.randrange(-10, 130)), u_char, fill=fillcolor, font=font)
im_cropped.paste(watermark, None, watermark)
im_cropped.save("/tmp/background-" + str(i) + '.jpg', format="JPEG")
except Exception as e:
print(str(e))
generate_background()

60
getdependencies.sh Executable file
View File

@ -0,0 +1,60 @@
#!/bin/bash
shopt -s nullglob dotglob
directory=(dependencies/*)
if [ ${#directory[@]} -gt 0 ]; then
read -p "Found Dependency Directory. Did you want to wipe? (y/n) " -n 1 -r
if [[ $REPLY =~ ^[Yy]$ ]]
then
rm -R dependencies
echo
read -p "Did you want to resync? (y/n) " -n 1 -r
if [[ $REPLY =~ ^[Yy]$ ]]
then
echo
echo "resyncing"
else
exit 0
fi
echo
echo "starting resync"
else
echo
exit 0
fi
echo
exit 0
fi
apt-get update
apt-get -y install git
mkdir dependencies
cd dependencies
git clone https://github.com/yorkane/socks-nginx-module.git
git clone https://github.com/nbs-system/naxsi.git
git clone https://github.com/openresty/headers-more-nginx-module.git
git clone https://github.com/openresty/echo-nginx-module.git
#some required stuff for lua/luajit. obviously versions should be ckecked with every install/update
git clone https://github.com/openresty/lua-nginx-module
cd lua-nginx-module
git checkout v0.10.16
cd ..
git clone https://github.com/openresty/luajit2
cd luajit2
git checkout v2.1-20200102
cd ..
git clone https://github.com/vision5/ngx_devel_kit
git clone https://github.com/openresty/lua-resty-string
git clone https://github.com/cloudflare/lua-resty-cookie
git clone https://github.com/ittner/lua-gd/
git clone https://github.com/bungle/lua-resty-session
cd ..

View File

@ -1,12 +1,13 @@
-- encryption key and salt must be shared across fronts. salt must be 8 chars -- encryption key and salt must be shared across fronts. salt must be 8 chars
local key = "encryption_key" local key = "encryption_key"
local salt = "salt1234" local salt = "1saltkey"
-- for how long the captcha is valid. 120 sec is for testing, 3600 1 hour should be production. -- for how long the captcha is valid. 120 sec is for testing, 3600 1 hour should be production.
local session_timeout = 3600 local session_timeout = 3600
aes = require "resty.aes" aes = require "resty.aes"
str = require "resty.string" str = require "resty.string"
cook = require "resty.cookie" cook = require "resty.cookie"
random = require "resty.random"
aes_128_cbc_sha512x1 = aes:new(key, salt, aes.cipher(128,"cbc"), aes.hash.sha512, 1) aes_128_cbc_sha512x1 = aes:new(key, salt, aes.cipher(128,"cbc"), aes.hash.sha512, 1)
@ -88,74 +89,111 @@ if ngx.var.request_method == "POST" and ngx.var.http_referer == nil then
ngx.exit(444) ngx.exit(444)
end end
-- check if cookie is blacklisted by rate limiter. if it is show the client a message and exit. can get creative with this.
local field, err = cookie:get("dcap")
local blocked_cookies = ngx.shared.blocked_cookies
local bct, btcflags = blocked_cookies:get(field)
if bct then
ngx.header.content_type = 'text/plain'
ngx.say("403 DDOS fliter killed your path. (You probably sent too many requests at once). Not calling you a bot, bot, but grab a new identity and try again.")
ngx.flush()
ngx.exit(200)
end
-- check cookie support similar to testcookie -- check cookie support similar to testcookie
if ngx.var.request_method == "GET" then if ngx.var.request_method == "GET" then
local args = ngx.req.get_uri_args()
if args['tca'] == "1" then
local field, err = cookie:get("dcap")
if err or not field then
ngx.exit(403)
end
-- if cookie cannot be decrypted most likely it has been messed with
local cookdata = aes_128_cbc_sha512x1:decrypt(fromhex(field))
if not cookdata then
ngx.header.content_type = 'text/plain'
ngx.say("403 DDOS fliter killed your path. (You probably sent too many requests at once). Not calling you a bot, bot, but grab a new identity and try again.")
ngx.flush()
ngx.exit(200)
end
cooktest = split(cookdata, "|")[1]
if cooktest ~= "cap_not_solved" and cooktest ~= "captcha_solved" then
ngx.exit(403)
end
end
-- try to set cookie. max-age is irrelevant as it can be faked and check is done against cookie content anyway. should be set to a large value otherwise it will annoy users
local field, err = cookie:get("dcap") local field, err = cookie:get("dcap")
if err then if err then
local tstamp = ngx.now() --local tstamp = ngx.now() + slidingscalefunction
local plaintext = "cap_not_solved|" .. tstamp .. "|1" local tstamp = ngx.now() + 10
local plaintext = "queue|" .. tstamp .. "|1|" .. random.token(random.number(10,20))
local ciphertext = tohex(aes_128_cbc_sha512x1:encrypt(plaintext)) local ciphertext = tohex(aes_128_cbc_sha512x1:encrypt(plaintext))
local ok, err = cookie:set({ local ok, err = cookie:set({
key = "dcap", value = ciphertext, path = "/", key = "dcap", value = ciphertext, path = "/",
domain = ngx.var.host, httponly = true, domain = ngx.var.host, httponly = true,
max_age = 21600, max_age = session_timeout,
samesite = "Strict" samesite = "Lax"
}) })
if not ok then if not ok then
ngx.log(ngx.ERR, err) ngx.log(ngx.ERR, err)
return return
end end
ngx.header.content_type = 'text/html' ngx.header.content_type = 'text/html'
ngx.say("<head> \ local file = io.open("/etc/nginx/queue.html")
<meta http-equiv=\"refresh\" content=\"1\"> \ local queue, err = file:read("*a")
</head><a href=\"/\">One moment...</p>") file:close()
ngx.say(queue)
ngx.flush() ngx.flush()
ngx.exit(200) ngx.exit(200)
else
plaintext = aes_128_cbc_sha512x1:decrypt(fromhex(field))
if not plaintext then
ngx.header.content_type = 'text/plain'
ngx.say("403 DDOS fliter killed your path. (You probably sent too many requests at once). Not calling you a bot, bot, but grab a new identity and try again.")
ngx.flush()
ngx.exit(200)
end
cookdata = split(plaintext,"|")
local expired = nil
if (cookdata[1] == "queue") then
--if (tonumber(cookdata[2])) > ngx.now() or (tonumber(cookdata[2])) > tonumber(cookdata[2]) + slidingscalefunction then
if (tonumber(cookdata[2])) > ngx.now() or (tonumber(cookdata[2])) > ngx.now() + 40 then
if pa ~= "no_proxy" then
local ok, err = ngx.timer.at(0, kill_circuit, ngx.var.remote_addr, ngx.var.proxy_protocol_addr)
if not ok then
ngx.log(ngx.ERR, "failed to create timer: ", err)
return
end
end
local blocked_cookies = ngx.shared.blocked_cookies
blocked_cookies:set(field, 1, 3600)
ngx.exit(444)
end
require "caphtml_d"
local expired = nil
displaycap(session_timeout)
ngx.flush()
ngx.exit(200)
elseif (cookdata[1] == "cap_not_solved") then
if (tonumber(cookdata[2]) + 60) > ngx.now() then
if pa ~= "no_proxy" then
local ok, err = ngx.timer.at(0, kill_circuit, ngx.var.remote_addr, ngx.var.proxy_protocol_addr)
if not ok then
ngx.log(ngx.ERR, "failed to create timer: ", err)
return
end
end
ngx.header.content_type = 'text/html'
ngx.say("<h1>THINK OF WHAT YOU HAVE DONE!</h1>")
ngx.say("<p>That captcha was generated just for you. And look at what you did. Ignoring the captcha... not even giving an incorrect answer to his meaningless existence. You couldn't even give him false hope. Shame on you.</p>")
ngx.say("<p>Don't immedately refresh for a new captcha! Try and fail. You must now wait about a minute for a new captcha to load.</p>")
ngx.flush()
ngx.exit(200)
end
-- captcha generator functions
require "caphtml_d"
local expired = nil
if (tonumber(cookdata[2]) + session_timeout) < ngx.now() then
expired = true
caperror = "Session expired"
end
if cookdata[1] ~= "captcha_solved" or expired then
displaycap(session_timeout)
ngx.flush()
ngx.exit(200)
end
end
end end
end end
-- captcha generator functions
require "caphtml_d"
local field, err = cookie:get("dcap")
if not field or field == nil then
displaycap()
ngx.flush()
ngx.exit(200)
end
-- check if cookie is blacklisted by rate limiter. if it is show the client a message and exit. can get creative with this.
local blocked_cookies = ngx.shared.blocked_cookies
local bct, btcflags = blocked_cookies:get(field)
if bct then
ngx.log(ngx.ERR, "Cookie " .. field .. " blacklisted.")
ngx.header.content_type = 'text/plain'
ngx.say("403 DDOS fliter killed your path. (You probably sent too many requests at once). Not calling you a bot, bot, but grab a new identity and try again.")
ngx.flush()
ngx.exit(200)
end
if ngx.var.request_method == "POST" then if ngx.var.request_method == "POST" then
local field, err = cookie:get("dcap") local field, err = cookie:get("dcap")
if err then if err then
@ -172,24 +210,28 @@ if ngx.var.request_method == "POST" then
end end
cookdata = split(plaintext,"|") cookdata = split(plaintext,"|")
local expired = nil local expired = nil
if (tonumber(cookdata[2]) + session_timeout) < ngx.now() then if (cookdata[1] == "cap_not_solved") then
expired = true if (tonumber(cookdata[2]) + session_timeout) < ngx.now() then
caperror = "Session expired" expired = true
displaycap() require "caphtml_d"
ngx.flush() caperror = "Session expired"
ngx.exit(200) displaycap(session_timeout)
end ngx.flush()
if cookdata[1] == "captcha_solved" and not expired then ngx.exit(200)
end
elseif (cookdata[1] == "captcha_solved") then
return return
end end
end end
require "caphtml_d"
-- resty has a library for parsing POST data but it's not really needed -- resty has a library for parsing POST data but it's not really needed
ngx.req.read_body() ngx.req.read_body()
local dataraw = ngx.req.get_body_data() local dataraw = ngx.req.get_body_data()
if dataraw == nil then if dataraw == nil then
caperror = "You didn't submit anything. Try again." caperror = "You didn't submit anything. Try again."
displaycap() displaycap(session_timeout)
ngx.flush() ngx.flush()
ngx.exit(200) ngx.exit(200)
end end
@ -213,24 +255,25 @@ if ngx.var.request_method == "POST" then
if (tonumber(cookdata[2]) + 60) < ngx.now() then if (tonumber(cookdata[2]) + 60) < ngx.now() then
caperror = "Captcha expired" caperror = "Captcha expired"
displaycap() displaycap(session_timeout)
ngx.flush() ngx.flush()
ngx.exit(200) ngx.exit(200)
end end
if sentcap == cookdata[3] then if string.lower(sentcap) == string.lower(cookdata[3]) then
local newcookdata = "" local newcookdata = ""
cookdata[1] = "captcha_solved" cookdata[1] = "captcha_solved"
for k,v in pairs(cookdata) do for k,v in pairs(cookdata) do
newcookdata = newcookdata .. "|" .. v newcookdata = newcookdata .. "|" .. v
end end
newcookdata = newcookdata .. "|" .. random.token(random.number(10,20))
local tstamp = ngx.now() local tstamp = ngx.now()
local ciphertext = tohex(aes_128_cbc_sha512x1:encrypt(newcookdata)) local ciphertext = tohex(aes_128_cbc_sha512x1:encrypt(newcookdata))
local ok, err = cookie:set({ local ok, err = cookie:set({
key = "dcap", value = ciphertext, path = "/", key = "dcap", value = ciphertext, path = "/",
domain = ngx.var.host, httponly = true, domain = ngx.var.host, httponly = true,
max_age = 21600, max_age = session_timeout,
samesite = "Strict" samesite = "Lax"
}) })
if not ok then if not ok then
ngx.say("cookie error") ngx.say("cookie error")
@ -244,39 +287,8 @@ if ngx.var.request_method == "POST" then
else else
caperror = "You Got That Wrong. Try again" caperror = "You Got That Wrong. Try again"
end end
displaycap(session_timeout)
else
caperror = "Session invalid or expired"
displaycap()
ngx.flush() ngx.flush()
ngx.exit(200) ngx.exit(200)
end end
end end
plaintext = aes_128_cbc_sha512x1:decrypt(fromhex(field))
if not plaintext then
ngx.header.content_type = 'text/plain'
ngx.say("403 DDOS fliter killed your path. (You probably sent too many requests at once). Not calling you a bot, bot, but grab a new identity and try again.")
ngx.flush()
ngx.exit(200)
end
cookdata = split(plaintext,"|")
if not cookdata then
displaycap()
ngx.flush()
ngx.exit(200)
end
local expired = nil
if (tonumber(cookdata[2]) + session_timeout) < ngx.now() then
expired = true
caperror = "Session expired"
end
if cookdata[1] ~= "captcha_solved" or expired then
displaycap()
ngx.flush()
ngx.exit(200)
end

View File

@ -1,3 +1,5 @@
################################## ##################################
## INTERNAL RULES IDS:1-999 ## ## INTERNAL RULES IDS:1-999 ##
################################## ##################################
@ -16,30 +18,30 @@
################################## ##################################
## SQL Injections IDs:1000-1099 ## ## SQL Injections IDs:1000-1099 ##
################################## ##################################
MainRule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000; #MainRule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex|drop" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000;
MainRule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001; #MainRule "str:\"" "msg:double quote" "mz:URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8,$XSS:8" id:1001;
MainRule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002; MainRule "str:0x" "msg:0x, possible hex encoding" "mz:URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002;
## Hardcore rules ## Hardcore rules
MainRule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003; MainRule "str:/*" "msg:mysql comment (/*)" "mz:URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003;
MainRule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004; MainRule "str:*/" "msg:mysql comment (*/)" "mz:URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004;
MainRule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005; MainRule "str:|" "msg:mysql keyword (|)" "mz:URL|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005;
MainRule "str:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006; MainRule "str:&&" "msg:mysql keyword (&&)" "mz:URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006;
## end of hardcore rules ## end of hardcore rules
MainRule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007; MainRule "str:--" "msg:mysql comment (--)" "mz:URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007;
MainRule "str:;" "msg:semicolon" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008; #MainRule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4,$XSS:8" id:1008;
MainRule "str:=" "msg:equal sign in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009; #MainRule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009;
MainRule "str:(" "msg:open parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010; MainRule "str:(" "msg:parenthesis, probable sql/xss" "mz:URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1010;
MainRule "str:)" "msg:close parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011; MainRule "str:)" "msg:parenthesis, probable sql/xss" "mz:URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1011;
MainRule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013; MainRule "str:'" "msg:simple quote" "mz:ARGS|URL|$HEADERS_VAR:Cookie" "s:$SQL:4,$XSS:8" id:1013;
MainRule "str:," "msg:comma" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015; #MainRule "str:," "msg:, in stuff" "mz:URL|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015;
MainRule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016; #MainRule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016;
MainRule "str:@@" "msg:double arobase (@@)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1017; MainRule "str:@@" "msg:double @@" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1017;
############################### ###############################
## OBVIOUS RFI IDs:1100-1199 ## ## OBVIOUS RFI IDs:1100-1199 ##
############################### ###############################
MainRule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100; #MainRule "str:http://" "msg:http:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100;
MainRule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101; #MainRule "str:https://" "msg:https:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101;
MainRule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102; MainRule "str:ftp://" "msg:ftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102;
MainRule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103; MainRule "str:php://" "msg:php:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103;
MainRule "str:sftp://" "msg:sftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1104; MainRule "str:sftp://" "msg:sftp:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1104;
@ -48,41 +50,37 @@ MainRule "str:data://" "msg:data:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "
MainRule "str:glob://" "msg:glob:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1107; MainRule "str:glob://" "msg:glob:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1107;
MainRule "str:phar://" "msg:phar:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1108; MainRule "str:phar://" "msg:phar:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1108;
MainRule "str:file://" "msg:file:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1109; MainRule "str:file://" "msg:file:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1109;
MainRule "str:gopher://" "msg:gopher:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1110; MainRule "str:gopher://" "msg:file:// scheme" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1110;
####################################### #######################################
## Directory traversal IDs:1200-1299 ## ## Directory traversal IDs:1200-1299 ##
####################################### #######################################
MainRule "str:.." "msg:double dot" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200; MainRule "str:.." "msg:double dot" "mz:ARGS|URL|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200;
MainRule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202; MainRule "str:/etc/passwd" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202;
MainRule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203; MainRule "str:c:\\" "msg:obvious windows path" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203;
MainRule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204; MainRule "str:cmd.exe" "msg:obvious probe" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204;
MainRule "str:\\" "msg:backslash" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205; MainRule "str:\\" "msg:backslash" "mz:ARGS|URL|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205;
#MainRule "str:/" "msg:slash in args" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:2" id:1206; #MainRule "str:/" "msg:slash in args" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:2" id:1206;
######################################## ########################################
## Cross Site Scripting IDs:1300-1399 ## ## Cross Site Scripting IDs:1300-1399 ##
######################################## ########################################
MainRule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302; MainRule "str:<" "msg:html open tag" "mz:ARGS|URL|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302;
MainRule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303; MainRule "str:>" "msg:html close tag" "mz:ARGS|URL|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303;
MainRule "str:[" "msg:open square backet ([), possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310; #MainRule "str:[" "msg:[, possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310;
MainRule "str:]" "msg:close square bracket (]), possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311; #MainRule "str:]" "msg:], possible js" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311;
MainRule "str:~" "msg:tilde (~) character" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312; MainRule "str:~" "msg:~ character" "mz:URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312;
MainRule "str:`" "msg:grave accent (`)" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314; MainRule "str:`" "msg:grave accent !" "mz:ARGS|URL|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314;
MainRule "rx:%[2|3]." "msg:double encoding" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315; #MainRule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315;
#################################### ####################################
## Evading tricks IDs: 1400-1500 ## ## Evading tricks IDs: 1400-1500 ##
#################################### ####################################
MainRule "str:&#" "msg:utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400; MainRule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400;
MainRule "str:%U" "msg:M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401; MainRule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401;
############################# #############################
## File uploads: 1500-1600 ## ## File uploads: 1500-1600 ##
############################# #############################
MainRule "rx:\.ph|\.asp|\.ht" "msg:asp/php file upload" "mz:FILE_EXT" "s:$UPLOAD:8" id:1500; MainRule "rx:\.ph|\.asp|\.ht*" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1500;
MainRule "str:/public/uploads/" "msg:Access to uploads" "mz:URL" "s:$UWA:8" id:42000400;
MainRule "str:/public/image/" "msg:Access to image folder" "mz:URL" "s:$UWA:8" id:42000401;
MainRule "str:/public/" "msg:Access to public folder" "mz:URL" "s:$UWA:8" id:42000402;
MainRule "str:/system/" "msg:Access to system folder" "mz:URL" "s:$UWA:8" id:42000403;

View File

@ -2,3 +2,6 @@ BasicRule wl:10;
BasicRule wl:20; BasicRule wl:20;
BasicRule wl:16; BasicRule wl:16;
BasicRule wl:12; BasicRule wl:12;
BasicRule wl:13;
BasicRule wl:1310;
BasicRule wl:1311;

View File

@ -70,18 +70,14 @@ cd ..
git clone https://github.com/bungle/lua-resty-session git clone https://github.com/bungle/lua-resty-session
cp -a lua-resty-session/lib/resty/session* /usr/local/lib/lua/resty/ cp -a lua-resty-session/lib/resty/session* /usr/local/lib/lua/resty/
git clone https://github.com/ittner/lua-gd/
cd lua-gd
gcc -o gd.so -DGD_XPM -DGD_JPEG -DGD_FONTCONFIG -DGD_FREETYPE -DGD_PNG -DGD_GIF -O2 -Wall -fPIC -fomit-frame-pointer -I/usr/local/include/luajit-2.1 -DVERSION=\"2.0.33r3\" -shared -lgd luagd.c
mv gd.so /usr/local/lib/lua/5.1/gd.so
cd ..
wget -O /usr/local/lib/lua/resty/aes_functions.lua https://github.com/c64bob/lua-resty-aes/raw/master/lib/resty/aes_functions.lua wget -O /usr/local/lib/lua/resty/aes_functions.lua https://github.com/c64bob/lua-resty-aes/raw/master/lib/resty/aes_functions.lua
#include seems to be a bit mssed up with luajit #include seems to be a bit mssed up with luajit
mkdir /etc/nginx/resty mkdir /etc/nginx/resty
ln -s /usr/local/lib/lua/resty/ /etc/nginx/resty/ ln -s /usr/local/lib/lua/resty/ /etc/nginx/resty/
wget -O /usr/local/lib/lua/resty/random.lua https://raw.githubusercontent.com/bungle/lua-resty-random/master/lib/resty/random.lua
make -j16 modules make -j16 modules
cp -r objs modules cp -r objs modules

View File

@ -2,6 +2,7 @@ user www-data;
worker_processes auto; worker_processes auto;
worker_priority -5; worker_priority -5;
worker_rlimit_nofile 1024000; worker_rlimit_nofile 1024000;
timer_resolution 10000ms;
pid /run/nginx.pid; pid /run/nginx.pid;
load_module modules/modules/ngx_http_headers_more_filter_module.so; load_module modules/modules/ngx_http_headers_more_filter_module.so;
load_module modules/modules/ngx_http_naxsi_module.so; load_module modules/modules/ngx_http_naxsi_module.so;
@ -11,7 +12,9 @@ load_module modules/modules/ndk_http_module.so;
load_module modules/modules/ngx_http_lua_module.so; load_module modules/modules/ngx_http_lua_module.so;
events { events {
worker_connections 4096; worker_connections 8096;
use epoll;
multi_accept on;
} }
http { http {
@ -28,18 +31,19 @@ http {
reset_timedout_connection on; reset_timedout_connection on;
lua_shared_dict blocked_cookies 100M; lua_shared_dict blocked_cookies 100M;
# Timeouts # Timeouts
client_body_timeout 10s; client_body_timeout 30s;
client_header_timeout 10s; client_header_timeout 30s;
keepalive_timeout 240s; keepalive_timeout 240s;
keepalive_requests 200000;
send_timeout 120s; send_timeout 120s;
client_max_body_size 10m; client_max_body_size 10m;
client_body_buffer_size 10m; client_body_buffer_size 10m;
proxy_connect_timeout 30s; proxy_connect_timeout 120s;
proxy_send_timeout 30s; proxy_send_timeout 120s;
proxy_read_timeout 30s; proxy_read_timeout 120s;
log_format detailed escape=json log_format detailed escape=json
'{' '{'
@ -57,7 +61,6 @@ http {
'"http_user_agent": "$http_user_agent"' '"http_user_agent": "$http_user_agent"'
'}'; '}';
#access_log /var/log/site.access.log detailed; #access_log /var/log/site.access.log detailed;
proxy_redirect off; proxy_redirect off;
# Gzipping Content # Gzipping Content
@ -75,7 +78,7 @@ http {
include /etc/nginx/naxsi_core.rules; include /etc/nginx/naxsi_core.rules;
add_header X-Content-Type-Options "nosniff"; add_header X-Content-Type-Options "nosniff";
add_header X-Frame-Options "DENY"; add_header X-Frame-Options "SAMEORIGIN";
add_header X-Xss-Protection "1; mode=block"; add_header X-Xss-Protection "1; mode=block";
## ##

1
queue.html Normal file

File diff suppressed because one or more lines are too long

86
random.lua Normal file
View File

@ -0,0 +1,86 @@
local require = require
local ffi = require "ffi"
local ffi_cdef = ffi.cdef
local ffi_new = ffi.new
local ffi_str = ffi.string
local ffi_typeof = ffi.typeof
local C = ffi.C
local type = type
local random = math.random
local randomseed = math.randomseed
local concat = table.concat
local tostring = tostring
local pcall = pcall
ffi_cdef[[
typedef unsigned char u_char;
u_char * ngx_hex_dump(u_char *dst, const u_char *src, size_t len);
int RAND_bytes(u_char *buf, int num);
]]
local ok, new_tab = pcall(require, "table.new")
if not ok then
new_tab = function () return {} end
end
local alnum = {
'A','B','C','D','E','F','G','H','I','J','K','L','M',
'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
'a','b','c','d','e','f','g','h','i','j','k','l','m',
'n','o','p','q','r','s','t','u','v','w','x','y','z',
'0','1','2','3','4','5','6','7','8','9'
}
local t = ffi_typeof "uint8_t[?]"
local function bytes(len, format)
local s = ffi_new(t, len)
C.RAND_bytes(s, len)
if not s then return nil end
if format == "hex" then
local b = ffi_new(t, len * 2)
C.ngx_hex_dump(b, s, len)
return ffi_str(b, len * 2), true
else
return ffi_str(s, len), true
end
end
local function seed()
local a,b,c,d = bytes(4):byte(1, 4)
return randomseed(a * 0x1000000 + b * 0x10000 + c * 0x100 + d)
end
local function number(min, max, reseed)
if reseed then seed() end
if min and max then return random(min, max)
elseif min then return random(min)
else return random() end
end
local function token(len, chars, sep)
chars = chars or alnum
local count
local token = new_tab(len, 0)
if type(chars) ~= "table" then
chars = tostring(chars)
count = #chars
local n
for i=1,len do
n = number(1, count)
token[i] = chars:sub(n, n)
end
else
count = #chars
for i=1,len do token[i] = chars[number(1, count)] end
end
return concat(token, sep)
end
seed()
return {
bytes = bytes,
number = number,
token = token
}

225
resty/aes_functions.lua Normal file
View File

@ -0,0 +1,225 @@
-- Copyright (C) by Yichun Zhang (agentzh)
local ffi = require "ffi"
local ffi_new = ffi.new
local ffi_gc = ffi.gc
local ffi_str = ffi.string
local ffi_copy = ffi.copy
local C = ffi.C
local setmetatable = setmetatable
local type = type
local _M = { _VERSION = '0.12' }
local mt = { __index = _M }
ffi.cdef[[
typedef struct engine_st ENGINE;
typedef struct evp_cipher_st EVP_CIPHER;
typedef struct evp_cipher_ctx_st EVP_CIPHER_CTX;
typedef struct env_md_ctx_st EVP_MD_CTX;
typedef struct env_md_st EVP_MD;
const EVP_MD *EVP_md5(void);
const EVP_MD *EVP_sha(void);
const EVP_MD *EVP_sha1(void);
const EVP_MD *EVP_sha224(void);
const EVP_MD *EVP_sha256(void);
const EVP_MD *EVP_sha384(void);
const EVP_MD *EVP_sha512(void);
const EVP_CIPHER *EVP_aes_128_ecb(void);
const EVP_CIPHER *EVP_aes_128_cbc(void);
const EVP_CIPHER *EVP_aes_128_cfb1(void);
const EVP_CIPHER *EVP_aes_128_cfb8(void);
const EVP_CIPHER *EVP_aes_128_cfb128(void);
const EVP_CIPHER *EVP_aes_128_ofb(void);
const EVP_CIPHER *EVP_aes_128_ctr(void);
const EVP_CIPHER *EVP_aes_192_ecb(void);
const EVP_CIPHER *EVP_aes_192_cbc(void);
const EVP_CIPHER *EVP_aes_192_cfb1(void);
const EVP_CIPHER *EVP_aes_192_cfb8(void);
const EVP_CIPHER *EVP_aes_192_cfb128(void);
const EVP_CIPHER *EVP_aes_192_ofb(void);
const EVP_CIPHER *EVP_aes_192_ctr(void);
const EVP_CIPHER *EVP_aes_256_ecb(void);
const EVP_CIPHER *EVP_aes_256_cbc(void);
const EVP_CIPHER *EVP_aes_256_cfb1(void);
const EVP_CIPHER *EVP_aes_256_cfb8(void);
const EVP_CIPHER *EVP_aes_256_cfb128(void);
const EVP_CIPHER *EVP_aes_256_ofb(void);
const EVP_CIPHER *EVP_aes_256_ctr(void);
EVP_CIPHER_CTX *EVP_CIPHER_CTX_new();
void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *a);
int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx,const EVP_CIPHER *cipher,
ENGINE *impl, unsigned char *key, const unsigned char *iv);
int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl,
const unsigned char *in, int inl);
int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl);
int EVP_DecryptInit_ex(EVP_CIPHER_CTX *ctx,const EVP_CIPHER *cipher,
ENGINE *impl, unsigned char *key, const unsigned char *iv);
int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl,
const unsigned char *in, int inl);
int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *outm, int *outl);
int EVP_BytesToKey(const EVP_CIPHER *type,const EVP_MD *md,
const unsigned char *salt, const unsigned char *data, int datal,
int count, unsigned char *key,unsigned char *iv);
]]
local hash
hash = {
md5 = C.EVP_md5(),
sha1 = C.EVP_sha1(),
sha224 = C.EVP_sha224(),
sha256 = C.EVP_sha256(),
sha384 = C.EVP_sha384(),
sha512 = C.EVP_sha512()
}
_M.hash = hash
local cipher
cipher = function (size, _cipher)
local _size = size or 128
local _cipher = _cipher or "cbc"
local func = "EVP_aes_" .. _size .. "_" .. _cipher
if C[func] then
return { size=_size, cipher=_cipher, method=C[func]()}
else
return nil
end
end
_M.cipher = cipher
function _M.new(self, key, salt, _cipher, _hash, hash_rounds)
local encrypt_ctx = C.EVP_CIPHER_CTX_new()
if encrypt_ctx == nil then
return nil, "no memory"
end
ffi_gc(encrypt_ctx, C.EVP_CIPHER_CTX_free)
local decrypt_ctx = C.EVP_CIPHER_CTX_new()
if decrypt_ctx == nil then
return nil, "no memory"
end
ffi_gc(decrypt_ctx, C.EVP_CIPHER_CTX_free)
local _cipher = _cipher or cipher()
local _hash = _hash or hash.md5
local hash_rounds = hash_rounds or 1
local _cipherLength = _cipher.size/8
local gen_key = ffi_new("unsigned char[?]",_cipherLength)
local gen_iv = ffi_new("unsigned char[?]",_cipherLength)
if type(_hash) == "table" then
if not _hash.iv or #_hash.iv ~= 16 then
return nil, "bad iv"
end
if _hash.method then
local tmp_key = _hash.method(key)
if #tmp_key ~= _cipherLength then
return nil, "bad key length"
end
ffi_copy(gen_key, tmp_key, _cipherLength)
elseif #key ~= _cipherLength then
return nil, "bad key length"
else
ffi_copy(gen_key, key, _cipherLength)
end
ffi_copy(gen_iv, _hash.iv, 16)
else
if salt and #salt ~= 8 then
return nil, "salt must be 8 characters or nil"
end
if C.EVP_BytesToKey(_cipher.method, _hash, salt, key, #key,
hash_rounds, gen_key, gen_iv)
~= _cipherLength
then
return nil
end
end
if C.EVP_EncryptInit_ex(encrypt_ctx, _cipher.method, nil,
gen_key, gen_iv) == 0 or
C.EVP_DecryptInit_ex(decrypt_ctx, _cipher.method, nil,
gen_key, gen_iv) == 0 then
return nil
end
return setmetatable({
_encrypt_ctx = encrypt_ctx,
_decrypt_ctx = decrypt_ctx
}, mt)
end
function _M.encrypt(self, s)
local s_len = #s
local max_len = s_len + 16
local buf = ffi_new("unsigned char[?]", max_len)
local out_len = ffi_new("int[1]")
local tmp_len = ffi_new("int[1]")
local ctx = self._encrypt_ctx
if C.EVP_EncryptInit_ex(ctx, nil, nil, nil, nil) == 0 then
return nil
end
if C.EVP_EncryptUpdate(ctx, buf, out_len, s, s_len) == 0 then
return nil
end
if C.EVP_EncryptFinal_ex(ctx, buf + out_len[0], tmp_len) == 0 then
return nil
end
return ffi_str(buf, out_len[0] + tmp_len[0])
end
function _M.decrypt(self, s)
local s_len = #s
local buf = ffi_new("unsigned char[?]", s_len)
local out_len = ffi_new("int[1]")
local tmp_len = ffi_new("int[1]")
local ctx = self._decrypt_ctx
if C.EVP_DecryptInit_ex(ctx, nil, nil, nil, nil) == 0 then
return nil
end
if C.EVP_DecryptUpdate(ctx, buf, out_len, s, s_len) == 0 then
return nil
end
if C.EVP_DecryptFinal_ex(ctx, buf + out_len[0], tmp_len) == 0 then
return nil
end
return ffi_str(buf, out_len[0] + tmp_len[0])
end
return _M

View File

@ -1,6 +1,6 @@
local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
function base64_encode(data) local function base64_encode(data)
return ((data:gsub('.', function(x) return ((data:gsub('.', function(x)
local r,b='',x:byte() local r,b='',x:byte()
for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
@ -13,7 +13,7 @@ function base64_encode(data)
end)..({ '', '==', '=' })[#data%3+1]) end)..({ '', '==', '=' })[#data%3+1])
end end
function base64_decode(data) local function base64_decode(data)
data = string.gsub(data, '[^'..b..'=]', '') data = string.gsub(data, '[^'..b..'=]', '')
return (data:gsub('.', function(x) return (data:gsub('.', function(x)
if (x == '=') then return '' end if (x == '=') then return '' end
@ -28,40 +28,105 @@ function base64_decode(data)
end)) end))
end end
function in_array(tab, val) function displaycap()
for index, value in ipairs(tab) do
if value == val then hour = random.number(0,11)
return true minute = random.number(0,59)
end shour = tostring(hour)
sminute = tostring(minute)
if string.len(shour) < 2 then shour = "0" .. shour end
if string.len(sminute) < 2 then sminute = "0" .. sminute end
local gd = require("gd")
local pickedtime = shour .. ":" .. sminute
local radios = {}
local ctimeindex = random.number(1,10)
radios[ctimeindex] = {}
radios[ctimeindex][1] = pickedtime
radios[ctimeindex][2] = shour .. sminute
for i = 1,ctimeindex-1,1 do
fshour = tostring(random.number(0,11))
fsminute = tostring(random.number(0,59))
if string.len(fshour) < 2 then fshour = "0" .. fshour end
if string.len(fsminute) < 2 then fsminute = "0" .. fsminute end
local fpickedtime = fshour .. ":" .. fsminute
radios[i] = {}
radios[i][1] = fpickedtime
radios[i][2] = fshour .. fsminute
end end
for i = ctimeindex+1,10,1 do
fshour = tostring(random.number(0,11))
fsminute = tostring(random.number(0,59))
if string.len(fshour) < 2 then fshour = "0" .. fshour end
if string.len(fsminute) < 2 then fsminute = "0" .. fsminute end
local fpickedtime = fshour .. ":" .. fsminute
radios[i] = {}
radios[i][1] = fpickedtime
radios[i][2] = fshour .. fsminute
end
local function createClock(size, hours, minutes)
local im = gd.createTrueColor(size, size)
local white = im:colorAllocate(random.number(200,255), random.number(200,255), random.number(200,255))
local gray = im:colorAllocate(random.number(100,150), random.number(100,150), random.number(100,150))
local black = im:colorAllocate(random.number(0,10), random.number(0,10), random.number(0,10))
local hrhand = im:colorAllocate(random.number(0,350), random.number(0,150), random.number(0,148))
local minhand = im:colorAllocate(random.number(0,350), random.number(0,150), random.number(0,148))
local cxy = size/2
im:filledRectangle(0, 0, size, size, white)
im:setThickness(2)
im:arc(cxy, cxy, size, size, 0, 360, black)
local ang = 0
local rang, gsize
while ang < 360 do
rang = math.rad(ang)
if (ang % 90) == 0 then
gsize = 0.75
elseif (ang % 5) == 0 then
gsize = 0.85
else
gsize = 0.90
end
im:line(
cxy + gsize * cxy * math.sin(rang),
size - (cxy + gsize * cxy * math.cos(rang)),
cxy + cxy * 0.9 * math.sin(rang),
size - (cxy + cxy * 0.9 * math.cos(rang)),
gray)
ang = ang + 6
end
im:setThickness(math.max(1, size/50))
im:line(cxy, cxy,
cxy + 0.45 * size * math.sin(math.rad(6*minutes)),
size - (cxy + 0.45 * size * math.cos(math.rad(6*minutes))),
hrhand)
im:setThickness(math.max(1, size/25))
rang = math.rad(30*hours + minutes/2)
im:line(cxy, cxy,
cxy + 0.25 * size * math.sin(rang),
size - (cxy + 0.25 * size * math.cos(rang)),
minhand)
im:setThickness(1)
local sp = math.max(1, size/20)
im:filledArc(cxy, cxy, sp, sp, 0, 360, black, gd.ARC)
return im
end end
local gd = require("gd") local im = createClock(190, hour, minute)
local imageraw = im:jpegStr(90)
local imageb64 = base64_encode(imageraw)
function displaycap()
math.randomseed(ngx.now())
local img_width = 150;
local img_height = 150;
local capgrid = {}
local checkmin = 1
local checkmax = 6
local checktotal = 0
local sessiondice = "";
while checktotal < checkmin do
for i=1,9,1 do
check = math.random(0,1)
if checktotal < checkmax and check == 1 then capgrid[i] = check else capgrid[i] = 0 end
if check == 1 then
checktotal = checktotal + 1
sessiondice = sessiondice .. tostring(i)
end
end
end
local cookie, err = cook:new() local cookie, err = cook:new()
if not cookie then if not cookie then
@ -71,71 +136,26 @@ function displaycap()
end end
local tstamp = ngx.now() local tstamp = ngx.now()
local newcookdata = "cap_not_solved|" .. tstamp .. "|" local newcookdata = "cap_not_solved|" .. tstamp .. "|" .. shour .. sminute
newcookdata = newcookdata .. sessiondice
newcookdata = newcookdata .. "|" .. random.token(random.number(10,20))
local ciphertext = tohex(aes_128_cbc_sha512x1:encrypt(newcookdata)) local ciphertext = tohex(aes_128_cbc_sha512x1:encrypt(newcookdata))
local ok, err = cookie:set({ local ok, err = cookie:set({
key = "dcap", value = ciphertext, path = "/", key = "dcap", value = ciphertext, path = "/",
domain = ngx.var.host, httponly = true, domain = ngx.var.host, httponly = true,
max_age = 21600, max_age = 21600,
samesite = "Strict" samesite = "Lax"
}) })
if not ok then if not ok then
ngx.say("cookie error") ngx.say("cookie error")
ngx.exit(200) ngx.exit(200)
end end
local symbols_zero = {'','','','','','','','','','','','','',''};
local symbols_one = {'','','','','','','','','','','','','',''};
local img = gd.createFromJpeg("/tmp/background-" .. math.random(0,25) .. ".jpg")
if img == nil then
img = gd.createTrueColor(150, 150)
local white = img:colorAllocate(255, 255, 255)
img:filledRectangle(0, 0, img_width, img_height, white)
end
img:setThickness(1)
-- if 0 each row will be horizontal
local draw_angle = 0
local current_row = 1
local capstring = ""
for i=1,9,1 do
local symbol_id = math.random(1,14)
local fillcolor = img:colorAllocate(math.random(5,255), math.random(5,255), math.random(5,255))
if capgrid[i] == 1 then
capstring = capstring .. symbols_one[symbol_id]
else
capstring = capstring .. symbols_zero[symbol_id]
end
capstring = capstring .. " "
if i % 3 == 0 then
if draw_angle == 1 then
angle = math.rad(math.random(0,10))
else
angle = 0
end
if current_row == 1 then
img:stringFT(fillcolor, "/etc/nginx/font.ttf", math.random(18,22), angle, math.random(10,50), math.random(30,60), capstring)
elseif current_row == 2 then
img:stringFT(fillcolor, "/etc/nginx/font.ttf", math.random(18,22), angle, math.random(10,50), math.random(60,90), capstring)
else
img:stringFT(fillcolor, "/etc/nginx/font.ttf", math.random(18,22), angle, math.random(10,50), math.random(100,130), capstring)
end
current_row = current_row + 1
capstring = ""
end
end
imgbase64 = base64_encode(img:pngStrEx(6))
ngx.header.content_type = 'text/html'; ngx.header.content_type = 'text/html';
ngx.say("<html lang=en> \ ngx.say("<!DOCTYPE html> \
<html lang=en> \
<head> \ <head> \
<title>DDOS Protection</title> \ <title>DDOS Protection</title> \
<meta charset=\"UTF-8\"> \ <meta charset=\"UTF-8\"> \
@ -166,34 +186,95 @@ if caperror ~= nil
then then
ngx.say("<p class=\"alert alert-danger text-center\"><strong>Error: </strong>" .. caperror .. "</p>") ngx.say("<p class=\"alert alert-danger text-center\"><strong>Error: </strong>" .. caperror .. "</p>")
else else
ngx.say("<p>Due to on-going DDOS attacks against our servers, you must complete a captcha challenge to prove you are human.</p>") ngx.say("<p>Prove that you are human. Select the time shown on the clock image.</p>")
end end
ngx.say("<form class=\"ddos_form\" method=\"post\"> \ ngx.say("<form class=\"ddos_form\" method=\"post\"> \
<div class=\"captchav2\" style=\"margin-bottom:15px;\"> \ <div class=\"captchav2\" style=\"margin-bottom:15px;\"> \
<div class=\"imgWrap\" style=\"border:1px solid #000;background-image:url(data:image/png;base64," .. imgbase64 .. "\"></div>") <div class=\"imgWrap\" style=\"border:2px solid #fff; max-width: 100%; border-radius: 50%; background-image:url(data:image/png;base64," .. imageb64 .. "\"></div>")
ngx.say("<div class=\"inputWrap\"> \ ngx.say("</div>");
<input type=\"checkbox\" name=\"cap\" value=\"1\"> \ ngx.say("<div style=\"margin-bottom: 15px;\">")
<input type=\"checkbox\" name=\"cap\" value=\"2\"> \ ngx.say("<select class=\"center\" name=\"cap\" required>")
<input type=\"checkbox\" name=\"cap\" value=\"3\"> \ for i = 0,11,1 do
<input type=\"checkbox\" name=\"cap\" value=\"4\"> \ if i < 10 then si = "0" .. tostring(i) else si = i end
<input type=\"checkbox\" name=\"cap\" value=\"5\"> \ ngx.say("<option value=\"" .. si .. "\">" .. si .. "</option>\n")
<input type=\"checkbox\" name=\"cap\" value=\"6\"> \ end
<input type=\"checkbox\" name=\"cap\" value=\"7\"> \ ngx.say("</select> : ")
<input type=\"checkbox\" name=\"cap\" value=\"8\"> \ ngx.say("<select name=\"cap\" required>")
<input type=\"checkbox\" name=\"cap\" value=\"9\">") for i = 0,59,1 do
ngx.say("<div class=\"c1\"></div> \ if i < 10 then si = "0" .. tostring(i) else si = i end
<div class=\"c2\"></div> \ ngx.say("<option value=\"" .. si .. "\">" .. si .. "</option>\n")
<div class=\"c3\"></div> \ end
<div class=\"c4\"></div> \ ngx.say("</select>")
<div class=\"c5\"></div> \ --ngx.say("<input type=\"text\" required name=\"cap\" maxlength=\"2\" size=\"2\" placeholder=\"hh\"> : ")
<div class=\"c6\"></div> \ --ngx.say("<input type=\"text\" required name=\"cap\" maxlength=\"2\" size=\"2\" placeholder=\"mm\">")
<div class=\"c7\"></div> \ ngx.say("</div>");
<div class=\"c8\"></div> \ ngx.say("<div class=\"expire\"> \
<div class=\"c9\"></div>") <div class=\"timer\"> \
ngx.say("</div> \ <div class=\"time-part-wrapper\"> \
</div> \ <div class=\"time-part seconds tens\"> \
<button type=\"submit\">Verify</button> \ <div class=\"digit-wrapper\"> \
<span class=\"digit\">0</span> \
<span class=\"digit\">5</span> \
<span class=\"digit\">4</span> \
<span class=\"digit\">3</span> \
<span class=\"digit\">2</span> \
<span class=\"digit\">1</span> \
<span class=\"digit\">0</span> \
</div> \
</div> \
<div class=\"time-part seconds ones\"> \
<div class=\"digit-wrapper\"> \
<span class=\"digit\">0</span> \
<span class=\"digit\">9</span> \
<span class=\"digit\">8</span> \
<span class=\"digit\">7</span> \
<span class=\"digit\">6</span> \
<span class=\"digit\">5</span> \
<span class=\"digit\">4</span> \
<span class=\"digit\">3</span> \
<span class=\"digit\">2</span> \
<span class=\"digit\">1</span> \
<span class=\"digit\">0</span> \
</div> \
</div> \
</div> \
<div class=\"time-part-wrapper\"> \
<div class=\"time-part hundredths tens\"> \
<div class=\"digit-wrapper\"> \
<span class=\"digit\">0</span> \
<span class=\"digit\">9</span> \
<span class=\"digit\">8</span> \
<span class=\"digit\">7</span> \
<span class=\"digit\">6</span> \
<span class=\"digit\">5</span> \
<span class=\"digit\">4</span> \
<span class=\"digit\">3</span> \
<span class=\"digit\">2</span> \
<span class=\"digit\">1</span> \
<span class=\"digit\">0</span> \
</div> \
</div> \
<div class=\"time-part hundredths ones\"> \
<div class=\"digit-wrapper\"> \
<span class=\"digit\">0</span> \
<span class=\"digit\">9</span> \
<span class=\"digit\">8</span> \
<span class=\"digit\">7</span> \
<span class=\"digit\">6</span> \
<span class=\"digit\">5</span> \
<span class=\"digit\">4</span> \
<span class=\"digit\">3</span> \
<span class=\"digit\">2</span> \
<span class=\"digit\">1</span> \
<span class=\"digit\">0</span> \
</div> \
</div> \
</div> \
</div> \
</div>")
ngx.say("<button class=\"before\" type=\"submit\">Verify</button> \
<button class=\"expired\" type=\"submit\"> Refresh (expired)</button> \
</form> \ </form> \
</div> \ </div> \
</div> \ </div> \

86
resty/core/random.lua Normal file
View File

@ -0,0 +1,86 @@
local require = require
local ffi = require "ffi"
local ffi_cdef = ffi.cdef
local ffi_new = ffi.new
local ffi_str = ffi.string
local ffi_typeof = ffi.typeof
local C = ffi.C
local type = type
local random = math.random
local randomseed = math.randomseed
local concat = table.concat
local tostring = tostring
local pcall = pcall
ffi_cdef[[
typedef unsigned char u_char;
u_char * ngx_hex_dump(u_char *dst, const u_char *src, size_t len);
int RAND_bytes(u_char *buf, int num);
]]
local ok, new_tab = pcall(require, "table.new")
if not ok then
new_tab = function () return {} end
end
local alnum = {
'A','B','C','D','E','F','G','H','I','J','K','L','M',
'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
'a','b','c','d','e','f','g','h','i','j','k','l','m',
'n','o','p','q','r','s','t','u','v','w','x','y','z',
'0','1','2','3','4','5','6','7','8','9'
}
local t = ffi_typeof "uint8_t[?]"
local function bytes(len, format)
local s = ffi_new(t, len)
C.RAND_bytes(s, len)
if not s then return nil end
if format == "hex" then
local b = ffi_new(t, len * 2)
C.ngx_hex_dump(b, s, len)
return ffi_str(b, len * 2), true
else
return ffi_str(s, len), true
end
end
local function seed()
local a,b,c,d = bytes(4):byte(1, 4)
return randomseed(a * 0x1000000 + b * 0x10000 + c * 0x100 + d)
end
local function number(min, max, reseed)
if reseed then seed() end
if min and max then return random(min, max)
elseif min then return random(min)
else return random() end
end
local function token(len, chars, sep)
chars = chars or alnum
local count
local token = new_tab(len, 0)
if type(chars) ~= "table" then
chars = tostring(chars)
count = #chars
local n
for i=1,len do
n = number(1, count)
token[i] = chars:sub(n, n)
end
else
count = #chars
for i=1,len do token[i] = chars[number(1, count)] end
end
return concat(token, sep)
end
seed()
return {
bytes = bytes,
number = number,
token = token
}

169
setup.sh Normal file → Executable file
View File

@ -3,15 +3,22 @@
#OPTIONS! #OPTIONS!
MASTERONION="dreadytofatroptsdj6io7l3xptbet6onoyno2yv7jicoxknyazubrad.onion" MASTERONION="dreadytofatroptsdj6io7l3xptbet6onoyno2yv7jicoxknyazubrad.onion"
TORAUTHPASSWORD="password" TORAUTHPASSWORD="changethispassowrd"
BACKENDONIONURL="biblemeowimkh3utujmhm6oh2oeb3ubjw2lpgeq3lahrfr2l6ev6zgyd.onion"
#set to true if you want to setup local proxy instead of proxy over Tor
LOCALPROXY=false
PROXYPASSURL="10.10.10.10:80"
#Shared Front Captcha Key. Key alphanumeric between 64-128. Salt needs to be exactly 8 chars. #Shared Front Captcha Key. Key alphanumeric between 64-128. Salt needs to be exactly 8 chars.
KEY="encryption_key" KEY="encryption_key"
SALT="salt1234" SALT="1saltkey"
SESSION_LENGTH=3600
#CSS Branding #CSS Branding
HEXCOLOR="#9b59b6" HEXCOLOR="#9b59b6"
SITENAME="dread"
#There is more branding you need to do in the resty/caphtml_d.lua file near the end. #There is more branding you need to do in the resty/caphtml_d.lua file near the end.
@ -25,7 +32,7 @@ NC='\033[0m' # No Color
printf "\r\nProvided by your lovely ${BLUE}/u/Paris${NC} from dread. \r\n" printf "\r\nProvided by your lovely ${BLUE}/u/Paris${NC} from dread. \r\n"
printf "with help from ${BLUE}/u/mr_white${NC} from whitehousemarket.\n" printf "with help from ${BLUE}/u/mr_white${NC} from whitehousemarket.\n"
echo "For the full effects of the DDOS prevention you will need to make sure to setup v3 onionbalance." echo "For the full effects of the DDOS prevention you will need to make sure to setup v3 onionbalance."
echo "Max 6-9 backend instances for each onion. This script will help make the backend instances." echo "Onionbalance v3 does have distinct descriptors in a forked version. Read the README.MD in the onionbalance folder for more information. "
if [ ${#MASTERONION} -lt 62 ]; then if [ ${#MASTERONION} -lt 62 ]; then
echo "MASTEWRONION doesn't have the correct length. The url needs to include the .onion at the end." echo "MASTEWRONION doesn't have the correct length. The url needs to include the .onion at the end."
@ -38,9 +45,18 @@ then
exit 0 exit 0
fi fi
sleep 5 shopt -s nullglob dotglob
echo "Proceeding to do the configuration and setup. This will take awhile." directory=(dependencies/*)
if [ ${#directory[@]} -gt 0 ]
then
echo "Dependency Folder Found!"
else
echo "You need to get the dependencies first. Run './getdependencies.sh'"
exit 0
fi
echo "Proceeding to do the configuration and setup. This will take awhile."
sleep 5
### Configuration ### Configuration
string="s/masterbalanceonion/" string="s/masterbalanceonion/"
@ -49,7 +65,17 @@ string+="/g"
sed -i $string site.conf sed -i $string site.conf
string="s/torauthpassword/" string="s/torauthpassword/"
string+="$torinput" string+="$TORAUTHPASSWORD"
string+="/g"
sed -i $string site.conf
string="s/backendurl/"
string+="$BACKENDONIONURL"
string+="/g"
sed -i $string site.conf
string="s/proxypassurl/"
string+="$PROXYPASSURL"
string+="/g" string+="/g"
sed -i $string site.conf sed -i $string site.conf
@ -63,16 +89,44 @@ string+="$SALT"
string+="/g" string+="/g"
sed -i $string lua/cap.lua sed -i $string lua/cap.lua
string="s/sessionconfigvalue/"
string+="$SESSION_LENGTH"
string+="/g"
sed -i $string lua/cap.lua
string="s/HEXCOLOR/" string="s/HEXCOLOR/"
string+="$HEXCOLOR" string+="$HEXCOLOR"
string+="/g" string+="/g"
sed -i $string cap_d.css sed -i $string cap_d.css
string="s/HEXCOLOR/"
string+="$HEXCOLOR"
string+="/g"
sed -i $string queue.html
string="s/SITENAME/"
string+="$SITENAME"
string+="/g"
sed -i $string queue.html
string="s/SITENAME/" string="s/SITENAME/"
string+="$SITENAME" string+="$SITENAME"
string+="/g" string+="/g"
sed -i $string resty/caphtml_d.lua sed -i $string resty/caphtml_d.lua
if $LOCALPROXY
then
string="s/#proxy_pass/"
string+="proxy_pass"
string+="/g"
sed -i $string site.conf
else
string="s/#socks_/"
string+="socks_"
string+="/g"
sed -i $string site.conf
fi
apt-get update apt-get update
apt-get install -y apt-transport-https lsb-release ca-certificates dirmngr apt-get install -y apt-transport-https lsb-release ca-certificates dirmngr
@ -84,44 +138,49 @@ echo "deb https://nginx.org/packages/debian/ buster nginx" > /etc/apt/sources.li
gpg --import A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc gpg --import A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc
gpg --export A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89 | apt-key add - gpg --export A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89 | apt-key add -
apt-key add nginx_signing.key apt-key add nginx_signing.key
apt-get update apt-get update
apt-get install -y tor nyx nginx apt-get install -y tor nyx nginx
apt-get install -y vanguards apt-get install -y vanguards
apt-get install -y build-essential zlib1g-dev libpcre3 libpcre3-dev uuid-dev gcc git wget curl libgd3 libgd-dev
command="nginx -v" command="nginx -v"
nginxv=$( ${command} 2>&1 ) nginxv=$( ${command} 2>&1 )
NGINXVERSION=$(echo $nginxv | grep -o '[0-9.]*$') NGINXVERSION=$(echo $nginxv | grep -o '[0-9.]*$')
NGINXOPENSSL="1.1.1d" NGINXOPENSSL="1.1.1d"
wget https://nginx.org/download/nginx-$NGINXVERSION.tar.gz wget https://nginx.org/download/nginx-$NGINXVERSION.tar.gz
tar -xzvf nginx-$NGINXVERSION.tar.gz tar -xzvf nginx-$NGINXVERSION.tar.gz
cp -R dependencies/* nginx-$NGINXVERSION/
cd nginx-$NGINXVERSION cd nginx-$NGINXVERSION
apt-get install -y build-essential zlib1g-dev libpcre3 libpcre3-dev uuid-dev gcc git wget curl libgd3 libgd-dev
git clone https://github.com/yorkane/socks-nginx-module.git
git clone https://github.com/nbs-system/naxsi.git
wget https://www.openssl.org/source/openssl-$NGINXOPENSSL.tar.gz wget https://www.openssl.org/source/openssl-$NGINXOPENSSL.tar.gz
tar -xzvf openssl-$NGINXOPENSSL.tar.gz tar -xzvf openssl-$NGINXOPENSSL.tar.gz
git clone https://github.com/openresty/headers-more-nginx-module.git
git clone https://github.com/openresty/echo-nginx-module.git
#some required stuff for lua/luajit. obviously versions should be ckecked with every install/update
git clone https://github.com/openresty/lua-nginx-module
cd lua-nginx-module
git checkout v0.10.16
cd ..
git clone https://github.com/openresty/luajit2
cd luajit2
git checkout v2.1-20200102
cd ..
git clone https://github.com/vision5/ngx_devel_kit
cd luajit2 cd luajit2
make -j8 && make install make -j8 && make install
cd .. cd ..
cd lua-resty-string
make install
cd ..
cd lua-resty-cookie
make install
cd ..
cd lua-gd
gcc -o gd.so -DGD_XPM -DGD_JPEG -DGD_FONTCONFIG -DGD_FREETYPE -DGD_PNG -DGD_GIF -O2 -Wall -fPIC -fomit-frame-pointer -I/usr/local/include/luajit-2.1 -DVERSION=\"2.0.33r3\" -shared -lgd luagd.c
mv gd.so /usr/local/lib/lua/5.1/gd.so
cd ..
cp -a lua-resty-session/lib/resty/session* /usr/local/lib/lua/resty/
export LUAJIT_LIB=/usr/local/lib export LUAJIT_LIB=/usr/local/lib
export LUAJIT_INC=/usr/local/include/luajit-2.1 export LUAJIT_INC=/usr/local/include/luajit-2.1
./configure --with-cc-opt='-Wno-stringop-overflow -Wno-stringop-truncation -Wno-cast-function-type' \ ./configure --with-cc-opt='-Wno-stringop-overflow -Wno-stringop-truncation -Wno-cast-function-type' \
@ -135,67 +194,66 @@ export LUAJIT_INC=/usr/local/include/luajit-2.1
--add-dynamic-module=ngx_devel_kit \ --add-dynamic-module=ngx_devel_kit \
--add-dynamic-module=lua-nginx-module --add-dynamic-module=lua-nginx-module
git clone https://github.com/openresty/lua-resty-string #https://github.com/c64bob/lua-resty-aes/raw/master/lib/resty/aes_functions.lua
cd lua-resty-string mv resty/aes_functions.lua /usr/local/lib/lua/resty/aes_functions.lua
make install
cd ..
git clone https://github.com/cloudflare/lua-resty-cookie
cd lua-resty-cookie
make install
cd ..
git clone https://github.com/bungle/lua-resty-session
cp -a lua-resty-session/lib/resty/session* /usr/local/lib/lua/resty/
git clone https://github.com/ittner/lua-gd/
cd lua-gd
gcc -o gd.so -DGD_XPM -DGD_JPEG -DGD_FONTCONFIG -DGD_FREETYPE -DGD_PNG -DGD_GIF -O2 -Wall -fPIC -fomit-frame-pointer -I/usr/local/include/luajit-2.1 -DVERSION=\"2.0.33r3\" -shared -lgd luagd.c
mv gd.so /usr/local/lib/lua/5.1/gd.so
cd ..
wget -O /usr/local/lib/lua/resty/aes_functions.lua https://github.com/c64bob/lua-resty-aes/raw/master/lib/resty/aes_functions.lua
mkdir /etc/nginx/resty/
#include seems to be a bit mssed up with luajit #include seems to be a bit mssed up with luajit
mkdir /etc/nginx/resty
ln -s /usr/local/lib/lua/resty/ /etc/nginx/resty/ ln -s /usr/local/lib/lua/resty/ /etc/nginx/resty/
make -j16 modules make -j16 modules
cp -r objs modules cp -r objs modules
rm -R /etc/nginx/modules
mkdir /etc/nginx/modules
mv modules /etc/nginx/modules mv modules /etc/nginx/modules
cd .. cd ..
rm /etc/nginx/nginx.conf
mv nginx.conf /etc/nginx/nginx.conf mv nginx.conf /etc/nginx/nginx.conf
rm /etc/nginx/naxsi_core.rules
mv naxsi_core.rules /etc/nginx/naxsi_core.rules mv naxsi_core.rules /etc/nginx/naxsi_core.rules
rm /etc/nginx/naxsi_whitelist.rules
mv naxsi_whitelist.rules /etc/nginx/naxsi_whitelist.rules mv naxsi_whitelist.rules /etc/nginx/naxsi_whitelist.rules
rm -R /etc/nginx/lua/
mv lua /etc/nginx/ mv lua /etc/nginx/
mv resty/* /etc/nginx/resty/resty/ mv resty/* /etc/nginx/resty/resty/
rm /etc/nginx/resty/caphtml_d.lua
mv /etc/nginx/resty/resty/caphtml_d.lua /etc/nginx/resty/caphtml_d.lua mv /etc/nginx/resty/resty/caphtml_d.lua /etc/nginx/resty/caphtml_d.lua
rm /etc/nginx/resty/resty/random.lua
mv random.lua /etc/nginx/resty/resty/random.lua
mv queue.html /etc/nginx/queue.html
rm -R /etc/nginx/sites-enabled/
mkdir /etc/nginx/sites-enabled/ mkdir /etc/nginx/sites-enabled/
mv site.conf /etc/nginx/sites-enabled/site.conf mv site.conf /etc/nginx/sites-enabled/site.conf
rm /etc/nginx/cap_d.css
#background generation
apt-get install -y python3-pil
mv gen_background.py /etc/nginx/gen_background.py
echo "* * * * * root python3 /etc/nginx/gen_background.py" > /etc/cron.d/background-generate
mv font.ttf /etc/nginx/font.ttf
mv cap_d.css /etc/nginx/cap_d.css mv cap_d.css /etc/nginx/cap_d.css
chown -R www-data:www-data /etc/nginx/ chown -R www-data:www-data /etc/nginx/
chown -R www-data:www-data /usr/local/lib/lua chown -R www-data:www-data /usr/local/lib/lua
chmod 755 startup.sh chmod 755 startup.sh
rm /startup.sh
mv startup.sh /startup.sh mv startup.sh /startup.sh
chmod 755 rc.local chmod 755 rc.local
rm /etc/rc.local
mv rc.local /etc/rc.local mv rc.local /etc/rc.local
rm /etc/sysctl.conf
mv sysctl.conf /etc/sysctl.conf mv sysctl.conf /etc/sysctl.conf
pkill tor pkill tor
mv torrc /etc/tor/torrc mv torrc /etc/tor/torrc
if $LOCALPROXY
then
echo "localproxy enabled"
else
mv torrc2 /etc/tor/torrc2
mv torrc3 /etc/tor/torrc3
fi
torhash=$(tor --hash-password $TORAUTHPASSWORD| tail -c 62) torhash=$(tor --hash-password $TORAUTHPASSWORD| tail -c 62)
string="s/hashedpassword/" string="s/hashedpassword/"
string+="$torhash" string+="$torhash"
@ -215,7 +273,7 @@ string+="$HOSTNAME"
string+="/g" string+="/g"
sed -i $string /etc/nginx/sites-enabled/site.conf sed -i $string /etc/nginx/sites-enabled/site.conf
echo "MasterOnionAddress $MASTERONION" >> /etc/tor/hidden_service/ob_config echo "MasterOnionAddress $MASTERONION" > /etc/tor/hidden_service/ob_config
pkill tor pkill tor
sleep 10 sleep 10
@ -223,8 +281,19 @@ sleep 10
sed -i "s/#HiddenServiceOnionBalanceInstance/HiddenServiceOnionBalanceInstance/g" /etc/tor/torrc sed -i "s/#HiddenServiceOnionBalanceInstance/HiddenServiceOnionBalanceInstance/g" /etc/tor/torrc
tor tor
if $LOCALPROXY
then
echo "localproxy enabled"
else
tor -f /etc/tor/torrc2
tor -f /etc/tor/torrc3
fi
nginx nginx
service vanguards start service vanguards start
nginx -s stop
nginx
clear clear

View File

@ -6,13 +6,16 @@
#keepalive 128; or proxy_bind on multiple local ips can be used to mitigate local port exhaustion #keepalive 128; or proxy_bind on multiple local ips can be used to mitigate local port exhaustion
#most likely with this setup it's not the case #most likely with this setup it's not the case
#if this runs on the same machine as the application server UNIX sockets should be used instead of TCP #if this runs on the same machine as the application server UNIX sockets should be used instead of TCP
upstream tor {
server 127.0.0.1:9060;
server 127.0.0.1:9070;
}
access_by_lua_no_postpone on; access_by_lua_no_postpone on;
lua_package_path "/etc/nginx/resty/?.lua;;"; lua_package_path "/etc/nginx/resty/?.lua;;";
init_by_lua_block { init_by_lua_block {
allowed_hosts = { "mainonion", allowed_hosts = { "mainonion",
"masterbalanceonion" "dreadytofatroptsdj6io7l3xptbet6onoyno2yv7jicoxknyazubrad.onion"
} }
function in_array(tab, val) function in_array(tab, val)
@ -63,17 +66,18 @@ init_by_lua_block {
function kill_circuit(premature, clientip, headerip) function kill_circuit(premature, clientip, headerip)
local circuitid = calc_circuit(headerip) local circuitid = calc_circuit(headerip)
local sockfile = "unix:/etc/tor/c1"
local response = "Closing circuit " .. circuitid .. " " local response = "Closing circuit " .. circuitid .. " "
local sock = ngx.socket.tcp() local sock = ngx.socket.tcp()
sock:settimeout(1000) sock:settimeout(1000)
local ok, err = sock:connect(clientip, 9051) local ok, err = sock:connect(sockfile)
if not ok then if not ok then
ngx.log(ngx.ERR, "failed to connect to tor: " .. err) ngx.log(ngx.ERR, "failed to connect to tor: " .. err)
return return
end end
ngx.log(ngx.ERR, "connected to tor") ngx.log(ngx.ERR, "connected to tor")
local bytes, err = sock:send("authenticate \"torauthpassword\"\n") local bytes, err = sock:send("authenticate \"changethispassowrd\"\n")
if not bytes then if not bytes then
ngx.log(ngx.ERR, "failed authenticate to tor: " .. err) ngx.log(ngx.ERR, "failed authenticate to tor: " .. err)
return return
@ -107,15 +111,15 @@ init_by_lua_block {
#limiting by proxy_protocol_addr won't work with V2 onions and maybe should be disabled. #limiting by proxy_protocol_addr won't work with V2 onions and maybe should be disabled.
#limiting by cookie_<name> works regarless and must be used, otherwise an attacker can solve a captcha by hand and add it to a script/bot #limiting by cookie_<name> works regarless and must be used, otherwise an attacker can solve a captcha by hand and add it to a script/bot
limit_req_zone $proxy_protocol_addr zone=circuits:50m rate=3r/s; limit_req_zone $proxy_protocol_addr zone=circuits:50m rate=6r/s;
limit_req_zone $cookie_dcap zone=capcookie:50m rate=3r/s; limit_req_zone $cookie_dcap zone=capcookie:50m rate=6r/s;
#proxy_protocol only makes sense with V3 onions (exportcircuitid) otherwise it will break things. #proxy_protocol only makes sense with V3 onions (exportcircuitid) otherwise it will break things.
#kill_circuit won't be used without it #kill_circuit won't be used without it
server { server {
listen 88 proxy_protocol backlog=16384; listen unix:/etc/tor/nginx1 proxy_protocol bind;
allow 127.0.0.1; allow unix:;
deny all; deny all;
#access_log /var/log/nginx/front_access.log; #access_log /var/log/nginx/front_access.log;
@ -175,7 +179,7 @@ server {
#naxsi seems to kick in before everything else except rate limiter but if it does trash traffic won't make it to the application servers anyway #naxsi seems to kick in before everything else except rate limiter but if it does trash traffic won't make it to the application servers anyway
#doesn't make sense to blacklist cookie as it will annoy users #doesn't make sense to blacklist cookie as it will annoy users
location = @waf { location @waf {
error_log /var/log/nginx/front_error.log; error_log /var/log/nginx/front_error.log;
default_type text/html; default_type text/html;
content_by_lua_block { content_by_lua_block {
@ -204,6 +208,20 @@ server {
} }
} }
location @502 {
default_type text/html;
content_by_lua_block {
ngx.say("<head><title>502 Timeout</title></head>")
ngx.say("<body bgcolor=\"white\">")
ngx.say("<center><h1>502 Timeout</h1></center>")
ngx.say("<hr><center><p>It seems this endgame front doesn't have a stable connection to the backend right now.</p></center>")
ngx.say("<center><p>To fix it you can try to reload the page. If that doesn't work, and you end back here, get a new circuit.</p></center>")
ngx.say("<center><p>If getting a new circuit doesn't work. Try to get a brand new Tor identity.</p></center>")
ngx.say("<center><p>If getting a new Tor identity doesn't work come back later.</p></center></body>")
}
}
location /kill { location /kill {
access_by_lua_block { access_by_lua_block {
proxyip = "no_proxy" proxyip = "no_proxy"
@ -242,15 +260,17 @@ server {
error_log /var/log/nginx/front_error.log; error_log /var/log/nginx/front_error.log;
#rate limits per circuit ID (won't work with V2 and maybe should be disabled) #rate limits per circuit ID (won't work with V2 and maybe should be disabled)
limit_req zone=circuits burst=6 nodelay; limit_req zone=circuits burst=10 nodelay;
error_page 503 =503 @ratelimit; error_page 503 =503 @ratelimit;
#rate limits based on captcha cookie. if an attacker or bot solves the capcha by hand and inputs the cookie in a script #rate limits based on captcha cookie. if an attacker or bot solves the capcha by hand and inputs the cookie in a script
#the cookie will be blacklisted by all fronts (eventually) and subsequent requests dropped. #the cookie will be blacklisted by all fronts (eventually) and subsequent requests dropped.
limit_req zone=capcookie burst=6 nodelay; limit_req zone=capcookie burst=10 nodelay;
error_page 503 =503 @ratelimit; error_page 503 =503 @ratelimit;
error_page 502 =502 @502;
#check if access captca is solved and other things #check if access captca is solved and other things
access_by_lua_file lua/cap.lua; access_by_lua_file lua/cap.lua;
@ -265,12 +285,14 @@ server {
include "/etc/nginx/naxsi_whitelist.rules"; include "/etc/nginx/naxsi_whitelist.rules";
error_log /etc/nginx/naxsi.log; error_log /etc/nginx/naxsi.log;
proxy_set_header Host $host; proxy_set_header Host $host;
#socks_pass socks5://127.0.0.1:9050; socks_pass socks5://tor;
#socks_set_host exampleprivatev3onion.onion; socks_set_host biblemeowimkh3utujmhm6oh2oeb3ubjw2lpgeq3lahrfr2l6ev6zgyd.onion;
#socks_set_header Host $host; socks_set_header Host $host;
#socks_redirect off; socks_redirect off;
#socks_http_version 1.1; socks_http_version 1.1;
proxy_pass http://10.10.10.10; socks_next_upstream error timeout invalid_header http_500 http_502 http_503;
#proxy_pass http://10.10.10.10:80;
header_filter_by_lua_block { header_filter_by_lua_block {
local cookie, err = cook:new() local cookie, err = cook:new()
if not cookie then if not cookie then

View File

@ -1,5 +1,7 @@
#!/bin/bash #!/bin/bash
tor tor
tor -f /etc/tor/torrc2
tor -f /etc/tor/torrc3
service vanguards start service vanguards start
exit 0 exit 0

View File

@ -58,8 +58,5 @@
# Log Martian Packets # Log Martian Packets
#net.ipv4.conf.all.log_martians = 1 #net.ipv4.conf.all.log_martians = 1
# #
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
net.core.somaxconn=16384 net.core.somaxconn=16384
net.core.netdev_max_backlog=100000 net.core.netdev_max_backlog=100000

12
torrc
View File

@ -3,17 +3,13 @@
# The instance servers run standard onion services. In Basic mode the # The instance servers run standard onion services. In Basic mode the
# control port does not need to be enabled. # control port does not need to be enabled.
#User debian-tor #User debian-tor
#DataDirectory /etc/tor/tor-data DataDirectory /etc/tor/1/
# ControlPort 9051
# CookieAuthentication 1
#SocksPort 0
RunAsDaemon 1 RunAsDaemon 1
HiddenServiceDir /etc/tor/hidden_service HiddenServiceDir /etc/tor/hidden_service
HiddenServicePort 80 127.0.0.1:88 HiddenServicePort 80 unix:/etc/tor/nginx1
HiddenServiceMaxStreams 8 HiddenServiceMaxStreams 15
HiddenServiceMaxStreamsCloseCircuit 1 HiddenServiceMaxStreamsCloseCircuit 1
HiddenServiceNumIntroductionPoints 3 HiddenServiceNumIntroductionPoints 3
HiddenServiceExportCircuitID haproxy HiddenServiceExportCircuitID haproxy
@ -21,5 +17,7 @@ HiddenServiceExportCircuitID haproxy
CookieAuthentication 1 CookieAuthentication 1
ControlPort 9051 ControlPort 9051
ControlPort unix:/etc/tor/c1 WorldWritable RelaxDirModeCheck
SocksPort unix:/etc/tor/s1 WorldWritable RelaxDirModeCheck
HashedControlPassword hashedpassword HashedControlPassword hashedpassword
HardwareAccel 1 HardwareAccel 1

7
torrc2 Normal file
View File

@ -0,0 +1,7 @@
#User debian-tor
DataDirectory /etc/tor/2/
RunAsDaemon 1
SocksPort 9060
ControlPort unix:/etc/tor/c2 WorldWritable RelaxDirModeCheck
CookieAuthentication 1

7
torrc3 Normal file
View File

@ -0,0 +1,7 @@
#User debian-tor
DataDirectory /etc/tor/3/
RunAsDaemon 1
SocksPort 9070
ControlPort unix:/etc/tor/c3 WorldWritable RelaxDirModeCheck
CookieAuthentication 1