Skip to content

Commit fa5a288

Browse files
authored
Fix waiting for gdb under WSL2 (#2470)
* Fix returning wrong pid in WSL for run_in_new_terminal We run cmd.exe which and other Windows processes before going back into WSL. The returned pid would be the one for the ephemeral cmd.exe process instead of the real command we wanted to launch. I don't know how to properly trace the execution and get the command pid, so scan for newer pids matching the command for a while instead as a workaround. We wrap the command in a script so psutil.Process.exe() returns the path to the shell instead of the real command. Look at psutil.Process.cmdline() too which contains the real program running right now. * Make `proc.wait_for_debugger` return the debugger pid If we fail to get the pid when launching gdb, grab it after tracing the debugger at least. gdb won't be closed when the exploit exits but at least we have the correct pid. * Fix working directory of run_in_new_terminal in WSL The process' cwd would be %WINDIR% due to cmd.exe not supporting WSL paths. * Update CHANGELOG
1 parent 69ab205 commit fa5a288

File tree

4 files changed

+33
-4
lines changed

4 files changed

+33
-4
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,14 @@ The table below shows which release corresponds to each branch, and what date th
7878
- [#2457][2457] Catch exception of non-ELF files in checksec.
7979
- [#2444][2444] Add `ELF.close()` to release resources
8080
- [#2413][2413] libcdb: improve the search speed of `search_by_symbol_offsets` in local libc-database
81+
- [#2470][2470] Fix waiting for gdb under WSL2
8182

8283
[2471]: https://github.com/Gallopsled/pwntools/pull/2471
8384
[2358]: https://github.com/Gallopsled/pwntools/pull/2358
8485
[2457]: https://github.com/Gallopsled/pwntools/pull/2457
8586
[2444]: https://github.com/Gallopsled/pwntools/pull/2444
8687
[2413]: https://github.com/Gallopsled/pwntools/pull/2413
88+
[2470]: https://github.com/Gallopsled/pwntools/pull/2470
8789

8890
## 4.14.0 (`beta`)
8991

pwnlib/gdb.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1251,7 +1251,7 @@ def preexec_fn():
12511251
gdb_pid = misc.run_in_new_terminal(cmd, preexec_fn = preexec_fn)
12521252

12531253
if pid and context.native:
1254-
proc.wait_for_debugger(pid, gdb_pid)
1254+
gdb_pid = proc.wait_for_debugger(pid, gdb_pid)
12551255

12561256
if not api:
12571257
return gdb_pid

pwnlib/util/misc.py

+24-1
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
import sys
1414
import tempfile
1515
import inspect
16+
import time
1617
import types
1718

1819
from pwnlib import atexit
1920
from pwnlib.context import context
2021
from pwnlib.log import getLogger
22+
from pwnlib.timeout import Timeout
2123
from pwnlib.util import fiddling
2224
from pwnlib.util import lists
2325
from pwnlib.util import packing
@@ -439,6 +441,11 @@ def run_in_new_terminal(command, terminal=None, args=None, kill_at_exit=True, pr
439441
tmp.flush()
440442
os.chmod(tmp.name, 0o700)
441443
argv = [which(terminal), tmp.name]
444+
# cmd.exe does not support WSL UNC paths as working directory
445+
# so it gets reset to %WINDIR% before starting wsl again.
446+
# Set the working directory correctly in WSL.
447+
elif terminal == 'cmd.exe':
448+
argv[-1] = "cd '{}' && {}".format(os.getcwd(), argv[-1])
442449

443450
log.debug("Launching a new terminal: %r" % argv)
444451

@@ -472,10 +479,26 @@ def run_in_new_terminal(command, terminal=None, args=None, kill_at_exit=True, pr
472479
kittyid = None
473480
if kittyid is None:
474481
log.error("Could not parse kitty window ID from output (%r)", out)
482+
elif terminal == 'cmd.exe':
483+
# p.pid is cmd.exe's pid instead of the WSL process we want to start eventually.
484+
# I don't know how to trace the execution through Windows and back into the WSL2 VM.
485+
# Do a best guess by waiting for a new process matching the command to be run.
486+
# Otherwise it's better to return nothing instead of a know wrong pid.
487+
from pwnlib.util.proc import pid_by_name
488+
pid = None
489+
ran_program = command.split(' ')[0] if isinstance(command, six.string_types) else command[0]
490+
t = Timeout()
491+
with t.countdown(timeout=5):
492+
while t.timeout:
493+
new_pid = pid_by_name(ran_program)
494+
if new_pid and new_pid[0] > p.pid:
495+
pid = new_pid[0]
496+
break
497+
time.sleep(0.01)
475498
else:
476499
pid = p.pid
477500

478-
if kill_at_exit:
501+
if kill_at_exit and pid:
479502
def kill():
480503
try:
481504
if terminal == 'qdbus':

pwnlib/util/proc.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ def match(p):
8282
try:
8383
if p.exe() == name:
8484
return True
85+
if p.cmdline()[0] == name:
86+
return True
8587
except Exception:
8688
pass
8789
return False
@@ -398,7 +400,7 @@ def wait_for_debugger(pid, debugger_pid=None):
398400
pid (int): PID of the process.
399401
400402
Returns:
401-
None
403+
The PID of the debugger that attached to the process.
402404
"""
403405
t = Timeout()
404406
with t.countdown(timeout=15):
@@ -416,9 +418,11 @@ def wait_for_debugger(pid, debugger_pid=None):
416418
else:
417419
time.sleep(0.01)
418420

419-
if tracer(pid):
421+
tracer_pid = tracer(pid)
422+
if tracer_pid:
420423
l.success()
421424
elif debugger_pid == 0:
422425
l.failure("debugger exited! (maybe check /proc/sys/kernel/yama/ptrace_scope)")
423426
else:
424427
l.failure('Debugger did not attach to pid %d within 15 seconds', pid)
428+
return tracer_pid

0 commit comments

Comments
 (0)