pwndbg / pwndbg

Exploit Development and Reverse Engineering with GDB Made Easy
https://pwndbg.re/
MIT License
7.48k stars 878 forks source link

pwndbg.proc.exe returns wrong path #623

Closed mzr closed 5 years ago

mzr commented 5 years ago

Description

When using pwntools gdb.debug():

pwndbg> got
exception occured: got: could not download remote file 'target:/lib64/ld-linux-x86-64.so.2':
error: remote i/o error: no such file or directory (<class 'oserror'>)

It works when I call gdb directly (without gdbserver).

My setup

Platform: Linux-5.0.9-2-MANJARO-x86_64-with-arch-Manjaro-Linux
Gdb:      8.2.1
Python:   3.7.3 (default, Mar 26 2019, 21:43:19)  [GCC 8.2.1 20181127]
Pwndbg:   1.1.0 build: 2b50703
Capstone: 4.0.1024
Unicorn:  1.0.1
This GDB was configured as follows:
   configure --host=x86_64-pc-linux-gnu --target=x86_64-pc-linux-gnu
             --with-auto-load-dir=$debugdir:$datadir/auto-load
             --with-auto-load-safe-path=$debugdir:$datadir/auto-load
             --with-expat
             --with-gdb-datadir=/usr/share/gdb (relocatable)
             --with-jit-reader-dir=/usr/lib/gdb (relocatable)
             --without-libunwind-ia64
             --with-lzma
             --without-babeltrace
             --without-intel-pt
             --disable-libmcheck
             --with-mpfr
             --with-python=/usr (relocatable)
             --with-guile
             --with-separate-debug-dir=/usr/lib/debug (relocatable)
             --with-system-gdbinit=/etc/gdb/gdbinit

Also tested with Ubuntu 16.04 docker container, and Ubuntu 14.04 docker container.

Steps to reproduce

  1. Create script.py:
#!/usr/bin/env python2

from pwn import *

context.log_level = 'DEBUG'

exe = context.binary = ELF('bin')
context.terminal = ['tmux', 'splitw', '-h']

def start(argv=[], *a, **kw):
    return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw)

gdbscript = '''
break *0x{exe.symbols.main:x}
continue
'''.format(**locals())

io = start()
io.interactive()
  1. echo '#include <stdio.h>\n int main(){ puts("a"); return 0;}' | gcc -xc -o bin -
  2. ./script.py
  3. Execute got inside pwndbg

Dump of debug logs and backtrace:

Interactive pane

➜  debug ./script.py                                                     
[DEBUG] PLT 0x102c puts                                                  
[*] '/home/mzr/debug/bin'                                                
    Arch:     amd64-64-little                                            
[DEBUG] PLT 0x102c puts                                                  
[*] '/home/mzr/debug/bin'                                                
    Arch:     amd64-64-little                                            
    RELRO:    Partial RELRO                                              
    Stack:    No canary found                                            
    NX:       NX enabled                                                 
    PIE:      PIE enabled                                                
