{"action":"closed","issue":{"url":"https://api.github.com/repos/chrsmith/google-security-research/issues/40","labels_url":"https://api.github.com/repos/chrsmith/google-security-research/issues/40/labels{/name}","comments_url":"https://api.github.com/repos/chrsmith/google-security-research/issues/40/comments","events_url":"https://api.github.com/repos/chrsmith/google-security-research/issues/40/events","html_url":"https://github.com/chrsmith/google-security-research/issues/40","id":58936355,"number":40,"title":"OS X IOKit kernel code execution due to heap overflow in IOHIKeyboardMapper::parseKeyMapping","user":{"login":"GoogleCodeExporter","id":9614759,"avatar_url":"https://avatars.githubusercontent.com/u/9614759?v=3","gravatar_id":"","url":"https://api.github.com/users/GoogleCodeExporter","html_url":"https://github.com/GoogleCodeExporter","followers_url":"https://api.github.com/users/GoogleCodeExporter/followers","following_url":"https://api.github.com/users/GoogleCodeExporter/following{/other_user}","gists_url":"https://api.github.com/users/GoogleCodeExporter/gists{/gist_id}","starred_url":"https://api.github.com/users/GoogleCodeExporter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/GoogleCodeExporter/subscriptions","organizations_url":"https://api.github.com/users/GoogleCodeExporter/orgs","repos_url":"https://api.github.com/users/GoogleCodeExporter/repos","events_url":"https://api.github.com/users/GoogleCodeExporter/events{/privacy}","received_events_url":"https://api.github.com/users/GoogleCodeExporter/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/chrsmith/google-security-research/labels/auto-migrated","name":"auto-migrated","color":"ededed"},{"url":"https://api.github.com/repos/chrsmith/google-security-research/labels/CVE-2014-4404","name":"CVE-2014-4404","color":"ededed"},{"url":"https://api.github.com/repos/chrsmith/google-security-research/labels/Deadline-90","name":"Deadline-90","color":"ededed"},{"url":"https://api.github.com/repos/chrsmith/google-security-research/labels/Deadline-Exceeded","name":"Deadline-Exceeded","color":"ededed"},{"url":"https://api.github.com/repos/chrsmith/google-security-research/labels/Finder-ianbeer","name":"Finder-ianbeer","color":"ededed"},{"url":"https://api.github.com/repos/chrsmith/google-security-research/labels/Fixed-2014-Oct-17","name":"Fixed-2014-Oct-17","color":"ededed"},{"url":"https://api.github.com/repos/chrsmith/google-security-research/labels/Id-607551907","name":"Id-607551907","color":"ededed"},{"url":"https://api.github.com/repos/chrsmith/google-security-research/labels/Product-iOS-Kernel","name":"Product-iOS-Kernel","color":"ededed"},{"url":"https://api.github.com/repos/chrsmith/google-security-research/labels/Product-OSX-Kernel","name":"Product-OSX-Kernel","color":"ededed"},{"url":"https://api.github.com/repos/chrsmith/google-security-research/labels/Reported-2014-Jun-30","name":"Reported-2014-Jun-30","color":"ededed"},{"url":"https://api.github.com/repos/chrsmith/google-security-research/labels/Severity-High","name":"Severity-High","color":"ededed"},{"url":"https://api.github.com/repos/chrsmith/google-security-research/labels/Vendor-Apple","name":"Vendor-Apple","color":"ededed"}],"state":"closed","locked":false,"assignee":null,"milestone":null,"comments":0,"created_at":"2015-02-25T17:13:41Z","updated_at":"2015-02-25T17:13:41Z","closed_at":"2015-02-25T17:13:41Z","body":"```\nThe function IOHIKeyboardMapper::parseKeyMapping(const UInt8 * map, UInt32 \nmappingLen, NXParsedKeyMapping * parsedMapping)\ncan be reached by a regular user by setting the HIDKeyMapping property of \nIOHIDKeyboard in the IOKit registry.\n\nThis function is responsible for parsing a binary keymapping file.\n\nThe function NextNum reads either 1 or two bytes from map depending on whether \nthe first two bytes in the file are non-zero.\n\n  IOHIKeyboardMapper::parseKeyMapping(const UInt8 * map, UInt32 mappingLen, NXParsedKeyMapping * parsedMapping)\n    ...\n    parsedMapping->numSeqs = NextNum(&nmd);         <-- (a)\n    if (parsedMapping->numSeqs <= maxSeqNum)        <-- (b)\n      return false;\n      \n    for(i = 0; i < parsedMapping->numSeqs; i++)\n    {\n      parsedMapping->seqDefs[i] = (unsigned char *)nmd.bp; <-- (c)\n      for(j=0, l=NextNum(&nmd); j<l; j++)\n      {\n              NextNum(&nmd);\n              NextNum(&nmd);\n      }\n    }\n\nAt (a) this function reads the number of sequence definitions follow, at (b) \nthis number is checked against a lower bound however\nthere is no upper-bound check. By specifying a large value for numSeqs (can be \nup to 0xffff) we can overflow the fixed-size seqDefs buffer at (c).\n\nseqDefs is defined here:\n  \n  typedef struct _NXParsedKeyMapping_ {\n    short shorts;\n    char  keyBits[NX_NUMKEYCODES];\n    int     maxMod;\n    unsigned char *modDefs[NX_NUMMODIFIERS];\n    int     numDefs;\n    unsigned char *keyDefs[NX_NUMKEYCODES];\n    int     numSeqs;\n    unsigned char *seqDefs[NX_NUMSEQUENCES];         <-- seqDefs ( #define NX_NUMSEQUENCES  128 )\n    int     numSpecialKeys;\n    unsigned short specialKeys[NX_NUMSPECIALKEYS];\n    const unsigned char *mapping;\n    int mappingLen;\n  } NXParsedKeyMapping;\n\nIt's actually very easy to get code execution with this buffer overflow:\n\nThis NXParsedKeyMapping is a member var of IOHIKeyboardMapper:\n\nclass IOHIKeyboardMapper : public OSObject\n{\nprivate:\n  IOHIKeyboard *    _delegate;\n  bool        _mappingShouldBeFreed;\n  NXParsedKeyMapping  _parsedMapping;                       <-- _parsedMapping.seqDefs will be overflowed\n  IOHIDSystem  *    _hidSystem;\n...\n  ExpansionData * _reserved;\n...\n  UInt32              _stickyKeys_State; \n  int           _stickyKeys_NumModifiersDown;\n  UInt8         _stickyKeys_Modifiers[kMAX_MODIFIERS];\n  StickyKeys_ToggleInfo * _stickyKeys_ShiftToggle;\n  StickyKeys_ToggleInfo * _stickyKeys_OptionToggle;\n  bool        _stateDirty;\n  OSDictionary *    _onParamDict;                           <-- pointer to an object with virtual method\n\nWe can easily cause a virtual method to be called on _onParamDict by ensuring \nthat parseKeyMapping returns false indicating\nan error parsing the keyfile. This will cause a call to: \nIOHIKeyboardMapper::free which will call stickyKeysFree:\n\n   void IOHIKeyboardMapper::stickyKeysfree (void)\n  {\n    ...\n    if (_onParamDict)            <-- we overwrite this pointer\n      _onParamDict->release();   <-- virtual method ( offset 0x28 in the vtable)\n\nSome offsets:\n\n_onParamDict is at offset 0xe30 in the IOHIDKeyboardMapper\n\nseqDefs is at offset 0x998 in NXParsedKeyMapping, and NXParsedKeyMapping is at \noffset 0x20 in IOHIKeyboardMapper:\n\n+0x000: IOHIKeyboardMapper\n...\n+0x020:   NXParsedKeyMapping _parsedMapping\n...\n+0x9b8:     unsigned char* seqDefs[]  -+\n...                                    | 0x478 bytes -> 0x8f pointers\n+0xe30:   OSDictionary* _onParamDict  -+\n\n\nSo the 0x90th seqDef entry will overlap with the _onParamDict pointer.\n\nLooking again at the parseKeyMapping function snippet where the overflow takes \nplace:\n\n  for(i = 0; i < parsedMapping->numSeqs; i++)\n  {\n    parsedMapping->seqDefs[i] = (unsigned char *)nmd.bp;\n    for(j=0, l=NextNum(&nmd); j<l; j++)\n    {\n            NextNum(&nmd);\n            NextNum(&nmd);\n    }\n\n(nmb.bp points to the next number to parse.)\n\nThis loop is parsing a length followed by twice that number of Nums.\n\nWe can supply 0x8f entries of: 0x00 0x00 (len=0)\nfollowed by one entry of: 0x00 0x02 (len = 0x2)\n                          0x00 0x78 0x56 0x00\n                          0x00 0x00 0x00 0x00\n\nwhich will overwrite the _onParamDict pointer with a pointer to: 0x5678000000 \nwhich will be intepreted as a pointer to\na vtable :-) We can then map that page in userspace and write a fake vtable at \n0x5678000200.\n\nWe can then force the parseKeyMapping function to return false here by \nsupplying a value larger than NX_NUMSPECIALKEYS:\n  numMods = NextNum(&nmd);\n  parsedMapping->numSpecialKeys = numMods;\n  if ( numMods > NX_NUMSPECIALKEYS )\n    return false;\n\nPoC attached.\n```\n\n-----\nOriginal issue reported on code.google.com by `ianb...@google.com` on 30 Jun 2014 at 2:47\n\nAttachments:\n* [key_mapping_exploit.c](https://storage.googleapis.com/google-code-attachments/google-security-research/issue-40/comment-0/key_mapping_exploit.c)\n"}}