nesser 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.
@@ -0,0 +1,127 @@
1
+ # Encoding: ASCII-8BIT
2
+ ##
3
+ # unpacker.rb
4
+ # Created June 20, 2017
5
+ # By Ron Bowes
6
+ #
7
+ # See: LICENSE.md
8
+ #
9
+ # DNS has some unusual properties that we have to handle, which is why I
10
+ # wrote this class. It handles building / parsing DNS packets and keeping
11
+ # track of where in the packet we currently are. The advantage, besides
12
+ # simpler unpacking, is that encoded names (with pointers to other parts
13
+ # of the packet) can be trivially handled.
14
+ ##
15
+
16
+ require 'hexhelper'
17
+
18
+ require 'nesser/dns_exception'
19
+ require 'nesser/packets/constants'
20
+
21
+ module Nesser
22
+ class Unpacker
23
+ attr_accessor :data, :offset
24
+
25
+ public
26
+ def initialize(data)
27
+ @data = data
28
+ @offset = 0
29
+ end
30
+
31
+ private
32
+ def _verify_results(results)
33
+ # If there's at least one nil included in our results, bad stuff happened
34
+ if results.index(nil)
35
+ raise(FormatException, "DNS packet was truncated (or we messed up parsing it)!")
36
+ end
37
+ end
38
+
39
+ # Unpack from the string, exactly like the normal `String#Unpack` method
40
+ # in Ruby, except that an offset into the string is maintained and updated.
41
+ public
42
+ def unpack(format)
43
+ if @offset >= @data.length
44
+ raise(FormatException, "DNS packet was invalid!")
45
+ end
46
+
47
+ results = @data[@offset..-1].unpack(format + "a*")
48
+ remaining = results.pop
49
+ @offset = @data.length - remaining.length
50
+
51
+ _verify_results(results)
52
+
53
+ return *results
54
+ end
55
+
56
+ public
57
+ def unpack_one(format)
58
+ results = unpack(format)
59
+
60
+ _verify_results(results)
61
+ if results.length != 1
62
+ raise(FormatException, "unpack_one() was passed a bad format string")
63
+ end
64
+
65
+ return results.pop()
66
+ end
67
+
68
+ # This temporarily changes the offset that we're reading from, runs the
69
+ # given block, then changes it back. This is used internally while
70
+ # unpacking names.
71
+ private
72
+ def _with_offset(offset)
73
+ old_offset = @offset
74
+ @offset = offset
75
+ yield
76
+ @offset = old_offset
77
+ end
78
+
79
+ # Unpack a name from the packet. Names are special, because they're
80
+ # encoded as:
81
+ # * A series of length-prefixed blocks, each indicating a segment
82
+ # * Blocks with a length the starts with two '1' bits (11xxxxx...), which
83
+ # contains a pointer to another name elsewhere in the packet
84
+ public
85
+ def unpack_name(depth:0)
86
+ segments = []
87
+
88
+ if depth > MAX_RECURSION_DEPTH
89
+ raise(FormatException, "It looks like this packet contains recursive pointers!")
90
+ end
91
+
92
+ loop do
93
+ # If no offset is given, just eat data from the normal source
94
+ len = unpack_one("C")
95
+
96
+ # Stop at the null terminator
97
+ if len == 0
98
+ break
99
+ end
100
+
101
+ # Handle "pointer" records by updating the offset
102
+ if (len & 0xc0) == 0xc0
103
+ # If the first two bits are 1 (ie, 0xC0), the next
104
+ # 10 bits are an offset, so we have to mask out the first two bits
105
+ # with 0x3F (00111111)
106
+ offset = ((len << 8) | unpack_one("C")) & 0x3FFF
107
+
108
+ _with_offset(offset) do
109
+ segments << unpack_name(depth:depth+1).split(/\./)
110
+ end
111
+
112
+ break
113
+ end
114
+
115
+ # It's normal, just unpack what we need to!
116
+ segments << unpack("a#{len}")
117
+ end
118
+
119
+ return segments.join('.')
120
+ end
121
+
122
+ public
123
+ def to_s()
124
+ return HexHelper::to_s(@data, offset: @offset)
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,94 @@
1
+ # Encoding: ASCII-8BIT
2
+ ##
3
+ # transaction.rb
4
+ # Created June 20, 2017
5
+ # By Ron Bowes
6
+ #
7
+ # See: LICENSE.md
8
+ #
9
+ # When a request comes in, a transaction is created and sent to the callback.
10
+ # The transaction can be used to respond to the request at any point in the
11
+ # future.
12
+ #
13
+ # Any methods with a bang ('!') in front will send the response back to the
14
+ # requester. Only one bang method can be called, any subsequent calls will
15
+ # throw an exception.
16
+ ##
17
+ module Nesser
18
+ class Transaction
19
+ attr_reader :request_packet, :response_packet, :sent
20
+
21
+ public
22
+ def initialize(s:, request_packet:, host:, port:)
23
+ @s = s
24
+ @request_packet = request_packet
25
+ @host = host
26
+ @port = port
27
+ @sent = false
28
+
29
+ @response_packet = request_packet.answer()
30
+ end
31
+
32
+ private
33
+ def not_sent!()
34
+ if @sent
35
+ raise ArgumentError("Already sent!")
36
+ end
37
+ end
38
+
39
+ public
40
+ def answer!(answers=[])
41
+ answers.each do |answer|
42
+ @response_packet.add_answer(answer)
43
+ end
44
+
45
+ reply!()
46
+ end
47
+
48
+ public
49
+ def error!(rcode)
50
+ not_sent!()
51
+
52
+ @response_packet.rcode = rcode
53
+ reply!()
54
+ end
55
+
56
+ # public
57
+ # def passthrough!(pt_host, pt_port, callback = nil)
58
+ # not_sent!()
59
+ #
60
+ # Nesser.query(@request.questions[0].name, {
61
+ # :server => pt_host,
62
+ # :port => pt_port,
63
+ # :type => @request.questions[0].type,
64
+ # :cls => @request.questions[0].cls,
65
+ # :timeout => 3,
66
+ # }
67
+ # ) do |response|
68
+ # # If there was a timeout, handle it
69
+ # if(response.nil?)
70
+ # response = @response
71
+ # response.rcode = Nesser::Packet::RCODE_SERVER_FAILURE
72
+ # end
73
+ #
74
+ # response.trn_id = @request.trn_id
75
+ # @s.send(response.serialize(), 0, @host, @port)
76
+ #
77
+ # # Let the callback know if anybody registered one
78
+ # if(callback)
79
+ # callback.call(response)
80
+ # end
81
+ # end
82
+ #
83
+ # @sent = true
84
+ # end
85
+
86
+ private
87
+ def reply!()
88
+ not_sent!()
89
+
90
+ @s.send(@response_packet.to_bytes(), 0, @host, @port)
91
+ @sent = true
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,3 @@
1
+ module Nesser
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'nesser/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "nesser"
8
+ spec.version = Nesser::VERSION
9
+ spec.authors = ["iagox86"]
10
+ spec.email = ["ron-git@skullsecurity.org"]
11
+
12
+ spec.summary = "A simple and straight forward DNS library, created for dnscat2"
13
+ spec.homepage = "https://github.com/iagox86/nesser"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
16
+ spec.require_paths = ["lib"]
17
+
18
+ spec.add_development_dependency "bundler"
19
+ spec.add_development_dependency "rake"
20
+ spec.add_development_dependency "simplecov"
21
+ spec.add_dependency "hexhelper"
22
+ end
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nesser
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - iagox86
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-06-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: simplecov
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
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: hexhelper
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'
69
+ description:
70
+ email:
71
+ - ron-git@skullsecurity.org
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".travis.yml"
78
+ - Gemfile
79
+ - README.md
80
+ - Rakefile
81
+ - bin/console
82
+ - bin/setup
83
+ - lib/nesser.rb
84
+ - lib/nesser/dns_exception.rb
85
+ - lib/nesser/packets/answer.rb
86
+ - lib/nesser/packets/constants.rb
87
+ - lib/nesser/packets/packer.rb
88
+ - lib/nesser/packets/packet.rb
89
+ - lib/nesser/packets/question.rb
90
+ - lib/nesser/packets/rr_types.rb
91
+ - lib/nesser/packets/unpacker.rb
92
+ - lib/nesser/transaction.rb
93
+ - lib/nesser/version.rb
94
+ - nesser.gemspec
95
+ homepage: https://github.com/iagox86/nesser
96
+ licenses: []
97
+ metadata: {}
98
+ post_install_message:
99
+ rdoc_options: []
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ requirements: []
113
+ rubyforge_project:
114
+ rubygems_version: 2.5.1
115
+ signing_key:
116
+ specification_version: 4
117
+ summary: A simple and straight forward DNS library, created for dnscat2
118
+ test_files: []