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

Gallopsled / pwntools / 16074384421

04 Jul 2025 12:59PM UTC coverage: 73.53% (-0.4%) from 73.885%
16074384421

push

github

peace-maker
Remove partial test implementation

The commands should be tested end-to-end instead of using doctests (later).

3804 of 6426 branches covered (59.2%)

13320 of 18115 relevant lines covered (73.53%)

0.74 hits per line

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

93.28
/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.misc import python_2_bytes_compatible
1✔
31
from pwnlib.util.packing import pack
1✔
32

33
log = getLogger(__name__)
1✔
34

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

39
variables={
1✔
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
del name, size, length
1✔
70

71

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

100

101
@python_2_bytes_compatible
1✔
102
class FileStructure(object):
1✔
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

161
    vars_=[]
1✔
162
    length={}
1✔
163

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

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

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

182
    def __len__(self):
1✔
183
        return len(bytes(self))
1✔
184

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

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

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

203
        Example:
204

205
            Payload for data uptil _IO_buf_end
206

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

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

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

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

264
        Example:
265

266
            Payload for writing 100 bytes to stdout from the address 0xcafebabe
267

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

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

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

292
        Example:
293

294
            Payload for reading 100 bytes from stdin into the address 0xcafebabe
295

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

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

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

320
        Example:
321

322
            Example payload if address of _IO_list_all is 0xfacef00d and fake vtable is at 0xcafebabe -
323

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