wordstress 0.0.1 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/README.md +1 -0
- data/bin/wordstress +51 -0
- data/lib/wordstress/site.rb +91 -0
- data/lib/wordstress/version.rb +1 -1
- data/lib/wordstress.rb +1 -4
- data/wordstress.gemspec +3 -0
- metadata +33 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 95999506c82f7c592ba1519d3135c0e374bb7d7d
|
4
|
+
data.tar.gz: d98e8756e937c3ff926036ea34ab8111be6b030d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d0e4d4519077f8a3beb10db5733d4ac98e68cc74a0eae589c4ae69fad7767233013f573d9ffdefeed2bd0b46b8a45d683aed1c5dc4b834ac6403702908e80d13
|
7
|
+
data.tar.gz: 189f863ab0ced45cef88ca6c87eed227d59d376db85a57d158cc88e2f428a774a7dc9040536169e687d1e64c622a8c787d69ef5069643911cb462b39f22c88c4
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -30,6 +30,7 @@ I want a security tool that follows 'the ruby way'.
|
|
30
30
|
* SQL and CSV output. Suitable for script integration
|
31
31
|
* Massive websites scan from text file
|
32
32
|
* SSL server rating using [Qualys SSL Labs rating guide](https://www.ssllabs.com/projects/rating-guide/)
|
33
|
+
* Whitebox testing using existing wordpress user
|
33
34
|
|
34
35
|
|
35
36
|
## Installation
|
data/bin/wordstress
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'getoptlong'
|
4
|
+
require 'json'
|
5
|
+
require 'codesake-commons'
|
6
|
+
|
7
|
+
require 'wordstress'
|
8
|
+
|
9
|
+
APPNAME = File.basename($0)
|
10
|
+
|
11
|
+
$logger = Codesake::Commons::Logging.instance
|
12
|
+
|
13
|
+
opts = GetoptLong.new(
|
14
|
+
[ '--version', '-v', GetoptLong::NO_ARGUMENT],
|
15
|
+
[ '--help', '-h', GetoptLong::NO_ARGUMENT]
|
16
|
+
)
|
17
|
+
|
18
|
+
opts.quiet=true
|
19
|
+
|
20
|
+
begin
|
21
|
+
opts.each do |opt, val|
|
22
|
+
case opt
|
23
|
+
when '--version'
|
24
|
+
puts "#{Wordstress::VERSION}"
|
25
|
+
Kernel.exit(0)
|
26
|
+
when '--help'
|
27
|
+
Kernel.exit(0)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
rescue GetoptLong::InvalidOption => e
|
31
|
+
$logger.helo APPNAME, Wordstress::VERSION
|
32
|
+
$logger.err e.message
|
33
|
+
Kernel.exit(-1)
|
34
|
+
end
|
35
|
+
|
36
|
+
target=ARGV.shift
|
37
|
+
$logger.helo APPNAME, Wordstress::VERSION
|
38
|
+
$logger.toggle_syslog
|
39
|
+
|
40
|
+
trap("INT") { $logger.die('[INTERRUPTED]') }
|
41
|
+
$logger.die("missing target") if target.nil?
|
42
|
+
|
43
|
+
$logger.log "scanning #{target}"
|
44
|
+
site = Wordstress::Site.new(target)
|
45
|
+
$logger.ok "wordpress version #{site.version[:version]} detected"
|
46
|
+
wp_vuln_hash = JSON.parse(site.wp_vuln_json)
|
47
|
+
$logger.ok "#{wp_vuln_hash["wordpress"]["vulnerabilities"].size} vulnerabilities found due wordpress version"
|
48
|
+
wp_vuln_hash["wordpress"]["vulnerabilities"].each do |v|
|
49
|
+
$logger.log "#{v["id"]} - #{v["title"]}"
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
module Wordstress
|
4
|
+
class Site
|
5
|
+
|
6
|
+
attr_reader :version, :wp_vuln_json
|
7
|
+
def initialize(name="")
|
8
|
+
begin
|
9
|
+
@uri = URI(name)
|
10
|
+
@raw_name = name
|
11
|
+
@valid = true
|
12
|
+
rescue
|
13
|
+
@valid = false
|
14
|
+
end
|
15
|
+
|
16
|
+
@robots_txt = get(@raw_name + "/robots.txt")
|
17
|
+
@readme_html = get(@raw_name + "/readme.html")
|
18
|
+
@homepage = get(@raw_name)
|
19
|
+
@version = detect_version
|
20
|
+
|
21
|
+
@wp_vuln_json = get_wp_vulnerabilities
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_wp_vulnerabilities
|
26
|
+
get_https("https://wpvulndb.com/api/v1/wordpresses/#{version_pad(@version[:version])}").body
|
27
|
+
end
|
28
|
+
|
29
|
+
def version_pad(version)
|
30
|
+
# 3.2.1 => 321
|
31
|
+
# 4.0 => 400
|
32
|
+
return version.gsub('.', '') if version.split('.').count == 3
|
33
|
+
return version.gsub('.', '')+'0' if version.split('.').count == 2
|
34
|
+
end
|
35
|
+
|
36
|
+
def detect_version
|
37
|
+
|
38
|
+
#
|
39
|
+
# 1. trying to detect wordpress version from homepage body meta generator
|
40
|
+
# tag
|
41
|
+
|
42
|
+
v_meta = ""
|
43
|
+
doc = Nokogiri::HTML(@homepage.body)
|
44
|
+
doc.xpath("//meta[@name='generator']/@content").each do |attr|
|
45
|
+
v_meta = attr.value.split(' ')[1]
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# 2. trying to detect wordpress version from readme.html in the root
|
50
|
+
# directory
|
51
|
+
|
52
|
+
v_readme = ""
|
53
|
+
doc = Nokogiri::HTML(@readme_html.body)
|
54
|
+
v_readme = doc.at_css('h1').children.last.text.chop.lstrip.split(' ')[1]
|
55
|
+
|
56
|
+
v_rss = ""
|
57
|
+
rss_doc = Nokogiri::HTML(@homepage.body)
|
58
|
+
rss = Nokogiri::HTML(get(rss_doc.css('link[type="application/rss+xml"]').first.attr('href')).body)
|
59
|
+
|
60
|
+
v_rss= rss.css('generator').text.split('=')[1]
|
61
|
+
|
62
|
+
return {:version => v_meta, :accuracy => 1.0} if v_meta == v_readme && v_meta == v_rss
|
63
|
+
return {:version => v_meta, :accuracy => 0.8} if v_meta == v_readme || v_meta == v_rss
|
64
|
+
end
|
65
|
+
|
66
|
+
def get(page)
|
67
|
+
return get_http(page) if @uri.scheme == "http"
|
68
|
+
return get_https(page) if @uri.scheme == "https"
|
69
|
+
end
|
70
|
+
|
71
|
+
def is_valid?
|
72
|
+
return @valid
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
def get_http(page)
|
77
|
+
uri = URI.parse(page)
|
78
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
79
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
80
|
+
return http.request(request)
|
81
|
+
end
|
82
|
+
def get_https(page)
|
83
|
+
uri = URI.parse(page)
|
84
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
85
|
+
http.use_ssl = true
|
86
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
87
|
+
return http.request(request)
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/lib/wordstress/version.rb
CHANGED
data/lib/wordstress.rb
CHANGED
data/wordstress.gemspec
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wordstress
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paolo Perego
|
@@ -38,10 +38,39 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: codesake-commons
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: json
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
41
69
|
description: wordstress is a security scanner for wordpress powered websites
|
42
70
|
email:
|
43
71
|
- thesp0nge@gmail.com
|
44
|
-
executables:
|
72
|
+
executables:
|
73
|
+
- wordstress
|
45
74
|
extensions: []
|
46
75
|
extra_rdoc_files: []
|
47
76
|
files:
|
@@ -50,7 +79,9 @@ files:
|
|
50
79
|
- LICENSE.txt
|
51
80
|
- README.md
|
52
81
|
- Rakefile
|
82
|
+
- bin/wordstress
|
53
83
|
- lib/wordstress.rb
|
84
|
+
- lib/wordstress/site.rb
|
54
85
|
- lib/wordstress/version.rb
|
55
86
|
- wordstress.gemspec
|
56
87
|
homepage: https://github.com/thesp0nge/wordstress
|