Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/js/libs/rsync/rsync.go
2851 views
1
package rsync
2
3
import (
4
"bytes"
5
"context"
6
"fmt"
7
"log/slog"
8
"net"
9
"strconv"
10
"time"
11
12
rsynclib "github.com/Mzack9999/go-rsync/rsync"
13
14
"github.com/praetorian-inc/fingerprintx/pkg/plugins"
15
"github.com/praetorian-inc/fingerprintx/pkg/plugins/services/rsync"
16
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
17
)
18
19
type (
20
// RsyncClient is a client for RSYNC servers.
21
// Internally client uses https://github.com/gokrazy/rsync driver.
22
// @example
23
// ```javascript
24
// const rsync = require('nuclei/rsync');
25
// const client = new rsync.RsyncClient();
26
// ```
27
RsyncClient struct{}
28
29
// IsRsyncResponse is the response from the IsRsync function.
30
// this is returned by IsRsync function.
31
// @example
32
// ```javascript
33
// const rsync = require('nuclei/rsync');
34
// const isRsync = rsync.IsRsync('acme.com', 873);
35
// log(toJSON(isRsync));
36
// ```
37
IsRsyncResponse struct {
38
IsRsync bool
39
Banner string
40
}
41
42
// ListSharesResponse is the response from the ListShares function.
43
// this is returned by ListShares function.
44
// @example
45
// ```javascript
46
// const rsync = require('nuclei/rsync');
47
// const client = new rsync.RsyncClient();
48
// const listShares = client.ListShares('acme.com', 873);
49
// log(toJSON(listShares));
50
RsyncListResponse struct {
51
Modules []string
52
Files []string
53
Output string
54
}
55
)
56
57
func connectWithFastDialer(executionId string, host string, port int) (net.Conn, error) {
58
dialer := protocolstate.GetDialersWithId(executionId)
59
if dialer == nil {
60
return nil, fmt.Errorf("dialers not initialized for %s", executionId)
61
}
62
return dialer.Fastdialer.Dial(context.Background(), "tcp", net.JoinHostPort(host, strconv.Itoa(port)))
63
}
64
65
// IsRsync checks if a host is running a Rsync server.
66
// @example
67
// ```javascript
68
// const rsync = require('nuclei/rsync');
69
// const isRsync = rsync.IsRsync('acme.com', 873);
70
// log(toJSON(isRsync));
71
// ```
72
func IsRsync(ctx context.Context, host string, port int) (IsRsyncResponse, error) {
73
executionId := ctx.Value("executionId").(string)
74
return memoizedisRsync(executionId, host, port)
75
}
76
77
// @memo
78
func isRsync(executionId string, host string, port int) (IsRsyncResponse, error) {
79
resp := IsRsyncResponse{}
80
81
timeout := 5 * time.Second
82
conn, err := connectWithFastDialer(executionId, host, port)
83
if err != nil {
84
return resp, err
85
}
86
defer func() {
87
_ = conn.Close()
88
}()
89
90
rsyncPlugin := rsync.RSYNCPlugin{}
91
service, err := rsyncPlugin.Run(conn, timeout, plugins.Target{Host: host})
92
if err != nil {
93
return resp, nil
94
}
95
if service == nil {
96
return resp, nil
97
}
98
resp.Banner = service.Version
99
resp.IsRsync = true
100
return resp, nil
101
}
102
103
// ListModules lists the modules of a Rsync server.
104
// @example
105
// ```javascript
106
// const rsync = require('nuclei/rsync');
107
// const client = new rsync.RsyncClient();
108
// const listModules = client.ListModules('acme.com', 873, 'username', 'password');
109
// log(toJSON(listModules));
110
// ```
111
func (c *RsyncClient) ListModules(ctx context.Context, host string, port int, username string, password string) (RsyncListResponse, error) {
112
executionId := ctx.Value("executionId").(string)
113
return listModules(executionId, host, port, username, password)
114
}
115
116
// ListShares lists the shares of a Rsync server.
117
// @example
118
// ```javascript
119
// const rsync = require('nuclei/rsync');
120
// const client = new rsync.RsyncClient();
121
// const listShares = client.ListFilesInModule('acme.com', 873, 'username', 'password', '/');
122
// log(toJSON(listShares));
123
// ```
124
func (c *RsyncClient) ListFilesInModule(ctx context.Context, host string, port int, username string, password string, module string) (RsyncListResponse, error) {
125
executionId := ctx.Value("executionId").(string)
126
return listFilesInModule(executionId, host, port, username, password, module)
127
}
128
129
func listModules(executionId string, host string, port int, username string, password string) (RsyncListResponse, error) {
130
fastDialer := protocolstate.GetDialersWithId(executionId)
131
if fastDialer == nil {
132
return RsyncListResponse{}, fmt.Errorf("dialers not initialized for %s", executionId)
133
}
134
135
address := net.JoinHostPort(host, strconv.Itoa(port))
136
137
// Create a bytes buffer for logging
138
var logBuffer bytes.Buffer
139
140
// Create a custom slog handler that writes to the buffer
141
logHandler := slog.NewTextHandler(&logBuffer, &slog.HandlerOptions{
142
Level: slog.LevelDebug,
143
})
144
145
// Create a logger that writes to our buffer
146
logger := slog.New(logHandler)
147
148
sr, err := rsynclib.ListModules(address,
149
rsynclib.WithClientAuth(username, password),
150
rsynclib.WithLogger(logger),
151
rsynclib.WithFastDialer(fastDialer.Fastdialer),
152
)
153
if err != nil {
154
return RsyncListResponse{}, fmt.Errorf("connect failed: %v", err)
155
}
156
157
result := RsyncListResponse{
158
Modules: make([]string, len(sr)),
159
Output: logBuffer.String(),
160
}
161
162
for i, item := range sr {
163
result.Modules[i] = string(item.Name)
164
}
165
166
return result, nil
167
}
168
169
func listFilesInModule(executionId string, host string, port int, username string, password string, module string) (RsyncListResponse, error) {
170
fastDialer := protocolstate.GetDialersWithId(executionId)
171
if fastDialer == nil {
172
return RsyncListResponse{}, fmt.Errorf("dialers not initialized for %s", executionId)
173
}
174
175
address := net.JoinHostPort(host, strconv.Itoa(port))
176
177
// Create a bytes buffer for logging
178
var logBuffer bytes.Buffer
179
180
// Create a custom slog handler that writes to the buffer
181
logHandler := slog.NewTextHandler(&logBuffer, &slog.HandlerOptions{
182
Level: slog.LevelDebug,
183
})
184
185
// Create a logger that writes to our buffer
186
logger := slog.New(logHandler)
187
188
sr, err := rsynclib.SocketClient(nil, address, module, ".",
189
rsynclib.WithClientAuth(username, password),
190
rsynclib.WithLogger(logger),
191
rsynclib.WithFastDialer(fastDialer.Fastdialer),
192
)
193
if err != nil {
194
return RsyncListResponse{}, fmt.Errorf("connect failed: %v", err)
195
}
196
197
// Try to list files to test authentication
198
list, err := sr.List()
199
if err != nil {
200
return RsyncListResponse{}, fmt.Errorf("authentication failed: %v", err)
201
}
202
203
result := RsyncListResponse{
204
Files: make([]string, len(list)),
205
Output: logBuffer.String(),
206
}
207
208
for i, item := range list {
209
result.Files[i] = string(item.Path)
210
}
211
212
return result, nil
213
}
214
215