phisher_phinder 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c8063750bef71fc06792e7c10440f99e979891612540b06abf4c66f2f368022d
4
- data.tar.gz: a94cd0e2438359bc626609216a91633deb589121ac92a2567c2a90625c0e30ae
3
+ metadata.gz: cfb338171a5bca59a9682dfe8157475e7ac1a6283d9410a8367fdbe78fed0be1
4
+ data.tar.gz: db30dccf14c2184cd01f4cb27e4d0cc305fda0ddb0b58205a6e3a077893e1eae
5
5
  SHA512:
6
- metadata.gz: 8cfc5618456ab2db9304232da36e6a5ae3e989edd1ba0993f5bd1f07feb27cfb4d1921052176630e002aa7847f0faee646f5f6690cdd650c34fd64ed843d0188
7
- data.tar.gz: bb5ef0c6f6f601a8c7d42ec2a7a8c8de976ccc9a86e438c56cedf0e4853908edb9efb3dfecfae515d300e0f2671cf321c3eaebe0105dad874a52e9be2fff7050
6
+ metadata.gz: 831b8c8a93dc8cf8105724960be8052c1aa47a16fb57336a683e9780b07ce89a72e2e081344826b5462582c1270ca3fcd8a359d8b5111f97c4925c37cd5c085e
7
+ data.tar.gz: c25e50d534e780eaeef883a8e6c4311c4c51176589ac80959adb880c0b9f190b5350dd752497f3b19eb8ede04c95bd527201cf18f5f96222890ba08893495dd9
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- phisher_phinder (0.2.0)
4
+ phisher_phinder (0.3.0)
5
5
  dotenv (~> 2.7.5)
6
6
  maxmind-geoip2 (~> 0.4.0)
7
7
  nokogiri (~> 1.11.0)
@@ -61,10 +61,8 @@ GEM
61
61
  http (~> 4.3)
62
62
  maxmind-db (~> 1.1)
63
63
  method_source (1.0.0)
64
- mini_portile2 (2.5.0)
65
64
  minitest (5.14.2)
66
- nokogiri (1.11.0)
67
- mini_portile2 (~> 2.5.0)
65
+ nokogiri (1.11.0-x86_64-linux)
68
66
  racc (~> 1.4)
69
67
  pry (0.13.1)
70
68
  coderay (~> 1.1)
data/README.md CHANGED
@@ -97,14 +97,14 @@ The output of PhisherPhinder will produce something similar to the below:
97
97
  +-----------+---------------+------------------+
98
98
 
99
99
 
100
- +---------------+------------------------------+------------------------------+--------------------------+
101
- | Trace |
102
- +---------------+------------------------------+------------------------------+--------------------------+
103
- | Sender IP | Sender Host | Advertised Sender | Recipient |
104
- +---------------+------------------------------+------------------------------+--------------------------+
105
- | 10.0.0.1 | host1.test.zzz | dodgyname.test.zzz | mx.google.com |
106
- | 10.0.0.2 | | othersdodgyname.text.zzz | host1.test.zzz |
107
- +---------------+------------------------------+------------------------------+--------------------------+
100
+ +--------------+----------------------+-----------------------+--------------------------------+-----------------------+-----------------------+
101
+ | Trace |
102
+ +--------------+----------------------+-----------------------+--------------------------------+-----------------------+-----------------------+
103
+ | Sender IP | IP Contacts | Sender Host | Host Contacts | Advertised Sender | Recipient |
104
+ +--------------+----------------------+-----------------------+--------------------------------+-----------------------+-----------------------+
105
+ | 10.0.0.1 | abuse@hosttwo.zzz | host.test.zzz | support@test.zzz | dodgy.test.zzz | mx.google.com |
106
+ | 10.0.0.2 | abuse@hostone.zzz | | | dodgy.othertest.zzz | mail.hostthree.zzz |
107
+ +--------------+----------------------+-----------------------+--------------------------------+-----------------------+-----------------------+
108
108
 
109
109
  ```
110
110
 
@@ -116,7 +116,8 @@ be trusted.
116
116
 
117
117
  The `Trace` secion shows a subset of the `Received` headers from the original (advertised, but not necessarily actual)
118
118
  origin (the last entry in the table) to the last external server to process the email before the recipient's mail host
119
- received the email.
119
+ received the email. The contacts are abuse contacts for the sender IP and the sender host as found in the results of a
120
+ WHOIS lookup. If this could not be resolved, these fields will be blank.
120
121
 
121
122
  ## Dependencies
122
123
  1. [Maxmind GeoIP2 User Account](https://dev.maxmind.com/geoip/geoip2/web-services/) - pay as you go
@@ -1,12 +1,15 @@
1
1
  require "phisher_phinder/version"
2
2
 
3
3
  require 'maxmind/geoip2'
4
+ require 'whois-parser'
4
5
 
5
6
  require_relative './phisher_phinder/command'
6
7
  require_relative './phisher_phinder/display'
7
8
 
8
9
  require_relative './phisher_phinder/body_hyperlink'
9
10
  require_relative './phisher_phinder/cached_geoip_client'
11
+ require_relative './phisher_phinder/contact_finder'
12
+ require_relative './phisher_phinder/whois_email_extractor'
10
13
  require_relative './phisher_phinder/null_lookup_client'
11
14
  require_relative './phisher_phinder/null_response'
12
15
  require_relative './phisher_phinder/geoip_ip_data'
@@ -13,7 +13,14 @@ module PhisherPhinder
13
13
  end
14
14
  ip_factory = PhisherPhinder::ExtendedIpFactory.new(geoip_client: lookup_client)
15
15
  mail_parser = PhisherPhinder::MailParser::Parser.new(ip_factory, line_ending)
16
- tracing_report = PhisherPhinder::TracingReport.new(mail_parser.parse(contents))
16
+ whois_client = Whois::Client.new
17
+ tracing_report = PhisherPhinder::TracingReport.new(
18
+ mail_parser.parse(contents),
19
+ PhisherPhinder::ContactFinder.new(
20
+ whois_client: whois_client,
21
+ extractor: PhisherPhinder::WhoisEmailExtractor.new
22
+ )
23
+ )
17
24
  tracing_report.report
18
25
  end
19
26
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PhisherPhinder
4
+ class ContactFinder
5
+ def initialize(whois_client:, extractor:)
6
+ @whois_client = whois_client
7
+ @extractor = extractor
8
+ end
9
+
10
+ def contacts_for(address)
11
+ whois_content = nil
12
+ begin
13
+ whois_content = case address
14
+ when ExtendedIp
15
+ @whois_client.lookup(address.ip_address.to_s).content
16
+ when String
17
+ hostname_parts = address.split('.')
18
+ whois_content = nil
19
+ until whois_content do
20
+ address = hostname_parts.join('.')
21
+ whois_record = @whois_client.lookup(address)
22
+ if whois_record.parser.available?
23
+ hostname_parts = hostname_parts[1..-1]
24
+ else
25
+ whois_content = whois_record.content
26
+ end
27
+ end
28
+ whois_content
29
+ end
30
+ rescue Whois::ServerNotFound
31
+ rescue Whois::AttributeNotImplemented
32
+ end
33
+
34
+ whois_content ? @extractor.abuse_contact_emails(whois_content) : []
35
+ end
36
+ end
37
+ end
@@ -32,14 +32,16 @@ module PhisherPhinder
32
32
  data = input_data[:tracing].map do |entry|
33
33
  [
34
34
  entry[:sender][:ip],
35
+ display_email_addresses(entry[:sender_contact_details][:ip][:email]),
35
36
  entry[:sender][:host],
37
+ display_email_addresses(entry[:sender_contact_details][:host][:email]),
36
38
  entry[:advertised_sender] || entry[:helo],
37
39
  entry[:recipient]
38
40
  ]
39
41
  end
40
42
 
41
43
  trace_table = Terminal::Table.new(
42
- headings: ['Sender IP', 'Sender Host', 'Advertised Sender', 'Recipient'],
44
+ headings: ['Sender IP', 'IP Contacts', 'Sender Host', 'Host Contacts', 'Advertised Sender', 'Recipient'],
43
45
  title: 'Trace',
44
46
  rows: data
45
47
  )
@@ -60,5 +62,9 @@ module PhisherPhinder
60
62
  output << [description, input_data[:origin][type].join(', ')]
61
63
  end
62
64
  end
65
+
66
+ def display_email_addresses(email_addresses)
67
+ email_addresses.map { |address| address.gsub(/[,<>]/, '') }.join(', ')
68
+ end
63
69
  end
64
70
  end
@@ -10,7 +10,7 @@ module PhisherPhinder
10
10
  end
11
11
 
12
12
  def ==(other)
13
- ip_address == other.ip_address && geoip_ip_data == other.geoip_ip_data
13
+ other.instance_of?(self.class) && ip_address == other.ip_address && geoip_ip_data == other.geoip_ip_data
14
14
  end
15
15
 
16
16
  def to_s
@@ -2,8 +2,9 @@
2
2
 
3
3
  module PhisherPhinder
4
4
  class TracingReport
5
- def initialize(mail)
5
+ def initialize(mail, contact_finder)
6
6
  @mail = mail
7
+ @contact_finder = contact_finder
7
8
  end
8
9
 
9
10
  def report
@@ -34,7 +35,14 @@ module PhisherPhinder
34
35
 
35
36
  def extract_tracing_headers(received_headers, latest_spf_entry)
36
37
  start = received_headers[:received].find_index { |h| h[:sender][:ip] == ip_address(latest_spf_entry) }
37
- received_headers[:received][start..-1]
38
+ received_headers[:received][start..-1].map do |h|
39
+ h.merge(
40
+ sender_contact_details: {
41
+ host: {email: @contact_finder.contacts_for(h[:sender][:host])},
42
+ ip: {email: @contact_finder.contacts_for(h[:sender][:ip])},
43
+ }
44
+ )
45
+ end
38
46
  end
39
47
 
40
48
  def extract_origin_headers(headers)
@@ -1,3 +1,3 @@
1
1
  module PhisherPhinder
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PhisherPhinder
4
+ class WhoisEmailExtractor
5
+ def abuse_contact_emails(contents)
6
+ if contents =~ /OrgAbuseEmail/
7
+ contents.scan(/OrgAbuseEmail:\s+(\S+)/).flatten.uniq
8
+ elsif contents =~ /Abuse contact for .+? is '([^']+)'/
9
+ [$1]
10
+ elsif contents =~ /Registrar Abuse Contact Email:\s+([\S]+)/
11
+ [$1]
12
+ elsif contents =~ /(abuse@[\S]+)/
13
+ [$1]
14
+ else
15
+ []
16
+ end
17
+ end
18
+ end
19
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: phisher_phinder
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rory McKinley
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-01-05 00:00:00.000000000 Z
11
+ date: 2021-01-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dotenv
@@ -249,6 +249,7 @@ files:
249
249
  - lib/phisher_phinder/body_hyperlink.rb
250
250
  - lib/phisher_phinder/cached_geoip_client.rb
251
251
  - lib/phisher_phinder/command.rb
252
+ - lib/phisher_phinder/contact_finder.rb
252
253
  - lib/phisher_phinder/display.rb
253
254
  - lib/phisher_phinder/expanded_data_processor.rb
254
255
  - lib/phisher_phinder/extended_ip.rb
@@ -276,6 +277,7 @@ files:
276
277
  - lib/phisher_phinder/simple_ip.rb
277
278
  - lib/phisher_phinder/tracing_report.rb
278
279
  - lib/phisher_phinder/version.rb
280
+ - lib/phisher_phinder/whois_email_extractor.rb
279
281
  - phisher_phinder.gemspec
280
282
  homepage: https://capefox.co
281
283
  licenses: