├── README.md ├── SUIDGuardNG.xcodeproj └── project.pbxproj └── SUIDGuardNG ├── Info.plist ├── SUIDGuardNG.cpp └── SUIDGuardNG.h /README.md: -------------------------------------------------------------------------------- 1 | SUIDGuard - A kernel extension adding mitigations to OS X to make exploitation harder 2 | 3 | Copyright (c) Stefan Esser / SektionEins GmbH, 2015. All rights reserved. 4 | stefan.esser@sektioneins.de - https://www.sektioneins.de/ 5 | 6 | **OFFICIAL WEBSITE** 7 | If you came here from a media article please checkout the official website https://www.suidguard.com 8 | 9 | ## About 10 | SUIDGuard is a TrustedBSD kernel driver that implements several mitigations to protect 11 | against weaknesses in the operating system usually abused in exploits. 12 | 13 | - protects SUID/SGID root binaries from DYLD_ environment variables 14 | by overwriting the string DYLD_ with XYLD_ 15 | - protects the O_APPEND flag usually used when opening e.g. logfiles 16 | from being disabled by someone with credentials that are different 17 | from those used to open the file 18 | - disallows execution of executables without a __PAGEZERO segment 19 | (stops NULL page exploits like e.g. tpwn) 20 | 21 | Tested with OS X Yosemite 10.10.5. 22 | 23 | ## Downloads for OSX 10.10.x 24 | 25 | **ATTENTION**: 26 | For ease of installation an autoloading version of this extension including 27 | a signed installer is available at 28 | 29 | DMG: https://www.suidguard.com/downloads/SUIDGuardNG-106.dmg 30 | 31 | PKG: https://www.suidguard.com/downloads/SUIDGuardNG-106.pkg 32 | 33 | (source code on GitHub might not always be latest) 34 | 35 | **WARNING**: These downloads are meant for OSX 10.10 only! Please **uninstall before upgrading to 10.10.4**!! (see issue #12 on details how to manually remove the extension after crash-on-boot) 36 | 37 | ## Downloads for OSX 10.11 38 | 39 | TBD 40 | -------------------------------------------------------------------------------- /SUIDGuardNG.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B1EF6CB61B61C3E9008C5BD4 /* SUIDGuardNG.h in Headers */ = {isa = PBXBuildFile; fileRef = B1EF6CB51B61C3E9008C5BD4 /* SUIDGuardNG.h */; }; 11 | B1EF6CB81B61C3E9008C5BD4 /* SUIDGuardNG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1EF6CB71B61C3E9008C5BD4 /* SUIDGuardNG.cpp */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXFileReference section */ 15 | B1EF6CB01B61C3E9008C5BD4 /* SUIDGuardNG.kext */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SUIDGuardNG.kext; sourceTree = BUILT_PRODUCTS_DIR; }; 16 | B1EF6CB41B61C3E9008C5BD4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 17 | B1EF6CB51B61C3E9008C5BD4 /* SUIDGuardNG.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SUIDGuardNG.h; sourceTree = ""; }; 18 | B1EF6CB71B61C3E9008C5BD4 /* SUIDGuardNG.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SUIDGuardNG.cpp; sourceTree = ""; }; 19 | /* End PBXFileReference section */ 20 | 21 | /* Begin PBXFrameworksBuildPhase section */ 22 | B1EF6CAC1B61C3E9008C5BD4 /* Frameworks */ = { 23 | isa = PBXFrameworksBuildPhase; 24 | buildActionMask = 2147483647; 25 | files = ( 26 | ); 27 | runOnlyForDeploymentPostprocessing = 0; 28 | }; 29 | /* End PBXFrameworksBuildPhase section */ 30 | 31 | /* Begin PBXGroup section */ 32 | B1EF6CA61B61C3E9008C5BD4 = { 33 | isa = PBXGroup; 34 | children = ( 35 | B1EF6CB21B61C3E9008C5BD4 /* SUIDGuardNG */, 36 | B1EF6CB11B61C3E9008C5BD4 /* Products */, 37 | ); 38 | sourceTree = ""; 39 | }; 40 | B1EF6CB11B61C3E9008C5BD4 /* Products */ = { 41 | isa = PBXGroup; 42 | children = ( 43 | B1EF6CB01B61C3E9008C5BD4 /* SUIDGuardNG.kext */, 44 | ); 45 | name = Products; 46 | sourceTree = ""; 47 | }; 48 | B1EF6CB21B61C3E9008C5BD4 /* SUIDGuardNG */ = { 49 | isa = PBXGroup; 50 | children = ( 51 | B1EF6CB51B61C3E9008C5BD4 /* SUIDGuardNG.h */, 52 | B1EF6CB71B61C3E9008C5BD4 /* SUIDGuardNG.cpp */, 53 | B1EF6CB31B61C3E9008C5BD4 /* Supporting Files */, 54 | ); 55 | path = SUIDGuardNG; 56 | sourceTree = ""; 57 | }; 58 | B1EF6CB31B61C3E9008C5BD4 /* Supporting Files */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | B1EF6CB41B61C3E9008C5BD4 /* Info.plist */, 62 | ); 63 | name = "Supporting Files"; 64 | sourceTree = ""; 65 | }; 66 | /* End PBXGroup section */ 67 | 68 | /* Begin PBXHeadersBuildPhase section */ 69 | B1EF6CAD1B61C3E9008C5BD4 /* Headers */ = { 70 | isa = PBXHeadersBuildPhase; 71 | buildActionMask = 2147483647; 72 | files = ( 73 | B1EF6CB61B61C3E9008C5BD4 /* SUIDGuardNG.h in Headers */, 74 | ); 75 | runOnlyForDeploymentPostprocessing = 0; 76 | }; 77 | /* End PBXHeadersBuildPhase section */ 78 | 79 | /* Begin PBXNativeTarget section */ 80 | B1EF6CAF1B61C3E9008C5BD4 /* SUIDGuardNG */ = { 81 | isa = PBXNativeTarget; 82 | buildConfigurationList = B1EF6CBB1B61C3E9008C5BD4 /* Build configuration list for PBXNativeTarget "SUIDGuardNG" */; 83 | buildPhases = ( 84 | B1EF6CAB1B61C3E9008C5BD4 /* Sources */, 85 | B1EF6CAC1B61C3E9008C5BD4 /* Frameworks */, 86 | B1EF6CAD1B61C3E9008C5BD4 /* Headers */, 87 | B1EF6CAE1B61C3E9008C5BD4 /* Resources */, 88 | ); 89 | buildRules = ( 90 | ); 91 | dependencies = ( 92 | ); 93 | name = SUIDGuardNG; 94 | productName = SUIDGuardNG; 95 | productReference = B1EF6CB01B61C3E9008C5BD4 /* SUIDGuardNG.kext */; 96 | productType = "com.apple.product-type.kernel-extension"; 97 | }; 98 | /* End PBXNativeTarget section */ 99 | 100 | /* Begin PBXProject section */ 101 | B1EF6CA71B61C3E9008C5BD4 /* Project object */ = { 102 | isa = PBXProject; 103 | attributes = { 104 | LastUpgradeCheck = 0720; 105 | ORGANIZATIONNAME = "SektionEins GmbH"; 106 | TargetAttributes = { 107 | B1EF6CAF1B61C3E9008C5BD4 = { 108 | CreatedOnToolsVersion = 6.4; 109 | }; 110 | }; 111 | }; 112 | buildConfigurationList = B1EF6CAA1B61C3E9008C5BD4 /* Build configuration list for PBXProject "SUIDGuardNG" */; 113 | compatibilityVersion = "Xcode 3.2"; 114 | developmentRegion = English; 115 | hasScannedForEncodings = 0; 116 | knownRegions = ( 117 | en, 118 | ); 119 | mainGroup = B1EF6CA61B61C3E9008C5BD4; 120 | productRefGroup = B1EF6CB11B61C3E9008C5BD4 /* Products */; 121 | projectDirPath = ""; 122 | projectRoot = ""; 123 | targets = ( 124 | B1EF6CAF1B61C3E9008C5BD4 /* SUIDGuardNG */, 125 | ); 126 | }; 127 | /* End PBXProject section */ 128 | 129 | /* Begin PBXResourcesBuildPhase section */ 130 | B1EF6CAE1B61C3E9008C5BD4 /* Resources */ = { 131 | isa = PBXResourcesBuildPhase; 132 | buildActionMask = 2147483647; 133 | files = ( 134 | ); 135 | runOnlyForDeploymentPostprocessing = 0; 136 | }; 137 | /* End PBXResourcesBuildPhase section */ 138 | 139 | /* Begin PBXSourcesBuildPhase section */ 140 | B1EF6CAB1B61C3E9008C5BD4 /* Sources */ = { 141 | isa = PBXSourcesBuildPhase; 142 | buildActionMask = 2147483647; 143 | files = ( 144 | B1EF6CB81B61C3E9008C5BD4 /* SUIDGuardNG.cpp in Sources */, 145 | ); 146 | runOnlyForDeploymentPostprocessing = 0; 147 | }; 148 | /* End PBXSourcesBuildPhase section */ 149 | 150 | /* Begin XCBuildConfiguration section */ 151 | B1EF6CB91B61C3E9008C5BD4 /* Debug */ = { 152 | isa = XCBuildConfiguration; 153 | buildSettings = { 154 | ALWAYS_SEARCH_USER_PATHS = NO; 155 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 156 | CLANG_CXX_LIBRARY = "libc++"; 157 | CLANG_ENABLE_MODULES = YES; 158 | CLANG_ENABLE_OBJC_ARC = YES; 159 | CLANG_WARN_BOOL_CONVERSION = YES; 160 | CLANG_WARN_CONSTANT_CONVERSION = YES; 161 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 162 | CLANG_WARN_EMPTY_BODY = YES; 163 | CLANG_WARN_ENUM_CONVERSION = YES; 164 | CLANG_WARN_INT_CONVERSION = YES; 165 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 166 | CLANG_WARN_UNREACHABLE_CODE = YES; 167 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 168 | COPY_PHASE_STRIP = NO; 169 | DEBUG_INFORMATION_FORMAT = dwarf; 170 | ENABLE_STRICT_OBJC_MSGSEND = YES; 171 | ENABLE_TESTABILITY = YES; 172 | GCC_C_LANGUAGE_STANDARD = gnu99; 173 | GCC_DYNAMIC_NO_PIC = NO; 174 | GCC_NO_COMMON_BLOCKS = YES; 175 | GCC_OPTIMIZATION_LEVEL = 0; 176 | GCC_PREPROCESSOR_DEFINITIONS = ( 177 | "DEBUG=1", 178 | "$(inherited)", 179 | ); 180 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 181 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 182 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 183 | GCC_WARN_UNDECLARED_SELECTOR = YES; 184 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 185 | GCC_WARN_UNUSED_FUNCTION = YES; 186 | GCC_WARN_UNUSED_VARIABLE = YES; 187 | MACOSX_DEPLOYMENT_TARGET = 10.11; 188 | MTL_ENABLE_DEBUG_INFO = YES; 189 | ONLY_ACTIVE_ARCH = YES; 190 | SDKROOT = macosx; 191 | }; 192 | name = Debug; 193 | }; 194 | B1EF6CBA1B61C3E9008C5BD4 /* Release */ = { 195 | isa = XCBuildConfiguration; 196 | buildSettings = { 197 | ALWAYS_SEARCH_USER_PATHS = NO; 198 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 199 | CLANG_CXX_LIBRARY = "libc++"; 200 | CLANG_ENABLE_MODULES = YES; 201 | CLANG_ENABLE_OBJC_ARC = YES; 202 | CLANG_WARN_BOOL_CONVERSION = YES; 203 | CLANG_WARN_CONSTANT_CONVERSION = YES; 204 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 205 | CLANG_WARN_EMPTY_BODY = YES; 206 | CLANG_WARN_ENUM_CONVERSION = YES; 207 | CLANG_WARN_INT_CONVERSION = YES; 208 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 209 | CLANG_WARN_UNREACHABLE_CODE = YES; 210 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 211 | COPY_PHASE_STRIP = NO; 212 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 213 | ENABLE_NS_ASSERTIONS = NO; 214 | ENABLE_STRICT_OBJC_MSGSEND = YES; 215 | GCC_C_LANGUAGE_STANDARD = gnu99; 216 | GCC_NO_COMMON_BLOCKS = YES; 217 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 218 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 219 | GCC_WARN_UNDECLARED_SELECTOR = YES; 220 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 221 | GCC_WARN_UNUSED_FUNCTION = YES; 222 | GCC_WARN_UNUSED_VARIABLE = YES; 223 | MACOSX_DEPLOYMENT_TARGET = 10.11; 224 | MTL_ENABLE_DEBUG_INFO = NO; 225 | SDKROOT = macosx; 226 | }; 227 | name = Release; 228 | }; 229 | B1EF6CBC1B61C3E9008C5BD4 /* Debug */ = { 230 | isa = XCBuildConfiguration; 231 | buildSettings = { 232 | CODE_SIGN_IDENTITY = "Developer ID Application"; 233 | COMBINE_HIDPI_IMAGES = YES; 234 | CURRENT_PROJECT_VERSION = 1.0.6d; 235 | INFOPLIST_FILE = SUIDGuardNG/Info.plist; 236 | MODULE_NAME = com.sektioneins.kext.SUIDGuardNG; 237 | MODULE_START = SUIDGuard_start; 238 | MODULE_STOP = SUIDGuard_stop; 239 | MODULE_VERSION = 1.0.6; 240 | PRODUCT_BUNDLE_IDENTIFIER = "com.sektioneins.driver.$(PRODUCT_NAME:rfc1034identifier)"; 241 | PRODUCT_NAME = "$(TARGET_NAME)"; 242 | WRAPPER_EXTENSION = kext; 243 | }; 244 | name = Debug; 245 | }; 246 | B1EF6CBD1B61C3E9008C5BD4 /* Release */ = { 247 | isa = XCBuildConfiguration; 248 | buildSettings = { 249 | CODE_SIGN_IDENTITY = "Developer ID Application"; 250 | COMBINE_HIDPI_IMAGES = YES; 251 | CURRENT_PROJECT_VERSION = 1.0.6; 252 | INFOPLIST_FILE = SUIDGuardNG/Info.plist; 253 | MODULE_NAME = com.sektioneins.kext.SUIDGuardNG; 254 | MODULE_START = SUIDGuard_start; 255 | MODULE_STOP = SUIDGuard_stop; 256 | MODULE_VERSION = 1.0.6; 257 | PRODUCT_BUNDLE_IDENTIFIER = "com.sektioneins.driver.$(PRODUCT_NAME:rfc1034identifier)"; 258 | PRODUCT_NAME = "$(TARGET_NAME)"; 259 | WRAPPER_EXTENSION = kext; 260 | }; 261 | name = Release; 262 | }; 263 | /* End XCBuildConfiguration section */ 264 | 265 | /* Begin XCConfigurationList section */ 266 | B1EF6CAA1B61C3E9008C5BD4 /* Build configuration list for PBXProject "SUIDGuardNG" */ = { 267 | isa = XCConfigurationList; 268 | buildConfigurations = ( 269 | B1EF6CB91B61C3E9008C5BD4 /* Debug */, 270 | B1EF6CBA1B61C3E9008C5BD4 /* Release */, 271 | ); 272 | defaultConfigurationIsVisible = 0; 273 | defaultConfigurationName = Release; 274 | }; 275 | B1EF6CBB1B61C3E9008C5BD4 /* Build configuration list for PBXNativeTarget "SUIDGuardNG" */ = { 276 | isa = XCConfigurationList; 277 | buildConfigurations = ( 278 | B1EF6CBC1B61C3E9008C5BD4 /* Debug */, 279 | B1EF6CBD1B61C3E9008C5BD4 /* Release */, 280 | ); 281 | defaultConfigurationIsVisible = 0; 282 | defaultConfigurationName = Release; 283 | }; 284 | /* End XCConfigurationList section */ 285 | }; 286 | rootObject = B1EF6CA71B61C3E9008C5BD4 /* Project object */; 287 | } 288 | -------------------------------------------------------------------------------- /SUIDGuardNG/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleName 12 | $(PRODUCT_NAME) 13 | CFBundlePackageType 14 | KEXT 15 | CFBundleShortVersionString 16 | 1.0.6 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1.0.6 21 | IOKitPersonalities 22 | 23 | SUIDGUARD 24 | 25 | CFBundleIdentifier 26 | com.sektioneins.driver.$(PRODUCT_NAME:rfc1034identifier) 27 | IOClass 28 | com_sektioneins_driver_SUIDGuard 29 | IOKitDebug 30 | 0 31 | IOMatchCategory 32 | com_sektioneins_driver_SUIDGuard 33 | IOProviderClass 34 | IOResources 35 | IOResourceMatch 36 | IOBSD 37 | 38 | 39 | NSHumanReadableCopyright 40 | Copyright © 2016 SektionEins GmbH. All rights reserved. 41 | OSBundleLibraries 42 | 43 | com.apple.kpi.bsd 44 | 14.0 45 | com.apple.kpi.dsep 46 | 14.0 47 | com.apple.kpi.iokit 48 | 14.0 49 | com.apple.kpi.libkern 50 | 14.0 51 | com.apple.kpi.mach 52 | 14.0 53 | com.apple.kpi.unsupported 54 | 14.0 55 | 56 | OSBundleRequired 57 | Root 58 | 59 | 60 | -------------------------------------------------------------------------------- /SUIDGuardNG/SUIDGuardNG.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // SUIDGuarNGd.cpp 3 | // SUIDGuard 4 | // 5 | // Created by Stefan Esser on 15/07/15. 6 | // Copyright (c) 2015 SektionEins GmbH. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | 12 | extern "C" { 13 | 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #define CONFIG_MACF 1 25 | #include 26 | #include 27 | #include 28 | 29 | /* we have to copy these structs because we need access to fg_cred */ 30 | 31 | /* file types */ 32 | typedef enum { 33 | DTYPE_VNODE = 1, /* file */ 34 | DTYPE_SOCKET, /* communications endpoint */ 35 | DTYPE_PSXSHM, /* POSIX Shared memory */ 36 | DTYPE_PSXSEM, /* POSIX Semaphores */ 37 | DTYPE_KQUEUE, /* kqueue */ 38 | DTYPE_PIPE, /* pipe */ 39 | DTYPE_FSEVENTS, /* fsevents */ 40 | DTYPE_ATALK /* (obsolete) */ 41 | } file_type_t; 42 | 43 | struct fileglob { 44 | LIST_ENTRY(fileglob) f_msglist;/* list of active files */ 45 | int32_t fg_flag; /* see fcntl.h */ 46 | int32_t fg_count; /* reference count */ 47 | int32_t fg_msgcount; /* references from message queue */ 48 | int32_t fg_lflags; /* file global flags */ 49 | kauth_cred_t fg_cred; /* credentials associated with descriptor */ 50 | const struct fileops { 51 | file_type_t fo_type; /* descriptor type */ 52 | int (*fo_read) (struct fileproc *fp, struct uio *uio, 53 | int flags, vfs_context_t ctx); 54 | int (*fo_write) (struct fileproc *fp, struct uio *uio, 55 | int flags, vfs_context_t ctx); 56 | #define FOF_OFFSET 0x00000001 /* offset supplied to vn_write */ 57 | #define FOF_PCRED 0x00000002 /* cred from proc, not current thread */ 58 | int (*fo_ioctl) (struct fileproc *fp, u_long com, 59 | caddr_t data, vfs_context_t ctx); 60 | int (*fo_select) (struct fileproc *fp, int which, 61 | void *wql, vfs_context_t ctx); 62 | int (*fo_close) (struct fileglob *fg, vfs_context_t ctx); 63 | int (*fo_kqfilter) (struct fileproc *fp, struct knote *kn, 64 | vfs_context_t ctx); 65 | int (*fo_drain) (struct fileproc *fp, vfs_context_t ctx); 66 | } *fg_ops; 67 | off_t fg_offset; 68 | void *fg_data; /* vnode or socket or SHM or semaphore */ 69 | void *fg_vn_data; /* Per fd vnode data, used for directories */ 70 | }; 71 | 72 | 73 | struct __vm_map 74 | { 75 | void * lck1, * lck2; 76 | void * links_prev, * links_next; 77 | uint64_t min_offset; 78 | }; 79 | 80 | /* For an unknown reason Apple considers proc_task to be a private API. 81 | Annoying when you want to create a security product. */ 82 | void * proc_task(void * proc) { 83 | uint64_t * p = (uint64_t *)proc; 84 | return (void *)p[3]; 85 | } 86 | 87 | /* Not a private API but missing from the headers ... */ 88 | void * get_task_map(void* t); 89 | 90 | /* purpose of this hook is to detect execution of SUID/SGID root binaries and 91 | when found it will scan the environment variables for this process in 92 | kernel memory and overwrite all DYLD_ variables to protect against weaknesses 93 | in the dyld code */ 94 | int suidguard_cred_label_update_execve(kauth_cred_t old_cred, kauth_cred_t new_cred, struct proc *p, struct vnode *vp, off_t offset, struct vnode *scriptvp, struct label *vnodelabel, struct label *scriptvnodelabel, struct label *execlabel, u_int *csflags, void *macpolicyattr, size_t macpolicyattrlen, int *disjointp) 95 | { 96 | struct image_params *imgp; 97 | 98 | /* get the current map and check its min_offet */ 99 | struct __vm_map * map = (struct __vm_map *) get_task_map(proc_task(p)); 100 | if (map->min_offset < 0x1000) { 101 | printf("SUIDGuard: disallowed execution of binary without a __PAGEZERO segment\n"); 102 | return 1; /* disallow execution */ 103 | } 104 | 105 | /* we can determine address of image_params structure from the csflags pointer */ 106 | /* some might consider this a dirty hack, but Apple makes it necessary */ 107 | imgp = (struct image_params *)((unsigned char *)csflags-offsetof(struct image_params, ip_csflags)); 108 | 109 | struct vnode_attr va; 110 | int error = 0; 111 | vfs_context_t ctx = NULL; 112 | 113 | /* ignore all non regular files */ 114 | if (!vnode_isreg(vp)) { 115 | goto exit; 116 | } 117 | 118 | /* create a new context */ 119 | if ((ctx = vfs_context_create(NULL)) == NULL) { 120 | error = ENOMEM; 121 | goto exit; 122 | } 123 | 124 | /* we only need a subset of the info */ 125 | VATTR_INIT(&va); 126 | VATTR_WANTED(&va, va_uid); 127 | VATTR_WANTED(&va, va_gid); 128 | VATTR_WANTED(&va, va_mode); 129 | if ((error = vnode_getattr(vp, &va, ctx))) { 130 | goto exit; 131 | } 132 | 133 | /* now check if this is a SUID/SGID root binary */ 134 | if ((va.va_mode & (VSUID|VSGID)) && ((va.va_uid == 0) || (va.va_gid == 0))) { 135 | 136 | int i; 137 | int found = 0; 138 | 139 | /* scan all the environment variables and disallow */ 140 | /* all DYLD_ variables to protect from flaws in dyld */ 141 | 142 | char *tmp = imgp->ip_endargv; 143 | for (i=0; iip_envc; i++) { 144 | if (strncmp(tmp, "DYLD_", 5) == 0) { 145 | tmp[0] = 'X'; 146 | found++; 147 | if (found <= 5) { 148 | printf("SUIDGuard: found and neutralized DYLD_ environment variable D%s for SUID/SGID root binary\n", tmp+1); 149 | } 150 | } 151 | tmp += strlen(tmp)+1; 152 | } 153 | if (found > 5) { 154 | printf("SUIDGuard: found and neutralized more than 5 DYLD_ environment variables for SUID/SGID root binary - skipping others to not flood log file\n"); 155 | } 156 | } 157 | 158 | exit: 159 | if (ctx) { 160 | vfs_context_rele(ctx); 161 | } 162 | 163 | /* maybe better to disallow execution whenever an error happened before */ 164 | if (error != 0) { 165 | return 1; 166 | } 167 | 168 | return 0; 169 | } 170 | 171 | 172 | /* we hook this policy hook and return 1 to activate the transition code */ 173 | int suidguard_cred_check_label_update_execve(kauth_cred_t old, struct vnode *vp, off_t offset, struct vnode *scriptvp, struct label *vnodelabel, struct label *scriptvnodelabel, struct label *execlabel, struct proc *p, void *macpolicyattr, size_t macpolicyattrlen) 174 | { 175 | /* AppleMobileFileIntegrity does this already but better not rely on it */ 176 | return 1; 177 | } 178 | 179 | /* we hook into fcntl() because it is generally a bad idea to allow deactivation 180 | of O_APPEND for files opened with the credentials of another user */ 181 | int suidguard_file_check_fcntl(kauth_cred_t cred, struct fileglob *fg, struct label *label, int cmd, user_long_t arg) 182 | { 183 | /* we only react if someone tries to use F_SETFL */ 184 | if (cmd != F_SETFL) { 185 | return 0; 186 | } 187 | 188 | /* ignore if this file is not opened with append */ 189 | if ((fg->fg_flag & FAPPEND) == 0) { 190 | return 0; 191 | } 192 | 193 | /* ignore if we are the super-user */ 194 | if (kauth_cred_issuser(cred)) { 195 | return 0; 196 | } 197 | 198 | /* ignore if we own the file */ 199 | if (kauth_cred_getuid(cred) == kauth_cred_getuid(fg->fg_cred)) { 200 | return 0; 201 | } 202 | 203 | /* ignore if caller is not trying to clear FAPPEND */ 204 | if (arg & FAPPEND) { 205 | return 0; 206 | } 207 | 208 | /* for now log this attempt and deny */ 209 | printf("SUIDGuard: blocked attempt from uid %u to clear O_APPEND flag on file owned by %u\n", kauth_cred_getuid(cred), kauth_cred_getuid(fg->fg_cred)); 210 | return EPERM; 211 | } 212 | 213 | static mac_policy_handle_t suidguard_handle = 0; 214 | 215 | static struct mac_policy_ops suidguard_ops = 216 | { 217 | .mpo_cred_check_label_update_execve = suidguard_cred_check_label_update_execve, 218 | .mpo_cred_label_update_execve = suidguard_cred_label_update_execve, 219 | .mpo_file_check_fcntl = suidguard_file_check_fcntl 220 | }; 221 | 222 | static struct mac_policy_conf suidguard_policy_conf = { 223 | .mpc_name = "suidguard", 224 | .mpc_fullname = "SUID Guard Kernel Extension", 225 | .mpc_labelnames = NULL, 226 | .mpc_labelname_count = 0, 227 | .mpc_ops = &suidguard_ops, 228 | .mpc_loadtime_flags = MPC_LOADTIME_FLAG_UNLOADOK, 229 | .mpc_field_off = NULL, 230 | .mpc_runtime_flags = 0 231 | }; 232 | 233 | kern_return_t SUIDGuard_start(kmod_info_t * ki, void *d); 234 | kern_return_t SUIDGuard_stop(kmod_info_t *ki, void *d); 235 | 236 | kern_return_t SUIDGuard_start(kmod_info_t * ki, void *d) 237 | { 238 | int r = mac_policy_register(&suidguard_policy_conf, &suidguard_handle, d); 239 | return r; 240 | } 241 | 242 | kern_return_t SUIDGuard_stop(kmod_info_t *ki, void *d) 243 | { 244 | int r = mac_policy_unregister(suidguard_handle); 245 | return r; 246 | } 247 | } 248 | 249 | class com_sektioneins_driver_SUIDGuard : public IOService 250 | { 251 | OSDeclareDefaultStructors(com_sektioneins_driver_SUIDGuard) 252 | public: 253 | virtual bool init(OSDictionary *dictionary = 0); 254 | virtual void free(void); 255 | virtual IOService *probe(IOService *provider, SInt32 *score); 256 | virtual bool start(IOService *provider); 257 | virtual void stop(IOService *provider); 258 | }; 259 | 260 | // This required macro defines the class's constructors, destructors, 261 | // and several other methods I/O Kit requires. 262 | OSDefineMetaClassAndStructors(com_sektioneins_driver_SUIDGuard, IOService) 263 | 264 | // Define the driver's superclass. 265 | #define super IOService 266 | 267 | bool com_sektioneins_driver_SUIDGuard::init(OSDictionary *dict) 268 | { 269 | bool result = super::init(dict); 270 | //SUIDGuard_start(NULL, NULL); 271 | return result; 272 | } 273 | 274 | void com_sektioneins_driver_SUIDGuard::free(void) 275 | { 276 | //SUIDGuard_stop(NULL, NULL); 277 | super::free(); 278 | } 279 | 280 | IOService *com_sektioneins_driver_SUIDGuard::probe(IOService *provider, 281 | SInt32 *score) 282 | { 283 | IOService *result = super::probe(provider, score); 284 | return result; 285 | } 286 | 287 | bool com_sektioneins_driver_SUIDGuard::start(IOService *provider) 288 | { 289 | bool result = super::start(provider); 290 | return result; 291 | } 292 | 293 | void com_sektioneins_driver_SUIDGuard::stop(IOService *provider) 294 | { 295 | super::stop(provider); 296 | } 297 | -------------------------------------------------------------------------------- /SUIDGuardNG/SUIDGuardNG.h: -------------------------------------------------------------------------------- 1 | /* add your code here */ 2 | --------------------------------------------------------------------------------