Partial commit
parent
5d2c02f402
commit
e255db9429
|
@ -1,22 +1,16 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/proto/kademlia'
|
||||
|
||||
module Rex
|
||||
module Proto
|
||||
##
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Minimal support for the newer Kademlia protocol, referred to here and often
|
||||
# elsewhere as Kademlia2. It is unclear how this differs from the old protocol.
|
||||
# This module provides methods for working with Kademlia
|
||||
#
|
||||
# Protocol details are hard to come by because most documentation is academic
|
||||
# in nature and glosses over the low-level network details. The best
|
||||
# documents I found on the protocol are:
|
||||
#
|
||||
# http://gbmaster.wordpress.com/2013/05/05/botnets-surrounding-us-an-initial-focus-on-kad/
|
||||
# http://gbmaster.wordpress.com/2013/06/16/botnets-surrounding-us-sending-kademlia2_bootstrap_req-kademlia2_hello_req-and-their-strict-cousins/
|
||||
# http://gbmaster.wordpress.com/2013/11/23/botnets-surrounding-us-performing-requests-sending-out-kademlia2_req-and-asking-contact-where-art-thou/
|
||||
#
|
||||
##
|
||||
module Kademlia
|
||||
###
|
||||
module Auxiliary::Kademlia
|
||||
include Rex::Proto::Kademlia
|
||||
|
||||
# Opcode for a BOOTSTRAP request
|
||||
BOOTSTRAP_REQ = 0x01
|
||||
# Opcode for a BOOTSTRAP response
|
||||
|
@ -43,15 +37,15 @@ module Kademlia
|
|||
# @return [Array] the discovered peer ID, TCP port, version and a list of peers
|
||||
# if the response if valid, nil otherwise
|
||||
def decode_bootstrap_res(response)
|
||||
type, body = decode_message(response)
|
||||
message = Message.from_data(response)
|
||||
# abort if this isn't a valid response
|
||||
return nil unless type = BOOTSTRAP_RES
|
||||
return nil unless body.size >= 23
|
||||
peer_id = decode_peer_id(body.slice!(0,16))
|
||||
tcp_port, version, num_peers = body.slice!(0,5).unpack('vCv')
|
||||
return nil unless message.type = BOOTSTRAP_RES
|
||||
return nil unless message.body.size >= 23
|
||||
peer_id = decode_peer_id(message.body.slice!(0,16))
|
||||
tcp_port, version, num_peers = message.body.slice!(0,5).unpack('vCv')
|
||||
# protocol says there are no peers and the body confirms this, so just return with no peers
|
||||
return [ tcp_port, version, []] if num_peers == 0 && body.blank?
|
||||
peers = decode_bootstrap_peers(body)
|
||||
return [ tcp_port, version, []] if num_peers == 0 && message.body.blank?
|
||||
peers = decode_bootstrap_peers(message.body)
|
||||
# abort if the peer data was invalid
|
||||
return nil unless peers
|
||||
[ peer_id, tcp_port, version, peers ]
|
||||
|
@ -61,7 +55,7 @@ module Kademlia
|
|||
#
|
||||
# @return [String] a PING request
|
||||
def ping
|
||||
encode_message(PING)
|
||||
Message.new(PING)
|
||||
end
|
||||
|
||||
# Decode a PING response, PONG
|
||||
|
@ -69,13 +63,13 @@ module Kademlia
|
|||
# @param response [String] the response to decode
|
||||
# @return [Integer] the source port from the PING response if the response is valid, nil otherwise
|
||||
def decode_pong(response)
|
||||
type, port = decode_message(response)
|
||||
message = Message.from_data(response)
|
||||
# abort if this isn't a pong
|
||||
return nil unless type == PONG
|
||||
return nil unless message.type == PONG
|
||||
# abort if the response is too large/small
|
||||
return nil unless port && port.size == 2
|
||||
return nil unless message.body && message.body.size == 2
|
||||
# this should always be equivalent to the source port from which the PING was received
|
||||
port.unpack('v')[0]
|
||||
message.body.unpack('v')[0]
|
||||
end
|
||||
|
||||
# Decode a list of peers from a BOOTSTRAP response
|
||||
|
@ -122,4 +116,3 @@ module Kademlia
|
|||
# end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -39,10 +39,10 @@ module Kademlia
|
|||
fail NotImplementedError, "Unable to handle #{message.length}-byte compressed Kademlia message"
|
||||
end
|
||||
return if header != STANDARD_PACKET
|
||||
Message.new(type,data[2, data.length]])
|
||||
Message.new(type, data[2, data.length])
|
||||
end
|
||||
|
||||
def to_s
|
||||
def to_str
|
||||
[STANDARD_PACKET, @type].pack('CC') + @body
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,12 +4,11 @@
|
|||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'rex/proto/kademlia'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Auxiliary::UDPScanner
|
||||
include Rex::Proto::Kademlia
|
||||
include Msf::Auxiliary::Kademlia
|
||||
|
||||
def initialize(info = {})
|
||||
super(
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'spec_helper'
|
||||
require 'msf/core/auxiliary/kademlia'
|
||||
|
||||
describe Msf::Auxiliary::Kademlia do
|
||||
subject(:kad) do
|
||||
mod = Module.new
|
||||
mod.extend described_class
|
||||
mod
|
||||
end
|
||||
|
||||
describe '#decode_pong' do
|
||||
it 'does not decode overly small pongs' do
|
||||
expect(kad.decode_pong("\xE4\x61\x01")).to eq(nil)
|
||||
end
|
||||
|
||||
it 'does not decode overly large pongs' do
|
||||
expect(kad.decode_pong("\xE4\x61\x01\x02\x03")).to eq(nil)
|
||||
end
|
||||
|
||||
it 'properly decodes valid pongs' do
|
||||
expect(kad.decode_pong("\xE4\x61\x9E\x86")).to eq(34462)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#decode_bootstrap_peer' do
|
||||
it 'does not decode overly small peer responses' do
|
||||
expect(kad.decode_bootstrap_peer("this is too small")).to eq(nil)
|
||||
end
|
||||
|
||||
it 'does not decode overly large peer responses' do
|
||||
expect(kad.decode_bootstrap_peer("this is much, much, much too large")).to eq(nil)
|
||||
end
|
||||
|
||||
it 'properly extracts peer info' do
|
||||
data =
|
||||
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + # peer ID
|
||||
"\x04\x28\xA8\xC0" + # 192.168.40.4
|
||||
"\x31\xd4" + # UDP port 54321
|
||||
"\x39\x30" + # TCP port 12345
|
||||
"\x08" # peer type
|
||||
peer_id, ip, udp_port, tcp_port, type = kad.decode_bootstrap_peer(data)
|
||||
expect(peer_id).to eq('3020100070605040B0A09080F0E0D0C')
|
||||
expect(ip).to eq('192.168.40.4')
|
||||
expect(udp_port).to eq(54321)
|
||||
expect(tcp_port).to eq(12345)
|
||||
expect(type).to eq(8)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#decode_bootstrap_peers' do
|
||||
it 'does not decode overly small bootstrap responses' do
|
||||
expect(kad.decode_bootstrap_peer("this is too small")).to eq(nil)
|
||||
end
|
||||
|
||||
it 'does not decode overly large bootstrap responses' do
|
||||
expect(kad.decode_bootstrap_peer("this is large enough but truncated")).to eq(nil)
|
||||
end
|
||||
|
||||
it 'properly extracts peers info' do
|
||||
data =
|
||||
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + # peer ID
|
||||
"\x04\x28\xA8\xC0" + # 192.168.40.4
|
||||
"\x31\xd4" + # UDP port 54321
|
||||
"\x39\x30" + # TCP port 12345
|
||||
"\x08" + # peer type
|
||||
"\x01\x01\x02\x02\x03\x03\x04\x04\x05\x05\x06\x06\x07\x07\x08\x08" + # peer ID
|
||||
"\x05\x28\xA8\xC0" + # 192.168.40.5
|
||||
"\x5c\x11" + # UDP port 4444
|
||||
"\xb3\x15" + # TCP port 5555
|
||||
"\x09" # peer type
|
||||
peers = kad.decode_bootstrap_peers(data)
|
||||
expect(peers.size).to eq(2)
|
||||
peer1_id, peer1_ip, peer1_udp, peer1_tcp, peer1_type = peers.first
|
||||
expect(peer1_id).to eq('3020100070605040B0A09080F0E0D0C')
|
||||
expect(peer1_ip).to eq('192.168.40.4')
|
||||
expect(peer1_udp).to eq(54321)
|
||||
expect(peer1_tcp).to eq(12345)
|
||||
expect(peer1_type).to eq(8)
|
||||
peer2_id, peer2_ip, peer2_udp, peer2_tcp, peer2_type = peers.last
|
||||
expect(peer2_id).to eq('2020101040403030606050508080707')
|
||||
expect(peer2_ip).to eq('192.168.40.5')
|
||||
expect(peer2_udp).to eq(4444)
|
||||
expect(peer2_tcp).to eq(5555)
|
||||
expect(peer2_type).to eq(9)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#decode_bootstrap_res' do
|
||||
it 'properly decodes valid bootstrap responses' do
|
||||
data = IO.read(File.join(File.dirname(__FILE__), 'kademlia_bootstrap_res.bin'))
|
||||
peer_id, tcp, version, peers = kad.decode_bootstrap_res(data)
|
||||
expect(peer_id).to eq('B54A83462529B21EF51FD54B956B07B0')
|
||||
expect(tcp).to eq(4662)
|
||||
expect(version).to eq(8)
|
||||
# don't bother checking every peer
|
||||
expect(peers.size).to eq(20)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#decode_peer_id' do
|
||||
it 'decodes a peer ID properly' do
|
||||
bytes = "\x00\x60\x89\x9B\x0A\x0B\xBE\xAE\x45\x35\xCB\x0E\x07\xA1\x77\x71"
|
||||
peer_id = "9B896000AEBE0B0A0ECB35457177A107"
|
||||
expect(kad.decode_peer_id(bytes)).to eq(peer_id)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#encode_peer' do
|
||||
skip 'encodes a peer ID properly' do
|
||||
bytes = "\x00\x60\x89\x9B\x0A\x0B\xBE\xAE\x45\x35\xCB\x0E\x07\xA1\x77\x71"
|
||||
peer_id = "9B896000AEBE0B0A0ECB35457177A107"
|
||||
expect(kad.encode_peer_id(peer_id)).to eq(bytes)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -10,6 +10,8 @@ describe Rex::Proto::Kademlia do
|
|||
end
|
||||
|
||||
describe '#encode_message' do
|
||||
let(:no_body) { "\xE4\x01" }
|
||||
let(:body) { "\xE4\x01p2p" }
|
||||
it 'properly encodes messages without a body' do
|
||||
expect(kad.encode_message(1)).to eq("\xE4\x01")
|
||||
end
|
||||
|
@ -45,109 +47,4 @@ describe Rex::Proto::Kademlia do
|
|||
expect(payload).to eq('testtesttest')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#decode_pong' do
|
||||
it 'does not decode overly small pongs' do
|
||||
expect(kad.decode_pong("\xE4\x61\x01")).to eq(nil)
|
||||
end
|
||||
|
||||
it 'does not decode overly large pongs' do
|
||||
expect(kad.decode_pong("\xE4\x61\x01\x02\x03")).to eq(nil)
|
||||
end
|
||||
|
||||
it 'properly decodes valid pongs' do
|
||||
expect(kad.decode_pong("\xE4\x61\x9E\x86")).to eq(34462)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#decode_bootstrap_peer' do
|
||||
it 'does not decode overly small peer responses' do
|
||||
expect(kad.decode_bootstrap_peer("this is too small")).to eq(nil)
|
||||
end
|
||||
|
||||
it 'does not decode overly large peer responses' do
|
||||
expect(kad.decode_bootstrap_peer("this is much, much, much too large")).to eq(nil)
|
||||
end
|
||||
|
||||
it 'properly extracts peer info' do
|
||||
data =
|
||||
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + # peer ID
|
||||
"\x04\x28\xA8\xC0" + # 192.168.40.4
|
||||
"\x31\xd4" + # UDP port 54321
|
||||
"\x39\x30" + # TCP port 12345
|
||||
"\x08" # peer type
|
||||
peer_id, ip, udp_port, tcp_port, type = kad.decode_bootstrap_peer(data)
|
||||
expect(peer_id).to eq('3020100070605040B0A09080F0E0D0C')
|
||||
expect(ip).to eq('192.168.40.4')
|
||||
expect(udp_port).to eq(54321)
|
||||
expect(tcp_port).to eq(12345)
|
||||
expect(type).to eq(8)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#decode_bootstrap_peers' do
|
||||
it 'does not decode overly small bootstrap responses' do
|
||||
expect(kad.decode_bootstrap_peer("this is too small")).to eq(nil)
|
||||
end
|
||||
|
||||
it 'does not decode overly large bootstrap responses' do
|
||||
expect(kad.decode_bootstrap_peer("this is large enough but truncated")).to eq(nil)
|
||||
end
|
||||
|
||||
it 'properly extracts peers info' do
|
||||
data =
|
||||
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + # peer ID
|
||||
"\x04\x28\xA8\xC0" + # 192.168.40.4
|
||||
"\x31\xd4" + # UDP port 54321
|
||||
"\x39\x30" + # TCP port 12345
|
||||
"\x08" + # peer type
|
||||
"\x01\x01\x02\x02\x03\x03\x04\x04\x05\x05\x06\x06\x07\x07\x08\x08" + # peer ID
|
||||
"\x05\x28\xA8\xC0" + # 192.168.40.5
|
||||
"\x5c\x11" + # UDP port 4444
|
||||
"\xb3\x15" + # TCP port 5555
|
||||
"\x09" # peer type
|
||||
peers = kad.decode_bootstrap_peers(data)
|
||||
expect(peers.size).to eq(2)
|
||||
peer1_id, peer1_ip, peer1_udp, peer1_tcp, peer1_type = peers.first
|
||||
expect(peer1_id).to eq('3020100070605040B0A09080F0E0D0C')
|
||||
expect(peer1_ip).to eq('192.168.40.4')
|
||||
expect(peer1_udp).to eq(54321)
|
||||
expect(peer1_tcp).to eq(12345)
|
||||
expect(peer1_type).to eq(8)
|
||||
peer2_id, peer2_ip, peer2_udp, peer2_tcp, peer2_type = peers.last
|
||||
expect(peer2_id).to eq('2020101040403030606050508080707')
|
||||
expect(peer2_ip).to eq('192.168.40.5')
|
||||
expect(peer2_udp).to eq(4444)
|
||||
expect(peer2_tcp).to eq(5555)
|
||||
expect(peer2_type).to eq(9)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#decode_bootstrap_res' do
|
||||
it 'properly decodes valid bootstrap responses' do
|
||||
data = IO.read(File.join(File.dirname(__FILE__), 'kademlia_bootstrap_res.bin'))
|
||||
peer_id, tcp, version, peers = kad.decode_bootstrap_res(data)
|
||||
expect(peer_id).to eq('B54A83462529B21EF51FD54B956B07B0')
|
||||
expect(tcp).to eq(4662)
|
||||
expect(version).to eq(8)
|
||||
# don't bother checking every peer
|
||||
expect(peers.size).to eq(20)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#decode_peer_id' do
|
||||
it 'decodes a peer ID properly' do
|
||||
bytes = "\x00\x60\x89\x9B\x0A\x0B\xBE\xAE\x45\x35\xCB\x0E\x07\xA1\x77\x71"
|
||||
peer_id = "9B896000AEBE0B0A0ECB35457177A107"
|
||||
expect(kad.decode_peer_id(bytes)).to eq(peer_id)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#encode_peer' do
|
||||
skip 'encodes a peer ID properly' do
|
||||
bytes = "\x00\x60\x89\x9B\x0A\x0B\xBE\xAE\x45\x35\xCB\x0E\x07\xA1\x77\x71"
|
||||
peer_id = "9B896000AEBE0B0A0ECB35457177A107"
|
||||
expect(kad.encode_peer_id(peer_id)).to eq(bytes)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue