Injecting additional TCP data in a WFP callout driver

Hello,

I was able to successfully implement a WFP callout driver at the FWPS_LAYER_ALE_CONNECT_REDIRECT_V4 layer and redirect a TCP connection to a local proxy server.

The proxy server receives the connection and I am able to forward it to the original remote address.

Now I have a requirement to support HTTP proxies. So before sending the packets to the proxy, I need to perform an HTTP CONNECT request to the proxy server to establish a tunnel. Once the response of this HTTP request is returned, I can start proxying the packets.

I really struggle to find the correct layer where I should plug into WFP to perform this initial request to the proxy server. I read about FWPM_LAYER_{INBOUND|OUTBOUND}IPPACKET_V{4|6}, FWPM_LAYER{INBOUND|OUTBOUND}_TRANSPORT_V{4|6}, FWPM_LAYER_STREAM_V{4|6}, FWPM_LAYER_ALE_AUTH_CONNECT_V{4|6}, FWPM_LAYER_ALE_FLOW_ESTABLISHED_V{4|6} but I am unsure which would be the correct to perform this injection.

The key here is to inject the additional packets of an HTTP CONNECT request before any other data is sent to the proxy but after the TCP connection to the proxy has been established. Also, on the return path, I need to ignore the response of the proxy server to this HTTP CONNECT request - it should not be forwarded back to the requesting client app as it should be completely transparent for it.

Any tips as how to achieve that would be greatly appreciated.

redirecting a TCP connection and tunneling one via HTTP proxy are very different things. You cannot simply inject HTTP context around the real data on a packet by packet basis. You need to establish a TCP connection, negotiate TLS etc. and then feed encapsulated packets to the HTTP proxy, and de-encapsulate and feed data on the return. Your code becomes a proxy for the proxy

@MBon2, thanks for your response. If I understand correctly, in the FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 callout, the TCP connection between the client and the proxy to which I redirected is already established. Isn’t it possible to then use the FWPM_LAYER_STREAM_V4 layer to mutate/inject an additional IP packet that will contain the HTTP CONNECT request?

I suppose you could try, but that seems much harder than establishing a new TCP connection. TLS negotiation is not just a matter of adding a header or a packet. It requires multiple round trips to negotiate the session parameters. And then all subsequent packets need to be encapsulated and reprocessed (encrypted / compressed)

I can make the HTTP CONNECT request to my proxy server over plain HTTP, it doesn’t require TLS. Once the proxy responds back with Status 200, I can start forwarding the actual packets (which may or may not be over TLS but it doesn’t really matter, it can be any TCP protocol).

Is it possible to somehow delay the outgoing data after establishing the TCP connection at the FWPM_LAYER_STREAM_V4 layer and inject some new packets?

I looked at the FwpsAllocateNetBufferAndNetBufferList method which allows me to create a new data stream with the desired payload and then inject it with FwpsStreamInjectAsync. But I am not sure what should I do with the original packets in my callout - contained at the layerData parameter. What flags should I set to delay sending them, if this is possible at all?

If this cannot be done, let me see if I understand your suggestion about establishing a new TCP connection. Do I need to have another TCP listener at user-land to which I perform the connection redirection? This listener, upon receiving a new connection, will perform the CONNECT request to the proxy server and start tunneling the data?

I have seen tools such as Proxifier or the NetFilter SDK which support this scenario without an additional listener and they use WFP with connect redirect, so I thought this might be possible to be achieved at the kernel driver only.

You have to buffer the data until you are ready to send it. you could proxy it via UM, and that’s probably what i would do until the HTTP side of things was well understood, but it can be all done in KM.

I am not sure I understand what you mean by proxying via UM. At the flow established callout (or some other), I can notify UM about the new TCP connection, but can this UM app somehow write data to this connection before responding back to KM? Can I access the underlying socket in UM and send and receive data to it?

You are making your life extremely difficult with your per packet approach.

  1. Intercept TCP connections (presumably on port 80/443) at ALE_CONNECT_REDIRECT_V4/6
  2. Redirect into a local proxy with context (where the connection was originally destined for)
  3. Use a local proxy to establish connection to your forward proxy / VPN / whatever route you like
  4. Tunnel the original outbound data down that connection
  5. Receive data back from your forward proxy
  6. Send it back into the system

The vast majority of this is user space code, which reduces the risk of your system
Jason

@Jason_Stephenson, thanks for the detailed steps. I have followed your suggestion and I have a question about the redirectContext parameter that I set in my driver:

PREDIRECT_CONTEXT context = (PREDIRECT_CONTEXT)ExAllocatePool2(
    NonPagedPool, 
    sizeof(PREDIRECT_CONTEXT), 
    TAG_NAME_REDIRECT_CONTEXT);

context->RemoteAddress = remoteAddress;
context->RemotePort = remotePort;
connectRequest->localRedirectContext = context;
connectRequest->localRedirectContextSize = sizeof(REDIRECT_CONTEXT);

I am able to successfully retrieve the context in my user-mode app using WSAIoctl. My question is how/when should I release the memory that I allocated in my driver?