[ Phrack Magazine Volume 8, Issue 53 July 8, 1998, article 13 of 15 [Designing and Attacking Port Scan Detection Tools --------[ solar designer ¹ø¿ª : Á¶À±Á¾ 1. ¼Ò°³ ÀÌ ±â»çÀÇ ¸ñÀûÀº IDS(ħÀÔÀ» °Ë»öÇÏ´Â ½Ã½ºÅÛ, intrusion detection systems)°¡ °®´Â °¡´É¼ºÀÌ °¡Àå ¸¹Àº ¹®Á¦(ƯÈ÷ Æ÷Æ® ½ºÄµ¿¡ ÀÇÇÑ °¡Àå °£´ÜÇÑ °ø°Ý)¸¦ º¸¿©ÁÖ´Â °ÍÀÌ´Ù. The purpose of this article is to show potential problems with intrusion detection systems (IDS), concentrating on one simple attack: port scans. This lets me cover all components of such a simplified IDS. Also, unlike ÀÌ ±â»ç´Â ÀÌ·± ´Ü¼øÇÑ IDSÀÇ ¸ðµç ÄÄÆ÷³ÍÆ®¸¦ º¸¿©°í, SNI paper(http://www.secnet.com/papers/IDS.PS)¿Í´Â ´Þ¸®, ÀÌ ±â»ç´Â ³×Æ®¿öÅ©¸¦ ±â¹ÝÀ¸·Î µÐ Åø¿¡ Á¦ÇÑÇÏÁö ¾Ê´Â´Ù. ½ÇÁ¦·Î, °£´ÜÇÏ°í ½Å·ÚÇÒ ¼ö ÀÖ´Â Æ÷Æ® ½ºÄµ °¨Áö Åø("scanlogd") À» ¸¶Áö ¸· ºÎºÐ¿¡¼­ º¼¼ö Àִµ¥ ÀÌ ÅøÀº host-based·Î ¸¸µé¾îÁ® ÀÖ´Ù. the great SNI paper (http://www.secnet.com/papers/IDS.PS), this article is not limited to network-based tools. In fact, the simple and hopefully reliable example port scan detection tool that you'll find at the end is host-based. 2. ¹«¾ùÀ» °Ë»öÇÒ¼ö Àִ°¡? Æ÷Æ® ½ºÄµÀº °ø°ÝÀÚ°¡ »ó´ë¹æ ³×Æ®¿öÅ©ÀÇ ¸¹Àº Æ÷Æ®(ÀϹÝÀûÀ¸·Î listeningÀ» ÇÏ°í ÀÖ´Â ¾Ê´Â Æ÷Æ®µµ Æ÷ÇÔ)¿¡ Á¢¼ÓÀ» ½ÃµµÇÏ´Â °ÍÀÌ´Ù. ¿©±â¼­ Æ÷Æ® ½ºÄµÀ» °¨ÁöÇÒ ¼ö ÀÖ´Â ÇÑ°¡Áö ½ÅÈ£ ´Â ¿©·¯ °³ÀÇ ÆÐŶÀÌ Çѱºµ¥ÀÇ IP ÁּҷκÎÅÍ ¼­·Î ´Ù¸¥ Æ÷Æ®¿¡ ªÀº ½Ã°£¿¡ Á¢¼ÓÀ» ½ÃµµÇÑ ´Ù´Â °ÍÀÌ´Ù. ´Ù¸¥ ½ÅÈ£´Â listeningÀ» ÇÏÁø ¾Ê´Â Æ÷Æ®¿¡ SYN¸¦ ÇÒ·Á´Â ½ÃµµÀÌ´Ù(SYN to a non-listening port). ºÐ¸íÈ÷ À§¿¡ ¾ð±ÞÇÑ 2°¡Áö¿Ü¿¡ ´Ù¸¥ ¸¹Àº ¹æ¹ýÀ¸·Î Æ÷Æ® ½ºÄµÀ» °¨ÁöÇÒ ¼ö ÀÖÀ» °ÍÀÌ´Ù. ÃÖ¾ÇÀÇ °æ¿ì ¸ðµç ÆÐŶ Çì´õ¸¦ ÆÄÀÏ·Î ÀúÀåÇÏ¿´´Ù°¡ ÀÌ°ÍÀ» ¼öÀÛ¾÷À¸·Î ºÐ ¼®ÇÒ ¼öµµ ÀÖÀ» °ÍÀÌ´Ù.(ÀÌ°Ç ¹«Áö ³ë°¡´Ù ÀÌ°ÚÁÒ ^^) A port scan involves an attacker trying many destination ports, usually including some that turn out not to be listening. One "signature" that could be used for detecting port scans is "several packets to different destination ports from the same source address within a short period of time". Another such signature could be "SYN to a non-listening port". Obviously, there are many other ways to detect port scans, up to dumping all the packet headers to a file and analyzing them manually (ouch). ÀÌ·¸°Ô ´Ù¸¥ ¸ðµç ¹æ¹ýµéÀº °¢°¢ ÀڽŸ¸ÀÇ ÀåÁ¡°ú ´ÜÁ¡À» °¡Áö°í ÀÖ°í, ¶ÇÇÑ ´Ù¾çÇÑ À߸øµÈ ±àÁ¤Á¤ÀÎ Á¡°ú À߸øµÈ ºÎÁ¤ÀûÀÎ Á¡À» ÃÊ·¡ÇÒ ¼öµµ ÀÖ´Ù. Áö±ÝºÎÅÍ´Â ÀÌ·¯ÇÑ Æ¯º°ÇÑ °ø°ÝÀÌ Æ÷Æ® Á¤º¸¸¦ ¾ò±â À§ÇÏ¿© °ø°ÝÇÏ´Â µ¿¾È °ø°ÝÀÚ°¡ ÀÚ½ÅÀ» µå·¯³»Áö ¾Ê°Å³ª ½ÇÁ¦ ¹ß½ÅÁö¸¦ ÃßÀûÇÒ ¼ö ¾øµµ·Ï ÇÏ´Â °ÍÀÌ °¡´ÉÇÏ´Ù´Â °ÍÀ» º¸°Ô µÉ °ÍÀÌ´Ù. All of these different methods have their own advantages and disadvantages, resulting in different numbers of "false positives" and "false negatives". Now, let me show that, for this particular attack type, it is always possible for an attacker to make her attack either very unlikely to be noticed, or very unlikely to be traced to its real origin, while still being able to obtain the port number information. °ø°Ý¿¡ ´ëÇÏ¿© Àß µå·¯³»Áö ¾Ê°Ô Çϱâ À§ÇÏ¿©, °ø°ÝÀÚ´Â ¸Å¿ì õõÈ÷ Æ÷Æ®¸¦ ½ºÄµÇÒ¼öµµ ÀÖ ´Ù. ÀϹÝÀûÀ¸·Î ´ë»óÀÌ µÇ´Â ½Ã½ºÅÛÀÌ idle(½ÇÁ¦ »óȲÀº Ʋ¸®Áö¸¸, ÇÑ °³ÀÇ ÆÐŶÀÌ non- listening Æ÷Æ®¿¡ º¸³»¸é ÀÌ°ÍÀ» ½Ã½ºÅÛ °ü¸®ÀÚ¿¡°Ô Å뺸ÇÏ´Â °æ¿ì)ÀÌ ¾Æ´Ï¶ó¸é, Àü¿¡ º¸³»Áø ÆÐŶÀÌ Æ÷Æ® ½ºÄµÇÏ´Â °ÍÀÌ ¾Æ´Ñ°Íó·³ ´À³¢µµ·Ï ÃæºÐÇÑ ½Ã°£ °£°ÝÀ» µÎ´Â °Í ¶ÇÇÑ °¡´ÉÇÏ ´Ù. To obscure the attack, an attacker could do the scan very slowly. Unless the target system is normally idle (in which case one packet to a non-listening port is enough for the admin to notice, not a likely real world situation), it is possible to make the delay between ports large enough for this to be likely not recognized as a scan. Á¤º¸¸¦ ¹Þ´Âµ¿¾È ½ºÄµÇÏ´Â Ãâó¸¦ °¨Ãß±â À§ÇÑ ¹æ¹ýÀ¸·Î´Â ¸¹Àº ¾çÀÇ spoofed Æ÷Æ® ½ºÄµÀ» º¸³»°í ±× Áß ¿ÀÁ÷ ÇÑ °³¸¸ ÀÚ½ÅÀÇ ½ÇÁ¦ ÁÖ¼Ò¸¦ Æ÷ÇÔÇÏ¿© º¸³»´Â °ÍÀÌ´Ù. ÀÌ °æ¿ì ¸¸¾à 1000°³ÀÇ ÆÐŶÀ» º¸³»°í ÀÌ°ÍÀ» ¸ðµÎ °¨ÁöÇؼ­ ½Ã½ºÅÛ °ü¸®ÀÚ¿¡°Ô º¸³»´õ¶óµµ, ½ÇÁ¦·Î ÀÌÁß ¿ÀÁ÷ ÇÑ °³ÀÇ ÆÐŶ¸¸ ½ÇÁ¦ ¼Ò½º·ÎºÎÅÍ ¿Â°ÍÀ̱⠶§¹®¿¡ ¾î´À ÆÐŶÀÌ ÁøÂ¥ ÆÐŶÀÎÁö ±¸º°ÇÒ ¼ö°¡ ¾ø°Ô µÈ´Ù. ¿ì¸®°¡ ÇÒ¼ö ÀÖ´Â ÃÖ¼±ÀÇ ¹æ¹ýÀº ¿ÀÁ÷ Æ÷Æ®°¡ ½ºÄµµÇ¾ú´Ù´Â °ÍÀ» Å뺸ÇÏ¿© ÁÖ´Â °Í »ÓÀÌ´Ù. A way to hide the origin of a scan, while still receiving the information, is to send a large amount (say, 999) of spoofed "port scans", and only on scan from the real source address. Even if all the scans (1000 of them) are detected and logged, there's no way to tell which of the source addresses is real. All we can tell is that we've been port scanned. ÀÌ·¯ÇÑ °ø°ÝÀÌ °¡´ÉÇÑ ÇÑ, °ø°ÝÀÚµéÀº Æ÷Æ®¸¦ ½ºÄµÇϱâ À§ÇÏ¿© ÀڽŵéÀÌ ´õ¿í´õ ¸¹Àº ÀÚ¿ø À» ÇÊ¿ä·Î ÇÏ°Ô µÉ °ÍÀÌ´Ù. ¸î¸î °ø°ÝÀÚµéÀº ÀÌ·¯ÇÑ º¹ÀâÇÏ°í ´À¸° °ø°ÝÀ» ¼±ÅÃÇÏÁö ¾Ê°Å³ª ´Ù¸¥ °ø°ÝÀÚµéÀº ±×µéÀÇ ½Ã°£À» ÅõÀÚ¸¦ ÇØ¾ß ÇÒ °ÍÀÌ´Ù. ÀÌ°Í ÇÑ°¡Áö¸¸À¸·Îµµ ÃÖ¼ÒÇÑÀÇ Æ÷ Æ® ½ºÄµ(°¨ÁöÇÒ ¼ö ÀÖ´Â °ø°Ý)À» °¨ÁöÇÏ´Â ÅøÀ» ¸¸µå´Â °Í¿¡ ´ëÇÑ ÃæºÐÇÑ ÀÌÀ¯°¡ µÉ °ÍÀÌ´Ù. Note that, while these attacks are possible, they obviously require more resources from the attacker to perform. Some attackers will likely choose not to use such complicated and/or slow attacks, and others will have to pay with their time. This alone is enough reason to still detect at least some port scans (the ones that are detectable). ÀÌ·¯ÇÑ °ø°ÝÀÇ °¡´É¼ºÀº ¿ì¸®ÀÇ ¸ñÀûÀÌ ¸ðµç Æ÷Æ® ½ºÄµ ¹æ¹ýÀ» °Ë»öÇÏ´Â °ÍÀÌ ¾Æ´Ï¶ó ÃæºÐ È÷ ½Å·ÚÇÒ ¼ö ÀÖ´Â °¡´ÉÇÑ ¸¹Àº Æ÷Æ® ½ºÄµ Á¾·ù¸¦ °Ë»öÇÏ´Â °ÍÀÌ´Ù. The possibility of such attacks means that our goal is not to detect all port scans (which is impossible), but instead, in my opinion, to detect as many port scan kinds as possible while still being reliable enough. 3. ¾î¶²ÇÑ Á¤º¸°¡ ¹ÏÀ»¸¸ ÇÑ°¡? ºÐ¸íÈ÷ ÀÚ½ÅÀÇ ÁÖ¼Ò´Â Àֱ⠶§¹®¿¡ Áõ°Å°¡ È®º¸µÇÁö ¾Ê´Â ÇÑ ÆÐŶ¿¡¼­ µé¾î¿À´Â ¹ß½ÅÁö ÁÖ ¼Ò¸¦ ¹ÏÀ¸¸é ¾ÊµÈ´Ù. ¾îÂîµÇ¾úµç Æ÷Æ® ½ºÄ³³Ê´Â ¶§¶§·Î spoofing ¹æ½ÄÀ» ÀÌ¿ëÇÏ¿© ÀÚ½ÅÀÇ ´Ù ¸¥ ÁÖ¼Ò¸¦ ½ÇÁ¦ ÁÖ¼Ò·Î Âø°¢Çϵµ·Ï ´Ù¸¥ Á¤º¸µéÀ» ÀϺη¯ º¸³»±âµµ ÇÑ´Ù. Obviously, the source address can be spoofed, so we can't trust it unless other evidence is available. However, port scanners sometimes leak extra information that can be used to tell something about the real origin of a spoofed port scan. ¿¹¸¦µé¾î, ¸¸¾à ¿ì¸®°¡ ¹ÞÀº ÆÐŶÀÇ ³¡ºÎºÐ¿¡ IP TTLÀÌ 255¸¦ °¡Áö°í ÀÖ´Ù¸é ¿ì¸®´Â ¹ß½Å Áö ÁÖ¼Ò°¡ ¾îµðµçÁö°£¿¡ ÀÏ´Ü ÀÚ½ÅÀÇ ·ÎÄà ³×Æ®¿öÅ©¿¡¼­ µ¥ÀÌÅÍ°¡ º¸³»Á³´Ù´Â °ÍÀ» ¾Ë¼ö ÀÖ ´Ù. ¸¸¾à TTLÀÌ 250À̶ó¸é ¿ì¸®´Â ¿ÀÁ÷ °ø°ÝÀÚ°¡ 5 hops À̳»¿¡ ÀÖ´Ù´Â °ÍÀ» °¡¸£ÄÑÁÙ¼ö ÀÖ´Ù. Áï, ÀÌ°ÍÀ¸·Î ¿ì¸®´Â Á¤È®È÷ ¾ó¸¶³ª ¸Ö¸® ¶³¾îÁ® ÀÖ´ÂÁö¸¦ ¾Ë ¼ö´Â ¾ø´Â °ÍÀÌ´Ù. For example, if the packets we receive have an IP TTL of 255 at our end, we know for sure that they're being sent from our local network regardless of what the source address field says. However, if TTL is 250, we can only tell that the attacker was no more than 5 hops away, we can't tell how far exactly she was for sure. ¿ì¸®´Â ½ÃÀ۵Ǵ TTL°ú ¼Ò½º Æ÷Æ® ³Ñ¹ö·ÎºÎÅÍ Æ÷Æ® ½ºÄ³³Ê ŸÀÔ(¿¹¸¦µé¾î "stealth" ½ºÄµ)³ª °ø°ÝÀÚ¿¡ ÀÇÇØ »ç¿ëµÇ¾îÁö´Â ¿î¿µÃ¼Á¦(¿¹¸¦µé¾î Full TCP Connection ½ºÄµ)¸¦ ¾Ë·Á ÁÙ ¼ö ÀÖ ´Ù. ±×·¯³ª, ÀÌ°Í ¶ÇÇÑ ¿ì¸®°¡ Àý´ëÀûÀ¸·Î ½Å·ÚÇÒ¸¸ÇÑ Á¤º¸´Â ¸øµÈ´Ù. ¿¹¸¦µé¾î, ¸®´ª½º°¡ TTLÀ» 64·Î ¼³Á¤ÇÏ´Â µ¿¾È nmapÀ¸·Î TTLÀ» 255·Î ¼³Á¤ÇÏ°í, ¹ß½ÅÁö Æ÷Æ® ¹øÈ£¸¦ 49274 ·Î ¼³Á¤ÇÒ¼ö Àֱ⠋š¹®ÀÌ´Ù. Starting TTL and source port number(s) can also give us a hint of what port scanner type (for "stealth" scans) or operating system (for full TCP connection scans) is used by the attacker. We can never be sure though. For example, nmap sets TTL to 255 and source port to 49724, while Linux kernel sets TTL to 64. 4. Information Source (E-box) Choice TCP Æ÷Æ® ½ºÄµ(stealth Æ÷ÇÔ)À» °¨ÁöÇϱâ À§ÇÏ¿© ¿ì¸®´Â RAW IP¿Í TCP ÆÐŶ Çì´õ¿¡ Á¢±ÙÇÒ ¼ö ÀÖ¾î¾ß ÇÑ´Ù. For detecting TCP port scans, including "stealth" ones, we need access to raw IP and TCP packet headers. ³×Æ®¿öÅ©¸¦ ±â¹ÝÀ¸·Î ÇÏ´Â IDS¿¡¼­´Â raw packetÀ» ¾ò±â À§ÇÏ¿© promiscuous ¸ðµå¸¦ »ç¿ëÇÏ ¿©¾ß ÇÑ´Ù. ¿©±â¿¡ °ü·ÃµÈ ¸ðµç ¹®Á¦´Â SNI ¹®¼­¿¡¼­ ¼³¸íÇÏ¿´´Ù. ¾îÂîµÇ¾úµç, ¸ðµç Æ÷Æ® ½º ĵÀ» °¨ÁöÇÏ´Â °ÍÀÌ ºÒ°¡´ÉÇϱ⠋š¹®¿¡ ¶§·Î´Â ÀÌ·¯ÇÑ ¹æ½ÄÀÌ ÀÌ¿Í °°Àº Á¾·ùÀÇ °ø°Ý¿¡´Â »ç¿ëÇÒ¸¸ ÇÏ´Ù. In a network-based IDS, we would use promiscuous mode for obtaining the raw packets. This has all the problems described in the SNI paper: both false positives and false negatives are possible. However, sometimes this might be acceptable for this attack type since it is impossible to detect all port scans anyway. È£½ºÆ®¸¦ ±â¹ÝÀ¸·Î ÇÏ´Â IDS¿¡¼­´Â ÆÐŶÀ» ¾ò´Â 2°¡Áö ¹æ¹ýÀÌ ÀÖ´Ù. TCP ¶Ç´Â IP ¼ÒÄÏ¿¡¼­ Raw µ¥ÀÌÅ͸¦ Àаųª, Ä¿³Î ³»ºÎ¿¡¼­ Á÷Á¢ µ¥ÀÌÅ͸¦ °¡Á®¿À´Â °ÍÀÌ´Ù.(loadable moduleÀ̳ª kenel patch·Î °¡´É) For a host-based IDS, there are two major ways of obtaining the packets: reading from a raw TCP or IP socket, or getting the data directly inside the kernel (via a loadable module or a kernel patch). Raw TCP ¼ÒÄÏÀ» »ç¿ëÇϸé SNI ¿¡¼­ ÁöÀûÇÑ ´ëºÎºÐÀÇ ¹®Á¦´Â Àû¿ëµÇÁö ¾Ê´Â´Ù. ¿ì¸®´Â ÀڽŠÀÇ Ä¿³Î¿¡ ÀÇÇØ ÀνĵǴ ÆÐŶÀ» ¾ò±â¸¸ ÇÏ¸é µÇ´Â °ÍÀÌ´Ù. ¾îÂîµÇ¾úµç, ÀÌ°ÍÀº ¿©ÀüÈ÷ ¼öµ¿ ÀûÀÎ ºÐ¼®(ÆÐŶÀ» ³õÄ¥¼ö Àֱ⠶§¹®)ÀÌ°í, ½ÇÆÐ È®·üÀÌ ÀÖ´Â ½Ã½ºÅÛÀÌ´Ù. ÀÌ°ÍÀº ¿ÀÁ÷ Æ÷Æ® ½ºÄµ¸¸À» À§ÇÑ °ÍÀ̶ó¸é »ç¿ëÇÒ ¼ö´Â ÀÖÁö¸¸, ¸¸¾à ÃßÈÄ¿¡ ´Ù¸¥ °ø°ÝµéÀ» °¨ÁöÇϱâ À§ÇÏ¿© ¼­´Â º°·Î ÁÁÀº ¹æ¹ýÀº ¾Æ´Ï´Ù. ¸¸¾à Raw IP ¼ÒÄÏ¿Ü¿¡ ´Ù¸¥ ¹æ½ÄÀ» »ç¿ëÇÑ´Ù¸é ¿ì¸®´Â "SNI ¹®Á¦Á¡"¿¡ ´ëÇÏ¿©¼­ Á»´õ »ý°¢ÇØ¾ß ÇÑ´Ù. ¾îÂîµÇ¾úµç, ¿ì¸®°¡ ¸¸µå´Â ¿¹Á¦¿¡¼­´Â Raw ¼ÒÄÏ À» »ç¿ëÇÒ °ÍÀÌ´Ù. When using a raw TCP socket, most of the problems pointed out by SNI do not apply: we are only getting the packets recognized by our own kernel. However, this is still passive analysis (we might miss packets) and a fail-open system. While probably acceptable for port scans only, this is not a good design if we later choose to detect other attacks. If we used a raw IP socket instead (some systems don't have raw TCP sockets), we would have more of the "SNI problems" again. Anyway, in my example code, I'm using a raw TCP socket. °¡Àå ½Å·ÚÇÒ ¼ö ÀÖ´Â IDS´Â ½ÇÁ¦ ±¸ÇöÇÏ·Á´Â ½Ã½ºÅÛÀÇ Ä¿³Î·ÎºÎÅÍ ¸î°¡Áö Áö¿øÀ» ¹Þ´Â °Í ÀÌ´Ù. ÀÌ°ÍÀº ¿ä±¸µÇ´Â ¸ðµç Á¤º¸¿¡ ´ëÇÏ¿© Á¢±Ù °¡´ÉÇÏ¿©¾ß ÇÏ°í ¶ÇÇÑ fail-close Àϼöµµ ÀÖ ¾î¾ß ÇÑ´Ù. ºÐ¸íÇÑ ´ÜÁ¡Àº Ä¿³Î ¸ðµâÀ̳ª ÆÐÄ¡¿¡ ´ëÇÑ ÀÌ½Ä °¡´É¼ºÀÌ ¸Å¿ì Àû´Ù´Â °ÍÀÌ´Ù. The most reliable IDS is one with some support from the target systems kernel. This has access to all the required information, and can even be fail-close. The obvious disadvantage is that kernel modules and patches aren't very portable. 5. Attack Signature (A-box) Choice Æ÷Æ® ½ºÄµÀ» °¨ÁöÇϱâ À§ÇÏ¿© »ç¿ëµÇ¾îÁö´Â ¿©·¯°¡Áö ½ÅÈ£µé¿¡ ´ëÇؼ­´Â À§¿¡¼­ ÀÌ¹Ì »ìÆì º¸¾Ò´Ù. ¿ì¸®°¡ ¼±ÅÃÇÒ °ø°Ý½ÅÈ£´Â ¿©ÀüÈ÷ false negatives¸¦ Ÿ´çÇÑ ¹üÀ§ ¾È¿¡¼­ Àû°Ô À¯Áö Çϸ鼭 false negatives¸¦ °¡´ÉÇÑ ÇÑ Àû°Ô À¯ÁöÇÏ´Â °ÍÀÌ´Ù. ¿©±â¼­ ¹°·Ð Ÿ´çÇÑ ¹üÀ§¶ó´Â °Í ÀÌ ºÐ¸íÇÏÁö ¾Ê±â´Â ÇÏÁö¸¸ ¸»ÀÌ´Ù. ³» °ßÇطδ ¿ì¸®°¡ °¨Áö ÇÏ·Á´Â °ø°ÝÀÇ ½É°¢¼º ¿©ºÎ (false negative¿¡ ´ëÇÑ ºñ¿ë)³ª °¨ÁöµÈ °ø°Ý¿¡ ´ëÇÑ Ã³¸® ¹æ¹ý(false positive¿¡ ´ëÇÑ ºñ¿ë)¿¡ µû¶ó °áÁ¤ÀÌ µÇ¾î¾ß ÇÒ °Í °°´Ù. ÀÌ µÎ°¡Áö ºñ¿ëÀº ¸ðµÎ°¡ »çÀÌÆ®¿¡ µû¶ó¼­ ´Þ¶óÁú ¼ö ÀÖ´Ù. ±×·¡¼­ IDS´Â »ç¿ëÀÚ°¡ Æ©´×À» ÇؾßÇÏ´Â °ÍÀÌ´Ù. It has already been mentioned above that different signatures can be used to detect port scans; they differ by numbers of false positives and false negatives. The attack signature that we choose should keep false positives as low as possible while still keeping false negatives reasonably low. It is however not obvious what to consider reasonable. In my opinion, this should depend on the severity of the attack we're detecting (the cost of a false negative), and on the actions taken for a detected attack (the cost of a false positive). Both of these costs can differ from site to site, so an IDS should be user-tunable. scanlogd¸¦ À§ÇÏ¿©¼­ ³ª´Â ´ÙÀ½°ú °°Àº °ø°Ý ½ÅÈ£¸¦ »ç¿ëÇÒ °ÍÀÌ´Ù. "ÃÖ¼ÒÇÑ °°Àº ¹ß½ÅÁö ÁÖ ¼Ò·ÎºÎÅÍ ½ºÄµ µÇ¾îÁö´Âµ¥ ÇÊ¿äÇÑ Æ÷Æ®ÀÇ COUNT(¼ýÀÚ)¿Í Æ÷Æ®°£¿¡ DELAY·Î ¼³Á¤ÇÑ ½Ã°£ ¾È¿¡ ¹ß»ýÇÑ °ø°Ý¿¡ ÇÑÁ¤À» µÐ´Ù. µÎ°¡Áö COUNT¿Í DELAY´Â µÑ´Ù ÀÓÀÇ·Î ¼³Á¤ÀÌ °¡´ÉÇÏ ´Ù. TCP Æ÷Æ®´Â ACK ºñÆ®°¡ ¼³Á¤ÀÌ µÇ¾î ÀÖÁö ¾ÊÀ¸¸é ½ºÄ³´×µÇ´Â °ÍÀ¸·Î °£ÁÖÇÑ´Ù. For scanlogd, I'm using the following attack signature: "at least COUNT ports need to be scanned from the same source address, with no longer than DELAY ticks between ports". Both COUNT and DELAY are configurable. A TCP port is considered to be scanned when receiving a packet without the ACK bit set. 6. Logging the Results (D-box) ¿ì¸®°¡ Çϵ峪, ÇÁ¸°Åͳª, ´Ù¸¥ ½Ã½ºÅÛµî ·Î±×¸¦ ¾î´À°÷¿¡ ±â·ÏÇϵçÁö, ¿ì¸®°¡ °®°í ÀÖ´Â ÀÚ ¿øÀÇ °ø°£Àº ÇÑÁ¤ÀÌ µÇ¾î ÀÖ´Ù. ÀúÀå °ø°£ÀÌ ¾øÀ» ¶§, ¿ì¸®´Â °á°ú¸¦ ÀÒ¾î¹ö¸®°Ô µÉ °ÍÀÌ´Ù. ´ëºÎºÐÀÇ ÀÌ·¯ÇÑ °æ¿ì, ·Î±× µ¥ÀÌÅÍ ÀúÀåÀ» ±×¸¸µÎ´øÁö ¿À·¡µÈ °á°ú µ¥ÀÌÅ͸¦ »õ·Î¿î µ¥ÀÌ ÅÍ·Î ¹Ù²Ù´Â ÀÛ¾÷À» ÇÏ°Ô µÉ °ÍÀÌ´Ù. Regardless of where we write our logs (a disk file, a remote system, or maybe even a printer), our space is limited. When storage is full, results will get lost. Most likely, either the logging stops, or old entries get replaced with newer ones. ÇÑ°¡Áö ºÐ¸íÇÑ °ø°ÝÀº Áß¿äÇÏÁö ¾ÊÀº Á¤º¸¸¦ ·Î±× µ¥ÀÌÅͷΠä¿ö³Ö°Ô ÇÏ´Â °ÍÀÌ´Ù. ±×·±´Ù À½ IDS°¡ Á¦´ë·Î µ¿ÀÛÇÏÁö ¾ÊÀ» ¶§ ºñ·Î¼­ º»°ÝÀûÀÎ °ø°ÝÀ» ÇÏ´Â °ÍÀÌ´Ù. Æ÷Æ® ½ºÄµ ¿¹Á¦ ¿¡¼­, ¹ß½ÅÁö ÁÖ¼Ò°¡ ¹Ù²ï port scanÀº ·Î±× µ¥ÀÌÅÍ¿¡ ÀúÀåÀÌ µÇ°í ½ÇÁ¦ °ø°ÝÀº ÃæºÐÈ÷ ½Ã½º ÅÛÀÌ Á¦´ë·Î µ¿ÀÛÇÏÁö ¾ÊÀº ´ÙÀ½¿¡ ½ÇÁ¦ Æ÷Æ® ½ºÄµÀÌ ÀÌ·ç¾îÁö°Ô µÈ´Ù. ÀÌ ¿¹Á¦´Â Á¦´ë·Î ¸¸µé¾îÁöÁö ¾ÊÀº Æ÷Æ® ½ºÄµ °¨Áö ÅøÀº À§¿¡ ¸»ÇÑ ¹æ¹ý¿¡ ÀÇÇÏ¿© ½±°Ô ·Î±×ÀÎÀ» ÇÇÇÒ¼ö ÀÖ°Ô µÉ °ÍÀÌ´Ù. An obvious attack is to fill up the logs with unimportant information, and then do the real attack with the IDS effectively disabled. For the port scans example, spoofed "port scans" could be used to fill up the logs, and the real attack could be a real port scan, possibly followed by a breakin. This example shows how a badly coded port scan detection tool could be used to avoid logging of the breakin attempt, which would get logged if the tool wasn't running. ÀÌ ¹®Á¦¿¡ ´ëÇÑ ÇÑ°¡Áö ÇØ°á ¹æ¹ýÀº ¾à°£ÀÇ Á¦¾à »çÇ×À» µÎ´Â °ÍÀÌ´Ù. ¿¹¸¦ µé¾î ¸ðµç °ø°Ý ÇüÅ¿¡ ´ëÇÏ¿© °¢°¢ "20Ãʾȿ¡ 5°³ÀÌÇÏÀÇ ¸Þ½ÃÁö¸¦ ÀúÀåÇ϶ó." °í °è¼ÓÀûÀ¸·Î °ø°ÝÀÌ µÇ¸é Àá½Ãµ¿¾È ÀÌ·¯ÇÑ Å¸ÀÔ¿¡ °ü·ÃµÈ °ø°Ý¿¡ ´ëÇؼ­´Â ·Î±× µ¥ÀÌÅ͸¦ ÀúÀåÇÏÁö ¾Ê´Â´Ù. ¹ß½ÅÁö ÁÖ¼Ò°¡ ¹Ù²îÁö ¾Ê´Â °ø°ÝµéÀ» À§ÇÏ¿©¼­, ÀÌ·¯ÇÑ Á¦ÇÑÀ» °ø°Ý Á¾·ùº°´ë½Å¿¡ ¹ß½ÅÁö ÁÖ¼Ò·Î ¹Ù²î¾î¼­ ÀúÀåÇÒ ¼öµµ ÀÖ´Ù. Æ÷Æ® ½ºÄµÀÌ ¹ß½ÅÁö ÁÖ¼Ò¸¦ ¹Ù²Ü¼ö Àֱ⠶§¹®¿¡, °ø°ÝÀÚ°¡ Áö°Å Á¢ ÀÚ½ÅÀÇ ÁÖ¼Ò¸¦ µå·¯³»Áö ¾ÊÀ»¼ö ÀÖÁö¸¸ °ø°ÝÀÚ ÀÚ½ÅÀ» ´Ù¸¥ °ø°Ý ŸÀÔ(Á¦¾à »çÇ×À» µÎÁö ¾Ê¾ÒÀ» ¶§ ±¸ÇöÇÒ ¼ö ÀÖ´Â °ø°Ý¹æ¹ý)À¸·Î ¼û±æ ¼ö´Â ¾ø´Â °ÍÀÌ´Ù. ÀÌ°ÍÀÌ ¹Ù·Î scanlogd¿¡¼­ ±¸ÇöÇÒ·Á°í ÇÏ´Â °ÍÀÌ´Ù. One solution for this problem would be to put rate limits (say, no more than 5 messages per 20 seconds) on every attack type separately, and, when the limit is reached, log this fact, and temporarily stop logging of attacks of this type. For attack types that can't be spoofed, such limits could be put per source address instead. Since port scans can be spoofed, this still lets an attacker not reveal her real address, but this doesn't let her hide another attack type this way, like she could do if we didn't implement the rate limits... that's life. This is what I implemented in scanlogd. ºñ½ÁÇÑ ÀåÁ¡°ú ´ÜÁ¡À» °¡Áö°í ÀÖ´Â ´Ù¸¥ ÇØ°áÃ¥Àº ¸ðµç °ø°ÝÀÚ¿¡ ´ëÇÏ¿©¼­ °¢°¢ ¸Þ½ÃÁö °ø °£À» µû·Î ÇÒ´çÇÏ´Â °ÍÀÌ´Ù. ÀÌ·¯ÇÑ ÇØ°á ¹æ¹ýµé ¸ðµÎ µ¿½Ã¿¡ ±¸ÇöÇÒ ¼ö ÀÖ´Ù. Another solution, which has similar advantages and disadvantages, is to allocate space for messages from every attack type separately. Both of these solutions can be implemented simultaneously. 7. What To Do About Port Scans? (R-box) ¸î°¡ÁöÀÇ IDS´Â À̵éÀÌ °¨ÁöÇس½ °ø°Ý¿¡ ´ëÇÏ¿©¼­ ´ëÀÀ ÇÒ ¼ö ÀÖ´Â ´É·ÂÀ» °¡Áö°í ÀÖ´Ù. ÀÌ ´É·ÂÀº ¾ÕÀ¸·ÎÀÇ °ø°ÝÀ» ¸·°Å³ª °ø°ÝÀÚ¿¡ ´ëÇÏ¿©¼­ ´Ù¸¥ ºÎ°¡ÀûÀÎ Á¤º¸¸¦ Ãß°¡·Î ¾òÀ» ¼ö ÀÖµµ·Ï ÇØÁØ´Ù. ºÒÇàÈ÷µµ ÀÌ·¯ÇÑ ±â´ÉµéÀº ¶Ù¾î³­ °ø°ÝÀÚ¿¡ ÀÇÇÏ¿©¼­ ³²¿ëµÇ¾îÁú ¼ö ÀÖ ´Ù. Some IDS are capable of responding to attacks they detect. The actions are usually directed to prevent further attacks and/or to obtain extra information about the attacker. Unfortunately, these features can often be abused by a smart attacker. °ø°Ý¿¡ ´ëÇÑ ÀϹÝÀûÀÎ ´ëÀÀÀº °ø°Ý È£½ºÆ®·ÎºÎÅÍ µ¥ÀÌÅ͸¦ ºí·Ï½ÃÅ°´Â °ÍÀÌ´Ù. ¿¹¸¦ µé¸é firewall·ÎºÎÅÍ Á¢±Ù °¡´ÉÇÑ ¸ñ·ÏÀ» °íÄ£´ÙµçÁö ¾Æ´Ï¸é ÀÌ¿Í À¯»çÇÑ ÀÛ¾÷À» ÇÏ´Â °ÍÀÌ´Ù. ¸¸ ¾à ¿ì¸®°¡ °¨ÁöÇÑ °ø°ÝÀÚ°¡ ¹ß½ÅÁö ÁÖ¼Ò¸¦ ¹Ù²Ü ¼ö ÀÖ´Ù¸é ÀÌ°ÍÀº ºÐ¸íÈ÷ DoS(Denial of Service) °ø°ÝÀ» °¡´ÉÇÏ°Ô ÇÑ´Ù. ±×·¯³ª ÀÌ ¹æ¹ýÀº ºÐ¸íÈ÷ ¹ß½ÅÁö ÁÖ¼Ò¸¦ ¹Ù²ÙÁö ¸øÇÏ´Â °ø °Ý¿¡¼­´Â DoS °ø°ÝÀ» °¡´ÉÇÏ°Ô²û ¸¸µéÁö´Â ¸øÇÑ´Ù. ¿Ö³ÄÇÏ¸é ¶§¶§·Î IP ÁÖ¼Ò´Â ´Ù¸¥ ¸¹Àº »ç¶÷°ú °øÀ¯(¿¹¸¦ µé¾î Ineternet ISP¸¦ »ç¿ëÇÏ´Â »ç¶÷µéÀº µ¿Àû ÁÖ¼Ò ÇÒ´ç ¹æ¹ýÀ» »ç¿ëÇϱ⠶§¹®¿¡ IP¸¦ °øÀ¯ÇÒ ¼ö ÀÖ´Ù.)ÇÒ¼öµµ Àֱ⠶§¹®ÀÌ´Ù. A typical action is to block the attacking host (re-configuring access lists of the firewall, or similar). This leads to an obvious Denial of Service (DoS) vulnerability if the attack we're detecting is spoofable (like a port scan is). It is probably less obvious that this leads to DoS vulnerabilities for non-spoofable attack types, too. That's because IP addresses are sometimes shared between many people; this is the case for ISP shell servers and dynamic dialup pools. ¶ÇÇÑ, ÀÌ·¯ÇÑ ¹æ¹ýÀ» »ç¿ëÇßÀ» ¶§ ÀÚ¿øÀÚü°¡ ÇÑÁ¤µÇ¾î ÀÖ´Â Å©±â¸¦ °®°í ÀÖ´Â ¸ðµç °Í (firewall ¸ñ·Ï Á¢±Ù, ¶ó¿ìÆà Å×À̺íµî)¿¡ ´ëÇÏ¿© ¸î °¡Áö ±¸Çö»óÀÇ ¹®Á¦Á¡ÀÌ ÀÖ´Ù. ¶ÇÇÑ ÀÚ¿ø ÀÇ ÇÑ°è¿¡ µµÂøÇϱâ Àü¿¡, CPU »ç¿ë·ü¿¡ ´ëÇÑ ¹®Á¦°¡ ÀÖ´Ù. ¸¸¾à IDS°¡ ÀÌ·¯ÇÑ ¹®Á¦¸¦ ¹« ½ÃÇÏ°Ô µÇ¸é, ÀÌ°ÍÀº ¹Ù·Î Àüü ³×Æ®¿öÅ©¿¡ ´ëÇÑ DoS°ø°ÝÀ» °¡´ÉÇÏ°Ô ¸¸µå´Â ¿äÀÎÀÌ µÈ ´Ù.(Áï, firewallÀÌ Á¡Á¡ ´Ù¿îµÇ´Â °ÍÀÌ´Ù.) There are also a few implementation problems with this approach: firewall access lists, routing tables, etc... are all of a limited size. Also, even before the limit is reached, there are CPU usage issues. If an IDS is not aware of these issues, this can lead to DoS of the entire network (say, if the firewall goes down). ³» °ßÇطδÂ, ¿ÀÁ÷ ±Ø¼Ò¼ÒÀÇ °æ¿ì¸¸ÀÌ ÀÌ·¯ÇÑ ÀÏÀ» ¹ß»ý ½Ãų °¡´É¼ºÀÌ ÀÖ´Ù. ÀÌÁß Æ÷Æ® ½º ĵ °ø°ÝÀº ÀÌ·¯ÇÑ ¹®Á¦¸¦ Àý´ë·Î ¹ß»ý½Ãų ¼ö ¾ø´Â Á¾·ùÀÇ °ø°ÝÀÌ´Ù. In my opinion, there're only very few cases in which such an action might be justified. Port scans are definitely not among those. ´Ù¸¥ ÀϹÝÀûÀÎ ´ëÀÀÃ¥Àº ´Ù¸¥ Á¤º¸¸¦ ¾ò±â À§ÇÏ¿© °Å²Ù·Î »ó´ë¹æÀÇ È£½ºÆ®¿¡ Á¢±ÙÀ» ½ÃµµÇÏ ´Â °ÍÀÌ´Ù. ¹ß½ÅÁö ÁÖ¼Ò¸¦ ¹Ù²Ù´Â °ø°ÝÀ» ´ëÇÏ¿©¼­, ¹æ¾îÀÚ°¡ ¸¶Áö¸·¿¡ °¡¼­´Â ÀÌ Á¤º¸¸¦ ´Ù ¸¥ È£½ºÆ®¿¡ ´ëÇÑ °ø°ÝÀ» Çϴµ¥ »ç¿ëÇÒ ¼ö µµ ÀÖ°Ô µÈ´Ù. ÀÌ°ÍÀº °ð ¾Æ¹«°Íµµ ÇÏÁö ¾Ê´Â °Íº¸´Ù´Â ÈξÀ ÁÁ´Ù. Another common action is to connect back to the attacking host to obtain extra information. For spoofable attacks, we might end up being used in attacking a third-party. We'd better not do anything for such attacks, including port scans. ¾îÂîµÇ¾úµç ¹ß½ÅÁö ÁÖ¼Ò¸¦ ¹Ù²ÙÁö ¸øÇÏ´Â °ø°Ý¿¡¼­´Â ¸î°¡Áö °æ¿ì¿¡ ¸¹Àº Á¶±â °æº¸¿Í ÇÔ²² ±¸ÇöÇÒ ¸¸ÇÑ °¡Ä¡¼ºÀ» °¡Áö°í ÀÖ´Ù. ÁÖ·Î ¿ì¸®´Â ³×Æ®¿öÅ© ´ë¿ªÆø, CPU Á¡À¯·ü, ¸Þ¸ð¸®µî ¸¹ Àº ÀÚ¿øÀ» ¼ÒºñÇÏÁö ¾Êµµ·Ï ÁÖÀÇÇÏ¿©¾ß ÇÑ´Ù. ºÐ¸íÈ÷, ÀÌ°ÍÀº °ø°ÝÀÚ·Î ÇÏ¿©±Ý ¸î °¡Áö ¿äû ¿¡ ´ëÇÏ¿©¼­´Â ½ÇÆÐÇÒ ¼ö ÀÖµµ·Ï ¸¸µé ¼ö ÀÖÀ¸³ª ÀÌ°Í ¿Ü¿¡´Â ¿ì¸®°¡ ÇÒ ¼ö ÀÖ´Â °ÍÀÌ ¾ø´Ù ´Â °ÍÀ» ÀǹÌÇÑ´Ù. However, for non-spoofable attacks, this might be worth implementing in some cases, with a lot of precautions. Mainly, we should be careful not to consume too many resources, including bandwidth (should limit request rate regardless of the attack rate, and limit the data size), CPU time, and memory (should have a timeout, and limit the number of requests that we do at a time). Obviously, this means that an attacker can still make some of the requests fail, but there's nothing we can do here. ÀÌ ¹®Á¦¿¡ °ü·ÃµÇ¼­´Â ftp://ftp.win.tue.nl/pub/security/murphy.ps.gz ¹®¼­¸¦ Âü°íÇϱ⠹ٶõ´Ù. Wietse Venea¿¡ ÀÇÇØ ÀÛ¼ºµÈ ÀÌ ¹®¼­´Â ±×ÀÇ À¯¸íÇÑ TCP wrapper ÆÐÅ°ÁöÀÇ ±¸ ¹öÀü¿¡ ÀÖ´Â ³»¿ë°ú À¯»çÇÏ°Ô °ø°Ý ´çÇϱ⠽¬¿î ½Ã½ºÅÛ¿¡ ´ëÇÏ¿© ÀÚ¼¼È÷ ¼³¸íÇÏ°í ÀÖ´Ù. See ftp://ftp.win.tue.nl/pub/security/murphy.ps.gz for an example of the issues involved. This paper by Wietse Venema details similar vulnerabilities in older versions of his famous TCP wrapper package. ÀÌ·¯ÇÑ ÀÌÀ¯·Î, scanlogd´Â Æ÷Æ® ½ºÄµ¿¡ ´ëÇÑ ·Î±× µ¥ÀÌÅ͸¦ ÀúÀåÇÏ´Â °Í ¿Ü¿¡´Â ¾Æ¹«°Íµµ ÇÏÁö ¾Ê´Â´Ù. For these reasons, scanlogd doesn't do anything but log port scans. You should probably take action yourself. What exactly you do is a matter of taste; I personally only check my larger logs (that I'm not checking normally) for activity near the port scan time. 8. ÀÚ·á ±¸Á¶¿Í ¾Ë°í¸®Áò ¼±Åà ÀϹÝÀûÀÎ ÀÀ¿ë ÇÁ·Î±×·¥À» À§ÇÏ¿©¼­ µ¥ÀÌÅ͸¦ ¼ÒÆ®ÇÏ°í ÀڷḦ ã´Â ¾Ë°í¸®ÁòÀ» ¼±ÅÃÇÒ ¶§ »ç¶÷µéÀº ÀϹÝÀûÀ¸·Î ƯÁ¤ °æ¿ì¿¡ ÃÖÀûÈ­¸¦ ½ÃŲ´Ù. ¾îÂîµÇ¾úµç, IDS¸¦ À§ÇÏ¿©¼­ ÃÖ¾ÇÀÇ °æ ¿ì¸¦ À§ÇÑ ½Ã³ª¸®¿À¸¦ Ç×»ó °í·ÁÇؾ߸¸ ÇÑ´Ù. Áï, °ø°ÝÀÚ°¡ ¹æ¾îÇÏ´Â ÇÁ·Î±×·¥ÀÌ ÁÁ¾ÆÇÏ´Â µ¥ÀÌÅ͸¸ °ñ¶ó¼­ ÁØ´Ù°í °¡Á¤À» ÇÏÀÚ. ¸¸¾à IDS°¡ ÀÌ·¯ÇÑ °ÍÀ» °í·ÁÇÏ¿©¼­ ¸¸µé¾îÁ³´Ù¸é ÀÌ ½Ã½ºÅÛÀº ¹®Á¦°¡ µÇ´Â µ¥ÀÌÅ͸¦ ¹«½Ã ÇÒ ¼ö ÀÖÁö¸¸, ±×·¸Áö ¾ÊÀº °æ¿ì¿¡´Â DoS °ø°ÝÀ» ÇÏ°Ô ÇÏ´Â ºô¹Ì¸¦ ÁÖ°Ô µÇ´Â °ÍÀ̱⠶§¹®ÀÌ´Ù.(7¿¡¼­ ¾ð±ÞÇÏ¿´´ø ¹®Á¦Á¡.) When choosing a sorting or data lookup algorithm to be used for a normal application, people are usually optimizing the typical case. However, for IDS the worst case scenario should always be considered: an attacker can supply our IDS with whatever data she likes. If the IDS is fail-open, she would then be able to bypass it, and if it's fail-close, she could cause a DoS for the entire protected system. ÀÌ°ÍÀ» ¿¹Á¦¸¦ ÅëÇÏ¿©¼­ »ìÆ캸ÀÚ. scanlogd¿¡¼­ ³ª´Â ¹ß½ÅÁö ÁÖ¼Ò µ¥ÀÌÅ͸¦ ã±â À§ÇÏ¿©¼­ Çؽ¬ Å×À̺íÀ» »ç¿ëÇß´Ù. ÀÌ ¹æ½ÄÀº ÀϹÝÀûÀ¸·Î Çؽ¬ Å×À̺í Å©±â°¡ ÃæºÐÈ÷(?) Å©´Ù¸é Á¦´ë ·Î Àß µ¿ÀÛÇÑ´Ù. Æò±ÕÀûÀ¸·Î µ¥ÀÌÅ͸¦ ã´Â ½Ã°£Àº ÀÌÁø Ž»ö(binary search)º¸´Ù´Â ºü¸£´Ù. ¾îÂîµÇ¾úµç, °ø°ÝÀÚ´Â Çؽ¬ Ãæµ¹À» ÀÏÀ¸Å°±â À§ÇÏ¿©¼­ ÀÚ½ÅÀÇ ÁÖ¼Ò¸¦ ¼±º°ÇÏ¿©¼­ º¸³¾ ¼ö ÀÖ´Ù. À̶§´Â Çؽ¬ Å×ÀÌºí °Ë»öÀ» ¼±Çü Ž»öÀ¸·Î ¹Ù²Ù´Â°Ô È¿°úÀûÀÌ´Ù. ¾ó¸¶³ª ¸¹Àº µ¥ÀÌÅÍ ¸¦ À¯ÁöÇϴ°¡¿¡ µû¶ó¼­ scanlogd ÇÁ·Î±×·¥ÀÌ Á¦½Ã°£¿¡ »õ·Î¿î ÆÐŶ µ¥ÀÌÅ͸¦ °¡Á®¿Ã ¼ö ÀÖ ´ÂÁö ¾ø´ÂÁö¸¦ ¾Ë ¼ö ÀÖ°Ô µÈ´Ù. ÀÌ°ÍÀº ¶ÇÇÑ È£½ºÆ®¸¦ ±â¹ÝÀ¸·Î ÇÏ´Â IDS¿¡¼­ ´Ù¸¥ ÇÁ·Î ¼¼½º°¡ »ç¿ëÇÒ ½Ã°£À» CPU·ÎºÎÅÍ »©¾Ñ¾Æ°¡°Ô µÉ °ÍÀÌ´Ù. Let me illustrate this by an example. In scanlogd, I'm using a hash table to lookup source addresses. This works very well for the typical case as long as the hash table is large enough (since the number of addresses we keep is limited anyway). The average lookup time is better than that of a binary search. However, an attacker can choose her addresses (most likely spoofed) to cause hash collisions, effectively replacing the hash table lookup with a linear search. Depending on how many entries we keep, this might make scanlogd not be able to pick new packets up in time. This will also always take more CPU time from other processes in a host-based IDS like scanlogd. ³ª´Â ÀÌ ¹®Á¦¸¦ Çؽ¬ Ãæµ¹ Ƚ¼ö¸¦ Á¦ÇѵÒÀ¸·Î ÇØ°áÀ» ÇÏ¿´°í °°Àº Çؽ¬ Å×À̺íÀÌ Á¦¾à »çÇ× ¿¡ µµ´ÞÇÏ¿´À» ¶§ °°Àº Çؽ¬ °ª¿¡ ´ëÇÑ µ¥ÀÌÅÍ´Â ¿À·¡µÈ µ¥ÀÌÅ͸¦ ¹ö¸®´Â ¹æ½ÄÀ» ÅÃÇÏ¿´´Ù. ÀÌ ¹æ½ÄÀº Æ÷Æ® ½ºÄµ¿¡¼­ »ç¿ëÇÒ ¸¸ Çϳª ´Ù¸¥ °ø°ÝÀ» °¨ÁöÇϴµ¥´Â Àû´çÇÏÁö ¾ÊÀ» ¼ö ÀÖ´Ù. ¸¸¾à ´Ù¸¥ °ø°Ý Á¾·ù¿¡ ´ëÇÏ¿©¼­µµ Áö¿øÇϱ⸦ ¿øÇÑ´Ù¸é Çؽ¬ Å×ÀÌºí¸»°í ´Ù¸¥ ¹æ¹ýÀÇ ¾Ë°í ¸®ÁòÀ» »ç¿ëÇÏ¿©¾ß ÇÒÁöµµ ¸ð¸¥´Ù. I've solved this problem by limiting the number of hash collisions, and discarding the oldest entry with the same hash value when the limit is reached. This is acceptable for port scans (remember, we can't detect all scans anyway), but might not be acceptable for detecting other attacks. If we were going to support some other attack type also, we would have to switch to a different algorithm instead, like a binary search. ¸¸¾à libc·ÎºÎÅÍ malloc(3)À̳ª free(3) °°Àº ¸Þ¸ð¸® °ü¸®ÀÚ¸¦ »ç¿ëÇÏ¸é °ø°ÝÀÚ´Â ÀÌ ÇÔ¼ö°¡ °®°í ÀÖ´Â Ãë¾àÁ¡À» ÀÌ¿ëÇÏ¿© °ø°ÝÀ» ÇÏ°Ô µÉ °ÍÀÌ´Ù. ÀÌ°ÍÀº CPU »ç¿ë·ü »Ó¸¸ ¾Æ´Ï¶ó ¸Þ ¸ð¸® °ü¸®¸¦ È¿À²ÀûÀ¸·Î ¸øÇؼ­(¿¹¸¦ µé¸é garbage collection) ¹ß»ýÇÏ´Â ¸Þ¸ð¸® ´©¼öÇü»óÀÌ ¹ß»ýÇÒ °ÍÀÌ´Ù. ½Å·ÚÇÒ¸¸ÇÑ IDS´Â ÀÚ±â ÀÚ½ÅÀÌ Á÷Á¢ ¸Þ¸ð¸® °ü¸®¸¦ ÇØ¾ß ÇÏ¸ç ¸Þ¸ð¸® ÇÒ´ç À» ÇÒ ¶§ ¸Å¿ì ÁÖÀǸ¦ ±â¿ï¿©¾ß ÇÑ´Ù. scanlogd¶ó´Â °£´ÜÇÑ ÅøÀ» À§Çؼ­ ³ª´Â µ¿Àû ¸Þ¸ð¸® ÇÒ´çÀ» ÀüÇô »ç¿ëÇÏÁö ¾Ê±â·Î °áÁ¤Çß´Ù. If we're using a memory manager (such as malloc(3) and free(3) from our libc), an attacker might be able to exploit its weaknesses in a similar way. This might include CPU usage issues and memory leaks because of not being able to do garbage collection efficiently enough. A reliable IDS should have its very own memory manager (the one in libc can differ from system to system), and be extremely careful with its memory allocations. For a tool as simple as scanlogd is, I simply decided not to allocate any memory dynamically at all. À§ ³»¿ëÀº ¿î¿µÃ¼Á¦ Ä¿³Î °°Àº ¿ëµµ¿¡µµ Àû¿ëµÉ ¼ö ÀÖ´Â °ÍÀ¸·Î ¾ð±ÞÀÇ °ª¾îÄ¡¸¦ °¡Áö°í ÀÖ ´Ù°í »ý°¢ÇÑ´Ù. ¿¹¸¦ µé¾î, Çؽ¬ Å×À̺íÀº ÇöÀç ¿¬°áµÇ¾î ÀÖ´Â °ÍÀ̳ª listening Æ÷Æ®µîÀ» ã ±â À§ÇÏ¿© ±¤¹üÀ§ÇÏ°Ô »ç¿ëµÇ¾îÁø´Ù. ÀϹÝÀûÀ¸·Î ½ÇÁ¦ À§Çè¿¡ ³ëÃâµÇÁö ¾Ê´Â ´Ù¸¥ ÇÑ°è°¡ ÀÖÀ» ¼ö ÀÖÀ¸³ª ÀÌ°ÍÀº ´õ¿í ´õ ¸¹Àº ¿¬±¸°¡ ÇÊ¿äÇÏ´Ù. It is probably worth mentioning that similar issues also apply to things like operating system kernels. For example, hash tables are widely used there for looking up active connections, listening ports, etc. There're usually other limits which make these not really dangerous though, but more research might be needed. 9. IDS and Other Processes ÀϹÝÀûÀÎ ¸ñÀûÀ¸·Î »ç¿ëµÇ´Â ¿î¿µÃ¼Á¦¿¡ ¼³Ä¡µÇ¾îÁø ³×Æ®¿öÅ©¸¦ ±â¹ÝÀ¸·Î ÇÏ´Â IDS³ª ¸ðµç È£½ºÆ®¸¦ ±â¹ÝÀ¸·Î ÇÏ´Â IDSµçÁö ½Ã½ºÅÛÀÇ ³ª¸ÓÁö ºÎºÐ(´Ù¸¥ ÇÁ·Î¼¼¼­µé, Ä¿³Î Æ÷ÇÔ)°ú IDS °£¿¡¼­ ¸î °¡ÁöÀÇ »óÈ£ÀÛ¿ëÀÌ ÀÖ´Ù. For network-based IDS that are installed on a general-purpose operating system, and for all host-based IDS, there's some interaction of the IDS with the rest of the system, including other processes and the kernel. ¿î¿µÃ¼Á¦ ¾È¿¡¼­ ¸î °¡ÁöÀÇ DoS ³ëÃâÀº °ø°ÝÀÚ·Î ÇÏ¿©±Ý ¾Æ¹«·± ÈçÀûµµ ³²±âÁö ¾Ê°í IDS ¸¦ »ç¿ë ¸øÇÏ°Ô ÇÒ ¼ö ÀÖµµ·Ï ¸¸µé ¼ö ÀÖ´Ù. ÀÌ°ÍÀº Ä¿³Î("teardrop"°ú °°Àº °Í)À̳ª ´Ù¸¥ ÇÁ ·Î¼¼¼­µé(UDP ¼­ºñ½º°¡ ¿¬°á Á¦ÇÑÀ̳ª ´Ù¸¥ ÀÚ¿ø¿¡ ´ëÇÑ Á¦ÇÑ ¾øÀÌ inetd¾È¿¡¼­ ½ÇÇàµÇ´Â °Í) ¾È¿¡¼­ °ø°ÝÇÒ ¼ö ÀÖ´Â ¿©Áö¸¦ µÒÀ¸·Î ÀÎÇؼ­ ½ÇÁ¦µµ °ø°ÝÀ» ´çÇÒ ¼ö ÀÖ´Ù. Some DoS vulnerabilities in the operating system might allow an attacker to disable the IDS (of course, only if it is fail-open) without it ever noticing. This can be done via vulnerabilities in both the kernel (like "teardrop") and in other processes (like having a UDP service enabled in inetd without a connection count limit and any resource limits). À¯»çÇÏ°Ô È£½º¸¦ ±â¹ÝÀ¸·Î ÇÏ´Â IDS°¡ Á¦´ë·Î ÄÚµùÀ» ÇÏÁö ¸øÇÑ´Ù¸é DoS °°Àº °ø°ÝÀ» ´ç ÇÒ ¼ö ÀÖ´Ù. Similarly, a poorly coded host-based IDS can be used for DoS attacks on other processes of the "protected" system. 10. Example Code ¸¶Áö¸·À¸·Î ¿©±â¿¡ ¸®´ª½º¸¦ À§ÇÑ scanlogd°¡ ÀÖ´Ù. ÀÌ°ÍÀº ´Ù¸¥ ½Ã½ºÅÛ¿¡¼­µµ ¿ª½Ã ÄÄÆÄÀÏ ÇÒ ¼ö ÀÖÀ¸³ª Raw TCP ¼ÒÄÏÀÌ ¾øÀ¸¹Ç·Î ÀÎÇØ Á¤»óÀûÀ¸·Î µ¿ÀÛÀ» ÇÏÁö ¾ÊÀ» ¼ö ÀÖ´Ù. ÃßÈÄ ¹öÀüÀ» À§Çؼ­´Â http://www.false.com/security/scanlogd/ »çÀÌÆ®¸¦ Âü°íÇϱ⠹ٶõ´Ù. Finally, here you get scanlogd for Linux. It may compile on other systems too, but will likely not work because of the lack of raw TCP sockets. For future versions see http://www.false.com/security/scanlogd/. NOTE THAT SOURCE ADDRESSES REPORTED CAN BE SPOOFED, DON'T TAKE ANY ACTION AGAINST THE ATTACKER UNLESS OTHER EVIDENCE IS AVAILABLE. <++> Scanlogd/scanlogd.c /* * Linux scanlogd v1.0 by Solar Designer. You're allowed to do whatever you * like with this software (including re-distribution in any form, with or * without modification), provided that credit is given where it is due, and * any modified versions are marked as such. There's absolutely no warranty. */ #include #include #include #include #include #include #include #include #include #include #include #include #if (linux) #define __BSD_SOURCE #endif #include #include #include /* * Port scan detection thresholds: at least COUNT ports need to be scanned * from the same source, with no longer than DELAY ticks between ports. */ #define SCAN_COUNT_THRESHOLD 10 #define SCAN_DELAY_THRESHOLD (CLK_TCK * 5) /* * Log flood detection thresholds: temporarily stop logging if more than * COUNT port scans are detected with no longer than DELAY between them. */ #define LOG_COUNT_THRESHOLD 5 #define LOG_DELAY_THRESHOLD (CLK_TCK * 20) /* * You might want to adjust these for using your tiny append-only log file. */ #define SYSLOG_IDENT "scanlogd" #define SYSLOG_FACILITY LOG_DAEMON #define SYSLOG_LEVEL LOG_ALERT /* * Keep track of up to LIST_SIZE source addresses, using a hash table of * HASH_SIZE entries for faster lookups, but limiting hash collisions to * HASH_MAX source addresses per the same hash value. */ #define LIST_SIZE 0x400 #define HASH_LOG 11 #define HASH_SIZE (1 << HASH_LOG) #define HASH_MAX 0x10 /* * Packet header as read from a raw TCP socket. In reality, the TCP header * can be at a different offset; this is just to get the total size right. */ struct header { struct ip ip; struct tcphdr tcp; char space[60 - sizeof(struct ip)]; }; /* * Information we keep per each source address. */ struct host { struct host *next; /* Next entry with the same hash */ clock_t timestamp; /* Last update time */ time_t start; /* Entry creation time */ struct in_addr saddr, daddr; /* Source and destination addresses */ unsigned short sport; /* Source port, if fixed */ int count; /* Number of ports in the list */ unsigned short ports[SCAN_COUNT_THRESHOLD - 1]; /* List of ports */ unsigned char flags_or; /* TCP flags OR mask */ unsigned char flags_and; /* TCP flags AND mask */ unsigned char ttl; /* TTL, if fixed */ }; /* * State information. */ struct { struct host list[LIST_SIZE]; /* List of source addresses */ struct host *hash[HASH_SIZE]; /* Hash: pointers into the list */ int index; /* Oldest entry to be replaced */ } state; /* * Convert an IP address into a hash table index. */ int hashfunc(struct in_addr addr) { unsigned int value; int hash; value = addr.s_addr; hash = 0; do { hash ^= value; } while ((value >>= HASH_LOG)); return hash & (HASH_SIZE - 1); } /* * Log this port scan. */ void do_log(struct host *info) { char s_saddr[32]; char s_daddr[32 + 8 * SCAN_COUNT_THRESHOLD]; char s_flags[8]; char s_ttl[16]; char s_time[32]; int index, size; unsigned char mask; /* Source address and port number, if fixed */ snprintf(s_saddr, sizeof(s_saddr), info->sport ? "%s:%u" : "%s", inet_ntoa(info->saddr), (unsigned int)ntohs(info->sport)); /* Destination address, if fixed */ size = snprintf(s_daddr, sizeof(s_daddr), info->daddr.s_addr ? "%s ports " : "ports ", inet_ntoa(info->daddr)); /* Scanned port numbers */ for (index = 0; index < info->count; index++) size += snprintf(s_daddr + size, sizeof(s_daddr) - size, "%u, ", (unsigned int)ntohs(info->ports[index])); /* TCP flags: lowercase letters for "always clear", uppercase for "always * set", and question marks for "sometimes set". */ for (index = 0; index < 6; index++) { mask = 1 << index; if ((info->flags_or & mask) == (info->flags_and & mask)) { s_flags[index] = "fsrpau"[index]; if (info->flags_or & mask) s_flags[index] = toupper(s_flags[index]); } else s_flags[index] = '?'; } s_flags[index] = 0; /* TTL, if fixed */ snprintf(s_ttl, sizeof(s_ttl), info->ttl ? ", TTL %u" : "", (unsigned int)info->ttl); /* Scan start time */ strftime(s_time, sizeof(s_time), "%X", localtime(&info->start)); /* Log it all */ syslog(SYSLOG_LEVEL, "From %s to %s..., flags %s%s, started at %s", s_saddr, s_daddr, s_flags, s_ttl, s_time); } /* * Log this port scan unless we're being flooded. */ void safe_log(struct host *info) { static clock_t last = 0; static int count = 0; clock_t now; now = info->timestamp; if (now - last > LOG_DELAY_THRESHOLD || now < last) count = 0; if (++count <= LOG_COUNT_THRESHOLD + 1) last = now; if (count <= LOG_COUNT_THRESHOLD) { do_log(info); } else if (count == LOG_COUNT_THRESHOLD + 1) { syslog(SYSLOG_LEVEL, "More possible port scans follow.\n"); } } /* * Process a TCP packet. */ void process_packet(struct header *packet, int size) { struct ip *ip; struct tcphdr *tcp; struct in_addr addr; unsigned short port; unsigned char flags; struct tms buf; clock_t now; struct host *current, *last, **head; int hash, index, count; /* Get the IP and TCP headers */ ip = &packet->ip; tcp = (struct tcphdr *)((char *)packet + ((int)ip->ip_hl << 2)); /* Sanity check */ if ((char *)tcp + sizeof(struct tcphdr) > (char *)packet + size) return; /* Get the source address, destination port, and TCP flags */ addr = ip->ip_src; port = tcp->th_dport; flags = tcp->th_flags; /* We're using IP address 0.0.0.0 for a special purpose here, so don't let * them spoof us. */ if (!addr.s_addr) return; /* Use times(2) here not to depend on someone setting the time while we're * running; we need to be careful with possible return value overflows. */ now = times(&buf); /* Do we know this source address already? */ count = 0; last = NULL; if ((current = *(head = &state.hash[hash = hashfunc(addr)]))) do { if (current->saddr.s_addr == addr.s_addr) break; count++; if (current->next) last = current; } while ((current = current->next)); /* We know this address, and the entry isn't too old. Update it. */ if (current) if (now - current->timestamp <= SCAN_DELAY_THRESHOLD && now >= current->timestamp) { /* Just update the TCP flags if we've seen this port already */ for (index = 0; index < current->count; index++) if (current->ports[index] == port) { current->flags_or |= flags; current->flags_and &= flags; return; } /* ACK to a new port? This could be an outgoing connection. */ if (flags & TH_ACK) return; /* Packet to a new port, and not ACK: update the timestamp */ current->timestamp = now; /* Logged this scan already? Then leave. */ if (current->count == SCAN_COUNT_THRESHOLD) return; /* Update the TCP flags */ current->flags_or |= flags; current->flags_and &= flags; /* Zero out the destination address, source port and TTL if not fixed. */ if (current->daddr.s_addr != ip->ip_dst.s_addr) current->daddr.s_addr = 0; if (current->sport != tcp->th_sport) current->sport = 0; if (current->ttl != ip->ip_ttl) current->ttl = 0; /* Got enough destination ports to decide that this is a scan? Then log it. */ if (current->count == SCAN_COUNT_THRESHOLD - 1) { safe_log(current); current->count++; return; } /* Remember the new port */ current->ports[current->count++] = port; return; } /* We know this address, but the entry is outdated. Mark it unused, and * remove from the hash table. We'll allocate a new entry instead since * this one might get re-used too soon. */ if (current) { current->saddr.s_addr = 0; if (last) last->next = last->next->next; else if (*head) *head = (*head)->next; last = NULL; } /* We don't need an ACK from a new source address */ if (flags & TH_ACK) return; /* Got too many source addresses with the same hash value? Then remove the * oldest one from the hash table, so that they can't take too much of our * CPU time even with carefully chosen spoofed IP addresses. */ if (count >= HASH_MAX && last) last->next = NULL; /* We're going to re-use the oldest list entry, so remove it from the hash * table first (if it is really already in use, and isn't removed from the * hash table already because of the HASH_MAX check above). */ /* First, find it */ if (state.list[state.index].saddr.s_addr) head = &state.hash[hashfunc(state.list[state.index].saddr)]; else head = &last; last = NULL; if ((current = *head)) do { if (current == &state.list[state.index]) break; last = current; } while ((current = current->next)); /* Then, remove it */ if (current) { if (last) last->next = last->next->next; else if (*head) *head = (*head)->next; } /* Get our list entry */ current = &state.list[state.index++]; if (state.index >= LIST_SIZE) state.index = 0; /* Link it into the hash table */ head = &state.hash[hash]; current->next = *head; *head = current; /* And fill in the fields */ current->timestamp = now; current->start = time(NULL); current->saddr = addr; current->daddr = ip->ip_dst; current->sport = tcp->th_sport; current->count = 1; current->ports[0] = port; current->flags_or = current->flags_and = flags; current->ttl = ip->ip_ttl; } /* * Hmm, what could this be? */ int main() { int raw, size; struct header packet; /* Get a raw socket. We could drop root right after that. */ if ((raw = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) { perror("socket"); return 1; } /* Become a daemon */ switch (fork()) { case -1: perror("fork"); return 1; case 0: break; default: return 0; } signal(SIGHUP, SIG_IGN); /* Initialize the state. All source IP addresses are set to 0.0.0.0, which * means the list entries aren't in use yet. */ memset(&state, 0, sizeof(state)); /* Huh? */ openlog(SYSLOG_IDENT, 0, SYSLOG_FACILITY); /* Let's start */ while (1) if ((size = read(raw, &packet, sizeof(packet))) >= sizeof(packet.ip)) process_packet(&packet, size); } <-->