Please note that these Trac pages are no longer being updated. Wiki contents/documentation have moved to GitHub.

EducationalAssignments/SecurityLayerPartOne

Building a Security Layer

This assignment will help you understand security mechanisms. You will be guided through the steps of creating a reference monitor using the security layer functionality in Repy V2. A reference monitor is an access control concept that refers to an abstract machine that mediates all access to objects by subjects. This can be used to allow, deny, or change the behavior of any set of calls. One critical aspect of creating a reference monitor is to ensure it cannot be bypassed. While not a perfect solution, it is useful to create test cases to see whether your security layer will work as expected. (The test cases may be turned in as part of the next assignment.)

This assignment is intended to prepare you for thinking about security paradigms in a functional way. The ideas of information, security and privacy have been embedded into the steps of this assignment.


Overview


In this assignment an attacker attempts to write "MZ" to the first two characters of a file. (Historically, this is an indicator for an  old executable format.) Your goal is to prevent the attacker from succeeding. Thus you must stop a program from creating a file with "MZ" as the first two characters. You will do this by adding security rules to the functions available for reading from a file and writing to a file. The future of the system depends on your ability to write secure code!

Three design paradigms are at work in this assignment: accuracy, efficiency, and security.

  • Accuracy: The security layer should only stop certain actions from being blocked. All other actions should be allowed. For example, If a user tries to write "MX" to a file this should be allowed. As a second example "ZM" should be allowed as well.
  • Efficiency: The security layer should use a minimum number of resources, so performance is not compromised. This means you should not do things like re-read the file before each write.
  • Security: The attacker should not be able to circumvent the security layer. Hence, if "MZ" can be written two the first two characters, the security is compromised.


Getting Python


Please note you must have Python 2.6 or 2.7 to complete this assignment. Instructions on how to get Python for Windows can be found here. If you are using Linux or a Mac it is likely you already have Python. In order to verify this, simply open a terminal and type python. Please check its version on the initial prompt.

Note:If you are using Windows, you will need python in your path variables. A tutorial on adding variables to your path in Windows can be found at  http://www.computerhope.com/issues/ch000549.htm

Getting RepyV2


The preferred way to get Repy is installing from source. For this, you check out the required Git repositories, and run a build script. (This page describes the process in greater detail). You can always update the repos later, and rebuild, so that you get the latest stable version of the Repy runtime.

Here's how to do that. Assuming you are running on a Unixoid OS,

# Create a directory for the required Git repositories
mkdir SeattleTestbed
cd SeattleTestbed

# Check out the repos required for building Repy
git clone https://github.com/SeattleTestbed/repy_v2.git

# Prepare a build directory, and build into it
cd repy_v2/scripts
mkdir ~/path/to/build/dir
python initialize.py
python build.py ~/path/to/build/dir

The build directory, which can be named whatever you want, is where you will put your security layer code and the programs that will test it. Once the build script has finished, this directory will contain a ready-to-use copy of the RepyV2 runtime, i.e. repy.py, restrictions.default, encasementlib.r2py, etc.


Use the command found below in order to run Repy files:

python repy.py restrictions.default encasementlib.r2py [security_layer].r2py [program].r2py

Please note Repy files end in extension .r2py.

In order to test whether or not these steps worked, please copy and paste the code found below for the sample security layer and sample attack. Where you can find the sample security layer and sample attack:

If you got an error, please go through the troubleshooting section found below.

Troubleshooting Repy code

If you can't get Repy files to run, some of the following common errors may have occurred:

  • using print instead of log:

Repy is a subset of Python, but its syntax is slightly different. For example, Python's print statement cannot be used; Repy has log for that. For a full list of acceptable syntax please see wiki:RepyV2API.

  • command line errors:

