{
	"id": "30c773d7-ec31-4868-a39a-f62b14999ac4",
	"created_at": "2026-04-06T00:15:58.449125Z",
	"updated_at": "2026-04-10T03:20:41.87962Z",
	"deleted_at": null,
	"sha1_hash": "d4554d4d6ca616a400ddee86c4859bd936f8aff5",
	"title": "When Kirbi walks the Bifrost",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 1116645,
	"plain_text": "When Kirbi walks the Bifrost\r\nBy Cody Thomas\r\nPublished: 2019-11-14 · Archived: 2026-04-05 15:54:06 UTC\r\nRed Teaming macOS\r\nApple moves forward in large jumps each time they release a new version of their Macintosh operating system.\r\nEach change can deprecate a whole suite of offensive tooling or add multiple extra hurdles. Because of this, red\r\nteamers and penetration testers historically gravitated towards things like Python and shell commands due to their\r\nstability. However, with the new direction Apple is headed, there’s no guarantee for external languages like\r\nPython, Ruby, or Perl to exist on a macOS endpoint by default.\r\nThis pushes development of new capabilities lower into the operating system or into Apple-specific programming\r\nlanguages. After all, if Python isn’t on the table, then neither are common tools like Empire, Impacket,\r\nor Responder. Even some of Apple’s own versions of scripting languages like Javascript for Automation (JXA) are\r\npoorly maintained and not feature complete.\r\nAs a red teamer, assuming you manage to get execution on a macOS endpoint, two of the biggest pain points for\r\nassessments deal with credential access and lateral movement. One thing people often forget though is that\r\ncredentials come in many varieties. There are more credentials on a macOS endpoint than just the user’s password\r\nhash or plaintext password. Specifically, there are Kerberos tickets. This is more useful on an Active Directory\r\n(AD) joined computer, but how common is that really?\r\nhttps://posts.specterops.io/when-kirbi-walks-the-bifrost-4c727807744f\r\nPage 1 of 11\n\nTwitter poll for frequency of seeing AD joined macOS endpoints in offensive engagements\r\nFrom the quick poll results, at least 30% of people encounter an AD joined Mac at least 25% of the time. To help\r\nwork with Kerberos tickets on macOS endpoints, I’m releasing a new, open source tool called Bifrost. Bifrost is\r\nan Objective C library that uses lower level Kerberos APIs and manual Kerberos network traffic to allow\r\ncollection, manipulation, exfiltration, and discovery of Kerberos related information on macOS.\r\nThe rest of this post focuses on macOS specific background knowledge, abuses regarding Kerberos, and defensive\r\nconsiderations.\r\nActive Directory is more than just Windows\r\nWhen talking about Active Directory (AD), most people initially think about Microsoft, Windows, and sometimes\r\nKerberos. Because of this, there are large amounts of research and tooling dedicated to the abuse and defense of\r\nthese attack surfaces. Tools like Bloodhound seek to illustrate AD attack paths with graph theory, and tools\r\nhttps://posts.specterops.io/when-kirbi-walks-the-bifrost-4c727807744f\r\nPage 2 of 11\n\nlike Rubeus and Kekeo aim to highlight Kerberos specific abuses on Windows. This post will cover some of the\r\nsame abuses such as over-pass-the-hash, kerberoasting, and pass-the-ticket, but from a macOS standpoint.\r\nHowever, as enterprises grow, more systems besides Windows need to be managed, secured, and maintained.\r\nThere are two main approaches to this problem: either join these new systems to the current AD environment, or\r\nfind some other means of centrally managing them. While not necessarily an every-day scenario, macOS\r\nendpoints joined to a Windows Active Directory environment is becoming more common. Most endpoint security\r\nvendors on macOS are still looking for python instances, malicious command line binaries, or direct keychain\r\naccess, which makes API access to Kerberos tickets very appealing from an offensive standpoint.\r\nKerberos Kerberos Kerberos\r\nSince AD boils down to a management tool for organizing users, groups, policies, and containers, there needs to\r\nbe auxiliary components that handle authentication and authorization. When it comes to network level\r\nauthentication, Kerberos is the de facto standard; however, Kerberos is not a Microsoft construct.\r\nKerberos was created by MIT as a solution to these network security problems. The Kerberos\r\nprotocol uses strong cryptography so that a client can prove its identity to a server (and vice versa)\r\nacross an insecure network connection.\r\nBecause Kerberos is open source and freely provided by MIT, there are a few main variants in use today.\r\nThe truest form is the actual MIT Kerberos implementation that’s used by various *nix applications.\r\nThe most common implementation is the Microsoft adaptation that’s used in conjunction with Active\r\nDirectory in Windows — this version is not open source.\r\nHeimdal Kerberos is another free and open source implementation of the Kerberos version 5 standard. This\r\nis the implementation included in macOS endpoints.\r\nGenerally, all of the frameworks built on the Kerberos version 5 specification have some degree of\r\ninteroperability, but implementations and attack surfaces vary wildly. The rest of this blog will focus on the\r\nHeimdal implementation on macOS.\r\nFor those not familiar with Kerberos, the following image is a nice high level overview:\r\nhttps://posts.specterops.io/when-kirbi-walks-the-bifrost-4c727807744f\r\nPage 3 of 11\n\nKerberos in a nutshell\r\nThe process takes a user’s password, hashes it, and uses that to get what’s called a Ticket Granting Ticket (TGT)\r\n— steps 1 and 2. At this point, the user’s plaintext password is not needed anymore. This TGT is the user’s\r\nidentity. When trying to access a network resource (file share, ssh, etc), the user presents this TGT to get a Service\r\nTicket — steps 3 and 4. This is a ticket specific to the user and the service they want to access. The final step is to\r\nthen provide this service ticket to the actual service to see if the user is authorized to access it, and if so, provide\r\naccess (like remotely mounting a share) — steps 5 and 6.\r\nThe key here is that instead of needing a user’s plaintext password or needing a user’s hash to pass-the-hash, a\r\nuser’s TGT or service ticket are also valid ways to remotely authenticate as that user.\r\nActive Directory Configuration\r\nThe first thing to discover from an offensive standpoint is the configuration of the AD joined macOS endpoint.\r\nMost times, there is a world readable file in  /etc/krb5.conf  that contains the configuration for interacting with\r\nthe Active Directory environment. This is not a firm requirement though. A macOS computer can be joined to AD\r\nwithout this file, but it’s very common. This file contains information such as:\r\nSupported encryption types for Ticket Granting Tickets (TGTs) and Service Tickets such as AES256,\r\nAES128, RC4-HMAC, and 3DES\r\nAllowed ticket flags such as forwardable, proxiable, and renewable\r\nTicket lifetimes\r\nDefault realms and Key Distribution Center (KDC) locations and ports\r\nDefault ticket storage options\r\nA user can also view the NETBIOS short-name for the domain they’re connected to by going to System\r\nPreferences -\u003e Users and Groups -\u003e Login Options -\u003e Network Account Server.\r\nTicket Storage on macOS\r\nhttps://posts.specterops.io/when-kirbi-walks-the-bifrost-4c727807744f\r\nPage 4 of 11\n\nThe next step is to identify where the Kerberos tickets are being stored. There are many different ways to store\r\nKerberos credentials based on the OS and level of security desired, but by default, macOS uses Credential Cache\r\n(ccache) entries with a KCM — a process-based credential cache.\r\nHeimdal implements a credential cache type named “KCM” where operations are transmitted to a\r\ndaemon process which manages the actual cache contents. On OS X 10.7 and later, the native default\r\ncredential cache type uses the KCM protocol via Mach RPC. It is typically referred to via the “API”\r\ncache type for continuity with Kerberos for Macintosh; the API and KCM cache types have the same\r\nnamespace in the native OS X Kerberos.\r\nOne way to view a limited subset of the credential cache is to open Keychain Access, click Keychain Access at the\r\ntop and select Ticket Viewer. This is the only GUI version to view some of the ticket information for the current\r\nuser. However, this is extremely limited. If Ticket Viewer is used to get and store Kerberos credentials, then this\r\ninformation is stored in the Keychain and can be revealed by providing the login keychain password. The UUIDs\r\nshown in the image below correspond to the credential cache names associated with the tickets:\r\nKerberos stored credentials in the keychain\r\nUsers normally interact with ticket storage and the macOS credential caches by using the following built-in\r\nbinaries which do not save anything into the Ticket Viewer:\r\nkinit — gets a user’s initial Ticket Granting Ticket (TGT) by taking in a plaintext password\r\nklist — lists some metadata about the entries in a specific ccache or lists out all of the visible ccaches in\r\nmemory\r\nktutil — works with keytab entries (more on that later)\r\nkcc — kerberos credential cache manipulations\r\nFrom an offensive perspective, relying on built-in commands on the command line is an easy way to get caught.\r\nThe Kerberos framework included in macOS by default exposes a bunch of lower level APIs for interacting with\r\nkeytabs and ccaches. To get the similar functionality as  klist , simply run the  list  action in Bifrost:\r\nhttps://posts.specterops.io/when-kirbi-walks-the-bifrost-4c727807744f\r\nPage 5 of 11\n\nThis loops through all of the discoverable in memory credential caches and displays information about the cache\r\nand all of the tickets inside (note: this does not need elevated permissions). The above example shows\r\nthe  LAB\\lab_admin ‘s default cache (indicated with a *) has a type of  API , meaning that the credentials are\r\nactually  KCM  credentials located in the kcm daemon. This cache has two entries — the user’s TGT and an entry\r\ndescribing information to the KCM on how to access this data (krb5 vs gssapi). The first entry actually contains\r\nthe user’s TGT though, so that can be dumped with the  -action dump -source tickets  command:\r\nThere are a few things to notice about the above screenshot from dumping tickets. Bifrost will first describe the\r\nticket it’s about to dump. This includes information such as the principal, the encryption type used, when it\r\nexpires, the flags present on the ticket, and the encryption key associated with the ticket. The next thing to notice\r\nis the giant base64 encoded blob labeled  Kirbi . To facilitate operational usage of Bifrost, all tickets dumped and\r\nimported are in the Kirbi format. This is the same format used by Mimikatz, Kekeo, and Rubeus, among others.\r\nThis allows tickets dumped from Bifrost to be immediately imported into a windows machine and vise versa.\r\nPassing The Ticket\r\nImporting tickets on macOS is analogous to importing tickets on Windows. The key to consider is that each\r\ncredential cache is like a different logon session in windows. All of the credentials in a single credential cache are\r\nsupposed to have the same client principal name. This would prevent having a TGT for Alice and a TGT for Bob\r\nin the same credential cache, but they could each have their own credential cache without conflicting. Let’s\r\nassume you have a base64 encoded Kirbi ticket from another macOS machine or from a Windows machine and\r\nyou want to import it. The following screenshot shows importing a CIFS service ticket to the Domain Controller\r\ninto the default credential cache for the current user (note: this does not need elevated permissions).\r\nIf you’re unsure about the contents of a Kirbi ticket and want to see what it actually contains, simply  -action\r\ndescribe  the ticket:\r\nOver-Pass-The-Hash\r\nA pretty common scenario in Windows is getting a user’s hash, but needing to extend that into the Kerberos realm\r\nto get a full TGT instead. In similar fashion to Will (@harmj0y) ‘s Rubeus project and post, Bifrost can manually\r\nconstruct the ASN1 required Kerberos traffic to take user hashes and get TGTs back (note: this does not require\r\nelevation). If you collect an NTLM hash on a Windows machine, you should select the  rc4  encryption type.\r\nAdditionally, since the Kerberos traffic is being manually constructed, there are more options available. If you\r\nhave the AES256 hash of the user’s password, but want something more easily cracked (like RC4), then specify\r\nthat with the  -tgtEnctype rc4  flag.\r\nKerberoasting\r\nNow that you can get valid TGTs and import them, the next step is to use them to access\r\nservices. Will covers Kerberoasting very thoroughly in his blog, so it won’t be covered in-depth here, but this is\r\nsomething that can be achieved through Bifrost as well. The basic principal is that any valid TGT can be used to\r\nrequest a ticket to any service. The resulting ticket has a portion that’s encrypted with the hash of password of the\r\nhttps://posts.specterops.io/when-kirbi-walks-the-bifrost-4c727807744f\r\nPage 6 of 11\n\nassociated service. So, if the service ticket is forced to use a weaker encryption, then it’s easier to crack the service\r\naccount’s password.\r\nSince the only difference between getting a normal service ticket and kerberoasting is specifically requesting a\r\nweaker encryption type for the final ticket, the  asktgs  command in Bifrost simply takes an additional flag of  -\r\nkerberoast true  to request that the service ticket be RC4 encrypted instead of the standard AES256.\r\nMultiple services can be specified at once by simply comma separating the SPNs. On macOS, there are four\r\ndefault SPNs set on the computer account:\r\nafpserver/spooky.lab.local\r\ncifs/spooky.lab.local\r\nhost/spooky.lab.local\r\nvnc/spooky.lab.local\r\nComputer$ Account\r\nActing as the local computer account to the domain is a little different in macOS than in Windows. In Windows,\r\nyou can inject into a SYSTEM process and automatically act as the computer$ account remotely — this is why\r\nyou can still query AD even though SYSTEM isn’t a domain account. If you’re able to unlock the system\r\nkeychain (normally just need local admin credentials), then you can read the computer$ account plaintext\r\npassword:\r\n/Active Directory/LAB Computer$ account password access\r\nhttps://posts.specterops.io/when-kirbi-walks-the-bifrost-4c727807744f\r\nPage 7 of 11\n\nBifrost can use this password and the  -action askhash  command to generate the password hashes needed to get\r\nTGTs and service tickets for this computer. This password and other user passwords might have characters that\r\ntend to break or cause issues with command-line parsing, so if that’s the case, simply use the  -\r\nbpassword  parameter instead and pass in the base64 version of the password as shown below:\r\nConverting plaintext passwords into Kerberos-usable hashes\r\nLeveraging Kerberos Tickets on macOS\r\nAfter you’ve done some Kerberos ticket manipulation, how do you actually leverage it to access resources within\r\nthe environment?\r\nTo access the SMB shares of a remote computer with your kerberos tickets, simply use mount:\r\nmount -t smbfs \"//computer/share\" /local/path\r\nIt’s important to remember that if a share is mounted, the local root user can access and traverse it. Any other users\r\non the box can also run the  mount  command to view all the current mounts (similar to a  net use  on windows).\r\nAlice seeing that lab_admin has a mounted share\r\nhttps://posts.specterops.io/when-kirbi-walks-the-bifrost-4c727807744f\r\nPage 8 of 11\n\nYou might run into issues with fully qualified domain names with the mount command, if that’s the case, just the\r\ncomputer’s name should work. The following video shows a normal user, Alice, taking the hash of the domain\r\nadmin password to then mount and traverse the DC’s C$ share:\r\nDemo video of using over-pass-the-hash to access a C$ share\r\nIf you import the tickets into a credential cache other than the default (indicated with a *) or want to use credential\r\ncaches on disk, you can specify  KRB5CCNAME=  as an environment parameter and specify the cache you want to\r\nuse. If you’re using one from memory, be sure to take the full name including the leading  API: , and if you’re\r\nusing one on disk, be sure to prepend  FILE:  to the path.\r\nSSH can also be configured to use Kerberos tickets, but that requires a lot more pieces to be in place for both the\r\nSSH client and the SSH server.\r\nKeytabs and Saved Hashes\r\nKeytab files are analogous to SSH keys. They store encryption information that’s used to authenticate a user to a\r\nservice. More specifically, keytab files contain the encryption keys used for Kerberos authentication and are tied\r\nto specific service principal names. The main point is that they can provide a mechanism for single-sign-on across\r\na wide variety of applications and can be used on all major operating systems. While they are stored in plain text,\r\nthey don’t quite include the plaintext of passwords.\r\nThere is a default keytab file on every macOS device whether it’s joined to a domain or not (more on that later)\r\nlocated at  /etc/krb5.keytab , but users can create their own at any time. The main binary for working with\r\nkeytabs is  ktutil . This default keytab is locked down to the  root  user and the  _keytabusers  group on\r\nmacOS with 640 permissions. When users create their own keytabs though, they can set different permissions on\r\nthem.\r\nCreating and dump a new keytab\r\nThere are a couple important things to note here. Since this is a *nix environment, case sensitivity is important. Be\r\nsure to put the domain in all capitals or your hashes won’t work. Additionally, each entry has exactly one key and\r\nhttps://posts.specterops.io/when-kirbi-walks-the-bifrost-4c727807744f\r\nPage 9 of 11\n\none encryption type, but there can be duplicate entries. If you noticed from above, the hash saved here is the same\r\nAES256 hash generated when using the plaintext password. Bifrost also supports specifying a keytab file and\r\nencryption type when requesting a TGT from the domain.\r\nA Note on the LKDC\r\nEvery macOS device since 10.5 includes its own entire local kerberos stack — complete with unique realm, krbtgt\r\nuser, and key distribution center. The Local Key Distribution Center (LKDC) was added to support Kerberos-based authentication between two Apple endpoints without requiring them to be joined to a central realm and\r\nKDC. This comes into play when accessing a shared service on a remote macOS endpoint (such as browsing the\r\nfile system).\r\nThis process is a bit different than Windows-based Kerberos and is pretty apparent when you dump tickets with\r\nBifrost after mounting a shared folder:\r\nThis blogpost won’t go into all the details of the LKDC, how it works, or how it’s different than the other AD\r\njoined Kerberos details in the rest of this blog (that’ll be a future post), but there are a few “easy wins” from this\r\ndata. The top highlighted area contains the  plaintext  password, username, and hostname of the remote\r\nmachine. When dumping that ticket with Bifrost, you’ll get a base64 version of the plaintext password used to\r\nmount the share. The bottom highlighted section shows where the share was mounted.\r\nKeep in mind that this is an entirely different Kerberos realm with different methods of generating keys than the\r\nrest of this blog and different than Microsoft’s implementation. Keys here can be found in\r\nthe  /etc/krb5.keytab  file as well as in\r\nthe  dsAttrTypeNative:HeimdalSRPKey and  dsAttrTypeNative:KerberosKeys  attributes in local user’s Open\r\nDirectory properties. You can use the  dscl  utility or the Orchard project to read these, but you must be elevated\r\nto access any of them. The values stored here are in ASN1 notation, so if you want to explore then you should use\r\nan ASN1 decoder to parse out the pieces.\r\nDefensive Considerations\r\nThere aren’t a lot of native opportunities on macOS to delve into these APIs, but Heimdal Kerberos does include\r\none way to at least enable debug-level logging on these API calls:\r\nsudo defaults write /Library/Preferences/com.apple.Kerberos logging -dict-add krb5 '0-/SYSLOG:'\r\nsudo defaults write /Library/Preferences/com.apple.Kerberos logging -dict-add kcm '0-/SYSLOG:'\r\nsudo defaults write com.apple.MITKerberosShim EnableDebugging -bool true\r\nsudo defaults write /Library/Preferences/com.apple.Kerberos logging -dict-add kcm '1-/ASL:'\r\nBy default, the  /Library/Preferences/com.apple.Kerberos.plist  file does not exist and is a world readable file\r\nwhen it is created. An attacker can read this file to determine potential logging capabilities and with elevation,\r\ndisable it.\r\nAdditionally, with the new Endpoint Security Framework (ESF), it’s possible to detect\r\nthe  Kerberos.Framework  being loaded into processes in real-time. This will depend a lot of the specific\r\nhttps://posts.specterops.io/when-kirbi-walks-the-bifrost-4c727807744f\r\nPage 10 of 11\n\nenvironment and which processes are normally leveraging this framework. Because Kerberos does offer a great\r\nway for Single-Sign-On (SSO) within an environment, there are likely to be a bunch of 3rd party applications that\r\nmake use of these APIs.\r\nSome of the Bifrost capabilities rely on doing manual Kerberos traffic to a domain controller (  asktgt  with a\r\nhash,  asktgs ,  s4u ). Because of this, there will be direct connections from the process running the Bifrost code\r\nto port 88 on the domain controller. Depending on the environment, this could potentially be an easy indicator.\r\nSource: https://posts.specterops.io/when-kirbi-walks-the-bifrost-4c727807744f\r\nhttps://posts.specterops.io/when-kirbi-walks-the-bifrost-4c727807744f\r\nPage 11 of 11",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"MITRE"
	],
	"references": [
		"https://posts.specterops.io/when-kirbi-walks-the-bifrost-4c727807744f"
	],
	"report_names": [
		"when-kirbi-walks-the-bifrost-4c727807744f"
	],
	"threat_actors": [],
	"ts_created_at": 1775434558,
	"ts_updated_at": 1775791241,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/d4554d4d6ca616a400ddee86c4859bd936f8aff5.pdf",
		"text": "https://archive.orkl.eu/d4554d4d6ca616a400ddee86c4859bd936f8aff5.txt",
		"img": "https://archive.orkl.eu/d4554d4d6ca616a400ddee86c4859bd936f8aff5.jpg"
	}
}