Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
SocksHandler is a flexible socksifier for sockets created by TCPSocket.new
, Socket.tcp
, or UDPSocket.new
that solves the following issues:
SOCKSSocket
is not easy to use
--enable-socks
, and even if it is available, we cannot use domain names that the network where the program runs cannot resolve since socket classes, including SOCKSSocket
, call getaddrinfo
at initialization.For more details, see the section "Related Work."
Install the gem and add to the application's Gemfile by executing:
$ bundle add socks_handler
If bundler is not being used to manage dependencies, install the gem by executing:
$ gem install socks_handler
Assuming that a SOCKS server that can access the host "nginx" is listening on 127.0.0.1:1080. You can prepare such an environment with the following docker-compose.yml:
version: "2.4"
services:
sockd:
image: wernight/dante
ports:
- 1080:1080
nginx:
image: nginx
Here is an example to create a socket that can access the host "nginx" from the Docker host:
require "socks_handler"
socket = TCPSocket.new("127.0.0.1", 1080) # or Socket.tcp("127.0.0.1", 1080)
SocksHandler::TCP.establish_connection(socket, "nginx", 80)
socket.write(<<~REQUEST.gsub("\n", "\r\n"))
HEAD / HTTP/1.1
Host: nginx
REQUEST
puts socket.gets #=> HTTP/1.1 200 OK
If you want to access the host through the SOCKS server implicitly, you can use SocksHandler.socksify
as follows:
require "socks_handler"
SocksHandler::TCP.socksify([
SocksHandler::ProxyAccessRule.new(
host_patterns: ["nginx"],
socks_server: "127.0.0.1:1080",
)
])
socket = TCPSocket.new("nginx", 80)
socket.write(<<~REQUEST.gsub("\n", "\r\n"))
HEAD / HTTP/1.1
Host: nginx
REQUEST
puts socket.gets #=> HTTP/1.1 200 OK
With SocksHandler::TCP.socksify
, other methods using TCPSocket.new
or Socket.tcp
also access the remote host through the SOCKS server:
require "net/http"
require "socks_handler"
SocksHandler::TCP.socksify([
SocksHandler::ProxyAccessRule.new(
host_patterns: ["nginx"],
socks_server: "127.0.0.1:1080",
)
])
Net::HTTP.start("nginx", 80) do |http|
pp http.head("/") #=> #<Net::HTTPOK 200 OK readbody=true>
end
For more details, see the document of SocksHandler::TCP.socksify
:
$ ri SocksHandler::TCP.socksify
Assuming that a SOCKS server that can access the host "echo", which is a UDP echo server, is listening on 127.0.0.1:1080. You can prepare such an environment with the following docker-compose.yml:
version: "2.4"
services:
sockd:
image: wernight/dante
ports:
- 1080:1080
- 1024-1030:1024-1030/udp
sysctls:
net.ipv4.ip_local_port_range: "1024 1030"
echo:
image: abicky/ncat:latest
command: -e /bin/cat -kul 7
init: true
Here is an example to create a socket that can access the host "nginx" from the Docker host:
require "socks_handler"
tcp_socket = TCPSocket.new("127.0.0.1", 1080) # or Socket.tcp("127.0.0.1", 1080)
udp_socket = SocksHandler::UDP.associate_udp(tcp_socket, "0.0.0.0", 0)
udp_socket.send("hello", 0, "echo", 7)
puts udp_socket.gets #=> hello
As SocksHandler
only socksifies TCP connections created by TCPSocket.new
or Socket.tcp
, it doesn't socksify connections created by native extensions.
For example, assuming that a SOCKS server that can access the host "mysql" is listening on 127.0.0.1:1080 as follows:
version: "2.4"
services:
sockd:
image: wernight/dante
mysql:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: password
The following code raises Mysql2::Error::ConnectionError
because the gem "mysql2" tries to connect to the server via a native extension:
require "mysql2"
SocksHandler.socksify([
SocksHandler::ProxyAccessRule.new(
host_patterns: ["mysql"],
socks_server: "127.0.0.1:1080",
)
])
client = Mysql2::Client.new(
host: "mysql",
port: 3306,
username: "root",
password: "password",
)
client.ping
#=> Unknown MySQL server host 'mysql' (8) (Mysql2::Error::ConnectionError)
The following projects provide similar gems:
On macOS, you can build ruby with SOCKSSocket
as follows:
$ brew install dante bison
$ git clone https://github.com/ruby/ruby.git
$ cd ruby
$ git checkout v3_2_2
$ ./configure --enable-socks
$ PATH="/usr/local/opt/bison/bin:$PATH" make -j$(nproc) install
$ cat <<EOF >/etc/socks.conf
route {
from: 0.0.0.0/0 to: 0.0.0.0/0 via: 127.0.0.1 port = 1080
proxyprotocol: socks_v5
method: none
}
EOF
Here is example code to access nginx launched using docker-compose.yml in the section "Usage":
require "socket"
ip = `docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker compose ps -q nginx)`.chomp
[ip, "nginx"].each do |host|
puts "Send an HTTP request to #{host}"
socket = SOCKSSocket.new(host, 80)
socket.write(<<~REQUEST.gsub("\n", "\r\n"))
HEAD / HTTP/1.1
Host: nginx
REQUEST
puts "Received: #{socket.gets}"
end
As you can see below, we cannot use the domain name "nginx" since socket classes, including SOCKSSocket
, call getaddrinfo
at initialization:
$ /usr/local/bin/ruby /path/to/code.rb
Send an HTTP request to 192.168.160.4
Received: HTTP/1.1 200 OK
Send an HTTP request to nginx
code.rb:6:in `initialize': getaddrinfo: nodename nor servname provided, or not known (SocketError)
from code.rb:6:in `new'
from code.rb:6:in `block in <main>'
from code.rb:4:in `each'
from code.rb:4:in `<main>'
ProxyChains-NG is a socksifier that works well even on macOS.
On macOS, you can install it as follows:
$ brew install proxychains-ng
Then edit /usr/local/etc/proxychains.conf
to use the socks server listening on 127.0.0.1:1080:
[ProxyList]
socks5 127.0.0.1 1080
ProxyChains-NG can socksify even connections created by native extensions. Here is example code to demonstrate it:
require "net/http"
require "mysql2"
Net::HTTP.start("nginx", 80) do |http|
pp http.head("/")
end
client = Mysql2::Client.new(
host: "mysql",
port: 3306,
username: "root",
password: "password",
)
puts client.ping
As you can see below, the program can access containers though the socks server:
$ proxychains4 /usr/local/bin/ruby /path/to/code.rb
[proxychains] config file found: /usr/local/etc/proxychains.conf
[proxychains] preloading /usr/local/Cellar/proxychains-ng/4.16/lib/libproxychains4.dylib
[proxychains] DLL init: proxychains-ng 4.16
[proxychains] Strict chain ... 127.0.0.1:1080 ... nginx:80 ... OK
#<Net::HTTPOK 200 OK readbody=true>
[proxychains] Strict chain ... 127.0.0.1:1080 ... mysql:3306 ... OK
true
However, it doesn't work if Ruby is managed by rbenv:
$ rbenv local
3.2.2
$ proxychains4 ruby /path/to/code.rb
[proxychains] config file found: /usr/local/etc/proxychains.conf
[proxychains] preloading /usr/local/Cellar/proxychains-ng/4.16/lib/libproxychains4.dylib
/Users/arabiki/.anyenv/envs/rbenv/versions/3.2.2/lib/ruby/3.2.0/net/http.rb:1271:in `initialize': Failed to open TCP connection to nginx:80 (getaddrinfo: nodename nor servname provided, or not known) (SocketError)
-- snip --
Maybe the reason is that macOS doesn't allow any system binaries to preload libraries and rbenv uses /usr/bin/env
:
$ proxychains4 env /usr/local/bin/ruby /path/to/code.rb
[proxychains] config file found: /usr/local/etc/proxychains.conf
[proxychains] preloading /usr/local/Cellar/proxychains-ng/4.16/lib/libproxychains4.dylib
/usr/local/lib/ruby/3.2.0/net/http.rb:1271:in `initialize': Failed to open TCP connection to nginx:80 (getaddrinfo: nodename nor servname provided, or not known) (SocketError)
-- snip --
Although ProxyChains-NG works well in almost all cases, it cannot use domain names to determine whether to access them through a socks proxy.
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and the created tag, and push the .gem
file to rubygems.org.
Bug reports and pull requests are welcome on GitHub at https://github.com/abicky/socks_handler.
The gem is available as open source under the terms of the MIT License.
FAQs
Unknown package
We found that socks_handler demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.