codesake 0.0.1 → 0.15.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ require 'aruba/cucumber'
@@ -1,5 +1,10 @@
1
1
  require "codesake/version"
2
+ require "codesake/cli"
3
+ require "codesake/kernel"
4
+ require "codesake/utils/files"
5
+ require "codesake/utils/secrets"
6
+ require "codesake/engine/text"
7
+ require "codesake/engine/generic"
8
+ require "codesake/engine/core"
9
+ require "codesake/engine/jsp"
2
10
 
3
- module Codesake
4
- # Your code goes here...
5
- end
@@ -0,0 +1,90 @@
1
+ require 'optparse'
2
+ require 'trimmy'
3
+
4
+ module Codesake
5
+ class Cli
6
+ attr_reader :options
7
+ attr_reader :targets
8
+
9
+ def parse(command_line)
10
+ @options = {}
11
+
12
+ return {:vulnerabilities=>:all} if (command_line.nil?) or (command_line.send(:empty?))
13
+
14
+ begin
15
+ option_parser =OptionParser.new do |opts|
16
+ executable_name = File.basename($PROGRAM_NAME)
17
+ opts.banner =
18
+ "codesake v#{Codesake::VERSION} - (C) 2012 - paolo@armoredcode.com\nReviews one or more source file for security issues.\n\nUsage #{executable_name} [options] sources\n"
19
+ # opts.on("-h", "--help") do
20
+ # @options[:help] = true
21
+ # end
22
+
23
+ opts.on("-v", "--version", "Show codesake version") do
24
+ @options[:version] = true
25
+ end
26
+
27
+ opts.on("-V", "--verbose", "Be verbose") do
28
+ @options[:verbose] = true
29
+ end
30
+
31
+ opts.on("-k KEYWORDS", "--add-keys", "Add the command separated list of strings as reserved keywords") do |val|
32
+ @options[:keywords] = val.trim.split(",")
33
+ end
34
+
35
+ opts.on("-o TARGET", "--output", "Write output to file, to json string or to db usin SQLite3") do |val|
36
+ @options[:output]=:screen
37
+ val=val.trim
38
+ @options[:output]=val.to_sym if (val.to_sym == :file) or (val.to_sym == :json) or (val.to_sym == :db)
39
+ end
40
+ opts.on("-C", "--confirmed-vulnerabilities", "Show only confirmed vulnerabilities") do
41
+ @options[:vulnerabilities] = :confirmed
42
+ end
43
+ opts.on("-A", "--all-vulnerabilities", "Show all vulnerabilities found [default]") do
44
+ @options[:vulnerabilities] = :all
45
+ end
46
+
47
+ end
48
+
49
+ rest = option_parser.parse(command_line)
50
+
51
+ @targets = []
52
+ @targets = build_target_list(rest[0].split(" ")) if expect_targets? and (! rest.empty?) and (! rest[0].nil?)
53
+ @options[:vulnerabilities] = :all if @options[:vulnerabilities].nil?
54
+ rescue OptionParser::InvalidOption => e
55
+ @options={:error=>true, :message=>e.message}
56
+ end
57
+ @options
58
+ end
59
+
60
+
61
+ def is_good_target?(target)
62
+ (!Dir.glob(target).empty?) or File.exists?(target) or File.directory?(target)
63
+ end
64
+
65
+ def has_errors?
66
+ (@options[:error])
67
+ end
68
+
69
+ def error_message
70
+ @options[:message] if has_errors?
71
+ end
72
+
73
+
74
+ private
75
+ def expect_targets?
76
+ (! @options[:help] ) and ( ! @options[:version])
77
+ end
78
+
79
+
80
+ def build_target_list(target_list)
81
+ ret = []
82
+ target_list.each do |target|
83
+ ret << {:target=>target, :valid=>is_good_target?(target)}
84
+ end
85
+
86
+ ret
87
+ end
88
+
89
+ end
90
+ end
@@ -0,0 +1,10 @@
1
+ module Codesake
2
+ module Engine
3
+ module Core
4
+ def analyse
5
+ []
6
+ end
7
+
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ module Codesake
2
+ module Engine
3
+ class Generic
4
+ include Codesake::Utils::Files
5
+
6
+ def initialize(filename)
7
+ @filename = filename
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,165 @@
1
+ require 'codesake/engine/core'
2
+
3
+ module Codesake
4
+ module Engine
5
+ class Jsp
6
+ include Codesake::Utils::Files
7
+ include Codesake::Utils::Secrets
8
+ include Codesake::Engine::Core
9
+
10
+ FALSE_POSITIVES = ["request.getContextPath()", "request.getLocalName()", "request.getLocalPort()"]
11
+
12
+
13
+ attr_reader :imports
14
+ attr_reader :attack_entrypoints
15
+ attr_reader :reflected_xss
16
+ attr_reader :cookies
17
+
18
+ def initialize(filename, options)
19
+ @filename = filename
20
+ @options = options
21
+
22
+ read_file
23
+ load_secrets
24
+ end
25
+
26
+ def analyse
27
+ ret = []
28
+ @reserved_keywords = find_reserved_keywords
29
+ @imports = find_imports
30
+ @attack_entrypoints = find_attack_entrypoints
31
+ @reflected_xss = find_reflected_xss
32
+ @cookies = find_cookies
33
+
34
+ @reserved_keywords.each do |secret|
35
+ ret << "reserved keyword found: \"#{secret[:matcher]}\" (#{@filename}@#{secret[:line]})"
36
+ end
37
+ @imports.each do |import|
38
+ ret << "imported package found: \"#{import[:package]}\""
39
+ end
40
+
41
+ @attack_entrypoints.each do |entry|
42
+ ret << "attack entrypoint found: parameter \"#{entry[:param]}\" stored in \"#{entry[:var]}\" (#{@filename}@#{entry[:line]})"
43
+ end
44
+ @reflected_xss.each do |entry|
45
+ ret << "suspicious reflected xss found: \"#{entry[:var]}\" (#{@filename}@#{entry[:line]})\"" if entry[:false_positive] and @options[:vulnerabilities] == :all
46
+ ret << "reflected xss found: \"#{entry[:var]}\" (#{@filename}@#{entry[:line]})\"" if ! entry[:false_positive]
47
+ end
48
+
49
+ @cookies.each do |c|
50
+ ret << "cookie \"#{c[:name]}\" found with value: \"#{c[:value]}\" (#{@filename}@#{c[:line]})"
51
+ end
52
+
53
+
54
+ ret
55
+ end
56
+
57
+
58
+
59
+ private
60
+ # redefined
61
+ def find_reserved_keywords
62
+
63
+ ret = []
64
+
65
+ @file_content.each_with_index do |l, i|
66
+ l = l.unpack("C*").pack("U*")
67
+ @secrets.each do |s|
68
+ ret << {:line=> i+1, :matcher=>s } if l.trim.include?(s)
69
+ end
70
+ end
71
+
72
+ ret
73
+ end
74
+
75
+ def find_cookies
76
+ ret = []
77
+
78
+ @file_content.each_with_index do |l, i|
79
+ l = l.unpack("C*").pack("U*")
80
+ m = /Cookie (.*?) = new Cookie \("(.*?)",(.*?)\)/.match(l);
81
+ ret << {:line => i+1, :var => m[1].trim, :name => m[2].trim.gsub("\"", ""), :value => m[3].trim.gsub("\"", "")} unless m.nil?
82
+
83
+ m = /Cookie (.*?) = new Cookie\("(.*?)",(.*?)\)/.match(l);
84
+ ret << {:line => i+1, :var => m[1].trim, :name => m[2].trim.gsub("\"", ""), :value => m[3].trim.gsub("\"", "")} unless m.nil?
85
+
86
+
87
+ m = /(.*?) = new Cookie \("(.*?)",(.*?)\)/.match(l);
88
+ ret << {:line => i+1, :var => m[1].trim, :name => m[2].trim.gsub("\"", ""), :value => m[3].trim.gsub("\"", "")} unless m.nil?
89
+
90
+
91
+ end
92
+ ret
93
+ end
94
+
95
+
96
+
97
+ def find_reflected_xss
98
+ ret = []
99
+ @file_content.each_with_index do |l, i|
100
+ # <%=avar%> #=> /<%=(\w+)%>/.match(a)[1] = avar
101
+ l = l.unpack("C*").pack("U*")
102
+ m = /<%=(.*?)%>/.match(l)
103
+ ret << {:line => i+1, :var=> m[1].trim, :false_positive=>Codesake::Engine::Jsp.is_false_positive?(m[1].trim)} unless m.nil?
104
+
105
+ m = /out\.println\((.*?)\)/.match(l)
106
+ ret << {:line => i+1, :var=> m[1].trim, :false_positive=>Codesake::Engine::Jsp.is_false_positive?(m[1].trim)} unless m.nil?
107
+
108
+ m = /out\.print\((.*?)\)/.match(l)
109
+ ret << {:line => i+1, :var=> m[1].trim, :false_positive=>Codesake::Engine::Jsp.is_false_positive?(m[1].trim)} unless m.nil?
110
+
111
+ m = /out\.write\((.*?)\)/.match(l)
112
+ ret << {:line => i+1, :var=> m[1].trim, :false_positive=>Codesake::Engine::Jsp.is_false_positive?(m[1].trim)} unless m.nil?
113
+
114
+ m = /out\.writeln\((.*?)\)/.match(l)
115
+ ret << {:line => i+1, :var=> m[1].trim, :false_positive=>Codesake::Engine::Jsp.is_false_positive?(m[1].trim)} unless m.nil?
116
+ end
117
+
118
+ ret
119
+ end
120
+
121
+ def self.is_false_positive?(var)
122
+ FALSE_POSITIVES.include?(var)
123
+ end
124
+
125
+
126
+ def find_attack_entrypoints
127
+ ret = []
128
+ @file_content.each_with_index do |l, i|
129
+ l = l.unpack("C*").pack("U*")
130
+ m = /request.getParameter\((.*?)\)/.match(l)
131
+ ret << {:line => i+1, :param => m[1].trim.gsub("\"", ""), :var => variable_from_line(l) } unless m.nil?
132
+
133
+ m = /request.getParameterValues\((.*?)\)/.match(l)
134
+ ret << {:line => i+1, :param => m[1].trim.gsub("\"", ""), :var => variable_from_line(l) } unless m.nil?
135
+
136
+ m = /request.getAttribute\((.*?)\)/.match(l)
137
+ ret << {:line => i+1, :param => m[1].trim.gsub("\"", ""), :var => variable_from_line(l) } unless m.nil?
138
+
139
+ end
140
+
141
+ ret
142
+ end
143
+ def variable_from_line(line)
144
+ ret = ""
145
+ left_operand = line.split('=')[0]
146
+ l = left_operand.split
147
+ l[l.size - 1]
148
+ end
149
+
150
+
151
+
152
+ def find_imports
153
+ ret = []
154
+ @file_content.each_with_index do |l, i|
155
+ l = l.unpack("C*").pack("U*")
156
+ m = /<%@page import="(.*?)"%>/.match(l)
157
+ ret << {:line => i+1, :package=>m[1].trim} unless m.nil?
158
+ end
159
+
160
+ ret
161
+ end
162
+
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,36 @@
1
+ require 'codesake/engine/core'
2
+
3
+ module Codesake
4
+ module Engine
5
+ class Text
6
+ include Codesake::Utils::Files
7
+ include Codesake::Utils::Secrets
8
+ include Codesake::Engine::Core
9
+
10
+ attr_reader :reserved_keywords
11
+
12
+ def initialize(filename)
13
+ @filename = filename
14
+ @raw_results = nil
15
+
16
+ read_file
17
+ load_secrets
18
+ end
19
+
20
+ def analyse
21
+ ret = []
22
+ @reserved_keywords = find_reserved_keywords
23
+ @reserved_keywords.each do |secret|
24
+ ret << "reserved keyword found: \"#{secret[:matcher]}\" (#{@filename}@#{secret[:line]})"
25
+ end
26
+
27
+ ret
28
+ end
29
+
30
+ def self.is_txt?(filename)
31
+ (File.extname(filename).empty? or File.extname(filename) == ".txt" or File.extname(filename) == ".conf" or File.extname(filename) == ".rc" or File.extname(filename) == ".bak" or File.extname(filename) == ".old" )
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,39 @@
1
+ require 'singleton'
2
+
3
+ module Codesake
4
+ class Kernel
5
+ include Singleton
6
+
7
+ attr_reader :engine
8
+
9
+ NONE = 0
10
+ TEXT = 1
11
+ JSP = 2
12
+ UNKNOWN = -1
13
+
14
+ def choose_engine(filename, options)
15
+
16
+
17
+ engine = nil
18
+
19
+ case detect(filename)
20
+ when TEXT
21
+ engine = Codesake::Engine::Text.new(filename)
22
+ when NONE
23
+ engine = Codesake::Engine::Generic.new(filename)
24
+ when JSP
25
+ engine = Codesake::Engine::Jsp.new(filename, options)
26
+ end
27
+ engine
28
+ end
29
+
30
+ def detect(filename)
31
+ return NONE if filename.nil? or filename.empty?
32
+ return TEXT if Codesake::Engine::Text.is_txt?(filename)
33
+ return JSP if (File.extname(filename) == ".jsp")
34
+
35
+
36
+ return UNKNOWN
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,25 @@
1
+ module Codesake
2
+ module Utils
3
+ module Files
4
+
5
+ attr_accessor :filename
6
+ attr_reader :file_content
7
+
8
+ def read_file
9
+ @file_content = []
10
+ @file_content = File.readlines(@filename) if File.exists?(@filename)
11
+ end
12
+
13
+ def lines
14
+ @file_content.count
15
+ end
16
+
17
+ def lines_of_comment
18
+ 0
19
+ end
20
+ def loc
21
+ 0
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,45 @@
1
+ module Codesake
2
+ module Utils
3
+ module Secrets
4
+ DEFAULT_SECRETS = ["secret", "password", "username", "login", "xxx", "fixme", "fix", "todo", "passwd"]
5
+ attr_accessor :secrets
6
+ attr_accessor :reserved_keywords
7
+
8
+ def initialize
9
+ load_secrets
10
+ end
11
+
12
+ def load_secrets
13
+ @secrets = DEFAULT_SECRETS
14
+ end
15
+
16
+ def add(word)
17
+ @secrets << word
18
+ @secrets
19
+ end
20
+
21
+ def reserved?(word)
22
+ @secrets.include?(word)
23
+ end
24
+
25
+ def find_reserved_keywords
26
+
27
+ ret = []
28
+
29
+ @file_content.each_with_index do |l, i|
30
+ l = l.unpack("C*").pack("U*")
31
+ l.split.each do |tok|
32
+ # ret << {:line=> i+1, :matcher=>tok, :source_line=>l} if @secrets.include?(tok.downcase)
33
+ ret << {:line=> i+1, :matcher=>tok } if @secrets.include?(tok.downcase)
34
+
35
+ end
36
+ end
37
+
38
+ ret
39
+ end
40
+
41
+
42
+
43
+ end
44
+ end
45
+ end