Details from the reporter, Rafal Wojtczuk
Attack steps (done by ring3 attacker)
- Map a frame at virtual address
(1<<47)-4096 - Any method to set the target of
sysretto a non-canonical address can potentially be used. This includes
ptrace,
sys_sigreturn,
sigaction,
execve, possibly others. The best solution is to add a check for the address being non-canonical close before executing
sysret. Note if the
syscallhandler ends with
iret, then even if
iretthrows
#GP,
rspis not controlled by the attacker, and such the situation can be handled safely.
- Place a
syscallinstruction at address
(1<<47)-2 - Place SOMETHING_MALICIOUS in general purpose registers
- Set
rspto AROUND_SOME_IMPORTANT_RING0_STRUCTURE
- Other scenarios are possible. Whenever the
#GPhandler runs with usermode
rsp, or does not do
swapgscorrectly, code execution may be possible.
- Jump to
syscallinstruction at
(1<<47)-2
At the
syscallhandler entry,
rcxwill be set to
(1<<47)-2+instruction_len(syscall) = 1<<47, which is non-canonical. Therefore, when the
syscallhandler terminates with
sysret,
#GP will be raised. This fault will be handled without a stack switch (assuming
#GPentry in
IDTdoes not include a nonzero IST index), as the faulting instruction is in ring0. Only Intel CPUs are affected. On AMD CPUs,
sysretcompletes the switch to ring3 before throwing
#GP, so the stack switch occurs. Also, immediately before
sysret, the
syscallhandler must restore
rspto the value set by ring3 (because
syscall/sysretdo not set
rsp). Therefore, the
#GPhandler will execute with
rspchosen by the attacker, so when
GPRsare pushed on the stack in the
#GPhandler prologue, SOMETHING_MALICIOUS will be placed at AROUND_SOME_IMPORTANT_RING0_STRUCTURE. This write-anything-anywhere primitive could be enough to hijack execution in ring0. Additionally, in many cases,
gsbase is not swapped in the
#GPprologue (as the fault originates in ring0), which may make the exploitation quite reliable and stable - overwrite
#PF IDTentry via stack push, trigger
#PFby
gs access, repair IDT (from IDT table of other cpu) in the shellcode, return to usermode.
(azért másoltam be ide, mert a CERT eltávolította az oldaláról időközben ezt a leírást... ;)