codesake_links 0.50

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ .rvmrc
2
+ *.swp
3
+ *.gem
4
+ .bundle
5
+ Gemfile.lock
6
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in w3ping.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010-2012 Paolo Perego
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # links
2
+
3
+ ## Introduction
4
+
5
+ [links](https://github.com/thesp0nge/links) is a tool for discovering a website
6
+ available pages without making too much noise.
7
+
8
+ The idea came to me during a penetration test since I had a bulk list of URLs
9
+ to check for availability and I wanted to automate this process.
10
+
11
+ ## Installing links
12
+
13
+ Installing links is easy. You can always obtain the latest stable code by using the following command:
14
+
15
+ ```
16
+ gem install links
17
+ ```
18
+
19
+ If you want to install a _pre_ release, such as a _release candidate_ you can do it this way:
20
+ ```
21
+ gem install links --pre
22
+ ```
23
+
24
+ ## Using links
25
+
26
+ After you installed links gem, you have the links command you can use this way:
27
+
28
+ ```
29
+ links http://www.some.org/somepage.html
30
+ ```
31
+
32
+ ## Contributing to links
33
+
34
+ * Check out the latest master to make sure the feature hasn't been implemented
35
+ or the bug hasn't been fixed yet
36
+ * Check out the issue tracker to make sure someone already hasn't requested it
37
+ and/or contributed it
38
+ * Fork the project
39
+ * Start a feature/bugfix branch
40
+ * Commit and push until you are happy with your contribution
41
+ * Make sure to add tests for it. This is important so I don't break it in a
42
+ future version unintentionally.
43
+ * Please try not to mess with the Rakefile, version, or history. If you want to
44
+ have your own version, or is otherwise necessary, that is fine, but please
45
+ isolate to its own commit so I can cherry-pick around it.
46
+
47
+ ## Copyright
48
+
49
+ Copyright (c) 2010-2013 Paolo Perego, <paolo@armoredcode.com>. See LICENSE for
50
+ further details.
51
+
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new
5
+
6
+ task :default => :spec
7
+ task :test => :spec
8
+
data/bin/links ADDED
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env ruby
2
+ require "rainbow"
3
+ require 'getoptlong'
4
+
5
+ require "codesake_commons"
6
+ require "codesake_links"
7
+
8
+ APPNAME = File.basename($0)
9
+
10
+ logger = Codesake::Commons::Logging.instance
11
+ opts = GetoptLong.new(
12
+ [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
13
+ [ '--version', '-v', GetoptLong::NO_ARGUMENT ],
14
+ [ '--bulk', '-b', GetoptLong::REQUIRED_ARGUMENT ],
15
+ [ '--proxy', '-P', GetoptLong::REQUIRED_ARGUMENT ],
16
+ [ '--code', '-c', GetoptLong::NO_ARGUMENT ],
17
+ [ '--robots', '-r', GetoptLong::NO_ARGUMENT ]
18
+ )
19
+ trap("INT") { logger.die("[INTERRUPTED]") }
20
+
21
+
22
+ list=[]
23
+ robots=false
24
+ bulk=false
25
+ show_code=false
26
+ proxy={:host=>nil, :port=>-1}
27
+
28
+ opts.each do |opt, arg|
29
+ case opt
30
+ when '--help'
31
+ puts "usage: links [-bvh] [filename]"
32
+ puts " -b filename: loads the url list from a plain text file"
33
+ puts " -r : parse robots.txt and make requests to disallowed urls"
34
+ puts " -c : shows the return code instead of human readable answer"
35
+ puts " -P host:port : connect using a proxy server. Useful in combination with Paros, Owasp Zap and other"
36
+ puts " -v : shows version information"
37
+ puts " -h : shows this help"
38
+ exit 0
39
+ when '--version'
40
+ puts "#{Codesake::Links::VERSION}"
41
+ exit 0
42
+ when '--code'
43
+ show_code = true
44
+ when '--proxy'
45
+ proxy[:host]=arg.split(':')[0]
46
+ proxy[:port]=arg.split(':')[1].to_i
47
+ when '--robots'
48
+ robots=true
49
+ when '--bulk'
50
+ bulk=true
51
+ if ! File.exists?(arg)
52
+ puts "links: file not found (#{arg})".color(:red)
53
+ exit 1
54
+ end
55
+ list = File.open(arg).readlines
56
+ if list.count <= 0
57
+ puts "links: invalid url list".color(:red)
58
+ exit 1
59
+ end
60
+ end
61
+ end
62
+
63
+ target = ARGV[0]
64
+ logger.helo "#{APPNAME} v#{Codesake::Links::VERSION} (C) 2013 - paolo@armoredcode.com is starting up"
65
+
66
+
67
+ list<<target if list.empty?
68
+
69
+ logger.die("links: missing target") if list[0].nil?
70
+
71
+ if robots
72
+ list = Codesake::Links::Api.robots(target)
73
+ logger.err "#{target}: no robots.txt found\n" if list.empty?
74
+ end
75
+
76
+ list.each do |l|
77
+ if robots or bulk
78
+ if ! l.start_with? '/'
79
+ l = '/'+l.chomp
80
+ end
81
+ if ! target.start_with? 'http://' and ! target.start_with? 'https://'
82
+ #defaulting to HTTP when no protocol has been supplied
83
+ target = "http://"+target
84
+ end
85
+
86
+ logger.log "#{target}#{l}:"
87
+ start = Time.now
88
+ code = Codesake::Links::Api.code(target+l, proxy)
89
+ stop = Time.now
90
+ else
91
+ logger.log "#{l}:"
92
+ start = Time.now
93
+ code = Codesake::Links::Api.code(l, proxy)
94
+ stop = Time.now
95
+ end
96
+
97
+ str=Codesake::Links::Api.human(code)
98
+
99
+ Codesake::Links::Utils.print_str(logger, str, start, stop) unless show_code
100
+ Codesake::Links::Utils.print_code(logger, str, code, start, stop) if show_code
101
+
102
+
103
+ if code == 301 or code == 302
104
+ start = Time.now
105
+ new_link = Codesake::Links::Api.follow(l, proxy)
106
+ stop = Time.now
107
+ logger.log "following from #{l} to #{new_link}\n"
108
+ str=Codesake::Links::Api.human(code)
109
+
110
+ Codesake::Links::Utils.print_str(logger, str, start, stop) unless show_code
111
+ Codesake::Links::Utils.print_code(logger, str, code, start, stop) if show_code
112
+
113
+ end
114
+
115
+ end
@@ -0,0 +1,33 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "codesake/links/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "codesake_links"
7
+ s.version = Codesake::Links::VERSION
8
+ s.authors = ["Paolo Perego"]
9
+ s.email = ["paolo@armoredcode.com"]
10
+ s.homepage = "http://codesake.com"
11
+ s.summary = %q{Fetch, discover and crawl what's available in a website.}
12
+ s.description = %q{During the first stage of a security test, it's useful to enumerate website urls without making too much noise. Links can help in this using robots.txt or link in a web page telling you the website contents.}
13
+ s.license = "BSD"
14
+
15
+ s.rubyforge_project = "links"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ # specify any dependencies here; for example:
23
+ s.add_development_dependency "rake"
24
+ s.add_development_dependency "rspec"
25
+ s.add_development_dependency "rainbow"
26
+ # s.add_runtime_dependency "rest-client"
27
+ s.add_runtime_dependency "rainbow"
28
+
29
+ s.add_dependency "nokogiri"
30
+ s.add_dependency "mechanize"
31
+
32
+ s.add_dependency "codesake_commons"
33
+ end
@@ -0,0 +1,130 @@
1
+ require "net/http"
2
+ require "nokogiri"
3
+
4
+ module Codesake
5
+
6
+ module Links
7
+ module Api
8
+
9
+ # include Links::Google
10
+
11
+ def self.get(url, proxy)
12
+ return Links::Api.request({:url=>url, :proxy=>proxy, :method=>:get})
13
+ end
14
+
15
+ def self.head(url, proxy)
16
+ return Links::Api.request({:url=>url, :proxy=>proxy, :method=>:head})
17
+ end
18
+
19
+ def self.code(url, proxy)
20
+ res = Links::Api.get(url, proxy)
21
+ (res.nil?)? -1 : res.code
22
+ end
23
+
24
+ def self.links(url, proxy)
25
+ res = Links::Api.get(url, proxy)
26
+ if res.nil?
27
+ return []
28
+ end
29
+ doc = Nokogiri::HTML.parse(res.body)
30
+ l = doc.css('a').map { |link| link['href'] }
31
+ l
32
+ end
33
+
34
+ # TESTING: SPIDERS, ROBOTS, AND CRAWLERS (OWASP-IG-001)
35
+ def self.robots(site, only_disallow=true)
36
+
37
+ if (! site.start_with? 'http://') and (! site.start_with? 'https://')
38
+ site = 'http://'+site
39
+ end
40
+
41
+ list = []
42
+ begin
43
+ res=Net::HTTP.get_response(URI(site+'/robots.txt'))
44
+ if (res.code != "200")
45
+ return []
46
+ end
47
+
48
+ res.body.split("\n").each do |line|
49
+ if only_disallow
50
+ if (line.downcase.start_with?('disallow'))
51
+ list << line.split(":")[1].strip.chomp
52
+ end
53
+ else
54
+ if (line.downcase.start_with?('allow') or line.downcase.start_with?('disallow'))
55
+ list << line.split(":")[1].strip.chomp
56
+ end
57
+ end
58
+ end
59
+ rescue
60
+ return []
61
+ end
62
+
63
+ list
64
+ end
65
+
66
+ def self.follow(url, proxy)
67
+ l = Links::Api.links(url)
68
+ l[0]
69
+ end
70
+
71
+ def self.human(code)
72
+ case code.to_i
73
+ when 200
74
+ return "Open"
75
+ when 301
76
+ return "Moved"
77
+ when 404
78
+ return "Non existent"
79
+ when 401
80
+ return "Closed"
81
+ when 403
82
+ return "Forbidden"
83
+ when -1
84
+ return "No answer"
85
+ else
86
+ return "Broken"
87
+ end
88
+ end
89
+
90
+ private
91
+
92
+ def self.request(options)
93
+ url = options[:url]
94
+ proxy = options[:proxy]
95
+ method = options[:method]
96
+
97
+ begin
98
+ uri = URI(url)
99
+ if uri.scheme == 'http'
100
+ Net::HTTP::Proxy(proxy[:host], proxy[:port]).start(uri.host) {|http|
101
+ if (method == :get)
102
+ res = http.get(uri.request_uri)
103
+ else
104
+ res = http.head(uri.request_uri)
105
+ end
106
+ return res
107
+ }
108
+ # res = Net::HTTP.get_response(URI(url))
109
+ else
110
+ request=Net::HTTP.new(uri.host, uri.port)
111
+ request.use_ssl=true
112
+ request.verify_mode = OpenSSL::SSL::VERIFY_NONE
113
+ if (method == :get)
114
+ res = request.get(uri.request_uri)
115
+ else
116
+ res = request.head(uri.request_uri)
117
+ end
118
+
119
+ end
120
+ return res
121
+ rescue
122
+ return nil
123
+ end
124
+
125
+ end
126
+
127
+
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,47 @@
1
+ require 'mechanize'
2
+ module Codesake
3
+
4
+ module Links
5
+
6
+ # Public: it implements check described into SEARCH ENGINE DISCOVERY/RECONNAISSANCE (OWASP-IG-002)
7
+ #
8
+ # The idea underneath is to use a search engine like google as tool to
9
+ # discovery entrypoints, web applications or whatever about the domain we
10
+ # want to test for.
11
+ #
12
+ # Please bear in mind that you **must be authorized** from the system owner
13
+ # before doing any sort of security test.
14
+ #
15
+ # Be ethical.
16
+ #
17
+ # Usage
18
+ # google = Links::Api::Google.search('somedomain.org')
19
+ # google.results.each |res| do
20
+ # puts "Discovered #{res}"
21
+ # end
22
+ module Google
23
+
24
+
25
+ attr_reader :results
26
+
27
+ def self.search(domain) do
28
+
29
+ a = Mechanize.new { |agent|
30
+ agent.user_agent_alias = 'Mac Safari'
31
+ }
32
+
33
+ a.get('http://google.com/') do |page|
34
+ search_result = page.form_with(:name => 'f') do |search|
35
+ search.q = 'Hello world'
36
+ end.submit
37
+
38
+ search_result.links.each do |link|
39
+ puts link.text
40
+ end
41
+ end
42
+ return []
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,26 @@
1
+ module Codesake
2
+
3
+ module Links
4
+ class Utils
5
+
6
+ def self.print_str(logger, str, start, stop)
7
+ logger.ok "#{str} (#{((stop-start) * 1000).round} msec)\n" if str == "Open"
8
+ logger.err " #{str} (#{((stop-start) * 1000).round} msec)\n" if (str == "Closed" or str == "Non existent")
9
+ logger.warn " #{str} (#{((stop-start) * 1000).round} msec)\n" if (str != "Closed" and str != "Non existent" and str != "Open")
10
+
11
+ return
12
+ end
13
+
14
+ def self.print_code(logger, str, code, start, stop)
15
+ logger.ok "#{code} (#{((stop-start) * 1000).round} msec)\n" if str == "Open"
16
+ logger.err " #{code} (#{((stop-start) * 1000).round} msec)\n" if (str == "Closed" or str == "Non existent")
17
+ logger.warn " #{code} (#{((stop-start) * 1000).round} msec)\n" if (str != "Closed" and str != "Non existent" and str != "Open")
18
+
19
+ return
20
+ end
21
+
22
+
23
+ end
24
+ end
25
+ end
26
+
@@ -0,0 +1,5 @@
1
+ module Codesake
2
+ module Links
3
+ VERSION = "0.50"
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ require "codesake/links/version"
2
+ require "codesake/links/utils"
3
+ require "codesake/links/api"
4
+
@@ -0,0 +1 @@
1
+ require 'links'
metadata ADDED
@@ -0,0 +1,181 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: codesake_links
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.50'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Paolo Perego
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-04-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rainbow
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rainbow
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: nokogiri
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: mechanize
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: codesake_commons
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ description: During the first stage of a security test, it's useful to enumerate website
127
+ urls without making too much noise. Links can help in this using robots.txt or link
128
+ in a web page telling you the website contents.
129
+ email:
130
+ - paolo@armoredcode.com
131
+ executables:
132
+ - links
133
+ extensions: []
134
+ extra_rdoc_files: []
135
+ files:
136
+ - .gitignore
137
+ - Gemfile
138
+ - LICENSE.txt
139
+ - README.md
140
+ - Rakefile
141
+ - bin/links
142
+ - codesake_links.gemspec
143
+ - lib/codesake/links/api.rb
144
+ - lib/codesake/links/google.rb
145
+ - lib/codesake/links/utils.rb
146
+ - lib/codesake/links/version.rb
147
+ - lib/codesake_links.rb
148
+ - spec/spec_helper.rb
149
+ homepage: http://codesake.com
150
+ licenses:
151
+ - BSD
152
+ post_install_message:
153
+ rdoc_options: []
154
+ require_paths:
155
+ - lib
156
+ required_ruby_version: !ruby/object:Gem::Requirement
157
+ none: false
158
+ requirements:
159
+ - - ! '>='
160
+ - !ruby/object:Gem::Version
161
+ version: '0'
162
+ segments:
163
+ - 0
164
+ hash: -1757904362167703742
165
+ required_rubygems_version: !ruby/object:Gem::Requirement
166
+ none: false
167
+ requirements:
168
+ - - ! '>='
169
+ - !ruby/object:Gem::Version
170
+ version: '0'
171
+ segments:
172
+ - 0
173
+ hash: -1757904362167703742
174
+ requirements: []
175
+ rubyforge_project: links
176
+ rubygems_version: 1.8.24
177
+ signing_key:
178
+ specification_version: 3
179
+ summary: Fetch, discover and crawl what's available in a website.
180
+ test_files:
181
+ - spec/spec_helper.rb