0

I stumbled upon the following piece of code here:

@pwnlib.memleak.MemLeak.NoNewlines
def fmtleak(addr):
    ...
    return res

printf_leaked = fmtleak.q(printf_got)
...

Can anybody please explain what @pwnlib.memleak.MemLeak.NoNewlines (on the beginning line) and fmtleak.q mean in this context? What sort of Python syntax are used, and what are they?

sherlock
  • 2,397
  • 3
  • 27
  • 44
  • `@pwnlib.memleak.MemLeak.NoNewlines` is a decorator. and I'm pretty sure `fmtleak.q` is a function. – Prajwal Jan 02 '17 at 10:43

3 Answers3

1

In python the symbol @ is used for a decorator.

@dec1
def func(arg1, arg2, ...):
    pass

which is equivalent to the following piece of code:

def func(arg1, arg2, ...):
    pass
func = dec1(func)

And as per your question .q is not a keyword in python syntax but it must be a function in your code.

fredtantini
  • 15,966
  • 8
  • 49
  • 55
Nitin Prakash
  • 328
  • 3
  • 15
1

Welcome to pwntools -- pwnlib.

I'm going to explain about what you're talking about. However I'm assuming that you know what Call stack is, and exploitation techniques such as Information leakage and Uncontrolled format string also known as Format string bug; so you need to learn what you don't know among them to understand what I'm going to say.


DynELF

The writer of the article you mentioned was trying to use DynELF, one of the features provided by pwntools that resolves remote functions using leaks. DynELF requires a Python function or a pwnlib.memleak.MemLeak object that leaks data at a given address as much as possible.

class pwnlib.dynelf.DynELF(leak, pointer=None, elf=None)

DynELF knows how to resolve symbols in remote processes via an infoleak(information leakage) or memleak(memory leakage) vulnerability encapsulated by pwnlib.memleak.MemLeak.


Decorator

First of all,

@pwnlib.memleak.MemLeak.NoNewlines
def fmtleak(addr):
    ...
    return res

printf_leaked = fmtleak.q(printf_got)

is equivalent to

def fmtleak(addr):
    ...
    return res
fmtleak = pwnlib.memleak.MemLeak.NoNewlines(fmtleak)
printf_leaked = fmtleak.q(printf_got)

; the @ is Python syntax for decorators.

So the fmtleak becomes an instance of MemLeak class, returned by the NoNewlines method.


NoNewlines

pwnlib.memleak.MemLeak.NoNewlines, which is a static method of pwnlib.memleak.MemLeak class, creates a pwnlib.memleak.MemLeak object, with a leak function(function to leak data) wrapped with a wrapper function that ignores leakage request for memory addresses whose memory representations contain a byte for a newline character, 0x0A('\n'). The reason why this is needed is because there are cases such as: leaking data at an address by using an uncontrolled format string from a function such as fgets, which stops to read when it reads a newline, which can lead to unintended data resulting when the address has a newline byte.

From the man page for the fgets function:

fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline.

From pwnlib/memleak.py:

@staticmethod
def NoNewlines(function):
    """Wrapper for leak functions such that addresses which contain newline
    bytes are not leaked.

    This is useful if the address which is used for the leak is provided by
    e.g. ``fgets()``.
    """

    @functools.wraps(function, updated=[])
    def whitespace_wrapper(address, *a, **kw):
        if '\n' in pack(address):
            log.info('Ignoring leak request for %#x: Contains newlines' % address)
            return None
        return function(address, *a, **kw)

    return MemLeak(whitespace_wrapper)

MemLeak class's q method

q method, which is of MemLeak class, tries to leak 64 bits of value at a given memory address with the leak function provided. If it cannot leak it completely, returns None.

From pwnlib/memleak.py:

def q(self, addr, ndx = 0):
    """q(addr, ndx = 0) -> int

    Leak qword at ``((uint64_t*) addr)[ndx]``

    Examples:

        >>> import string
        >>> data = string.ascii_lowercase
        >>> l = MemLeak(lambda a: data[a:a+16], reraise=False)
        >>> l.q(0) == unpack('abcdefgh', 64)
        True
        >>> l.q(18) == unpack('stuvwxyz', 64)
        True
        >>> l.q(19) is None
        True
    """
    return self._b(addr, ndx, 8)
0

@ in Python is the sign for a Decorator

A decorator dynamically alter the functionality of a function, method, or class without having to directly use subclasses or change the source code of the function being decorated. In general it is a design pattern that allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class. In Python decorators are simply wrappers to existing functions. Here is a guide to it: http://thecodeship.com/patterns/guide-to-python-function-decorators/

sunwarr10r
  • 4,420
  • 8
  • 54
  • 109