Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve pwnup template, gdbserver detection #1148

Merged
merged 8 commits into from
May 30, 2018
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions pwnlib/data/templates/pwnup.mako
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,9 @@ def start(argv=[], *a, **kw):
%endif
gdbscript = '''
%if ctx.binary:
set sysroot
Copy link
Member

@zachriggle zachriggle May 10, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC, set sysroot with no arguments is a no-op. Update: Nope, it's worse.

(gdb) show sysroot
The current system root is "target:".
(gdb) set sysroot
(gdb) show sysroot
The current system root is "".

set sysroot / (which I think you meant?) is incorrect for SSH-forwarded GDB sessions and Android devices.

set sysroot will also break for foreign-architecture binaries which are emulated under QEMU:

pwntools/pwnlib/gdb.py

Lines 424 to 430 in 20cb049

else:
qemu_port = random.randint(1024, 65535)
qemu_user = qemu.user_path()
sysroot = sysroot or qemu.ld_prefix(env)
if not qemu_user:
log.error("Cannot debug %s binaries without appropriate QEMU binaries" % context.arch)
args = [qemu_user, '-g', str(qemu_port)] + args

Finally, you don't want to set the sysroot in your GDB script. Pass the sysroot= argument into the gdb.debug() or gdb.attach() call, that's what it's there for. (Side bar, we don't actually invoke set sysroot for native-arch binaries, even if it's provided. This is a small bug.)

You might want to expose a --sysroot argument to pwn template to do this conditionally.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I don't use set sysroot I get the following error:

Temporary breakpoint 1 at 0x605: file x.c, line 2.    
Warning:                                              
Cannot insert breakpoint 1.                           
Cannot access memory at address 0x605                 
                                                      
/tmp/pwnsIcbJL.gdb:7: Error in sourced command file:  
Command aborted.                                      

The right side is with set sysroot, the left without, both include tbreak main and continue:
screenshot from 2018-05-10 19-25-24

Copy link
Member

@zachriggle zachriggle May 10, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you run your script with DEBUG to see the actual gdbscript being executed? (i.e. python foo.py LOCAL GDB DEBUG).

Can you also provide the first line of gdb --version and all of gdb --configuration?

EDIT: And gdbserver --version.

Thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

➜  tmp gdb --version
GNU gdb (GDB) 8.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".
➜  tmp gdb --configuration
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
             --with-python=/usr (relocatable)
             --with-guile
             --with-separate-debug-dir=/usr/lib/debug (relocatable)
             --with-system-gdbinit=/etc/gdb/gdbinit
             --without-babeltrace

("Relocatable" means the directory can be moved with the GDB installation
tree, and GDB will still find it.)

➜  tmp gdbserver --version
GNU gdbserver (GDB) 8.1
Copyright (C) 2018 Free Software Foundation, Inc.
gdbserver is free software, covered by the GNU General Public License.
This gdbserver was configured as "x86_64-pc-linux-gnu"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's the output of ./exploit.py GDB LOCAL DEBUG

screenshot from 2018-05-10 20-59-40

%if 'main' in ctx.binary.symbols:
break *0x{exe.symbols.main:x}
%else:
break *0x{exe.entry:x}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to remain. In the event symbols are unavailable, gdbserver will break at the entry point to the linker, not the entry point to the main binary.

In the event that the binary is PIE (and exe.entry is a small value ~close to zero), this has the same effect as setting a breakpoint on the first instruction because of a convenient bug in gdb: https://reverseengineering.stackexchange.com/q/8724

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting an invalid breakpoint in the gdbscript appears to be an error:

0x00007f9dc32aefb0 in _start () from target:/lib64/ld-linux-x86-64.so.2
Temporary breakpoint 1 at 0x520
Warning:
Cannot insert breakpoint 1.
Cannot access memory at address 0x520

/tmp/pwnrsyWx3.gdb:8: Error in sourced command file:
Command aborted.

I still end up in _start and am unable to continue the program from there:

gef➤  continue
Continuing.
Warning:
Cannot insert breakpoint 1.
Cannot access memory at address 0x520

Command aborted.

tbreak main
%endif
%endif
continue
Expand All @@ -161,6 +160,8 @@ continue

io = start()

if args.GDB:
log.info(io.recvline())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be added in pwnlib.gdb, not to the template, probably right after this:

pwntools/pwnlib/gdb.py

Lines 782 to 783 in 20cb049

if pid and context.native:
proc.wait_for_debugger(pid)

The issue is that doesn't actually happen all of the time -- only for certain (newer?) versions of gdbserver. We can have a .recvline(timeout=0.5) and then check to see if the line starts how we expect. If not, we'll need to .unrecv(...) the data to put it back.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try adding it after that if statement.

Copy link
Contributor Author

@ambiso ambiso May 10, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see there's already been done some handling for this in gdb.debug:

pwntools/pwnlib/gdb.py

Lines 465 to 469 in 20cb049

# gdbserver outputs a message when a client connects
garbage = gdbserver.recvline(timeout=1)
if "Remote debugging from host" not in garbage:
gdbserver.unrecv(garbage)

Maybe gdb.debug should be used instead?

When running locally gdb.debug is used, however the fix does not trigger.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When running locally, gdb.debug spawns gdbserver and then invokes gdb.attach.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep.

I've adjusted the code in gdb.debug. It should handle gdb's message correctly now.

%if not quiet:
# shellcode = asm(shellcraft.sh())
# payload = fit({
Expand Down