Skip to content

Instantly share code, notes, and snippets.

@zb3
Last active March 27, 2016 14:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zb3/8ae70823b8c6e993e9c3 to your computer and use it in GitHub Desktop.
Save zb3/8ae70823b8c6e993e9c3 to your computer and use it in GitHub Desktop.
netoff command to run a process without access to the network. This should be a suid binary (of course the code is unsafe, just a PoC) (won't work with unified cgroup hierarchy)
gcc netoff.c -o netoff
cp netoff /usr/bin/
cp netoff /usr/bin/netlo
chmod 4755 /usr/bin/netoff
chmod 4755 /usr/bin/netlo
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
/*
netoff - no network @ all
netlo - only loopback
requires:
-> iptables 1.6+
-> net_cls cgroup (non-unified hierarchy)
1. This is a PoC only - don't take it too seriously... (even though I am actually using this)
2. This is NOT the same as unshare.
The packets will be filtered, but the interface exists, so this isn't immediately visible,
also this is more flexible as you can manually modify those iptables rules, but it's not future-proof
<<
in the future, there will be no net_cls controller, and a different model of cgroups will be used...
in short: (probably) we'll create a new cgroup and we'll use nftables to filter that, basing on cgroup's PATH
>>
*/
#define ARRAY_SIZE(array) \
(sizeof(array) / sizeof(*array))
char *check_command[] = {"/usr/bin/iptables", "-C", "OUTPUT", "-m", "cgroup", "--cgroup", "263", "-j", "DROP", NULL};
char *install_commands[6][12] = {
{"/usr/bin/iptables", "-A", "OUTPUT", "-m", "cgroup", "--cgroup", "263", "-j", "DROP", NULL},
{"/usr/bin/iptables", "-A", "INPUT", "-m", "cgroup", "--cgroup", "263", "-j", "DROP", NULL},
{"/usr/bin/iptables", "-A", "OUTPUT", "-o", "lo", "-m", "cgroup", "--cgroup", "264", "-j", "ACCEPT", NULL},
{"/usr/bin/iptables", "-A", "INPUT", "-i", "lo", "-m", "cgroup", "--cgroup", "264", "-j", "ACCEPT", NULL},
{"/usr/bin/iptables", "-A", "OUTPUT", "-m", "cgroup", "--cgroup", "264", "-j", "DROP", NULL},
{"/usr/bin/iptables", "-A", "INPUT", "-m", "cgroup", "--cgroup", "264", "-j", "DROP", NULL}
};
mode_t getumask()
{
mode_t mask = umask(0);
umask (mask);
return mask;
}
void oh_mai_god(const char* msg)
{
fprintf(stderr, "%s\n", msg);
fflush(stderr);
exit(-1);
}
int safer_fork()
{
int child = fork();
if (child == -1)
oh_mai_god("can't fork!");
return child;
}
//the idea is to alert that we won't work
//or else print the output which can also say that we won't...
//IOW: the app may run with the network, but we *should* get some info in stderr/stdout
//brilliant security indeed
void safer_execv(const char *path, char *const argv[])
{
execv(path, argv);
oh_mai_god("can't execv!");
}
void safer_write(int fd, const void *buf, size_t count)
{
if (write(fd, buf, count) == -1)
oh_mai_god("can't write!");
}
int check_if_our_rules_are_installed______yep_that_s_all_this_function_does()
{
int child = safer_fork();
int statval;
if (!child)
safer_execv("/usr/bin/iptables", check_command);
waitpid(child, &statval, 0);
return !WEXITSTATUS(statval);
}
int main(int argc, char **argv)
{
if (argc<2)
{
printf("-1 not enough jquery!\n");
return -1;
}
int allow_local = strstr(argv[0], "netlo") != NULL;
uid_t ouid = getuid();
gid_t ogid = getgid();
seteuid(0);
mode_t mask = getumask();
mkdir("/sys/fs/cgroup/net_cls/nonet", 0777 & ~mask);
mkdir("/sys/fs/cgroup/net_cls/lonet", 0777 & ~mask);
int root_cgroup_file = open("/sys/fs/cgroup/net_cls/tasks", O_WRONLY | O_CLOEXEC);
int class_file;
class_file = open("/sys/fs/cgroup/net_cls/nonet/net_cls.classid", O_WRONLY);
safer_write(class_file, "263", 3); close(class_file);
class_file = open("/sys/fs/cgroup/net_cls/lonet/net_cls.classid", O_WRONLY);
safer_write(class_file, "264", 3); close(class_file);
char our_pid[1024];
sprintf(our_pid, "%d", getpid());
int group_tasks = open(allow_local ? "/sys/fs/cgroup/net_cls/lonet/tasks" : "/sys/fs/cgroup/net_cls/nonet/tasks", O_WRONLY);
safer_write(group_tasks, our_pid, strlen(our_pid));
close(group_tasks);
if (!check_if_our_rules_are_installed______yep_that_s_all_this_function_does())
{
for(int t=0;t<ARRAY_SIZE(install_commands);t++)
{
if (!safer_fork())
safer_execv("/usr/bin/iptables", install_commands[t]);
wait(NULL);
}
}
if (!check_if_our_rules_are_installed______yep_that_s_all_this_function_does())
oh_mai_god("can't install our rulez!");
if (setgid(ogid) != 0 || setuid(ouid) != 0)
oh_mai_god("can't drop privileges!");
//block all signals - we want them to be delivered to the child process
sigset_t sigmask;
sigfillset(&sigmask);
sigprocmask(SIG_SETMASK, &sigmask, NULL);
if (!safer_fork())
execvp(argv[1], argv+1);
wait(NULL);
//remove our pid from the group by adding it to the root group
write(root_cgroup_file, our_pid, strlen(our_pid));
close(root_cgroup_file);
return 0;
}
@zb3
Copy link
Author

zb3 commented Mar 26, 2016

You need iptables v1.6, coz previous versions don't have the cgroup module

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment