]> Cypherpunks repositories - nncp.git/commitdiff
Prevent path traversal during freq/file
authorEugene Medvedev <rn3aoh.g@gmail.com>
Fri, 19 Sep 2025 13:12:18 +0000 (16:12 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Fri, 19 Sep 2025 13:50:14 +0000 (16:50 +0300)
As it currently stands, NNCP is vulnerable to path traversal attacks with
freq and file functions: Despite the requirement for both to supply full path
in configuration, both types of packets will accept and act upon paths containing
"..". Most obviously, this allows one to request any file NNCP has access to,
like its own configuration file with the private keys in it.
Likewise, a sent file can break out of the incoming directory in the same manner
and be written anywhere on the system that the user can write to.

This patch is my take on dealing with this by by limiting path traversal to
below the configured full path. It does nothing about, e.g., symlinks,
and I'm not sure anything should be done about those.

src/toss.go

index 0ea4098c75408b8222ba34ec45207de97dd26d92..4d3ceecd22d7703b7628be7e5be2ff129aa3aa44 100644 (file)
@@ -312,6 +312,17 @@ func jobProcess(
                        return err
                }
                dir := filepath.Join(*incoming, path.Dir(dst))
+               if !strings.HasPrefix(dir, *incoming) {
+                       err = errors.New("incoming path traversal")
+                       ctx.LogE("rx-traversal", les, err, func(les LEs) string {
+                               return fmt.Sprintf(
+                                       "Tossing file %s/%s (%s): %s: traversal",
+                                       sender.Name, pktName,
+                                       humanize.IBytes(pktSize), dst,
+                               )
+                       })
+                       return err
+               }
                if err = os.MkdirAll(dir, os.FileMode(0777)); err != nil {
                        ctx.LogE("rx-mkdir", les, err, func(les LEs) string {
                                return fmt.Sprintf(
@@ -542,11 +553,26 @@ func jobProcess(
                        )
                        return err
                }
+               srcPath := filepath.Join(*freqPath, src)
+               if !strings.HasPrefix(srcPath, *freqPath) {
+                       err = errors.New("freqing path traversal")
+                       ctx.LogE(
+                               "rx-no-freq", les, err,
+                               func(les LEs) string {
+                                       return fmt.Sprintf(
+                                               "Tossing freq %s/%s (%s): %s -> %s",
+                                               sender.Name, pktName,
+                                               humanize.IBytes(pktSize), src, dst,
+                                       )
+                               },
+                       )
+                       return err
+               }
                if !opts.DryRun {
                        err = ctx.TxFile(
                                sender,
                                pkt.Nice,
-                               filepath.Join(*freqPath, src),
+                               srcPath,
                                dst,
                                sender.FreqChunked,
                                sender.FreqMinSize,