David Waugh

Using an ESA Rule to run a command on a windows machine

Blog Post created by David Waugh Employee on Dec 22, 2016

A colleague here at RSA posed an interesting problem so I thought I would share with you how I solved it.


Imagine the following scenario. A windows machine has visited a potentially suspicious website. This is detected by an ESA rule and an incident would be created. Normal SOC procedures say that when such an incident happens the ECAT Agent should be installed on the windows machine so that further analysis can take place. It is a busy day in the SOC and unfortunately there is a long time gap between when the agent is actually installed. Can this actually be automated?


The answer is yes and here is how....


First of all, I have a centos 6 server where I will run the commands from. This is to avoid installing any extra packages on the ESA Server that could potentially cause problems.


On the centos 6 server I have installed the openvas-smb package. This provides the winexe linux command.

This is a tool that allows you to run psexec type commands from a linux machine.


[root@centos6 ~]# yum install wmi
Loaded plugins: fastestmirror, refresh-packagekit
Setting up Install Process
Loading mirror speeds from cached hostfile
* atomic: mirror1.34sp.com
* base: mirror.steadfast.net
* centosplus: mirror.steadfast.net
* contrib: mirror.steadfast.net
* epel: mirror.ibcp.fr
* extras: mirror.steadfast.net
* updates: mirror.steadfast.net
Package wmi is obsoleted by openvas-smb, trying to install openvas-smb-1.0.1-1.el6.art.x86_64 instead
Resolving Dependencies
--> Running transaction check
---> Package openvas-smb.x86_64 0:1.0.1-1.el6.art will be installed
--> Processing Dependency: libroken.so.18(HEIMDAL_ROKEN_1.0)(64bit) for package: openvas-smb-1.0.1-1.el6.art.x86_64
--> Processing Dependency: libkrb5.so.26(HEIMDAL_KRB5_2.0)(64bit) for package: openvas-smb-1.0.1-1.el6.art.x86_64
--> Processing Dependency: libhdb.so.9(HEIMDAL_HDB_1.0)(64bit) for package: openvas-smb-1.0.1-1.el6.art.x86_64
--> Processing Dependency: libgssapi.so.3(HEIMDAL_GSS_2.0)(64bit) for package: openvas-smb-1.0.1-1.el6.art.x86_64
--> Processing Dependency: libgnutls.so.28(GNUTLS_1_4)(64bit) for package: openvas-smb-1.0.1-1.el6.art.x86_64
--> Processing Dependency: libasn1.so.8(HEIMDAL_ASN1_1.0)(64bit) for package: openvas-smb-1.0.1-1.el6.art.x86_64
--> Processing Dependency: atomic-gnutls3-gnutls for package: openvas-smb-1.0.1-1.el6.art.x86_64
--> Processing Dependency: atomic-glib2-glib2 for package: openvas-smb-1.0.1-1.el6.art.x86_64
--> Processing Dependency: libwind.so.0()(64bit) for package: openvas-smb-1.0.1-1.el6.art.x86_64
--> Processing Dependency: libroken.so.18()(64bit) for package: openvas-smb-1.0.1-1.el6.art.x86_64
--> Processing Dependency: libkrb5.so.26()(64bit) for package: openvas-smb-1.0.1-1.el6.art.x86_64
--> Processing Dependency: libhx509.so.5()(64bit) for package: openvas-smb-1.0.1-1.el6.art.x86_64
--> Processing Dependency: libheimntlm.so.0()(64bit) for package: openvas-smb-1.0.1-1.el6.art.x86_64
--> Processing Dependency: libhdb.so.9()(64bit) for package: openvas-smb-1.0.1-1.el6.art.x86_64
--> Processing Dependency: libhcrypto.so.4()(64bit) for package: openvas-smb-1.0.1-1.el6.art.x86_64
--> Processing Dependency: libgssapi.so.3()(64bit) for package: openvas-smb-1.0.1-1.el6.art.x86_64
--> Processing Dependency: libgnutls.so.28()(64bit) for package: openvas-smb-1.0.1-1.el6.art.x86_64
--> Processing Dependency: libasn1.so.8()(64bit) for package: openvas-smb-1.0.1-1.el6.art.x86_64
--> Running transaction check
---> Package atomic-glib2-glib2.x86_64 0:2.41.0-2.el6.art will be installed
---> Package atomic-gnutls3-gnutls.x86_64 1:3.1.25-1.el6.art will be installed
--> Processing Dependency: libnettle.so.4()(64bit) for package: 1:atomic-gnutls3-gnutls-3.1.25-1.el6.art.x86_64
--> Processing Dependency: libhogweed.so.2()(64bit) for package: 1:atomic-gnutls3-gnutls-3.1.25-1.el6.art.x86_64
---> Package heimdal-libs.x86_64 0:1.6.0-0.9.20140621gita5adc06.el6 will be installed
--> Running transaction check
---> Package nettle.x86_64 0:2.7.1-3.el6.art will be installed
--> Finished Dependency Resolution

Dependencies Resolved

Package Arch Version Repository Size
openvas-smb x86_64 1.0.1-1.el6.art atomic 3.3 M
Installing for dependencies:
atomic-glib2-glib2 x86_64 2.41.0-2.el6.art atomic 2.2 M
atomic-gnutls3-gnutls x86_64 1:3.1.25-1.el6.art atomic 610 k
heimdal-libs x86_64 1.6.0-0.9.20140621gita5adc06.el6 epel 1.0 M
nettle x86_64 2.7.1-3.el6.art atomic 307 k

Transaction Summary
Install 5 Package(s)

Total download size: 7.4 M
Installed size: 28 M
Is this ok [y/N]: y
Downloading Packages:
(1/5): atomic-glib2-glib2-2.41.0-2.el6.art.x86_64.rpm | 2.2 MB 00:02
(2/5): atomic-gnutls3-gnutls-3.1.25-1.el6.art.x86_64.rpm | 610 kB 00:00
(3/5): heimdal-libs-1.6.0-0.9.20140621gita5adc06.el6.x86_64.rpm | 1.0 MB 00:01
(4/5): nettle-2.7.1-3.el6.art.x86_64.rpm | 307 kB 00:00
(5/5): openvas-smb-1.0.1-1.el6.art.x86_64.rpm | 3.3 MB 00:01
Total 1.1 MB/s | 7.4 MB 00:06
Running rpm_check_debug
Running Transaction Test
Transaction Test Succeeded
Running Transaction
Warning: RPMDB altered outside of yum.
Installing : atomic-glib2-glib2-2.41.0-2.el6.art.x86_64 1/5
Installing : heimdal-libs-1.6.0-0.9.20140621gita5adc06.el6.x86_64 2/5
Installing : nettle-2.7.1-3.el6.art.x86_64 3/5
Installing : 1:atomic-gnutls3-gnutls-3.1.25-1.el6.art.x86_64 4/5
Installing : openvas-smb-1.0.1-1.el6.art.x86_64 5/5
Verifying : nettle-2.7.1-3.el6.art.x86_64 1/5
Verifying : heimdal-libs-1.6.0-0.9.20140621gita5adc06.el6.x86_64 2/5
Verifying : openvas-smb-1.0.1-1.el6.art.x86_64 3/5
Verifying : atomic-glib2-glib2-2.41.0-2.el6.art.x86_64 4/5
Verifying : 1:atomic-gnutls3-gnutls-3.1.25-1.el6.art.x86_64 5/5

openvas-smb.x86_64 0:1.0.1-1.el6.art

Dependency Installed:
atomic-glib2-glib2.x86_64 0:2.41.0-2.el6.art atomic-gnutls3-gnutls.x86_64 1:3.1.25-1.el6.art heimdal-libs.x86_64 0:1.6.0-0.9.20140621gita5adc06.el6
nettle.x86_64 0:2.7.1-3.el6.art



After installing the package I then created a file called credentials.cfg containing the account I would use to run the commands on my windows target machine.




I was then able to run command from my centos 6 server to a windows machine as follows:


root@centos6 ~]# winexe -A credentials.cfg // 'cmd.exe /c echo "hello"'
You have mail in /var/spool/mail/root
[root@centos6 ~]# winexe -A credentials.cfg // 'hostname'


This now gives us the capability of running commands from our Centos 6 machine onto a windows machine.


The next part of the puzzle was to allow the ESA Server to be able to connect to our Centos 6 Server to be able to run commands. We set up passwordless SSH keys to do this. The important thing here is that when scripts run on the ESA Server, they are run under the user "notification" so we have to set this up as follows:


SSH onto the ESA Server as root, then type sudo su notification

This switches us to the user notification.


[notification@rsaesa tmp]$ whoami
[notification@rsaesa tmp]$ cd ~
[notification@rsaesa .ssh]$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/notification/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/notification/.ssh/id_rsa.
Your public key has been saved in /home/notification/.ssh/id_rsa.pub.
The key fingerprint is:
d9:ed:61:48:db:c1:aa:75:d5:b7:64:3f:ac:1c:c4:2b notification@rsaesa
The key's randomart image is:
+--[ RSA 2048]----+
| |
| .. . |
| . oo.oo|
| + *.o= +|
| S *E*o +.|
| o +o.o .|
| . .o |
| |
| |
[notification@rsaesa .ssh]$ ssh-copy-id root@centos6.waugh.local
root@centos6.waugh.local's password:
Now try logging into the machine, with "ssh 'root@centos6.waugh.local'", and check in:


to make sure we haven't added extra keys that you weren't expecting.

[notification@rsaesa .ssh]$ ssh root@centos6.waugh.local "winexe -A credentials.cfg // 'hostname'"
[notification@rsaesa .ssh]$ exit


Here we can see that we can run a command on the ESA Server, which will run a command on our Centos 6 Server which will then actually run a command on our target windows machine.


We now define our ESA Rule. My ESA Rule fires whenever a suspiciousIP is detected.



My ESA Script is as follows (Attached as file ESAScript to import into the Global Notifications Page). Note this is python so indentations matter. Please import the file into your system to see the format of the script.


#!/usr/bin/env python
from smtplib import SMTP
import subprocess
import datetime
import json
import sys

def dispatch(alert):
The default dispatch just prints the 'last' alert to /tmp/esa_alert.json. Alert details
are available in the Python hash passed to this method e.g. alert['id'], alert['severity'],
alert['module_name'], alert['events'][0], etc.
These can be used to implement the external integration required.
with open("/tmp/esa_alert.json", mode='w') as alert_file:
alert_file.write(json.dumps(alert, indent=True))

def read():
# Define our metakey tuples
# Each tuple is ["metakey","meta key description, "value"]
metakeys=[ ["device_type","Device Type: ",""], \
["device_ip","Device IP: ",""] , \
["device_class","Device Class: ",""] , \
["ip_src","Source IP: ",""] , \
["ip_dst","Dest IP: ",""] , \
["ip_srcport","Source Port: ",""], \
["ip_dstport","Dest Port: ",""] , \
["ec_activity","Activity: ",""] ,\
["ec_subject","Subject: ",""] ,\
["ec_theme", "Theme: ",""] ,\
["event_description", "Description: ",""] ,\
["event_cat_name","Category: ",""] ,\
["msg_id", "Message ID: ",""] ,\
["event_source_id", "Concentrator: ",""] \

# Keys we want in our Subject
subj_metakeys=[ ["device_type","Device Type: ","" ] , \
["msg_id", "Message ID: ",""] ]

sa_server = ''
brokerid = '6'
smtp_server = ''
smtp_port = '25'
smtp_user = ''
smtp_pass = ''
from_addr = "RSA Security Analytics <RSA@SecurityAnalytics.com>"
to_addr = ['securityanalytics@waugh.com']

# Get data from JSON
esa_alert = json.loads(open('/tmp/esa_alert.json').read())
#Extract Variables (Add as required)

module_name = esa_alert["module_name"]
except KeyError:
module_name = "null"

sessionid = str(esa_alert["events"][0]["sessionid"])
except KeyError:
sessionid = "null"
ip_src = str(esa_alert["events"][0]["ip_src"])
except KeyError:
ip_src = "null"

#Extract Values for each of our Variables
for tuples in metakeys:
tuples[2] = str(esa_alert["events"][0][tuples[0]])
except KeyError:
tuples[2] = "null"

#Extract Our Subject Variables
for tuples in subj_metakeys:
tuples[2] = str(esa_alert["events"][0][tuples[0]])
except KeyError:
tuples[2] = "null"

event_source_id =esa_alert["events"][0]["event_source_id"]
except KeyError:
event_source_id = "null"

#Work out Concentrator ID depending on Event Source
if event_source_id.startswith( "" ):
elif event_source_id.startswith( "" ):
elif event_source_id.startswith( "CONC3" ):
elif event_source_id.startswith( "CONC4" ):

# Sends Email
smtp = SMTP()

# Runs command
mycommand = "ssh root@centos6.waugh.local \"winexe -A credentials.cfg //" + ip_src + " \'hostname\'\""
myresult = ""

# Runs a System Command
p = subprocess.Popen(mycommand,shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in p.stdout.readlines():
myresult = myresult + line + "\n",
retval = p.wait()

date = datetime.datetime.now().strftime( "%d/%m/%Y %H:%M" )
subj = ( module_name ) + " :: " + ( date )
for subj_meta in subj_metakeys:
subj += ":: " + ( subj_meta[2])

header = "Use the Investigation tab within Security Analytics to view more details related to this alert.\n\n"
header += ""
header += str( concid )
header += "/navigate/event/DETAILS/"
header += str( sessionid )
header += "\n\n"

message_text = "Alert Name: \t\t%s\n" % ( module_name )
message_text += "Date/Time: \t\t%s\n" % ( date )
message_text += "Command: \t\t%s\n" % ( mycommand )
message_text += "Return Value: \t\t%s\n" % ( retval )
message_text += "Result: \t\t%s\n" % ( myresult )

for tuple in metakeys:
message_text += tuple[1] + "\t\t%s\n" % ( tuple[2] )

body_text= header + message_text
msg = "From: %s\nTo: %s\nSubject: %s\nDate: %s\n\n%s\n" % ( from_addr, to_addr, subj, date, body_text )

smtp.sendmail(from_addr, to_addr, msg)

if __name__ == "__main__":


You can import the rule as follows:


After this, when the ESA Rule is triggered, the command will be run and you will also get an email of the results of the command.


Sample Email Body:




Here what we have achieved is that we run the hostname command on the machine when the ESA Rule triggers.


If you wanted to install ECAT for example we need to be a bit more inventive.


On our centos command server we use the following file 


more installECAT.sh

#Mount the remote machine to copy over ECAT Agent
mount -t cifs //$1/c$ /mnt/temp-mount -o credentials=~/credentials.cfg
#Mount our ECAT source directory where the Agent.exe package lives
mount -t cifs //$/Packages/ECAT /mnt/ecat -o credentials=~/credentials.cfg
cp -rf /mnt/ecat/ecatagent43proxy.exe /mnt/temp-mount/ecatagent43proxy.exe
winexe -A credentials.cfg //$1 'cmd.exe /c "c:\ecatagent43proxy.exe"'
rm /mnt/temp-mount/ecatagent43proxy.exe
umount /mnt/temp-mount


To install ECAT on a remotemachine we would run ./installECAT.sh


We now incorporate this into our ECATInstall Script, by replacing the existing command line with:

# Runs command
mycommand = "ssh root@centos6.waugh.local \"./installECAT.sh " + ip_src + "\""


When we ping a suspiciousIP, ECAT then gets installed on the windows machine.