Mitigating Code Repurposing Attacks


As I mentioned in a previous
blog
, there are some pretty creative (and destructive) things people can do
with your code if you’re not careful. Just as a kitchen knife can be used to
cut cheese or to kill someone, so your code can be used to increase
productivity or wreak digital havoc.

It’s not all bad news
though. There are several things you can do to help mitigate attacks based on
code repurposing, although unfortunately there is no panacea and many of these
techniques will not be applicable to all given scenarios. A much shorter
article that mentions some key mitigating factors can be found in the VSTO
documentation online
. But if you’ve got a spare chunk of time, by all means
read on.

Thanks to Siew Moi, Mike
Howard, and the Office security folks for giving this a once-over before
posting 🙂

Root Cause

The root cause of
repurposing comes from trusting input
data
. Michael Howard has a lot to say about this in Chapter 10 of Writing Secure Code 2nd
Edition
(aptly titled All Input is
Evil
), but in repurposing attacks it’s not just your code that is trusting
potentially bad data, it’s the end-user, too!

With a typical
web-based application, checking your inputs isn’t so hard since you mostly need
to worry about things like form fields, query strings, and HTTP headers. (You
may also need to worry about any external systems you connect to, such as a
database, if that figures into your threat model). In most cases, you can
assume that the server your application is being hosted on is trustworthy,
along with any other data on it such as configuration files, web page content,
and so on.

The problem with
Office-based development is that your code is hosted inside a very dynamic,
very mobile container (the document), and in general you cannot assume that any part of that document’s content is
trustworthy.

Not the warning text
you put in BIG BOLD WARNING TEXT at
the top of the document.

Not the names or
positions of the controls on the document.

Not the hidden
worksheets where you store database connection strings.

Not the
configuration settings you put inside a hidden region with white text on a
white background in 1 point WingDings font.

Nothing.

So our goal is to do
one or more — always think of “defence in depth” —  of the following things:

  1. Eliminate our dependence on information
    inside the document

  2. Ensure that the information in the
    document is trustworthy

  3. Check that the information inside the
    document falls within a “reasonable” range

  4. Provide cues to the user about the
    purpose of the code they are running

  5. Elicit explicit user consent before
    performing potentially harmful actions

  6. Probably some more things here!

So let’s look at
these in turn.

Eliminate dependence on untrustworthy information

In my previous blog,
I used the example of a “Format my Drive” document that contained
instructions for formatting your hard drive, along with Yes and No buttons that
invoked the appropriate code. In this sample, the code doesn’t make any decisions based on inputs (it has none), but
the user does. The code assumes that
the user will always be presented with adequate contextual information (eg,
“clicking this button will format your drive!”), but this is not the
case. The user has no idea that there is no
link
between the text surrounding the buttons and the actions the buttons
take; they think if the text says “Download unlimited free MP3s now!”
then that’s what the button will do.

Oops!

So even in this
case, the code implicitly trusts its “inputs” (the user invoking the
code) and it should not be. Something as dangerous as a “Format my
Drive” control should provide additional feedback to the user about the
actions they are about to perform, as noted in one of the next sections of this
blog.

It’s also a good
idea to think about the things you can
depend on. Resources on the local machine, such as files or registry keys,
should in general be trustworthy (see note below!). So should other servers
that your code communicates with, as long as they are under your control. And
of course you can trust your own code not to have been tampered with, as long
as it is signed or living in a secure location.

NOTE: I said above that you can trust the local machine. This assumes we
are trying to mitigate against repurposing attacks where one user sends a
malformed document to another user in an attempt to get them to do something harmful to themselves. You
cannot trust resources on the client
computer in other scenarios, such as when you are building a server-side
solution, because then a hostile client could be used to subvert your security
system. Servers need to protect themselves from malicious clients, and clients
need to protect themselves from malicious servers. Different scenarios call for
different threat models and different mitigation strategies.

So if your solution
needs to persist data such as user preferences or other snippets of information
that you come to rely on, you can write it to the registry or to a config file
(not in %ProgramFiles%, but in %UserProfile%) or use some other
mechanism to persist it. Just don’t persist it in the document, because then
you’ll never be able to read it out again without worrying about the contents.

Contacting servers
is a bit trickier. You might want to contact, say, a trusted web service on http://myserver/ to download information, but if
you hard-code that URL into your solution then you will have trouble when you
need to move the service to another machine. The obvious answer is to stick the
URL of the server inside the document… but you can’t do that because it’s not
trustworthy! This is where you could do something like an Active Directory
lookup to figure out which server to contact. I’ve been told it’s possible, but
I’m not an AD expert so you’ll have to figure that one out on your own ;-).

Ensure the information is trustworthy

Let’s say you
absolutely have to rely on the information inside the document, for whatever
reason. Now you have to make sure the content in the document is as trustworthy
as your code itself, which means severely limiting what can be done with the
document.

Two properties of
code that makes it “easy” to secure in the CLR are that:

i)                   
You can
sign the code, which can be used to detect any modifications made to it

ii)                 
You can
keep it in a fixed location, which can be used to ensure nobody can modify /
overwrite it

Unfortunately,
neither of these two properties apply to documents in the general sense. The
whole point of having document-based solutions is so that you can modify them
(thereby making signatures pretty useless) and that you can send them around to
people, copy them to your hard drive, and so on (thereby making location pretty
useless). Nevertheless, depending on the scenario you may be able to do one of
these things in your solutions.

Signing a document
in Office 2003 is pretty easy — go to Tools
-> Options -> Security and click on the Digital Signatures button, where you
can select one of your certificates and add your signature to the document.
Once the document is signed, no-one can tamper with it in any way without
breaking the signature, but as I mentioned in my Old
Fashioned Security blog
, there’s a big problem with signing content:

  • Unless the recipient expects the
    document to be signed (and knows how to verify it), it doesn’t really help
    you. An attacker will just remove the signature altogether and go on their
    wicked way

So either you have
to educate all your users about how to inspect digital signatures, or you can
do something about it yourself.

In Word, you can use
the object model inside your code to check if the active document contains a
signature, and if so if it is from the right person (ie, you) and if it is
valid. Inside your startup code, you can do something similar to the following
to ensure that your code is running inside a genuine document that you created
(if Rob had finished the
WordML to HTML transform by now, this would be in glorious Technicolor,
but alas he hasn’t so it’s not):

  Private Sub
ThisDocument_Open() Handles ThisDocument.Open



    Dim signature As Office.Signature

    Dim foundSignature As Boolean = False



    If
(ThisDocument.Signatures.Count < 1) Then

      MessageBox.Show(“This document
is not signed.”)

      ‘ Take
appropriate action here…

      ThisDocument.Close()

    End If



    For Each signature In
ThisDocument.Signatures

      ‘ Bail out early
on invalid signatures

      If
((signature.IsValid = False) Or _

        (signature.IsCertificateRevoked
= True)) Then

        MessageBox.Show(“This
document’s signature is bad.”)

        ThisDocument.Close()

      End If



      ‘ Add the
appropriate strings here

      If
((signature.Signer = “ACME Corp”) And _

        (signature.Issuer =
“Verisign”)) Then

        
You could also check the sign date if you want

        MessageBox.Show(“This
document is signed correctly.”)

        foundSignature = True

        Exit
For

      End If

    Next



    If
(foundSignature = False) Then

      MessageBox.Show(“This
document’s signature is missing.”)

      ‘ Take
appropriate action here…

      ThisDocument.Close()

    End If



  End Sub

There’s a bit of a
chicken-and-egg problem here for both VBA and VSTO developers here, but for
different reasons. With VBA, because the code is included in the document’s
signature you have to sign the document after
you have written and tested your code. This could require you to build, test,
and re-sign your document many times, or it could require you to have some kind
of mode where the document ignores invalid signatures while in development
(remember, this cannot be a flag
stored in the document itself, but it could be a registry key you use to
disable signature checking on your dev box). With VSTO, the assembly is built
and signed independently from the document, but since the custom properties
which link to the assembly form part of the signature, it is not possible to
move the assembly after the document is signed, so that must be done as the
final step before officially releasing the document.

This
“problem” of having the code (or the link to the code) be part of the
signature actually helps us prevent some attacks as well. Imagine, as outlined
in my previous blog, that you have a Budget document and an HR document. Both documents
are signed, and both assemblies are signed, but by some horribly bad
coincidence, it’s possible to point the HR document at the Budget code (or
vice-versa) and do some damage to the document recipient. This will not work,
since swapping out the code (or the link to the code) will break the signature.
The only real attack possible here is if you can take over the URL at which the
linked code lives, in which case you could replace the HR assembly with the
Budget assembly. So you need to make sure your servers are protected from
having unauthorised people uploading content!

One potential
problem here is that the Signature
object in Word only tells you the name of the signer and the authority that
issues that certificate, but not the rest of the trust chain up to the root
authority. It is possible (but unlikely) to have two signatures, both created
by “Bob Smith” and issued by “ACME Corp Authority”, but for
them to be two different Bob Smiths issued from two different ACME Corp Authorities.
(Thankfully you still have to trust the root authority, so it’s not as if any
old hacker could create such a hierarchy in order to fool your code, but it’s
something to keep in mind). Siew Moi has written a great
article on Word signatures
, and it includes a download that has some more
code to play with.

