Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/rex/post/meterpreter/packet.rb
Views: 11784
# -*- coding: binary -*-1require 'openssl'2require 'rex/post/meterpreter/command_mapper'34module Rex5module Post6module Meterpreter78#9# Constants10#11PACKET_TYPE_REQUEST = 012PACKET_TYPE_RESPONSE = 113PACKET_TYPE_PLAIN_REQUEST = 1014PACKET_TYPE_PLAIN_RESPONSE = 111516#17# TLV Meta Types18#19TLV_META_TYPE_NONE = 020TLV_META_TYPE_STRING = (1 << 16)21TLV_META_TYPE_UINT = (1 << 17)22TLV_META_TYPE_RAW = (1 << 18)23TLV_META_TYPE_BOOL = (1 << 19)24TLV_META_TYPE_QWORD = (1 << 20)25TLV_META_TYPE_COMPRESSED = (1 << 29)26TLV_META_TYPE_GROUP = (1 << 30)27TLV_META_TYPE_COMPLEX = (1 << 31)2829# Exclude compressed from the mask since other meta types (e.g. RAW) can also30# be compressed31TLV_META_MASK = (32TLV_META_TYPE_STRING |33TLV_META_TYPE_UINT |34TLV_META_TYPE_RAW |35TLV_META_TYPE_BOOL |36TLV_META_TYPE_QWORD |37TLV_META_TYPE_GROUP |38TLV_META_TYPE_COMPLEX39)4041#42# TLV base starting points43#44TLV_RESERVED = 045TLV_EXTENSIONS = 2000046TLV_USER = 4000047TLV_TEMP = 600004849#50# TLV Specific Types51#52TLV_TYPE_ANY = TLV_META_TYPE_NONE | 053TLV_TYPE_COMMAND_ID = TLV_META_TYPE_UINT | 154TLV_TYPE_REQUEST_ID = TLV_META_TYPE_STRING | 255TLV_TYPE_EXCEPTION = TLV_META_TYPE_GROUP | 356TLV_TYPE_RESULT = TLV_META_TYPE_UINT | 4575859TLV_TYPE_STRING = TLV_META_TYPE_STRING | 1060TLV_TYPE_UINT = TLV_META_TYPE_UINT | 1161TLV_TYPE_BOOL = TLV_META_TYPE_BOOL | 126263TLV_TYPE_LENGTH = TLV_META_TYPE_UINT | 2564TLV_TYPE_DATA = TLV_META_TYPE_RAW | 2665TLV_TYPE_FLAGS = TLV_META_TYPE_UINT | 276667TLV_TYPE_CHANNEL_ID = TLV_META_TYPE_UINT | 5068TLV_TYPE_CHANNEL_TYPE = TLV_META_TYPE_STRING | 5169TLV_TYPE_CHANNEL_DATA = TLV_META_TYPE_RAW | 5270TLV_TYPE_CHANNEL_DATA_GROUP = TLV_META_TYPE_GROUP | 5371TLV_TYPE_CHANNEL_CLASS = TLV_META_TYPE_UINT | 5472TLV_TYPE_CHANNEL_PARENTID = TLV_META_TYPE_UINT | 557374TLV_TYPE_SEEK_WHENCE = TLV_META_TYPE_UINT | 7075TLV_TYPE_SEEK_OFFSET = TLV_META_TYPE_UINT | 7176TLV_TYPE_SEEK_POS = TLV_META_TYPE_UINT | 727778TLV_TYPE_EXCEPTION_CODE = TLV_META_TYPE_UINT | 30079TLV_TYPE_EXCEPTION_STRING = TLV_META_TYPE_STRING | 3018081TLV_TYPE_LIBRARY_PATH = TLV_META_TYPE_STRING | 40082TLV_TYPE_TARGET_PATH = TLV_META_TYPE_STRING | 40183TLV_TYPE_MIGRATE_PID = TLV_META_TYPE_UINT | 40284TLV_TYPE_MIGRATE_PAYLOAD = TLV_META_TYPE_RAW | 40485TLV_TYPE_MIGRATE_ARCH = TLV_META_TYPE_UINT | 40586TLV_TYPE_MIGRATE_BASE_ADDR = TLV_META_TYPE_UINT | 40787TLV_TYPE_MIGRATE_ENTRY_POINT = TLV_META_TYPE_UINT | 40888TLV_TYPE_MIGRATE_SOCKET_PATH = TLV_META_TYPE_STRING | 40989TLV_TYPE_MIGRATE_STUB = TLV_META_TYPE_RAW | 41190TLV_TYPE_LIB_LOADER_NAME = TLV_META_TYPE_STRING | 41291TLV_TYPE_LIB_LOADER_ORDINAL = TLV_META_TYPE_UINT | 4139293TLV_TYPE_TRANS_TYPE = TLV_META_TYPE_UINT | 43094TLV_TYPE_TRANS_URL = TLV_META_TYPE_STRING | 43195TLV_TYPE_TRANS_UA = TLV_META_TYPE_STRING | 43296TLV_TYPE_TRANS_COMM_TIMEOUT = TLV_META_TYPE_UINT | 43397TLV_TYPE_TRANS_SESSION_EXP = TLV_META_TYPE_UINT | 43498TLV_TYPE_TRANS_CERT_HASH = TLV_META_TYPE_RAW | 43599TLV_TYPE_TRANS_PROXY_HOST = TLV_META_TYPE_STRING | 436100TLV_TYPE_TRANS_PROXY_USER = TLV_META_TYPE_STRING | 437101TLV_TYPE_TRANS_PROXY_PASS = TLV_META_TYPE_STRING | 438102TLV_TYPE_TRANS_RETRY_TOTAL = TLV_META_TYPE_UINT | 439103TLV_TYPE_TRANS_RETRY_WAIT = TLV_META_TYPE_UINT | 440104TLV_TYPE_TRANS_HEADERS = TLV_META_TYPE_STRING | 441105TLV_TYPE_TRANS_GROUP = TLV_META_TYPE_GROUP | 442106107TLV_TYPE_MACHINE_ID = TLV_META_TYPE_STRING | 460108TLV_TYPE_UUID = TLV_META_TYPE_RAW | 461109TLV_TYPE_SESSION_GUID = TLV_META_TYPE_RAW | 462110111TLV_TYPE_RSA_PUB_KEY = TLV_META_TYPE_RAW | 550112TLV_TYPE_SYM_KEY_TYPE = TLV_META_TYPE_UINT | 551113TLV_TYPE_SYM_KEY = TLV_META_TYPE_RAW | 552114TLV_TYPE_ENC_SYM_KEY = TLV_META_TYPE_RAW | 553115116#117# Pivots118#119TLV_TYPE_PIVOT_ID = TLV_META_TYPE_RAW | 650120TLV_TYPE_PIVOT_STAGE_DATA = TLV_META_TYPE_RAW | 651121TLV_TYPE_PIVOT_NAMED_PIPE_NAME = TLV_META_TYPE_STRING | 653122123124#125# Core flags126#127LOAD_LIBRARY_FLAG_ON_DISK = (1 << 0)128LOAD_LIBRARY_FLAG_EXTENSION = (1 << 1)129LOAD_LIBRARY_FLAG_LOCAL = (1 << 2)130131#132# Sane defaults133#134GUID_SIZE = 16135NULL_GUID = "\x00" * GUID_SIZE136137def self.generate_command_id_map_c138id_map = CommandMapper.get_commands(*%w{139core140stdapi141priv142extapi143sniffer144winpmem145kiwi146unhook147espia148incognito149python150powershell151lanattacks152peinjector153})154155command_ids = id_map.map {|k, v| "#define COMMAND_ID_#{k.upcase} #{v}"}156%Q^157/*!158* @file common_command_ids.h159* @brief Declarations of command ID values160* @description This file was generated #{::Time.now.utc}. Do not modify directly.161*/162#ifndef _METERPRETER_SOURCE_COMMON_COMMAND_IDS_H163#define _METERPRETER_SOURCE_COMMON_COMMAND_IDS_H164165#{command_ids.join("\n")}166167#endif168^169end170171def self.generate_command_id_map_java172id_map = CommandMapper.get_commands(*%w{ core stdapi })173command_ids = id_map.map {|k, v| " public static final int #{k.upcase} = #{v};"}174%Q^175package com.metasploit.meterpreter.command;176177/**178* All supported Command Identifiers179*180* @author Generated by a tool @ #{::Time.now.utc}181*/182public interface CommandId {183#{command_ids.join("\n")}184}185^186end187188def self.generate_command_id_map_php_lib(lib, id_map)189command_ids = id_map.map {|k, v| "define('COMMAND_ID_#{k.upcase}', #{v});"}190%Q^191# ---------------------------------------------------------------192# --- THIS CONTENT WAS GENERATED BY A TOOL @ #{::Time.now.utc}193# IDs for #{lib}194#{command_ids.join("\n")}195# ---------------------------------------------------------------196^197end198199def self.generate_command_id_map_php200%Q^201#{self.generate_command_id_map_php_lib('metsrv', CommandMapper.get_commands('core'))}202203#{self.generate_command_id_map_php_lib('stdapi', CommandMapper.get_commands('stdapi'))}204^205end206207def self.generate_command_id_map_python208id_map = CommandMapper.get_commands(*%w{ core stdapi })209command_ids = id_map.map {|k, v| " (#{v}, '#{k.downcase}'),"}210%Q^211# ---------------------------------------------------------------212# --- THIS CONTENT WAS GENERATED BY A TOOL @ #{::Time.now.utc}213COMMAND_IDS = (214#{command_ids.join("\n")}215)216# ---------------------------------------------------------------217^218end219220def self.generate_command_id_map_python_extension221id_map = CommandMapper.get_commands(*%w{222core223stdapi224priv225extapi226sniffer227winpmem228kiwi229unhook230espia231incognito232python233powershell234lanattacks235peinjector236})237command_ids = id_map.map {|k, v| "COMMAND_ID_#{k.upcase} = #{v}"}238%Q^239# ---------------------------------------------------------------240# --- THIS CONTENT WAS GENERATED BY A TOOL @ #{::Time.now.utc}241242#{command_ids.join("\n")}243244# ---------------------------------------------------------------245^246end247248def self.generate_command_id_map_csharp249id_map = CommandMapper.get_commands(*%w{250core251stdapi252priv253extapi254sniffer255winpmem256kiwi257unhook258espia259incognito260python261powershell262lanattacks263peinjector264})265command_ids = id_map.map {|k, v| "#{k.split('_').map(&:capitalize).join} = #{v},"}266%Q^267/// <summary>268// This content was generated by a tool @ #{::Time.now.utc}269/// </summary>270namespace MSF.Powershell.Meterpreter271{272public enum CommandId273{274#{command_ids.join("\n ")}275}276}277^278end279280###281#282# Base TLV (Type-Length-Value) class283#284###285class Tlv286attr_accessor :type, :value, :compress287288HEADER_SIZE = 8289290##291#292# Constructor293#294##295296#297# Returns an instance of a TLV.298#299def initialize(type, value = nil, compress=false)300@type = type301@compress = compress302303if (value != nil)304if (type & TLV_META_TYPE_STRING == TLV_META_TYPE_STRING)305if (value.kind_of?(Integer))306@value = value.to_s307else308@value = value.dup309end310else311@value = value312end313end314end315316def _tlv_type_string(value)317tlv_names = ::Rex::Post::Meterpreter::CommandMapper.get_tlv_names(value).map { |name| name.to_s.gsub('TLV_TYPE_', '').gsub('PACKET_TYPE_', '') }318319case tlv_names.length320when 0321"unknown-#{value}"322when 1323tlv_names.first324else325# In the off-chance we have multiple TLV types which have the same value326# https://github.com/rapid7/metasploit-framework/issues/16267327# Sort it to ensure consistency across tests328"oneOf(#{tlv_names.sort_by(&:to_s).join(',')})"329end330end331332def inspect333utype = type ^ TLV_META_TYPE_COMPRESSED334group = false335meta = case (utype & TLV_META_MASK)336when TLV_META_TYPE_STRING; "STRING"337when TLV_META_TYPE_UINT; "INT"338when TLV_META_TYPE_RAW; "RAW"339when TLV_META_TYPE_BOOL; "BOOL"340when TLV_META_TYPE_QWORD; "QWORD"341when TLV_META_TYPE_GROUP; group=true; "GROUP"342when TLV_META_TYPE_COMPLEX; "COMPLEX"343else; 'unknown-meta-type'344end345346stype = case type347when PACKET_TYPE_REQUEST; 'Request'348when PACKET_TYPE_RESPONSE; 'Response'349else; _tlv_type_string(type)350end351352group ||= (self.class.to_s =~ /Packet/)353if group354has_command_ids = type == PACKET_TYPE_RESPONSE && (self.method == COMMAND_ID_CORE_ENUMEXTCMD || self.method == COMMAND_ID_CORE_LOADLIB)355if has_command_ids356longest_command_id = self.get_tlvs(TLV_TYPE_UINT).map(&:value).max357longest_command_id_length = longest_command_id.to_s.length358end359360tlvs_inspect = "tlvs=[\n"361@tlvs.each { |t|362if t.type == TLV_TYPE_UINT && has_command_ids && longest_command_id_length363command_name = ::Rex::Post::Meterpreter::CommandMapper.get_command_name(t.value)364command_output = "command=#{command_name}>\n"365this_value_length = t.value.to_s.length366adjusted_command_name = command_output.rjust(command_output.length + longest_command_id_length - this_value_length)367tlvs_inspect << " #{t.inspect.gsub(/>$/, '')} " << adjusted_command_name368else369tlvs_inspect << " #{t.inspect}\n"370end371}372tlvs_inspect << "]"373else374val = value.inspect375# Known list of datatypes that shouldn't be truncated, as their values are useful when debugging376is_val_truncation_allowed = ![377Rex::Post::Meterpreter::TLV_TYPE_UUID,378Rex::Post::Meterpreter::Extensions::Priv::TLV_TYPE_FS_FILE_PATH,379Rex::Post::Meterpreter::Extensions::Priv::TLV_TYPE_FS_SRC_FILE_PATH,380Rex::Post::Meterpreter::Extensions::Stdapi::TLV_TYPE_FILE_PATH,381Rex::Post::Meterpreter::Extensions::Stdapi::TLV_TYPE_DIRECTORY_PATH,382Rex::Post::Meterpreter::Extensions::Stdapi::TLV_TYPE_STAT_BUF,383Rex::Post::Meterpreter::Extensions::Stdapi::TLV_TYPE_PROCESS_PATH,384].include?(type)385if is_val_truncation_allowed && val.length > 50386val = val[0,50] + ' ..."'387end388tlvs_inspect = "meta=#{meta.ljust(10)} value=#{val}"389if type == TLV_TYPE_COMMAND_ID390begin391command_name = ::Rex::Post::Meterpreter::CommandMapper.get_command_name(value)392rescue393command_name = nil394end395tlvs_inspect <<= " command=#{command_name || 'unknown'}"396end397end398"#<#{self.class} type=#{stype.ljust(15)} #{tlvs_inspect}>"399end400401##402#403# Conditionals404#405##406407#408# Checks to see if a TLVs meta type is equivalent to the meta type passed.409#410def meta_type?(meta)411return (self.type & meta == meta)412end413414#415# Checks to see if the TLVs type is equivalent to the type passed.416#417def type?(type)418return self.type == type419end420421#422# Checks to see if the TLVs value is equivalent to the value passed.423#424def value?(value)425return self.value == value426end427428##429#430# Serializers431#432##433434#435# Converts the TLV to raw.436#437def to_r438# Forcibly convert to ASCII-8BIT encoding439raw = value.to_s.unpack("C*").pack("C*")440441if (self.type & TLV_META_TYPE_STRING == TLV_META_TYPE_STRING)442raw += "\x00"443elsif (self.type & TLV_META_TYPE_UINT == TLV_META_TYPE_UINT)444raw = [value].pack("N")445elsif (self.type & TLV_META_TYPE_QWORD == TLV_META_TYPE_QWORD)446raw = [ self.htonq( value.to_i ) ].pack("Q<")447elsif (self.type & TLV_META_TYPE_BOOL == TLV_META_TYPE_BOOL)448if (value == true)449raw = [1].pack("c")450else451raw = [0].pack("c")452end453end454455# check if the tlv is to be compressed...456if @compress457raw_uncompressed = raw458# compress the raw data459raw_compressed = Rex::Text.zlib_deflate( raw_uncompressed )460# check we have actually made the raw data smaller...461# (small blobs often compress slightly larger then the original)462# if the compressed data is not smaller, we dont use the compressed data463if( raw_compressed.length < raw_uncompressed.length )464# if so, set the TLV's type to indicate compression is used465self.type = self.type | TLV_META_TYPE_COMPRESSED466# update the raw data with the uncompressed data length + compressed data467# (we include the uncompressed data length as the C side will need to know this for decompression)468raw = [ raw_uncompressed.length ].pack("N") + raw_compressed469end470end471472[raw.length + HEADER_SIZE, self.type].pack("NN") + raw473end474475#476# Translates the raw format of the TLV into a sanitize version.477#478def from_r(raw)479self.value = nil480481length, self.type = raw.unpack("NN");482483# check if the tlv value has been compressed...484if( self.type & TLV_META_TYPE_COMPRESSED == TLV_META_TYPE_COMPRESSED )485# set this TLV as using compression486@compress = true487# remove the TLV_META_TYPE_COMPRESSED flag from the tlv type to restore the488# tlv type to its original, allowing for transparent data compression.489self.type = self.type ^ TLV_META_TYPE_COMPRESSED490# decompress the compressed data (skipping the length and type DWORD's)491raw_decompressed = Rex::Text.zlib_inflate( raw[HEADER_SIZE..length-1] )492# update the length to reflect the decompressed data length (+HEADER_SIZE for the length and type DWORD's)493length = raw_decompressed.length + HEADER_SIZE494# update the raw buffer with the new length, decompressed data and updated type.495raw = [length, self.type].pack("NN") + raw_decompressed496end497498if (self.type & TLV_META_TYPE_STRING == TLV_META_TYPE_STRING)499if (raw.length > 0)500self.value = raw[HEADER_SIZE..length-2]501else502self.value = nil503end504elsif (self.type & TLV_META_TYPE_UINT == TLV_META_TYPE_UINT)505self.value = raw.unpack("NNN")[2]506elsif (self.type & TLV_META_TYPE_QWORD == TLV_META_TYPE_QWORD)507self.value = raw.unpack("NNQ<")[2]508self.value = self.ntohq( self.value )509elsif (self.type & TLV_META_TYPE_BOOL == TLV_META_TYPE_BOOL)510self.value = raw.unpack("NNc")[2]511512if (self.value == 1)513self.value = true514else515self.value = false516end517else518self.value = raw[HEADER_SIZE..length-1]519end520521length522end523524protected525526def htonq(value)527if [1].pack( 's' ) == [1].pack('n')528return value529else530[value].pack('Q<').reverse.unpack('Q<').first531end532end533534def ntohq(value)535htonq(value)536end537538end539540###541#542# Group TLVs contain zero or more TLVs543#544###545class GroupTlv < Tlv546attr_accessor :tlvs547548##549#550# Constructor551#552##553554#555# Initializes the group TLV container to the supplied type556# and creates an empty TLV array.557#558def initialize(type)559super(type)560561self.tlvs = []562end563564##565#566# Group-based TLV accessors567#568##569570#571# Enumerates TLVs of the supplied type.572#573def each(type = TLV_TYPE_ANY, &block)574get_tlvs(type).each(&block)575end576577#578# Synonym for each.579#580def each_tlv(type = TLV_TYPE_ANY, &block)581each(type, &block)582end583584#585# Enumerates TLVs of a supplied type with indexes.586#587def each_with_index(type = TLV_TYPE_ANY, &block)588get_tlvs(type).each_with_index(&block)589end590591#592# Synonym for each_with_index.593#594def each_tlv_with_index(type = TLV_TYPE_ANY, &block)595each_with_index(type, block)596end597598#599# Returns an array of TLVs for the given type.600#601def get_tlvs(type)602if type == TLV_TYPE_ANY603self.tlvs604else605type_tlvs = []606607self.tlvs.each() { |tlv|608if (tlv.type?(type))609type_tlvs << tlv610end611}612613type_tlvs614end615end616617##618#619# TLV management620#621##622623#624# Adds a TLV of a given type and value.625#626def add_tlv(type, value = nil, replace = false, compress=false)627628# If we should replace any TLVs with the same type...remove them first629if replace630each(type) { |tlv|631if (tlv.type == type)632self.tlvs.delete(tlv)633end634}635end636637if (type & TLV_META_TYPE_GROUP == TLV_META_TYPE_GROUP)638tlv = GroupTlv.new(type)639else640tlv = Tlv.new(type, value, compress)641end642643self.tlvs << tlv644645tlv646end647648#649# Adds zero or more TLVs to the packet.650#651def add_tlvs(tlvs)652if tlvs653tlvs.each { |tlv|654add_tlv(tlv['type'], tlv['value'])655}656end657end658659#660# Gets the first TLV of a given type.661#662def get_tlv(type, index = 0)663type_tlvs = get_tlvs(type)664665if type_tlvs.length > index666type_tlvs[index]667else668nil669end670671end672673#674# Returns the value of a TLV if it exists, otherwise nil.675#676def get_tlv_value(type, index = 0)677tlv = get_tlv(type, index)678679(tlv != nil) ? tlv.value : nil680end681682#683# Returns an array of values for all tlvs of type type.684#685def get_tlv_values(type)686get_tlvs(type).collect { |a| a.value }687end688689#690# Checks to see if the container has a TLV of a given type.691#692def has_tlv?(type)693get_tlv(type) != nil694end695696#697# Zeros out the array of TLVs.698#699def reset700self.tlvs = []701end702703##704#705# Serializers706#707##708709#710# Converts all of the TLVs in the TLV array to raw and prefixes them711# with a container TLV of this instance's TLV type.712#713def to_r714raw = ''715716self.each() { |tlv|717raw << tlv.to_r718}719720[raw.length + HEADER_SIZE, self.type].pack("NN") + raw721end722723#724# Converts the TLV group container from raw to all of the individual725# TLVs.726#727def from_r(raw)728offset = HEADER_SIZE729730# Reset the TLVs array731self.tlvs = []732self.type = raw.unpack("NN")[1]733734# Enumerate all of the TLVs735while offset < raw.length-1736737tlv = nil738739# Get the length and type740length, type = raw[offset..offset+HEADER_SIZE].unpack("NN")741742if (type & TLV_META_TYPE_GROUP == TLV_META_TYPE_GROUP)743tlv = GroupTlv.new(type)744else745tlv = Tlv.new(type)746end747748tlv.from_r(raw[offset..offset+length])749750# Insert it into the list of TLVs751tlvs << tlv752753# Move up754offset += length755end756end757758end759760###761#762# The logical meterpreter packet class763#764###765class Packet < GroupTlv766attr_accessor :created_at767attr_accessor :raw768attr_accessor :session_guid769attr_accessor :encrypt_flags770attr_accessor :length771772##773#774# The Packet container itself has a custom header that is slightly different than the775# typical TLV packets. The header contains the following:776#777# XOR KEY - 4 bytes778# Session GUID - 16 bytes779# Encrypt flags - 4 bytes780# Packet length - 4 bytes781# Packet type - 4 bytes782# Packet data - X bytes783#784# If the encrypt flags are zero, then the Packet data is just straight TLV values as785# per the normal TLV packet structure.786#787# If the encrypt flags are non-zer, then the Packet data is encrypted based on the scheme.788#789# Flag == 1 (AES256)790# IV - 16 bytes791# Encrypted data - X bytes792#793# The key that is required to decrypt the data is stored alongside the session data,794# and hence when the packet is initially parsed, only the header is accessed. The795# packet itself will need to be decrypted on the fly at the point that it is required796# and at that point the decryption key needs to be provided.797#798###799800XOR_KEY_SIZE = 4801ENCRYPTED_FLAGS_SIZE = 4802PACKET_LENGTH_SIZE = 4803PACKET_TYPE_SIZE = 4804PACKET_HEADER_SIZE = XOR_KEY_SIZE + GUID_SIZE + ENCRYPTED_FLAGS_SIZE + PACKET_LENGTH_SIZE + PACKET_TYPE_SIZE805806AES_IV_SIZE = 16807808ENC_FLAG_NONE = 0x0809ENC_FLAG_AES256 = 0x1810ENC_FLAG_AES128 = 0x2811812##813#814# Factory815#816##817818#819# Creates a request with the supplied method.820#821def Packet.create_request(method = nil)822Packet.new(PACKET_TYPE_REQUEST, method)823end824825#826# Creates a response to a request if one is provided.827#828def Packet.create_response(request = nil)829response_type = PACKET_TYPE_RESPONSE830method = nil831id = nil832833if (request)834if (request.type?(PACKET_TYPE_PLAIN_REQUEST))835response_type = PACKET_TYPE_PLAIN_RESPONSE836end837838method = request.method839840if request.has_tlv?(TLV_TYPE_REQUEST_ID)841id = request.get_tlv_value(TLV_TYPE_REQUEST_ID)842end843end844845packet = Packet.new(response_type, method)846847if id848packet.add_tlv(TLV_TYPE_REQUEST_ID, id)849end850851packet852end853854##855#856# Constructor857#858##859860#861# Initializes the packet to the supplied packet type and method,862# if any. If the packet is a request, a request identifier is863# created.864#865def initialize(type = nil, method = nil)866super(type)867868if method869self.method = method870end871872self.created_at = ::Time.now873self.raw = ''874875# If it's a request, generate a random request identifier876if ((type == PACKET_TYPE_REQUEST) ||877(type == PACKET_TYPE_PLAIN_REQUEST))878rid = ''87988032.times { |val| rid << rand(10).to_s }881882add_tlv(TLV_TYPE_REQUEST_ID, rid)883end884end885886def add_raw(bytes)887self.raw << bytes888end889890def raw_bytes_required891# if we have the xor bytes and length ...892if self.raw.length >= PACKET_HEADER_SIZE893# return a value based on the length of the data indicated by894# the header895xor_key = self.raw.unpack('a4')[0]896decoded_bytes = xor_bytes(xor_key, raw[0, PACKET_HEADER_SIZE])897_, _, _, length, _ = decoded_bytes.unpack('a4a16NNN')898length + PACKET_HEADER_SIZE - HEADER_SIZE - self.raw.length899else900# Otherwise ask for the remaining bytes for the metadata to get the packet length901# So we can do the rest of the calculation next time902PACKET_HEADER_SIZE - self.raw.length903end904end905906def aes_encrypt(key, data)907size = key.length * 8908raise ArgumentError.new('AES key width must be 128 or 256 bits') unless (size == 128 || size == 256)909# Create the required cipher instance910aes = OpenSSL::Cipher.new("AES-#{size}-CBC")911# Generate a truly random IV912iv = aes.random_iv913914# set up the encryption915aes.encrypt916aes.key = key917aes.iv = iv918919# encrypt and return the IV along with the result920return iv, aes.update(data) + aes.final921end922923def aes_decrypt(key, iv, data)924size = key.length * 8925raise ArgumentError.new('AES key width must be 128 or 256 bits') unless (size == 128 || size == 256)926# Create the required cipher instance927aes = OpenSSL::Cipher.new("AES-#{size}-CBC")928# Generate a truly random IV929930# set up the encryption931aes.decrypt932aes.key = key933aes.iv = iv934935# decrypt!936aes.update(data) + aes.final937end938939#940# Override the function that creates the raw byte stream for941# sending so that it generates an XOR key, uses it to scramble942# the serialized TLV content, and then returns the key plus the943# scrambled data as the payload.944#945def to_r(session_guid = nil, key = nil)946xor_key = (rand(254) + 1).chr + (rand(254) + 1).chr + (rand(254) + 1).chr + (rand(254) + 1).chr947948raw = (session_guid || NULL_GUID).dup949tlv_data = GroupTlv.instance_method(:to_r).bind(self).call950951if key && key[:key] && (key[:type] == ENC_FLAG_AES128 || key[:type] == ENC_FLAG_AES256)952# encrypt the data, but not include the length and type953iv, ciphertext = aes_encrypt(key[:key], tlv_data[HEADER_SIZE..-1])954# now manually add the length/type/iv/ciphertext955raw << [key[:type], iv.length + ciphertext.length + HEADER_SIZE, self.type, iv, ciphertext].pack('NNNA*A*')956else957raw << [ENC_FLAG_NONE, tlv_data].pack('NA*')958end959960# return the xor'd result with the key961xor_key + xor_bytes(xor_key, raw)962end963964#965# Decrypt the packet based on the content of the encryption flags.966#967def decrypt_packet(key, encrypt_flags, data)968# TODO: throw an error if the expected encryption isn't the same as the given969# as this could be an indication of hijacking or side-channel packet addition970# as highlighted by Justin Steven on github.971if key && key[:key] && key[:type] && encrypt_flags == key[:type] && (encrypt_flags == ENC_FLAG_AES128 || encrypt_flags == ENC_FLAG_AES256)972iv = data[0, AES_IV_SIZE]973aes_decrypt(key[:key], iv, data[iv.length..-1])974else975data976end977end978979def parse_header!980xor_key = self.raw.unpack('a4')[0]981data = xor_bytes(xor_key, self.raw[0..PACKET_HEADER_SIZE])982_, self.session_guid, self.encrypt_flags, self.length, self.type = data.unpack('a4a16NNN')983end984985#986# Override the function that reads from a raw byte stream so987# that the XORing of data is included in the process prior to988# passing it on to the default functionality that can parse989# the TLV values.990#991def from_r(key=nil)992self.parse_header!993xor_key = self.raw.unpack('a4')[0]994data = xor_bytes(xor_key, self.raw[PACKET_HEADER_SIZE..-1])995raw = decrypt_packet(key, self.encrypt_flags, data)996super([self.length, self.type, raw].pack('NNA*'))997end998999#1000# Xor a set of bytes with a given XOR key.1001#1002def xor_bytes(xor_key, bytes)1003xor_key = xor_key.bytes1004result = ''1005i = 01006bytes.each_byte do |b|1007result << (b ^ xor_key[i % xor_key.length]).chr1008i += 11009end1010result1011end10121013##1014#1015# Conditionals1016#1017##10181019#1020# Checks to see if the packet is a response.1021#1022def response?1023(self.type == PACKET_TYPE_RESPONSE || self.type == PACKET_TYPE_PLAIN_RESPONSE)1024end10251026##1027#1028# Accessors1029#1030##10311032#1033# Checks to see if the packet's method is equal to the supplied method.1034#1035def method?(method)1036(get_tlv_value(TLV_TYPE_COMMAND_ID) == method)1037end10381039#1040# Sets the packet's method TLV to the method supplied.1041#1042def method=(method)1043raise ArgumentError.new("Packet.method must be an integer. Current value is #{method}") unless method.is_a?(Integer)1044add_tlv(TLV_TYPE_COMMAND_ID, method, true)1045end10461047#1048# Returns the value of the packet's method TLV.1049#1050def method1051get_tlv_value(TLV_TYPE_COMMAND_ID)1052end10531054#1055# Checks to see if the packet's result value is equal to the supplied1056# result.1057#1058def result?(result)1059(get_tlv_value(TLV_TYPE_RESULT) == result)1060end10611062#1063# Sets the packet's result TLV.1064#1065def result=(result)1066add_tlv(TLV_TYPE_RESULT, result, true)1067end10681069#1070# Gets the value of the packet's result TLV.1071#1072def result1073get_tlv_value(TLV_TYPE_RESULT)1074end10751076#1077# Gets the value of the packet's request identifier TLV.1078#1079def rid1080get_tlv_value(TLV_TYPE_REQUEST_ID)1081end1082end108310841085end; end; end108610871088