From 4b42bdf2468d72e8086b38585656ab8e256d7a04 Mon Sep 17 00:00:00 2001 From: UnfamiliarLegacy <74633542+UnfamiliarLegacy@users.noreply.github.com> Date: Sat, 15 Jul 2023 07:00:21 +0200 Subject: [PATCH 1/6] Improve Nitro socket url matching --- .../connection/proxy/nitro/http/NitroHttpProxyFilter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxyFilter.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxyFilter.java index b9795ec..3233f90 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxyFilter.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxyFilter.java @@ -16,9 +16,9 @@ import java.util.regex.Pattern; public class NitroHttpProxyFilter extends HttpFiltersAdapter { - private static final String NitroConfigSearch = "\"socket.url\""; + private static final String NitroConfigSearch = "socket.url"; private static final String NitroClientSearch = "configurationUrls:"; - private static final Pattern NitroConfigPattern = Pattern.compile("\"socket\\.url\":.?\"(wss?://.*?)\"", Pattern.MULTILINE); + private static final Pattern NitroConfigPattern = Pattern.compile("[\"']socket\\.url[\"']:.?[\"'](wss?://.*?)[\"']", Pattern.MULTILINE); // https://developers.cloudflare.com/fundamentals/get-started/reference/cloudflare-cookies/ private static final HashSet CloudflareCookies = new HashSet<>(Arrays.asList( From 9bb98c1739b2276771ea9964a563cb384f618840 Mon Sep 17 00:00:00 2001 From: UnfamiliarLegacy <74633542+UnfamiliarLegacy@users.noreply.github.com> Date: Mon, 11 Dec 2023 05:01:56 +0100 Subject: [PATCH 2/6] Update config pattern --- .../connection/proxy/nitro/http/NitroHttpProxyFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxyFilter.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxyFilter.java index 3233f90..30694ba 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxyFilter.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxyFilter.java @@ -18,7 +18,7 @@ public class NitroHttpProxyFilter extends HttpFiltersAdapter { private static final String NitroConfigSearch = "socket.url"; private static final String NitroClientSearch = "configurationUrls:"; - private static final Pattern NitroConfigPattern = Pattern.compile("[\"']socket\\.url[\"']:.?[\"'](wss?://.*?)[\"']", Pattern.MULTILINE); + private static final Pattern NitroConfigPattern = Pattern.compile("[\"']socket\\.url[\"']:.+[\"'](wss?://.*?)[\"']", Pattern.MULTILINE); // https://developers.cloudflare.com/fundamentals/get-started/reference/cloudflare-cookies/ private static final HashSet CloudflareCookies = new HashSet<>(Arrays.asList( From bea6848fdc344d4ca7cffb9bd9bef37e7fd416c7 Mon Sep 17 00:00:00 2001 From: UnfamiliarLegacy <74633542+UnfamiliarLegacy@users.noreply.github.com> Date: Tue, 12 Dec 2023 01:21:04 +0100 Subject: [PATCH 3/6] Update socket url matching --- .../connection/proxy/nitro/http/NitroHttpProxyFilter.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxyFilter.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxyFilter.java index 30694ba..e6e1152 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxyFilter.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxyFilter.java @@ -18,7 +18,7 @@ public class NitroHttpProxyFilter extends HttpFiltersAdapter { private static final String NitroConfigSearch = "socket.url"; private static final String NitroClientSearch = "configurationUrls:"; - private static final Pattern NitroConfigPattern = Pattern.compile("[\"']socket\\.url[\"']:.+[\"'](wss?://.*?)[\"']", Pattern.MULTILINE); + private static final Pattern NitroConfigPattern = Pattern.compile("[\"']socket\\.url[\"']:(\\s+)?[\"'](wss?:.*?)[\"']", Pattern.MULTILINE); // https://developers.cloudflare.com/fundamentals/get-started/reference/cloudflare-cookies/ private static final HashSet CloudflareCookies = new HashSet<>(Arrays.asList( @@ -95,11 +95,11 @@ public class NitroHttpProxyFilter extends HttpFiltersAdapter { final Matcher matcher = NitroConfigPattern.matcher(responseBody); if (matcher.find()) { - final String originalWebsocket = matcher.group(1); + final String originalWebsocket = matcher.group(2).replace("\\/", "/"); final String replacementWebsocket = callback.replaceWebsocketServer(this.url, originalWebsocket); if (replacementWebsocket != null) { - responseBody = responseBody.replace(originalWebsocket, replacementWebsocket); + responseBody = responseBody.replace(matcher.group(2), replacementWebsocket); responseModified = true; } } From 88ba87a8194268193f56ce6686b59760dc292f06 Mon Sep 17 00:00:00 2001 From: UnfamiliarLegacy <74633542+UnfamiliarLegacy@users.noreply.github.com> Date: Mon, 27 May 2024 19:20:00 +0200 Subject: [PATCH 4/6] Use SSL WebSockets for Nitro to prevent CSP issues --- .../proxy/nitro/NitroProxyProvider.java | 10 ++-- .../http/NitroCertificateSniffingManager.java | 23 +++++++- .../proxy/nitro/http/NitroHttpProxy.java | 53 ++++++++----------- .../nitro/http/NitroSslContextFactory.java | 20 +++++++ .../nitro/websocket/NitroWebsocketProxy.java | 25 +++++++-- 5 files changed, 93 insertions(+), 38 deletions(-) create mode 100644 G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroSslContextFactory.java diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/NitroProxyProvider.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/NitroProxyProvider.java index b86a4fc..c073a1a 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/NitroProxyProvider.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/NitroProxyProvider.java @@ -6,6 +6,8 @@ import gearth.protocol.connection.HProxySetter; import gearth.protocol.connection.HState; import gearth.protocol.connection.HStateSetter; import gearth.protocol.connection.proxy.ProxyProvider; +import gearth.protocol.connection.proxy.nitro.http.NitroAuthority; +import gearth.protocol.connection.proxy.nitro.http.NitroCertificateSniffingManager; import gearth.protocol.connection.proxy.nitro.http.NitroHttpProxy; import gearth.protocol.connection.proxy.nitro.http.NitroHttpProxyServerCallback; import gearth.protocol.connection.proxy.nitro.websocket.NitroWebsocketProxy; @@ -13,7 +15,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; -import java.net.ServerSocket; import java.util.concurrent.atomic.AtomicBoolean; public class NitroProxyProvider implements ProxyProvider, NitroHttpProxyServerCallback, StateChangeListener { @@ -32,11 +33,14 @@ public class NitroProxyProvider implements ProxyProvider, NitroHttpProxyServerCa private String originalCookies; public NitroProxyProvider(HProxySetter proxySetter, HStateSetter stateSetter, HConnection connection) { + final NitroAuthority authority = new NitroAuthority(); + final NitroCertificateSniffingManager certificateManager = new NitroCertificateSniffingManager(authority); + this.proxySetter = proxySetter; this.stateSetter = stateSetter; this.connection = connection; - this.nitroHttpProxy = new NitroHttpProxy(this); - this.nitroWebsocketProxy = new NitroWebsocketProxy(proxySetter, stateSetter, connection, this); + this.nitroHttpProxy = new NitroHttpProxy(this, certificateManager); + this.nitroWebsocketProxy = new NitroWebsocketProxy(proxySetter, stateSetter, connection, this, certificateManager); this.abortLock = new AtomicBoolean(); } diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroCertificateSniffingManager.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroCertificateSniffingManager.java index 35f4c2d..c5c3d63 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroCertificateSniffingManager.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroCertificateSniffingManager.java @@ -20,12 +20,31 @@ public class NitroCertificateSniffingManager implements MitmManager { private static final boolean DEBUG = false; private final BouncyCastleSslEngineSource sslEngineSource; + private final Authority authority; - public NitroCertificateSniffingManager(Authority authority) throws RootCertificateException { + public NitroCertificateSniffingManager(Authority authority) { + this.authority = authority; try { sslEngineSource = new BouncyCastleSslEngineSource(authority, true, true, null); } catch (final Exception e) { - throw new RootCertificateException("Errors during assembling root CA.", e); + throw new RuntimeException(new RootCertificateException("Errors during assembling root CA.", e)); + } + } + + public Authority getAuthority() { + return authority; + } + + public SSLEngine websocketSslEngine(String commonName) { + final SubjectAlternativeNameHolder san = new SubjectAlternativeNameHolder(); + + san.addDomainName("localhost"); + san.addIpAddress("127.0.0.1"); + + try { + return sslEngineSource.createCertForHost(commonName, san); + } catch (Exception e) { + throw new FakeCertificateException("Failed to create WebSocket certificate", e); } } diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxy.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxy.java index c0098f3..cb7e0ba 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxy.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxy.java @@ -12,8 +12,6 @@ import javafx.scene.control.ButtonType; import javafx.scene.control.Label; import org.littleshoot.proxy.HttpProxyServer; import org.littleshoot.proxy.impl.DefaultHttpProxyServer; -import org.littleshoot.proxy.mitm.Authority; -import org.littleshoot.proxy.mitm.RootCertificateException; import java.io.File; import java.io.IOException; @@ -25,20 +23,20 @@ public class NitroHttpProxy { private static final String ADMIN_WARNING_KEY = "admin_warning_dialog"; private static final AtomicBoolean SHUTDOWN_HOOK = new AtomicBoolean(); - private final Authority authority; private final NitroOsFunctions osFunctions; private final NitroHttpProxyServerCallback serverCallback; + private final NitroCertificateSniffingManager certificateManager; private HttpProxyServer proxyServer = null; - public NitroHttpProxy(NitroHttpProxyServerCallback serverCallback) { + public NitroHttpProxy(NitroHttpProxyServerCallback serverCallback, NitroCertificateSniffingManager certificateManager) { this.serverCallback = serverCallback; - this.authority = new NitroAuthority(); + this.certificateManager = certificateManager; this.osFunctions = NitroOsFunctionsFactory.create(); } private boolean initializeCertificate() { - final File certificate = this.authority.aliasFile(".pem"); + final File certificate = this.certificateManager.getAuthority().aliasFile(".pem"); // All good if certificate is already trusted. if (this.osFunctions.isRootCertificateTrusted(certificate)) { @@ -80,7 +78,7 @@ public class NitroHttpProxy { return false; } - return this.osFunctions.installRootCertificate(this.authority.aliasFile(".pem")); + return this.osFunctions.installRootCertificate(this.certificateManager.getAuthority().aliasFile(".pem")); } /** @@ -100,33 +98,28 @@ public class NitroHttpProxy { public boolean start() { setupShutdownHook(); - try { - proxyServer = DefaultHttpProxyServer.bootstrap() - .withPort(NitroConstants.HTTP_PORT) - .withManInTheMiddle(new NitroCertificateSniffingManager(authority)) - .withFiltersSource(new NitroHttpProxyFilterSource(serverCallback)) - .withTransparent(true) - .start(); + proxyServer = DefaultHttpProxyServer.bootstrap() + .withPort(NitroConstants.HTTP_PORT) + .withManInTheMiddle(this.certificateManager) + .withFiltersSource(new NitroHttpProxyFilterSource(serverCallback)) + .withTransparent(true) + .start(); - if (!initializeCertificate()) { - proxyServer.stop(); + if (!initializeCertificate()) { + proxyServer.stop(); - System.out.println("Failed to initialize certificate"); - return false; - } - - if (!registerProxy()) { - proxyServer.stop(); - - System.out.println("Failed to register certificate"); - return false; - } - - return true; - } catch (RootCertificateException e) { - e.printStackTrace(); + System.out.println("Failed to initialize certificate"); return false; } + + if (!registerProxy()) { + proxyServer.stop(); + + System.out.println("Failed to register certificate"); + return false; + } + + return true; } public void pause() { diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroSslContextFactory.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroSslContextFactory.java new file mode 100644 index 0000000..9e12b69 --- /dev/null +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroSslContextFactory.java @@ -0,0 +1,20 @@ +package gearth.protocol.connection.proxy.nitro.http; + +import org.eclipse.jetty.util.ssl.SslContextFactory; + +import javax.net.ssl.SSLEngine; + +public class NitroSslContextFactory extends SslContextFactory.Server { + + private final NitroCertificateSniffingManager certificateManager; + + public NitroSslContextFactory(NitroCertificateSniffingManager certificateManager) { + this.certificateManager = certificateManager; + } + + @Override + public SSLEngine newSSLEngine(String host, int port) { + System.out.printf("[NitroSslContextFactory] Creating SSLEngine for %s:%d%n", host, port); + return certificateManager.websocketSslEngine(host); + } +} diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/websocket/NitroWebsocketProxy.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/websocket/NitroWebsocketProxy.java index c60f8c2..a21244c 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/websocket/NitroWebsocketProxy.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/websocket/NitroWebsocketProxy.java @@ -4,7 +4,8 @@ import gearth.protocol.HConnection; import gearth.protocol.connection.HProxySetter; import gearth.protocol.connection.HStateSetter; import gearth.protocol.connection.proxy.nitro.NitroProxyProvider; -import org.eclipse.jetty.server.Connector; +import gearth.protocol.connection.proxy.nitro.http.NitroCertificateSniffingManager; +import gearth.protocol.connection.proxy.nitro.http.NitroSslContextFactory; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; @@ -21,19 +22,37 @@ public class NitroWebsocketProxy { private final HStateSetter stateSetter; private final HConnection connection; private final NitroProxyProvider proxyProvider; + private final NitroCertificateSniffingManager certificateManager; private final Server server; + private final int serverPort; - public NitroWebsocketProxy(HProxySetter proxySetter, HStateSetter stateSetter, HConnection connection, NitroProxyProvider proxyProvider) { + public NitroWebsocketProxy(HProxySetter proxySetter, + HStateSetter stateSetter, + HConnection connection, + NitroProxyProvider proxyProvider, + NitroCertificateSniffingManager certificateManager) { this.proxySetter = proxySetter; this.stateSetter = stateSetter; this.connection = connection; this.proxyProvider = proxyProvider; - this.server = new Server(0); + this.certificateManager = certificateManager; + this.server = new Server(); + this.serverPort = 0; } public boolean start() { try { + // Configure SSL. + final NitroSslContextFactory sslContextFactory = new NitroSslContextFactory(this.certificateManager); + final ServerConnector sslConnector = new ServerConnector(server, sslContextFactory); + + sslConnector.setPort(this.serverPort); + + // Add SSL to the server. + server.addConnector(sslConnector); + + // Configure the WebSocket. final ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); context.setContextPath("/"); From 776d16ed49fd2c1577428ccff5cd7bf1d45c958a Mon Sep 17 00:00:00 2001 From: UnfamiliarLegacy <74633542+UnfamiliarLegacy@users.noreply.github.com> Date: Tue, 28 May 2024 19:31:15 +0200 Subject: [PATCH 5/6] Extra cloudflare cookies --- .../connection/proxy/nitro/http/NitroHttpProxyFilter.java | 7 ++++++- .../connection/proxy/nitro/os/windows/NitroWindows.java | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxyFilter.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxyFilter.java index e6e1152..5d9978d 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxyFilter.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/http/NitroHttpProxyFilter.java @@ -24,11 +24,16 @@ public class NitroHttpProxyFilter extends HttpFiltersAdapter { private static final HashSet CloudflareCookies = new HashSet<>(Arrays.asList( "__cflb", "__cf_bm", + "__cfseq", "cf_ob_info", "cf_use_ob", "__cfwaitingroom", "__cfruid", - "cf_clearance" + "_cfuvid", + "cf_clearance", + "cf_chl_rc_i", + "cf_chl_rc_ni", + "cf_chl_rc_m" )); private static final String HeaderAcceptEncoding = "Accept-Encoding"; diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/os/windows/NitroWindows.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/os/windows/NitroWindows.java index ec55105..fb7d370 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/os/windows/NitroWindows.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/os/windows/NitroWindows.java @@ -15,7 +15,8 @@ public class NitroWindows implements NitroOsFunctions { /** * Semicolon separated hosts to ignore for proxying. */ - private static final String PROXY_IGNORE = "discord.com;discordapp.com;github.com;"; + // habba.io; + private static final String PROXY_IGNORE = "discord.com;discordapp.com;github.com;challenges.cloudflare.com;"; /** * Checks if the certificate is trusted by the local machine. From 1fe7bb22e6e35ed19d847c461cd6d094b63d8f92 Mon Sep 17 00:00:00 2001 From: UnfamiliarLegacy <74633542+UnfamiliarLegacy@users.noreply.github.com> Date: Sat, 1 Jun 2024 18:46:13 +0200 Subject: [PATCH 6/6] Update ws to wss --- .../protocol/connection/proxy/nitro/NitroProxyProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/NitroProxyProvider.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/NitroProxyProvider.java index c073a1a..94d6869 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/NitroProxyProvider.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/NitroProxyProvider.java @@ -126,7 +126,7 @@ public class NitroProxyProvider implements ProxyProvider, NitroHttpProxyServerCa public String replaceWebsocketServer(String configUrl, String websocketUrl) { originalWebsocketUrl = websocketUrl; - return String.format("ws://127.0.0.1:%d", websocketPort); + return String.format("wss://127.0.0.1:%d", websocketPort); } @Override