Skip to content

Instantly share code, notes, and snippets.

@eqvinox
Created January 27, 2023 11:05
Show Gist options
  • Select an option

  • Save eqvinox/6b3f801f88885ec961d8a308c1f6e38d to your computer and use it in GitHub Desktop.

Select an option

Save eqvinox/6b3f801f88885ec961d8a308c1f6e38d to your computer and use it in GitHub Desktop.
/* compile: gcc -o creds creds.c -lcap */
#define _GNU_SOURCE 1
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <sys/capability.h>
#include <sys/fsuid.h>
#include <sys/prctl.h>
#include <linux/securebits.h>
#if 0
static pid_t gettid(void)
{
return (pid_t)syscall(__NR_gettid);
}
#endif
static pthread_t t1, t2;
static void show_state(void)
{
const char *self;
pthread_t t_self;
pid_t tid;
uid_t ruid, euid, suid, fsuid;
cap_t caps;
char *caps_text;
t_self = pthread_self();
self = (t_self == t1) ? "T1" : (t_self == t2) ? "T2" : "--";
tid = gettid();
getresuid(&ruid, &euid, &suid);
fsuid = setfsuid(-1);
caps = cap_get_proc();
caps_text = cap_to_text(caps, NULL);
printf("thread %s (%6ld) uid{ real=%ld eff=%ld save=%ld fs=%ld } caps{%s}\n",
self, (long)tid, (long)ruid, (long)euid, (long)suid, (long)fsuid,
caps_text);
cap_free(caps_text);
}
static int t1_fd, t2_fd;
/* T2 just observes and prints its state when told to do so */
static void *t2func(void *arg)
{
char buf[1];
ssize_t nread;
while (1) {
nread = read(t2_fd, buf, 1);
if (nread <= 0)
break;
show_state();
write(t2_fd, buf, 1);
}
close(t2_fd);
return NULL;
}
static void show_poke_t2(void)
{
char buf[1] = { '\0' };
show_state();
write(t1_fd, buf, 1);
read(t1_fd, buf, 1);
}
#define E(x) do { if ((x)) { fprintf(stderr, "%s failed: %m\n", #x); exit(1); } } while (0)
static bool test_libcap;
static bool test_libcapng;
static const char *do_caps;
/* T1 does the actual work */
static void *t1func(void *arg)
{
show_poke_t2();
if (test_libcap) {
cap_t caps = cap_from_text(do_caps);
printf("\nthread T1 cap_set_proc(%s)\n", do_caps);
E(cap_set_proc(caps));
cap_free(caps);
show_poke_t2();
}
printf("\nthread T1 changing fsuid to 1234\n");
setfsuid(1234);
show_poke_t2();
printf("\nthread T1 changing euid to 2345\n");
E(setresuid(-1, 2345, -1));
show_poke_t2();
printf("\nthread T1 changing fsuid to 3456\n");
setfsuid(3456);
show_poke_t2();
printf("\nthread T1 changing fsuid to 0\n");
setfsuid(0);
show_poke_t2();
printf("\nthread T1 changing euid to 0\n");
E(setresuid(-1, 0, -1));
show_poke_t2();
/*
printf("\nthread T1 changing fsuid to 4567\n");
setfsuid(4567);
show_poke_t2();
*/
close(t1_fd);
return NULL;
}
static void showsecbits(void)
{
unsigned long secbits;
secbits = prctl(PR_GET_SECUREBITS, 0, 0, 0, 0);
printf("secbits: KEEP_CAPS=%s NO_SETUID_FIXUP=%s\n",
(secbits & SECBIT_KEEP_CAPS) ? "true" : "false",
(secbits & SECBIT_NO_SETUID_FIXUP) ? "true" : "false");
}
static void changesecbits(unsigned long clear, unsigned long set)
{
unsigned long secbits;
int ret;
secbits = prctl(PR_GET_SECUREBITS, 0, 0, 0, 0);
secbits &= ~clear;
secbits |= set;
ret = prctl(PR_SET_SECUREBITS, secbits, 0, 0, 0);
if (ret)
perror("prctl(PR_SET_SECUREBITS)");
printf("-- change securebits(-%#lx, +%#lx)\n", clear, set);
showsecbits();
}
int main(int argc, char **argv)
{
int spair[2];
int ch;
showsecbits();
while ((ch = getopt(argc, argv, "KkXxc:")) != -1) {
switch (ch) {
case 'K':
changesecbits(0, SECBIT_KEEP_CAPS);
break;
case 'k':
changesecbits(SECBIT_KEEP_CAPS, 0);
break;
case 'X':
changesecbits(0, SECBIT_NO_SETUID_FIXUP);
break;
case 'x':
changesecbits(SECBIT_NO_SETUID_FIXUP, 0);
break;
case 'c':
test_libcap = true;
do_caps = optarg;
break;
}
}
printf("\n");
show_state();
socketpair(AF_UNIX, SOCK_SEQPACKET, 0, spair);
t1_fd = spair[0];
t2_fd = spair[1];
pthread_create(&t1, NULL, t1func, NULL);
pthread_create(&t2, NULL, t2func, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment