One attack vector was due to a page that accepted a parameter containing a relative URL to another page in the application. It ended up being a tailor-made way for someone to avoid the blacklisting altogether. When a request was received, this special page caused a forward (not a redirect) to the specified URL. The code that rejected angle brackets was hit on the request from the client only, and not when the forwarding was done. Therefore, "<" and ">" could be slipped in by double URL-encoding them in the initial request as follows: %253C and %253E (%25 is a URL-encoded percent sign).
The other XSS attack vector was via JavaScript event handlers. This technique is available when user-supplied data is output to the attribute list of a page element. This often happens for an <input> tag, where the HTML form has multiple text inputs. The user enters the data, but one or two values may be invalid, so the application returns the same page and prepopulates the values that were entered. An attacker can employ XSS by closing off the previous attribute with a double-quote (assuming double-quotes were used) and injecting an event handler such as onMouseOver, onFocus, or onClick.
An example injection is: x%22%20onFocus%3d%22alert('xss')%22%20%22. The resulting HTML might look like this:
<input type="text" name="val5" value="x" onFocus="alert('xss')" "">
Notice there were no angle brackets used in this attack, so blacklisting those characters did not provide any protection.