mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-06-03 14:59:09 -07:00
split PS wrapper and payload (CVE-2018-16859) (#49142)
* prevent scriptblock logging from logging payload contents * added tests to verify no payload contents in PS Operational event log * fix script action to send split-aware wrapper * fix CLIXML error parser (return to -EncodedCommand exposed problems with it)
This commit is contained in:
parent
e7104a445b
commit
8c1f701e6e
12 changed files with 91 additions and 30 deletions
|
@ -127,14 +127,17 @@ class ActionModule(ActionBase):
|
|||
# PowerShell runs the script in a special wrapper to enable things
|
||||
# like become and environment args
|
||||
if self._connection._shell.SHELL_FAMILY == "powershell":
|
||||
# FIXME: use a more public method to get the exec payload
|
||||
# FUTURE: use a more public method to get the exec payload
|
||||
pc = self._play_context
|
||||
exec_data = ps_manifest._create_powershell_wrapper(
|
||||
to_bytes(script_cmd), {}, env_dict, self._task.async_val,
|
||||
pc.become, pc.become_method, pc.become_user,
|
||||
pc.become_pass, pc.become_flags, substyle="script"
|
||||
)
|
||||
script_cmd = "-"
|
||||
# build the necessary exec wrapper command
|
||||
# FUTURE: this still doesn't let script work on Windows with non-pipelined connections or
|
||||
# full manual exec of KEEP_REMOTE_FILES
|
||||
script_cmd = self._connection._shell.build_module_command(env_string='', shebang='#!powershell', cmd='')
|
||||
|
||||
result.update(self._low_level_execute_command(cmd=script_cmd, in_data=exec_data, sudoable=True, chdir=chdir))
|
||||
|
||||
|
|
|
@ -282,6 +282,7 @@ class Connection(ConnectionBase):
|
|||
# starting a new interpreter to save on time
|
||||
b_command = base64.b64decode(cmd.split(" ")[-1])
|
||||
script = to_text(b_command, 'utf-16-le')
|
||||
in_data = to_text(in_data, errors="surrogate_or_strict", nonstring="passthru")
|
||||
display.vvv("PSRP: EXEC %s" % script, host=self._psrp_host)
|
||||
else:
|
||||
# in other cases we want to execute the cmd as the script
|
||||
|
|
|
@ -103,6 +103,7 @@ import traceback
|
|||
import json
|
||||
import tempfile
|
||||
import subprocess
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
HAVE_KERBEROS = False
|
||||
try:
|
||||
|
@ -531,14 +532,17 @@ class Connection(ConnectionBase):
|
|||
return (result.status_code, result.std_out, result.std_err)
|
||||
|
||||
def is_clixml(self, value):
|
||||
return value.startswith(b"#< CLIXML")
|
||||
return value.startswith(b"#< CLIXML\r\n")
|
||||
|
||||
# hacky way to get just stdout- not always sure of doc framing here, so use with care
|
||||
def parse_clixml_stream(self, clixml_doc, stream_name='Error'):
|
||||
clear_xml = clixml_doc.replace(b'#< CLIXML\r\n', b'')
|
||||
doc = xmltodict.parse(clear_xml)
|
||||
lines = [l.get('#text', '').replace('_x000D__x000A_', '') for l in doc.get('Objs', {}).get('S', {}) if l.get('@S') == stream_name]
|
||||
return '\r\n'.join(lines)
|
||||
clixml = ET.fromstring(clixml_doc.split(b"\r\n", 1)[-1])
|
||||
namespace_match = re.match(r'{(.*)}', clixml.tag)
|
||||
namespace = "{%s}" % namespace_match.group(1) if namespace_match else ""
|
||||
|
||||
strings = clixml.findall("./%sS" % namespace)
|
||||
lines = [e.text.replace('_x000D__x000A_', '') for e in strings if e.attrib.get('S') == stream_name]
|
||||
return to_bytes('\r\n'.join(lines))
|
||||
|
||||
# FUTURE: determine buffer size at runtime via remote winrm config?
|
||||
def _put_file_stdin_iterator(self, in_path, out_path, buffer_size=250000):
|
||||
|
|
|
@ -55,6 +55,7 @@ import base64
|
|||
import os
|
||||
import re
|
||||
import shlex
|
||||
import pkgutil
|
||||
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.module_utils._text import to_text
|
||||
|
@ -208,9 +209,11 @@ class ShellModule(ShellBase):
|
|||
return self._encode_script(script)
|
||||
|
||||
def build_module_command(self, env_string, shebang, cmd, arg_path=None):
|
||||
bootstrap_wrapper = pkgutil.get_data("ansible.executor.powershell", "bootstrap_wrapper.ps1")
|
||||
|
||||
# pipelining bypass
|
||||
if cmd == '':
|
||||
return '-'
|
||||
return self._encode_script(script=bootstrap_wrapper, strict_mode=False, preserve_rc=False)
|
||||
|
||||
# non-pipelining
|
||||
|
||||
|
@ -218,8 +221,10 @@ class ShellModule(ShellBase):
|
|||
cmd_parts = list(map(to_text, cmd_parts))
|
||||
if shebang and shebang.lower() == '#!powershell':
|
||||
if not self._unquote(cmd_parts[0]).lower().endswith('.ps1'):
|
||||
# we're running a module via the bootstrap wrapper
|
||||
cmd_parts[0] = '"%s.ps1"' % self._unquote(cmd_parts[0])
|
||||
cmd_parts.insert(0, '&')
|
||||
wrapper_cmd = "type " + cmd_parts[0] + " | " + self._encode_script(script=bootstrap_wrapper, strict_mode=False, preserve_rc=False)
|
||||
return wrapper_cmd
|
||||
elif shebang and shebang.startswith('#!'):
|
||||
cmd_parts.insert(0, shebang[2:])
|
||||
elif not shebang:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue