Sure, we may benefit from whatever shelter is derived from running a less widely used/understood system, but relying solely on security through obscurity with your U2 system is, to put it nicely, extremely naive in this day and age. It just doesn’t add up when you pay thousands of dollars for firewalls and other network security paraphernalia and wouldn’t dream of allowing raw user input through in your SQL-based applications.
U2 may have different syntactical spices and a different method of representing data than its mainstream counterparts but the core principles behind secure coding practices still apply:
- Reduce your attack surface.
- Assume all externally sourced data may be malicious.
- Apply the Principle of Least Privilege.
- Principle of Defence in Depth.
- Fail Securely.
The list goes on.
So, what specific vulnerabilities should we look out for?
Let us start with the humble EXECUTE/PERFORM statements in UniData and UniVerse. SQL Injection is a widely know subject, but how many U2 developers have considered UniQuery/RetrieVe Injection? Did you know that in some cases, malformed UniQuery in a EXECUTE can drop you to ECL?
As developers in the U2 world, the same lessons learnt in SQL Injection can and should be applied when using *EXECUTE/*PERFORM, etc. Sanitise your input!
Do you have any statements that work like this?
EXECUTE 'SELECT MYFILE WITH FIRST.NAME = "':TAINTED.INPUT:'"'
In this case, TAINTED.INPUT is either supplied by a user or is comes from an external source. The results from the SELECT statement are now compromised and can contain any data. Take for instance the following input for this contrived example:
" OR WITH CC.NUMBER = "4657000000000000
Essentially, this converts the innocent SELECT statement which, for example, was used to search for customer’s first names to get contact numbers, into one which can be used to find Credit Card numbers (hopefully though, your CC numbers are encrypted in some manner). Even worse, if your program displays error messages that reveal record names when they cannot be read, then an attacker with patience can reveal almost any data they want from your system.
Remember, in UniData you can use the ‘USING’ keyword to specify any FILE to source the dictionaries (UniVerse does not have this, I believe). Aside the all the usual manipulation of results, the USING means that if someone can control the first few lines of a record (temp data dumping file anyone?) then using the SUBR() call, they can even cause programs and subroutines to be called!
Before you EVER use input from a user or an external source, make sure it is validated and sanitised. Expecting a number? Use MATCH and 1N0N. Expecting a name? Make sure it is doesn’t contain double quotes. Don’t want to allow ‘searching’ with your SELECT? For example, I use the following check to ensure the user input doesn’t escape the SELECT string with double quotes or attempt a wildcard search with [ or ].
IF TAINTED.INPUT MATCH "~'['0X~']'" AND NOT(INDEX(PCNAME, '"', 1)) THEN
Further to this UniQuery/RetrieVe Injection vulnerability, earlier I mentioned that in certain situations you could cause it to crash to ECL.
Are you running UniData in PICK mode? If you are, I suggest you type ‘UDT.OPTIONS’ at ECL right now and continue down until you see whether ’41 U_UDT_SERVER’ is set to ON or OFF. Now, did it say OFF? If so, then read on because you may be vulnerable.
While that option is turned off, certain malformed UniQuery statements can cause you to crash straight to ECL, even if you are in a program called by a program.
Lets see an example. First, compile the following program in Pick Mode.
CRT "Enter the program to select"
CRT "Executing query..."
EXECUTE 'SELECT BP WITH @ID="':PROG.NAME:'"' CAPTURING INFO RETURNING RESULT
CRT "Checking results..."
IF SYSTEM(11) > 0 THEN
CRT "Program FOUND!"
CRT "Program doesn't exist"
Now, when you run the program (I called it CRASHTEST), put a record ID that exists in BP. Try again with a record ID that doesn’t exist. Your results should be something like this:
Looks good. Program works as expected. Underneath this simple program though, lies a
bug feature in the Pick Parser. To show this, I will use a specifically formed input that will make the programs SELECT malformed in a manner to gain ECL access. This time when you run the program (with UDT.OPTIONS 41 off) type in this input, including quotes:
In this case, the program will crash out before ever returning.
There are 2 ways to deal with this. The first way is to set UDT.OPTIONS 41 to ON. This will result in the EXECUTE returning so we can handle it whatever way we wish.
The other way is to set ON.ABORT. I created a VOC paragraph for this, called EXCEPTION as follows:
DISPLAY This program aborted
Running the same test above now results in:
Personally, I believe the method that returns control to the program (UDT.OPTIONS 41) and handles it accordingly (always check what was set by RETURNING) is the safest since it doesn’t give away that the program has a compromised EXECUTE statement. However, this may not always be a viable option, so make sure you at least have ON.ABORT set.