gauntlt 0.0.7 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +2 -1
- data/.travis.yml +7 -1
- data/Gemfile +1 -1
- data/README.md +14 -8
- data/bin/gauntlt +28 -46
- data/examples/curl/cookies.attack +12 -0
- data/examples/curl/simple.attack +9 -0
- data/examples/curl/verbs.attack +19 -0
- data/examples/nmap/os_detection.attack +16 -0
- data/examples/nmap/simple.attack +16 -0
- data/examples/nmap/tcp_ping_ports.attack +16 -0
- data/examples/nmap/xml_output.attack +18 -0
- data/examples/sslyze/sslyze.attack +23 -0
- data/features/attack.feature +30 -19
- data/features/attacks/curl.feature +23 -15
- data/features/attacks/nmap.feature +16 -93
- data/features/attacks/sqlmap.feature +2 -3
- data/features/attacks/sslyze.feature +7 -29
- data/features/help.feature +3 -30
- data/features/step_definitions/config_steps.rb +1 -1
- data/features/step_definitions/support_steps.rb +15 -0
- data/features/support/hooks.rb +2 -2
- data/features/tags.feature +44 -0
- data/gauntlt.gemspec +0 -2
- data/lib/gauntlt.rb +7 -11
- data/lib/gauntlt/attack.rb +26 -22
- data/lib/gauntlt/attack_adapters/curl.rb +35 -1
- data/lib/gauntlt/attack_adapters/support/cli_helper.rb +1 -1
- data/lib/gauntlt/attack_adapters/support/cookie_helper.rb +3 -10
- data/lib/gauntlt/version.rb +1 -1
- data/spec/gauntlt/attack_spec.rb +13 -35
- data/spec/gauntlt_spec.rb +7 -14
- metadata +12 -30
- data/features/attacks/cookies.feature +0 -25
- data/features/attacks/http_methods.feature +0 -33
- data/features/step_definitions/aruba_extension_steps.rb +0 -3
- data/features/support/attack_steps.rb +0 -1
- data/features/support/profile/profile.xml +0 -5
- data/lib/gauntlt/attack_adapters/cookies.rb +0 -11
- data/lib/gauntlt/attack_adapters/http_methods.rb +0 -12
@@ -9,9 +9,8 @@ Feature: sqlmap attack
|
|
9
9
|
Background:
|
10
10
|
Given "sqlmap" is installed
|
11
11
|
"""
|
12
|
-
When I run `gauntlt
|
13
|
-
Then it should pass
|
14
|
-
And the output should contain:
|
12
|
+
When I run `gauntlt`
|
13
|
+
Then it should pass with:
|
15
14
|
"""
|
16
15
|
1 step (1 passed)
|
17
16
|
"""
|
@@ -1,36 +1,14 @@
|
|
1
1
|
Feature: sslyze attack
|
2
2
|
|
3
|
+
@slow
|
3
4
|
Scenario:
|
4
5
|
Given an attack "sslyze" exists
|
5
|
-
And
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
And the target hostname is "google.com"
|
12
|
-
|
13
|
-
Scenario: Ensure no anonymous certificates
|
14
|
-
When I launch an "sslyze" attack with:
|
15
|
-
\"\"\"
|
16
|
-
python <sslyze_path> <hostname>:443
|
17
|
-
\"\"\"
|
18
|
-
Then the output should not contain:
|
19
|
-
\"\"\"
|
20
|
-
Anon
|
21
|
-
\"\"\"
|
22
|
-
|
23
|
-
# Scenario: Make sure that the certificate key size is at least 2048
|
24
|
-
# Given the target hostname is "google.com"
|
25
|
-
# When I launch an "sslyze" attack with:
|
26
|
-
# \"\"\"
|
27
|
-
# python <sslyze_path> <hostname>:443
|
28
|
-
# \"\"\"
|
29
|
-
# Then the key size should be at least 2048
|
30
|
-
"""
|
31
|
-
When I run `gauntlt attack --name sslyze --attack-file sslyze.attack`
|
32
|
-
Then it should pass
|
33
|
-
And the output should contain:
|
6
|
+
And I copy the attack files from the "examples/sslyze" folder
|
7
|
+
And the following attack files exist:
|
8
|
+
| filename |
|
9
|
+
| sslyze.attack |
|
10
|
+
When I run `gauntlt`
|
11
|
+
Then it should pass with:
|
34
12
|
"""
|
35
13
|
4 steps (4 passed)
|
36
14
|
"""
|
data/features/help.feature
CHANGED
@@ -8,35 +8,8 @@ Feature: Display help info
|
|
8
8
|
When I run `gauntlt --help`
|
9
9
|
Then the output should contain:
|
10
10
|
"""
|
11
|
-
|
12
|
-
"""
|
13
|
-
|
14
|
-
Scenario: Attack help
|
15
|
-
When I run `gauntlt attack -h -n nmap`
|
16
|
-
Then the output should contain:
|
17
|
-
"""
|
18
|
-
usage: gauntlt attack -n [attack-name] -a [attack-file]
|
19
|
-
"""
|
20
|
-
|
21
|
-
Scenario: A user runs gauntlt without any arguments
|
22
|
-
When I run `gauntlt`
|
23
|
-
Then the output should contain:
|
24
|
-
"""
|
25
|
-
Try --help for help
|
26
|
-
"""
|
27
|
-
|
28
|
-
Scenario: A user runs the attack command without specifying attack name
|
29
|
-
When I run `gauntlt attack`
|
30
|
-
Then the output should contain:
|
31
|
-
"""
|
32
|
-
Available attacks:
|
33
|
-
|
34
|
-
cookies
|
35
|
-
curl
|
36
|
-
http_methods
|
37
|
-
nmap
|
38
|
-
sqlmap
|
39
|
-
sslyze
|
11
|
+
gauntlt is a ruggedization framework
|
40
12
|
|
41
|
-
|
13
|
+
Usage:
|
14
|
+
gauntlt <path>+
|
42
15
|
"""
|
@@ -2,4 +2,19 @@ Then /^debug$/ do
|
|
2
2
|
require 'debugger'
|
3
3
|
debugger
|
4
4
|
nil
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'pathname'
|
8
|
+
Given /^I copy the attack files from the "(.*?)" folder$/ do |folder|
|
9
|
+
Dir.glob("./#{folder}/**/*.attack").each do |path|
|
10
|
+
name = Pathname.new(path).basename.to_s
|
11
|
+
contents = File.read(path)
|
12
|
+
write_file(name, contents)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
Given /^the following attack files exist:$/ do |table|
|
17
|
+
table.hashes.each do |hsh|
|
18
|
+
check_file_presence [hsh['filename']], true
|
19
|
+
end
|
5
20
|
end
|
data/features/support/hooks.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
Before('@slow') do
|
2
|
-
@aruba_timeout_seconds =
|
3
|
-
end
|
2
|
+
@aruba_timeout_seconds = 30
|
3
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
Feature: Run attacks by tag
|
2
|
+
|
3
|
+
Background:
|
4
|
+
Given an attack "nmap" exists
|
5
|
+
And a file named "nmap.attack" with:
|
6
|
+
"""
|
7
|
+
Feature: my nmap attacks
|
8
|
+
|
9
|
+
@foo
|
10
|
+
Scenario: Foo
|
11
|
+
Given the target hostname is "foo"
|
12
|
+
|
13
|
+
@bar
|
14
|
+
Scenario: Bar
|
15
|
+
Given the target hostname is "bar"
|
16
|
+
"""
|
17
|
+
|
18
|
+
Scenario: Run attack for one tag
|
19
|
+
When I run `gauntlt --tags @foo`
|
20
|
+
Then it should pass with:
|
21
|
+
"""
|
22
|
+
Feature: my nmap attacks
|
23
|
+
|
24
|
+
@foo
|
25
|
+
"""
|
26
|
+
And the stdout should contain:
|
27
|
+
"""
|
28
|
+
1 scenario (1 passed)
|
29
|
+
1 step (1 passed)
|
30
|
+
"""
|
31
|
+
|
32
|
+
Scenario: Run attack by exluding one tag
|
33
|
+
When I run `gauntlt --tags ~@foo`
|
34
|
+
Then it should pass with:
|
35
|
+
"""
|
36
|
+
Feature: my nmap attacks
|
37
|
+
|
38
|
+
@bar
|
39
|
+
"""
|
40
|
+
And the stdout should contain:
|
41
|
+
"""
|
42
|
+
1 scenario (1 passed)
|
43
|
+
1 step (1 passed)
|
44
|
+
"""
|
data/gauntlt.gemspec
CHANGED
@@ -16,7 +16,6 @@ Gem::Specification.new do |s|
|
|
16
16
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
17
|
s.require_paths = ["lib"]
|
18
18
|
|
19
|
-
# specify any dependencies here; for example:
|
20
19
|
s.add_development_dependency "cucumber"
|
21
20
|
s.add_development_dependency "rspec", "~> 2.11"
|
22
21
|
s.add_development_dependency "aruba"
|
@@ -24,7 +23,6 @@ Gem::Specification.new do |s|
|
|
24
23
|
|
25
24
|
s.add_runtime_dependency "cucumber"
|
26
25
|
s.add_runtime_dependency "aruba"
|
27
|
-
s.add_runtime_dependency "curb"
|
28
26
|
s.add_runtime_dependency "nokogiri"
|
29
27
|
s.add_runtime_dependency "trollop"
|
30
28
|
end
|
data/lib/gauntlt.rb
CHANGED
@@ -13,27 +13,23 @@ module Gauntlt
|
|
13
13
|
|
14
14
|
GAUNTLT_DIR = File.join(CURRENT_DIR, 'gauntlt')
|
15
15
|
|
16
|
-
|
16
|
+
ATTACK_ADAPTERS_DIR = File.join(GAUNTLT_DIR, 'attack_adapters')
|
17
17
|
|
18
|
-
|
18
|
+
ATTACK_ADAPTERS_GLOB_PATTERN = ATTACK_ADAPTERS_DIR + '/*.rb'
|
19
19
|
|
20
20
|
class << self
|
21
|
-
def
|
22
|
-
Dir.glob(
|
21
|
+
def attack_adapters
|
22
|
+
Dir.glob(ATTACK_ADAPTERS_GLOB_PATTERN)
|
23
23
|
end
|
24
24
|
|
25
25
|
def attacks
|
26
|
-
|
26
|
+
attack_adapters.map do |full_path|
|
27
27
|
File.basename(full_path, '.rb')
|
28
28
|
end.sort
|
29
29
|
end
|
30
30
|
|
31
|
-
def
|
32
|
-
|
33
|
-
end
|
34
|
-
|
35
|
-
def attack(name, options={})
|
36
|
-
Attack.new(name, options).run
|
31
|
+
def attack(path, tags=[])
|
32
|
+
Attack.new(path, tags).run
|
37
33
|
end
|
38
34
|
end
|
39
35
|
end
|
data/lib/gauntlt/attack.rb
CHANGED
@@ -3,39 +3,43 @@ require 'cucumber/cli/main'
|
|
3
3
|
|
4
4
|
module Gauntlt
|
5
5
|
class Attack
|
6
|
-
class
|
7
|
-
class ExecutionFailed <
|
8
|
-
|
9
|
-
attr_accessor :name, :opts, :attack_file
|
10
|
-
|
11
|
-
def initialize(name, opts={})
|
12
|
-
if opts[:attack_file] && File.exists?( opts[:attack_file] )
|
13
|
-
self.name = name
|
14
|
-
self.opts = opts
|
15
|
-
self.attack_file = opts[:attack_file]
|
16
|
-
else
|
17
|
-
raise NotFound.new("No '#{opts[:attack_file]}' attack found")
|
18
|
-
end
|
19
|
-
end
|
6
|
+
class NoFilesFound < StandardError; end
|
7
|
+
class ExecutionFailed < StandardError; end
|
20
8
|
|
21
|
-
|
22
|
-
File.expand_path( File.dirname(__FILE__) )
|
23
|
-
end
|
9
|
+
attr_accessor :path, :attack_files, :tags
|
24
10
|
|
25
|
-
def
|
26
|
-
|
11
|
+
def initialize(path, tags=[])
|
12
|
+
self.path = path
|
13
|
+
self.attack_files = attack_files_for(path)
|
14
|
+
self.tags = tags
|
15
|
+
|
16
|
+
raise NoFilesFound.new("No files found in path: #{path}") if attack_files.empty?
|
27
17
|
end
|
28
18
|
|
29
19
|
def run
|
30
|
-
|
20
|
+
args = attack_files + ['--strict', '--require', adapters_dir]
|
21
|
+
args += ['--tags', tags] unless tags.empty?
|
31
22
|
|
32
|
-
cli = Cucumber::Cli::Main.new(
|
23
|
+
cli = Cucumber::Cli::Main.new(args)
|
33
24
|
|
34
25
|
if cli.execute! # cucumber failed, returning true
|
35
26
|
raise ExecutionFailed.new("Bad or undefined attack!")
|
36
27
|
else # cucumber executed successfully, returning false
|
37
|
-
|
28
|
+
true
|
38
29
|
end
|
39
30
|
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def attack_files_for(path)
|
34
|
+
path.split(' ').map{|p| Dir.glob(p)}.flatten
|
35
|
+
end
|
36
|
+
|
37
|
+
def base_dir
|
38
|
+
File.expand_path( File.dirname(__FILE__) )
|
39
|
+
end
|
40
|
+
|
41
|
+
def adapters_dir
|
42
|
+
File.join(base_dir, "attack_adapters")
|
43
|
+
end
|
40
44
|
end
|
41
45
|
end
|
@@ -1,3 +1,37 @@
|
|
1
|
+
When /^"curl" is installed$/ do
|
2
|
+
ensure_cli_installed("curl")
|
3
|
+
end
|
4
|
+
|
5
|
+
When /^I launch a "curl" attack$/ do
|
6
|
+
# curl custom output
|
7
|
+
# from:
|
8
|
+
# http://beerpla.net/2010/06/10/how-to-display-just-the-http-response-code-in-cli-curl/
|
9
|
+
#
|
10
|
+
# for more output variables, see:
|
11
|
+
# http://man.he.net/man1/curl
|
12
|
+
@raw_response = `curl --silent --output /dev/null --write-out "%{http_code}" "#{hostname}"`
|
13
|
+
@response = {
|
14
|
+
:code => @raw_response
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
When /^I launch a "curl" attack with:$/ do |command|
|
19
|
+
command.gsub!('<hostname>', hostname)
|
20
|
+
run command
|
21
|
+
end
|
22
|
+
|
1
23
|
Then /^the response code should be "(.*?)"$/ do |http_code|
|
2
|
-
@response.
|
24
|
+
@response[:code].should == http_code
|
25
|
+
end
|
26
|
+
|
27
|
+
When /^I launch a "cookies" attack$/ do
|
28
|
+
set_cookies( cookies_for(hostname) )
|
29
|
+
end
|
30
|
+
|
31
|
+
Then /^the following cookies should be received:$/ do |table|
|
32
|
+
names = table.hashes.map{|h| h['name'] }
|
33
|
+
names.each do |name|
|
34
|
+
cookies.any?{|s| s =~ /^#{name}/}.should be_true
|
35
|
+
# TODO: check other values in table
|
36
|
+
end
|
3
37
|
end
|
@@ -1,18 +1,11 @@
|
|
1
|
-
require 'curb'
|
2
|
-
|
3
1
|
module Gauntlt
|
4
2
|
module Support
|
5
3
|
module CookieHelper
|
6
4
|
def cookies_for(url)
|
7
|
-
|
8
|
-
c = Curl::Easy.perform(url) do |curl|
|
9
|
-
curl.follow_location = true
|
10
|
-
curl.enable_cookies = true
|
5
|
+
output = `curl --include --location --head --silent "#{url}"`
|
11
6
|
|
12
|
-
|
13
|
-
|
14
|
-
end
|
15
|
-
end
|
7
|
+
output.scan(/^Set-Cookie:.+$/).map do |header|
|
8
|
+
"#{$1}=#{$2}" if header =~ /^Set-Cookie: ([^=]+)=([^;]+;)/
|
16
9
|
end
|
17
10
|
end
|
18
11
|
|
data/lib/gauntlt/version.rb
CHANGED
data/spec/gauntlt/attack_spec.rb
CHANGED
@@ -2,64 +2,42 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Gauntlt::Attack do
|
4
4
|
before do
|
5
|
-
|
5
|
+
Gauntlt::Attack.any_instance.stub(:attack_files_for).with(:foo).and_return([:bar])
|
6
6
|
end
|
7
7
|
|
8
8
|
subject{
|
9
|
-
Gauntlt::Attack.new(:foo
|
9
|
+
Gauntlt::Attack.new(:foo)
|
10
10
|
}
|
11
11
|
|
12
12
|
describe :initialize do
|
13
13
|
context "attack file exists for passed name" do
|
14
|
-
it "sets
|
15
|
-
subject.
|
16
|
-
subject.
|
14
|
+
it "sets path and attack_files" do
|
15
|
+
subject.path.should == :foo
|
16
|
+
subject.attack_files.should == [:bar]
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
context "
|
20
|
+
context "attack_files_for returns an empty array" do
|
21
21
|
it "raises an error if the attack file does not exist" do
|
22
|
-
|
22
|
+
Gauntlt::Attack.any_instance.stub(:attack_files_for).with(:foo).and_return([])
|
23
23
|
|
24
24
|
expect {
|
25
|
-
Gauntlt::Attack.new(:foo
|
26
|
-
}.to raise_error Gauntlt::Attack::
|
25
|
+
Gauntlt::Attack.new(:foo)
|
26
|
+
}.to raise_error Gauntlt::Attack::NoFilesFound
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
-
describe :base_dir do
|
32
|
-
it "returns the full path for the attack.rb file" do
|
33
|
-
File.should_receive(:dirname).and_return(:foo)
|
34
|
-
File.should_receive(:expand_path).with(:foo)
|
35
|
-
|
36
|
-
subject.base_dir
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
describe :attacks_dir do
|
41
|
-
it "joins attacks to base_dir" do
|
42
|
-
subject.should_receive(:base_dir).and_return(:bar)
|
43
|
-
File.should_receive(:join).with(:bar, 'attack_adapters')
|
44
|
-
|
45
|
-
subject.attacks_dir
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
31
|
describe :run do
|
50
32
|
it "executes the attack file, specifies failure for undefined steps and specifies the attacks_dir" do
|
51
|
-
subject.should_receive(:
|
52
|
-
subject.should_receive(:
|
53
|
-
|
54
|
-
mock_io = mock('io')
|
55
|
-
mock_io.stub(:string)
|
56
|
-
StringIO.stub(:new).and_return(mock_io)
|
33
|
+
subject.should_receive(:adapters_dir).and_return('/bar')
|
34
|
+
subject.should_receive(:attack_files).and_return(['/bar/baz.attack'])
|
57
35
|
|
58
36
|
mock_cli = mock(Cucumber::Cli::Main)
|
59
37
|
mock_cli.should_receive(:execute!)
|
60
|
-
Cucumber::Cli::Main.should_receive(:new).with(['/bar/baz.attack', '--strict', '--require', '/bar']
|
38
|
+
Cucumber::Cli::Main.should_receive(:new).with(['/bar/baz.attack', '--strict', '--require', '/bar']).and_return(mock_cli)
|
61
39
|
|
62
|
-
subject.run
|
40
|
+
subject.run.should be_true
|
63
41
|
end
|
64
42
|
|
65
43
|
it "returns nil if if Cucumber::Cli::Main.execute succeeds (i.e. returns nil)" do
|