augure-profiler 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a668c990f083c8eb2d00f67ce45ccb99fb37525ee1013e484e63cf6b00f0b561
4
- data.tar.gz: f86407235e580f36e236b52b1c406dfb7159b5d9dea733ec5d293ff68ac54678
3
+ metadata.gz: 68f4e4a0deb75c047b7a76b09695771e42a28d1b81a36d7ecabf211061022078
4
+ data.tar.gz: 178c357a361d80a14934c4d0fa871f545fbd0c680ab6708f47e0f6e486f5b2df
5
5
  SHA512:
6
- metadata.gz: 1493d1d5d121787f04ce661350f6eb89922cfe64555f15e9cf6cd90c7d543f45cd7d7e8eff4fcfaf7b4ad9b1492f7fd362cce5b043f10d80e319895824f2cf5f
7
- data.tar.gz: 3a19f8acd46eaa23537b10bf279442631e93a567fd22d81e5aec918e50b63c5ae782874c8779836afa3815f6f5a21bd7c8169fd84e0e2450b182ff214c4d13f4
6
+ metadata.gz: a78bc1cdb1adf17cc25ca268508d6462b47da49466ffdedd4bfdbaae2be694c2ba74898bc822f949c98ad076ca1071f776b7fe2be6a3bbf6b6ed97484f171729
7
+ data.tar.gz: f99e40af9b29d45c48dc1ae999f4c5b18403b251ce854b78a14b83ab50e58c5812878620c3668501e1357aae7368397838b3e25737516807a7d95ffd7d585c68
@@ -23,6 +23,7 @@ module AugureProfiler
23
23
  ["int80_ret", "cd 80 c3"], # int 0x80 ; ret
24
24
  ["pop_rdi_ret", "5f c3"], # pop rdi ; ret
25
25
  ["pop_rsi_ret", "5e c3"], # pop rsi ; ret
26
+ ["pop_rsi_r15_ret", "5e 41 5f c3"], # pop rsi ; pop r15 ; ret (the callme flavor)
26
27
  ["pop_rdx_ret", "5a c3"], # pop rdx ; ret
27
28
  ["pop_rax_ret", "58 c3"], # pop rax ; ret
28
29
  ["JMP_STACK", "ff e4"], # jmp esp/rsp - renamed per arch
@@ -49,6 +50,34 @@ module AugureProfiler
49
50
  facts
50
51
  end
51
52
 
53
+ # the csu byte signatures, both known compiler flavors: the popper
54
+ # = pop rbx,rbp,r12,r13,r14,r15 ; ret (the chain re-enters at the
55
+ # pop rbx), the mov block ends in the csu call signature and starts
56
+ # 9 bytes earlier (three 3-byte register movs, REX.W/REX.WR 0x89
57
+ # opcodes - the mov order is the compiler's choice: r15->rdx on
58
+ # recent gcc, r13->rdx on the classic layout)
59
+ CSU_POPPER_BIN = ["5b5d415c415d415e415fc3"].pack("H*").freeze
60
+ CSU_CALL_BIN = ["41ff14dc"].pack("H*").freeze
61
+
62
+ def self.csu_gadget_addrs(bytes, base, extent)
63
+ win = bytes.is_a?(String) ? bytes.b : bytes.pack("C*").b
64
+ win = win.byteslice(0, extent) if extent && extent < win.bytesize
65
+ popper = win.index(CSU_POPPER_BIN)
66
+ mov = find_csu_mov(win)
67
+ {popper: popper ? base + popper : nil, mov: mov ? base + mov : nil}
68
+ end
69
+
70
+ def self.find_csu_mov(win)
71
+ off = 0
72
+ while (call = win.index(CSU_CALL_BIN, off))
73
+ start = call - 9
74
+ return start if start >= 0 && [0x4c, 0x44].include?(win.getbyte(start)) && win.getbyte(start + 1) == 0x89
75
+
76
+ off = call + 1
77
+ end
78
+ nil
79
+ end
80
+
52
81
  private
53
82
 
54
83
  def emit_checksec(facts)
@@ -90,6 +119,10 @@ module AugureProfiler
90
119
  # needs the address, not just the existence.
91
120
  @elf.symbols.each do |sym|
92
121
  next unless sym.name.to_s.match?(/\Awin\z|win\z|flag\z/)
122
+ # an IMPORT is not a win HERE: the ret2csu shape imports its
123
+ # ret2win from the companion library; the symbol's value is 0
124
+ # and a built [qword win] payload would jump to NULL
125
+ next if sym.shndx.to_s == "UNDEF"
93
126
 
94
127
  facts.add("target_function", "true")
95
128
  facts.add("win_symbol", sym.name, sym.value.to_i)
@@ -189,15 +222,39 @@ module AugureProfiler
189
222
  hits.map { |(_order, type, addr)| [type, addr] }
190
223
  end
191
224
 
192
- # Static binaries expose the __libc_csu_init universal gadgets under
193
- # well-known symbols; their presence IS the gadget fact.
225
+ # Static binaries expose the __libc_csu_init universal gadgets; their
226
+ # presence IS the gadget fact. The two useful sequences sit at
227
+ # compiler-dependent offsets INSIDE the function (the entry/+0x40
228
+ # guess broke on the newer mov rdx,r15 flavor), so both are located
229
+ # by byte signature within the symbol's extent: the popper fact aims
230
+ # at the pop rbx (the chain entry, the leading add rsp,8 stays one
231
+ # slot earlier for layouts that need it), the mov block by its call
232
+ # signature. The pack teaches the arg mapping for its target.
194
233
  def emit_csu_gadgets(facts)
195
- names = @elf.symbols.map(&:name).compact
196
- return unless names.include?("__libc_csu_init")
197
-
198
234
  sym = @elf.symbols.find { |s| s.name == "__libc_csu_init" }
199
- facts.add("gadget", "csu_popper", sym.value.to_i)
200
- facts.add("gadget", "csu_mov", sym.value.to_i + 0x40)
235
+ return unless sym
236
+
237
+ base = sym.value.to_i
238
+ extent = sym.size.to_i
239
+ extent = 0x100 if extent <= 0
240
+ section = executable_sections.find do |s|
241
+ s.addr.to_i <= base && base < s.addr.to_i + s.size.to_i
242
+ end
243
+ return unless section
244
+
245
+ window = bytes_slice(section, base - section.addr.to_i)
246
+ return if window.nil? || window.empty?
247
+
248
+ addrs = self.class.csu_gadget_addrs(window, base, extent)
249
+ facts.add("gadget", "csu_popper", addrs[:popper]) if addrs[:popper]
250
+ facts.add("gadget", "csu_mov", addrs[:mov]) if addrs[:mov]
251
+ end
252
+
253
+ def bytes_slice(section, from)
254
+ bytes = section_bytes(section)
255
+ return nil if bytes.nil?
256
+
257
+ bytes[from, bytes.size - from] || []
201
258
  end
202
259
  end
203
260
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AugureProfiler
4
- VERSION = "0.0.1"
4
+ VERSION = "0.0.2"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: augure-profiler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gregory Epp