Permalink
Browse files

Initial commit

  • Loading branch information...
melvinsh committed Aug 23, 2016
0 parents commit 7bd62edbdc4151fffbd587d61ae05672de6522bc
@@ -0,0 +1,4 @@
+.DS_Store
+*.swp
+!output/.gitkeep
+output/*
@@ -0,0 +1,7 @@
+source 'https://rubygems.org'
+
+gem 'pry'
+
+gem 'tty'
+gem 'http'
+gem 'nokogiri'
@@ -0,0 +1,92 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ addressable (2.4.0)
+ coderay (1.1.1)
+ domain_name (0.5.20160615)
+ unf (>= 0.0.5, < 1.0.0)
+ equatable (0.5.0)
+ http (2.0.3)
+ addressable (~> 2.3)
+ http-cookie (~> 1.0)
+ http-form_data (~> 1.0.1)
+ http_parser.rb (~> 0.6.0)
+ http-cookie (1.0.2)
+ domain_name (~> 0.5)
+ http-form_data (1.0.1)
+ http_parser.rb (0.6.0)
+ method_source (0.8.2)
+ mini_portile2 (2.1.0)
+ necromancer (0.3.0)
+ nokogiri (1.6.8)
+ mini_portile2 (~> 2.1.0)
+ pkg-config (~> 1.1.7)
+ pastel (0.6.1)
+ equatable (~> 0.5.0)
+ tty-color (~> 0.3.0)
+ pkg-config (1.1.7)
+ pry (0.10.4)
+ coderay (~> 1.1.0)
+ method_source (~> 0.8.1)
+ slop (~> 3.4)
+ slop (3.6.0)
+ tty (0.5.0)
+ equatable (~> 0.5.0)
+ pastel (~> 0.6.0)
+ tty-color (~> 0.3.0)
+ tty-command (~> 0.1.0)
+ tty-cursor (~> 0.3.0)
+ tty-pager (~> 0.4.0)
+ tty-platform (~> 0.1.0)
+ tty-progressbar (~> 0.9.0)
+ tty-prompt (~> 0.6.0)
+ tty-screen (~> 0.5.0)
+ tty-spinner (~> 0.2.0)
+ tty-table (~> 0.5.0)
+ tty-which (~> 0.1.0)
+ tty-color (0.3.0)
+ tty-command (0.1.0)
+ pastel (~> 0.6.0)
+ tty-cursor (0.3.0)
+ tty-pager (0.4.0)
+ tty-screen (~> 0.5.0)
+ tty-which (~> 0.1.0)
+ verse (~> 0.4.0)
+ tty-platform (0.1.0)
+ tty-progressbar (0.9.0)
+ tty-screen (~> 0.5.0)
+ tty-prompt (0.6.0)
+ necromancer (~> 0.3.0)
+ pastel (~> 0.6.0)
+ tty-cursor (~> 0.3.0)
+ tty-platform (~> 0.1.0)
+ wisper (~> 1.6.1)
+ tty-screen (0.5.0)
+ tty-spinner (0.2.0)
+ tty-table (0.5.0)
+ equatable (~> 0.5.0)
+ necromancer (~> 0.3.0)
+ pastel (~> 0.6.0)
+ tty-screen (~> 0.5.0)
+ unicode_utils (~> 1.4.0)
+ verse (~> 0.4.0)
+ tty-which (0.1.0)
+ unf (0.1.4)
+ unf_ext
+ unf_ext (0.0.7.2)
+ unicode_utils (1.4.0)
+ verse (0.4.0)
+ unicode_utils (~> 1.4.0)
+ wisper (1.6.1)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ http
+ nokogiri
+ pry
+ tty
+
+BUNDLED WITH
+ 1.12.5
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2016 Melvin Lammerts
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
@@ -0,0 +1,35 @@
+vcsmap
+========
+
+![vcsmap screenshot](https://cloud.githubusercontent.com/assets/1312973/17897353/2ec8c11a-6954-11e6-9906-4f4f0926f58d.png)
+
+vcsmap is a plugin-based tool to scan public version control systems (GitHub and [possibly Gitlab soon](https://gitlab.com/gitlab-org/gitlab-ee/issues/556)) for sensitive information like access tokens and credentials.
+
+## Installation
+Download the source code and run the following command. You might need build tools like `gcc`.
+``` ruby
+$ bundle
+```
+
+## Usage
+vcsmap already includes a couple of plugins. Each plugin looks for files that match a certain search query, and extracts the right data from those files. To view all plugins run:
+```
+$ ruby vcsmap.rb list
+```
+
+To use a plugin, look up the name of the plugin (`[plugin_name]`) and run the following command:
+```
+$ ruby vcsmap.rb run {plugin_name} {pages} {--no-ascii}
+```
+- You need to specify the amount of pages you want to search. Each (GitHub) page has 10 possible results.
+- Use the `--no-ascii` flag (last) to disable rendering the results as an ASCII table in the command line.
+
+## Plugin development
+To add a new plugin, create an entry for it in `lib/plugin_list.rb` and create a new plugin class that extends `Vcsmap::Plugin::BasePlugin` in `lib/plugins`.
+
+Each plugin has a `@search_string` that is used to search for matching files, and a couple of regex matchers to extract the needed data. Of course you don't need to use regex, it's also possible to parse the file (e.g. with `JSON`), as long as your plugin returns a valid `table_header` and matching `credentials`.
+
+**The easiest way** to develop a new plugin is to copy an simple existing one (like `lib/plugins/solr_dataconfig.rb`) and modify it until it fits your requirements.
+
+## DISCLAIMER
+Using the data obtained by this tool to gain unauthorized to access computer systems or services is **ILLEGAL** and therefore punisable by law. The author of this tool is **not** responsible for any damage caused by use/abuse of this tool.
@@ -0,0 +1,76 @@
+module Vcsmap
+ class CLI
+ def initialize(arguments)
+ @command = arguments[0]
+ @plugin = arguments[1]
+ @pages = arguments[2]
+ @output = arguments[3]
+ end
+
+ def run
+ case @command
+ when 'list'
+ list_plugins
+ when 'run'
+ run_plugin(@output === '--no-ascii')
+ when nil
+ abort "vcsmap requires a command. #{usage}"
+ else
+ abort "Command not recognized. #{usage}"
+ end
+ end
+
+ private
+
+ def usage
+ 'Check out README.md for instructions.'
+ end
+
+ def list_plugins
+ puts "Available plugins:\n\n"
+ PluginList.render_list
+ exit
+ end
+
+ def run_plugin(no_ascii)
+ begin
+ plugin = PluginList.find(@plugin)
+ plugin = Object.const_get(plugin[:class_name]).new
+ rescue KeyError
+ abort "Cannot find plugin with name '#{@plugin}'."
+ rescue NameError
+ abort "The plugin '#{@plugin}' has not been implemented yet."
+ end
+
+ abort 'Specify a number of pages to load after the plugin name (1 = ~10 results).' unless @pages
+
+ puts 'Searching for matching files ...'
+ provider = Vcsmap::Provider::GitHub.new
+ results = provider.search(plugin, @pages.to_i)
+
+ bar = Vcsmap::ProgressBar.new(results.count)
+
+ data = []
+
+ results.each do |result|
+ bar.step
+ file = HTTP.follow(true).get(result).body.to_s
+ credentials = plugin.credentials(file)
+ credentials << result.split('/').slice(3, 2).join('/')
+ # TODO: make an object that holds credentials and has empty? and valid? methods.
+ # this object should also be able to filter 'false' creds like localhost and default ones.
+ data << credentials unless credentials[1].nil? || credentials[1].empty?
+ end
+
+ bar.clear
+
+ csv_writer = Vcsmap::CsvWriter.new(@plugin, plugin.table_header, data)
+ csv_writer.write!
+
+ unless no_ascii
+ table = TTY::Table.new plugin.table_header << 'Repo', data
+ puts table.render(:ascii)
+ end
+ end
+ end
+end
@@ -0,0 +1,33 @@
+module Vcsmap
+ class CsvWriter
+ def initialize(plugin_name, plugin_header, data)
+ @file_path = file_path(plugin_name)
+ @header = header(plugin_header)
+ @data = data
+ end
+
+ def write!
+ puts "Writing CSV to #{@file_path} ..."
+ CSV.open(@file_path, 'wb', force_quotes: true) do |csv|
+ csv << @header
+ @data.each do |line|
+ csv << line
+ end
+ end
+ end
+
+ private
+
+ def folder
+ 'output/'
+ end
+
+ def file_path(plugin_name)
+ folder + "#{DateTime.now.strftime('%Y%m%dT%H%M')}-#{plugin_name}.csv"
+ end
+
+ def header(plugin_header)
+ plugin_header << 'Repo'
+ end
+ end
+end
@@ -0,0 +1,4 @@
+module Vcsmap
+ module Plugin
+ end
+end
@@ -0,0 +1,47 @@
+module Vcsmap
+ class PluginList
+ PLUGINS = {
+ 'wordpress_config': {
+ title: 'Wordpress configuration files',
+ description: 'Extracts database credentials from wp-config.php.',
+ class_name: 'Vcsmap::Plugin::WordpressConfig'
+ },
+ 'google_oauth': {
+ title: 'Google oAuth tokens',
+ description: 'Extracts oAuth credentials from client_secrets.json.',
+ class_name: 'Vcsmap::Plugin::GoogleOauth'
+ },
+ 'filezilla_xml': {
+ title: 'Filezilla configuration XML',
+ description: 'Extracts FTP credentials from Filezilla configuration files.',
+ class_name: 'Vcsmap::Plugin::FilezillaXml'
+ },
+ 'solr_dataconfig': {
+ title: 'Solr dataConfig.xml credentials',
+ description: 'Extracts JdbcDataSource credentials from a Solor dataConfig.xml.',
+ class_name: 'Vcsmap::Plugin::SolrDataconfig'
+ },
+ 'sublime_github': {
+ title: 'Sublime Text GitHub tokens',
+ description: 'Extracts GitHub tokens from the Sublime Text settings file for GitHub.',
+ class_name: 'Vcsmap::Plugin::GithubSublimesettings'
+ }
+ }
+
+ def self.all
+ PLUGINS.sort
+ end
+
+ def self.find(name)
+ PLUGINS.fetch(name.to_sym)
+ end
+
+ def self.render_list
+ all.each do |plugin|
+ puts Pastel.new.green "[#{plugin[0]}] #{plugin[1][:title]}"
+ puts plugin[1][:description]
+ puts
+ end
+ end
+ end
+end
@@ -0,0 +1,19 @@
+module Vcsmap
+ module Plugin
+ class BasePlugin
+ attr_reader :search_string
+
+ def capture_match(regex, file)
+ match = regex.match(clean_file(file))
+ return '' if match.nil?
+ match.captures.first
+ end
+
+ private
+
+ def clean_file(file)
+ file.scrub
+ end
+ end
+ end
+end
@@ -0,0 +1,37 @@
+module Vcsmap
+ module Plugin
+ class FilezillaXml < Vcsmap::Plugin::BasePlugin
+ def initialize
+ @search_string = 'filename:filezilla.xml+Pass'
+ @host_regex = /<Host>(.*)<\/Host>/
+ @username_regex = /<User>(.*)<\/User>/
+ @password_regex = /<Pass>(.*)<\/Pass>/
+ @encoded_password_regex = /<Pass encoding="base64">(.*)<\/Pass>/
+ @port_regex = /<Port>(.*)<\/Port>/
+ end
+
+ def credentials(file)
+ @host = capture_match(@host_regex, file)
+ @user = capture_match(@username_regex, file)
+ @pass = find_password(file)
+ @port = capture_match(@port_regex, file)
+ ['FTP', @host, @port, @user, @pass]
+ end
+
+ def table_header
+ %w(Protocol Host Port Username Password)
+ end
+
+ private
+
+ def find_password(file)
+ @pass = capture_match(@password_regex, file)
+ @base64_pass = capture_match(@encoded_password_regex, file)
+
+ return @pass unless @pass.empty?
+ return Base64.decode64(@base64_pass) unless @base64_pass.empty?
+ ''
+ end
+ end
+ end
+end
Oops, something went wrong.

0 comments on commit 7bd62ed

Please sign in to comment.