Compare commits
No commits in common. "725f6501937cd410a071f85cf3037b8279be4179" and "fa9b7b183800b7ae6c85f6cbdf7150b69268affe" have entirely different histories.
725f650193
...
fa9b7b1838
2
.gitignore
vendored
2
.gitignore
vendored
@ -21,8 +21,6 @@ build/
|
|||||||
*.pb.go
|
*.pb.go
|
||||||
|
|
||||||
# Go workspace file
|
# Go workspace file
|
||||||
cryptolib
|
|
||||||
go.work
|
go.work
|
||||||
.idea
|
.idea
|
||||||
config.yml
|
config.yml
|
||||||
!examples/config.yml
|
|
||||||
|
19
Dockerfile
19
Dockerfile
@ -1,19 +0,0 @@
|
|||||||
FROM golang:1.21-alpine AS builder
|
|
||||||
WORKDIR /app
|
|
||||||
COPY ./ ./
|
|
||||||
RUN set -eux; \
|
|
||||||
apk add --no-cache bash make git protoc protobuf-dev dumb-init mailcap ca-certificates tzdata; \
|
|
||||||
update-ca-certificates; \
|
|
||||||
make install_protobuf build
|
|
||||||
|
|
||||||
FROM scratch
|
|
||||||
WORKDIR /
|
|
||||||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
|
||||||
COPY --from=builder /etc/mime.types /etc/mime.types
|
|
||||||
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
|
|
||||||
COPY --from=builder /usr/bin/dumb-init /bin/dumb-init
|
|
||||||
COPY --from=builder /app/build/sshpoke /sshpoke
|
|
||||||
EXPOSE 25680
|
|
||||||
EXPOSE 25681
|
|
||||||
ENTRYPOINT ["/bin/dumb-init", "--"]
|
|
||||||
CMD ["/sshpoke"]
|
|
26
Makefile
26
Makefile
@ -8,7 +8,7 @@ GO_VERSION=$(shell go version | sed -e 's/go version //')
|
|||||||
VERSION=$(shell git describe --tags 2>/dev/null || git log --format="v0.0.0-%h" -n 1 || echo "v0.0.0-unknown")
|
VERSION=$(shell git describe --tags 2>/dev/null || git log --format="v0.0.0-%h" -n 1 || echo "v0.0.0-unknown")
|
||||||
LDFLAGS="-X 'github.com/Neur0toxine/sshpoke/cmd.Version=${VERSION}'"
|
LDFLAGS="-X 'github.com/Neur0toxine/sshpoke/cmd.Version=${VERSION}'"
|
||||||
|
|
||||||
.PHONY: run clean_backend clone_sshlib
|
.PHONY: run clean_backend
|
||||||
|
|
||||||
build: generate deps fmt
|
build: generate deps fmt
|
||||||
@echo " ► Building with ${GO_VERSION}"
|
@echo " ► Building with ${GO_VERSION}"
|
||||||
@ -42,23 +42,17 @@ install_protobuf:
|
|||||||
@go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
|
@go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
|
||||||
@go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
|
@go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
|
||||||
|
|
||||||
clone_cryptolib:
|
update_sshlib:
|
||||||
@rm -rf cryptolib && \
|
@rm -rf cryptolib && \
|
||||||
git clone https://go.googlesource.com/crypto cryptolib && \
|
git clone https://go.googlesource.com/crypto cryptolib && \
|
||||||
mv cryptolib/internal/poly1305 cryptolib/ssh/internal/ && \
|
rm -rf pkg/proto/ssh && \
|
||||||
find cryptolib/ssh -type f -name '*.go' -exec sed -i 's?golang.org/x/crypto/ssh?github.com/Neur0toxine/sshpoke/pkg/proto/ssh?g' {} \; && \
|
|
||||||
find cryptolib/ssh -type f -name '*.go' -exec sed -i 's?golang.org/x/crypto/internal/poly1305?github.com/Neur0toxine/sshpoke/pkg/proto/ssh/internal/poly1305?g' {} \; && \
|
|
||||||
find cryptolib/ssh -type f -name '*_test.go' -delete && \
|
|
||||||
rm -rf cryptolib/ssh/test && \
|
|
||||||
rm -rf cryptolib/ssh/testdata
|
|
||||||
|
|
||||||
update_sshlib_patch:
|
|
||||||
@diff -Naru cryptolib/ssh pkg/proto/ssh > patch/ssh_proto_patches.patch
|
|
||||||
|
|
||||||
update_sshlib: clone_cryptolib
|
|
||||||
@mv pkg/proto/ssh pkg/proto/ssh.bak && \
|
|
||||||
mv cryptolib/ssh pkg/proto/ && \
|
mv cryptolib/ssh pkg/proto/ && \
|
||||||
patch -p0 < patch/ssh_proto_patches.patch && \
|
mv cryptolib/internal/poly1305 pkg/proto/ssh/internal/ && \
|
||||||
rm -rf pkg/proto/ssh.bak && \
|
find pkg/proto/ssh -type f -name '*.go' -exec sed -i 's?golang.org/x/crypto/ssh?github.com/Neur0toxine/sshpoke/pkg/proto/ssh?g' {} \; && \
|
||||||
|
find pkg/proto/ssh -type f -name '*.go' -exec sed -i 's?golang.org/x/crypto/internal/poly1305?github.com/Neur0toxine/sshpoke/pkg/proto/ssh/internal/poly1305?g' {} \; && \
|
||||||
|
find pkg/proto/ssh -type f -name '*_test.go' -delete && \
|
||||||
|
patch -p0 < patch/ssh_fakehost.patch && \
|
||||||
|
rm -rf pkg/proto/ssh/test && \
|
||||||
|
rm -rf pkg/proto/ssh/testdata && \
|
||||||
rm -rf cryptolib
|
rm -rf cryptolib
|
||||||
|
|
||||||
|
@ -1,21 +1,18 @@
|
|||||||
# Enable or disable debug logging. Default: false
|
# Enable or disable debug logging.
|
||||||
debug: true
|
debug: true
|
||||||
# API settings.
|
# API settings.
|
||||||
api:
|
api:
|
||||||
web:
|
# Local port for Web API. Will be bound to localhost.
|
||||||
# Local port for Web API. Will be bound to localhost.
|
web_port: 25680
|
||||||
port: 25680
|
# Local port for plugin API. Will listen on all interfaces because it has auth.
|
||||||
token: "VbNG6T6wYmj9YHM6etuZgN35"
|
plugin_port: 25681
|
||||||
plugin:
|
|
||||||
# Local port for plugin API. Will listen on all interfaces because it has auth.
|
|
||||||
port: 25681
|
|
||||||
# Docker client preferences.
|
# Docker client preferences.
|
||||||
docker:
|
docker:
|
||||||
# Extract client params from the environment. Default: true
|
# Extract client params from the environment.
|
||||||
from_env: true
|
from_env: true
|
||||||
# Cert path for the Docker client.
|
# Cert path for the Docker client.
|
||||||
cert_path: ~
|
cert_path: ~
|
||||||
# Set it to false to disable TLS cert verification. Default: true
|
# Set it to false to disable TLS cert verification.
|
||||||
tls_verify: true
|
tls_verify: true
|
||||||
# Docker host. Can be useful for running containers alongside remote plugin (although it sounds weird to do so).
|
# Docker host. Can be useful for running containers alongside remote plugin (although it sounds weird to do so).
|
||||||
host: ~
|
host: ~
|
||||||
@ -37,19 +34,14 @@ servers:
|
|||||||
# This disables remote host resolution and forcibly uses server IP for remote host.
|
# This disables remote host resolution and forcibly uses server IP for remote host.
|
||||||
# It's the same as this syntax for sish: `ssh -R addr:80:localhost:80 your.sish.server`
|
# It's the same as this syntax for sish: `ssh -R addr:80:localhost:80 your.sish.server`
|
||||||
# Set this to true if you're using sish, otherwise you'll get weird domains with IP's in them.
|
# Set this to true if you're using sish, otherwise you'll get weird domains with IP's in them.
|
||||||
# Default: false
|
|
||||||
fake_remote_host: true
|
fake_remote_host: true
|
||||||
# Disables PTY request for this server. Default: false
|
# Disables PTY request for this server.
|
||||||
nopty: true
|
nopty: true
|
||||||
# Requests interactive shell for SSH sessions. Should be `true` for the `commands`.
|
# Requests interactive shell for SSH sessions. Should be `true` for the `commands`.
|
||||||
# You can also pass a string with shell binary, for example, "/bin/sh".
|
# You can also pass a string with shell binary, for example, "/bin/sh".
|
||||||
# Note: commands will be executed using provided shell binary.
|
# Note: commands will be executed using provided shell binary.
|
||||||
# Note (2): some servers won't send you any data until you request a shell even without a PTY.
|
|
||||||
# You can use a combination of `nopty: true` & `shell: true`. Also, even with PTY you may need `shell` to be `true`.
|
|
||||||
# Default: false
|
|
||||||
shell: false
|
shell: false
|
||||||
# Spoof client version with provided (value below is taken directly from OpenSSH). This value must be compliant with RFC-4253.
|
# Spoof client version with provided (value below is taken directly from OpenSSH). This value must be compliant with RFC-4253.
|
||||||
# Default: SSH-2.0-Go
|
|
||||||
client_version: "SSH-2.0-OpenSSH_9.5"
|
client_version: "SSH-2.0-OpenSSH_9.5"
|
||||||
# Authentication data.
|
# Authentication data.
|
||||||
auth:
|
auth:
|
||||||
@ -65,9 +57,9 @@ servers:
|
|||||||
mode: multi
|
mode: multi
|
||||||
# Keep-alive settings. Remove to disable keep-alive completely.
|
# Keep-alive settings. Remove to disable keep-alive completely.
|
||||||
keepalive:
|
keepalive:
|
||||||
# Interval for keep-alive requests in seconds. Default: 0 (disabled).
|
# Interval for keep-alive requests in seconds.
|
||||||
interval: 1
|
interval: 1
|
||||||
# How many attempts should fail to forcibly restart the connection. Default: 0 (disabled).
|
# How many attempts should fail to forcibly restart the connection.
|
||||||
max_attempts: 2
|
max_attempts: 2
|
||||||
# Regular expression that will be used to extract domain from stdout & stderr. Useful for services like sish or
|
# Regular expression that will be used to extract domain from stdout & stderr. Useful for services like sish or
|
||||||
# localhost.run. `commands` output will also be parsed by this regex.
|
# localhost.run. `commands` output will also be parsed by this regex.
|
||||||
@ -75,7 +67,6 @@ servers:
|
|||||||
# - !webUrl - any HTTP or HTTPS URL.
|
# - !webUrl - any HTTP or HTTPS URL.
|
||||||
# - !httpUrl - any HTTP URL.
|
# - !httpUrl - any HTTP URL.
|
||||||
# - !httpsUrl - any HTTPS URL.
|
# - !httpsUrl - any HTTPS URL.
|
||||||
# Default: "" (disabled).
|
|
||||||
domain_extract_regex: "!httpsUrl"
|
domain_extract_regex: "!httpsUrl"
|
||||||
# Host keys to prevent MITM. You can obtain those via `ssh-keyscan <address>` (specify `-p` for non-standard port).
|
# Host keys to prevent MITM. You can obtain those via `ssh-keyscan <address>` (specify `-p` for non-standard port).
|
||||||
# Always use '|' YAML syntax here (not '>') or sshpoke won't be able to parse keys.
|
# Always use '|' YAML syntax here (not '>') or sshpoke won't be able to parse keys.
|
||||||
@ -104,13 +95,6 @@ servers:
|
|||||||
interval: 1
|
interval: 1
|
||||||
max_attempts: 2
|
max_attempts: 2
|
||||||
domain_extract_regex: "!webUrl"
|
domain_extract_regex: "!webUrl"
|
||||||
# Read output data from raw SSH packets. This can help if domain_extract_regex couldn't catch the domain.
|
|
||||||
# You can also enable debug logging - it should contain outputs from ssh server.
|
|
||||||
# Default: false
|
|
||||||
read_raw_packets: true
|
|
||||||
# Enable or disable sessions output. Disabling this will stop domain_extract_regex from reading commands output.
|
|
||||||
# Default: true
|
|
||||||
read_sessions_output: false
|
|
||||||
- name: ssh-demo-commands
|
- name: ssh-demo-commands
|
||||||
driver: ssh
|
driver: ssh
|
||||||
params:
|
params:
|
@ -1,10 +0,0 @@
|
|||||||
version: "3.8"
|
|
||||||
services:
|
|
||||||
sshpoke:
|
|
||||||
image: "neur0toxine/sshpoke:latest"
|
|
||||||
container_name: "sshpoke"
|
|
||||||
network_mode: host
|
|
||||||
restart: always
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
||||||
- ./config.yml:/config.yml
|
|
@ -61,7 +61,7 @@ func (p *pluginAPI) receiverForContext(ctx context.Context) plugin.Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func StartPluginAPI() {
|
func StartPluginAPI() {
|
||||||
port := config.Default.API.Plugin.Port
|
port := config.Default.API.PluginPort
|
||||||
if port == 0 {
|
if port == 0 {
|
||||||
port = plugin2.DefaultPort
|
port = plugin2.DefaultPort
|
||||||
}
|
}
|
||||||
|
@ -19,17 +19,8 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type API struct {
|
type API struct {
|
||||||
Web WebAPI `mapstructure:"web"`
|
WebPort int `mapstructure:"web_port" validate:"gte=0,lte=65535"`
|
||||||
Plugin PluginAPI `mapstructure:"plugin"`
|
PluginPort int `mapstructure:"plugin_port" validate:"gte=0,lte=65535"`
|
||||||
}
|
|
||||||
|
|
||||||
type WebAPI struct {
|
|
||||||
Port int `mapstructure:"port" validate:"gte=0,lte=65535"`
|
|
||||||
Token string `mapstructure:"token"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PluginAPI struct {
|
|
||||||
Port int `mapstructure:"port" validate:"gte=0,lte=65535"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type DockerConfig struct {
|
type DockerConfig struct {
|
||||||
|
@ -20,7 +20,6 @@ import (
|
|||||||
"github.com/Neur0toxine/sshpoke/pkg/dto"
|
"github.com/Neur0toxine/sshpoke/pkg/dto"
|
||||||
"github.com/Neur0toxine/sshpoke/pkg/proto/ssh"
|
"github.com/Neur0toxine/sshpoke/pkg/proto/ssh"
|
||||||
"github.com/Neur0toxine/sshpoke/pkg/proto/ssh/knownhosts"
|
"github.com/Neur0toxine/sshpoke/pkg/proto/ssh/knownhosts"
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const KnownHostsFile = "known_hosts"
|
const KnownHostsFile = "known_hosts"
|
||||||
@ -86,40 +85,15 @@ func (d *SSH) forward(val sshtun.Forward, domainMatcher func(string)) conn {
|
|||||||
d.Log())
|
d.Log())
|
||||||
ctx, cancel := context.WithCancel(d.Context())
|
ctx, cancel := context.WithCancel(d.Context())
|
||||||
tunDbgLog := d.Log().With("ssh-output", val.Remote.String())
|
tunDbgLog := d.Log().With("ssh-output", val.Remote.String())
|
||||||
var outputReaderCb sshtun.SessionCallback
|
|
||||||
if d.params.ReadSessionsOutput == nil || (*d.params.ReadSessionsOutput) {
|
|
||||||
outputReaderCb = sshtun.OutputReaderCallback(func(msg string) {
|
|
||||||
msg = strings.TrimSpace(msg)
|
|
||||||
if msg == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
tunDbgLog.Debug("session: ", msg)
|
|
||||||
if domainMatcher != nil {
|
|
||||||
domainMatcher(msg)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
go tun.Connect(ctx,
|
go tun.Connect(ctx,
|
||||||
d.buildHandshakeLineCallback(domainMatcher, tunDbgLog),
|
|
||||||
sshtun.BannerDebugLogCallback(tunDbgLog),
|
sshtun.BannerDebugLogCallback(tunDbgLog),
|
||||||
outputReaderCb)
|
sshtun.OutputReaderCallback(func(msg string) {
|
||||||
return conn{ctx: ctx, cancel: cancel, tun: tun}
|
d.Log().Debug(msg)
|
||||||
}
|
|
||||||
|
|
||||||
func (d *SSH) buildHandshakeLineCallback(domainMatcher func(string), tunDbgLog *zap.SugaredLogger) func(string) {
|
|
||||||
if d.params.ReadRawPackets {
|
|
||||||
return func(msg string) {
|
|
||||||
msg = strings.TrimSpace(msg)
|
|
||||||
if msg == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
tunDbgLog.Debugf("ssh: %s", msg)
|
|
||||||
if domainMatcher != nil {
|
if domainMatcher != nil {
|
||||||
domainMatcher(msg)
|
domainMatcher(msg)
|
||||||
}
|
}
|
||||||
}
|
}))
|
||||||
}
|
return conn{ctx: ctx, cancel: cancel, tun: tun}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SSH) buildHostKeyCallback() ssh.HostKeyCallback {
|
func (d *SSH) buildHostKeyCallback() ssh.HostKeyCallback {
|
||||||
@ -306,10 +280,6 @@ func (d *SSH) remoteEndpoint(remoteHost string) sshtun.Endpoint {
|
|||||||
if port == 0 {
|
if port == 0 {
|
||||||
port = 80
|
port = 80
|
||||||
}
|
}
|
||||||
if remoteHost == "" && !d.params.FakeRemoteHost {
|
|
||||||
// Listen on all interfaces if no host was provided.
|
|
||||||
remoteHost = "0.0.0.0"
|
|
||||||
}
|
|
||||||
return sshtun.Endpoint{
|
return sshtun.Endpoint{
|
||||||
Host: remoteHost,
|
Host: remoteHost,
|
||||||
Port: port,
|
Port: port,
|
||||||
|
@ -15,8 +15,6 @@ type Params struct {
|
|||||||
Auth types.Auth `mapstructure:"auth"`
|
Auth types.Auth `mapstructure:"auth"`
|
||||||
KeepAlive types.KeepAlive `mapstructure:"keepalive"`
|
KeepAlive types.KeepAlive `mapstructure:"keepalive"`
|
||||||
DomainExtractRegex string `mapstructure:"domain_extract_regex" validate:"validregexp"`
|
DomainExtractRegex string `mapstructure:"domain_extract_regex" validate:"validregexp"`
|
||||||
ReadRawPackets bool `mapstructure:"read_raw_packets"`
|
|
||||||
ReadSessionsOutput *bool `mapstructure:"read_sessions_output"`
|
|
||||||
Mode types.DomainMode `mapstructure:"mode" validate:"required,oneof=single multi"`
|
Mode types.DomainMode `mapstructure:"mode" validate:"required,oneof=single multi"`
|
||||||
FakeRemoteHost bool `mapstructure:"fake_remote_host"`
|
FakeRemoteHost bool `mapstructure:"fake_remote_host"`
|
||||||
NoPTY bool `mapstructure:"nopty"`
|
NoPTY bool `mapstructure:"nopty"`
|
||||||
|
@ -39,11 +39,11 @@ type TunnelConfig struct {
|
|||||||
type BoolOrStr string
|
type BoolOrStr string
|
||||||
|
|
||||||
func (b BoolOrStr) IsBool() bool {
|
func (b BoolOrStr) IsBool() bool {
|
||||||
return b == "true" || b == "false" || b == "1" || b == "0" || b == ""
|
return b == "true" || b == "false"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b BoolOrStr) Falsy() bool {
|
func (b BoolOrStr) Falsy() bool {
|
||||||
return b == "" || b == "0" || b == "false"
|
return b == "" || b == "false"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b BoolOrStr) String() string {
|
func (b BoolOrStr) String() string {
|
||||||
@ -60,8 +60,7 @@ func New(address, user string, auth []ssh.AuthMethod, sc TunnelConfig, log *zap.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tunnel) Connect(ctx context.Context,
|
func (t *Tunnel) Connect(ctx context.Context, bannerCb ssh.BannerCallback, sessionCb SessionCallback) {
|
||||||
hsLineCb func(msg string), bannerCb ssh.BannerCallback, sessionCb SessionCallback) {
|
|
||||||
if t.connected.Load() {
|
if t.connected.Load() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -70,7 +69,7 @@ func (t *Tunnel) Connect(ctx context.Context,
|
|||||||
backoffTime := backoff.ExponentialWithCappedMax(100*time.Millisecond, 5*time.Second)
|
backoffTime := backoff.ExponentialWithCappedMax(100*time.Millisecond, 5*time.Second)
|
||||||
for {
|
for {
|
||||||
t.connected.Store(true)
|
t.connected.Store(true)
|
||||||
err := t.connect(ctx, hsLineCb, bannerCb, sessionCb)
|
err := t.connect(ctx, bannerCb, sessionCb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.log.Error("connect error: ", err)
|
t.log.Error("connect error: ", err)
|
||||||
}
|
}
|
||||||
@ -87,13 +86,9 @@ func (t *Tunnel) Connect(ctx context.Context,
|
|||||||
|
|
||||||
// connect once to the SSH server. if the connection breaks, we return error and the caller
|
// connect once to the SSH server. if the connection breaks, we return error and the caller
|
||||||
// will try to re-connect
|
// will try to re-connect
|
||||||
func (t *Tunnel) connect(ctx context.Context,
|
func (t *Tunnel) connect(ctx context.Context, bannerCb ssh.BannerCallback, sessionCb SessionCallback) error {
|
||||||
hsLineCb func(string), bannerCb ssh.BannerCallback, sessionCb SessionCallback) error {
|
|
||||||
t.log.Debug("connecting")
|
t.log.Debug("connecting")
|
||||||
sshConfig := &ssh.ClientConfig{
|
sshConfig := &ssh.ClientConfig{
|
||||||
Config: ssh.Config{
|
|
||||||
HandshakePacketReader: newHandshakePerLineReader(hsLineCb),
|
|
||||||
},
|
|
||||||
User: t.user,
|
User: t.user,
|
||||||
Auth: t.authMethods,
|
Auth: t.authMethods,
|
||||||
HostKeyCallback: t.tunConfig.HostKeyCallback,
|
HostKeyCallback: t.tunConfig.HostKeyCallback,
|
||||||
|
@ -15,7 +15,7 @@ import (
|
|||||||
// assume that the underlying net.Conn abruptly died.
|
// assume that the underlying net.Conn abruptly died.
|
||||||
func (t *Tunnel) keepAlive(ctx context.Context, client *ssh.Client, wg *sync.WaitGroup) {
|
func (t *Tunnel) keepAlive(ctx context.Context, client *ssh.Client, wg *sync.WaitGroup) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
if t.tunConfig.KeepAliveInterval <= 0 || t.tunConfig.KeepAliveMax <= 0 {
|
if t.tunConfig.KeepAliveInterval == 0 || t.tunConfig.KeepAliveMax == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
package sshtun
|
|
||||||
|
|
||||||
type handshakePerLineReader struct {
|
|
||||||
buf []byte
|
|
||||||
off int
|
|
||||||
cb func(line string)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newHandshakePerLineReader(cb func(string)) func([]byte) {
|
|
||||||
if cb == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
lr := &handshakePerLineReader{cb: cb, buf: make([]byte, 1024)}
|
|
||||||
return lr.packet
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *handshakePerLineReader) packet(p []byte) {
|
|
||||||
for _, char := range p {
|
|
||||||
if (char == 10 || char == 13) && len(h.buf) > 0 {
|
|
||||||
h.cb(string(h.buf[:h.off]))
|
|
||||||
h.off = 0
|
|
||||||
}
|
|
||||||
if cap(h.buf) <= (h.off + 1) {
|
|
||||||
newBuf := make([]byte, cap(h.buf)+256)
|
|
||||||
copy(newBuf, h.buf)
|
|
||||||
h.buf = newBuf
|
|
||||||
}
|
|
||||||
h.buf[h.off] = char
|
|
||||||
h.off++
|
|
||||||
}
|
|
||||||
}
|
|
@ -37,7 +37,7 @@ func OutputReaderCallback(callback func(string)) SessionCallback {
|
|||||||
|
|
||||||
func BannerDebugLogCallback(log *zap.SugaredLogger) ssh.BannerCallback {
|
func BannerDebugLogCallback(log *zap.SugaredLogger) ssh.BannerCallback {
|
||||||
return func(msg string) error {
|
return func(msg string) error {
|
||||||
log.Debug("banner: ", msg)
|
log.Debug(msg)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,5 +2,5 @@ package types
|
|||||||
|
|
||||||
type KeepAlive struct {
|
type KeepAlive struct {
|
||||||
Interval int `mapstructure:"interval" validate:"gte=0"`
|
Interval int `mapstructure:"interval" validate:"gte=0"`
|
||||||
MaxAttempts int `mapstructure:"max_attempts" validate:"gte=0"`
|
MaxAttempts int `mapstructure:"max_attempts" validate:"gte=1"`
|
||||||
}
|
}
|
||||||
|
@ -1,68 +1,5 @@
|
|||||||
diff -Naru cryptolib/ssh/cipher.go pkg/proto/ssh/cipher.go
|
--- cryptolib/ssh/tcpip.go 2023-11-18 22:20:50.609146922 +0300
|
||||||
--- cryptolib/ssh/cipher.go 2023-11-21 18:52:03.117248053 +0300
|
+++ pkg/proto/ssh/tcpip.go 2023-11-18 22:19:08.891684669 +0300
|
||||||
+++ pkg/proto/ssh/cipher.go 2023-11-21 17:19:04.688042738 +0300
|
|
||||||
@@ -16,8 +16,8 @@
|
|
||||||
"hash"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
- "golang.org/x/crypto/chacha20"
|
|
||||||
"github.com/Neur0toxine/sshpoke/pkg/proto/ssh/internal/poly1305"
|
|
||||||
+ "golang.org/x/crypto/chacha20"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
diff -Naru cryptolib/ssh/common.go pkg/proto/ssh/common.go
|
|
||||||
--- cryptolib/ssh/common.go 2023-11-21 18:52:03.217249582 +0300
|
|
||||||
+++ pkg/proto/ssh/common.go 2023-11-21 17:28:23.221203344 +0300
|
|
||||||
@@ -7,14 +7,13 @@
|
|
||||||
import (
|
|
||||||
"crypto"
|
|
||||||
"crypto/rand"
|
|
||||||
+ _ "crypto/sha1"
|
|
||||||
+ _ "crypto/sha256"
|
|
||||||
+ _ "crypto/sha512"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"math"
|
|
||||||
"sync"
|
|
||||||
-
|
|
||||||
- _ "crypto/sha1"
|
|
||||||
- _ "crypto/sha256"
|
|
||||||
- _ "crypto/sha512"
|
|
||||||
)
|
|
||||||
|
|
||||||
// These are string constants in the SSH protocol.
|
|
||||||
@@ -279,6 +278,9 @@
|
|
||||||
// The allowed MAC algorithms. If unspecified then a sensible default is
|
|
||||||
// used. Unsupported values are silently ignored.
|
|
||||||
MACs []string
|
|
||||||
+
|
|
||||||
+ // Called on every incoming handshake packet for client. Only receives data and extended data packets.
|
|
||||||
+ HandshakePacketReader func(p []byte)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetDefaults sets sensible values for unset fields in config. This is
|
|
||||||
diff -Naru cryptolib/ssh/handshake.go pkg/proto/ssh/handshake.go
|
|
||||||
--- cryptolib/ssh/handshake.go 2023-11-21 18:52:03.209249460 +0300
|
|
||||||
+++ pkg/proto/ssh/handshake.go 2023-11-21 18:51:58.681180283 +0300
|
|
||||||
@@ -401,6 +401,14 @@
|
|
||||||
t.printPacket(p, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
+ if t.config.HandshakePacketReader != nil && p[0] == msgChannelData {
|
|
||||||
+ data, err := decode(p)
|
|
||||||
+ packetData, ok := data.(*channelDataMsg)
|
|
||||||
+ if err == nil && ok && packetData != nil {
|
|
||||||
+ t.config.HandshakePacketReader(packetData.Rest)
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
if first && p[0] != msgKexInit {
|
|
||||||
return nil, fmt.Errorf("ssh: first packet should be msgKexInit")
|
|
||||||
}
|
|
||||||
diff -Naru cryptolib/ssh/tcpip.go pkg/proto/ssh/tcpip.go
|
|
||||||
--- cryptolib/ssh/tcpip.go 2023-11-21 18:52:03.129248237 +0300
|
|
||||||
+++ pkg/proto/ssh/tcpip.go 2023-11-21 17:19:04.688042738 +0300
|
|
||||||
@@ -101,14 +101,18 @@
|
@@ -101,14 +101,18 @@
|
||||||
// ListenTCP requests the remote peer open a listening socket
|
// ListenTCP requests the remote peer open a listening socket
|
||||||
// on laddr. Incoming connections will be available by calling
|
// on laddr. Incoming connections will be available by calling
|
@ -7,13 +7,14 @@ package ssh
|
|||||||
import (
|
import (
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
_ "crypto/sha1"
|
|
||||||
_ "crypto/sha256"
|
|
||||||
_ "crypto/sha512"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
_ "crypto/sha1"
|
||||||
|
_ "crypto/sha256"
|
||||||
|
_ "crypto/sha512"
|
||||||
)
|
)
|
||||||
|
|
||||||
// These are string constants in the SSH protocol.
|
// These are string constants in the SSH protocol.
|
||||||
@ -278,9 +279,6 @@ type Config struct {
|
|||||||
// The allowed MAC algorithms. If unspecified then a sensible default is
|
// The allowed MAC algorithms. If unspecified then a sensible default is
|
||||||
// used. Unsupported values are silently ignored.
|
// used. Unsupported values are silently ignored.
|
||||||
MACs []string
|
MACs []string
|
||||||
|
|
||||||
// Called on every incoming handshake packet for client. Only receives data and extended data packets.
|
|
||||||
HandshakePacketReader func(p []byte)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDefaults sets sensible values for unset fields in config. This is
|
// SetDefaults sets sensible values for unset fields in config. This is
|
||||||
|
@ -401,14 +401,6 @@ func (t *handshakeTransport) readOnePacket(first bool) ([]byte, error) {
|
|||||||
t.printPacket(p, false)
|
t.printPacket(p, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.config.HandshakePacketReader != nil && p[0] == msgChannelData {
|
|
||||||
data, err := decode(p)
|
|
||||||
packetData, ok := data.(*channelDataMsg)
|
|
||||||
if err == nil && ok && packetData != nil {
|
|
||||||
t.config.HandshakePacketReader(packetData.Rest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if first && p[0] != msgKexInit {
|
if first && p[0] != msgKexInit {
|
||||||
return nil, fmt.Errorf("ssh: first packet should be msgKexInit")
|
return nil, fmt.Errorf("ssh: first packet should be msgKexInit")
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user