sekureco 0.0.1
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.
- checksums.yaml +7 -0
- data/lib/sekureco/html_page.rb +65 -0
- data/lib/sekureco/http_client.rb +61 -0
- data/lib/sekureco/web_crawler.rb +125 -0
- data/lib/sekureco.rb +9 -0
- metadata +47 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: df8b1826ede11dcfaebae255e3e86b6a137beb8b
|
|
4
|
+
data.tar.gz: 26874f5e0fb27f88b638d623aaf6c490a20debfc
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 904a29783ba52f8b40c0b87b2231f428d87ca70a1877319c90060dd554f2a6e0a950c1193080c02ac0d819ae4a7ed0f46edf1bdba47ad056dbbdc71d122e6e36
|
|
7
|
+
data.tar.gz: 7be203e0b10b76f2dca7bac37c47b5017b8e83be03ef61c15b727a1b80ec2f3f44576e3e8aa95a035215d8a076d4cfa4c220a145c722eeb72d2cd921c0826d16
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
require 'nokogiri'
|
|
2
|
+
require 'uri'
|
|
3
|
+
|
|
4
|
+
module Sekureco
|
|
5
|
+
|
|
6
|
+
class HtmlPage
|
|
7
|
+
def initialize html_doc
|
|
8
|
+
@html_doc = Nokogiri::HTML(html_doc)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def content
|
|
12
|
+
@html_doc.to_html
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def body
|
|
16
|
+
@html_doc.css("body").to_html
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def parsed_body black_list = []
|
|
20
|
+
html = self.body.dup
|
|
21
|
+
black_list.each { |word| html.gsub! word, "" }
|
|
22
|
+
html.gsub(/(name=\"authenticity_token\" value=\".*\"|authenticity_token=[^;]*;|\?([^=]+=[^\&\"]+)+)/, '')
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def links
|
|
26
|
+
@html_doc.css("a")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def scripts_with filter
|
|
30
|
+
@html_doc.css("script.#{filter}")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def forms
|
|
34
|
+
@html_doc.css("form")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def has_forms?
|
|
38
|
+
@html_doc.at_css("form")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def csrf_token
|
|
42
|
+
self.has_csrf_tokens? ? @html_doc.css('meta[name="csrf-token"]').first['content'] : ''
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def has_csrf_tokens?
|
|
46
|
+
!@html_doc.css('meta[name="csrf-token"]').empty?
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def to_s
|
|
50
|
+
self.parsed_body
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def log
|
|
54
|
+
2.times { puts }
|
|
55
|
+
if self.to_s.include? 'resizerElement'
|
|
56
|
+
puts "Error"
|
|
57
|
+
else
|
|
58
|
+
puts self
|
|
59
|
+
end
|
|
60
|
+
2.times { puts }
|
|
61
|
+
puts "=" * 80
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require 'net/http'
|
|
2
|
+
require 'uri'
|
|
3
|
+
|
|
4
|
+
module Sekureco
|
|
5
|
+
|
|
6
|
+
class HttpClient
|
|
7
|
+
|
|
8
|
+
include Net
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
@cookies = {}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def get uri
|
|
15
|
+
puts "GET #{uri.to_s}"
|
|
16
|
+
HTTP.start(uri.host, uri.port) do |http|
|
|
17
|
+
get_request = HTTP::Get.new uri
|
|
18
|
+
get_request['Cookie'] = cookies
|
|
19
|
+
@curr_response = http.request(get_request)
|
|
20
|
+
set_cookies
|
|
21
|
+
end
|
|
22
|
+
@curr_response
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def post uri, params = {}
|
|
26
|
+
puts "POST #{uri.to_s}"
|
|
27
|
+
puts "params: #{params}"
|
|
28
|
+
HTTP.start(uri.host, uri.port) do |http|
|
|
29
|
+
post_request = HTTP::Post.new uri
|
|
30
|
+
post_request['Cookie'] = cookies
|
|
31
|
+
post_request.set_form_data(params)
|
|
32
|
+
@curr_response = http.request(post_request)
|
|
33
|
+
set_cookies
|
|
34
|
+
end
|
|
35
|
+
@curr_response
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def set_cookies
|
|
41
|
+
unless @curr_response.nil?
|
|
42
|
+
new_cookies = @curr_response.get_fields('set-cookie')
|
|
43
|
+
unless new_cookies.nil?
|
|
44
|
+
new_cookies.each do |cookie|
|
|
45
|
+
key, value = cookie.split("; ").first.split("=")
|
|
46
|
+
@cookies[key] = value
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def cookies
|
|
53
|
+
cookies_field = []
|
|
54
|
+
@cookies.each do |k, v|
|
|
55
|
+
cookies_field << "#{k}=#{v}"
|
|
56
|
+
end
|
|
57
|
+
cookies_field.join("; ")
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
require_relative 'html_page'
|
|
2
|
+
require_relative 'http_client'
|
|
3
|
+
|
|
4
|
+
module Sekureco
|
|
5
|
+
|
|
6
|
+
class WebCrawler
|
|
7
|
+
|
|
8
|
+
def initialize url, port, username = nil, password = nil, app_token = 'foo'
|
|
9
|
+
@uri = URI "#{url}:#{port}/"
|
|
10
|
+
@http_client = HttpClient.new
|
|
11
|
+
@source_page = HtmlPage.new @http_client.get(@uri).body
|
|
12
|
+
@visited = {}
|
|
13
|
+
@default_username = username
|
|
14
|
+
@default_password = password
|
|
15
|
+
@black_list = []
|
|
16
|
+
@app_token = app_token
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def crawl
|
|
20
|
+
@queue = [@source_page]
|
|
21
|
+
mark_as_visited @source_page
|
|
22
|
+
@distance = { @source_page => 0 }
|
|
23
|
+
until @queue.empty?
|
|
24
|
+
@current_page = @queue.shift
|
|
25
|
+
@current_page.log
|
|
26
|
+
unless too_deep? @current_page
|
|
27
|
+
test_forms_of @current_page if @current_page.has_forms?
|
|
28
|
+
@current_page.links.each do |current_link|
|
|
29
|
+
next_link = URI.join(@uri.to_s, URI::encode(parse(current_link)))
|
|
30
|
+
next if next_link.path.include? 'logout'
|
|
31
|
+
response = if current_link["data-method"]
|
|
32
|
+
@http_client.post(next_link, { '_method' => current_link['data-method'],
|
|
33
|
+
'authenticity_token' => @current_page.csrf_token })
|
|
34
|
+
else
|
|
35
|
+
@http_client.get(next_link)
|
|
36
|
+
end
|
|
37
|
+
next_page = HtmlPage.new(response.body)
|
|
38
|
+
unless already_visited?(next_page)
|
|
39
|
+
next_page.log
|
|
40
|
+
mark_as_visited next_page
|
|
41
|
+
update_distance next_page
|
|
42
|
+
@queue << next_page
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
@vulnerabilities = true if detect_embedded_scripts_in(@current_page)
|
|
47
|
+
puts "funcionou" if @vulnerabilities
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def test_forms_of html_page
|
|
52
|
+
html_page.forms.each do |form|
|
|
53
|
+
params = {}
|
|
54
|
+
form.css("input, textarea").each do |input|
|
|
55
|
+
field_name = input['name']
|
|
56
|
+
next if field_name.nil?
|
|
57
|
+
if field_name.include? "password"
|
|
58
|
+
params[field_name] = @default_password || random_string
|
|
59
|
+
elsif possible_login_fields.any? { |s| field_name.include? s }
|
|
60
|
+
params[field_name] = @default_username || random_string
|
|
61
|
+
else
|
|
62
|
+
params[field_name] = input['value'] || input['content'] || xss_attack
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
response = @http_client.post(URI.join(@uri.to_s, form['action']), params)
|
|
66
|
+
next_page = HtmlPage.new(response.body)
|
|
67
|
+
unless already_visited?(next_page)
|
|
68
|
+
next_page.log
|
|
69
|
+
mark_as_visited next_page
|
|
70
|
+
update_distance next_page
|
|
71
|
+
@queue << next_page
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def test_application
|
|
77
|
+
2.times { self.crawl }
|
|
78
|
+
@vulnerabilities
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
def mark_as_visited page
|
|
84
|
+
@visited[page.parsed_body] = true
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def already_visited? page
|
|
88
|
+
@visited[page.parsed_body]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def update_distance page
|
|
92
|
+
@distance[page] = @distance[@current_page] + 1
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def too_deep? page
|
|
96
|
+
@distance[page] >= 2
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def possible_login_fields
|
|
100
|
+
%w(username user name email login)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def random_string
|
|
104
|
+
@random_string ||= (0..(1 + rand(16))).map { ('a'..'z').to_a[rand(26)] }.join
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def xss_attack
|
|
108
|
+
"<script class='#{@app_token}'>alert('It worked!');</script>"
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def detect_embedded_scripts_in html_page
|
|
112
|
+
html_page.scripts_with(@app_token).any?
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def parse link
|
|
116
|
+
uri = URI::parse(link["href"])
|
|
117
|
+
if uri.query
|
|
118
|
+
uri.path + "?" + uri.query
|
|
119
|
+
else
|
|
120
|
+
uri.path
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
end
|
data/lib/sekureco.rb
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
require_relative 'sekureco/web_crawler'
|
|
2
|
+
|
|
3
|
+
default_email = "joao-bonfim"
|
|
4
|
+
default_password = "test"
|
|
5
|
+
website = "http://localhost"
|
|
6
|
+
puts "Crawling #{website}"
|
|
7
|
+
2.times { puts }
|
|
8
|
+
wc = Sekureco::WebCrawler.new(website, 3000, default_email, default_password)
|
|
9
|
+
puts wc.test_application
|
metadata
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: sekureco
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Pedro de Lyra
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2017-10-17 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: A tool to perform penetration tests on web applications
|
|
14
|
+
email: pedrodelyra@gmail.com
|
|
15
|
+
executables: []
|
|
16
|
+
extensions: []
|
|
17
|
+
extra_rdoc_files: []
|
|
18
|
+
files:
|
|
19
|
+
- lib/sekureco.rb
|
|
20
|
+
- lib/sekureco/html_page.rb
|
|
21
|
+
- lib/sekureco/http_client.rb
|
|
22
|
+
- lib/sekureco/web_crawler.rb
|
|
23
|
+
homepage: http://rubygems.org/gems/sekureco
|
|
24
|
+
licenses:
|
|
25
|
+
- MIT
|
|
26
|
+
metadata: {}
|
|
27
|
+
post_install_message:
|
|
28
|
+
rdoc_options: []
|
|
29
|
+
require_paths:
|
|
30
|
+
- lib
|
|
31
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
32
|
+
requirements:
|
|
33
|
+
- - ">="
|
|
34
|
+
- !ruby/object:Gem::Version
|
|
35
|
+
version: '0'
|
|
36
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
requirements: []
|
|
42
|
+
rubyforge_project:
|
|
43
|
+
rubygems_version: 2.6.11
|
|
44
|
+
signing_key:
|
|
45
|
+
specification_version: 4
|
|
46
|
+
summary: Pentest automation tool
|
|
47
|
+
test_files: []
|