codesake_links 0.50

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 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