Tip: Audit virtual machine for setuid files

The script after the fold searches for setuid and setgid files on a disk image or in a virtual machine. When you run it you will see output like this:

# chmod +x findsuid.ml
# ./findsuid.ml RHEL60
/dev/vg_rhel6brewx64/lv_root:/sbin/mount.nfs is setuid file
/dev/vg_rhel6brewx64/lv_root:/sbin/netreport is setgid file
/dev/vg_rhel6brewx64/lv_root:/sbin/pam_timestamp_check is setuid file
/dev/vg_rhel6brewx64/lv_root:/sbin/unix_chkpwd is setuid file
/dev/vg_rhel6brewx64/lv_root:/usr/bin/Xorg is setuid file
/dev/vg_rhel6brewx64/lv_root:/usr/bin/at is setuid file
/dev/vg_rhel6brewx64/lv_root:/usr/bin/chage is setuid file
/dev/vg_rhel6brewx64/lv_root:/usr/bin/chfn is setuid file
/dev/vg_rhel6brewx64/lv_root:/usr/bin/chsh is setuid file

You could make simple adaptions to this script to audit for all sorts of things of interest: public writeable directories, unusual SELinux labels, hard links to setuid files, over-sized files. With more work you could look for files with unusual/changed checksums, infected files and so on.

#!/usr/bin/ocamlrun /usr/bin/ocaml
#load "unix.cma";;
#directory "+guestfs";;
#load "mlguestfs.cma";;

open Printf

let (//) = Filename.concat

let () =
  let prog = Filename.basename Sys.executable_name in
  if Array.length Sys.argv < 2 then (
    eprintf "Usage: %s guest\n" prog;
    exit 1

  (* Open guest domain. *)
  let g = new Guestfs.guestfs () in
  ignore (g#add_domain ~readonly:true Sys.argv.(1));
  g#launch ();

  (* If VMs could be encrypted you would have to
   * add additional calls to luks* here.
   * See fish/inspect.c:inspect_do_decrypt()

  (* Useful functions to test file types.  Note
   * can't use the regular stat functions, because
   * these modes are the ones defined in the Linux
   * ABI, not the host OS.
  let rec file_type mask mode =
    Int64.logand mode 0o170000L = mask
  (*and is_socket mode =
    file_type 0o140000L mode
  and is_symlink mode =
    file_type 0o120000L mode*)
  and is_regular_file mode =
    file_type 0o100000L mode
  (*and is_block mode =
    file_type 0o060000L mode*)
  and is_directory mode =
    file_type 0o040000L mode
  (*and is_char mode =
    file_type 0o020000L mode
  and is_fifo mode =
    file_type 0o010000L mode*)

  and is_suid mode = test_bit 0o4000L mode
  and is_sgid mode = test_bit 0o2000L mode
  (*and is_svtx mode = test_bit 0o1000L mode*)

  and test_bit mask mode =
    Int64.logand mode mask = mask

  (* Visit each file in the mounted filesystem.
   * TODO: Add libguestfs APIs to make this simpler
   * and faster.
  let rec visit f dir =
    let names = g#ls dir in
    let stats = lstatlist dir names in
    let entries = List.combine (Array.to_list names) stats in
    List.iter (f dir) entries;
    let dirs = List.filter entry_is_dir entries in
    List.iter (
      fun (name, stat) ->
        visit f (dir // name)
    ) dirs
  and entry_is_dir (_, { Guestfs.mode = mode }) =
    is_directory mode
  and lstatlist dir = function
    | [| |] -> []
    | names ->
        (* Split large requests so we don't overrun
         * the libguestfs protocol limit.
        let len = Array.length names in
        let first, rest =
          if len <= 1000 then names, [| |]
          else (
            Array.sub names 0 1000,
            Array.sub names 1000 (len-1000)
          ) in
        let stats = g#lstatlist dir first in
        Array.to_list stats @ lstatlist dir rest

  (* Test each file, printing those that match
   * the criteria.
  let search dev dir (name, { Guestfs.mode = mode }) =
    if is_regular_file mode && is_suid mode then
      printf "%s:%s is setuid file\n%!" dev (dir // name)
    else if is_regular_file mode && is_sgid mode then
      printf "%s:%s is setgid file\n%!" dev (dir // name)

  (* Search every mountable filesystem. *)
  let fses = g#list_filesystems () in
  List.iter (
    fun (dev, _) ->
      g#umount_all ();
      let mounted_ok =
        try g#mount_ro dev "/"; true
        with exn -> false in
      if mounted_ok then
        visit (search dev) "/"
  ) fses

Leave a comment

Filed under Uncategorized

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.