Jump to content

WAN Access treated like LAN when "X-Forwarded-For" header is set, allowing login w/o password


pse

Recommended Posts

Hi,

 

I've got a premiere Emby-Server V4.3.1.0 running on Windows with remote connections enabled. It seems that one can trick the server into believing that a request is comming from LAN by setting the "X-Forwarded-For" request header. Testcase:

wget --no-check-certificate -O - https://xyz.dyndns.org:8920/Users/authenticatebyname \
  --header 'x-emby-authorization: MediaBrowser Client="Emby Web", Device="WGET", DeviceId="XYZ", Version="4.3.1.0"' \
  --header 'content-type: application/json' \
  --post-data='{"Username":"peter","Pw":""}'

(xyz.dyndns.org is fake. 'peter' is a LAN user that is configured to NOT have remote access) The request will fail with "401 unauthorized" as expected.

 

Now, when extending the above request by --header 'X-Forwarded-For: 127.0.0.1', the login succeeds. :o Any other IP from the 192.168.*.*  range seems to do the trick as well.

 

Can anyone reproduce this with their server?
 

 

Link to comment
Share on other sites

Happy2Play

Don't really know anything about this but you have "LAN Network" options on the Dashboard-Networks.

 

 

 

LAN networks:
Comma separated list of IP addresses or IP/netmask entries for networks that will be considered on local network when enforcing bandwidth restrictions. If set, all other IP addresses will be considered to be on the external network and will be subject to the external bandwidth restrictions. If left blank, only the server's subnet and common private IP subnets (10.0.0.0/8, 192.168.0.0/24, etc.) are considered to be on the local network.
Link to comment
Share on other sites

Hate to reply to myself, but this becomes even better:

 

When disabling remote connections alltogether, a remote request to index.html fails as expected:

wget --no-check-certificate -O - https://xyz.dndns.org:8920

Again, just adding X-Forwarded-For: 127.0.0.1 allows remote access.

Link to comment
Share on other sites

I tried setting LAN network to 192.168.x.0/24 and even bind to to 192.168.x.y only.

 

This might be a IPv4 vs IPv6 issue: While the server is running in private 192.168.x.* behind the router, it does get a public IPv6 address. My tests above are all done using IPv6. I can't connect to to my home network via IPv4, as my internet provider uses DS-Lite.

  • Like 1
Link to comment
Share on other sites

pwhodges

So have you specified to Emby what IPv6 address range is local?  On my network I had to include two IPv6 subnets, but that probably varies with different ISPs.

 

Paul

Link to comment
Share on other sites

Hi,  yes typically this header is used to ensure the server will treat an incoming request as coming from a remote device, but you found a way to trick it into the opposite behavior. We'll have to make sure this isn't allowed. Thanks.

  • Like 1
Link to comment
Share on other sites

So have you specified to Emby what IPv6 address range is local?  On my network I had to include two IPv6 subnets, but that probably varies with different ISPs.

I did not define any IPv6 prefix as "local". I don't see much sense in that, as there are always the link-local adresses (fe80:..) which should implicitly be local in Emby. Besides that, there are two publicly routable IPv6 adresses (one of them "temporary" for IPv6 privacy extensions IIRC).

 

In any case, anything that I don't explictly define as "lokal" should be treated "remote" and should not be allowed to access emby unless Remote Access is enabled.

 

BTW, setting an address to bind to does not seem to work, the emby process always ends up listening on 0.0.0.0 and ::.

 

There should be a very clear definition of what a "remote connection" is and how it is detected. IMO it would be best to allow setting a separte Port for external connections. Then, there can be no more mistakes in identifying local vs. remote connections.

Edited by pse
Link to comment
Share on other sites

pwhodges

I did not define any IPv6 prefix as "local". I don't see much sense in that, as there are always the link-local adresses (fe80:..) which should implicitly be local in Emby. Besides that, there are two publicly routable IPv6 adresses (one of them "temporary" for IPv6 privacy extensions IIRC).

 

