concuss 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c5ead6fff82661378bb379172fb4e62a3d22c5a312d363cc9c49a8ebce268e40
4
- data.tar.gz: 9bce360c65581ef6ec55ecd6084609680fff69a4480f7ef75041a8cb1c8df756
3
+ metadata.gz: ebbc8f1ad06752a93ba6b66ef4fa666a693532a792b71d14ff830ae1a7537494
4
+ data.tar.gz: 8a1b02193ffb5bac321fbad3a4905c7b00f9ddfec1df543ab755aba9c564b4e2
5
5
  SHA512:
6
- metadata.gz: 5f83174439a0930992841f179026927addfd43b3aab14ce08738959f28a04a23eff6b40d83ca610586b5d23aa22c9073d59c0fa04843e0fe15bcf8f865a51302
7
- data.tar.gz: 517db0408b735b4f4741f720f23222e5c9ab2f4cdc5b09f3a943df65e54b33dfc68256bcd3b9fda1d350c597003b6ce7cbb6bcc08e39a6b15f8d1233872f2ad3
6
+ metadata.gz: f9407c5f49b7dc5b2f03ad0b69e00e21b6f1940ea6167c630ffe59fd4366bd1e099c68e061fe2943c9c3c41ff4103ce781b50e9ed85a268627ab7e2fe41b0050
7
+ data.tar.gz: e891c23429f6931b81bf9b00a9b38bb4ecb361ebc3c40d35dead0b7f5510e8a7ed33cd88c27dbb532d438baed12126cabf588cf04dc82448e64b45584402a0a3
data/CHANGELOG.md CHANGED
@@ -1,5 +1,7 @@
1
- ## [Unreleased]
1
+ ## [0.2.0] - 2023-01-29
2
+
3
+ * Add support for custom user agents. If none provided, used a default user agent that identifies itself as Concuss/VERSION
2
4
 
3
5
  ## [0.1.0] - 2023-01-18
4
6
 
