They generally can't, because of CORS. The only reason this "hack" works is because the timing of the rejection is different between non-resolving domain request and resolving-but-rejected request. But if you run something on https://192.168.2.1 it can't be accessed from a web app running on https://my-own-domain.com unless the service running at 192.168.2.1 allows the "Origin" my-own-domain.com.
Technically they can make the request. The server will receive and respond to the request. CORS is applied by the browser which prevents the response from being read. This is why CORS doesn't prevent a request from mutating something on a server. A CSRF token does.
Is that true? There’s a preflight request that checks with the server as to what’s allowed and then the browser issues the original request if it’s ok, right?
"Simple" requests are sent without a OPTIONS preflight check. It's because old style forms could always do a cross-origin request, so you have to protect against it anyway.
First of all, you could probably do a timing attack on CORS response, you'd just have to deal with how much they're cached.
Depends on the resource request. For example, <img> can be used to load remote resources without CORS, since the image data isn't shared with JS (trying to read it via a canvas marks it "tainted", and errors read requests). Meltdown/Spectre breaks this barrier down, which led to the introduction of COOP/COEP headers that require CORS on remote requests and would break this attack—except that you can ask the browser to send requests without cookies, re-introducing this timing attack.
And none of this helps you if the server in question isn't actually a HTTP server, but something else (that only ever expected to be exposed on localhost). Depending on the exact syntax it expects, it's plausible that a HTTP request (even a CORS request) might be misinterpreted to do something nefarious.