Skip to content

Commit e255db9

Browse files
committed
Partial commit
1 parent 5d2c02f commit e255db9

File tree

6 files changed

+142
-137
lines changed

6 files changed

+142
-137
lines changed

lib/msf/core/auxiliary/kademlia.rb

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,16 @@
11
# -*- coding: binary -*-
2+
require 'rex/proto/kademlia'
23

3-
module Rex
4-
module Proto
5-
##
6-
#
7-
# Minimal support for the newer Kademlia protocol, referred to here and often
8-
# elsewhere as Kademlia2. It is unclear how this differs from the old protocol.
9-
#
10-
# Protocol details are hard to come by because most documentation is academic
11-
# in nature and glosses over the low-level network details. The best
12-
# documents I found on the protocol are:
4+
module Msf
5+
6+
###
137
#
14-
# http://gbmaster.wordpress.com/2013/05/05/botnets-surrounding-us-an-initial-focus-on-kad/
15-
# http://gbmaster.wordpress.com/2013/06/16/botnets-surrounding-us-sending-kademlia2_bootstrap_req-kademlia2_hello_req-and-their-strict-cousins/
16-
# http://gbmaster.wordpress.com/2013/11/23/botnets-surrounding-us-performing-requests-sending-out-kademlia2_req-and-asking-contact-where-art-thou/
8+
# This module provides methods for working with Kademlia
179
#
18-
##
19-
module Kademlia
10+
###
11+
module Auxiliary::Kademlia
12+
include Rex::Proto::Kademlia
13+
2014
# Opcode for a BOOTSTRAP request
2115
BOOTSTRAP_REQ = 0x01
2216
# Opcode for a BOOTSTRAP response
@@ -43,15 +37,15 @@ def bootstrap
4337
# @return [Array] the discovered peer ID, TCP port, version and a list of peers
4438
# if the response if valid, nil otherwise
4539
def decode_bootstrap_res(response)
46-
type, body = decode_message(response)
40+
message = Message.from_data(response)
4741
# abort if this isn't a valid response
48-
return nil unless type = BOOTSTRAP_RES
49-
return nil unless body.size >= 23
50-
peer_id = decode_peer_id(body.slice!(0,16))
51-
tcp_port, version, num_peers = body.slice!(0,5).unpack('vCv')
42+
return nil unless message.type = BOOTSTRAP_RES
43+
return nil unless message.body.size >= 23
44+
peer_id = decode_peer_id(message.body.slice!(0,16))
45+
tcp_port, version, num_peers = message.body.slice!(0,5).unpack('vCv')
5246
# protocol says there are no peers and the body confirms this, so just return with no peers
53-
return [ tcp_port, version, []] if num_peers == 0 && body.blank?
54-
peers = decode_bootstrap_peers(body)
47+
return [ tcp_port, version, []] if num_peers == 0 && message.body.blank?
48+
peers = decode_bootstrap_peers(message.body)
5549
# abort if the peer data was invalid
5650
return nil unless peers
5751
[ peer_id, tcp_port, version, peers ]
@@ -61,21 +55,21 @@ def decode_bootstrap_res(response)
6155
#
6256
# @return [String] a PING request
6357
def ping
64-
encode_message(PING)
58+
Message.new(PING)
6559
end
6660

6761
# Decode a PING response, PONG
6862
#
6963
# @param response [String] the response to decode
7064
# @return [Integer] the source port from the PING response if the response is valid, nil otherwise
7165
def decode_pong(response)
72-
type, port = decode_message(response)
66+
message = Message.from_data(response)
7367
# abort if this isn't a pong
74-
return nil unless type == PONG
68+
return nil unless message.type == PONG
7569
# abort if the response is too large/small
76-
return nil unless port && port.size == 2
70+
return nil unless message.body && message.body.size == 2
7771
# this should always be equivalent to the source port from which the PING was received
78-
port.unpack('v')[0]
72+
message.body.unpack('v')[0]
7973
end
8074

8175
# Decode a list of peers from a BOOTSTRAP response
@@ -122,4 +116,3 @@ def decode_peer_id(bytes)
122116
# end
123117
end
124118
end
125-
end

