Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SOCKSification #20

Closed
JeremyRand opened this issue Jan 9, 2023 · 3 comments
Closed

SOCKSification #20

JeremyRand opened this issue Jan 9, 2023 · 3 comments

Comments

@JeremyRand
Copy link
Member

JeremyRand commented Jan 9, 2023

It would be useful to be able to redirect outgoing TCP traffic over a SOCKS proxy, like what torsocks does. There are at least 2 ways we might be able to do this:

  1. The application will try to issue a connect syscall, followed by a send syscall. We can intercept the connect syscall and change the destination IP/port to that of the SOCKS proxy. Then, we can intercept the send syscall, which will contain a pointer (in tracee memory space) to the buffer that the application wants to send. We can prepend a SOCKS handshake to that buffer. This was my original idea while writing the project proposal for Outreachy. It's not a great design because it requires fiddling with the tracee's memory, which is not consistent with our goal of memory safety.
  2. Linux v5.6 added a new feature that allows the tracer to duplicate a file descriptor from the tracee without needing to mess with the tracee's memory. This is much more in the spirit of security and memory safety [1], and is probably preferable. The relevant feature is called pidfd_getfd. Documentation exists on man7.org, and further context is available at Phoronix, LWN, and copyconstruct.medium.com, among other sites that I found with a quick DuckDuckGo . The implementation would look like this: we intercept the entrance to the connect syscall, and change the destination IP/port like in approach 1. Then we intercept the exit of the connect syscall, which returns a file descriptor containing the established socket. We duplicate that file descriptor to the tracer via pidfd_getfd, and then immediately perform a SOCKS5 handshake with that socket in the tracer. We then resume the tracee, and everything is SOCKSified; no need to intercept the send syscall.

I am not worried about requiring Linux v5.6. It excludes Debian Buster (Linux v4.19), but I don't think anyone is seriously using Buster for anything security-focused. It also excludes environments like Talos Skiroot (Linux v5.5), but I'm quite sure no one is running Tor inside Skiroot. Debian Buster-Backports and Debian Bullseye are both on Linux v5.10, which will handle it fine. There is a security-focused use case where Linux v5.6 isn't available, which is the ast GPU driver; this driver is popular with security-focused users, but is partially broken in Linux releases from v5.6 to v6.2 inclusive. But I do not think that's worth making our code more complex; users who run the ast driver and need to SOCKSify with ptrace can reasonably be expected to install Linux v6.3 or higher.

Maybe there are other approaches, but the above two are the ones that occurred to me.

[1] This is part of a general trend in Linux to try to avoid forcing programs to manually do memory management that the kernel already can easily do. See kexec_file_load, which is a safer replacement for kexec_load, for another example that's directly relevant to u-root.

@JeremyRand
Copy link
Member Author

@robertmin1 found an existing Go library for pidfd_getfd.

@JeremyRand
Copy link
Member Author

Possibly useful SOCKS5 library.

@robertmin1
Copy link
Collaborator

Method two was prefered and implemented.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants