by
Christof Wollenhaupt, Foxpert
Login, Logout, Knock out
Microsoft
has a lot of experience and knowledge in developing secure applications and actually
does so. What sounds like a failed attempt to be funny, is surprisingly true.
Microsoft spent hundred of millions dollars in the research of security, has
got worldwide recognized security experts and is permanently challenged by tens
of thousands of security experts and its competition.
This makes
it even more surprising that most developers start from the ground with the
security aspects of their own applications. Hence, it's obvious that security
is not the primary focus of software developers. Without hesitation, they
demand administrator privileges for their application and implement their own
login dialog to restrict access. They write their own encryption routines to
encrypt single field in a table. They spend a lot of effort to protect tables
in case of power failures.
For all
these problems there are readily available solutions that are – almost with
certainty - superior to the homemade solutions. Using the login dialog and
password check I'd like to show how many security leaks a Visual FoxPro
application could have. Technically, such a login dialog is very simple. The
user enters user name and password. The application executes a query. Execution
continues if user name and password are valid. Otherwise the application
terminates, usually after a few unsuccessful attempts.
* ASSUME: We
located the correct record in USER.DBF
IF NOT
ALLTRIM(User.cPassword) == m.lcPassword
* invalid password
ELSE
* password is OK
ENDIF
That looks
pretty safe, doesn't it? However, what happens if lcPassword is NULL? Every
expression that contains NULL is NULL itself. A condition that is NULL cannot
be true, never. If an IF condition results in NULL, Visual FoxPro always
executes the ELSE branch. In the sample above, the user can log on
successfully, if he only knows a correct user name. In the password textbox all
he needs to do is to hit CTRL+0, and Visual FoxPro assigns NULL to the Value
property of the textbox. This way it's quite easy to log on as an
Administrator.
Check
every IF command if the condition can be NULL
The sample
above is only dangerous if the attacker knows a valid user name. Otherwise, he
wouldn't make it beyond the SEEK like that I omitted in the sample.
Unfortunately, user names are often easy to guess. It's particular easy for a
stranger to guess a user name if he doesn't have to enter them, but can
comfortably pick one from a combobox. If then – as demonstrated in the VFP
Tastrade sample – a picture of each employee is included, no one should be
surprised if passwords or other information are quickly discovered and harm is
done. In Windows XP or MSN Messenger, all possible login names are even clearly
listed, as well. Since Microsoft's designs have always been copied in the past
it's just a matter of time to see similar login dialogs in other applications,
too.
Comparing
strings in Visual FoxPro is everything but a simple topic. If two strings are
considered to be equal depends on a number of factors. These are SET EXACT, SET
NEAR, SET COLLATE, SET ANSI, as well as various codepage related settings. Take
a look at the following sample and find out how to bypass the validation
routine:
* ASSUME: We
located the correct record in USER.DBF
lcPassword =
NVL(m.lcPassword,"")
&& live and learn...
IF ALLTRIM(User.cPassword)
= m.lcPassword
* invalid
password
ELSE
*
password is OK
ENDIF
And, have
you been successful? Instead of using the == operator, the code above uses the
= operator. This operator respects the current SET EXACT setting. The default
is OFF which means that the left-hand string is only compared up to the length
of the right-hand string. The expression always returns .T. if the value on the
right side is an empty string. Logging on successfully is just a matter of not
entering any password, at all!
Check
all string operations for unwanted side effects, especially =, <=, <,
>, =>, #, != and <>.
Up to now
we only focused on the actual string comparison. But what about the User.DBF
table itself? Maybe we can open the table outside the application. If the
password is stored in clear text, you don't need any further explanations.
Mostly, the password is encrypted before it's stored in a table. Surprisingly,
only two algorithms cover most such encryptions. Either, the password is XORed
with a secret key, or the ASCII value each character in the password is
incremented or decremented by a fixed or variable value.
XOR is a
very popular encryption mechanism. If you XOR a string with a secret key, you
get an encrypted text. If you XOR the encrypted text with the secret key, you
get back the original string. But if you XOR the original string with the
encrypted text, you get… the secret key. Whoops. The addition/subtraction
tricks, too, are easy to find, especially if the same differences are used for
all passwords. All you need is a pair of encrypted and plain passwords. Those
don't even have to be valid.
To get at
them is often easier than necessary. If user data is stored in a SQL server, an
application typically uses an ODBC query to verify the password. If the query
returns a record, the password was correct. If, on the other hand, there's no
match for the password and user name entered, the password was wrong. That
means, on the client side, the password is encrypted and the assembled SELECT
statement is sent to the server using SQLEXEC() or REQUERY(). In other words,
the encrypted password is sent across the network without further protection.
An ODBC Trace Tool or a Network Monitor can easily reveal them.
With local
data the opposite approach might be possible. To make it easier the application
might copy the user table into a cursor and decrypt it. All accesses go to the
cursor instead of the table. That's secure as long as Visual FoxPro has enough
memory. If that ceases to be the case, Visual FoxPro starts swapping cursors to
disk as .TMP files. You can analyze these files to your heart's content, if you
kill VFP in the task manager or turn off the computer abnormally. Nonetheless,
there are much easier approaches for Visual FoxPro based data that are
discussed later in this article.
The problem
is the same in all cases: The encrypted password is stored in the table. The
solution cannot be to encrypt the password even better. The solution must be
not to save the password in the first place! But how to validate a password if
it's not stored somewhere? Think about what you actually want to accomplish.
The content of the password doesn't matter, at all. All you want to know is if
the entered password matches the stored value. For that, you don't need the
password itself; a place holder is just as good.
Such a
place holder is called a hash. A hash algorithm converts a string of arbitrary
length into a single number that is most likely different for different input
strings. We all know such functions for checksum calculations. The Windows
CryptAPI provides functions to obtain such hash values using very secure
algorithms. To make it easier to use this API, Visual FoxPro contains a class
library named _CRYPTAPI.VCX since version 7. To validate the password you
compare the hash value of the entered password against the stored hash value in
the user table. Even if an attacker has both values, he can not conclude from
those of other passwords. Better, even if he knows the algorithm that was used
to obtain the password, that's of no use. To obtain a password for a known
hash, he would have to invert the algorithm. Most hash algorithms are
asymmetric, though. While it's easy to obtain a hash value, you need to
calculate the hash value for any possible password to get the opposite. This
method is called a brute force attack, and is usually the last resort as
it is takes a very, very long time. For good algorithms on today's computers
this can be millions of billion years. On the other hand, brute force attacks
have a 100% chance of success… if you have enough time.
Never
store secret information, just place holders.
That's not
only true for data, but also for the contents of the memory. If there's an
error in an application, Visual FoxPro makes it extremely easy to collect
information about the current state and environment. You cannot only get the
line number and error code, but information about every aspect of Visual FoxPro
using
LIST STATUS |
MEMORY | OBJECT | CONNECTIONS | DLLS
Such
information are often stored in error logs. You can really make a hacker happy
if you store the password of the user in a variable. He only has to wade
through the LIST MEMORY logs to find a considerable number of passwords with no
effort at all. That's even true if you only store the password temporarily in a
procedure. It's much harder to cause an error just in this moment, but often
this is possible by deleting an index tag or a file.
Unencrypted
system information do not belong into an error log.
In general,
deleting of files is one possibility to gain access to a system. In cases of an
error, many applications pick a default value that is comfortable to the
developer or user, but hardly secure. Suppose, you have the following function
to obtain the return value of a modal VCX form:
loForm =
CREATEOBJECT(tcForm)
loForm.Show()
uRetVal =
loForm.uRetVal
RETURN uRetVal
And call
this function from the main program like this
llLoginOK =
CallModalVCX("frmLogin")
In your
tests, this function works flawlessly every time. But, what happens if there's
an error while loading the form? If you, for instance, bind a textbox to a
table and that table doesn't exist, Visual FoxPro doesn't create the form at
all. In the code above, loForm would be .F. in the first line and cause errors
in the next two lines. Hence, uRetVal would never be defined and the RETURN
line would fail, too. Visual FoxPro always returns a value from a function and
a procedure. If you don't provide Visual FoxPro with a value, Visual FoxPro
uses its default value: .T. Since .T. is also the return value for a
successful login, the user can login by renaming a file.
Error
handling is always critical. In case of an error you should always think
carefully about which options a user may have. Ignoring an error can be an
option during development, but not at runtime. You should always cancel the
current function. That means, either terminate the application immediately, or
use RETURN TO or TRY…CATCH at the READ EVENTS to continue execution at a
predetermined point. If you log the error, you shouldn't store information that
aren't absolutely necessary. If you have the possibility and need these data,
better encrypt error logs.
Don't
let the user continue program execution in case of an error.
Admittedly,
causing an error to bypass a security check can be called an advanced
technique. So far, we focused on comparing the value. But locating the record,
too, could cause security issues. One variation of causing an error is
SEEK lcUser
While SEEK
can search NULL values, this is only true if the indexed field also allows NULL
values. Otherwise, SEEK cancels out with an error. If that error is ignored,
the record pointer is still on the first record and the password validated. The
hacker therefore doesn't have to know the name of the user. Since typically the
first record contains a test user for the developer or the administrator,
there're good chances that this attack results in using a privileged account.
More and
more applications, though, do not save data in FoxPro tables, but in a SQL
server database. To send a query to the SQL server, the application assembles
SQL statements and often sends them with SQL Pass-Through to the SQL server.
The following code tries to determine, if the password is correct. How long do
you need to crack the following code?
lcSQL =
"SELECT * FROM User WHERE cUsername='"+;
NVL(m.lcUser,"")+"' AND nHash="+TRANSFORM(NVL(lnHash,0))
IF
SQLEXEC(lnHandle,m.lcSQL)>0 and RECCOUNT("SQLResult")>0
* OK
ELSE
* Error
ENDIF
The
password query has been replaced through a hash value query, but the user name
is still inserted into the query unchanged. That allows the hacker to modify
the query as he wishes, for instance, when he enters the following user name:
' or 1=1 or 1=1
The
resulting query always returns records. Additionally, the code checks if the
SQL server returns records at all. The correct verification would be to check
if exactly one record is returned. Multiple records in the result set would
mean that there are multiple records for the same user and the same password.
Such a constellation should have been prevented by the database. The
possibilities are not limited to altering a simple query. Depending on the
backend you can send multiple commands in a single command string. A hacker
could create new users, change passwords and execute stored procedure in one
pass from the login dialog.
Always
validate user input before using it any further.
Another
tendency of developers plays an important role in security. Most SQL servers
have a very powerful user right management system. For each table and each
column you can specify if a user can read or write to it. You can specify if
records can be deleted or added. You can precisely define who can alter the database,
like adding a new table, adding a stored procedure, adding or deleteing users,
and so forth.
In reality,
though, these features are hardly used. Typically all users of the application
share a common user account that has been created for the application. This
user often has got the rights that the administrator of the application - or
even the developer - needs. Sometimes, developers even use the sysadmin account
provided by the SQL server. Often, such a configuration doesn't seem to be a
problem as the application controls access and only the developer and the
system administrator know the password. Let me rephrase this for you: The only
protection of the database is the login dialog of the application. You have
seen on the last few pages how little security that could mean. If the user can
control the SQL server right from the login dialog, it might be the safest
solution to drop that dialog completely.
Use the
security mechanisms that you server offers.
Similar
problems do not only exist with SQLEXEC(). In Visual FoxPro, too, there are
many possibilities to shoot oneself into the foot: macro substitution,
EVALUATE() and SCRIPTEXEC(). If input values are used in conjunction with these
features without further validation, a hacker has the entire arsenal of Visual
FoxPro at his hands. As we will see later, you only need a single point in the
application to execute code to turn off most conventional security mechanisms.
All input
values have to be validated if they are in an acceptable form. The most important
rule is to strip off all terminating characters. In Visual FoxPro this is the
quotation mark, the single quotation mark, and square brackets that are used as
string delimiters. With HTML these are mostly "<" and
">" which you should remove from the input stream. In FoxPro you
can easily use the CHRTRAN() function for that. For security reasons, you
should focus on valid characters and remove all others:
CHRTRAN(cString,CHRTRAN(cString,"ABCDE…Z1234567890",""),"")
The inner
CHRTRAN removes all valid characters. The remaining characters are invalid. The
outer CHRTRAN uses this string to remove invalid characters from the original
string.
App Attack!
In the
previous chapter you have learned how easy it is to bypass many security
mechanisms in many Visual FoxPro applications. Someone attacking my
application? Impossible! You are right, if you think of terrorist activities,
evil former employees or prestige-addicted script kiddies. In reality, though,
the much smaller dimensions are those that are a risk to your application.
That could
be a competitor who wants to learn about the inner workings of your application
or, purely, wants to perform a data conversion for one of your former
customers. If the core of your application is the database that you provided,
your client might want to use that data in a different way than he is allowed
to. When using address databases for mail merge letters, such a user could try
to avoid the cost associated with every address he uses. Or, you have a
colleague who believes to be smarter than you and feels forced to prove that.
The worst group, however, is called the "power user". When the
possibilities of the application are not sufficient, this group of users tries
to user alternative applications to modify data, typically using Excel or
Access. Upon saving you quickly end up with invalid values or memo fields that
are truncated to 254 characters. Of course, no body did anything.
The
frequency with which developers ask in online forums for encryption tools for
application and data proofs a steadily increasing need for security. The
increasing consciousness of security issues becomes an issue especially for
independent consultants. More and more customers want to know how secure an
application is and even let developers sign that somehow. If a developer signs
such a paper believing in the security of tools like Refox or Cryptor, and
there is an attack, this at least weakens the own position, or can be very
expensive at max.
More over,
there's a trend to distributed applications even in the FoxPro universe. This
trend is not as far as many papers from Microsoft want to make us believe, but
at least the integration of the internet is widely a fact, already. An
automatic update via FTP or sending an email in case of an error doesn't sound
like an unreasonable request of customers, anymore. For internal applications
used by bigger companies another common request is to access the application
with a browser via the intranet. The reason is usually a strategic decision of
the management, even though technically such a solution might not make sense.
Increasingly, SQL server is discovered and accepted as a more reliable data
storage for Visual FoxPro applications.
The most
common security mechanisms in Visual FoxPro applications try to protect against
an external attack. To secure data developers often use an encryption library.
There are two fundamental approaches to data encryption in Visual FoxPro: file
and field based encryption. With the field based encryption, the program
encrypts the content before each write access. Reading is done the opposite
way: The content of the field is passed to a decryption function. Such a field
can only be indexed on the encrypted contents. Creating an index on the
decryption function would put the clear text of the protected fields into the
CDX file where they can be recovered easily. Due to this restriction, you can
only search for exact matches on encrypted fields. Searching on anything else
involves the decryption function and disables Rushmore.
File based
encryption doesn't suffer from this restriction. Depending on your needs you
have multiple possibilities. If you want to prevent unauthorized users from
reading data, the easiest solution is the Encryption File System (EFS). Such a
file system comes with Windows 2000 and Windows XP. To enable it, set the file
attribute "encrypted" in the file properties dialog. You access the
file like before, because encryption works transparently. The application only
sees decrypted data, on the hard disk there are only encrypted files.
If more
than the current user should access these files, for instance, across a network
you must have a Domain Controller. The Domain Controller manages the keys used
for encryption. Windows EFS uses the DES algorithm. Depending on the Windows
version, this is 40 Bit DES, 56 Bit DES, DESX or 3DES. The last one is
currently considered to be quite safe and is the same used in bank transactions
and ATM machines across Europe.
EFS is a
good solution for internal applications running on the company network.
Activating EFS is not a developer, rather an administrative decision and can be
performed independently of the application. The administrator, and not the
developer, controls file access rights. Those without access cannot read any
data, even if they steal the hard disk and use NTFS file systems for DOS, or
similar low-level tools. To read data, you need the keys that are stored on the
Domain Controller. On that machine, the keys only become available when the
hacker knows the administrator password. Decryption of these files therefore
requires the computer with the desired data, the domain controller and access
as an administrator. Nothing is perfectly secure, though. Since 2003 there are
tools available that use a brute force attack in a reasonable time frame if
part of the password is known.
If you need
to protect data in a peer-to-peer network or you want to encrypt files before
sending them to your users, there's only one tool available for FoxPro: Cryptor
from XiTech. When Windows loads a DLL it creates a list of all functions and
their addresses in memory. For each DLL there's such a jump list. Cryptor works
by altering these jump lists. For instance, the function at index #224 in
NTDLL.DLL is the NtReadFile() function that is used to read files. Cryptor has
its own version of NtReadFile. In the list it changes the address at position
224 to point to its own version and saves the old address in some list. When
now Visual FoxPro calls the NtReadFile function, it takes the function pointer at
position 224 and calls Cryptor this way. Cryptor, on the other hand, calls the
original function, decrypts all data and returns them to Visual FoxPro.
Calling the
function is therefore entirely transparently and works for all utilities that
run in the VFP process including third-party libraries and ActiveX controls.
The disadvantage is that Cryptor has to be updated for each version of Windows.
If Microsoft implements a new file access function, Cryptor has to implement
it, as well. Otherwise, some functions would return decrypted, some encrypted
contents. Should that happen at a write access, the file would be lost for
ever. A further disadvantage of these tools is that Cryptor has to modify the
jump list of DLLs. There are tools designed to prevent exactly this, because
they either use the same trick, or because this behavior is typical for bad
programs like viruses or worms. For instance, as of writing this, Cryptor does
not work together with KonXise, even though both are from the same company.
Additionally, Cryptor has to be loaded into its own process. This is the case
when you launch an EXE. When creating a DLL you have to live with potential
restrictions, though.
When
protecting the application the goal is usually to prevent that someone can
decompile the application and recover the sources. All products that don't
produce machine code, but so-called P-code are especially vulnerable for
de-compilation. If names like class names, method names, etc. are stored in
clear text, because they are used to resolve dependencies, there are usually
very good chances to restore the sources completely. There's a difference if
the restored code reads
PROCEDURE
_1(_2,_3)
_1 = _2 *
_3
RETURN _1*_1._1
Or
PROCEDURE
LineTotal(tnAmount,tnPrice)
lnSum = tnAmount
* tnPrice
RETURN lnSum *
Tax.Rate
Products
like Visual FoxPro, Java and .NET are especially vulnerable for this kind of
de-compilation. The approaches to protect applications are different in these
products. In FoxPro the most common approaches are branding and encryption. In
.NET the mostly used technique is obfuscation. That means that all variables,
methods, etc. are renamed so that they don't make any sense. Obfuscators try
specifically to reuse names as much as possible. In the sample above, "_1"
was used in four different places: as a procedure name, a variable, an alias
and a field name. Additionally, obfuscators alter the code in a way that it
does the same, but doesn't look anymore like the code produced by the compiler.
The first
approach in FoxPro was branding. That means that a brander writes a specific
tag into the compiled file, like a password hash, for instance (yes, the
manufacturers, too, know now to save passwords). When the same tool is used to
decompile a program, this tag is checked and eventually the tool refuses to
decompile the code. It's important to understand that it is the responsibility
of the decompiler to respect this tag. Because the only thing changed in the
program is this tag, Visual FoxPro doesn't notice this modification and can
execute the application as usual. Here's a list of de-compilation tools for
FoxPro:
product |
Web site |
ReFox |
http://www.xitech-europe.co.uk/ReFox.html |
AntiPro |
http://www.frog.cz/prod02.htm |
UnFox
|
http://asm001.home.chinaren.com/source.htm http://www.weihong.com/tools.htm |
SecurityFox |
http://www.taketech.com |
If you use
a different decompiler, you can de-compile an application without problems that
has been branded by other tools. For complete protection you would have to
brand your application with all tools. In the past when only few of such tools
existed, that was not a big issue, but today developers in many countries
around the world have been created decompilers that ignore other tags. Via the
internet these tools are now available to all developers worldwide.
Additionally, you can find patches on the internet that remove these tags. To
find them simply search Google for "Refox" in combination with words
like "warez", "crack" or "hack". Most pages are
Russian or in various Asian languages, though.
Contemporary
protection tools therefore encrypt the compiled EXE. An application treated in
this manner is not recognized by Visual FoxPro anymore. In addition it's
therefore necessary to add a loader program that decrypts the application and
then calls the Visual FoxPro runtime. This technique is used by tools like
ReFox with Level II, KonXise or Armadillo. A lot of new tools are currently
under development or just out in their first releases. Differences among the
tools are if decryption happens in memory or into a file, if the file is
additionally compressed, and so on. The basic principle doesn't change, though.
Even though
there are cracks for these tools and hacker constantly try to bypass their
protection or to hack their encryption algorithm, all of these tools do their
job quite well. However, just because one part is working you shouldn't expect
that your application is secure. All these tools have one thing in common: they
protect the application or data against external access. The Visual FoxPro
application typically doesn't notice it is protected. Visual FoxPro can read
files encrypted with Cryptor and execute application protected with KonXise.
All of
these tools do not protect against attacks from the inside. The most successful
strategy for internal attacks can be summarized as Code Injection
meaning that code is executed inside the application. Visual FoxPro is
extremely vulnerable for this kind of manipulation
Code Injection
Let me make
some legal remarks. Decompilation and modification of executable files is
prohibited by the license agreement of most products. Additionally, most countries
have laws that make decompilation illegal even if such a clause is missing from
the license agreement. In Europe there are only few legal exceptions for
decompilation without the permission of the intellectual property owner. Should
you need to decompile or modify (patch) an application, you should, in any
case, contact the owner of the rights of this application and, eventually, a
lawyer specialized in this subject. The following explanations are meant to
give you an idea of what others could do with your code and to enable you to
protect against that.
Code
injection means that external code is executed inside the application. That
doesn't have to be a negative thing, though. A Debugger in Windows, for
example, loads one of its own DLLs into the debugged application to control the
flow of execution in that process. How the external code gets into the
application differs.
If a FoxPro
application uses a FLL to encrypt data, it's quite easy to bypass this. The FLL
has to exist physically on the disk for FoxPro being able to load it. In
contrast to their counterparts in DOS, the PLB libraries, FLLs cannot be
included into the application and loaded from their. In most cases the FLL
remains on disk and is therefore easy to manipulate. The same applies to DLL in
all Windows applications. In a Windows application you could write your own DLL
that provides the same functions as the original DLL. Then you could capture
all function calls and forward them to the actual DLL. Creating such a DLL can
be automated if the interface definition is available. Now you are in the
position to capture passwords passed on to that DLL, save them and use them
later as needed. To protect against such a manipulation, you should create a
checksum of FLL and DLL files, for instance, using the MD5 hash algorithm.
Before loading a library verify the current checksum against one embedded in
your application.
In Visual
FoxPro you don't have to work that hard. When calling a function Visual FoxPro
searches in the current program, in procedure files, in FLLs, in API
declarations, in stored procedures, as external EXEs or APPs, or external
FXP's. The exact search order varies between different versions of FoxPro. In
any case, though, Visual FoxPro executes a FXP in the current directory when it
didn't find any other procedure of that name anywhere else. Hence, if you
change function names in a FLL and at the same time provide an FXP with that
name in the current directory of the application, then the FXP is loaded and
executed.
You don't have
to be that clever to get at secret data. Imagine you had encrypted all tables
with a tool like Cryptor. With an external attack the user has virtually no
chance of decrypting these data if you picked a good password. Inside the
application, however, you give the password to Cryptor. Visual FoxPro can then
read all registered files without limitation. Developers tend to use the same
password for all tables which is, moreover, stored inside the application
somewhere. Because of its support of file masks, usually all tables are
registered at once. The only remaining protection is therefore that only
approved user get into the application. You might remember from the first
chapter what the probability of this is like. If all tables are registered and
not those only to which the current user has access to, you only need minimal
rights to have access to all data.
Sufficient
is including the report designer. If a user can modify reports in an
application, he can open the data environment of the report and add any
arbitrary table. The report designer offers even more surprises. Not only can
you open tables on the disk. In the file open dialog you may enter any name of
an included table that will then be opened in read-only mode. That might be
useless for regular tables.
Class
libraries and forms are nothing but tables. If you know the name of a class
library, you can open it in the report designer. You find that name typically
in the error messages. That's another reason to encrypt error logs and to not
to display more than absolutely necessary. Even if you encrypted your
application with any of the available protection tools this still works. The
VCX in the report designer is decrypted and unprotected (how else should VFP be
able to execute it).
There are
many possibilities to execute code in your Visual FoxPro application. For
instance, a user could write FoxPro code into a text file and then enter the
following expression in the report designer:
SCRIPTEXEC(FILETOSTR("Datei.TXT"))
In Visual
FoxPro 6.0 and earlier this requires to create an FXP, but that's not really an
issue. The report designer is really unreliable. Therefore either leave it out
or isolate the designer into a separate application that is launched with RUN.
If the report designer isn't available, what can you do to inject code into the
application? The next easiest way is to run the application in the development
environment. Put SET STEP ON onto an ON KEY LABEL to suspend the application at
any time. If the application is encrypted this isn't an option as Visual FoxPro
won't recognize the EXE as a Visual FoxPro application in the IDE. What we are
looking for are ways to execute code that is not included into the EXE. If you
can't imagine such a way here are some examples:
_STARTUP
or COMMAND line in the config.FPW
Index
expressions in tables
Stored
procedures
Triggers
Field
validation rules
Database
events
Reports
External
forms, classes, queries or programs
All
kind of scripts that are executed by the application
Macro
substitution, EXECSCRIPT(), EVALUATE() or name expressions that are not
validated.
Some
options are not available in all versions or the runtime libraries. For
instance, the COMMAND line is ignored in the runtime version of the more recent
Visual FoxPro versions. One trick, though, works almost every time. A free
table used by the application is added to a database. Depending on the version
of Visual FoxPro that you are using, you can now use trigger, validation rules
and database events in that new database to add your own code. If you open a
table in Visual FoxPro that is part of a database and you haven't opened the
database yet, then Visual FoxPro kindly opens the database for you. If database
events are enabled Visual FoxPro triggers the DBC_OpenData event before even
the table is opened. This event can contain arbitrary code that, for instance,
copies all tables into unencrypted files onto a huge removable disk.
If plain
tables are insecure, OK, let's use SQL server. That secures data and your code
is safe, anyway, you might think. Unfortunately, that's wrong. All class
libraries, that is, VCX files, are tables. When loading a class Visual FoxPro
opens them in the system data session. If you manage to execute code in the
system data session, there's nothing that stops you from copying all loaded VCX
files. While accessing the system data session (#0) sounds impossible at first,
it's actually not a big issue. When evaluating an index expression, a field
validation rule or a trigger, Visual FoxPro always activates the correct work
area. Such a work area is always in a data session. All you need is a trigger
in a table that Visual FoxPro opens in the system data session. As soon as
Visual FoxPro starts making changes to that table, it executes the trigger code
in the system data session.
The
following program uses this trick to automatically copy all loaded VCX files
onto the hard disk. In contrast to FXP files you cannot encrypt VCX files.
Therefore they can not be protected by branding tools. VCXes extracted this way
are entirely unprotected and can be decompiled by any decompiler available on
the market. If you compiled the application with debug information that is not
even necessary as the entire source code is still contained in the VCX! That's
true even if you used any available encryption tool that can't be cracked
externally.
It's very
important to notice that this is not a problem with these products. They work
as advertised. The reason is simply that the code below exploits a design in
Visual FoxPro against which those tools were never designed to protect. These
tools prevent decompilation from the outside. For technical reasons they can't
do anything against code injection. There's no way for them to distinguish good
code from bad code as far as security is concerned.
*========================================================
* Monitor the
system datasession for loaded VCX libraries
* and save them
to disk.
*========================================================
If Version(2) ==
2
Return
Endif
MessageBox("Starting...")
* Create an
empty Resource file in a new database
Local lnSelect
Set Safety off
lnSelect =
Select()
Select 0
Create Database
ExtractVCX
USE Sys(2005)
Again
Copy Structure
to ExtractVCX Database ExtractVCX
Select
(m.lnSelect)
* Add a trigger
to the resource file
Create Trigger
On ExtractVCX For Insert as ExtrVCX()
Create Trigger
On ExtractVCX For Update as ExtrVCX()
Create Trigger
On ExtractVCX For Delete as ExtrVCX()
Set Resource to
ExtractVCX
Procedure
ExtrVCX
If
Set("DataSession") == 0
Activate Screen
? "..."
Local laTables[1], lnTable, lnSelect
lnSelect = Select()
For lnTable=1 to AUsed(laTables)
Select (laTables[m.lnTable,2])
If JustExt(Dbf()) == "VCX"
If not File("__"+JustFname(Dbf()))
Activate Screen
? "Extracting: "+JustFname(Dbf())
Copy To ("__"+JustFname(Dbf()))
Endif
Endif
Endfor
Select (m.lnSelect)
Endif
Return .T.
If you can
modify the database, you could put this code into the DBC_OpenData event. The
EXTRVCX function is called by a trigger. The trigger gets called on any write
access to the resource file. That happens, for instance, when you close a
report preview or a BROWSE window. Since most applications use a report designer,
bets are good for you. Terminating an application, too, writes to the resource
file. You might have to move EXTRVCX to a different location in this case,
though, as the database might have been closed already.
Proactive protective
Maybe the
previous pages left you with a minor bad feeling, as most applications have at
least one of these security leaks. Don't panic! Analyze the situation of your
application and you might find out that there's not much of a problem, or lots
of reasons to panic NOW! This analysis doesn't have to be a professional
security risk analysis if you don't work in a security relevant area. As a
starter it's often sufficient to think about the various aspects of security.
The most common reason for security risks in applications is not the stupidity
of the developer. They simply exist because nobody has thought about them. The
most important questions for you to ask are:
Who
could be interested in getting at my code or data?
Who
has got the possibility to do that?
What
abilities and knowledge do these persons have?
With the
first question you find those that have an interest in attacking your
application. The second question covers those that have (even a theoretically)
possibility to do so. If you have a web shop, everyone with internet access
could potentially break into your computer, but only a limited group, like the
competitor, actually has an interest in specifically hacking into your
application. This is an important difference, because FoxPro is more a niche
product, and maybe even your application. That makes it much less probable that
there are tools for automated attacks that Script Kiddies might use. On
the one side this means that you less likely get into a broad attack in which
you just happen to be the victim by chance. On the other hand it means that
each attack is most likely targeted at you specifically. A hacker needs to have
a certain amount of knowledge about FoxPro to compromise your application.
That's different for ASP.NET applications running on IIS, for instance.
Security leaks in that configuration spread quickly and tools are created that
use them automatically. To attack a computer that is vulnerable to this attack
you don't need to know how the attack works, just where to get the programs to
perform the attack.
In most
cases it's sufficient to protect the application against decompilation using
ReFox or similar tools, to encrypt data files and to ensure that the LogIn
dialog is really secure. That protects against the curious persons who simply
have to try if the application can be decompiled or files be modified in Excel.
A professional hacker won't stop that, but without these measurements your
application would be completely unprotected. So even if ReFox might not protect
against all types of attack, it's wrong not to use ReFox or a similar tool.
The big
question is: how paranoid you should get? No matter what you do, there's a way
round it. It's only a matter of time and effort. On the other hand, security
measurements often lead to inconveniences for the honest user. And security
costs money. Before you start protecting your application in many months' of
work, you should clarify how much security is needed. For security issues the
same is true as for all design issues. The early in the life cycle you start
thinking about it, the cheaper it is. Try to achieve secure habits. Most
solutions, like removing invalid characters from a string, take only minutes or
less if you apply them right from the beginning.
Once you
have figured out against whom you need to protect and how effort you want to
spend on that, you could go through the following list of solutions. This list
is neither complete, nor are these solutions fail-safe.
The rule,
the most important rule, is: Avoid any access from the outside. For the code
this means to use branding and encryption tools. For data this means to control
access. Encrypt data if you cannot trust the administrator. Work with the
administrator to limit access to data.
All
security boundaries in your data should match those of the storage system. If
you store data on a SQL server, this is usually not an issue no matter what the
design looks like since you can control access down to the field level. You
only have to use these possibilities! Try to collaborate with the system administrator
right from the beginning to find a design that not only makes data retrieval
easy, but also allows for easy maintenance.
When using
DBF files the smallest unit is a file. Hence, a user should either be allowed
to access all or no data in a file. If a user is only allowed to see certain
fields, you immediately have security issue. In this case, the application has
to control access which makes it vulnerable for code injection attacks. If
certain fields can only be seen by administrators, put these fields into a
second table. Use the primary key to create a one-to-one relationship. Restrict
access to the administrator table to the administrator group on the operating
system level.
For programs,
as well, there are simply techniques to make them more secure. For instance,
you could use an include file to replace names in the program with other
strings. That's especially easy if you use PRG files for the class definitions.
In a .H file you could define, for example:
#DEFINE
LineTotal kfjewaoirujsoidfjhoiasdj9424hrfjskjgfhiu943894sfdhkycvnmcxbvxcjh
#DEFINE
lnCount kfjewaoirujsoidfjhoiasdj9424hrfjskjdgfhiu943894sfdhkycvnmcxbvxcjh
#DEFINE
lnPrice kfjewaoirujsoidfjhoiasdj9424hrfjskjgfhiu943894sfdhkycqvnmcxbvxcjh
#DEFINE
lnSum kfjewaoirujsoiidfjhoiasdj9424hrfjskjgfhiu943894sfdhkycvnmcxbvxcjh
If you
include this file into all your programs, Visual FoxPro replaces the names when
compiling your code. If therefore a user decompiles your application he doesn't
see the easy names on the left side, but the slightly more complex ones on the
right side. As you probably have quickly noticed, those names have only minor
difference which makes them hard to distinguish. This way you make
decompilation even easier, especially if some names are contained in other
names. Then a hacker can't even easily use search and replace features to
replace variables with clear text.
You have to
follow some rules, though. There are some things you can't redefine. This includes
all property names of VCX based classes, the names of database objects and all
names belonging to a table. Since you can only replace generically, you have to
exclude such names from the list of #DEFINEs. Pay attention when using macro
substitution as the name has been replaced upon compilation and you need to use
the long name to access variables. If such a name is used in a string, use []
as string delimiter instead of "" or ''. If your application
encounters an error, the error message also contains the long name. If your
users should be able to report errors on the phone, you need to use simpler
names. Best start with _1 and count upwards.
After you
prevented external access through encryption and replacing names (obfuscation),
or at least raised the bar significantly, you should work on preventing that
unauthorized code is executed in your application. The only way to protect
against execution of machine code is the integrated user and security
management of the operating system. If you give the program and the computer
out of your hands, there's nothing you can do. But you have a certain level of
control over the execution of VFP code. Especially Visual FoxPro code usually
offers the easiest way to access data. Preventing that execution is a huge step
forward to securing the application.
First of
all, you should include a Config.FPW into your application as this file has
precedence over external files. The Config.FPW file is one possibility to
execute code before your application run. Especially you can redirect system
files to files that are modified and included index expressions, triggers, etc.
If your
application happens to notice that the environment is different from what it
should look like, immediately quit the application. As this hinders debugging,
create special debug versions that do not have this limitation. Never release
versions that include debug code. You can be assured that someone finds out how
to activate that debug mode. When checking the environment pay attention to the
Config.FPW and FoxUser.DBF files, that no tables are open that shouldn't be
open, that no settings have been changed, that no FLLs are loaded that
shouldn't be loaded and that no ON KEY LABEL are active. In the development
environment quit the application immediately, otherwise you risk that the
application runs in the debugger. Even if the code is not visible, a hacker can
still step through the code line by line and check the contents and the changes
of variables and properties.
Verify all
inputs before you process them. Remove invalid characters from a string,
especially delimiting characters. Check all calls to SQLEXEC(), EXECSCRIPT(),
EVALUATE() and the usage of macro substitution.
Before you
access a file you should ensure that it hasn't been manipulated. For tables
verify the table header and if a table has been added to a database. For index
check the expressions. Open a database as a table and validate stored
procedures if there are added database events and validation rules have been
added to tables. Libraries (FLLs) should be included and copied to disk with a
random name before it is loaded. Alternatively use a hash code (MD5) to
determine a finger print of the file and validate this hash value every time
you load the DLL. This all is quite a lot of work. You rather be sure that you
need that level of security.
FoxPro
specific files require special attention. VCX, but also SCX, files are easy to
extract. Important algorithms therefore belong into a PRG. To extract those
from an encrypted application is significantly more difficult.
If you have
to use Visual FoxPro reports, check the report file for manipulation like an
index. Parse all expressions. Expressions that you can't uniquely identify
indicate a potential manipulation. Remove them as they might call external FXP
files that perform malicious actions. It's best to include FRX files into the
application, too, and copy them out as needed.
To securely
use the Visual FoxPro report designer, create a COM EXE server. Instead of
accessing encrypted, real data, this EXE accesses a second data directory with
test data. These files can be empty or contain sample data. If the user tries
to access tables through the report designer, he fails since the COM EXE server
has never decrypted the original set of tables. Executing code is limited to
the report designer. All saved reports need to be checked for manipulations
before they are execute in the real application environment. Remember that you
can add reports to a database, too, to execute code before the report is opened.
That means, before you open it with USE, you must check the header for
manipulations, just like any table.
Even if
it's quite tempting to store certain things as VFP programs, be very, very
careful with that. Just as easy as you can change these data driven
programs, others can make changes not in your interests. Don't assume that such
configuration files only contain valid date. If you need to create programs,
encrypt them or calculate a checksum using a relatively secure algorithm such
as MD5.
If you
encrypt files never decrypt them longer than necessary. If, for example, you
encrypt the user table, register it only after the user entered a password and
deregister it once you have checked the password. Tables for which a user has
no rights should not be registered, at all. This might result in each file
having a different password, but this method increases security significantly.
Passwords
are another critical part. As soon as a password is known, it can be used to
compromise your application. Such passwords are often vulnerable to brute
force attacks. If a user can take an encrypted file back home, he can use a
program to try all possible passwords without being disturbed. Short passwords
or passwords that have a meaning as a word are taboo. In the internet there are
word lists with hundred of thousands of commonly used passwords (ever wondered
how some dubious web sites make their money when there content is free… after
you registered with a password?). Such lists are used by hackers, but you, too,
should use them to search for your password. Consider that one can check
between hundred thousand and several millions Windows passwords per minute if
one has certain hash keys from the registry. Especially for passwords used by
applications there's absolutely no reason not to use difficult, hard to
remember passwords with digits and special characters.
FoxPro
tables are inherently insecure as the entire content can be accessed. If you
need more security, either use a SQL server, or encapsulate data access into a
DCOM/COM EXE server, or use web services to access tables. Then you always have
full control about who access which data.
If you have
expensive algorithms you should consider not giving the binary code out of your
hands. Usually you can't avoid that. If you could offer your application as a
web service on your own machine and your client only access the web service,
use this possibility. There's no better protection against decompilation.
Consider
non-technical aspects, too. If you encounter a violation of the license
agreement, you might want to sue the violator. Usually that is only possible in
the country of residence of that user. Before you compete with Russian or
Chinese lawyers, check with your own lawyer if you can restrict sales and
re-sales to countries in which you can use legal actions.
Security is
a topic we all should think about more and we will be forced think about more
in the future. A lot can be done with zero to nothing efforts if you consider
security right from the beginning. Not only web applications are affected, but
all kind of applications. Visual FoxPro makes it attackers very easy. A very
high degree of security in a Visual FoxPro application is only possible with
extremely high efforts and additional tools, if at all.