410 likes | 766 Views
Part 4: Secure Coding. Many attacks are due to invalidated input to programs and faulty programming Secure Coding: Immensely important Not taught, not well understood Major problem with software development Languages used commonly are unsafe C, C++ are unsafe Java is much safer
E N D
Part 4: Secure Coding • Many attacks are due to invalidated input to programs and faulty programming • Secure Coding: • Immensely important • Not taught, not well understood • Major problem with software development • Languages used commonly are unsafe • C, C++ are unsafe • Java is much safer • We will use C to show common programming errors Code examples from “Secure Coding in C and C++” by Robert Seacord.
Two fundamental attacks • Buffer Overflow - writing past the end of an array or structure • Arbitrary Memory Write - writing to some arbitrary memory location The data written and the memory location chosen is under the control of the input data Input data is crafted by the attacker
gets is BAD • Never use gets, there is no safe way to use gets. • Even if you use getc, there could be problems (be careful) //gets problem void main (void) { char password[80]; puts ( “ Enter 8 char password: “); gets(password); }
Strings are Trouble • Strcpy, strcat are unsafe, in most cases. //string problems void main (int argc, char *argv[] ) { char name[2048]; // which line may overwrite? strcpy (name, argv[1]); strcat (name, “ = “); strcat (name, argv[2]); } //good int main (int argc, char *argv[] ) { char *buff = (char *)malloc(strlen(argv[1]+1); if (buf != NULL) { strcpy(buff, argv[1]); printf(“argv[1] =%s\n”, buff); } else { /* could not alloc, recover… */ } return 0;}
Off by one errors • Spot the errors //error int main (int argc, char *argv[] ) { char source[10]; strcpy(source, “0123456789”) char *dest = (char *)malloc(strlen(source)); for (int I = 1; i<=11; i++) { dest[i] = source[i]; } dest[i] = ‘\0’; printf(“dest = %s\n”, dest); }
More Grief • Counting errors • What happens after execution (difficult)? //bad, counting errors int main (int argc, char *argv[] ) { char a[16], b[16], c[32]; strcpy(a, ”0123456789abcdef”); strcpy(b, ”0123456789abcdef”); strcpy(c, a); strcpy(c, b); printf(”c = %s \n”, c); }
Double Dumb • Not just gets, more trouble bool IsPasswdOK(void) { char password[12]; gets(password); if (!strcmp(password, “goodpass”)) return(true) else return(false); }
Safe String Operations fgets, gets_s : Allows input size specifications strcpy_s, strcat_s : same strncpy (dest, source, size-1) : watch out for counting errors strncat : same Microsoft STRSAFE library n = read(fd, buff, n) // safe but complex programming needed
Overflow “protection” • Compiler checked bounds • Often inadequate, slow • Stackgap : introduce random gaps in stack • Canaries • Libsafe : replaces normal string operations, ensures overflows do not cross stack frame boundaries • Nothing can match proper input validation
Attack prone • Looks safe, but is not, wrong use of safe functions • Attacker can cause arc injection // call wrong function void good_f(char* str) { … } void main ( ); { static char buf [10] static void (*fptr)(char *str); fptr = &good_fl strnpy(buf, argv[1], strlen(argv[1]) fptr(argv[2]); } Attacker can overwrite fptr and provide args to fptr
Memory Layout generic Unix Win32 code code Reserved Reserved Reserved Reserved Reserved Reserved data Global data heap heap BSS segment heap heap heap stack code constant vars static vars stack stack stack stack un-init-vars
Where do these go? // memory layout static int initial = 1; // global data static int non-init; // BSS segment void main(int argc, char* argv[]) // stack { int i = 1, j; //stack static int x = 2 // global data static int y; // BSS segment int *buf = malloc (30); // buff on stack, contents on heap }
Arbitrary Memory Write • Via Overflow // bad memory write void foo (int* arg, int len); { char buff [50] ; int val = 25; int *ptr; = 30; memcpy(buff, arg, len); *ptr = val; … }
Function Pointer • Use of function pointer may be harmful // attackable function calls good-f(char *str) { printf(str) }; // watch out void main(int argc, char* argv[]) { void (*fptr) (const char *str); fptr = &good_f; *fptr(“hi ”); //attackable Good_f(“there\n”); //non-attackable }
Dangling Pointer • Free list structure is complex, and damage can be done by: • Using free memory • Double free • Prestored pointers are a source of errors p = malloc(50); …. Some code …… free(p) …… Some code …… *p is used here
Free Memory Error • Check the return value of malloc, else you may get… //referencing free memory int *mat(int *a, *b, n) { int *c = malloc(n * sizeof(int)); // no check? int i; for (i=0; i<n; i++) c += a[i] + b[i]; return c; } //two errors, subtle for (p=head; p != NULL; p = p->next) { // error 1 q = p-> next; free(p) ; // error 2 }
Double Free • More common that you think // freeing same memory twice x = malloc (size); //work on it free(x); y = malloc(size); //work on it; free(x); // often a cut and paste error
The Unlink Attack • Overflow in allocated memory may make unlinking dangerous Buffer overflow in HEAP – unlink attack int main(int arrgc, char *argv[]); { char first, second, third; first = malloc (222); second = malloc (12); third = malloc (12); strcpy(first, argv[1]); // may mess up mem alloc free(first); free(second); free(third); }
Mitigations • Make pointer null after free free(p); p = NULL; • Use consistent strategy • Do it in constructors and destructors • Do it at beginning and end of each module • Pair malloc and free • Keep you own free list (fixed size items) • Canaries • Guard pages • Randomization of malloc strategy
Integers are rather complex • C/C++ has many integer types • Unsigned, short, long, long long • Integers have ranks • Integer promotions are not well understood, conversions are also used and can cause errors (truncation, overflow) • Mixing unsigned ans signed integers are a problem, assigning negative numbers to unsigned integers is common (attack)
Size Matters • Obvious error INTEGER Security int main (int argc, char **argv); { unsigned short int total; total = strlen(argv[1])+strlen(argv[2]); char *buf = malloc (total); strcpy (buf, argv[1]); strcpy (buf, argv2)); }
Automatic promotions and casting • May not work, and then may //integer promotions char c1, c2, c3 result; c1 = 100; c2 = 90; c3 = -120; result = c1+c2+c3; // will work correctly (BAD)
From Real Life • Jpeg file comment size includes some other 2 bytes //JPEG vulnerability void getcomment(unsigned int len, char *src) { unsigned int size; size = len - 2; char comment = (char *) malloc(size + 1); memcpy (comment, src, size); } read the value of len from JPG file and then get comment.
Easy Heap Overflow • What does memcpy expect as type of len? // buffer overflow with –ve number main (argc, argv) { int len, char buf [size]; //size is a constant len = atoi(argv[1]); if (len <= size) ( memcpy(buf, argv[2], len); else printf(“too much data”); }
skip // truncation error main(argc, argv) { unsigned short int total; total = strlen(argv[1])+strlen(argv[2]); char *buf = malloc (total) strcpy(buf, argv[1]); strcat(buf, argv[2]); }
Skip // truncation problem with conversion bool func(char * name, long cbBuf); unsigned short bufSize = cbBuf; char *buf = (char *) malloc (bufSize); if buf { memcpy ( buf, name, cbBuf); }
Attackers think -ve • Most programs do not check for –ve values //negative index int *table = null; int insert(int pos, int value) { if (!table) table = maloc (400) if (pos > 99) { return -1} table[pos] = value; }
printf can be harmful • Check for user provided input • What does argv[0] actually contain? //usage exploit void usage(char *p) { char str[1024]; snprintf(str, 1024, “Usage: %s <targets>\n”, p); printf(usageStr); } Main() {if argc not > 2 usage(argv[0]); //easier to attack … printf(argv[1]);
Printf attacks //simplest func(char *user) { printf(user);} attack1: %s%s%s%s%s%s………… may crash attack2: %08x, %08x, %08x, ………… attack3: use %x a few times and then use %s attack4: \xdc\xf5\x42\x01%08x%08x%08x%08x%s (very tricky)
skip //buffer overflow char buffer[512]; sprintf(buffer, “Wrong command: %s\n”, user); //stretchable buffer char outbuf[512], buffer[512]; sprintf(buffer, “Wrong Command: %.400s”, user); sprintf(outbuf, buffer); -> input %497\x3c\xd3\xff\xbf\....[shellcode]
Memory Writes with printf • %n = write # of chars outputted%u wite integer using u spaces //overwriting memory int i; printf(“Hello %n\n”, (int*) &i); also: printf(“\xdc\xf5\x42\x0108x%08x%08x%08x%n”) //[write specific address] int i; printf("%10u%n", 1, &i); //i=10 printf("%100u%n", 1, &i); //i=100
An incomplete example unsigned char foo[8] printf(%x16u%n, 1, &foo[0]); printf(%x32u%n, 1, &foo[1]); printf(%x64u%n, 1, &foo[2]); printf(%x128u%n, 1, &foo[3]); printf("%16u%16u%32u%64u%n", 1, &foo[1], 1, &foo[2], 1, &foo[3], 1, &foo[4]);
Args to Prinf void foo(int p,int q, char *r, int s, int t) { int a[4] = {1,2,3,4}; print the stack here }; main() { foo(55, 56,"abcdefgh", 57, 58); }; bfabf458: 1 bfabf45c: 2 bfabf460: 3 bfabf464: 4 bfabf468: bfabf488 // base pointer bfabf46c: 8048410 // return address bfabf470: 37 // 55 - arg 1 bfabf474: 38 // 56 - arg 2 bfabf478: 8048520 // address for "abc... bfabf47c: 39 // 57 - arg 3 bfabf480: 3a // 58 - arg 4