有关于ssdp安全的文章
ssdp攻击和防御
组播 单播和广播
组播方式解决了单播情况下数据的重复拷贝及带宽的重复占用,也解决了广播方式下带宽资源的浪费,我们知道单播在发送者和每一接收者之间实现点对点网络连接。如果一台发送者同时给多个的接收者传输相同的数据,也必须相应的复制多份的相同数据包。如果有大量主机希望获得数据包的同一份拷贝时,将导致发送者负担沉重、延迟长、网络拥塞;为保证一定的服务质量需增加硬件和带宽。
组播在发送者和每一接收者之间实现点对多点网络连接。如果一台发送者同时给多个接收者传输相同的数据,也只需复制一份相同的数据包。它提高了数据传送效率,减少了骨干网络出现拥塞的可能性。实际上,组播在局域网里,是由交换机和路由器等硬件等完成一次数据的存储,而转发给其他的加入组播组的成员的。
地址
224.0.0.0~224.0.0.255为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其它地址供路由协议使用。实际上,这是属于D类地址。
224.0.1.0~238.255.255.255 为用户可用的组播地址(临时组地址),全网范围内有效。
upnp
plug and play 即插即用协议里面使用的 ssdp(imple Service Discovery Protocol)就是属于组播协议,简单服务发现协议,使用组播地址 239.255.255.255,端口1900 。使用wireshark等该端口的包,是可以看见很多包的,因为网关也使用这一协议。实际上,里面大量充斥的是类http协议,也有ssdp ddos 攻击的可能。
Mbone
Mbone是一种跨越网络。它是一个相互连接的子网和路由器的集合,这些子网和路由器支持IP组播业务流的传送。作为因特网上的虚拟网络,Mbone通过隧道(Tunneling)来旁路因特网上无组播能力的路由器。但是,不是所有路由器能够支持这种网络,所以,这个只能是实验性质,所以,组播,一般只能在局域网里实现。
如何实现
以下用用实例来说明, c# 和 c++来实现ssdp协议的组播,看似简短,但是以下程序真的是一个小却能使用的ssdp搜索程序,加上tcp的接收和发送,就可以做控制点,凡是有开始,从这个简单的c#程序着手,会有收获。
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace ssdp
{
class Program
{
static void Main(string[] args)
{
IPEndPoint LocalEndPoint = new IPEndPoint(IPAddress.Any, 23000);
IPEndPoint MulticastEndPoint = new IPEndPoint(IPAddress.Parse("239.255.255.250"), 1900);
Socket UdpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
UdpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
UdpSocket.Bind(LocalEndPoint);
UdpSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(MulticastEndPoint.Address, IPAddress.Any));
UdpSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 2);
UdpSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastLoopback, true);
Console.WriteLine("UDP-Socket setup done...\r\n");
string SearchString = "M-SEARCH * HTTP/1.1\r\nHOST:239.255.255.250:1900\r\nMAN:\"ssdp:discover\"\r\nST:ssdp:all\r\nMX:3\r\n\r\n";
UdpSocket.SendTo(Encoding.UTF8.GetBytes(SearchString), SocketFlags.None, MulticastEndPoint);
Console.WriteLine("M-Search sent...\r\n");
byte[] ReceiveBuffer = new byte[3200];
int ReceivedBytes = 0;
while (true)
{
if (UdpSocket.Available > 0)
{
ReceivedBytes = UdpSocket.Receive(ReceiveBuffer, SocketFlags.None);
if (ReceivedBytes > 0)
{
Console.WriteLine(Encoding.UTF8.GetString(ReceiveBuffer,0, ReceivedBytes));
}
}
}
}
}
}
windows 下 c++组播
/*
* author :qianbo
* function:组播类
* 注意生成对象发送和接收是两个,生成根据 SOKM_REV 还是SOKM_SND
*/
#ifndef __GROUPSOCK_H1_
#define __GROUPSOCK_H1_
#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#endif
#include <stdio.h>
namespace CorePhone
{
enum{
SOCKM_REV,
SOCKM_SND
};
#define BUFSIZE 1500
class CGroupSock
{
private:
//以下为第二版多播通信,
WSADATA _wsd;
struct sockaddr_in _local,
_remote,
_from;
SOCKET _sock, _sockM;
int _len ;//= sizeof(struct sockaddr_in),
int _optval;
int _ret;
int _Receive_Send; //(0,1)
private:
BOOL _isConnected;
protected:
//BOOL InitWinsock2();
public:
BOOL Initialize(int RS);
BOOL JoinGroup(const char* ip = NULL,int port = 0);
BOOL SendTo(const char *pBuf,int nlen);
int ReceiveData(char *pBuf,int bufferlen);
public:
CGroupSock(void);
~CGroupSock(void);
};
};
#endif
/*
Author:钱波
email: 418511899@qq.com
wei: 18091589062
func :类 windows 下组播
time: 2018年5月30日
*/
#include "GroupSock.h"
namespace CorePhone
{
CGroupSock::CGroupSock(void)
{
_len = sizeof(struct sockaddr_in);
}
CGroupSock::~CGroupSock(void)
{
closesocket(_sock);
}
BOOL CGroupSock::Initialize(int RS)
{
_Receive_Send = RS;
return 0;
}
BOOL CGroupSock::JoinGroup(const char* ip, int port)
{
//
//以下为第二版windows多播通信,加入了根通信
if ((_sock = WSASocket(AF_INET, SOCK_DGRAM, 0, NULL, 0,
WSA_FLAG_MULTIPOINT_C_LEAF
| WSA_FLAG_MULTIPOINT_D_LEAF
| WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
{
printf("socket failed with: %d\n", WSAGetLastError());
return FALSE;
}
// Bind to the local interface. This is done to receive data.
_local.sin_family = AF_INET;
//_local.sin_port = 0;//htons(port);
_local.sin_port = 0;
if (_Receive_Send == SOCKM_REV)
_local.sin_port = htons(port);
_local.sin_addr.s_addr = INADDR_ANY;
char optval = FALSE; // 屏蔽回播
if (setsockopt(_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof(int)) < 0)
{
return FALSE;
}
if (bind(_sock, (struct sockaddr *)&_local,
sizeof(_local)) == SOCKET_ERROR)
{
printf("bind failed with: %d\n", WSAGetLastError());
closesocket(_sock);
WSACleanup();
return FALSE;
}
// Setup the SOCKADDR_IN structure describing the multicast
// group we want to join
//
_remote.sin_family = AF_INET;
_remote.sin_port = htons(port);
_remote.sin_addr.s_addr = inet_addr(ip);
//
// Change the TTL to something more appropriate
//
_optval = 32;
if (setsockopt(_sock, IPPROTO_IP, IP_MULTICAST_TTL,
(char *)&_optval, sizeof(int)) == SOCKET_ERROR)
{
printf("setsockopt(IP_MULTICAST_TTL) failed: %d\n",
WSAGetLastError());
closesocket(_sock);
WSACleanup();
return FALSE;
}
// Disable loopback if needed
//
_optval = 0;
if (setsockopt(_sock, IPPROTO_IP, IP_MULTICAST_LOOP,
(char *)&_optval, sizeof(_optval)) == SOCKET_ERROR)
{
printf("setsockopt(IP_MULTICAST_LOOP) failed: %d\n",
WSAGetLastError());
closesocket(_sock);
WSACleanup();
return FALSE;
}
if ((_sockM = WSAJoinLeaf(_sock, (SOCKADDR *)&_remote,
sizeof(_remote), NULL, NULL, NULL, NULL,
JL_BOTH)) == INVALID_SOCKET)
{
printf("WSAJoinLeaf() failed: %d\n", WSAGetLastError());
closesocket(_sock);
WSACleanup();
_isConnected = FALSE;
return FALSE;
}
_isConnected = TRUE;
return TRUE;
}
BOOL CGroupSock::SendTo(const char *pBuf, int len)
{
if (sendto(_sock, pBuf, len, 0,
(struct sockaddr *)&_remote,
sizeof(_remote)) == SOCKET_ERROR)
{
printf("sendto failed with: %d\n", WSAGetLastError());
closesocket(_sockM);
closesocket(_sock);
WSACleanup();
return FALSE;
}
return TRUE;
}
int CGroupSock::ReceiveData(char *pBuf, int BufLen)
{
int ret = 0;
if ((ret = recvfrom(_sock, pBuf, BufLen, 0,
(struct sockaddr *)&_from, &_len)) == SOCKET_ERROR)
{
//printf("recvfrom failed with: %d\n", WSAGetLastError());
closesocket(_sockM);
closesocket(_sock);
WSACleanup();
return -1;
}
return ret;
}
}
使用boost 库来接收ssdp 组播
从上可以看出网关和chrome都在发ssdp信息,而chrome是发的搜索信息,网关噼里啪啦发了一堆回应信息
#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include "boost/bind.hpp"
//ssdp 协议
const short multicast_port = 1900;
class receiver
{
public:
receiver(boost::asio::io_context& io_service,
const boost::asio::ip::address& listen_address,
const boost::asio::ip::address& multicast_address)
: socket_(io_service)
{
// Create the socket so that multiple may be bound to the same address.
boost::asio::ip::udp::endpoint listen_endpoint(
listen_address, multicast_port);
socket_.open(listen_endpoint.protocol());
socket_.set_option(boost::asio::ip::udp::socket::reuse_address(true));
socket_.bind(listen_endpoint);
// Join the multicast group.
socket_.set_option(
boost::asio::ip::multicast::join_group(multicast_address));
socket_.async_receive_from(
boost::asio::buffer(data_, max_length), sender_endpoint_,
boost::bind(&receiver::handle_receive_from, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_receive_from(const boost::system::error_code& error,
size_t bytes_recvd)
{
if (!error)
{
std::cout.write(data_, bytes_recvd);
std::cout << std::endl;
socket_.async_receive_from(
boost::asio::buffer(data_, max_length), sender_endpoint_,
boost::bind(&receiver::handle_receive_from, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
}
private:
boost::asio::ip::udp::socket socket_;
boost::asio::ip::udp::endpoint sender_endpoint_;
enum { max_length = 1024 };
char data_[max_length];
};
int main(int argc, char* argv[])
{
try
{
boost::asio::io_context io_service;
receiver r(io_service,
boost::asio::ip::address::from_string("192.168.1.144"),
boost::asio::ip::address::from_string("239.255.255.250"));
io_service.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
这一次是接收信息,未完待续,下面一节会讲SSDP及DDOS攻击,以及ssdp设备发现的过程。以及如何攻击这种协议。