5
- - Initial release
7
+ * Initial release
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- concuss (0.1.0)
4
+ concuss (0.2.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -1,11 +1,16 @@
1
1
  # Concuss
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/concuss.svg)](https://badge.fury.io/rb/concuss)
4
+ [![Build Status](https://github.com/patricktulskie/concuss/actions/workflows/main.yml/badge.svg)](https://github.com/patricktulskie/concuss/actions/workflows/main.yml)
5
+
3
6
  ## What is it?
4
7
 
5
8
  Concuss is a tool for banging against a url with a bunch of different headers to look for potential vulnerabilities.
6
9
 
7
10
  It works by sending a custom or a random string to a webserver for a specific url in a variety of headers to see if it's able to get that string to appear on the page. If so, you'll get a HIT and you can evaluate that header to see if it's useful for some kind of XSS, cache poisoning, or some other form of injection from malformed headers.
8
11
 
12
+ Often times web app and framework developers assume that headers from the client are not malicious or manipulated and will just write them out to the page in one form or another. This project is a specialized tool to find instances of this so that they can be fixed before they are abused.
13
+
9
14
  ## What it is NOT.
10
15
 
11
16
  Concuss is not a tool for automating vulnerabilities, nor should it be used to certify that a page is safe from vulnerabilities. It should not be used on applications or website that you do not personally have permission to scan.
@@ -39,17 +44,39 @@ While concuss is designed to be used as a command line tool, you can also includ
39
44
  require 'concuss'
40
45
 
41
46
  concuss = Concuss.new(url: 'http://localhost:4567', file: 'header_file.txt', header_set: :standard, test_string: "OOGABOOGA")
42
- concuss.attack!
47
+ report = concuss.attack!
43
48
  ```
44
49
 
45
- This will spit out the results, which isn't super useful if you need to post process them... I'll work on that though.
50
+ From there, you'll get a `Concuss::Report` object that contains the raw data as well as filters for `hits`, `misses`, `headers`, and the `url` in the event you've done a bunch of these.
51
+
52
+ ## Demo
53
+
54
+ There is a sample app with a vulnerability to test with in the `vuln_app` directory. It takes the contents of `X-CSRF-Token` and spits them out blindly.
55
+
56
+ Here's some steps to get going with that:
57
+
58
+ ```
59
+ docker-compose build
60
+ docker-compose run console bash
61
+
62
+ # You should see the vuln_app container boot up and
63
+ # then you'll land in bash on the console container
64
+
65
+ bin/concuss http://vuln_app:4567 -h non_standard
66
+ ```
46
67
 
47
68
  ## Development
48
69
 
49
- After checking out the repo, run `script/setup` to install dependencies. Then, run `rspec` to run the tests. You can also run `script/console` for an interactive prompt that will allow you to experiment.
70
+ Primarily, you'll want to use Docker to do debugging and development. To get into the console, just run `docker-compose run console bash` and from there you can run `bin/concuss` against the sample vulnerable app, or you can run `rspec` to run the specs.
71
+
72
+ If you prefer to develop without docker, after checking out the repo, run `bundle install` to install dependencies. Then, run `rspec` to run the tests.
73
+
74
+ When developing using docker or on your bare machine you can also run `script/console` for an interactive prompt that will allow you to experiment. This will give you access to the underlying classes and let you experiment outside the confines of the CLI.
50
75
 
51
76
  If you add features or fix bugs, please write specs and open up a PR.
52
77
 
78
+ Note: Concuss was designed to easily install on most systems with ruby 3+. As such, its only dependencies are in the ruby standard library.
79
+
53
80
  ## Contributing
54
81
 
55
82
  Bug reports and pull requests are welcome on GitHub at https://github.com/patricktulskie/concuss
data/bin/concuss CHANGED
@@ -19,6 +19,10 @@ OptionParser.new do |opts|
19
19
  opts.on("-t", "--test-string STRING", "Set a custom test string. If none specified, it sets a random string to match on.") do |v|
20
20
  options[:test_string] = v
21
21
  end
22
+
23
+ opts.on("-a", "--user-agent STRING", "Set a custom user agent. If none specified, it defaults to Concuss/#{Concuss::VERSION}") do |v|
24
+ options[:user_agent] = v
25
+ end
22
26
  end.parse!
23
27
 
24
28
  if ARGV[0].nil?
@@ -27,5 +31,9 @@ if ARGV[0].nil?
27
31
  end
28
32
 
29
33
  options[:url] = ARGV[0]
34
+ options[:formatter] = Concuss::Formatters::Table
35
+ options[:progress_type] = :dots
36
+
37
+ puts "Scanning #{options[:url]}"
30
38
 
31
39
  Concuss.new(**options).attack!
@@ -0,0 +1,31 @@
1
+ class Concuss::Formatters::Table
2
+
3
+ HEADINGS = ['HEADER', 'RESPONSE_CODE', 'HIT/MISS'].freeze
4
+
5
+ def initialize(report)
6
+ @report = report
7
+ end
8
+
9
+ def print
10
+ header_max_length = @report.headers.map(&:length).max
11
+ response_code_length = HEADINGS[1].length
12
+ hit_header_length = HEADINGS[2].length
13
+
14
+ header_format = "%-#{header_max_length}s | %-#{response_code_length}s | %-0s\n"
15
+ separator = '-' * (header_max_length + response_code_length + hit_header_length + 6)
16
+
17
+ puts header_format % HEADINGS
18
+ puts separator
19
+ print_data(@report.hits, header_format)
20
+ print_data(@report.misses, header_format)
21
+ end
22
+
23
+ private
24
+
25
+ def print_data(data, header_format)
26
+ data.sort.each do |header, values|
27
+ puts header_format % [header, values[:response_code], values[:hit] ? 'HIT' : 'MISS']
28
+ end
29
+ end
30
+ end
31
+
@@ -0,0 +1,6 @@
1
+ class Concuss
2
+ module Formatters
3
+ end
4
+ end
5
+
6
+ require_relative 'formatters/table'
@@ -30,7 +30,6 @@ class Concuss::Headers
30
30
  'Range',
31
31
  'Referer',
32
32
  'TE',
33
- 'User-Agent',
34
33
  'Upgrade',
35
34
  'Via',
36
35
  'Warning'
@@ -0,0 +1,20 @@
1
+ class Concuss::Report
2
+ attr_reader :data, :url
3
+
4
+ def initialize(data:, url:)
5
+ @url = url
6
+ @data = data
7
+ end
8
+
9
+ def hits
10
+ data.select { |_, value| value[:hit] }
11
+ end
12
+
13
+ def misses
14
+ data.reject { |_, value| value[:hit] }
15
+ end
16
+
17
+ def headers
18
+ data.keys
19
+ end
20
+ end
@@ -2,30 +2,44 @@ require 'securerandom'
2
2
  require 'net/http'
3
3
 
4
4
  class Concuss::Runner
5
- attr_reader :headers, :url, :test_string
5
+ DEFAULT_USER_AGENT = "Concuss/#{Concuss::VERSION}"
6
6
 
7
- def initialize(headers:, url:, test_string: nil)
7
+ attr_reader :headers, :url, :test_string, :user_agent, :progress_type
8
+
9
+ def initialize(headers:, url:, test_string: nil, user_agent: nil, progress_type: nil)
8
10
  @headers = headers
9
11
  @url = url
10
12
  @test_string = test_string || SecureRandom.hex(25)
13
+ @user_agent = user_agent || DEFAULT_USER_AGENT
14
+ @progress_type = progress_type
11
15
  end
12
16
 
13
17
  def run
14
18
  uri = URI(@url)
19
+ report_data = { }
15
20
 
16
21
  @headers.each do |header|
17
22
  response = Net::HTTP.get_response(uri,
18
- { header => test_string }
23
+ {
24
+ header => test_string,
25
+ 'User-Agent' => user_agent
26
+ }
19
27
  )
20
28
 
21
- if response.code == "200" && response.body.include?(@test_string)
22
- result = "HIT"
23
- else
24
- result = "MISS"
25
- end
29
+ hit = response.code == "200" && response.body.include?(@test_string)
30
+ report_data[header] = { response_code: response.code, hit: hit }
26
31
 
27
- puts "#{header} - #{response.code} - #{result}"
32
+ case progress_type
33
+ when :full
34
+ puts "#{header} - #{response.code} - #{hit ? "HIT" : "MISS"}"
35
+ when :dots
36
+ print "."
37
+ end
28
38
  end
39
+
40
+ puts "" if progress_type == :dots
41
+
42
+ return Concuss::Report.new(data: report_data, url: @url)
29
43
  end
30
44
  end
31
45
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Concuss
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/concuss.rb CHANGED
@@ -3,24 +3,32 @@
3
3
  class Concuss
4
4
  class Error < StandardError; end
5
5
 
6
- attr_reader :url, :file, :header_set, :headers, :test_string
6
+ attr_reader :url, :file, :header_set, :headers, :test_string, :user_agent, :formatter, :progress_type
7
7
 
8
- def initialize(url:, file: nil, header_set: :all, test_string: nil)
8
+ def initialize(url:, file: nil, header_set: :all, test_string: nil, user_agent: nil, formatter: nil, progress_type: nil)
9
9
  @url = url
10
10
  @file = file
11
11
  @header_set = file.nil? ? header_set : :file
12
12
  @test_string = test_string
13
+ @user_agent = user_agent
14
+ @formatter = formatter
15
+ @progress_type = progress_type
13
16
 
14
17
  @headers = Concuss::Headers.new(header_set: @header_set, file: @file).group
15
18
  end
16
19
 
17
20
  def attack!
18
- runner = Concuss::Runner.new(headers: headers, url: url, test_string: test_string)
21
+ runner = Concuss::Runner.new(headers: headers, url: url, test_string: test_string, user_agent: user_agent, progress_type: progress_type)
22
+ results = runner.run
19
23
 
20
- runner.run
24
+ formatter.new(results).print if formatter
25
+
26
+ return results
21
27
  end
22
28
  end
23
29
 
24
30
  require_relative "concuss/version"
31
+ require_relative "concuss/report"
25
32
  require_relative "concuss/headers"
26
33
  require_relative "concuss/runner"
34
+ require_relative "concuss/formatters"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: concuss
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrick Tulskie
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-21 00:00:00.000000000 Z
11
+ date: 2023-02-04 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Test websites for header injection issues
14
14
  email:
@@ -27,7 +27,10 @@ files:
27
27
  - Rakefile
28
28
  - bin/concuss
29
29
  - lib/concuss.rb
30
+ - lib/concuss/formatters.rb
31
+ - lib/concuss/formatters/table.rb
30
32
  - lib/concuss/headers.rb
33
+ - lib/concuss/report.rb
31
34
  - lib/concuss/runner.rb
32
35
  - lib/concuss/version.rb
33
36
  homepage: https://github.com/patricktulskie/concuss
@@ -45,7 +48,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
45
48
  requirements:
46
49
  - - ">="
47
50
  - !ruby/object:Gem::Version
48
- version: 2.6.0
51
+ version: 3.0.0
49
52
  required_rubygems_version: !ruby/object:Gem::Requirement
50
53
  requirements:
51
54
  - - ">="