[+] Starting local process '/usr/bin/gdbserver' argv=['/usr/bin/gdbserver
', '--multi', '--no-disable-randomization', 'localhost:0', '/home/mzr/deb
ug/bin'] : pid 29135                                                     
[DEBUG] Received 0x49 bytes:                                             
    'Process /home/mzr/debug/bin created; pid = 29139\n'                 
    'Listening on port 35135\n'                                          
[DEBUG] Wrote gdb script to '/tmp/pwnSyBMn1.gdb'                         
    file /home/mzr/debug/bin                                             
    target remote 127.0.0.1:35135                                        
    file /home/mzr/debug/bin                                             

    break *0x1139                                                        
    continue                                                             
[*] running in new terminal: /usr/bin/gdb -q  "/home/mzr/debug/bin" -x "/
tmp/pwnSyBMn1.gdb"                                                       
[DEBUG] Launching a new terminal: ['/usr/bin/tmux', 'splitw', '-h', '/usr
/bin/gdb -q  "/home/mzr/debug/bin" -x "/tmp/pwnSyBMn1.gdb"']             
[*] Switching to interactive mode                                        
[DEBUG] Received 0x25 bytes:                                             
    'Remote debugging from host 127.0.0.1\n'                             
Remote debugging from host 127.0.0.1                                     
$                                                                       

GDB pane

pwndbg> got
Traceback (most recent call last):
  File "/home/mzr/sources/pwndbg/pwndbg/commands/__init__.py", line 135, 
in __call__
    return self.function(*args, **kwargs)
  File "/home/mzr/sources/pwndbg/pwndbg/commands/__init__.py", line 226, 
in _OnlyWhenRunning
    return function(*a, **kw)
  File "/home/mzr/sources/pwndbg/pwndbg/commands/got.py", line 28, in got
    relro_status = pwndbg.wrappers.checksec.relro_status()
  File "/home/mzr/sources/pwndbg/pwndbg/commands/__init__.py", line 215, 
in _OnlyWithFile
    return function(*a, **kw)
  File "/home/mzr/sources/pwndbg/pwndbg/wrappers/__init__.py", line 28, i
n _OnlyWithCommand
    return function(*a, **kw)
  File "/home/mzr/sources/pwndbg/pwndbg/wrappers/checksec.py", line 25, i
n relro_status
    out = get_raw_out()
  File "/home/mzr/sources/pwndbg/pwndbg/commands/__init__.py", line 215, 
in _OnlyWithFile
    return function(*a, **kw)
  File "/home/mzr/sources/pwndbg/pwndbg/wrappers/__init__.py", line 28, i
n _OnlyWithCommand
    return function(*a, **kw)
  File "/home/mzr/sources/pwndbg/pwndbg/memoize.py", line 48, in __call__
    value = self.func(*args, **kwargs)
  File "/home/mzr/sources/pwndbg/pwndbg/wrappers/checksec.py", line 18, i
n get_raw_out
    local_path = pwndbg.file.get_file(pwndbg.proc.exe)
  File "/home/mzr/sources/pwndbg/pwndbg/file.py", line 48, in get_file
    "Error: %s" % (path, error))
OSError: Could not download remote file 'target:/lib64/ld-linux-x86-64.so
.2':
Error: Remote I/O error: No such file or directory

Reproducing the same behaviour without pwntools

But in the exactly same way as pwntootls debug log says.

1.

➜ ~ /usr/bin/gdbserver --multi --no-disable-randomization localhost:0 /home/mzr/debug/bin
Process /home/mzr/debug/bin created; pid = 29395
Listening on port 39501

2.

➜ ~ cat > /tmp/script_.gdb << EOL
file /home/mzr/debug/bin
target remote 127.0.0.1:39501
file /home/mzr/debug/bin

break *0x1139
continue
EOL
  1. ➜ ~ /usr/bin/gdb -q /home/mzr/debug/bin -x /tmp/script_.gdb
  2. Execute got inside pwndbg:
    pwndbg> got
    Exception occured: got: Could not download remote file 'target:/lib64/ld-linux-x86-64.so.2':
    Error: Remote I/O error: No such file or directory (<class 'OSError'>)

    It also gives the same python backtrace.

Bug in gdbscript generation?

Modyfying gdbscript to (removed second file /home/mzr/debug/bin):

➜ ~ cat > /tmp/script_.gdb << EOL
file /home/mzr/debug/bin
target remote 127.0.0.1:39501

break *0x1139
continue
EOL

makes got work flawlessly.

Running script.py on Ubuntu 14.04 docker container:

>>> pwndbg.proc.exe
'/usr/lib/debug/lib/x86_64-linux-gnu/ld-2.19.so'`

Also got works when I run gdbserver manually without second call to file command. Second call to file command makes next call to pwndbg.proc.exe return dynamic linker shared library.

Is it pwntools or pwndbg?

I'm not really sure. Probably pwntools shouldn't put second file command into gdbscript. Also, what happens in pwndbg is that property exe of a module class in pwndbg/proc.py returns path to shared library instead of correct path to executable. exe property blindly returns first found objfile. Shouldn't this function look similar to this?

@property
def exe(self):
    return gdb.current_progspace().filename

This change makes got work when running script.py. IMO it also looks more elegant and stable than current implementation. PR in #624.

disconnect3d commented 5 years ago

Fixed in #624!