$30 off During Our Annual Pro Sale. View Details »

Smashing the Stack (For Fun and Profit)

Dylan Nugent
December 15, 2018

Smashing the Stack (For Fun and Profit)

An introductory session on stack smashing and some of the content in the seminal Aleph One paper. Pairs with interactive content which can be found on GitHub here: https://github.com/Dylnuge/stack-smashing.

Dylan Nugent

December 15, 2018
Tweet

Other Decks in Technology

Transcript

  1. @dylnuge Image Credits: Super Mario Wiki, Nintendo What is Stack

    Smashing? A form of buffer overflow exploit—we are going to write to memory we’re not supposed to (don’t worry if this doesn’t make sense yet) Modern computers try to protect against this, but it’s far from useless knowledge, even today We’ll come back to Mario at the end (no, seriously)
  2. @dylnuge Resources We’re going to do some real exploits! Download

    the ZIP and follow along! All the source for this is in my stack-smashing repo: https:/ /github.com/Dylnuge/stack- smashing The Aleph1 paper this is about (in part) was published in Phrack. A copy of the text can be found here: https:/ /insecure.org/stf/ smashstack.html
  3. @dylnuge Clobbering Variables Where do variables go? Each square here

    is 1 byte (8 bits). Each row has 4 squares, so 4 bytes (32 bits) 0x1c 0x00 0x1f 0x03
  4. @dylnuge Clobbering Variables Where do variables go? Let’s push a

    value on this stack: int myInt = 0; 0x1c 0x00 0x1f 0x03 00 00 00 00
  5. @dylnuge Clobbering Variables Where do variables go? Let’s push a

    value on this stack: int myInt = 0;
 int secondInt = 42; 0x1c 0x00 0x1f 0x03 00 00 00 00 2A 00 00 00
  6. @dylnuge Clobbering Variables Where do variables go? Let’s push a

    value on this stack: int myInt = 0;
 int secondInt = 42; Note that as we add to the stack, we move “up” in memory, to lower addresses 0x1c 0x00 0x1f 0x03 00 00 00 00 2A 00 00 00
  7. @dylnuge Clobbering Variables What about strings (text)? char username[8] =

    “dylnuge”; Notes on strings: ASCII format Ends with a 00 (called a “null terminator”) 0x1c 0x00 0x1f 0x03 00 00 00 00 2A 00 00 00 64 79 6C 6E 75 67 65 00
  8. @dylnuge Clobbering Variables Let’s read the string from the user

    instead char username[8]; 0x1c 0x00 0x1f 0x03 00 00 00 00 2A 00 00 00 00 00 00 00 00 00 00 00
  9. @dylnuge Clobbering Variables Let’s read the string from the user

    instead char username[8];
 gets(username); I enter “dylnuge” and it’s copied into the variable username 0x1c 0x00 0x1f 0x03 00 00 00 00 2A 00 00 00 00 00 00 00 00 00 00 00 64 79 6C 6E 75 67 65 00
  10. @dylnuge Clobbering Variables Let’s read the string from the user

    instead char username[8];
 gets(username); I enter “dylnuge” and it’s copied into the variable username But that fits. What if I did “dylnuge evil”? 0x1c 0x00 0x1f 0x03 00 00 00 00 2A 00 00 00 00 00 00 00 00 00 00 00 64 79 6C 6E 75 67 65 00
  11. @dylnuge Clobbering Variables Let’s read the string from the user

    instead char username[8];
 gets(username); I enter “dylnuge” and it’s copied into the variable username But that fits. What if I did “dylnuge evil”? 0x1c 0x00 0x1f 0x03 00 00 00 00 65 76 69 6C 00 00 00 00 00 00 00 00 64 79 6C 6E 75 67 65 20
  12. @dylnuge Clobbering Variables int myInt = 0;
 int secondInt =

    42;
 char username[8];
 gets(username);
 printf(“%d\n”, secondInt); 0x1c 0x00 0x1f 0x03 00 00 00 00 65 76 69 6C 00 00 00 00 00 00 00 00 64 79 6C 6E 75 67 65 20
  13. @dylnuge Clobbering Variables int myInt = 0;
 int secondInt =

    42;
 char username[8];
 gets(username);
 printf(“%d\n”, secondInt); We’d expect to print the value 42, but we’d actually print 1818850917 0x1c 0x00 0x1f 0x03 00 00 00 00 65 76 69 6C 00 00 00 00 00 00 00 00 64 79 6C 6E 75 67 65 20
  14. @dylnuge Clobbering Variables int myInt = 0;
 int secondInt =

    42;
 char username[8];
 gets(username);
 printf(“%d\n”, secondInt); We’d expect to print the value 42, but we’d actually print 1818850917 (that’s 0x6C697665) 0x1c 0x00 0x1f 0x03 00 00 00 00 65 76 69 6C 00 00 00 00 00 00 00 00 64 79 6C 6E 75 67 65 20
  15. @dylnuge Side Note: Endianness int myInt = 0x01020304;
 int secondInt

    = 42; Why do these values look weird? We can choose to write a number from most to least significant byte (big-endian) or the other way around (little- endian) Intel (x86) uses little-endian 0x1c 0x00 0x1f 0x03 04 03 02 01 2A 00 00 00
  16. @dylnuge Abusing Function Calls What happens when we call a

    function? What info does our function need? 0x1c 0x00 0x1f 0x03
  17. @dylnuge Arguments Abusing Function Calls What happens when we call

    a function? What info does our function need? Arguments to the function 0x1c 0x00 0x1f 0x03
  18. @dylnuge Return Pointer Arguments Abusing Function Calls What happens when

    we call a function? What info does our function need? Arguments to the function Where to come back to 0x1c 0x00 0x1f 0x03
  19. @dylnuge Frame Pointer Return Pointer Arguments Abusing Function Calls What

    happens when we call a function? What info does our function need? Arguments to the function Where to come back to What the old stack was 0x1c 0x00 0x1f 0x03
  20. @dylnuge Frame Pointer Return Pointer Arguments Abusing Function Calls This

    is “C Calling Convention” There’s no rules things go in this order (so long as they’re consistent and the program knows where to find them) 0x1c 0x00 0x1f 0x03
  21. @dylnuge Frame Pointer Return Pointer Arguments Abusing Function Calls This

    is “C Calling Convention” There’s no rules things go in this order (so long as they’re consistent and the program knows where to find them) As our new function runs, it may put new things on the stack 0x1c 0x00 0x1f 0x03
  22. @dylnuge 2A 00 00 00 Frame Pointer Return Pointer Arguments

    Abusing Function Calls int myVar = 42; 0x1c 0x00 0x1f 0x03
  23. @dylnuge 2A 00 00 00 Frame Pointer Return Pointer Arguments

    Abusing Function Calls int myVar = 42;
 char username[8]; 0x1c 0x00 0x1f 0x03 00 00 00 00 00 00 00 00
  24. @dylnuge 2A 00 00 00 Frame Pointer Return Pointer Arguments

    Abusing Function Calls int myVar = 42;
 char username[8];
 gets(username); 0x1c 0x00 0x1f 0x03 00 00 00 00 00 00 00 00
  25. @dylnuge 2A 00 00 00 Frame Pointer Return Pointer Arguments

    Abusing Function Calls int myVar = 42;
 char username[8];
 gets(username); You can probably see some things we could do with this… 0x1c 0x00 0x1f 0x03 00 00 00 00 00 00 00 00
  26. @dylnuge Process Memory All programs like to think they have

    all the system memory, so they get their own isolated address space. High Low 0x00000000 0xffffffff For right now, we’re changing the scale here. We’ll change it back after this word from our sponsors.
  27. @dylnuge Process Memory They need to put things in this

    space: High Low 0x00000000 0xffffffff
  28. @dylnuge Process Memory They need to put things in this

    space: A stack High Low 0x00000000 0xffffffff
  29. @dylnuge Process Memory They need to put things in this

    space: A stack The code itself (text) High Low 0x00000000 0xffffffff
  30. @dylnuge Process Memory They need to put things in this

    space: A stack The code itself (text) Dynamic data (heap) High Low 0x00000000 0xffffffff
  31. @dylnuge Process Memory They need to put things in this

    space: A stack The code itself (text) Dynamic data (heap) Note that the “text” segment includes all libraries the code uses High Low 0x00000000 0xffffffff
  32. @dylnuge Process Memory Function calls point to the same, consistent

    memory address in the text segment High Low 0x00000000 0xffffffff
  33. @dylnuge Process Memory Function calls point to the same, consistent

    memory address in the text segment High Low 0x00000000 0xffffffff Image Credit: Celeste (Matt Makes Games)
  34. @dylnuge Process Memory Function calls point to the same, consistent

    memory address in the text segment The objdump command will be incredibly useful here. Example time! High Low 0x00000000 0xffffffff
  35. @dylnuge 2A 00 00 00 Frame Pointer Return Pointer Arguments

    Abusing Function Calls int myVar = 42;
 char username[8];
 gets(username); You can probably see some things we could do with this… 0x1c 0x00 0x1f 0x03 00 00 00 00 00 00 00 00
  36. @dylnuge 2A 00 00 00 Frame Pointer Return Pointer Arguments

    Abusing Function Calls int myVar = 42;
 char username[8];
 gets(username); You can probably see some things we could do with this… Like return to an arbitrary function 0x1c 0x00 0x1f 0x03 00 00 00 00 00 00 00 00
  37. @dylnuge 2A 00 00 00 Frame Pointer Return Pointer Arguments

    Abusing Function Calls int myVar = 42;
 char username[8];
 gets(username); You can probably see some things we could do with this… Like return to an arbitrary function The frame pointer will die, but we don’t care 0x1c 0x00 0x1f 0x03 00 00 00 00 00 00 00 00
  38. @dylnuge Powering Up So what can we put on the

    stack? Image Credit: CBS Paramount
  39. @dylnuge Side Note: x86 32-bit Intel (and AMD) processors use

    a machine language called “x86” (or “i386”) 64-bit ones use a similar language called “x64” (or “x86_64” or “amd_64”) Instructions in x86 (and x64) are variable length.
  40. @dylnuge Side Note: x86 Here’s code to subtract 57286 from

    58623 b8 ff e4 00 00 mov eax,0xe4ff
 2d c6 df 00 00 sub eax,0xdfc6
  41. @dylnuge Side Note: x86 Here’s code to subtract 57286 from

    58623 b8 ff e4 00 00 mov eax,0xe4ff
 2d c6 df 00 00 sub eax,0xdfc6 The result of this is stored in the register %eax
  42. @dylnuge Side Note: x86 Here’s code to subtract 57286 from

    58623 b8 ff e4 00 00 mov eax,0xe4ff
 2d c6 df 00 00 sub eax,0xdfc6 The result of this is stored in the register %eax Registers are storage spaces on the processor itself—they’re like variables, but there’s not that many of them, so we’re constantly copying them back and forth from memory
  43. @dylnuge Other Victims
 Variables Buffer Return Pointer Frame Pointer Writing

    Our Own Code Here’s a stack where we know the address that our buffer is at High 0x00aabbcc
  44. @dylnuge Shell code can continue through these (clobbered) Shell code

    (assembly code is just numbers) 0x00aabbcc Who Cares? Writing Our Own Code Here’s a stack where we know the address that our buffer is at High 0x00aabbcc
  45. @dylnuge Shell code can continue through these (clobbered) Shell code

    (assembly code is just numbers) 0x00aabbcc Who Cares? Writing Our Own Code Here’s a stack where we know the address that our buffer is at Imagine that we put the assembly equivalent of execv(“/bin/bash”) High 0x00aabbcc
  46. @dylnuge Shell code can continue through these (clobbered) Shell code

    (assembly code is just numbers) 0x00aabbcc Who Cares? Writing Our Own Code Here’s a stack where we know the address that our buffer is at Imagine that we put the assembly equivalent of execv(“/bin/bash”) Now we have a shell (hence “shell code”) High 0x00aabbcc
  47. @dylnuge Shell code can continue through these (clobbered) Shell code

    (assembly code is just numbers) 0x00aabbcc Who Cares? NOP NOP, Who’s There? Even if we can execute on the stack, we might not know the exact stack pointer at the time we’re read in High 0x00aabb??
  48. @dylnuge Shell code can start here 90 90 90 90

    90 90 90 90 90 90 90 90 0x00aabbcc Who Cares? NOP NOP, Who’s There? NOP (0x90 in 32-bit x86)
 - Do nothing High 0x00aabb??
  49. @dylnuge Shell code can start here 90 90 90 90

    90 90 90 90 90 90 90 90 0x00aabbcc Who Cares? NOP NOP, Who’s There? NOP (0x90 in 32-bit x86)
 - Do nothing When we run, we jump somewhere into the NOP “sled” (hopefully) High 0x00aabb??
  50. @dylnuge Shell code can start here 90 90 90 90

    90 90 90 0x00aabbcc Who Cares? NOP NOP, Who’s There? NOP (0x90 in 32-bit x86)
 - Do nothing When we run, we jump somewhere into the NOP “sled” (hopefully) High 0x00aabb?? Image Credit: Mercury Productions
  51. @dylnuge Other
 Variables Buffer Return Pointer Frame Pointer ROP ROP

    Till You Drop OK…what if we don’t have even a guess? High Low
  52. @dylnuge Other
 Variables Buffer Return Pointer Frame Pointer ROP ROP

    Till You Drop OK…what if we don’t have even a guess? Or what if our shell code is already in the program’s text? High Low
  53. @dylnuge Other
 Variables Buffer Return Pointer Frame Pointer ROP ROP

    Till You Drop OK…what if we don’t have even a guess? Or what if our shell code is already in the program’s text? With tons of assembly in libraries, we can jump to a part of the code that wasn’t intended High Low
  54. @dylnuge ROP ROP Till You Drop Recall this (32-bit, x86)

    assembly code: b8 ff e4 00 00 mov eax,0xe4ff This is innocuous code that could appear in any library. It moves the number 58,623 into a register
  55. @dylnuge ROP ROP Till You Drop Recall this (32-bit, x86)

    assembly code: b8 ff e4 00 00 mov eax,0xe4ff This is innocuous code that could appear in any library. It moves the number 58,623 into a register But if we jump to the second byte of it, it does something very different…
  56. @dylnuge ROP ROP Till You Drop Recall this (32-bit, x86)

    assembly code: b8 ff e4 00 00 mov eax,0xe4ff This is innocuous code that could appear in any library. It moves the number 58,623 into a register But if we jump to the second byte of it, it does something very different… ff e4 jmp esp
  57. @dylnuge ROP ROP Till You Drop This is called “return

    oriented programming” Segments of code in a library that can be interpreted differently when read from an offset that starts in the middle of an instruction are called “gadgets”
  58. @dylnuge ROP ROP Till You Drop This is called “return

    oriented programming” Segments of code in a library that can be interpreted differently when read from an offset that starts in the middle of an instruction are called “gadgets” The example I gave no longer works very well
  59. @dylnuge ROP ROP Till You Drop This is called “return

    oriented programming” Segments of code in a library that can be interpreted differently when read from an offset that starts in the middle of an instruction are called “gadgets” The example I gave no longer works very well But other gadgets can still be found—the sheer quantity of assembly code makes the odds high.
  60. @dylnuge Modern Prevention Modern compilers, OSes, and processors do try

    to stop you from making this mistake Some of the ways they do this include Memory Segmentation and DEP: Preventing data sections from being executed as code
  61. @dylnuge Modern Prevention Modern compilers, OSes, and processors do try

    to stop you from making this mistake Some of the ways they do this include Memory Segmentation and DEP: Preventing data sections from being executed as code ASLR and PIE: Randomizing the memory space of the OS and the executable on load
  62. @dylnuge Modern Prevention Modern compilers, OSes, and processors do try

    to stop you from making this mistake Some of the ways they do this include Memory Segmentation and DEP: Preventing data sections from being executed as code ASLR and PIE: Randomizing the memory space of the OS and the executable on load Stack canaries: Detecting stack smashing when it happens
  63. @dylnuge Modern Examples But stack smashing and other buffer overflow

    attacks still come up all the time Kind of like code injections (e.g. SQL injection), they’re hard to kill
  64. @dylnuge Modern Examples But stack smashing and other buffer overflow

    attacks still come up all the time Kind of like code injections (e.g. SQL injection), they’re hard to kill Image Credit: xkcd 1354
  65. @dylnuge Modern Examples But stack smashing and other buffer overflow

    attacks still come up all the time Kind of like code injections (e.g. SQL injection), they’re hard to kill Image Credits: xkcd 1354, Nintendo
  66. @dylnuge Follow Ups If you enjoyed this talk: Phrak, PoC||GTFO,

    Academic Papers DEFCON, BSides, Tech Talks, Meetups CTFs and Coding Challenges (dcdark.net, cryptopals.com, hackthebox.eu, etc) Teach Someone This! (Or something else!) Happy to take questions and discuss further any time!