前節の「認証がない」に加えて、memcachedは、脆弱性のあるバージョン(1.5.5)までは、デフォルトで11211/UDP、11211/TCPでListenしています。
ここで、UDPの特性として「3wayハンドシェイクがない」ことに気が付くと思います。これを利用して、送信元を偽装してパケットを送ることができます。
下記は、送信元を偽装したパケットを送るサンプルプログラムです。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/ip.h> #include <netinet/udp.h> #define MAX_PAC_SIZE 4096 void setup_ip_header(struct iphdr *iph) { iph->ihl = 5; iph->version = 4; iph->tot_len = sizeof(struct iphdr) + sizeof(struct udphdr) + 15; iph->protocol = IPPROTO_UDP; iph->saddr = inet_addr("172.16.148.146"); iph->daddr = inet_addr("172.16.148.145"); } void setup_udp_header(struct udphdr *udph) { udph->source = htons(5000); udph->dest = htons(12345); udph->check = 0; udph->len=htons(sizeof(struct udphdr) + 15); } int main(int argc, char *argv[ ]) { struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); char datagram[MAX_PAC_SIZE]; addr.sin_addr.s_addr=inet_addr("172.16.148.145"); struct iphdr *iph = (struct iphdr *)datagram; struct udphdr *udph = (/*u_int8_t*/void *)iph + sizeof(struct iphdr); int s = socket(PF_INET, SOCK_RAW, IPPROTO_TCP); memset(datagram, 0, MAX_PAC_SIZE); setup_ip_header(iph); setup_udp_header(udph); int boolean = 1; if(setsockopt(s, IPPROTO_IP, IP_HDRINCL, (const char *)&boolean, sizeof (boolean)) < 0){ fprintf(stderr, "Error: setsockopt() - Cannot set HDRINCL!\n"); exit(-1); } fprintf(stdout, "Send data...\n"); sendto(s, datagram, iph->tot_len, 0, (struct sockaddr *) &addr, sizeof(addr)); return 0; }
このプログラムは、あくまでも送信元偽装のサンプルで作ったものなので、きちんと作り込んでいません。プログラムを見て分かる通り、下記でUDPパケットを送るプログラムになっています。
このプログラムをコンパイルして、ホスト172.16.148.1上でsudoなどを用いてroot権限で実行すると、送信元を偽装して172.16.148.145に送ってくれます。172.16.148.145でキャプチャーしたパケットをtcpdumpで見ると、送信元が「172.16.148.146」に偽装できています(図5)。
ここまで述べた【1】【2】の条件がそろってしまったために起きたのが、今回の「memcachedを踏み台にしたDoSの問題」ということになります(図6)。
図6の1.では、攻撃者(今回の例では、172.16.148.1)が、問題のあるバージョンのmemcachedサーバ(今回の例では172.16.148.145)に、送信元をターゲットのサーバ(今回の例では172.16.148.146)に偽装して、データをGetで要求します(それ以前に大きいデータをSetしておき、その値をGetすることも考えられます)。
図6の2.では、memcachedのサーバが、ターゲットのサーバに対してデータを送ります。
図6の状況でPoCを動かした際のパケットキャプチャーが図7です。
memcachedのサーバ(172.16.148.145)からターゲット(172.16.148.146)にmemcachedプロトコルでの通信が大量に発生しているのが分かります。
実際には、問題のあるmemcached(UDP上でListenしたまま)がインターネット上で多数公開されていたため、複数のサーバから一気にターゲットのサーバにデータを送り付けてDDoS攻撃となったようです。
今回の脆弱性の修正は、もっとも単純でかつ効果的なもので、「memcachedがデフォルトではUDPでListenしない」というものです。memcached 1.5.6で脆弱性が修正されています。詳しい情報は、お使いのディストリビューションの情報を確認した方がいいと思います。
今回の問題は「memcachedのUDPがインターネット上からアクセスできるサーバが幾つもあった」ということに起因しています。そのため、昔からよく言われている下記2つの基本的な心掛けが大切になってきます。
皆さまも、インターネット上にサーバを公開する際には、基本的な心掛けを忘れないようにしましょう。
略歴:OSSのセキュリティ専門家として20年近くの経験があり、主にOS系のセキュリティに関しての執筆や講演を行う。大手ベンダーや外資系、ユーザー企業などでさまざまな立場を経験。2015年からサイオステクノロジーのOSS/セキュリティエバンジェリストとして活躍し、同社でSIOSセキュリティブログを連載中。
CISSP:#366942
近著:『Linuxセキュリティ標準教科書』(LPI-Japan)」
Copyright © ITmedia, Inc. All Rights Reserved.