gauntlt 0.0.7 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- 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
|