The importance of filtering error messages
Friday December 14, 2007 at 11:18 am CST
Posted by HongZheng Zhou
When scanning machines for vulnerabilities, version information is one of the most common pieces of information to rely on. If you know the version of the OS or installed software, you know what that machine is vulnerable to. This is trivial when scanning with an authenticated connection. But if authentication is not an option, other less obvious means are needed.
Using the IBM Websphere Application Server for this example (I could have used any number of applications), I found that different releases of version 6.0 and 6.1 throw almost the same exceptions. For example, after requesting a non-existent .jsp page “/non-exist-ibmwas-0123456789.jsp”, IBM Websphere 6.0 returned:

The errors appear to give a line number in the source code. Making the same request (one fixed length url during the scanning) against different versions returned similar errors, but with different line numbers.
I selected 17 exception points to compare the error code line numbers in different releases of Websphere 6.0:
com.ibm.ws.jsp.webcontainerext.JSPExtensionProcessor.findWrapper
(JSPExtensionProcessor.java:
com.ibm.ws.jsp.webcontainerext.JSPExtensionProcessor.handleRequest
(JSPExtensionProcessor.java:
com.ibm.ws.webcontainer.webapp.WebApp.handleRequest
(WebApp.java:
com.ibm.ws.webcontainer.webapp.WebGroup.handleRequest
(WebGroup.java:
com.ibm.ws.webcontainer.VirtualHost.handleRequest
(VirtualHost.java:
com.ibm.ws.webcontainer.WebContainer.handleRequest
(WebContainer.java:
com.ibm.ws.webcontainer.channel.WCChannelLink.ready
(WCChannelLink.java:
com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleDiscrimination
(HttpInboundLink.java:
com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleNewInformation
(HttpInboundLink.java:
com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.ready
(HttpInboundLink.java:
com.ibm.ws.tcp.channel.impl.NewConnectionInitialReadCallback.sendToDiscriminaters
(NewConnectionInitialReadCallback.java:
com.ibm.ws.tcp.channel.impl.NewConnectionInitialReadCallback.complete
(NewConnectionInitialReadCallback.java:
com.ibm.ws.tcp.channel.impl.WorkQueueManager.requestComplete
(WorkQueueManager.java:
com.ibm.ws.tcp.channel.impl.WorkQueueManager.attemptIO
(WorkQueueManager.java:
com.ibm.ws.tcp.channel.impl.WorkQueueManager.workerRun
(WorkQueueManager.java:
com.ibm.ws.tcp.channel.impl.WorkQueueManager$Worker.run
(WorkQueueManager.java:
com.ibm.ws.util.ThreadPool$Worker.run
(ThreadPool.java:
The table below shows the error line numbers generated by the different releases.
6.0.0.0:
246, 228, 2841, 220, 204, 1681, 77, 421, 367, 276,
201, 103, 548, 601, 934, 1021, 1332
6.0.0.2:
246, 228, 2841, 220, 204, 1681, 77, 465, 404, 282,
201, 103, 548, 601, 934, 1021, 1332
6.0.0.3:
251, 233, 2841, 220, 204, 1700, 77, 466, 405, 283,
201, 103, 555, 608, 941, 1028, 1332
6.0.1.0:
266, 248, 2872, 220, 204, 1779, 77, 466, 405, 283,
201, 103, 555, 608, 941, 1028, 1394
6.0.1.2:
266, 248, 2872, 220, 204, 1806, 77, 466, 405, 283,
201, 103, 555, 608, 941, 1028, 1394
6.0.2.0:
257, 239, 2905, 220, 204, 1829, 84, 469, 408, 286,
201, 103, 566, 619, 952, 1039, 1455
6.0.2.1:
257, 239, 2905, 220, 204, 1829, 84, 469, 408, 286,
201, 103, 566, 619, 952, 1039, 1455
6.0.2.3:
257, 239, 2916, 220, 204, 1831, 84, 469, 408, 286,
201, 103, 566, 619, 952, 1039, 1455
6.0.2.5:
257, 239, 2933, 221, 210, 1912, 84, 472, 411, 288,
201, 103, 566, 619, 952, 1039, 1462
6.0.2.7:
268, 250, 2954, 221, 210, 1912, 84, 472, 411, 288,
207, 109, 566, 619, 952, 1039, 1469
6.0.2.9:
268, 250, 2965, 221, 210, 1931, 84, 472, 411, 288,
207, 109, 566, 619, 952, 1039, 1470
6.0.2.11:
268, 250, 3003, 221, 210, 1958, 88, 472, 411, 288,
207, 109, 566, 619, 952, 1039, 1470
6.0.2.13:
268, 250, 3004, 221, 210, 1958, 89, 472, 411, 288,
207, 109, 566, 619, 952, 1039, 1471
6.0.2.15:
270, 252, 3071, 236, 210, 1958, 89, 472, 411, 288,
207, 109, 566, 619, 952, 1039, 1471
6.0.2.17:
270, 252, 3071, 236, 210, 1958, 98, 472, 411, 288,
207, 109, 566, 619, 952, 1039, 1471
6.0.2.19:
270, 252, 3146, 250, 212, 1958, 112, 472, 411, 288,
207, 109, 566, 619, 952, 1039, 1471
6.0.2.21:
271, 253, 3174, 253, 229, 1970, 114, 472, 411, 288,
207, 109, 566, 619, 952, 1039, 1471
As you can see, most line numbers increase (don’t decrease, actually) as the release number increases.
Using the line number list of older releases as the baselines, if one line number in the newer release is bigger than in the old release, I add 1. If less, I subtract 1. The result is shown below:
6.0.0.0 0 baseline
6.0.0.2 3
6.0.0.2 0 baseline
6.0.0.3 10
6.0.0.3 0 baseline
6.0.1.0 5
6.0.1.0 0 baseline
6.0.1.2 1
6.0.1.2 0 baseline
6.0.2.0 9
6.0.2.0 0 baseline
6.0.2.1 0 - no difference, can’t recognize the releases
6.0.2.1 0 baseline
6.0.2.3 2
6.0.2.3 0 baseline
6.0.2.5 7
6.0.2.5 0 baseline
6.0.2.7 6
6.0.2.7 0 baseline
6.0.2.9 3
6.0.2.9 0 baseline
6.0.2.11 3
6.0.2.11 0 baseline
6.0.2.13 3
6.0.2.13 0 baseline
6.0.2.15 4
6.0.2.15 0 baseline
6.0.2.17 1
6.0.2.17 0 baseline
6.0.2.19 4
6.0.2.19 0 baseline
6.0.2.21 7
So now all it takes to determine the specific release of an IBM Websphere server is to make the same request for a page that we know doesn’t exist, compare the returned line number list to the array of known releases and line number lists. And in theory, when we encounter an unknown release (one that’s not in our list of releases), we can use the add/subtract method to know which release it is below and which release it is higher than.
You can see why it’s important to filter error information web servers return to users. Detailed error messages leak many types of useful information to attackers, such debug messages (stack traces information here) should never be presented to users by production application servers. It should return some generic error information if something unexpected occurs. If applications are designed to return some application generated error messages, it will be more difficult to attacker to stage a further attack.
