Here is the same content in en-us.
From the April 2002 issue of MSDN Magazine
Track and Report Server Attacks Quickly and Easily with the .NET Networking Classes
|G. Andrew Duthie|
|This article assumes you're familiar with Visual Basic .NET, ASP.NET, and IIS|
|Level of Difficulty 1 2 3 |
|Download the code for this article: NetClass.exe (39KB) |
|SUMMARY To help stop the spread of worms, viruses, and other hostile activity, it is important to track down and report the servers used in these attacks along with those used to send spam. Many Web administrators, however, don't take the time to track them because the manual process can be quite cumbersome. The Microsoft .NET Framework comes to the rescue with several networking classes, including the Dns class and the TcpClient class, that abstract away the complexity of performing DNS and WHOIS lookups. These classes make it easy to create a simple, straightforward ASP.NET-based utility for performing these lookups and automating this very important task.
|ave you ever gone through your Web server logs and found evidence of an attack—maybe someone attempting to use a known exploit but failing because you've already applied the patch? In these days of Code Red and Nimda worms, you probably have. You may also have wanted to track down the various unsavory persons who keep sending you spam, but didn't know which tools you could use to accomplish this task.|
In this article, I'll introduce you to DNS and WHOIS in the Microsoft® .NET Framework, and show you how these technologies can provide you with some pretty cool tools for tracking down and reporting attacks on your servers.
The Problem Most Web server log entries contain only the IP address of those who have accessed or attempted to access your server. The following example from the Internet Information Services (IIS) Web log for the Default Web site shows someone attempting to do something naughty:
If you're looking for your log, note that by default, the log is located at %windir%\system32\LogFiles\W3SVC1\. In this example (note that I have substituted fake IP addresses), the user at 0.0.0.0 is attempting to run cmd.exe on the machine represented by 184.108.40.206 via a known exploit in IIS that can allow a malicious user to run commands on the remote system. Fortunately, since I have the appropriate patch in place, the nefarious attempt fails, and the malicious user receives a 403—Forbidden response. (For more information on HTTP status codes, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4.)
-07-07 20:56:01 0.0.0.0 - 220.127.116.11 80 GET /winnt/system32/cmd.exe
/c+dir 403 -
Naturally, the first time I saw one of these in my server logs, I was rather surprised. I didn't think I was a target for such attacks, since the sites on my server are few and not heavily advertised. But with the widespread appearance of network-aware worms like Code Red and Nimda, everyone is a target, large or small. In fact, because of the way that these worms operate, working sequentially or randomly through all addresses in a subdomain, you're at greater risk than ever before, which means that you must be both more vigilant in protecting your systems from attack—applying patches, removing unnecessary script mappings, samples, and so on—and be more active in identifying and reporting the source of attacks in the cases that you find them.
This last step is especially important because without reporting these attacks, the Internet Service Providers' (ISPs) networks from which these attacks originate will have little incentive to work on preventive measures such as shutting down the accounts of Joe User, who doesn't care enough to patch his system, and so ends up playing host to Code Red or Nimda.
Worms like Code Red and Nimda are designed to primarily target systems within the same IP address range as the infected system. Being somewhat restricted in the range of IP addresses means that ISPs could have a substantial impact on stemming the flow of these worms by shutting down abusive accounts within their address range. But they won't do that unless they start receiving complaints, which is where responsible admins like you and I come in. Identifying and reporting attacks is as much a responsibility of a server administrator as is thwarting attacks.
So while it's good to see from the log entry example that the hack attempt failed, I'm not willing to leave it at that. I want to make sure that somebody knows what's going on and maybe get the account that launched the attack shut down. But how do I track down the appropriate party with only an IP address to go on?
Enter DNS and WHOIS You can think of the Domain Name System (DNS), as a sort of phone book for the Internet. Web servers, e-mail servers, and other servers on the Internet use DNS to translate the friendly names (like microsoft.com) that people like to use to IP addresses that computers use to locate one another. Why is this important? Because if the server from which an attack on your site originates, or the person spamming you has a DNS entry mapped to their IP address, you can determine what domain they're coming from and report them to the appropriate authorities in that domain. For example, one of the attempts on my server came from a computer located at a major university. I was able to determine this by looking up the DNS name for the IP address in my log. (I also confirmed this through a WHOIS lookup, which I'll get to soon.) You'll see how to do this from ASP.NET later in the article.
So assuming you actually get a DNS name, such as myServer.domainsIsUs.com, what can you do with this information? Well, this is where the next tool, WHOIS, comes in.
WHOIS is both a protocol and service, defined by the Internet Engineering Task Force (IETF) in RFC 954 (http://www.ietf.org/rfc/rfc0954.txt?number=954), which allows users on the Internet to query one of a number of servers containing information (including names, addresses, contact information, and so on) about the registered owners of domain names, as well as the registered owners of various blocks of IP addresses. Once you have obtained a domain name from your DNS lookup, you can do a WHOIS lookup on the domain name against a WHOIS server such as http://www.netsol.com/cgi-bin/whois/whois, and retrieve information on the registered owner of that domain. This information should include e-mail addresses for the technical and administrative contacts for the domain. Now you have someone you can complain to. Now let's look at how to use DNS and WHOIS with the .NET Framework.
DNS and WHOIS with the .NET Framework Part of the reason that server admins haven't been more ardent in tracking down and reporting attacks and spammers is that it has traditionally been an arduous manual task, and one for which the Windows® platform has until now provided little built-in support. While some versions of Windows ship with a command-line utility called nslookup that allows you to perform DNS queries, this utility is not especially user friendly. It does not provide any support for performing DNS queries from a Web interface, nor does it provide built-in support for automation.
Current versions of Windows do not provide any built-in support at all for running WHOIS queries without building your own custom WHOIS utility using TCP/IP sockets. As a result, admins have relied on third-party utilities, which make the task somewhat easier, but still time-consuming. I'll show you how to simplify this task as much as possible.
The Dns and TcpClient Classes The .NET Framework provides object-oriented classes that abstract a great deal of the complexity involved in performing DNS and WHOIS lookups (as well as other network operations). Two of those classes are Dns and TcpClient, which can be found in the System.Net and System.Net.Sockets namespaces, respectively.
The Dns class abstracts the task of performing DNS lookups and allows you to look up either the hostnames associated with a given IP address (passed either as a string or using the IPAddress helper class), or the IP addresses associated with a given hostname. The TcpClient class abstracts the tasks of opening a connection to another server on a given TCP port, making a request across the connection, and retrieving the result.
To illustrate how simple it is to use these classes in .NET, let's take a look at the code required to create a simple component in Visual Basic® .NET that uses the Dns and TcpClient classes to perform DNS and WHOIS lookups. My example component consists of two class files, DNS.vb and WHOIS.vb. The code for DNS.vb is shown in Figure 1, and the code for WHOIS.vb is shown in Figure 2.
Using the Dns Class In Figure 1, I define a namespace that is common to both class files and a class called MyDNS, which contains an overloaded Lookup method that further abstracts the task of performing a DNS lookup. Clients of this component will only need to know how to call the single Lookup method, passing either a string containing the hostname (such as "microsoft.com") or an instance of the IPAddress helper class containing the IP address for which you want to find a hostname. The latter overridden method definition is the one I'll use more often, since it's the IP address from a Web server log that I'll be starting from. You'll see how to create an instance of an IPAddress from a string containing the IP address when I address the code for creating a client of this component.
Both of the overridden Lookup method definitions return an array of strings containing either the list of hostnames associated with the supplied IP address or the list of IP addresses associated with the supplied hostname.
Making use of the Dns class is relatively simple and straightforward. All of the methods of the Dns class are static, which means that you do not need to create an instance of the class in order to call its methods.
To perform the lookups, I call either the GetHostByAddress method, which takes an IP address (either in the form of the IPAddress helper class, as in my example, or as a dotted-quad formatted string like "127.0.0.1") from which to resolve the hostname, or the GetHostByName method, which takes a string argument containing the hostname to resolve. Both methods return an instance of another helper class, called IPHostEntry.
The IPHostEntry class contains the Aliases property, the HostName property, and the AddressList property. The Aliases property is a string array populated with any aliases associated with the IP address passed to GetHostByAddress. The HostName property contains the main domain name associated with the IP address passed to GetHostByAddress. The AddressList property is a collection populated with instances of the IPAddress helper class representing the IP address or addresses returned by the GetHostByName method.
Since the Aliases property returns a string array, I copy this array to a local string array, ReDim the array to extend its upper bound by 1, and add the HostName property of the IPHostEntry instance. If there are no aliases, I simply return the HostName. To return the list of IP addresses as a string array, I iterate over the collection using a For loop and use the ToString method of the IPAddress class to add the dotted-quad string representation of the IP address to a string array, which is then returned to the caller of the Lookup method.
Using the TcpClient Class As you can see in Figure 2, using the TcpClient class is a bit more involved than using the Dns class, but it's still pretty straightforward. First, I have added a number of new Imports statements. I import the System.IO namespace and the System.Net.Sockets namespace. The System.IO namespace contains the Stream class used in the example, and the System.Net.Sockets namespace contains the TcpClient class used in the example.
Like MyDNS, the MyWHOIS class exposes two overloaded versions of a method named Lookup. In the MyWHOIS class, however, one of these takes an instance of IPAddress as an argument, and the other takes a string argument containing the hostname or IP address to look up and an optional Boolean argument indicating whether the string argument is an IP address. This allows the second overloaded version to handle the implementation for lookups by both IP address and hostname. This way the client can call the Lookup method of MyWHOIS in exactly the same fashion as the Lookup method of MyDNS, but nearly all of the implementation is contained within the second overloaded method to simplify maintenance.
If an IPAddress instance is passed in, the first overloaded version of Lookup extracts the string representation of the IP address and calls the second version, passing True for the optional second argument. This has the effect of modifying the WHOIS server to which the request will be directed.
For IP address lookups, the server used is http://whois.arin.net, which is the authoritative WHOIS server for looking up the ownership of IP blocks in North, Central, and South America. Lookups by host or domain name are sent to http://whois.networksolutions.com, which is one of a number of WHOIS servers containing information on registered owners of domain names.
In addition to these servers, there are also community-run servers (such as http://whois.geektools.com) that are able to automatically forward WHOIS queries to the appropriate authoritative WHOIS server, which can simplify the task of locating an IP block owner even if you don't know the correct WHOIS server for that region.
In the second overloaded version of the Lookup method in Figure 2, I create:
I then append a vbCrLf to the string passed into the method (since otherwise the request to the target server may hang or time out), and connect to the desired WHOIS server (based on whether the argument passed in is an IP address).
- A new instance of the TcpClient class.
- A NetworkStream object that allows me to send and receive data from the target server.
- StreamWriter and StreamReader objects for sending and receiving data. Using the StreamReader and StreamWriter classes allows me to send and retrieve string data instead of sending and receiving byte arrays.
- A string variable to receive the output from the target server.
Once the connection has been established, I call the GetStream method of the TcpClient class, which returns a stream object that I then use to create the StreamWriter object. With the StreamWriter object created, I call its Write method to pass the Hostname string argument to the stream, and then call the Flush method to send the data to the target server.
Finally, I create a StreamReader object from the WhoIsStream object, call the ReadToEnd method of the StreamReader object to retrieve the WHOIS response into the ResponseString string variable, close both the WhoIsStream and WhoIsClient objects, and return the ResponseString value to the calling client.
I can compile the MyDNS and MyWHOIS classes into a single managed assembly using the command-line compiler for Visual Basic .NET (but not from the IDE). This is done by executing the following command at a command prompt in the folder in which the class files reside:
Note that the /debug option is not required unless you plan to use a debugger such as the .NET SDK debugger to step through the code in the class at run time. I used the SDK debugger extensively while developing the sample code for this article, and I highly recommend it for those who still prefer Notepad or another text editor over Visual Studio® .NET.
vbc /t:library /r:System.DLL /out:DNS_WHOIS.DLL WHOIS.vb DNS.vb /
Creating a Simple Client Once I've compiled the component into the DNS_WHOIS.dll assembly, I can use it from any client, whether it be a console application, a Windows Forms application, or an ASP.NET application. Figure 3 shows the code for an ASP.NET Web Forms page called client.aspx that acts as a client for the component I just compiled. For the code in Figure 3 to work, it must be placed in an IIS application root that also contains a bin subdirectory. The compiled DNS_WHOIS.dll assembly should be placed in the bin directory to allow the ASP.NET page to locate the assembly.
The code in Figure 3 uses the ASP.NET @ Import directive, which is the equivalent of the Visual Basic .NET Imports statement, to import the Graymad and System.Net namespaces to the page. In the <body> section of the page, an HtmlTable control is added declaratively, and its cells are populated with some static text, an ASP.NET Label control, Textbox control, RadioButton controls in ASP.NET, and an ASP.NET Button control. The initial output of client.aspx is shown in Figure 4.
Figure 4Lookup Interface
As you can see in Figure 4, I've kept the interface very simple. It consists of one textbox, two radio buttons to choose between DNS and WHOIS lookups, and a Submit button. If you enter an IP address into the textbox (say, 18.104.22.168, which is one of the IP addresses for the msdn.microsoft.com domain) and click the button, the output would look like that in Figure 5.
Figure 5IP Lookup
All of the code necessary to perform the DNS and WHOIS lookups is contained within a server-side script block in the head section of the page and is executed by the Page_Load event handler. The first thing I do is check the page-level IsPostBack property, which tells me whether the current request is a result of the user submitting the Web Forms page. If it is, then I'm in business, and I create a string variable to contain the IP address or hostname entered in the textbox, a string array to contain the IP addresses or hostname/aliases returned from a DNS query, a counter variable for looping, and a new ASP.NET TextBox control to display the results of the query. I then set the desired properties of the TextBox control and look to see which of the radio buttons is checked to determine which type of query to run.
For both queries, I handle the question of whether the user entered an IP address or a hostname by attempting to create an instance of the IPAddress class from the string variable Host within a Try�Catch block. If a FormatException is thrown, I know that the string is not in the format of an IP address, so I assume that it is a hostname and proceed accordingly. While this technique is quite effective, there is performance overhead associated with throwing and catching exceptions, so this technique may not be ideal for applications requiring the highest performance.
For DNS queries, I populate the Addresses local string array with the results of the call to the Lookup query, passing in either the IPAddress instance or the Host string variable. I then loop through the contents of the array, writing each string in the array to the ResponseText TextBox control's Text property.
Since the Lookup method in the MyWHOIS class returns a string rather than a string array, I can simply return the result of the call to the Lookup method directly to the ResponseText.Text property for WHOIS queries. Because the Lookup method definitions in both the MyDNS and the MyWHOIS classes use the Shared keyword (the Visual Basic .NET equivalent of Static), I do not need to instantiate the MyDNS or MyWHOIS classes to call the Lookup method. Figure 6 shows the output of a WHOIS query for 22.214.171.124. As you can see, the WHOIS results for a specific IP address returns the owner information for the IP block to which the IP address belongs. This includes the owner's name and mailing address, as well as contact information for the coordinator for that IP block. You can use this information to report abuse of the IP addresses within the IP block.
Figure 6WHOIS Query Output
Because my example WHOIS component queries only http://whois.arin.net, IP addresses for European, Asian, or other regions outside of ARIN's purview will not resolve. In this case, the result would look something like Figure 7, which shows a listing referring the client to the RIPE WHOIS database, which contains IP block registration info for the European region. One potential enhancement of the client.aspx example shown is to add a dropdown listbox containing the names of common WHOIS servers, which would allow the user to choose this server at run time.
Figure 7Arin.net Query
For the sake of simplicity, the examples in this article do not make full use of structured exception handling to prevent exceptions from bubbling up to the browser level. A production-quality application based on these examples should at a minimum provide exception handling for the SocketException class, which is thrown by the TcpClient class for a variety of reasons. The exception handling logic could either retry the connection or request or return an informative message.
Conclusion The Microsoft .NET Framework provides simple and straightforward access to DNS and WHOIS information via the Dns and TcpClient classes. As I showed in my example, you can further simplify the process of performing DNS and WHOIS lookups by creating a component that abstracts the underlying code to call the Dns and TcpClient class methods into a few static methods. You could further simplify the use of this component and improve reusability by moving the code that uses the component into a user control or a custom server control, or by developing a custom server control that implements all of the code included in the component. An example of a user control that implements the UI and logic in my client.aspx example is available in the downloadable sample code for this article, along with a Web Forms page, client2.aspx, that makes use of the user control. The code also includes an example of a console client for the DNS_WHOIS.dll component.
| For related articles see:|
Spoofing and Reporting
Acceptable Use Policies
For background information see:
| G. Andrew Duthie is the founder and Principal of Graymad Enterprises Inc., specializing in training and consulting in ASP, ASP.NET, and other Web development technologies. He is the author of ASP.NET Step By Step (Microsoft Press, 2001). Andrew can be reached at email@example.com or through http://www.graymad.com.|