1

Is it possible to read and extract HTTP request headers via JavaScript?

i.e. something like;

var req = new XMLHttpRequest();
req.open(‘GET’, document.location, false);
req.send(null);
var headers = req.getAllResponseHeaders();
console.log(headers);

But instead of reading HTTP response headers (as per snippet above), it reads the HTTP request headers?

The reason I'm asking about this is that the above snippet (altered to read request HTTP headers and not response HTTP headers) will be an external JavaScript file that will successfully be called through an XSS vulnerability. External JS files are permitted here due to the lack of a CORS policy. The above code snippet (again, altered to read request headers) would then allow me to extract the HTTP request header value via scraping the http request headers from a null (or mock) HTTP request to then have the specific request header (X-CSRF-TOKEN) included within a specially crafted HTTP request which is accepted by the server and therefore bypasses the flawed CSRF protection mechanism in order to execute CSRF.

I have created a functional exploit within a localhost demo I spun up where this works for reading response headers, but I'm needing some form of capability whereby JavaScript can read HTTP request headers. Is this something that is possible in this context?

I have elaborated on this in slightly further detail below (please excuse any duplicated details);

The CSRF target request contains a CSRF-Token (anti-CSRF protection mechanism). This is a static value that is only unique per user session (and not dynamic upon individual requests) - hence the flaw here. I am trying to create a JavaScript file which I will call remotely via a Cross-site Scripting (XSS) flaw (works due to lack of CORS policy) where this JavaScript will execute within the context of the victim user. As a result, HTTP request headers are automatically added to the HTTP request by the application as I am now performing client-side requests within the context of the victim user under this localhost application (basically like being able to navigate the website while authenticated as the victim user). This is why there is no need for me to supply HTTP request headers other than the request header I need to extract (X-CSRF-TOKEN) via the XmlHttpRequest (or whatever). I just need to be able to read the X-CSRF-TOKEN HTTP request header that is set in the HTTP request (not response), extract this value, and include within a XmlHttpRequest (or whatever) to craft a request that will be accepted by the server in order to bypass the faulty anti-CSRF mechanism and successfully perform CSRF.

I have written an exploit for this but it pertains to HTTP response header cookie values within the HTTP response headers, but this obviously doesn't work as the XSRF-TOKEN cookies are not used in this context for anti-CSRF. It's the X-CSRF-TOKEN HTTP Request header that is used for the anti-CSRF mechanism. I was thinking I could do something like req.open('GET', document.location, false); Again since this will be executing under the context of the victim user, shouldn't it be trivial to scrape the X-CSRF-TOKEN HTTP request header here while submitting something like req.open('GET', document.location, false);?

Any help will be much appreciated. I hope I have explained this well enough for you to understand what it is I'm trying to do (it's late here).

Thank you for your time, /J

3
  • You can re-define/wrap the whole dang window.XMLHttpRequest (or fetch() if you want to be modern) class if needed, or just the .setRequestHeader() method to one that logs as it does the original thing as well ala cachedMethodRef.apply(this, arguments)
    – dandavis
    Mar 1, 2021 at 17:46
  • Hi @dandavis, thanks for your response. Are you able to provide an example of how I can achieve this? My JavaScript is quite basic. Your assistance will be greatly appreciated!
    – Sarah M.
    Mar 1, 2021 at 23:37
  • see my example wrapping of send(),, setRequestHeader should be malleable just the same.
    – dandavis
    Mar 2, 2021 at 5:04

2 Answers 2

1

It looks like all you're trying to do is get an anti-CSRF token? You shouldn't need to look at request headers for that at all. In order to set custom header, the initiating JS needs to know the expected value. Most likely, you can just pull it out of a JS variable or similar. Alternatively, if the value in the header is supposed to match a value in the body, you can just choose your own value to send both in the header and the body.

If you must get the original value but can't read it out of JS (or out of the DOM, or a cookie, or re-derive it from other client-visible data), one option would be to shim the relevant XHR and/or fetch functions. By overwriting the functions with ones that expose the value of any custom request headers, the legitimate scripts will leak their hidden values to your injected script. Alternatively, consider that the legit script presumably had to get that header value from the server. Thus, you can just request the source of the header (probably a script file but possible a data file such as JSON, or a HTML file containing inline script) from the server, and parse the value out of it yourself.

0

You might want to look into Service Workers:

A service worker is a web worker that implements a programmable network proxy that can respond to web/HTTP requests of the main document. It is able to check the availability of a remote server and to cache content when that server is available, and serve that content later to the document. Service workers, like any other web workers, work separately from the main document context. Service workers can handle push notifications and synchronize data in the background, cache or retrieve resource requests, intercept network requests and receive centralized updates independently of the document that registered them, even when that document is not loaded.

See also: FHantke's approach to a remote MiTM with Service Workers: https://betterprogramming.pub/man-in-the-middle-attacks-via-javascript-service-workers-52647ac929a2

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .