Select Page

Part 1: The SQL Injection Method

Sometimes we need to query for more than one alarm group using the AlmDbViewCtrl ActiveX control in Wonderware InTouch. The AlmDbViewCtrl uses a SQL query to query the Alarm DB Logger’s SQL database, and it does not automatically display alarms and events belonging to subgroups of the current alarm group configured in the GroupName parameter.

Under most circumstances, I would not recommend SQL Injection as a programming method for SCADA. To be perfectly clear, SQL injection is a vulnerability hack generally used to maliciously modify or gain unauthorized access to a database or computer. On a secured SCADA system, however, this method will allow us to query the alarms and events database for the information we want to see.

I’d really love to see Wonderware add a method to this ActiveX control to handle multiple alarm groups in a similar fashion across all their Alarm Viewer ActiveX controls, but until that day comes, here’s how to inject some SQL tidbits into the ActiveX controls SQL query to display alarms from all the groups we’d like to see.

The AlmDbViewCtrl.GroupName Property

In the following InTouch script, I’m using the AlarmTreeViewerCtrl control on my alarm history window to display my alarm groups. When I click on an alarm group, I update the AlmDbViewCtrl control to display the active alarm group’s alarm history.

{Dynamically update single alarm group in AlmDbViewCtrl}
#AlmDbViewCtrl.GroupName = #AlarmTreeViewerCtrl.ElementSelection;
#AlmDbViewCtrl.Refresh

Let’s assume that we’ve selected an alarm group in AlarmTreeViewerCtrl called RTUs. In the code above, clicking the RTUs alarm group in the AlarmTreeViewerCtrl will automatically set the AlmDbViewCtrl’s GroupName property to “RTUs” and refresh the data to display only alarms belonging to the RTUs alarm group. Let’s also assume the RTUs alarm group has several child groups called Tank1, Tank2, PS1, and PS2 like so:

Alarm Groups

Query for Multiple Alarm Group “Areas”

Since the GroupName property only allows me to set one alarm group name, I wouldn’t be able to display the alarms from the RTUs group and all its child groups at the same time using the code snippet above. So we have to go about this in a different way. Enter SQL injection (SQLi). SQL injection is really just a matter of injecting a small piece of SQL code into a full SQL query to alter the query to return different data than it normally would. I’m going to simplify the following query for the readability’s sake, so let’s say we have the following SQL query:

SELECT * FROM v_AlarmEventHistoryInternal WHERE Area = 'RTUs'

This is a simplified version of the actual SQL query the AlmDbViewCtrl is using to get data from the WWALMDB database when the GroupName property is set to “RTUs”. Using SQL injection, we can append more Area = ” statements just after the word RTUs and before the last single-quote.

Let’s start off by querying for RTUs and Tank1 alarms simultaneously. We’re going to set the AlmDbViewCtrl.GroupName property like so:

IF #AlarmTreeViewerCtrl1.ElementSelection == "RTUs" THEN
#AlmDbViewCtrl2.GroupName = "RTUs' OR Area = ‘Tank1";
ENDIF;

Notice how I didn’t use a single quote before RTUs or after Tank1. This is because we are injecting, or inserting, RTUs’ OR Area=’Tank1 into the SQL query above, and there are already beginning and ending single-quotes around the word RTUs. Our modified, SQL injected query will tank this query

SELECT * FROM v_AlarmEventHistoryInternal WHERE Area = 'RTUs'

and turn it into this query with the injected SQL underlined for clarity:

SELECT * FROM v_AlarmEventHistoryInternal WHERE Area = 'RTUs' OR Area = 'Tank1'

We can modify our injected SQL to query for the PS1 alarm group alarms as in addition to RTUs and Tank1 alarms.

IF #AlarmTreeViewerCtrl1.ElementSelection == "RTUs" THEN
#AlmDbViewCtrl2.GroupName = "RTUs' OR Area = ‘Tank1' OR Area = 'PS1";
ENDIF;

In order to display the Tank2 and PS2 alarms, we could continue explicitly OR-ing the Area values together in the GroupName property or we could use a wildcard in a SQL LIKE operator like this:

IF #AlarmTreeViewerCtrl1.ElementSelection == "RTUs" THEN
#AlmDbViewCtrl2.GroupName = "RTUs' OR Area LKE ‘Tank%' OR Area LIKE 'PS%";
ENDIF;

By replacing the unique part of each alarm group with the % symbol, the resulting SQL query will return alarms from all areas the begin with “Tank” or “PS”.

Query for ALL Alarm Group Areas

Last, what if we want to query for ALL the available alarm groups. We could manually inject each alarm group into the GroupName property, or we could use an always true variable as a wildcard to return all the alarm groups with a lot less typing. The trick is to specify one alarm group, usually the top-most parent group, and an always true expression like ‘x’ = ‘x’. Here’s an example:

IF #AlarmTreeViewerCtrl1.ElementSelection == "RTUs" THEN
#AlmDbViewCtrl2.GroupName = "RTUs' OR 'x'='x";
ENDIF;

Limitations

The biggest drawback to using the SLQ injection method is InTouch’s 131-character string limit. The GroupName property string is limited to 131 characters and will be truncated if the property string is longer than 131 characters. However, by strategically naming or prefixing child alarm groups, we can use the SQL injection method to easily query for many combinations of alarm groups.

Check back in a couple weeks for Part 2 where we will dive into modifying the AlarmFavorites file at runtime as another method to display multiple alarm groups in the AlmDbViewCtrl control in InTouch.