Skip to content

Commit f17f091

Browse files
committed
use clipboard to get the result of the command
1 parent 581fa4a commit f17f091

1 file changed

Lines changed: 87 additions & 69 deletions

File tree

nxc/protocols/rdp.py

Lines changed: 87 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ def __init__(self, args, db, host):
3232
self.domain = None
3333
self.server_os = None
3434
self.iosettings = RDPIOSettings()
35-
self.iosettings.channels = []
3635
self.iosettings.video_out_format = VIDEO_FORMAT.RAW
3736
self.iosettings.clipboard_use_pyperclip = False
3837
self.protoflags_nla = [
@@ -407,7 +406,9 @@ async def _send_win_r(self):
407406

408407
async def execute_cmd(self, payload, encoding=None, capture_screenshot=False):
409408
"""Execute a command using cmd.exe"""
410-
self.logger.debug(f"Executing command: {payload}")
409+
# Append | clip to send output to clipboard
410+
payload_with_clip = f"{payload} | clip & exit"
411+
self.logger.debug(f"Executing command: {payload_with_clip}")
411412

412413
# Create a connection
413414
try:
@@ -416,6 +417,28 @@ async def execute_cmd(self, payload, encoding=None, capture_screenshot=False):
416417
except Exception as e:
417418
self.logger.debug(f"Error connecting to RDP: {e!s}")
418419
return None
420+
421+
self.logger.debug("Waiting for clipboard to be ready...")
422+
clipboard_ready = False
423+
await asyncio.sleep(self.args.cmd_delay)
424+
425+
timeout_counter = 0
426+
while not clipboard_ready and timeout_counter < 100: # 10 second timeout
427+
try:
428+
data = await asyncio.wait_for(self.conn.ext_out_queue.get(), timeout=0.1)
429+
if hasattr(data, "type") and data.type.name == "CLIPBOARD_READY":
430+
clipboard_ready = True
431+
self.logger.debug("Clipboard is ready!")
432+
break
433+
except asyncio.TimeoutError:
434+
timeout_counter += 1
435+
continue
436+
except Exception as e:
437+
self.logger.debug(f"Error waiting for clipboard: {e!s}")
438+
break
439+
440+
if not clipboard_ready:
441+
self.logger.fail("Warning: Clipboard may not be fully initialized, no output can be retrieved")
419442

420443
# Wait for desktop to be available
421444
await asyncio.sleep(self.args.cmd_delay)
@@ -438,41 +461,27 @@ async def execute_cmd(self, payload, encoding=None, capture_screenshot=False):
438461
await self._send_enter()
439462
await asyncio.sleep(self.args.cmd_delay)
440463

441-
# Type the command
442-
self.logger.debug(f"Typing command: {payload}")
443-
await self._send_keystrokes(payload)
464+
# Type the command with | clip
465+
self.logger.debug(f"Typing command: {payload_with_clip}")
466+
await self._send_keystrokes(payload_with_clip)
444467
await self._send_enter()
445468

469+
# Wait for command to execute
446470
await asyncio.sleep(self.args.cmd_delay)
471+
472+
# Get the current clipboard text
473+
self.logger.debug("Getting clipboard content...")
474+
clipboard_text = await self.conn.get_current_clipboard_text()
447475

448-
# Take a screenshot if requested
449-
if capture_screenshot and self.conn is not None:
450-
self.logger.highlight("Waiting for screen to update...")
451-
await asyncio.sleep(self.args.screentime) # Additional wait to ensure screen is updated
452-
453-
self.logger.debug(f"Desktop buffer has data: {self.conn.desktop_buffer_has_data}")
454-
if self.conn.desktop_buffer_has_data:
455-
try:
456-
self.logger.debug("Capturing command output screenshot")
457-
buffer = self.conn.get_desktop_buffer(VIDEO_FORMAT.PIL)
458-
screenshots_dir = os.path.expanduser("~/.nxc/screenshots")
459-
os.makedirs(screenshots_dir, exist_ok=True) # Ensure the directory exists
460-
filename = os.path.join(screenshots_dir, f"{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}.png")
461-
buffer.save(filename, "png")
462-
self.logger.highlight(f"Command output screenshot saved: {filename}")
463-
except Exception as e:
464-
self.logger.debug(f"Error taking screenshot: {e!s}")
465-
else:
466-
self.logger.debug("No desktop buffer data available for screenshot")
467-
468-
# Exit CMD
469-
self.logger.debug("Exiting CMD")
470-
await self._send_keystrokes("exit")
471-
await self._send_enter()
472-
await asyncio.sleep(0.5)
473-
476+
if clipboard_text:
477+
self.logger.debug("Command output retrieved from clipboard:")
478+
for line in clipboard_text.lstrip().strip("\n").splitlines():
479+
self.logger.highlight(line)
480+
else:
481+
self.logger.warning("Clipboard is empty or contains non-text data")
482+
474483
self.logger.debug("Command execution completed")
475-
return True
484+
return clipboard_text
476485

477486
finally:
478487
# Always clean up the connection
@@ -485,7 +494,9 @@ async def execute_cmd(self, payload, encoding=None, capture_screenshot=False):
485494

486495
async def execute_ps(self, payload, capture_screenshot=False):
487496
"""Execute a command using PowerShell"""
488-
self.logger.debug(f"Executing PowerShell command: {payload}")
497+
# Append | clip to send output to clipboard
498+
payload_with_clip = f"{payload} | clip & exit"
499+
self.logger.debug(f"Executing PowerShell command: {payload_with_clip}")
489500

490501
# Create a connection
491502
try:
@@ -494,6 +505,28 @@ async def execute_ps(self, payload, capture_screenshot=False):
494505
except Exception as e:
495506
self.logger.debug(f"Error connecting to RDP: {e!s}")
496507
return None
508+
509+
self.logger.debug("Waiting for clipboard to be ready...")
510+
clipboard_ready = False
511+
await asyncio.sleep(self.args.cmd_delay)
512+
513+
timeout_counter = 0
514+
while not clipboard_ready and timeout_counter < 100: # 10 second timeout
515+
try:
516+
data = await asyncio.wait_for(self.conn.ext_out_queue.get(), timeout=0.1)
517+
if hasattr(data, "type") and data.type.name == "CLIPBOARD_READY":
518+
clipboard_ready = True
519+
self.logger.debug("Clipboard is ready!")
520+
break
521+
except asyncio.TimeoutError:
522+
timeout_counter += 1
523+
continue
524+
except Exception as e:
525+
self.logger.debug(f"Error waiting for clipboard: {e!s}")
526+
break
527+
528+
if not clipboard_ready:
529+
self.logger.fail("Warning: Clipboard may not be fully initialized, no output can be retrieved")
497530

498531
# Wait for desktop to be available
499532
await asyncio.sleep(self.args.cmd_delay)
@@ -516,42 +549,27 @@ async def execute_ps(self, payload, capture_screenshot=False):
516549
await self._send_enter()
517550
await asyncio.sleep(self.args.cmd_delay)
518551

519-
# Type the PowerShell command
520-
self.logger.debug(f"Typing PowerShell command: {payload}")
521-
await self._send_keystrokes(payload)
552+
# Type the PowerShell command with | clip
553+
self.logger.debug(f"Typing PowerShell command: {payload_with_clip}")
554+
await self._send_keystrokes(payload_with_clip)
522555
await self._send_enter()
523556

524-
# Wait longer for command to complete execution
525-
await asyncio.sleep(self.args.cmd_delay) # Increased wait time to ensure command completes
526-
527-
# Take a screenshot if requested
528-
if capture_screenshot and self.conn is not None:
529-
self.logger.highlight("Waiting for screen to update...")
530-
await asyncio.sleep(self.args.screentime) # Additional wait to ensure screen is updated
531-
532-
self.logger.debug(f"Desktop buffer has data: {self.conn.desktop_buffer_has_data}")
533-
if self.conn.desktop_buffer_has_data:
534-
try:
535-
self.logger.debug("Capturing PowerShell command output screenshot")
536-
buffer = self.conn.get_desktop_buffer(VIDEO_FORMAT.PIL)
537-
screenshots_dir = os.path.expanduser("~/.nxc/screenshots")
538-
os.makedirs(screenshots_dir, exist_ok=True) # Ensure the directory exists
539-
filename = os.path.join(screenshots_dir, f"{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}.png")
540-
buffer.save(filename, "png")
541-
self.logger.highlight(f"PowerShell command output screenshot saved: {filename}")
542-
except Exception as e:
543-
self.logger.debug(f"Error taking screenshot: {e!s}")
544-
else:
545-
self.logger.debug("No desktop buffer data available for screenshot")
546-
547-
# Exit PowerShell
548-
self.logger.debug("Exiting PowerShell")
549-
await self._send_keystrokes("exit")
550-
await self._send_enter()
551-
await asyncio.sleep(0.5)
557+
# Wait for command to execute
558+
await asyncio.sleep(self.args.cmd_delay)
559+
560+
# Get the current clipboard text
561+
self.logger.debug("Getting clipboard content...")
562+
clipboard_text = await self.conn.get_current_clipboard_text()
552563

553-
self.logger.debug("PowerShell command execution completed")
554-
return True
564+
if clipboard_text:
565+
self.logger.debug("PowerShell command output retrieved from clipboard:")
566+
for line in clipboard_text.strip("\n"):
567+
self.logger.highlight(line)
568+
else:
569+
self.logger.warning("Clipboard is empty or contains non-text data")
570+
571+
self.logger.debug("Command execution completed")
572+
return clipboard_text
555573

556574
finally:
557575
if self.conn is not None:
@@ -578,16 +596,16 @@ def execute(self, payload=None, shell_type="cmd"):
578596
if capture_screenshot:
579597
self.logger.info("Will capture screenshot of command output")
580598

581-
self.logger.success(f"Executing {shell_type} command: {payload} with delay {self.args.cmd_delay} seconds")
599+
self.logger.success(f"Executing command: {payload} with delay {self.args.cmd_delay} seconds")
582600

583601
try:
584602
result = asyncio.run(self.execute_cmd(payload, capture_screenshot=capture_screenshot)) if shell_type == "cmd" else asyncio.run(self.execute_ps(payload, capture_screenshot=capture_screenshot))
585603

586604
if result:
587-
self.logger.success("Command execution completed")
605+
self.logger.debug("Command execution completed")
588606
return result
589607
except Exception as e:
590-
self.logger.debug(f"Command execution error: {e!s}")
608+
self.logger.error(f"Command execution error: {e!s}")
591609
if shell_type == "cmd":
592610
self.logger.info("Cannot execute command via cmd - now switching to PowerShell to attempt execution")
593611
try:

0 commit comments

Comments
 (0)