Last active
March 27, 2016 14:53
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
gcc netoff.c -o netoff | |
cp netoff /usr/bin/ | |
cp netoff /usr/bin/netlo | |
chmod 4755 /usr/bin/netoff | |
chmod 4755 /usr/bin/netlo |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You need iptables v1.6, coz previous versions don't have the
cgroup
module