files are missing: In the above command line call, you must have repy.py, restrictions.default encasementlib.r2py, the security layer and the program you want to run in the current working directory. If any or all of the above files are not in that directory then you will not be able to repy files. If this is the case, it is likely you are trying to run repy files from the wrong directory. Since repy.py exists in multiple directories, it is possible to run .r2py files from directories other than the the one you are supposed to use.


Tutorials for Repy and Python


Now that you have Repy and Python, you may need a refresher on how to use them. The following tutorials are excellent sources of information.


Building the security layer


RepyV2SecurityLayers explains the syntax used to build a reference monitor. The general tutorials above will aid in looking up other details about Repy. Remember, you have no idea how the attacker will try to penetrate your security layer, so it is important that you leave nothing to chance! Your reference monitor should try to stop every attack you can think of. Not just one or two. A good reference monitor will do this and incorporate the design paradigms of accuracy, efficiency and security defined above.

A basic (and inadequate) defense

Time to start coding! Let's inspect a basic security layer.

"""
This security layer interposes on a textfile 
and gives it open, close, read and write access.
However, a user cannot start the document with the letters,
MZ.
If a user tries to the security layer will raise an exception.
Note:
  This security layer uses encasementlib.r2py, restrictions.default, repy.py and python
  Also you need to give it an application to run.
  This security layer never runs explicitly but instead interposes functions
  from above layers.

"""	

# This is just an example global variable.   It stores a list of the calls the application makes.
BUFFER = []


class SecureFile():
  def __init__(self,privilegedfo):
    self.privilegedfo = privilegedfo

  def readat(self,bytes,offset):
    BUFFER.append("readat")
    return self.privilegedfo.readat(bytes,offset)

  def writeat(self,data,offset):
    BUFFER.append("writeat")
    if data.startswith("MZ") and (offset == 0):	
      raise ValueError("Cannot start file with MZ!")
    else:
      BUFFER.append("writeat")
      return self.privilegedfo.writeat(data,offset)
	
  def close(self):
    BUFFER.append("close")
    return self.privilegedfo.close()


def secure_openfile(filename, create):
  BUFFER.append("openfile")
  # Open a file from the layer below (likely the sandbox)
  privilegedfo = openfile(filename,create)
  return SecureFile(privilegedfo)


# You can ignore the code below here.   It just sets of the security layer so that the application calls it instead of
# the original API functions.
TYPE="type"
ARGS="args"
RETURN="return"
EXCP="exceptions"
TARGET="target"
FUNC="func"
OBJC="objc"

sec_file_def = {"obj-type":SecureFile,
    "name":"SecureFile",
    "readat":{TYPE:FUNC,ARGS:((int,long,type(None)),(int,long)),EXCP:Exception,RETURN:str,TARGET:SecureFile.readat},
    "writeat":{TYPE:FUNC,ARGS:(str,(int,long)),EXCP:Exception,RETURN:(None),TARGET:SecureFile.writeat},
    "close":{TYPE:FUNC,ARGS:None,EXCP:None,RETURN:(bool,type(None)),TARGET:SecureFile.close}
}

CHILD_CONTEXT_DEF["openfile"] = {TYPE:OBJC,ARGS:(str,bool),EXCP:Exception,RETURN:sec_file_def,TARGET:secure_openfile}

secure_dispatch_module()

Using the example layer

Keep in mind the above security layer would only stop one kind of attack. Thus if an attacker doesn't know much about file input/output this will probably stop them. However there are a bunch of tricks that can be used in order to circumvent this security layer easily. For instance, the above reference monitor isn't thread safe. For an introduction to thread safety please read  wiki/Thread_safety.


Code analysis

The writeat() function attempts follow the design principles of accuracy, efficiency, and security. However it does so inadequately. Accuracy is attempted by only blocking "MZ" from being written. If any other characters are written, the writeat() function executes normally. Efficiency is preserved because the number of test cases is small. Also security is attempted because "MZ" cannot be written to a file as easily. It is important to note that no security system is perfect. It is more important to make a security system that is infeasible to break, rather than impossible to break. However this security layer does not yet create the necessary infeasibility requirements for most attacks. Thus we see that there is a grey area of what is an acceptable level of impedance.

