pwntools 0.1.0 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +96 -15
- data/Rakefile +8 -2
- data/lib/pwn.rb +10 -7
- data/lib/pwnlib/abi.rb +61 -0
- data/lib/pwnlib/asm.rb +357 -0
- data/lib/pwnlib/constants/constant.rb +19 -3
- data/lib/pwnlib/constants/constants.rb +46 -20
- data/lib/pwnlib/constants/linux/amd64.rb +32 -1
- data/lib/pwnlib/constants/linux/i386.rb +2 -0
- data/lib/pwnlib/context.rb +128 -27
- data/lib/pwnlib/dynelf.rb +122 -54
- data/lib/pwnlib/elf/elf.rb +340 -0
- data/lib/pwnlib/errors.rb +31 -0
- data/lib/pwnlib/ext/array.rb +2 -1
- data/lib/pwnlib/ext/helper.rb +6 -5
- data/lib/pwnlib/ext/integer.rb +2 -1
- data/lib/pwnlib/ext/string.rb +3 -2
- data/lib/pwnlib/logger.rb +245 -0
- data/lib/pwnlib/memleak.rb +59 -29
- data/lib/pwnlib/pwn.rb +27 -9
- data/lib/pwnlib/reg_sort.rb +109 -110
- data/lib/pwnlib/runner.rb +53 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/common.rb +16 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/infloop.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/memcpy.rb +35 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/mov.rb +131 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/nop.rb +18 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/popad.rb +28 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/pushstr.rb +66 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/pushstr_array.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/ret.rb +33 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/setregs.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/cat.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/execve.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/exit.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/linux.rb +16 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/ls.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/open.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/sh.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/sleep.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/syscall.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/helper.rb +115 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/common.rb +16 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/infloop.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/memcpy.rb +34 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/mov.rb +93 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/nop.rb +18 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/pushstr.rb +41 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/pushstr_array.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/setregs.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/cat.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/execve.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/exit.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/linux.rb +16 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/ls.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/open.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/sh.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/sleep.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/syscall.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/common.rb +29 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/infloop.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/memcpy.rb +17 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/mov.rb +17 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/pushstr.rb +17 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/pushstr_array.rb +86 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/setregs.rb +84 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/cat.rb +54 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/execve.rb +72 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/exit.rb +34 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/linux.rb +16 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/ls.rb +67 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/open.rb +47 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/sh.rb +53 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/sleep.rb +52 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/syscall.rb +52 -0
- data/lib/pwnlib/shellcraft/registers.rb +148 -0
- data/lib/pwnlib/shellcraft/shellcraft.rb +73 -0
- data/lib/pwnlib/timer.rb +67 -0
- data/lib/pwnlib/tubes/buffer.rb +99 -0
- data/lib/pwnlib/tubes/process.rb +155 -0
- data/lib/pwnlib/tubes/serialtube.rb +114 -0
- data/lib/pwnlib/tubes/sock.rb +101 -0
- data/lib/pwnlib/tubes/tube.rb +442 -0
- data/lib/pwnlib/ui.rb +21 -0
- data/lib/pwnlib/util/cyclic.rb +97 -94
- data/lib/pwnlib/util/fiddling.rb +288 -220
- data/lib/pwnlib/util/getdents.rb +85 -0
- data/lib/pwnlib/util/hexdump.rb +116 -112
- data/lib/pwnlib/util/lists.rb +58 -0
- data/lib/pwnlib/util/packing.rb +223 -228
- data/lib/pwnlib/util/ruby.rb +19 -0
- data/lib/pwnlib/version.rb +3 -1
- data/test/abi_test.rb +22 -0
- data/test/asm_test.rb +177 -0
- data/test/constants/constant_test.rb +2 -0
- data/test/constants/constants_test.rb +5 -2
- data/test/context_test.rb +14 -3
- data/test/data/assembly/aarch64.s +19 -0
- data/test/data/assembly/amd64.s +21 -0
- data/test/data/assembly/arm.s +9 -0
- data/test/data/assembly/i386.s +21 -0
- data/test/data/assembly/mips.s +16 -0
- data/test/data/assembly/mips64.s +6 -0
- data/test/data/assembly/powerpc.s +18 -0
- data/test/data/assembly/powerpc64.s +36 -0
- data/test/data/assembly/sparc.s +33 -0
- data/test/data/assembly/sparc64.s +5 -0
- data/test/data/assembly/thumb.s +37 -0
- data/test/data/echo.rb +16 -0
- data/test/data/elfs/Makefile +24 -0
- data/test/data/elfs/amd64.frelro.elf +0 -0
- data/test/data/elfs/amd64.frelro.pie.elf +0 -0
- data/test/data/elfs/amd64.nrelro.elf +0 -0
- data/test/data/elfs/amd64.prelro.elf +0 -0
- data/test/data/elfs/amd64.static.elf +0 -0
- data/test/data/elfs/i386.frelro.pie.elf +0 -0
- data/test/data/elfs/i386.prelro.elf +0 -0
- data/test/data/elfs/source.cpp +19 -0
- data/test/data/flag +1 -0
- data/test/data/lib32/ld.so.2 +0 -0
- data/test/data/lib32/libc.so.6 +0 -0
- data/test/data/lib64/ld.so.2 +0 -0
- data/test/data/lib64/libc.so.6 +0 -0
- data/test/dynelf_test.rb +62 -25
- data/test/elf/elf_test.rb +147 -0
- data/test/ext_test.rb +4 -2
- data/test/files/use_pwn.rb +3 -6
- data/test/files/use_pwnlib.rb +2 -1
- data/test/full_file_test.rb +6 -0
- data/test/logger_test.rb +120 -0
- data/test/memleak_test.rb +5 -33
- data/test/reg_sort_test.rb +4 -1
- data/test/runner_test.rb +32 -0
- data/test/shellcraft/infloop_test.rb +27 -0
- data/test/shellcraft/linux/cat_test.rb +87 -0
- data/test/shellcraft/linux/ls_test.rb +109 -0
- data/test/shellcraft/linux/sh_test.rb +120 -0
- data/test/shellcraft/linux/sleep_test.rb +68 -0
- data/test/shellcraft/linux/syscalls/execve_test.rb +137 -0
- data/test/shellcraft/linux/syscalls/exit_test.rb +57 -0
- data/test/shellcraft/linux/syscalls/open_test.rb +87 -0
- data/test/shellcraft/linux/syscalls/syscall_test.rb +84 -0
- data/test/shellcraft/memcpy_test.rb +50 -0
- data/test/shellcraft/mov_test.rb +99 -0
- data/test/shellcraft/nop_test.rb +27 -0
- data/test/shellcraft/popad_test.rb +30 -0
- data/test/shellcraft/pushstr_array_test.rb +92 -0
- data/test/shellcraft/pushstr_test.rb +109 -0
- data/test/shellcraft/registers_test.rb +33 -0
- data/test/shellcraft/ret_test.rb +31 -0
- data/test/shellcraft/setregs_test.rb +63 -0
- data/test/shellcraft/shellcraft_test.rb +30 -0
- data/test/test_helper.rb +61 -2
- data/test/timer_test.rb +42 -0
- data/test/tubes/buffer_test.rb +46 -0
- data/test/tubes/process_test.rb +105 -0
- data/test/tubes/serialtube_test.rb +162 -0
- data/test/tubes/sock_test.rb +68 -0
- data/test/tubes/tube_test.rb +320 -0
- data/test/ui_test.rb +18 -0
- data/test/util/cyclic_test.rb +3 -1
- data/test/util/fiddling_test.rb +12 -3
- data/test/util/getdents_test.rb +33 -0
- data/test/util/hexdump_test.rb +9 -10
- data/test/util/lists_test.rb +22 -0
- data/test/util/packing_test.rb +5 -3
- metadata +357 -37
data/lib/pwnlib/dynelf.rb
CHANGED
@@ -1,60 +1,130 @@
|
|
1
1
|
# encoding: ASCII-8BIT
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'elftools'
|
2
5
|
|
3
6
|
require 'pwnlib/context'
|
4
7
|
require 'pwnlib/memleak'
|
5
8
|
require 'pwnlib/util/packing'
|
6
9
|
|
7
|
-
# TODO(hh): Use ELF datatype instead of magic offset
|
8
|
-
|
9
10
|
module Pwnlib
|
10
11
|
# DynELF class, resolve symbols in loaded, dynamically-linked ELF binaries.
|
11
|
-
# Given a function which can leak data at an arbitrary address,
|
12
|
-
# any symbol in any loaded library can be resolved.
|
12
|
+
# Given a function which can leak data at an arbitrary address, any symbol in any loaded library can be resolved.
|
13
13
|
class DynELF
|
14
|
-
|
15
|
-
DT_GNU_HASH = 0x6ffffef5
|
16
|
-
DT_HASH = 4
|
17
|
-
DT_STRTAB = 5
|
18
|
-
DT_SYMTAB = 6
|
19
|
-
|
20
|
-
attr_reader :libbase
|
14
|
+
attr_reader :libbase # @return [Integer] Base of lib.
|
21
15
|
|
16
|
+
# Instantiate a {Pwnlib::DynELF} object.
|
17
|
+
#
|
18
|
+
# @param [Integer] addr
|
19
|
+
# An address known to be inside the ELF.
|
20
|
+
#
|
21
|
+
# @yieldparam [Integer] leak_addr
|
22
|
+
# The start address that the leaker should leak from.
|
23
|
+
#
|
24
|
+
# @yieldreturn [String]
|
25
|
+
# A leaked non-empty byte string, starting from +leak_addr+.
|
22
26
|
def initialize(addr, &block)
|
23
27
|
@leak = ::Pwnlib::MemLeak.new(&block)
|
24
|
-
@libbase =
|
25
|
-
@elfclass = {
|
28
|
+
@libbase = find_base(addr)
|
29
|
+
@elfclass = { 0x1 => 32, 0x2 => 64 }[@leak.b(@libbase + 4)]
|
26
30
|
@elfword = @elfclass / 8
|
27
|
-
@unp = ->(x) { Util::Packing.public_send({ 32 => :u32, 64 => :u64 }[@elfclass], x) }
|
28
31
|
@dynamic = find_dynamic
|
29
32
|
@hshtab = @strtab = @symtab = nil
|
30
33
|
end
|
31
34
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
35
|
+
# Lookup a symbol from the ELF.
|
36
|
+
#
|
37
|
+
# @param [String] symbol
|
38
|
+
# The symbol name.
|
39
|
+
#
|
40
|
+
# @return [Integer, nil]
|
41
|
+
# The address of the symbol, or +nil+ if not found.
|
42
|
+
def lookup(symbol)
|
43
|
+
symbol = symbol.to_s
|
44
|
+
sym_size = { 32 => 16, 64 => 24 }[@elfclass]
|
45
|
+
# Leak GNU_HASH section header.
|
46
|
+
nbuckets = @leak.d(hshtab)
|
47
|
+
symndx = @leak.d(hshtab + 4)
|
48
|
+
maskwords = @leak.d(hshtab + 8)
|
49
|
+
|
50
|
+
l_gnu_buckets = hshtab + 16 + (@elfword * maskwords)
|
51
|
+
l_gnu_chain_zero = l_gnu_buckets + (4 * nbuckets) - (4 * symndx)
|
52
|
+
|
53
|
+
hsh = gnu_hash(symbol)
|
54
|
+
bucket = hsh % nbuckets
|
55
|
+
|
56
|
+
i = @leak.d(l_gnu_buckets + bucket * 4)
|
57
|
+
return nil if i.zero?
|
58
|
+
|
59
|
+
hsh2 = 0
|
60
|
+
while (hsh2 & 1).zero?
|
61
|
+
hsh2 = @leak.d(l_gnu_chain_zero + i * 4)
|
62
|
+
if ((hsh ^ hsh2) >> 1).zero?
|
63
|
+
sym = symtab + sym_size * i
|
64
|
+
st_name = @leak.d(sym)
|
65
|
+
name = @leak.n(strtab + st_name, symbol.length + 1)
|
66
|
+
if name == ("#{symbol}\x00")
|
67
|
+
offset = { 32 => 4, 64 => 8 }[@elfclass]
|
68
|
+
st_value = unpack(@leak.n(sym + offset, @elfword))
|
69
|
+
return @libbase + st_value
|
70
|
+
end
|
71
|
+
end
|
72
|
+
i += 1
|
73
|
+
end
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
|
77
|
+
# Leak the BuildID of the remote libc.so.
|
78
|
+
#
|
79
|
+
# @return [String?]
|
80
|
+
# Return BuildID in hex format or +nil+.
|
81
|
+
def build_id
|
82
|
+
build_id_offsets.each do |offset|
|
83
|
+
next unless @leak.n(@libbase + offset + 12, 4) == "GNU\x00"
|
84
|
+
|
85
|
+
return @leak.n(@libbase + offset + 16, 20).unpack1('H*')
|
86
|
+
end
|
87
|
+
nil
|
37
88
|
end
|
38
89
|
|
39
90
|
private
|
40
91
|
|
92
|
+
PAGE_SIZE = 0x1000
|
93
|
+
PAGE_MASK = ~(PAGE_SIZE - 1)
|
94
|
+
|
95
|
+
def unpack(x)
|
96
|
+
Util::Packing.public_send({ 32 => :u32, 64 => :u64 }[@elfclass], x)
|
97
|
+
end
|
98
|
+
|
41
99
|
# Function used to generated GNU-style hashes for strings.
|
42
100
|
def gnu_hash(s)
|
43
101
|
s.bytes.reduce(5381) { |acc, elem| (acc * 33 + elem) & 0xffffffff }
|
44
102
|
end
|
45
103
|
|
104
|
+
# Get the base address of the ELF, based on heuristic of finding ELF header.
|
105
|
+
# A known address in ELF should be given.
|
106
|
+
def find_base(ptr)
|
107
|
+
ptr &= PAGE_MASK
|
108
|
+
loop do
|
109
|
+
return @base = ptr if @leak.n(ptr, 4) == "\x7fELF"
|
110
|
+
|
111
|
+
ptr -= PAGE_SIZE
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
46
115
|
def find_dynamic
|
47
116
|
e_phoff_offset = { 32 => 28, 64 => 32 }[@elfclass]
|
48
|
-
e_phoff = @libbase +
|
117
|
+
e_phoff = @libbase + unpack(@leak.n(@libbase + e_phoff_offset, @elfword))
|
49
118
|
phdr_size = { 32 => 32, 64 => 56 }[@elfclass]
|
50
119
|
loop do
|
51
120
|
ptype = @leak.d(e_phoff)
|
52
|
-
break if ptype == PT_DYNAMIC
|
121
|
+
break if ptype == ELFTools::Constants::PT::PT_DYNAMIC
|
122
|
+
|
53
123
|
e_phoff += phdr_size
|
54
124
|
end
|
55
125
|
offset = { 32 => 8, 64 => 16 }[@elfclass]
|
56
|
-
dyn =
|
57
|
-
# Sometimes this is an offset instead of an address
|
126
|
+
dyn = unpack(@leak.n(e_phoff + offset, @elfword))
|
127
|
+
# Sometimes this is an offset instead of an address.
|
58
128
|
dyn += @libbase if (0...0x400000).cover?(dyn)
|
59
129
|
dyn
|
60
130
|
end
|
@@ -64,47 +134,45 @@ module Pwnlib
|
|
64
134
|
ptr = @dynamic
|
65
135
|
loop do
|
66
136
|
tmp = @leak.n(ptr, @elfword * 2)
|
67
|
-
d_tag =
|
68
|
-
d_addr =
|
137
|
+
d_tag = unpack(tmp[0, @elfword])
|
138
|
+
d_addr = unpack(tmp[@elfword, @elfword])
|
69
139
|
break if d_tag.zero?
|
70
140
|
return d_addr if tag == d_tag
|
141
|
+
|
71
142
|
ptr += dyn_size
|
72
143
|
end
|
73
144
|
nil
|
74
145
|
end
|
75
146
|
|
76
|
-
def
|
77
|
-
|
78
|
-
|
79
|
-
nbuckets = @leak.d(@hshtab)
|
80
|
-
symndx = @leak.d(@hshtab + 4)
|
81
|
-
maskwords = @leak.d(@hshtab + 8)
|
82
|
-
|
83
|
-
l_gnu_buckets = @hshtab + 16 + (@elfword * maskwords)
|
84
|
-
l_gnu_chain_zero = l_gnu_buckets + (4 * nbuckets) - (4 * symndx)
|
147
|
+
def hshtab
|
148
|
+
@hshtab ||= find_dt(ELFTools::Constants::DT::DT_GNU_HASH)
|
149
|
+
end
|
85
150
|
|
86
|
-
|
87
|
-
|
151
|
+
def strtab
|
152
|
+
@strtab ||= find_dt(ELFTools::Constants::DT::DT_STRTAB)
|
153
|
+
end
|
88
154
|
|
89
|
-
|
90
|
-
|
155
|
+
def symtab
|
156
|
+
@symtab ||= find_dt(ELFTools::Constants::DT::DT_SYMTAB)
|
157
|
+
end
|
91
158
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
nil
|
159
|
+
# Given the corpus of almost all libc to have been released on RedHat, Fedora, Ubuntu, Debian,
|
160
|
+
# etc. over the past several years, there is a strong possibility the GNU Build ID section will
|
161
|
+
# be at one of the specified addresses.
|
162
|
+
def build_id_offsets
|
163
|
+
{
|
164
|
+
i386: [0x174],
|
165
|
+
arm: [0x174],
|
166
|
+
thumb: [0x174],
|
167
|
+
aarch64: [0x238],
|
168
|
+
amd64: [0x270, 0x174],
|
169
|
+
powerpc: [0x174],
|
170
|
+
powerpc64: [0x238],
|
171
|
+
sparc: [0x174],
|
172
|
+
sparc64: [0x270]
|
173
|
+
}[context.arch.to_sym] || []
|
108
174
|
end
|
175
|
+
|
176
|
+
include Context
|
109
177
|
end
|
110
178
|
end
|
@@ -0,0 +1,340 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ostruct'
|
4
|
+
|
5
|
+
require 'elftools'
|
6
|
+
require 'one_gadget/one_gadget'
|
7
|
+
require 'rainbow'
|
8
|
+
|
9
|
+
require 'pwnlib/logger'
|
10
|
+
|
11
|
+
module Pwnlib
|
12
|
+
# ELF module includes classes for parsing an ELF file.
|
13
|
+
module ELF
|
14
|
+
# Main class for using {Pwnlib::ELF} module.
|
15
|
+
class ELF
|
16
|
+
# @return [OpenStruct] GOT symbols.
|
17
|
+
attr_reader :got
|
18
|
+
|
19
|
+
# @return [OpenStruct] PLT symbols.
|
20
|
+
attr_reader :plt
|
21
|
+
|
22
|
+
# @return [OpenStruct] All symbols.
|
23
|
+
attr_reader :symbols
|
24
|
+
|
25
|
+
# @return [Integer] Base address.
|
26
|
+
attr_reader :address
|
27
|
+
|
28
|
+
# Instantiate an {Pwnlib::ELF::ELF} object.
|
29
|
+
#
|
30
|
+
# Will show checksec information to stdout.
|
31
|
+
#
|
32
|
+
# @param [String] path
|
33
|
+
# The path to the ELF file.
|
34
|
+
# @param [Boolean] checksec
|
35
|
+
# The checksec information will be printed to stdout after ELF loaded. Pass +checksec: false+ to disable this
|
36
|
+
# feature.
|
37
|
+
#
|
38
|
+
# @example
|
39
|
+
# ELF.new('/lib/x86_64-linux-gnu/libc.so.6')
|
40
|
+
# # RELRO: Partial RELRO
|
41
|
+
# # Stack: No canary found
|
42
|
+
# # NX: NX enabled
|
43
|
+
# # PIE: PIE enabled
|
44
|
+
# #=> #<Pwnlib::ELF::ELF:0x00559bd670dcb8>
|
45
|
+
def initialize(path, checksec: true)
|
46
|
+
@path = File.realpath(path)
|
47
|
+
@elf_file = ELFTools::ELFFile.new(File.open(path, 'rb'))
|
48
|
+
load_got
|
49
|
+
load_plt
|
50
|
+
load_symbols
|
51
|
+
@address = base_address
|
52
|
+
@load_addr = @address
|
53
|
+
@one_gadgets = nil
|
54
|
+
show_info(@path) if checksec
|
55
|
+
end
|
56
|
+
|
57
|
+
# Set the base address.
|
58
|
+
#
|
59
|
+
# Values in following tables will be changed simultaneously:
|
60
|
+
# got
|
61
|
+
# plt
|
62
|
+
# symbols
|
63
|
+
# one_gadgets
|
64
|
+
#
|
65
|
+
# @param [Integer] val
|
66
|
+
# Address to be changed to.
|
67
|
+
#
|
68
|
+
# @return [Integer]
|
69
|
+
# The new address.
|
70
|
+
def address=(val)
|
71
|
+
old = @address
|
72
|
+
@address = val
|
73
|
+
[@got, @plt, @symbols].compact.each do |tbl|
|
74
|
+
tbl.each_pair { |k, _| tbl[k] += val - old }
|
75
|
+
end
|
76
|
+
@one_gadgets&.map! { |off| off + val - old }
|
77
|
+
end
|
78
|
+
|
79
|
+
# Return the protection information, wrapper with color codes.
|
80
|
+
#
|
81
|
+
# @return [String]
|
82
|
+
# The checksec information.
|
83
|
+
def checksec
|
84
|
+
[
|
85
|
+
'RELRO:'.ljust(10) + {
|
86
|
+
full: Rainbow('Full RELRO').green,
|
87
|
+
partial: Rainbow('Partial RELRO').yellow,
|
88
|
+
none: Rainbow('No RELRO').red
|
89
|
+
}[relro],
|
90
|
+
'Stack:'.ljust(10) + {
|
91
|
+
true => Rainbow('Canary found').green,
|
92
|
+
false => Rainbow('No canary found').red
|
93
|
+
}[canary?],
|
94
|
+
'NX:'.ljust(10) + {
|
95
|
+
true => Rainbow('NX enabled').green,
|
96
|
+
false => Rainbow('NX disabled').red
|
97
|
+
}[nx?],
|
98
|
+
'PIE:'.ljust(10) + {
|
99
|
+
true => Rainbow('PIE enabled').green,
|
100
|
+
false => Rainbow(format('No PIE (0x%x)', address)).red
|
101
|
+
}[pie?]
|
102
|
+
].join("\n")
|
103
|
+
end
|
104
|
+
|
105
|
+
# The method used in relro.
|
106
|
+
#
|
107
|
+
# @return [:full, :partial, :none]
|
108
|
+
def relro
|
109
|
+
return :none unless @elf_file.segment_by_type(:gnu_relro)
|
110
|
+
return :full if dynamic_tag(:bind_now)
|
111
|
+
|
112
|
+
flags = dynamic_tag(:flags)
|
113
|
+
return :full if flags && (flags.value & ::ELFTools::Constants::DF_BIND_NOW) != 0
|
114
|
+
|
115
|
+
flags1 = dynamic_tag(:flags_1)
|
116
|
+
return :full if flags1 && (flags1.value & ::ELFTools::Constants::DF_1_NOW) != 0
|
117
|
+
|
118
|
+
:partial
|
119
|
+
end
|
120
|
+
|
121
|
+
# Is this ELF file has canary?
|
122
|
+
#
|
123
|
+
# Actually judged by if +__stack_chk_fail+ in got symbols.
|
124
|
+
#
|
125
|
+
# @return [Boolean] Yes or not.
|
126
|
+
def canary?
|
127
|
+
@got.respond_to?('__stack_chk_fail') || @symbols.respond_to?('__stack_chk_fail')
|
128
|
+
end
|
129
|
+
|
130
|
+
# Is stack executable?
|
131
|
+
#
|
132
|
+
# @return [Boolean] Yes or not.
|
133
|
+
def nx?
|
134
|
+
!@elf_file.segment_by_type(:gnu_stack).executable?
|
135
|
+
end
|
136
|
+
|
137
|
+
# Is this ELF file a position-independent executable?
|
138
|
+
#
|
139
|
+
# @return [Boolean] Yes or not.
|
140
|
+
def pie?
|
141
|
+
@elf_file.elf_type == 'DYN'
|
142
|
+
end
|
143
|
+
|
144
|
+
# There's too many objects inside, let pry not so verbose.
|
145
|
+
# @return [String]
|
146
|
+
def inspect
|
147
|
+
"#<Pwnlib::ELF::ELF:#{::Pwnlib::Util::Fiddling.hex(__id__)}>"
|
148
|
+
end
|
149
|
+
|
150
|
+
# Yields the ELF's virtual address space for the specified string or regexp.
|
151
|
+
# Returns an Enumerator if no block given.
|
152
|
+
#
|
153
|
+
# @param [String, Regexp] needle
|
154
|
+
# The specified string to search.
|
155
|
+
#
|
156
|
+
# @return [Enumerator<Integer>]
|
157
|
+
# An enumerator for offsets in ELF's virtual address space.
|
158
|
+
#
|
159
|
+
# @example
|
160
|
+
# ELF.new('/bin/sh', checksec: false).find('ELF')
|
161
|
+
# #=> #<Enumerator: ...>
|
162
|
+
#
|
163
|
+
# ELF.new('/bin/sh', checksec: false).find(/E.F/).each { |i| puts i.hex }
|
164
|
+
# # 0x1
|
165
|
+
# # 0x11477
|
166
|
+
# # 0x1c84f
|
167
|
+
# # 0x1d5ee
|
168
|
+
# #=> true
|
169
|
+
def search(needle)
|
170
|
+
return enum_for(:search, needle) unless block_given?
|
171
|
+
|
172
|
+
load_address_fixup = @address - @load_addr
|
173
|
+
stream = @elf_file.stream
|
174
|
+
@elf_file.each_segments do |seg|
|
175
|
+
addr = seg.header.p_vaddr
|
176
|
+
memsz = seg.header.p_memsz
|
177
|
+
offset = seg.header.p_offset
|
178
|
+
|
179
|
+
stream.pos = offset
|
180
|
+
data = stream.read(memsz).ljust(seg.header.p_filesz, "\x00")
|
181
|
+
|
182
|
+
offset = 0
|
183
|
+
loop do
|
184
|
+
offset = data.index(needle, offset)
|
185
|
+
break if offset.nil?
|
186
|
+
|
187
|
+
yield (addr + offset + load_address_fixup)
|
188
|
+
offset += 1
|
189
|
+
end
|
190
|
+
end
|
191
|
+
true
|
192
|
+
end
|
193
|
+
alias find search
|
194
|
+
|
195
|
+
# Returns one-gadgets of glibc.
|
196
|
+
#
|
197
|
+
# @return [Array<Integer>]
|
198
|
+
# Returns array of one-gadgets, see examples.
|
199
|
+
#
|
200
|
+
# @example
|
201
|
+
# ELF::ELF.new('/lib/x86_64-linux-gnu/libc.so.6').one_gadgets[0]
|
202
|
+
# #=> 324293 # 0x4f2c5
|
203
|
+
#
|
204
|
+
# @example
|
205
|
+
# libc = ELF::ELF.new('/lib/x86_64-linux-gnu/libc.so.6')
|
206
|
+
# libc.one_gadgets[1]
|
207
|
+
# #=> 324386 # 0x4f322
|
208
|
+
#
|
209
|
+
# libc.address = 0x7fff7fff0000
|
210
|
+
# libc.one_gadgets[1]
|
211
|
+
# #=> 140735341130530 # 0x7fff8003f322
|
212
|
+
#
|
213
|
+
# @example
|
214
|
+
# libc = ELF::ELF.new('/lib/x86_64-linux-gnu/libc.so.6')
|
215
|
+
# context.log_level = :debug
|
216
|
+
# libc.one_gadgets[0]
|
217
|
+
# # [DEBUG] 0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
|
218
|
+
# # constraints:
|
219
|
+
# # rcx == NULL
|
220
|
+
# #=> 324293
|
221
|
+
def one_gadgets
|
222
|
+
return @one_gadgets if @one_gadgets
|
223
|
+
|
224
|
+
gadgets = OneGadget.gadgets(file: @path, details: true, level: 1)
|
225
|
+
@one_gadgets = gadgets.map { |g| g.offset + address }
|
226
|
+
@one_gadgets.instance_variable_set(:@gadgets, gadgets)
|
227
|
+
|
228
|
+
class << @one_gadgets
|
229
|
+
def [](idx)
|
230
|
+
super.tap { log.debug(@gadgets[idx].inspect) }
|
231
|
+
end
|
232
|
+
|
233
|
+
def first
|
234
|
+
self[0]
|
235
|
+
end
|
236
|
+
|
237
|
+
def last
|
238
|
+
self[-1]
|
239
|
+
end
|
240
|
+
|
241
|
+
include ::Pwnlib::Logger
|
242
|
+
end
|
243
|
+
|
244
|
+
@one_gadgets
|
245
|
+
end
|
246
|
+
|
247
|
+
private
|
248
|
+
|
249
|
+
def show_info(path)
|
250
|
+
log.info(path.inspect)
|
251
|
+
log.indented(checksec, level: ::Pwnlib::Logger::INFO)
|
252
|
+
end
|
253
|
+
|
254
|
+
# Get the dynamic tag with +type+.
|
255
|
+
# @return [ELFTools::Dynamic::Tag, nil]
|
256
|
+
def dynamic_tag(type)
|
257
|
+
dynamic = @elf_file.segment_by_type(:dynamic) || @elf_file.section_by_name('.dynamic')
|
258
|
+
return nil if dynamic.nil? # No dynamic table presents, might be statically linked.
|
259
|
+
|
260
|
+
dynamic.tag_by_type(type)
|
261
|
+
end
|
262
|
+
|
263
|
+
# Load got symbols
|
264
|
+
def load_got
|
265
|
+
@got = OpenStruct.new
|
266
|
+
sections_by_types(%i(rel rela)).each do |rel_sec|
|
267
|
+
symtab = @elf_file.section_at(rel_sec.header.sh_link)
|
268
|
+
next unless symtab.respond_to?(:symbol_at)
|
269
|
+
|
270
|
+
rel_sec.relocations.each do |rel|
|
271
|
+
symbol = symtab.symbol_at(rel.symbol_index)
|
272
|
+
next if symbol.nil? # Unusual case.
|
273
|
+
|
274
|
+
@got[symbol.name] = rel.header.r_offset.to_i
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
PLT_OFFSET = 0x10 # magic offset, correct in i386/amd64.
|
280
|
+
# Load all plt symbols.
|
281
|
+
def load_plt
|
282
|
+
# Unlike pwntools-python, which use unicorn emulating instructions to find plt(s).
|
283
|
+
# Here only use section information, which won't find any plt(s) when compile option '-Wl' is enabled.
|
284
|
+
#
|
285
|
+
# The implementation here same as pwntools-python 3.5, and supports i386 and amd64 only.
|
286
|
+
@plt = nil
|
287
|
+
plt_sec = @elf_file.section_by_name('.plt')
|
288
|
+
return log.warn('No PLT section found, PLT not loaded') if plt_sec.nil?
|
289
|
+
|
290
|
+
rel_sec = @elf_file.section_by_name('.rel.plt') || @elf_file.section_by_name('.rela.plt')
|
291
|
+
return log.warn('No REL.PLT section found, PLT not loaded') if rel_sec.nil?
|
292
|
+
|
293
|
+
symtab = @elf_file.section_at(rel_sec.header.sh_link)
|
294
|
+
return unless symtab.respond_to?(:symbol_at) # unusual case
|
295
|
+
|
296
|
+
@plt = OpenStruct.new
|
297
|
+
address = plt_sec.header.sh_addr.to_i + PLT_OFFSET
|
298
|
+
rel_sec.relocations.each do |rel|
|
299
|
+
symbol = symtab.symbol_at(rel.symbol_index)
|
300
|
+
next if symbol.nil? # unusual case
|
301
|
+
|
302
|
+
@plt[symbol.name] = address
|
303
|
+
address += PLT_OFFSET
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
# Load all exist symbols.
|
308
|
+
def load_symbols
|
309
|
+
@symbols = OpenStruct.new
|
310
|
+
@elf_file.each_sections do |section|
|
311
|
+
next unless section.respond_to?(:symbols)
|
312
|
+
|
313
|
+
section.each_symbols do |symbol|
|
314
|
+
# Don't care symbols without a name.
|
315
|
+
next if symbol.name.empty?
|
316
|
+
next if symbol.header.st_value.zero?
|
317
|
+
|
318
|
+
# TODO(david942j): handle symbols with the same name.
|
319
|
+
@symbols[symbol.name] = symbol.header.st_value.to_i
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
def sections_by_types(types)
|
325
|
+
types.map { |type| @elf_file.sections_by_type(type) }.flatten
|
326
|
+
end
|
327
|
+
|
328
|
+
def base_address
|
329
|
+
return 0 if pie?
|
330
|
+
|
331
|
+
# Find the min of PT_LOAD's p_vaddr
|
332
|
+
@elf_file.segments_by_type(:load)
|
333
|
+
.map { |seg| seg.header.p_vaddr }
|
334
|
+
.min
|
335
|
+
end
|
336
|
+
|
337
|
+
include ::Pwnlib::Logger
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|