Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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
    Stack Smashing
    (For Fun And Profit)

    View full-size slide

  2. @dylnuge
    What is Stack Smashing?

    View full-size slide

  3. @dylnuge
    Image Credits: Super Mario Wiki, Nintendo
    What is Stack Smashing?

    View full-size slide

  4. @dylnuge
    What is Stack Smashing?
    Image Credits: Super Mario Wiki, Nintendo

    View full-size slide

  5. @dylnuge
    What is Stack Smashing?
    Image Credits: Super Mario Wiki, Nintendo, Universal Pictures

    View full-size slide

  6. @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)

    View full-size slide

  7. @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

    View full-size slide

  8. @dylnuge
    Clobbering Variables

    View full-size slide

  9. @dylnuge
    Clobbering Variables
    Where do variables go?

    View full-size slide

  10. @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

    View full-size slide

  11. @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

    View full-size slide

  12. @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

    View full-size slide

  13. @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

    View full-size slide

  14. @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

    View full-size slide

  15. @dylnuge
    Side Note: ASCII
    Source: Simple English Wikipedia

    View full-size slide

  16. @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

    View full-size slide

  17. @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

    View full-size slide

  18. @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

    View full-size slide

  19. @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

    View full-size slide

  20. @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

    View full-size slide

  21. @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

    View full-size slide

  22. @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

    View full-size slide

  23. @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

    View full-size slide

  24. @dylnuge
    Interactive Example 1

    View full-size slide

  25. @dylnuge
    Abusing Function Calls
    What happens when we
    call a function?
    0x1c
    0x00
    0x1f
    0x03

    View full-size slide

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

    View full-size slide

  27. @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

    View full-size slide

  28. @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

    View full-size slide

  29. @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

    View full-size slide

  30. @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

    View full-size slide

  31. @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

    View full-size slide

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

    View full-size slide

  33. @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

    View full-size slide

  34. @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

    View full-size slide

  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

    View full-size slide

  36. @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.

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  41. @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

    View full-size slide

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

    View full-size slide

  43. @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)

    View full-size slide

  44. @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

    View full-size slide

  45. @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

    View full-size slide

  46. @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

    View full-size slide

  47. @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

    View full-size slide

  48. @dylnuge
    Interactive Example 2

    View full-size slide

  49. @dylnuge
    Powering Up
    So what can we put on the stack?

    View full-size slide

  50. @dylnuge
    Powering Up
    So what can we put on the stack?
    Image Credit: CBS Paramount

    View full-size slide

  51. @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.

    View full-size slide

  52. @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

    View full-size slide

  53. @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

    View full-size slide

  54. @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

    View full-size slide

  55. @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

    View full-size slide

  56. @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

    View full-size slide

  57. @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

    View full-size slide

  58. @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

    View full-size slide

  59. @dylnuge
    Interactive Example 3
    (which we’ll leave as homework)

    View full-size slide

  60. @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??

    View full-size slide

  61. @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??

    View full-size slide

  62. @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??

    View full-size slide

  63. @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

    View full-size slide

  64. @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

    View full-size slide

  65. @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

    View full-size slide

  66. @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

    View full-size slide

  67. @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

    View full-size slide

  68. @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…

    View full-size slide

  69. @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

    View full-size slide

  70. @dylnuge
    ROP ROP Till You Drop
    This is called “return oriented programming”

    View full-size slide

  71. @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”

    View full-size slide

  72. @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

    View full-size slide

  73. @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.

    View full-size slide

  74. @dylnuge
    Modern Prevention
    Modern compilers, OSes, and processors do try
    to stop you from making this mistake

    View full-size slide

  75. @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

    View full-size slide

  76. @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

    View full-size slide

  77. @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

    View full-size slide

  78. @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

    View full-size slide

  79. @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

    View full-size slide

  80. @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

    View full-size slide

  81. @dylnuge
    Modern Examples
    Video Credit: SethBling (https:/
    /www.youtube.com/watch?v=HxFh1CJOrTU)

    View full-size slide

  82. @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!

    View full-size slide