IAXClient是用于实现IAX2 VoIP协议的开放源码函数库。 IAXClient对IAX消息的处理上存在内存破坏漏洞,远程攻击者可能利用此漏洞在服务器上执行任意指令。 IAX消息被称为帧。iaxclient/lib/libiax2/src/iax2.h文件中定义了两个帧类型,分别是IAX完整帧(full frame)和IAX小型帧(mini-frame)。 /* Full frames are always delivered reliably */ struct ast_iax2_full_hdr { unsigned short scallno; /* Source call number -- high bit must be 1 */ unsigned short dcallno; /* Destination call number -- high bit is 1 if retransmission */ unsigned int ts; /* 32-bit timestamp in milliseconds (from 1st transmission) */ unsigned char oseqno; /* Packet number (outgoing) */ unsigned char iseqno; /* Packet number (next incoming expected) */ char type; /* Frame type */ unsigned char csub; /* Compressed subclass */ unsigned char iedata[0]; } __PACKED; /* Mini header is used only for voice frames -- delivered unreliably */ struct ast_iax2_mini_hdr { unsigned short callno; /* Source call number -- high bit must be 0, rest must be non-zero */ unsigned short ts; /* 16-bit Timestamp (high 16 bits from last...
IAXClient是用于实现IAX2 VoIP协议的开放源码函数库。 IAXClient对IAX消息的处理上存在内存破坏漏洞,远程攻击者可能利用此漏洞在服务器上执行任意指令。 IAX消息被称为帧。iaxclient/lib/libiax2/src/iax2.h文件中定义了两个帧类型,分别是IAX完整帧(full frame)和IAX小型帧(mini-frame)。 /* Full frames are always delivered reliably */ struct ast_iax2_full_hdr { unsigned short scallno; /* Source call number -- high bit must be 1 */ unsigned short dcallno; /* Destination call number -- high bit is 1 if retransmission */ unsigned int ts; /* 32-bit timestamp in milliseconds (from 1st transmission) */ unsigned char oseqno; /* Packet number (outgoing) */ unsigned char iseqno; /* Packet number (next incoming expected) */ char type; /* Frame type */ unsigned char csub; /* Compressed subclass */ unsigned char iedata[0]; } __PACKED; /* Mini header is used only for voice frames -- delivered unreliably */ struct ast_iax2_mini_hdr { unsigned short callno; /* Source call number -- high bit must be 0, rest must be non-zero */ unsigned short ts; /* 16-bit Timestamp (high 16 bits from last ast_iax2_full_hdr) */ /* Frametype implicitly VOICE_FRAME */ /* subclass implicit from last ast_iax2_full_hdr */ unsigned char data[0]; } __PACKED; 解析通过网络接收的IAX报文是由iaxclient/lib/libiax2/src/iax.c中实现的iax_net_process()函数完成的。以下截取自该文件的revision 536: struct iax_event *iax_net_process(unsigned char *buf, int len, struct sockaddr_in *sin) { struct ast_iax2_full_hdr *fh = (struct ast_iax2_full_hdr *)buf; struct ast_iax2_mini_hdr *mh = (struct ast_iax2_mini_hdr *)buf; struct iax_session *session; if (ntohs(fh->scallno) & IAX_FLAG_FULL) { /* Full size header */ [A] if (len < sizeof(struct ast_iax2_full_hdr)) { DEBU(G \"Short header received from \\%s\n\", inet_ntoa(sin->sin_addr)); IAXERROR \"Short header received from \\%s\n\", inet_ntoa(sin->sin_addr)); } /* We have a full header, process appropriately */ session = iax_find_session(sin, ntohs(fh->scallno) & ~IAX_FLAG_FULL, ntohs(fh->dcallno) & ~IAX_FLAG_RETRANS, 1); if (!session) session = iax_txcnt_session(fh, len-sizeof(struct ast_iax2_full_hdr), sin, ntohs(fh->scallno) & ~IAX_FLAG_FULL, ntohs(fh->dcallno) & ~IAX_FLAG_RETRANS); if (session) return iax_header_to_event(session, fh, len - sizeof(struct ast_iax2_full_hdr), sin); DEBU(G \"No session?\n\"); return NULL; } else { [B] if (len < sizeof(struct ast_iax2_mini_hdr)) { DEBU(G \"Short header received from \\%s\n\", inet_ntoa(sin->sin_addr)); IAXERROR \"Short header received from \\%s\n\", inet_ntoa(sin->sin_addr)); } /* Miniature, voice frame */ session = iax_find_session(sin, ntohs(fh->scallno), 0, 0); if (session) return iax_miniheader_to_event(session, mh, len - sizeof(struct ast_iax2_mini_hdr)); DEBU(G \"No session?\n\"); return NULL; } } len参数是从同一文件中实现的iax_net_read()函数接收到的,其值设置为recvfrom(2)函数调用的返回值,也就是从网络读取的字节数。buf参数是指向栈中分配的固定大小缓冲区的指针,数据从iax_net_read()函数读取。 函数在[A]和[B]执行了长度检查以确保所接收到的报文不是截短了的full-frame或mini-frame,但在输出错误消息后仍可能出现过小的报文,这就导致了两个可利用的漏洞。 IAX2截短full-frame漏洞 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 在[A]的情况下full-frame报文是由以下执行流处理的: iax_net_read() iax_net_process() iax_find_session() iax_txcnt_session() iax_header_to_event() 同一文件中也实现了iax_txcnt_session: static struct iax_session *iax_txcnt_session(struct ast_iax2_full_hdr *fh, int datalen, struct sockaddr_in *sin, short callno, short dcallno) { int subclass = uncompress_subclass(fh->csub); unsigned char buf[ 65536 ]; /* allocated on stack with same size asiax_net_read() */ struct iax_ies ies; struct iax_session *cur; if ((fh->type != AST_FRAME_IAX) || (subclass != IAX_COMMAND_TXCNT) || (!datalen)) { return NULL; /* special handling for TXCNT only */ } [C] memcpy(buf, fh->iedata, datalen); /* prepare local buf for iax_parse_ies() */ if (iax_parse_ies( &ies, buf, datalen)) { return NULL; /* Unable to parse IE\'\'s */ } ... datalen参数接收iax_net_process()传送的值,其计算方法为datalen = len-sizeof(struct ast_iax2_full_hdr)。如果full frame报文是从网络读取的话,该值就可能小于0。 然后在[C]使用了负值datalen的memcpy就会在栈中固定大小的缓冲区buf触发溢出。攻击者可以通过创建11字节长的UDP报文触发这个漏洞,执行任意代码。 由于使用了负数长度参数的malloc(2)、memset(2)和memcpy(2)组合,iax_header_to_event()函数也存在类似问题,在这种情况下是堆溢出。 IAX2截短mini-frame漏洞 ~~~~