Files
metasploit-gs/lib/msf/core/exploit/git/packfile.rb
T
2021-08-12 10:18:12 -05:00

196 lines
5.2 KiB
Ruby

module Msf
module Exploit::Git
SIGNATURE = 'PACK'
VERSION = 2
##
# object types
##
OBJ_COMMIT = 1
OBJ_TREE = 2
OBJ_BLOB = 3
OBJ_TAG = 4
# ? = 5
# type 5 is reserved
# see: https://git-scm.com/docs/pack-format
OBJ_OFS_DELTA = 6
OBJ_REF_DELTA = 7
class Packfile
attr_reader :version, :git_objects, :data, :checksum
def initialize(version = nil, objs)
@version = version.nil? ? VERSION : version.to_i
@git_objects = objs.kind_of?(Array) ? objs : [ objs ]
@data = header
@data << format_objects
@checksum = Digest::SHA1.hexdigest(@data)
@data << [ @checksum ].pack('H*')
end
def header
hdr = SIGNATURE
hdr << [ @version ].pack('N')
hdr << [ @git_objects.length ].pack('N')
hdr
end
# Each object has a variable-sized
# header, with the size being determined
# by the length of the object's original,
# uncompressed content
def format_objects
type = 0
obj_stream = []
@git_objects.each do |obj|
byte_amt = 1
obj_data_size = obj.content.length
case obj.type
when 'blob'
type = OBJ_BLOB
when 'tree'
type = OBJ_TREE
when 'commit'
type = OBJ_COMMIT
end
num_bits = 0
num = obj_data_size
while num != 0
num /= 2
num_bits += 1
end
# the first byte can only hold
# four bits of the size of the
# object's content since the
# leading bits are reserved for
# value of MSB and object type
if num_bits > 4
if num_bits > 11
byte_amt = num_bits / 7
byte_amt += 1 if (num_bits % 7 > 0)
else
byte_amt = 2
end
end
shift = 0
(1..byte_amt).each do |byte|
curr_byte = 0
# set msb if needed
if byte < byte_amt
curr_byte |= 128
end
# set the object type
# set last four bits for content size
if byte == 1
curr_byte |= (type << 4)
curr_byte |= (obj_data_size & 15)
else
curr_byte = (obj_data_size >> 4 >> shift) & 127
shift += 7
end
obj_stream << [ curr_byte ].pack('C*')
end
# Since the object type is denoted in the preceding
# info, we only store the compressed object data
obj_stream << Rex::Text.zlib_deflate(obj.content, Zlib::DEFAULT_COMPRESSION)
end
obj_stream = obj_stream.join
end
def self.read_packfile(data)
return nil unless data && !data.empty?
pack_start = data.index('PACK')
return nil unless pack_start
data = data[pack_start..-1]
version = data[4..7].unpack('N').first
obj_count = data[8..11].unpack('N').first
puts("Reading packfile containing #{obj_count} objects")
curr_pos = 12
type = ''
pack_objs = []
(1..obj_count).each do |obj_index|
# determine the current object's type first
first_byte = data[curr_pos].unpack('C').first
num_type = (first_byte & 0b01110000) >> 4
case num_type
when OBJ_COMMIT
type = 'commit'
when OBJ_TREE
type = 'tree'
when OBJ_BLOB
type = 'blob'
when OBJ_OFS_DELTA
type = 'ofs-delta'
when OBJ_REF_DELTA
type = 'ref-delta'
end
# now determine the size of the object's uncompressed data
shift = 4
curr_byte = first_byte
size = curr_byte & 0b00001111
keep_reading = false
puts "Current byte: #{curr_byte}"
if curr_byte >= 128
puts 'KEEP READING'
keep_reading = true
end
curr_pos += 1
while keep_reading
curr_byte = data[curr_pos].unpack('C').first
puts "Current byte: #{curr_byte}"
if curr_byte < 128
keep_reading = false
end
size = (curr_byte << shift) | size
shift += 7
curr_pos += 1
end
# now decompress content and create Git object
case type
when 'ofs-delta'
require 'pry';binding.pry
when 'ref-delta'
#require 'pry';binding.pry
base_obj_sha = data[curr_pos..curr_pos+19].unpack('H*').first
curr_pos += 20
end
puts "Decompressing content, size #{size}, for object: #{obj_index}"
puts "Position number: #{curr_pos}"
puts "Byte at current position: #{data[curr_pos].unpack('C').first}"
content = Rex::Text.zlib_inflate(data[curr_pos..-1])
puts "Decompressed content: #{content}, length: #{content.length}"
sha1, compressed = GitObject.build_object(type, content)
pack_objs << GitObject.new(type, content, sha1, compressed)
# update curr_pos to point to next obj header
puts "Compressing object ##{obj_index} content in order to find next object"
compressed_len = Rex::Text.zlib_deflate(content, Zlib::DEFAULT_COMPRESSION).length
curr_pos = curr_pos + compressed_len
end
pack_objs
end
end
end
end