lib/ronin/recon/builtin/api/crt_sh.rb in ronin-recon-0.1.0.rc2 vs lib/ronin/recon/builtin/api/crt_sh.rb in ronin-recon-0.1.0
- old
+ new
@@ -17,12 +17,14 @@
# You should have received a copy of the GNU Lesser General Public License
# along with ronin-recon. If not, see <https://www.gnu.org/licenses/>.
#
require 'ronin/recon/worker'
+require 'ronin/support/text/patterns/network'
require 'async/http/internet/instance'
+require 'set'
module Ronin
module Recon
module API
#
@@ -31,14 +33,15 @@
#
class CrtSh < Worker
register 'api/crt_sh'
- summary 'Queries https://crt.sh and returns host from each domains certificate.'
+ summary 'Queries https://crt.sh'
description <<~DESC
- Queries https://crt.sh and returns host from each domains certificate.
+ Queries https://crt.sh and returns the host names from each valid
+ certificate for the domain.
DESC
accepts Domain
outputs Host
intensity :passive
@@ -65,11 +68,16 @@
@client = Async::HTTP::Client.new(
Async::HTTP::Endpoint.for('https','crt.sh')
)
end
+ # Regular expression to verify valid host names.
#
+ # @api private
+ HOST_NAME_REGEX = /\A#{Support::Text::Patterns::HOST_NAME}\z/
+
+ #
# Returns host from each domains certificate.
#
# @param [Values::Domain] domain
# The domain value to check.
#
@@ -79,18 +87,21 @@
#
# @yieldparam [Values::Host] host
# The host from certificate.
#
def process(domain)
- Async do
- path = "/?dNSName=#{domain}&exclude=expired&output=json"
- response = @client.get(path)
- certs = JSON.parse(response.read, symbolize_names: true)
+ path = "/?dNSName=#{domain}&exclude=expired&output=json"
+ response = @client.get(path)
+ certs = JSON.parse(response.read, symbolize_names: true)
+ hostnames = Set.new
- certs.each do |cert|
- if (common_name = cert[:common_name])
- yield Host.new(common_name)
- end
+ certs.each do |cert|
+ common_name = cert[:common_name]
+
+ if common_name &&
+ common_name =~ HOST_NAME_REGEX &&
+ hostnames.add?(common_name)
+ yield Host.new(common_name)
end
end
end
end