Need Assistance with Monitor/Script

Im trying to build a custom monitor that will do the following

Run powershell command and get the value of a property
If the result is equal to Enabled, trigger the alarm.
Unforuneately I have little to no experience with python so unsure of the best way to do this. I have pieced together some other bits of code but I just cant get it working. Any assistance is appreciated.


import os
import sys
import _winreg
import subprocess
import ctypes

def alert(arg):
sys.stderr.write("%d%d%d" % (arg, arg, arg))

# Please use "alert(1)" to turn on the monitor(trigger an alert)
# Please use "alert(0)" to turn off the monitor(disable an alert)
# Please do not change above block and write your script below

def checkSMB1():
ps_command=r'Get-WindowsOptionalFeature -Online -FeatureName SMB1Protocol | Select-Object -ExpandProperty State'
class disable_file_system_redirection:
_disable = ctypes.windll.kernel32.Wow64DisableWow64FsRedirect ion
_revert = ctypes.windll.kernel32.Wow64RevertWow64FsRedirecti on
def __enter__(self):
self.old_value = ctypes.c_long()
self.success = self._disable(ctypes.byref(self.old_value))
def __exit__(self, type, value, traceback):
if self.success:
self._revert(self.old_value)

with disable_file_system_redirection():
process=subprocess.Popen('powershell "%s"'%ps_command, shell=True, stdout=subprocess.PIPE)
result=process.communicate()
ret=process.returncode
return str(result)


checkSMB1()
if checkSMB1() == "Enabled":
alert(1)
print 'SMBv1 is Enabled.'
else:
alert(0)
print 'SMBv1 was not detected.'

Hi @minntech ,

I’ll share how I accomplished this. I rarely use python as well so I’m sure there might be a better way, but this worked for me

The first thing I do, is wrap my powershell script code in python and have python create and execute the script when the procedure/monitor runs on the endpoint.
Here is a small example of the python code that executes a powershell script. I’ll break it down a little below and highlight the important parts


import os
import sys
import _winreg

def alert(arg):
   sys.stderr.write("%d%d%d" % (arg, arg, arg))

# Please use "alert(1)" to turn on the monitor(trigger an alert)
# Please use "alert(0)" to turn off the monitor(disable an alert)
# Please do not change above block and write your script below

ps_content=r"""
#####Powershell Code Starts Here
<#
    Invoke-iTarianAlert
    .Version
     0.0

    .SYNOPSIS
    Sample function that outputs a value to trigger iTarian RMM monitors

    .OUTPUTS
    System.Management.Automation.PSObject

    .NOTES
    Author: EZTechhelp
#>

function Invoke-iTarianAlert
{
  param (
    [String]$Monitored_Condition
  )
 
  if ($Monitored_Condition -eq 'Problem')
  {
    write-output 'alert(1)'
  }
  else
  {
    write-output 'alert(0)'
  }
}
Invoke-iTarianAlert -Monitored_Condition "Problem"
#####Powershell Code Ends Here
"""
print ("Executing Powershell Script")
alertactive = "alert(1)"
alertinactive = "alert(0)"
def ecmd(command):
    import ctypes
    from subprocess import PIPE, Popen
    
    class disable_file_system_redirection:
        _disable = ctypes.windll.kernel32.Wow64DisableWow64FsRedirection
        _revert = ctypes.windll.kernel32.Wow64RevertWow64FsRedirection
        def __enter__(self):
            self.old_value = ctypes.c_long()
            self.success = self._disable(ctypes.byref(self.old_value))
        def __exit__(self, type, value, traceback):
            if self.success:
                self._revert(self.old_value)
    
    with disable_file_system_redirection():
        obj = Popen(command, shell = True, stdout = PIPE, stderr = PIPE)
    out, err = obj.communicate()
    ret=obj.returncode
    if ret==0:
        if out:
            if alertactive in out.strip():
                alert(1)
                print "!!Trigger value detected, raise the alarm!!"
                return out.strip()
            else:
                alert(0)
                print "Trigger value not detected, return to your duties"
                return out.strip()
        else:
            return ret
    else:
        if err:
            return err.strip()
        else:
            return ret

file_name='Invoke-iTarianAlert.ps1'
file_path=os.path.join(os.environ['TEMP'], file_name)
with open(file_path, 'wb') as wr:
    wr.write(ps_content)

ecmd('powershell "Set-ExecutionPolicy RemoteSigned"')
print ecmd('powershell "%s"'%file_path)

os.remove(file_path)

  • Your Powershell script code is placed inbetween the quotes of ps_content
```

ps_content=r"""
#Powershell script code goes here
“”"


<ul>
<li>Python prints an optional message (I like for verbose logging reasons) then sets 1 variable "alertactive"
<li>"Alertactive" contains the value that python will compare to powershells output after its executed
</ul> 

print (“Executing Powershell Script”)
alertactive = “alert(1)”


<ul>
<li>Then this python code is what compares the powershell output to "alertactive" and then triggers or doesnt trigger the alert
</ul> 

if alertactive in out.strip():
alert(1)
print “!!Trigger value detected, raise the alarm!!”
return out.strip()
else:
alert(0)
print “Trigger value not detected, return to your duties”
return out.strip()


<ul>
<li>Its checking if the value of "alertactive" is contained within out.strip() where "out" is the variable that holds all the values/data the powershell script output when it was executed
<li>In our example powershell script, when the script runs, if the $monitor_condition equals "Problem", the powershell script outputs a value of "alert(1)"
<li>So when python checks if powershells output ("out") contains the value of "alertactive" (which we previously set as "alert(1)") it matches in this case so python executes "alert(1)" directly which triggers the RMM alert
<li>It also prints a message that we found an alert trigger. Printed messages are similiar to write-host in powershell and these should show up in procedure logs and alert email messages
</ul> (NOTE: i've seen a varying degree of success with alert emails showing the output. Not sure if a bug. Most of the time it does. Its always shown within the logs for me at least)
<ul>
<li>If powershells output does NOT match "alertactive" (else) then python executes "alert(0)" which tells RMM there is no alert.
<li>"Return out.strip()" tells python to output the output of powershell. So if you have verbose messages in powershell this will allow them to show up in procedure logs and alert emails
</ul> 
The rest of the python code should be fairly straight forward, it takes the ps_content and creates the powershell script (in temp directory), executes the script, then lastly removes the temporary powershell script

I hope that this might help you a little. Again, I'm sure there is a much easier way to do this, but this has worked consistently for me, since I pretty much only use powershell scripts

Thanks!
Mike

Thank you for that detailed explanation. It certainly helps.
I feel like the code I originally posted is very close to achieving the goal - I just wasn’t sure how to check the output string of the powershell command and then compare it to what I expect to trigger the alert.

For example the one-liner ps code that is in there will returned “Enabled” if SMBv1 is installed and enabled. I basically want to take that result of the PS code and say if the output of the ps command returns “enabled” then alert1 otherwise alert0.

Ill see if I can adapt your suggestions to get it working.

Ok so I believe this may be what you want


import os
import sys
import _winreg
import subprocess
import ctypes

def alert(arg): <ul>sys.stderr.write("%d%d%d" % (arg, arg, arg))</ul></ul>
 
# Please use "alert(1)" to turn on the monitor(trigger an alert)
# Please use "alert(0)" to turn off the monitor(disable an alert)
# Please do not change above block and write your script below

def checkSMB1(): <ul>ps_command=r'Get-WindowsOptionalFeature -Online -FeatureName SMB1Protocol | Select-Object -ExpandProperty State'</ul></ul>
  <ul>class disable_file_system_redirection:</ul></ul>
  <ul><ul>_disable = ctypes.windll.kernel32.Wow64DisableWow64FsRedirect ion</ul></ul>
  <ul><ul>_revert = ctypes.windll.kernel32.Wow64RevertWow64FsRedirecti on</ul></ul>
  <ul>def __enter__(self):</ul></ul>
  <ul><ul>self.old_value = ctypes.c_long()</ul></ul>
  <ul><ul>self.success = self._disable(ctypes.byref(self.old_value))</ul></ul>
  <ul>def __exit__(self, type, value, traceback):</ul></ul>
  <ul><ul>if self.success:</ul></ul>
  <ul><ul>self._revert(self.old_value)
 </ul></ul>
  <ul>with disable_file_system_redirection():</ul></ul>
  <ul><ul>process=subprocess.Popen('powershell "%s"'%ps_command, shell=True, stdout=subprocess.PIPE)</ul></ul>
  <ul>result=process.communicate()</ul>
  <ul>ret=process.returncode</ul>
  <ul>if result == "Enabled":</ul>
  <ul><ul>alert(1)</ul>
  <ul><ul>print 'SMBv1 is Enabled.'</ul>
  <ul><ul>return str(result)</ul>
  <ul>else:</ul>
  <ul><ul>alert(0)</ul>
  <ul><ul>print 'SMBv1 was not detected.'</ul>
  <ul><ul>return str(result)</ul>
 
checkSMB1()

This worked for me in a quick test. I basically just moved the output processing into checkSMB1() to grab the powershell output from “result” which gets the piped output from process.communicate()

Alternatively you can also do this, which just grabs the output of the checkSMB() function into a variable that you can validate over and is a minor change from your original code


import os
import sys
import _winreg
import subprocess
import ctypes

def alert(arg): <ul>sys.stderr.write("%d%d%d" % (arg, arg, arg))</ul></ul>
 
# Please use "alert(1)" to turn on the monitor(trigger an alert)
# Please use "alert(0)" to turn off the monitor(disable an alert)
# Please do not change above block and write your script below

def checkSMB1(): <ul>ps_command=r'Get-WindowsOptionalFeature -Online -FeatureName SMB1Protocol | Select-Object -ExpandProperty State'</ul></ul>
  <ul>class disable_file_system_redirection:</ul></ul>
  <ul><ul>_disable = ctypes.windll.kernel32.Wow64DisableWow64FsRedirect ion</ul></ul>
  <ul><ul>_revert = ctypes.windll.kernel32.Wow64RevertWow64FsRedirecti on</ul></ul>
  <ul>def __enter__(self):</ul></ul></ul>
  <ul><ul>self.old_value = ctypes.c_long()</ul></ul>
  <ul><ul>self.success = self._disable(ctypes.byref(self.old_value))</ul></ul>
  <ul>def __exit__(self, type, value, traceback):</ul>
  <ul><ul>if self.success:</ul>
  <ul><ul><ul>self._revert(self.old_value)
 </ul>
  <ul>with disable_file_system_redirection():</ul>
  <ul><ul>process=subprocess.Popen('powershell "%s"'%ps_command, shell=True, stdout=subprocess.PIPE)</ul>
  <ul>result=process.communicate()</ul>
  <ul>ret=process.returncode</ul>
  <ul><ul>return str(result)</ul>
 
smbresults = checkSMB1()
if smbresults == "Enabled": <ul>alert(1)</ul>
  <ul>print 'SMBv1 is Enabled.'</ul>
 else:  <ul>alert(0)</ul>
  <ul>print 'SMBv1 was not detected.'</ul>
 

I feel like Im getting closer. I adjusted the script a bit and now its showing the output is correct - Enabled. However I think there is something wrong with the actual checking part because in Itarian log it shows its running alert(0) command instead of alert(1)


def checkSMB1():
ps_command=r'Get-WindowsOptionalFeature -Online -FeatureName SMB1Protocol | Select-Object -ExpandProperty State'
class disable_file_system_redirection:
_disable = ctypes.windll.kernel32.Wow64DisableWow64FsRedirect ion
_revert = ctypes.windll.kernel32.Wow64RevertWow64FsRedirecti on
def __enter__(self):
self.old_value = ctypes.c_long()
self.success = self._disable(ctypes.byref(self.old_value))
def __exit__(self, type, value, traceback):
if self.success:
self._revert(self.old_value)

with disable_file_system_redirection():
p=subprocess.Popen('powershell "%s"'%ps_command, shell=True, stdout=subprocess.PIPE).communicate()[0]
print p.split()


smbresults = checkSMB1()
if smbresults == "Enabled":
alert(1)
print 'SMBv1 is Enabled.'
else:
alert(0)
print 'SMBv1 was not detected.'

Here is the Itarian log output:


Custom Script Monitor : Standard Output: ['Enabled']
None
SMBv1 was not detected.
- Error Output: 000

Oops I had an error in my last code, sorry about that. The output of the SMB1 function doesn’t EXACTLY equal Enabled or Disabled. So just changed it to check with IN (aka LIKE or MATCH). I’ve verified the below works in a monitor with a test machine that I enabled SMBv1 on


import os
import sys
import _winreg
import subprocess
import ctypes

def alert(arg): <ul>sys.stderr.write("%d%d%d" % (arg, arg, arg))</ul></ul>
 
 
 
 
# Please use "alert(1)" to turn on the monitor(trigger an alert)
# Please use "alert(0)" to turn off the monitor(disable an alert)
# Please do not change above block and write your script below

def checkSMB1(): <ul>ps_command=r'Get-WindowsOptionalFeature -Online -FeatureName SMB1Protocol | Select-Object -ExpandProperty State'</ul></ul>
  <ul>class disable_file_system_redirection:</ul></ul>
  <ul><ul>_disable = ctypes.windll.kernel32.Wow64DisableWow64FsRedirect ion</ul></ul>
  <ul><ul>_revert = ctypes.windll.kernel32.Wow64RevertWow64FsRedirecti on</ul></ul>
  <ul>def __enter__(self):</ul></ul>
  <ul><ul>self.old_value = ctypes.c_long()</ul></ul>
  <ul><ul>self.success = self._disable(ctypes.byref(self.old_value))</ul></ul>
  <ul>def __exit__(self, type, value, traceback):</ul>
  <ul>if self.success:</ul>
  <ul><ul>self._revert(self.old_value)
 </ul>
  <ul>with disable_file_system_redirection():</ul>
  <ul><ul>process=subprocess.Popen('powershell "%s"'%ps_command, shell=True, stdout=subprocess.PIPE)</ul>
  <ul>result=process.communicate()</ul>
  <ul>ret=process.returncode</ul>
  <ul><ul>return str(result)</ul>
 
 
 
 
smbresults = checkSMB1()
if "Enabled" in smbresults: <ul>alert(1)</ul>
  <ul>print 'SMBv1 is Enabled.'</ul>
 
 
 
 else: <ul>alert(0)</ul>
  <ul>print 'SMBv1 was not detected.'</ul>
 
 
 
 

Alternatively, if you want to make sure the output of SMB1 returns only the expected powershell output that is exactly Enabled or Disabled, you can change this section

 <ul>with disable_file_system_redirection():</ul>
  <ul><ul>process=subprocess.Popen('powershell "%s"'%ps_command, shell=True, stdout=subprocess.PIPE)</ul>
  <ul>out, result=process.communicate()</ul>
  <ul>ret=process.returncode</ul>
  <ul>return out.strip()</ul>
 
 
 
 

This pipes the output of the the powershell execution results to variable “out” (similar to my first example). strip() just ensures the output is cleaned of any extra white-spaces from the beginning or end of the string. Then you could use if smbresults == “Enabled”. Personally I’m generally more fond of doing likes and matches (pythons in) just in case extra unexpected output returns

If you want to ensure SMBv1 is always disabled, you could add auto remediation that runs a script to disable SMBv1 if detected on a endpoint

I was able to get it working.
Appreciate all the help.
At least now I have a template to build others like it.
I check tons of stuff with powershell. Im sure there are probably ways to do it directly with python but save me the trouble from having to learn something new.

Glad you got it working. I also stick with powershell as I only work with windows, so its a better choice than python in that scenario. Plus pythons indentation drives me nuts lol