You are on page 1of 5

Ruchika Gupta

cs161-cb
Spring 2013
Paul Pearce
Dis W 10-11

CS 161 - HOMEWORK 1
Problem 1
Line 26Flaw: The scanf procedure call allows for buffer overflow if the user enters data longer than
expected input.
Fix: Put bound checks to prevent input that is too long. So, for example we can do:
scanf("%32s: %256s -> %32s", publisher.name, content, to);

Line 12Flaw: It is possible that malloc can return null. In this case, using buf can cause a null pointer
exception.
Fix: Check to make sure that buf is not null prior to using it.
Line 14Flaw: Once again, the size of the input is not being checked. If it is too large it can cause a buffer
overflow. Buffer overflow can generate other security vulnerabilities.
Fix: Put in bounds checks.
Flaw: We do not check that sender.name and msg contain expected input. If they dont, they can
possibly invoke dangerous functions.
Fix: We should do a regex check to ensure that sender.name and msg contain expected content.
For instance, we could check that they are strings with alphanumeric characters.
Flaw: dst can contain just about anything including certain flags such as l and any address.
Fix: Once again, we can do a regex check to ensure that the dst is as expected and doesnt
contain other flags, commands etc.
Line 16Flaw: This line is subject to format string vulnerabilities. If there is things like %n in buf, the
attacker can get control over the system.
Fix: Make sure that both arguments do not contain %.Also, instead of using fprintf we can use a
safer print such as sprintf.
Line 1Flaw: debug has not been initialized
Fix: Initialize debug to be 0.

Problem 2
(a) I will use the fourth, first, and second gadgets:
xorl
%ecx, %ecx
//register %ecx now contains 0
addl $42, %ecx
//register %ecx now contains 42
movl %ecx, %eax
//register %eax now contains 42

Ruchika Gupta

(b) B:0x1eg4cafe
C:0x1eg4face
D: 0x1eg4f00d
Initially, %ebp points to A. Now we execute leave so %esp points to A. We pop off %ebp
so %ebp points to the sfp value and %esp points to B. Then we pop off address at rip and
put it in %eip making it the next instruction to be executed, so in this the first gadget will
execute. Since we popped off content at B, %esp points to C. Now within the gadget
there is a return. So the instruction at C is popped off, moving %esp to D, and C is put
into %eip. Thus now second gadget will execute. The third gadget will execute in similar
fashion.
(c) B: 0x1ffa4b28
C: 0x1ffa4b28
D:0xbfdecae0
E: 0xbfdecaf0
Notes:
leave =>
ret

=>

mov1 %ebp %esp


pop1 %ebp
pop1 %eip

Initially, ebp points to A. Now we execute leave so esp points to A. We pop off ebp so
ebp points to the sfp value and esp points to B. Then we pop off address at rip and put it
in %eip making it the next instruction to be executed, so in this case system will execute.
Due to previous pop, esp points to C. We enter systems prologue. Ebp is pushed onto
stack and then both esp and eb p point to B. We access the argument at an offset of 8
from ebp so we access D. Thus at D we should put the argument we want system to use.
Now in leave we pop ebp off the stack so esp points to C. Now we pop off address at C,
in this case system, and put it in eip so system is the next to execute. Now %esp points to
D. System is now executing so we go through prologue: we put ebp on the stack and now
ebp and esp point to C. We get the argument at an offset off 8 from C which is at E so we
should put second argument at E. Now to leave we pop ebp of the stack so esp points to
D. The next instruction we would pull off from D.
(d) B:0x1ffa4b28
C:0x1eg4dead
D:0xbfdecae0
E: 0x1ffa4b28
F: 0x1ffa4b28
G:0xbfdecaf0
H:0xbfdecaf8

#address to system (*)


#address of our pop gadget
#address of our argument to system(*)
#address to system(**)
#address to system(***)
#address of our argument to system(**)
#address of our argument to system(***)

