3

I follow this tutorial in order to understand how the 64 bits shellcode work.

So, I code this shellcode and it's works:

BITS 64

xor rax, rax
mov qword rbx, '//bin/sh'
shr rbx, 0x8
push rbx
mov rdi, rsp
push rax
mov rdx, rsp
push rdi
mov rsi, rsp
mov al, 59
syscall

I understand all of this code except two instructions:

mov qword rbx, '//bin/sh'
shr rbx, 0x8

I understand that a qword is a data of size 64 bits but when I remove qword the shellcode doesn't work. Why I need to add qword?

And after the instruction shr, my strings should be looks like '%00//bin/s' no? Why not use shl instead of shr?

2

I understand that a qword is a data of size 64 bits but when I remove qword the shellcode doesn't work. Why I need to add qword?

Because you're moving a qword constant. When you type '//bin/sh' you're expressing an 8-char array. By prefixing with qword you're instructing the assembler to treat the right hand operand as an inline 64-bit integer. By default it will probably treat it as a pointer to that string, or some other behaviour.

And after the instruction shr, my strings should be looks like '%00//bin/s' no?

Integers on x86 architecture processors are stored as little-endian. If your compiler took it literally, your string would be encoded in ASCII as follows:

/  /  b  i  n  /  s  h
2f 2f 62 69 6e 2f 73 68 

Which you can also show in the following instruction:

mov rbx, 0x2f2f62696e2f7368

This encodes as follows:

48 BB 68 73 2F 6E 69 62 2F 2F

We can split this up into 3 sections:

48        BB                     68 73 2F 6E 69 62 2F 2F
|         |                      |---------------------|
 \         \                             literal
  64-bit     B8 = MOV instr
  prefix        + reg operand
                + r/m operand

In this case the mov instruction byte (BB) also contains a 7-bit field which describes the register we're moving into (rbx) and the type of right-hand operand (literal).

But note carefully: the literal is not in the same order as the ASCII representation we saw above. Instead it is reversed due to little-endianness.

The problem here, of course, is this can't possibly be what happens in your shellcode. If the name was reversed in the bytes then the function you call would see hs/nib// and fail. Instead, your assembler is smart enough to realise that you want those ASCII characters in byte-order, which means it really encodes the following instruction:

mov rbx, 0x68732f6e69622f2f

This produces the following encoded instruction:

48 BB 2F 2F 62 69 6E 2F 73 68

As you can see, the // comes first (2F 2F) in the byte-order representation.

Why not use shl instead of shr?

You do both. Your code is missing an instruction, which can be seen in the tutorial. After the shl rbx, 0x8 there immediately follows a shr rbx, 0x8.

Their explanation does it justice:

After shifting 0x08 to the left (think of each numeric placeholder in the string as a 4, so moving 8 bytes over actually moves us two spaces) pushes the 11 right off the end. Now we have this:

0x68732f6e69622f00

Then we put it back like before by using shr by the same value, and we get this:

0x0068732f6e69622f

This successfully gives us our string terminated by a nullbyte without generating a single nullbyte in the machine code.

The first slash is simply a sacrificial character which gets discarded during the shl.

The reason for this trick instead of just loading the null in the first place or masking the rightmost byte with zero is that it requires no null-bytes in the exploit payload, which is very useful if input is ignored after a null (e.g. if a string is being copied with strcpy).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.