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

EducationalAssignments/SecurityLayerPartOne

Version 100 (modified by ericms, 7 years ago)

--

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 a  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/Repy


Please note you must have Python2.5 or Python2.6, to complete this assignment. Instructions on how to get Python for windows can be found here. If you are using a Mac, Python can be downloaded at  http://www.python.org/getit/mac/. If you are using linux it is likely you already have Python, in order to verify this, simply open a terminal and type python. It should load a screen which will tell you the version you are using.

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

Repy can be downloaded at  https://seattle.cs.washington.edu/geni/download/flibble/. Once you have Repy, the RepyTutorial will help you understand the basics of using Repy.

How to get RepyV2


Now that you have Repy, you simply need to get RepyV2. Using the command line, navigate to the directory where repy is installed and then run the following command to get RepyV2:

svn co https://seattle.cs.washington.edu/svn/seattle/branches/repy_v2

If the above command didn't work, you may need to download subversion. Subversion can be downloaded at,  http://subversion.apache.org/packages.html, the windows installation is found at the bottom of the page.

Once you have RepyV2, navigate to the directory repy_v2, then create a new directory and run the command found below. The new directory will be the destdir in the command found below. This command will initialize the downloaded files into the newly created directory.

python preparetest.py -t [destdir]

The directory you decide to unpack the files to should be the directory you use to run all your code! Please save all files you intend to run in this directory.

Finally use the command found below in order to run repy files:

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

Please note Repy files end in extension .repy.

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. If they failed, please go through the trouble shooting section found below.

Trouble shooting repy

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 however it does not have the same syntax. For a full list of acceptable syntax please see: [wiki : RepyApi]

  • command line errors:

files are missing: In the above command line call, you must have repy.py, restrictions.default encasementlib.repy, 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 .repy files from directories other than the the one you are supposed to use.

  • Downloading the wrong version of seattle:

Seattle is operating system dependent. If you download the windows version, you need to use the windows command line. For windows 7 this is powershell. You can open a new terminal by going to start, search, type powershell. If you downloaded the linux version you must use a linux OS and linux terminal.

Advanced trouble shooting:

To run the unit test, which will automatically tell you if you have errors with your installation please see:


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 an excellent source 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.repy, restrictions.default, repy.py and python
	Also you need to give it an application to run.
	This security layer never runs explicitedly but instead interposes functions
	from above layers.
	
	"""	
	
BUFFER = []
	
TYPE="type"
ARGS="args"
RETURN="return"
EXCP="exceptions"
TARGET="target"
FUNC="func"
OBJC="objc"

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

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

	def writeat(self,data,offset):
		BUFFER.append("SecureFile.writeat")
		if data.startswith("MZ") and (offset == 0):	
			raise ValueError("Cannot start file with MZ!")
		else:
			BUFFER.append("SecureFile.writeat")
			return self.file.writeat(data,offset)
	
	def close(self):
		BUFFER.append("SecureFile.close")
		return self.file.close()
	
	
sec_file_def = {"obj-type":SecureFile,
	            "name":"SecureFile",
	            "readat":{TYPE:FUNC,ARGS:((int,long),(int,long)),EXCP:Exception,RETURN:str,TARGET:SecureFile.readat},
	            "writeat":{TYPE:FUNC,ARGS:(str,(int,long)),EXCP:Exception,RETURN:(int,long),TARGET:SecureFile.writeat},
	            "close":{TYPE:FUNC,ARGS:None,EXCP:None,RETURN:(bool,type(None)),TARGET:SecureFile.close}
	           }

def secure_openfile(filename, create):
  BUFFER.append("openfile")
  f = openfile(filename,create)
  return SecureFile(f)

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 attackers 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!!!")
 
# No Exception was raise
else:
  log("Wrote an MZ!!!")
 
finally:
  # Close the file after our attempt.
	myfile.close()

Note: All attacks should be written as Repy files, using the .repy 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.repy [security_layer].repy [attack_program].repy

Make sure you went through the "How to get RepyV2" section or this won't work!

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: FutureRepyAPI

  • 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.repy [security_layer1].repy [security_layer2].repy [security_layer3].repy [program].repy

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 reference monitor which stops an attacker from writing 'p0wnd' anywhere in a file. You should also test the security layer by trying to write 'p0wnd' anywhere in the 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].repy
  • For extra credit turn in a second repy file called extra_credit_[ name or id number].repy

Attachments