In any case, anything that I don't explictly define as "lokal" should be treated "remote" and should not be allowed to access emby unless Remote Access is enabled.

 

I define my IPv6 prefix as local so that other machines on the network are recognised as local - they don't always use link-local addressing, it seems to me.

 

But I take your point that anything not defined as local should be treated as remote.

 

Paul

Link to comment
Share on other sites

I define my IPv6 prefix as local so that other machines on the network are recognised as local - they don't always use link-local addressing, it seems to me.

You are right, same here: The DNS server in my router returns two AAAA records, one for the link-local and another for the temporary public IPv6 address, and seems to prefer the public one.

Link to comment
Share on other sites

  • 1 month later...

Yes -- I made the request via an externally hostet reverse proxy, connecting back to my machine.

But the original testcase still is valid, doing the wget from an external host.

Link to comment
Share on other sites

Anything I can help to track this down? I would test this with IPv4 only, but as already mentioned my DS-Lite setup does not connecting to my home network via IPv4. IMO the cleanest solution would be to have a separate socket (binding to selected external IPs and/or an external port) dedicated for external connections.

 

Btw, thanx for supporting HTTP2 in the latest release! And for your quick responses.

Link to comment
Share on other sites

pir8radio

does it make any difference setting emby to a non existent LAN network, in this case a single IP address that would be considered "the lan"?  I think this is only used for transcoding purposes but just curious.:

 

5e80b5a069326_Screenshotfrom202003290948

Edited by pir8radio
Link to comment
Share on other sites

pir8radio

does it make any difference setting emby to a non existent LAN network, in this case a single IP address that would be considered "the lan"?  I think this is only used for transcoding purposes but just curious.:

 

5e80b5a069326_Screenshotfrom202003290948

 

 

 

To add to this post, i'm trying to spoof both request headers to 127.0.0.1 and I'm not getting into the server without needing my password...     Can you test your method on my server?  just click my avatar for my server url.

 

 

EDIT: I think i partially misunderstood your initial concern.  I thought you could spoof either header and login to the server as "admin" without entering any credentials.   But as I understand it this only affects users you have set to local access only, they can spoof their way into being able to login remotely?     If that is the case, this is only a minor bug then not as serious as I first took it...    

 

Ignore my previous post then as a test, it wont help you.    Sorry for the misunderstanding. 

Edited by pir8radio
Link to comment
Share on other sites

Chaning LAN Networks to any non-existing IP-Adress does not change "127.0.0.1" as being detected as "local" when set in X-Forwarded-For. However, using one of my actual local adresses in X-Forwarded-For does not make me "local" any more.

 

Yes, this is not about being able to bypass credentials of a user, so it's not THAT serious. But the "login.html" page which is shown to local users as more information than "manuallogin.html" (e.g. available users). And there is the usecase of having no passwords for local users -- I don't want those to be accessible from outside.

Link to comment
Share on other sites

  • 1 year later...

*bump*. I just re-checked this with 4.6.0.50, the problem still persist.

To resummarize: By setting a simple HTTP header, emby can be tricked into acecpting remote connections as local ones, with has (at least) the following implications:

The remote user isn't presented this "manual login screen" any more (sorry for the screenshots not being EN, but you'll get the idea).

grafik.png.0bdb2f3e344c292defb3f98796cf8159.png

Instead, the users sees:

grafik.png.c13dfd1183275364bfb805cc4b02031d.png

This is the login screen usually displayed only to local users. Any of them can log in (with password or even without if you don't care about passwords in your home network). Even those users who have "allow remote connections" (not sure if the translation is correct) unchecked are able to. All settings that distinguish between local/remote connection, e.g. bandwidth limitting, isn't applied correctly.

I think this is a grave security issue, which shouldn't be taken lightly. I'd love to have a go at the network code, but that's closed source 😕

As suggested before, I think it might be a good idea to have a separate listening port for external connections. Relying on the source IP of incomming connections is not very safe and error prone.

Link to comment
Share on other sites

Was the actual IP used internal or external?

If internal it shouldn't do that.

It should first check to see if an IP is local, if not it can not be considered a local address.
Next it should check the headers to see if they are present which is needed when using a proxy.

But an external IP should never be able to "spoof" a local IP address.

Link to comment
Share on other sites

This time, the external connection was IPv4 and port-forwarded (and thus NATed) by my router, so I guess the actual IP of the last hop was the router's one.
But last year, I tested this exposing the external (global) IPv6 address of the emby server, so NATing there, but same problem. This just shows that relying on the source IP isn't reliable at all.

But this is all besides the point -- by setting "X-Forwarded-For", I can make emby think the connection is local anyway. This works with a header value of "127.0.0.1", "192.168.x.y" (where x is my local Class C) or even any other x! I also tested a public class A ("10.0.0.1") but that wasn't allowed.

Link to comment
Share on other sites

Just verified same behavior with IPv6 exposed.

Another detail: When setting "local networks" to "192.168.x.0/24" instead of just leaving it blank, spoofing as "192.168.x.y" doesn't work with any x any more, just my the configured one. But this is no relief since spoofing as "127.0.0.1" will still let emby think you're local, no matter where you come from.

@cayars is right, there shouldn't be any way to spoof at all. You'll need to find out where "X-Forwarded-For" is used (probably in the HTTP server library).

[10 minutes later] ... just had a look at the sources before emby went closed source:

https://github.com/MediaBrowser/Emby/blob/1d7c2ab4bfebdae31f89fdaabd3b68782ccf495a/MediaBrowser.ServerApplication/SocketSharp/WebSocketSharpRequest.cs

 

        private string remoteIp;
        public string RemoteIp
        {
            get
            {
                return remoteIp ??
                    (remoteIp = (CheckBadChars(XForwardedFor)) ??
                                (NormalizeIp(CheckBadChars(XRealIp)) ??
                                (request.RemoteEndPoint != null ? NormalizeIp(request.RemoteEndPoint.Address.ToString()) : null)));
            }
        }

I'm no C# dev so can't dig into this further, but this code (and ValidateRequest() look exactly like what I suspected: IP address from X-Forwarded-For (or X-Real-IP, for that matter) headers is used as "remote endpoint" address, and this is then later checked against the local IPs. Not secure at all.

Note that ignoring the headers won't fix the security problem, since TCP source addresses can be spoofed as well (although not that easily).

Link to comment
Share on other sites

41 minutes ago, pse said:

 

@cayars is right, there shouldn't be any way to spoof at all. You'll need to find out where "X-Forwarded-For" is used (probably in the HTTP server library).

Well you could technically be able to spoof an external IP but should never be able to spoof a local IP. However, you need to keep in mind if you have a local proxy the burden of checking this is on the proxy and not Emby as it will always see the local proxy IP address.

  • Like 1
Link to comment
Share on other sites

42 minutes ago, cayars said:

However, you need to keep in mind if you have a local proxy the burden of checking this is on the proxy and not Emby as it will always see the local proxy IP address.

I don't think this is true: When opening a port on the router via IPv4, simple DNAT is enough to forward the incoming packet to the service. The service will see the original, external source IP. When answering to this packet, it can simply send a response to the external IP, which is then masqueraded on its way out like any request from the local network to the outside.

The scenario you are describing would only happen with some sort of user-space port forwarding, which I don't think is common. And with IPv6, there is no NAT at the router and the emby server will receive a global IP address as well. So there is no burdening this issue on the router IMO.

Back to emby: If at least the IP-based checks were working as advertised, this would still be a burden to the user who would need to know all of this. But the state this is now, any IP-based check is complete nonsense as I can just put a header into the request telling emby what my IP is, and it will believe me. This is misleading and insecure. There are some options that rely on this "security feature" working, like the per-user checkbox to allow connecting from a remote network.

But even if this worked correctly, I suggest not making the user rely on an inherently insecure property of IP, the source address. Why not clearly distinguish between internal and external using two different ports?

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...