This is the mail archive of the systemtap@sourceware.org mailing list for the systemtap project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: User Stack Trace


On Wed, Apr 18, 2007 at 11:08:30AM -0700, Stone, Joshua I wrote:
> grundy wrote:
> >On Tue, Apr 17, 2007 at 03:39:58PM -0400, Frank Ch. Eigler wrote:
> >>Or containing corrupt/malicious stack data?
> >Now this is interesting. One would have to corrupt return address prior
> >to the syscall we're tracing. They would have to know exactly what we're
> >tracing (possible) because once that call started to return it would
> >turn left into the lake (if we weren't probing that call).
> 
> The left turn can be avoided by simply not returning. :)  A malicious 
> program might look something like:
> 
> int main() {
> 	...
> 	// Do whatever tricks to munge up the return address..
> 	...
> 	// Loop for a while, making calls that we hope get probed...
> 	...
> 	exit(0);
> }
They would have to munge the return address for the current function,
otherwise there would be no looping :-). An evildoer could certainly
set up a bunch of threads that attacked everything and its sister, and
existing attack vectors on other products certainly show that sort of
sledgehammer nuance. 

> Or, for that matter, the program could just restore the correct return 
> address before it returns...
I don't think that is practical, though it would be handy to have a
sys_my-stack-is-hosed-please-fix-my-program() ^_^
> 
> Are there checks to make sure the addresses are reasonable?  I wonder if 
> a malicious program could also lead your stack walk astray -- perhaps 
> tricking you into printing a data value from some sensitive kernel area.
There are a few things that might help. One is the memory map of the
program. If the next address fits the map, cool. If not, drop it. This
is the next piece of the puzzle I've been working on.

Here is a snip that gives /proc/[pid]/map style information for an
address:

        mm = get_task_mm(current);
        
        if (!(vma = find_vma(mm, addr)))
                return 0;

        file = vma->vm_file;
        flags = vma->vm_flags;
                        
        if (file) {     
                struct inode *inode = vma->vm_file->f_dentry->d_inode;
                dev = inode->i_sb->s_dev;
                ino = inode->i_ino;
        }

        offset = ((unsigned long)addr - (unsigned long)vma->vm_start);
                
        _stp_printf("%08lx-%08lx %c%c%c%c %08lx %02x:%02x %lu ",
                        vma->vm_start,
                        vma->vm_end,
                        flags & VM_READ ? 'r' : '-',
                        flags & VM_WRITE ? 'w' : '-',
                        (flags & VM_EXEC) ? 'x' : '-',
                        flags & VM_MAYSHARE ? 's' : 'p',
                        vma->vm_pgoff << PAGE_SHIFT,
                        MAJOR(dev), MINOR(dev), ino);

The basic find_vma portion seems relatively safe. So for every address
we want to chain back through, make sure it resides in the program's
map. Now, since I want to be able to get the name of the vma for each
address in the backtrace (It's either have a call to get the processes
map saved when we need a backtrace and sort it out afterward, or plunk
it into the trace. I have a mostly working example that will give:

return_address vma->start:offset_in_vma vma_name(including path)

You can take items of interest and get line numbers pretty quick from
there. The code that does it is pretty ugly, might be dangerous (not
sure, and until I am I guess it is), things like this:

	/* d_path takes some locks we might not want to take here. */
	char *p = d_path(file->f_dentry, file->f_vfsmnt, buffer, MAXSTRINGLEN);
	_stp_printf("%s", p);

Yikes, I kinda turned left there. Where I was going with all that, is we
can verify an address is in the program's address map, and doing that
for each step will gather information I wanted to use anyway.

> It looks like _stp_copy_from_user will make the right checks to make 
> sure that the address is within the user's address space.  Your first 
> print in the loop reads head->ret directly though, where head = 
> nregs->ebp -- what if I faked my %ebp to point to kernel memory?
You would probably get a snippet of kernel memory, copy from users in
x86 land are often able to work on either. After that it would tank
pretty quick because you'd need really good aim to randomly chain.

> Or a more direct concern, even for non-malicious programs, what if the 
> memory at %ebp is paged out?  This is unlikely, but it's still possible, 
> right?  I think this first access to %ebp must be protected by a 
> _stp_copy_from_user as well.
_stp_copy_from_user does the right thing, checks access and returns a
null string if the location isn't available. 

> I'm not sure how much this potential kernel-pointer problem needs to be 
> a concern though, as that compromised value is only returned to the 
> person running the script (who already has root access, and could 
> already write a script to access that value directly).
nod.

> This is a must, to maintain our general SystemTap promise that scripts 
> will never get trapped in an infinite loop.
agreed, a tapset or runtime suitable solution will give up the ghost
after a reasonable amount of tries. 

Thanks
Mike


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]