Ruchika Gupta
Note: As we can tell from the end of the previous parts note, if we want to execute
system a third time, the address for system would need to be stored at D but we need D to
store the argument to the first system call. We cant possibly store two different things in
the same place. Thus, we need to have a different tactic when trying to call system three
times. Instead of doing two system calls in a row we will sacrifice our second call to
system with something that will help us reposition our stack pointer (%esp). We must use
a gadget. Of the gadgets we have in (a) the most useful seems to be the third one. A pop
will help us move %esp. We replace our second system call with the address of this
gadget. This gadget at C gets popped off the stack and executed. %esp is at D. When the
pop executes %esp is now at E. Then in return the address at E gets puts into the
instruction pointer and here we can put our second call to system. We are now in the
same position as c so we interleave the last two system calls in the same way.
As we can see the gadget was necessary in letting us chain third system call.
(e) Address Space Layout Randomization. Randomize where the instructions such as
system are stored by choosing random offsets of memory locations at which to place
preexisting code when restarting the machine.
Cost: This takes up extra computing time. As mentioned on the end of the second
page of an article1 on address randomization, debugging and supporting
randomized executables can be much more difficult.
Limitations: The addresses cannot feasibly be truly random for attackers and
there is only so much granularity one can achieve. Attackers can still probe
around with the same system and find patterns to possibly determine parts of the
layout. Also
Canry. After the rip, put in a canary, a randomly chosen 32 bit number, and check to
make sure it is the same before using rip. If someone overflows it you can detect that
there is a problem.
Cost: Every type you call a procedure you have to generate/put in a new canary
on the stack. Every time you return, you have to doublecheck the canary. Since,
you can have many procedure calls and returns this can add a noticeable amount
of extra time just for checking.
Limitations: A canary can help detect a simple buffer overflow but if someone
knows it exists they can ensure not overwriting the canary on a buffer overflow.
For instance, one can figure out its value and rewrite original on top but still
overwrite rip on the stack.
Use heap. We can store return addresses in random locations on the heap and then on the
stack instead of having a rip, you could have a pointer to the heap and grab the return
address of the heap. A buffer overflow will no longer overwrite the return address but the
pointer to the heap and since the address will be outside the heap range we can detect it.
Cost: The execution of instructions will be much more complex maintaining state
on the stack as well as the heap.
Limitations: There are probably ways that attackers can modify the heap and
chain things together still but it is likely to be more complicated.
External Source:
1. http://cseweb.ucsd.edu/~hovav/dist/asrandom.pdf

Ruchika Gupta

Problem 3
(a) Human Factors - When humans think they are protected they will feel they no longer
the need to protect themselves as much. Also, humans cant enter the password at light
speed thus allowing other humans to see what ones password is.
Know your threat model Initially, the threat model was keyloggers but now the threat
model has changed. It is now other people who can physically see the cursor on the
screen of whoever is entering his/her password.
(b) Complete mediation: It is important to check access to every object. In this case, since
an HTTPS connection to Google is allowed someone can access something they should
not be able to access by going around paying for the service. Instead, the airline should
have blocked access to 3rd-party sites to ensure complete mediation.
(c) Security through obscurity Just because, the default code is not openly announced
does not mean it is safe to use. Potential invaders have the incentive to find that code and
unless its private it wont be safe.
(d) Least privilege: any application should only be granted the minimum access it needs to
successfully complete its job. This reduces the applications scope of harm. The flashlight
application does not need to know location, phone call state, or have full network access
to provide all its functionalities, so such privileges should not be granted.
Human factors- For an average person looking to download the application he or she is
likely to not read through all the notifications while downloading an app so is unlikely to
change the default privileges granted to the application.

Problem 4
The code is not memory safe. The while loop keeps incrementing j until the separation character
is found but if the separation character is not found it will eventually make an incorrect memory
access beyond the scope of s[]. Replace the while line with the following modification to ensure
we only check until the end of the array: while(s[j] != sep && j<n)
I will go through the program several times as done in lecture.
Iteration1:
void zeroOut(char s[], char sep, int n){
int j=0;
int k;
/* Requires: s!= NULL && 0 <= j && j<size(s) */
while(j<n && s[j] != sep){
j++;
}
for (k=j+1; k<n; k++){
/* Requires: s!= NULL && 0 <= k && k<size(s) */
s[k] = 0;
}
/* Requires: s!= NULL && 0 <= k && k<size(s) */
s[k] = \0;
}

Ruchika Gupta
Iteration 2: Lets propagate up some requirements. Also, we already know trivially that j<=0
because we initialize it as 0 and incrementing one by one and get checked for being less than n
prevents j from ever overflowing despite being an int. Because k uses j+1 asits initialization we
know that k must also be greater than or equal to 0 (in fact it is greater than or equal to 1). By
making n<=size(s) a precondition, we can remove it as a requirement for within the procedure.
/*requires: s != NULL && n<=size(s)*/
void zeroOut(char s[], char sep, int n) {
int j=0;
int k;
/*invariant: j>=0 && n<=size(s)*/
/* requires: j<n */
while(j<n && s[j] != sep){
j++;
}
for (k=j+1; k<n; k++){
/*invariant: k>=0 && n<=size(s)*/
/* requires k<n */
s[k] = 0;
}
/* requires: 0 <= k && k<size(s) */
s[k] = \0;
}
Iteration 3: We know trivially that the loop condition automatically makes the requirements that
j < n and k < n both true. After the second loop we know that after k fails once it must be n+1
and since within the loop we know that k<n we know that k is at most n+1 and n is <= size(s).
Also the largest j can be is n, this occurs when separation character is not found. The largest k
can be is n+1=size(s).
/*requires: s != NULL && n<=size(s)*/
void zeroOut(char s[], char sep, int n){
int j=0;
int k;
/*invariant: j>=0 && j<n && n<=size(s)*/
while(j<n && s[j] != sep){
j++;
}
for (k=j+1; k<n; k++){
/*invariant: k>=0 && k<n && n<=size(s)*/
s[k] = 0;
}
/* invariant: 0 <= k && k<=size(s) */
s[k] = \0;
}

You might also like