In itself, merely having
%32%35 decoded to
25 in a URL is neither an error nor a sign of a vulnerability. In fact, it's what RFC 3986, section 2.3 says should happen (emphasis mine):
2.3. Unreserved Characters
Characters that are allowed in a URI but do not have a reserved purpose are called unreserved. These include uppercase and lowercase letters, decimal digits, hyphen, period, underscore, and tilde.
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
URIs that differ in the replacement of an unreserved character with its corresponding percent-encoded US-ASCII octet are equivalent: they identify the same resource. However, URI comparison implementations do not always perform normalization prior to comparison (see Section 6). For consistency, percent-encoded octets in the ranges of ALPHA (%41-%5A and %61-%7A), DIGIT (%30-%39), hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) should not be created by URI producers and, when found in a URI, should be decoded to their corresponding unreserved characters by URI normalizers.
In fact, it's quite possible that it may not be your application that's decoding those characters at all, but your webserver, which tries to normalize the URLs it receives before passing them on to your application.
To tell whether or not your application (or webserver) is decoding too much, you should test it with characters that actually shouldn't be decoded according to the standard, and which could have an actual effect on the parsing of the URL or the HTML it's embedded in. Examples of such characters include:
The general URL delimiter characters (
gen-delims in RFC 3986, section 2.2), especially the characters
%23) — these should never appear un-escaped in the path portion of a URL.
Characters that are not permitted in URLs at all, such as newlines (
%0A), spaces (
HTML metacharacters, such as
%3E) — having these inappropriately decoded (and not replaced by the corresponding HTML character entities) could corrupt the HTML markup and potentially create an HTML injection / XSS vulnerability.
Note that several of these (
>) are in the list of forbidden URL characters above, and should thus always be percent-encoded; the others (
') may, and in some cases must, appear in URLs without percent-encoding, but in such cases they (especially
&) must be encoded as the corresponding HTML character entity (e.g.
% sign, encoded as
%25, should never be decoded to its literal form within a URL; if it is, it can invalidate the URL or, worse, form a valid but unexpected percent-escape sequence with the characters following it. While this is unlikely to allow a direct XSS attack, it is certainly a bug, and could open up other vulnerabilities by allowing malicious input to pass validation.
In particular, the test where you observed
%22 being replaced by the HTML entity
" suggests that a) there is some inappropriate decoding (or insufficient re-encoding) going on, yet b) the URL, though strictly speaking invalid, is being properly HTML-escaped, which should prevent it from corrupting the HTML markup in a way that would allow an XSS attack.