Arbitrary Read/Write
Last updated
Last updated
Set the _fileno
to the file descriptor of stdout
Set _flag & ~_IO_NO_WRITES
Set _flag |= _IO_CURRENTLY_PUTTING
Set the write_base
and write_ptr
to memory address which you want to read
Set _IO_read_end
equal to _IO_write_base
or though not always reliable, in pwntools:
upon fclose()
, if buffer is not empty, it will be flushed (written to the _fileno
)
Set the _fileno
to the file descriptor of choice
Set write_base
to the start of write address
Set write_ptr
to the end of write address
be careful of closing if _fileno
is set to stdout
or stdin
explained in-depth in this writeup:
we can trace the function calls as the following,
which then calls _IO_FINISH()
,
referring to the jump table, we can see that the implementation for __finish
is _IO_new_file_finish()
which will call the flush macro _IO_do_flush()
and is defined as follows:
which writes write_base
up until write_ptr
refer to fwrite.
Set the _fileno
to file descriptor of stdin
Set _flag &~ _IO_NO_READS
Set _flag |= _IO_CURRENTLY_PUTTING
Set read_base
equals to read_ptr
to NULL
Set the buf_base
and buf_end
to memory address which you want to write
buf_end - buf_base
> size of fread (BUFFER SIZE > READ SIZE) (or other function, scanf, etc)
or though not always reliable, in pwntools:
fwrite(const void *buf, size_t size, size_t count, FILE *fp);
Set fp->_flags
to _IO_MAGIC | ~_IO_LINE_BUF | ~_IO_CURRENTLY_PUTTING
Set buf
to the data wish to be written
Set fp->write_ptr
to the start of write address
Set fp->write_end
to the end of write address
we can trace the function calls as the following,
which calls _IO_sputn()
macro:
referring to the jump table, we can see that the implementation for __xsputn
is _IO_new_file_xsputn()
our interest lies in __mempcpy (f->_IO_write_ptr, s, count);
where it will do memcpy
of destination write_ptr
and source s
(buf).
first, note that initially count
and to_do
is set to 0 as default, then modified over the course of the function execution.
at [3] we need count
to be bigger than to_do
which can be easily done because it is 0 as default, so we just need to somehow able to change count
to be bigger than 0 which also a condition in [2]
though the block at [0] also modifies count
, I think its the route at [1] is more trivial and intuitive. so we need to satisfy [1] while dissatisfy [0].
when all of those requirements are fulfilled, it will then call memcpy
which corresponds to write profit.