lib/rex/proto/kademlia/message.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ def self.from_data(data)
3939
fail NotImplementedError, "Unable to handle #{message.length}-byte compressed Kademlia message"
4040
end
4141
return if header != STANDARD_PACKET
42-
Message.new(type,data[2, data.length]])
42+
Message.new(type, data[2, data.length])
4343
end
4444

45-
def to_s
45+
def to_str
4646
[STANDARD_PACKET, @type].pack('CC') + @body
4747
end
4848
end

modules/auxiliary/scanner/kademlia/server_info.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,11 @@
44
##
55

66
require 'msf/core'
7-
require 'rex/proto/kademlia'
87

98
class Metasploit3 < Msf::Auxiliary
109
include Msf::Auxiliary::Report
1110
include Msf::Auxiliary::UDPScanner
12-
include Rex::Proto::Kademlia
11+
include Msf::Auxiliary::Kademlia
1312

1413
def initialize(info = {})
1514
super(
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# -*- coding: binary -*-
2+
require 'spec_helper'
3+
require 'msf/core/auxiliary/kademlia'
4+
5+
describe Msf::Auxiliary::Kademlia do
6+
subject(:kad) do
7+
mod = Module.new
8+
mod.extend described_class
9+
mod
10+
end
11+
12+
describe '#decode_pong' do
13+
it 'does not decode overly small pongs' do
14+
expect(kad.decode_pong("\xE4\x61\x01")).to eq(nil)
15+
end
16+
17+
it 'does not decode overly large pongs' do
18+
expect(kad.decode_pong("\xE4\x61\x01\x02\x03")).to eq(nil)
19+
end
20+
21+
it 'properly decodes valid pongs' do
22+
expect(kad.decode_pong("\xE4\x61\x9E\x86")).to eq(34462)
23+
end
24+
end
25+
26+
describe '#decode_bootstrap_peer' do
27+
it 'does not decode overly small peer responses' do
28+
expect(kad.decode_bootstrap_peer("this is too small")).to eq(nil)
29+
end
30+
31+
it 'does not decode overly large peer responses' do
32+
expect(kad.decode_bootstrap_peer("this is much, much, much too large")).to eq(nil)
33+
end
34+
35+
it 'properly extracts peer info' do
36+
data =
37+
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + # peer ID
38+
"\x04\x28\xA8\xC0" + # 192.168.40.4
39+
"\x31\xd4" + # UDP port 54321
40+
"\x39\x30" + # TCP port 12345
41+
"\x08" # peer type
42+
peer_id, ip, udp_port, tcp_port, type = kad.decode_bootstrap_peer(data)
43+
expect(peer_id).to eq('3020100070605040B0A09080F0E0D0C')
44+
expect(ip).to eq('192.168.40.4')
45+
expect(udp_port).to eq(54321)
46+
expect(tcp_port).to eq(12345)
47+
expect(type).to eq(8)
48+
end
49+
end
50+
51+
describe '#decode_bootstrap_peers' do
52+
it 'does not decode overly small bootstrap responses' do
53+
expect(kad.decode_bootstrap_peer("this is too small")).to eq(nil)
54+
end
55+
56+
it 'does not decode overly large bootstrap responses' do
57+
expect(kad.decode_bootstrap_peer("this is large enough but truncated")).to eq(nil)
58+
end
59+
60+
it 'properly extracts peers info' do
61+
data =
62+
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + # peer ID
63+
"\x04\x28\xA8\xC0" + # 192.168.40.4
64+
"\x31\xd4" + # UDP port 54321
65+
"\x39\x30" + # TCP port 12345
66+
"\x08" + # peer type
67+
"\x01\x01\x02\x02\x03\x03\x04\x04\x05\x05\x06\x06\x07\x07\x08\x08" + # peer ID
68+
"\x05\x28\xA8\xC0" + # 192.168.40.5
69+
"\x5c\x11" + # UDP port 4444
70+
"\xb3\x15" + # TCP port 5555
71+
"\x09" # peer type
72+
peers = kad.decode_bootstrap_peers(data)
73+
expect(peers.size).to eq(2)
74+
peer1_id, peer1_ip, peer1_udp, peer1_tcp, peer1_type = peers.first
75+
expect(peer1_id).to eq('3020100070605040B0A09080F0E0D0C')
76+
expect(peer1_ip).to eq('192.168.40.4')
77+
expect(peer1_udp).to eq(54321)
78+
expect(peer1_tcp).to eq(12345)
79+
expect(peer1_type).to eq(8)
80+
peer2_id, peer2_ip, peer2_udp, peer2_tcp, peer2_type = peers.last
81+
expect(peer2_id).to eq('2020101040403030606050508080707')
82+
expect(peer2_ip).to eq('192.168.40.5')
83+
expect(peer2_udp).to eq(4444)
84+
expect(peer2_tcp).to eq(5555)
85+
expect(peer2_type).to eq(9)
86+
end
87+
end
88+
89+
describe '#decode_bootstrap_res' do
90+
it 'properly decodes valid bootstrap responses' do
91+
data = IO.read(File.join(File.dirname(__FILE__), 'kademlia_bootstrap_res.bin'))
92+
peer_id, tcp, version, peers = kad.decode_bootstrap_res(data)
93+
expect(peer_id).to eq('B54A83462529B21EF51FD54B956B07B0')
94+
expect(tcp).to eq(4662)
95+
expect(version).to eq(8)
96+
# don't bother checking every peer
97+
expect(peers.size).to eq(20)
98+
end
99+
end
100+
101+
describe '#decode_peer_id' do
102+
it 'decodes a peer ID properly' do
103+
bytes = "\x00\x60\x89\x9B\x0A\x0B\xBE\xAE\x45\x35\xCB\x0E\x07\xA1\x77\x71"
104+
peer_id = "9B896000AEBE0B0A0ECB35457177A107"
105+
expect(kad.decode_peer_id(bytes)).to eq(peer_id)
106+
end
107+
end
108+
109+
describe '#encode_peer' do
110+
skip 'encodes a peer ID properly' do
111+
bytes = "\x00\x60\x89\x9B\x0A\x0B\xBE\xAE\x45\x35\xCB\x0E\x07\xA1\x77\x71"
112+
peer_id = "9B896000AEBE0B0A0ECB35457177A107"
113+
expect(kad.encode_peer_id(peer_id)).to eq(bytes)
114+
end
115+
end
116+
end

spec/lib/rex/proto/kademlia/message_spec.rb

Lines changed: 2 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
end
1111

1212
describe '#encode_message' do
13+
let(:no_body) { "\xE4\x01" }
14+
let(:body) { "\xE4\x01p2p" }
1315
it 'properly encodes messages without a body' do
1416
expect(kad.encode_message(1)).to eq("\xE4\x01")
1517
end
@@ -45,109 +47,4 @@
4547
expect(payload).to eq('testtesttest')
4648
end
4749
end
48-
49-
describe '#decode_pong' do
50-
it 'does not decode overly small pongs' do
51-
expect(kad.decode_pong("\xE4\x61\x01")).to eq(nil)
52-
end
53-
54-
it 'does not decode overly large pongs' do
55-
expect(kad.decode_pong("\xE4\x61\x01\x02\x03")).to eq(nil)
56-
end
57-
58-
it 'properly decodes valid pongs' do
59-
expect(kad.decode_pong("\xE4\x61\x9E\x86")).to eq(34462)
60-
end
61-
end
62-
63-
describe '#decode_bootstrap_peer' do
64-
it 'does not decode overly small peer responses' do
65-
expect(kad.decode_bootstrap_peer("this is too small")).to eq(nil)
66-
end
67-
68-
it 'does not decode overly large peer responses' do
69-
expect(kad.decode_bootstrap_peer("this is much, much, much too large")).to eq(nil)
70-
end
71-
72-
it 'properly extracts peer info' do
73-
data =
74-
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + # peer ID
75-
"\x04\x28\xA8\xC0" + # 192.168.40.4
76-
"\x31\xd4" + # UDP port 54321
77-
"\x39\x30" + # TCP port 12345
78-
"\x08" # peer type
79-
peer_id, ip, udp_port, tcp_port, type = kad.decode_bootstrap_peer(data)
80-
expect(peer_id).to eq('3020100070605040B0A09080F0E0D0C')
81-
expect(ip).to eq('192.168.40.4')
82-
expect(udp_port).to eq(54321)
83-
expect(tcp_port).to eq(12345)
84-
expect(type).to eq(8)
85-
end
86-
end
87-
88-
describe '#decode_bootstrap_peers' do
89-
it 'does not decode overly small bootstrap responses' do
90-
expect(kad.decode_bootstrap_peer("this is too small")).to eq(nil)
91-
end
92-
93-
it 'does not decode overly large bootstrap responses' do
94-
expect(kad.decode_bootstrap_peer("this is large enough but truncated")).to eq(nil)
95-
end
96-
97-
it 'properly extracts peers info' do
98-
data =
99-
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + # peer ID
100-
"\x04\x28\xA8\xC0" + # 192.168.40.4
101-
"\x31\xd4" + # UDP port 54321
102-
"\x39\x30" + # TCP port 12345
103-
"\x08" + # peer type
104-
"\x01\x01\x02\x02\x03\x03\x04\x04\x05\x05\x06\x06\x07\x07\x08\x08" + # peer ID
105-
"\x05\x28\xA8\xC0" + # 192.168.40.5
106-
"\x5c\x11" + # UDP port 4444
107-
"\xb3\x15" + # TCP port 5555
108-
"\x09" # peer type
109-
peers = kad.decode_bootstrap_peers(data)
110-
expect(peers.size).to eq(2)
111-
peer1_id, peer1_ip, peer1_udp, peer1_tcp, peer1_type = peers.first
112-
expect(peer1_id).to eq('3020100070605040B0A09080F0E0D0C')
113-
expect(peer1_ip).to eq('192.168.40.4')
114-
expect(peer1_udp).to eq(54321)
115-
expect(peer1_tcp).to eq(12345)
116-
expect(peer1_type).to eq(8)
117-
peer2_id, peer2_ip, peer2_udp, peer2_tcp, peer2_type = peers.last
118-
expect(peer2_id).to eq('2020101040403030606050508080707')
119-
expect(peer2_ip).to eq('192.168.40.5')
120-
expect(peer2_udp).to eq(4444)
121-
expect(peer2_tcp).to eq(5555)
122-
expect(peer2_type).to eq(9)
123-
end
124-
end
125-
126-
describe '#decode_bootstrap_res' do
127-
it 'properly decodes valid bootstrap responses' do
128-
data = IO.read(File.join(File.dirname(__FILE__), 'kademlia_bootstrap_res.bin'))
129-
peer_id, tcp, version, peers = kad.decode_bootstrap_res(data)
130-
expect(peer_id).to eq('B54A83462529B21EF51FD54B956B07B0')
131-
expect(tcp).to eq(4662)
132-
expect(version).to eq(8)
133-
# don't bother checking every peer
134-
expect(peers.size).to eq(20)
135-
end
136-
end
137-
138-
describe '#decode_peer_id' do
139-
it 'decodes a peer ID properly' do
140-
bytes = "\x00\x60\x89\x9B\x0A\x0B\xBE\xAE\x45\x35\xCB\x0E\x07\xA1\x77\x71"
141-
peer_id = "9B896000AEBE0B0A0ECB35457177A107"
142-
expect(kad.decode_peer_id(bytes)).to eq(peer_id)
143-
end
144-
end
145-
146-
describe '#encode_peer' do
147-
skip 'encodes a peer ID properly' do
148-
bytes = "\x00\x60\x89\x9B\x0A\x0B\xBE\xAE\x45\x35\xCB\x0E\x07\xA1\x77\x71"
149-
peer_id = "9B896000AEBE0B0A0ECB35457177A107"
150-
expect(kad.encode_peer_id(peer_id)).to eq(bytes)
151-
end
152-
end
15350
end

0 commit comments

Comments
 (0)