Now let's look at the structure of writeat(). First off, the if-statement contains the mitigation. If the "alarm" is not tripped then the writeat() function is executed normally. Within the if-statement data.startswith("MZ") is used to check for an illegal write. Specifically, data is a variable used to store information which will be written to the file. And the method startswith() checks to see if the file begins with certain characters, in this case "MZ".

The second condition, (offset == 0) determines the starting point of the cursor before it writes to the file. Thus offset = 0, means we start writing at the beginning of the file. Similarly if offset =4, the computer would start writing to the 5th position of the file. Starting with 0 rather than 1 has heuristic meaning in computer science.

Testing your security layer


In this part of the assignment you will pretend to be an attacker. Remember the attacker's objective is to write "MZ" to a file. By understanding how the attacker thinks, you will be able to write better security layers. Perhaps while attacking your security layer you will think of a new mitigation that should have been implemented. Keep in mind attacks are attempts to mitigate a given security protocol. If even one case succeeds, then your security layer has been compromised. Thus the attack you write should include several methods of attempting to write "MZ" to a file. An example of an attack is found below:

# Open a file
myfile=openfile("look.txt",True)
 
# Attempt to write "MZ" to the file
try:
    myfile.writeat("MZ",0)
 
# It raised an Exception (as it was supposed to):
except ValueError:
    log("The security layer correctly blocked the write!!!\n")
 
# No Exception was raise
else:
    log("Wrote an MZ!!!\n")
 
finally:
    # Close the file after our attempt.
    myfile.close()

Note: All attacks should be written as Repy files, using the .r2py extension.

Code Analysis

It is important to keep in mind that only lowercase file names are allowed. So in the above code, specifically:

# Open a file
myfile=openfile("look.txt",True)

look.txt is a valid file name, however Look.txt is not. Examples of other invalid files names are, look@.txt, look/.txt, and look().txt. Essentially all non-alphanumeric characters are not allowed.

This code attempts to write "MZ" to the file directly. First the file is opened using myfile=openfile("look.txt",True). Next myfile.writeat("MZ",0) tries to write "MZ" to the file. The 0 refers to an offset of zero. The try: statement tells the program to "try" this case. Notice that the except is executed if an error is raised. If the security layer fails the test then the else statement is executed. The finally: statement will always run, closing the file.

Running your security layer


Finally, type the following commands at the terminal to run your security layer with your attack program

python repy.py restrictions.default encasementlib.r2py [security_layer].r2py [attack_program].r2py

Make sure you use filenames that are all lowercase letters!


Notes and Resources


  • A tutorial on how to write security layers can be found here. At the end of the tutorial there is a second example on how to test security layers.
  • For a complete list of syntax in Repyv2 please visit: RepyV2API

  • Note: It is possible to add multiple security layers to Repy, this may be useful for testing different mitigations separately. This is done with the following command at the terminal:

python repy.py restrictions.default encasementlib.r2py [security_layer1].r2py [security_layer2].r2py [security_layer3].r2py [program].r2py

Your security layer should produce no output!!''' If it encounters an attempt to write MZ, you should raise a ValueError and allow execution to continue.

  • In repy log replaces print from python. This may be helpful when testing if Repy installed correctly.


Extra Credit


For extra credit, make a second security layer which stops an attacker from writing 'p0wnd' anywhere in a file. This security layer will be similar to the first one you wrote, except now you must handle the case of offset != 0 as well as a few other things. You should also write an attack layer which attempts to write 'p0wnd' to a file.

Hint: Since this string can be written anywhere in the file, there will most likely be more test cases.

What to turn in?


  • Turn in a repy file called reference_monitor_[ name or id number].r2py
  • For extra credit turn in a second repy file called extra_credit_[ name or id number].r2py

Attachments