Unfortunately, there
is no programmatic support for checking digital signatures in Excel, so you
cannot use this technique. Also, you may not have the money to buy a
certificate from Verisign or Thawte or one of the other big vendors, and
you may not have your own PKI servers with which to issue your own
certificates. In this case, you can “tie” your document to a specific
location, such as a trusted (and read-only) share on a server, or to a specific
“install” location on the user’s machine. (This is similar to how the
SiteLock
feature works for ActiveX controls). At startup, you can check that the
document’s location is where it is supposed to be, and take appropriate actions
(such as closing the document) if the document comes from a suspicious
location. This has the problem of baking URLs or other locations into your
code, which means it’s impossible to ever move the solution if you need to, so
you may want to use a Registry key or Active Directory property or some other
external (but still trusted) mechanism to figure out if the document is hosted
in a secure location:

  Private Sub
ThisDocument_Open() Handles ThisDocument.Open



    If (ThisDocument.FullName
<> _

      “\appserversecuremyapp.doc”) Then

      MessageBox.Show(“This document
is not secure”)

      ThisDocument.Close()

    
End If



  End Sub

Obviously if you
require your document to be signed, that means the user can never actually
modify it and then save it. And if you require it to be in a specific location,
that means the user can’t put it on their desktop or mail it to a friend.
That’s kind of a problem for most documents, since they are designed to be
modified and saved and moved around. (I show a slightly less draconian version
of this in the next section).

One way around this
is to have your “document” act like a mini application hosted inside
Word or Excel, and have it load and save its own “documents”. This is
made super-easy by the XML support in Word and Excel 2003, where you can import
or export the XML content of a document to a separate file without actually
saving all the markup and other stuff that makes up your
“application”. As long as your solution lends itself well to having only
the data saved and loaded into your static template, you should be good to go
— just provide your own “Open” and “Save” mechanisms
inside your document that load and save XML files that are pumped right into
the mapped XML structure of your document.

Here’s some sample
code for doing it in Excel — Word is harder since you can’t automatically
import XML into a Word document; you need to load the DOM yourself and insert
text into each node of the document. For this simple solution, you can create a
spreadsheet with pretty formatting, etc. and then do the following:

1)    
Map an
XML Schema into the document (a really basic one follows if you need it)

2)    
Add a
single Command Button from the Controls
Toolbox
toolbar. Open the Properties window for it and give it a name of cmdLoad. Delete the Caption so it is blank.

3)    
(Optional)
Digitally sign the document with a certificate to prove that you can do this
without modifying the content

Here’s a simple schema you can map to a spreadsheet. Save it
with an extension of .XSD

 

<?xml version=”1.0″ encoding=”utf-8″ ?>

<
xs:schema id=”simple”

  targetNamespace=”http://tempuri.org/simple.xsd”

  elementFormDefault=”qualified”

  xmlns=”http://tempuri.org/simple.xsd”

  xmlns:mstns=”http://tempuri.org/simple.xsd”

  xmlns:xs=”http://www.w3.org/2001/XMLSchema”>

  <xs:element name=”SimpleTest”>

    <xs:complexType>

      <xs:sequence>

        <xs:element name=”FirstName” type=”xs:string” />

        <xs:element name=”LastName” type=”xs:string” />

      </xs:sequence>

    </xs:complexType>

  </xs:element>

</
xs:schema>

And here’s the code you can use – just dump it into the default codespit
of a VSTO solution, nuking the original ThisWorkbook_Open
handler:

 

  Private WithEvents
cmdLoad As MSForms.CommandButton



  ‘ Called when the workbook is opened.

  Private Sub ThisWorkbook_Open() Handles
ThisWorkbook.Open

    InitialiseUI()

  End Sub



  Private Sub InitialiseUI()

    cmdLoad = FindControl(“cmdLoad”)



    If (cmdLoad Is Nothing) Then

      MsgBox(“Document is
corrupt!”)

    End If



    cmdLoad.Caption = “Load XML”

    cmdLoad.AutoSize = True

  End Sub



  Private Sub cmdLoad_Click() Handles
cmdLoad.Click

    LoadXml()

  End Sub



  Private Sub LoadXml()

    ‘ Gather the filename through
a custom dialog, etc.

    Dim filename As String =
“C:tempexport.xml”

    ThisWorkbook.XmlMaps(1).Import(filename)



    ‘ Don’t prompt user to save

    ThisWorkbook.Saved = True

  End Sub



  Private Sub SaveXml()

    ‘ Gather the filename through
a custom dialog, etc.

    Dim filename As String =
“C:tempexport.xml”

    ThisWorkbook.XmlMaps(1).Export(filename, True)



    ‘ Don’t prompt user to save

    ThisWorkbook.Saved = True

  End Sub



  
‘ Handle the Save event to just
save the data

  Private Sub ThisWorkbook_BeforeSave( _

    ByVal SaveAsUI As Boolean, ByRef Cancel As Boolean) _

    Handles
ThisWorkbook.BeforeSave

    SaveXml()

    Cancel = False

  End Sub

Another way to
ensure that the information in the document is trustworthy without resorting to
signatures or site-locking your document is to ensure that you create it all
yourself, from within your code, and that you make it easily understandable by
the user. For example, if you have a button on a Word document that will format
the hard drive when it is clicked, don’t just give the button a caption of Yes and rely on the surrounding text to
explain what it does. A more descriptive caption, such as

Format Drive

C: will be much better.

And don’t just set
the Caption of the button using the
property grid in Word or Excel, since that value can be changed by the attacker
and is not stored as part of your VBA or .NET assembly. Even if your code is
signed, the attacker can change that property and not invalidate the signature
(it will invalidate the signature of
the document, if it has one, but it won’t
invalidate the signature of the code). Instead, you should explicitly set the
caption of your button during your initialisation code, so that it always says
what you want it to say. This is what I did above in the sample Excel code.

Don’t rely on
automatically generating warning text into the document though (eg, injecting
This will format your drive!
in big red letters at the start of the document), since no matter what you do
the bad guys will probably figure out a way of obscuring it (hiding the region,
placing a white bitmap over the top of the text, etc.).

If you need more UI
than you can get by putting captions on your buttons, then you need to
implement some kind of form (see below) or <gasp> build an ActiveX
control where you can display all your information in a reliable manner. If you
don’t have to have your control hosted directly on the document surface, a
SmartDocument solution offers a really good alternative because the attacker
has no way to influence what appears in the Task Pane. You can be sure that any
warning text or other UI you place in the Task Pane will be there when the user
runs the solution.

Another way to avoid
“untrustworthy” callers in VBA is to make your methods Private (VSTO does not have this
problem since there is no way through the Office UI to invoke managed code). If
you are placing controls on the document surface, make sure you use ActiveX
controls (from the “Control Toolbox” toolbar), not Forms controls
(from the “Forms” toolbar). Adding an event handler to an ActiveX
control creates a private method that cannot be invoked by any means other than
firing the event on the named control, whereas the macros you assign to Forms
controls are just public subroutines that can be hooked up to any old event
source. Also, since Forms controls aren’t programmable, you can’t
programmatically change their content (as described above).

If you are building CommandBars or menu items, don’t use
the Office UI to hookup event handlers, since again they must be public and
hence repurposable (swap the “Save” event handler for the
“Delete” one…). Instead you should build up the CommandBar programmatically (so you can
set the text and images on each control as outlined above) and declare all your
controls inside your solution WithEvents
so you can handle the events with a private handler, not a public sub. There is
some sample code for VSTO in this
article
, and the sample solutions that ship with the product also show how
to create menus and toolbars. Some sample VBA code is below:

 

  Private WithEvents
cmdSave As CommandBarButton

  Private WithEvents
cmdDelete As CommandBarButton



  Private Sub
Document_Open()

    CreateCommandBar

  End Sub



  Private Sub CreateCommandBar()

    Dim bar As CommandBar

    Dim i As Integer

    Dim oldContext As
Object

  

    On Error GoTo Handler

  

    ‘ Delete any existing instances of the bar

    ‘ Must loop backwards since we’re deleting items

    For i = CommandBars.Count
To 1 Step -1

      Set bar = CommandBars(i)

      If
((bar.BuiltIn = False) And _

        (bar.Name = “My
Bar”)) Then

        bar.Delete

      End If

    Next

      

    ‘ Create our own temporary bar

    Set bar =
CommandBars.Add(“My Bar”, , , True)

    bar.Visible = True

  

    ‘ Add new buttons

    Set cmdSave =
bar.Controls.Add(msoControlButton)

    cmdSave.Caption = “Save”

    cmdSave.Style = msoButtonCaption

  

    Set cmdDelete =
bar.Controls.Add(msoControlButton)

    cmdDelete.Caption = “Delete”

    cmdDelete.Style = msoButtonCaption



    ‘ Exit the sub

    Return

  

  Handler:

  

    MsgBox “CommandBar not created:” & _

      vbNewLine & _

      Err.Description, _

      vbOKOnly Or vbInformation,
“Error”

  

  End Sub



  Private Sub

Comments (3)

  1. Siew Moi Khor says:

    btw, forgot to mention besides really liking this blog entry — think it’s extremely well written; very useful guidelines and great code repurposing mitigation steps… i also like the shortcut key tips very much. Do please keep them coming.

    Thanks a lot Peter for all the great blogs and sharing your vast and in-depth knowledge on security, coding, etc… Really appreciate it.

Skip to main content