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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/README.md +28 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/nesser.rb +149 -0
- data/lib/nesser/dns_exception.rb +18 -0
- data/lib/nesser/packets/answer.rb +79 -0
- data/lib/nesser/packets/constants.rb +111 -0
- data/lib/nesser/packets/packer.rb +97 -0
- data/lib/nesser/packets/packet.rb +186 -0
- data/lib/nesser/packets/question.rb +43 -0
- data/lib/nesser/packets/rr_types.rb +290 -0
- data/lib/nesser/packets/unpacker.rb +127 -0
- data/lib/nesser/transaction.rb +94 -0
- data/lib/nesser/version.rb +3 -0
- data/nesser.gemspec +22 -0
- metadata +118 -0
@@ -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
|
data/nesser.gemspec
ADDED
@@ -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: []
|