mirror of
https://github.com/FalconOpsLLC/goexec
synced 2026-06-06 15:44:27 +00:00
feat: auto-generate upload dest path, add upload confirmation check
- --upload-dest is now optional; defaults to C:\Windows\Temp\<UUID> (same pattern as --out remote path generation) - After upload, confirm file exists via SMB stat and log path + size - Add --no-upload-confirm flag to skip the confirmation check - Add UploadConfirmer optional interface in io.go - Add ConfirmUpload method on FileStager (stats remote file, logs details) Co-authored-by: Carter <carter-falconops@users.noreply.github.com>
This commit is contained in:
+2
-4
@@ -51,7 +51,8 @@ func registerExecutionFlags(fs *pflag.FlagSet) {
|
||||
|
||||
func registerExecutionUploadFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&uploadSource, "upload", "", "Upload local `file` to remote filesystem")
|
||||
fs.StringVar(&uploadDest, "upload-dest", "", "Remote destination `path` for uploaded file")
|
||||
fs.StringVar(&uploadDest, "upload-dest", "", "Remote destination `path` for uploaded file (default: random temp path)")
|
||||
fs.BoolVar(&exec.Upload.NoConfirm, "no-upload-confirm", false, "Skip upload confirmation check")
|
||||
}
|
||||
|
||||
func registerExecutionOutputFlags(fs *pflag.FlagSet) {
|
||||
@@ -185,9 +186,6 @@ func argsUpload(methods ...string) func(cmd *cobra.Command, args []string) error
|
||||
return args(append(as, func(*cobra.Command, []string) (err error) {
|
||||
|
||||
if uploadSource != "" {
|
||||
if uploadDest == "" {
|
||||
return fmt.Errorf("--upload-dest is required when --upload is set")
|
||||
}
|
||||
exec.Upload.Reader, err = os.Open(uploadSource)
|
||||
if err != nil {
|
||||
return fmt.Errorf("open upload file: %w", err)
|
||||
|
||||
@@ -185,6 +185,9 @@ Authors: FalconOps LLC (@FalconOpsLLC),
|
||||
}
|
||||
|
||||
if uploadSource != "" {
|
||||
if uploadDest == "" {
|
||||
uploadDest = `C:\Windows\Temp\` + uuid.NewString()
|
||||
}
|
||||
exec.Upload.RemotePath = uploadDest
|
||||
exec.Upload.Provider = &smb.FileStager{
|
||||
Client: &smbClient,
|
||||
|
||||
@@ -35,11 +35,18 @@ type ExecutionOutput struct {
|
||||
}
|
||||
|
||||
type ExecutionUpload struct {
|
||||
NoConfirm bool
|
||||
RemotePath string
|
||||
Provider InputProvider
|
||||
Reader io.ReadCloser
|
||||
}
|
||||
|
||||
// UploadConfirmer is an optional interface that InputProvider implementations
|
||||
// can satisfy to confirm a file was successfully uploaded.
|
||||
type UploadConfirmer interface {
|
||||
ConfirmUpload(ctx context.Context) error
|
||||
}
|
||||
|
||||
type ExecutionInput struct {
|
||||
StageFile io.ReadCloser
|
||||
Executable string
|
||||
|
||||
@@ -124,7 +124,13 @@ func ExecuteCleanMethod(ctx context.Context, module CleanExecutionMethod, execIO
|
||||
log.Error().Err(err).Msg("Upload failed")
|
||||
return fmt.Errorf("upload: %w", err)
|
||||
}
|
||||
log.Info().Msg("Upload succeeded")
|
||||
if !execIO.Upload.NoConfirm {
|
||||
if confirmer, ok := execIO.Upload.Provider.(UploadConfirmer); ok {
|
||||
if err = confirmer.ConfirmUpload(ctx); err != nil {
|
||||
log.Warn().Err(err).Msg("Upload confirmation failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Execute (only if a command/executable was provided)
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/FalconOpsLLC/goexec/pkg/goexec"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
type FileStager struct {
|
||||
@@ -61,3 +62,26 @@ func (o *FileStager) Upload(ctx context.Context, reader io.Reader) (err error) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ConfirmUpload checks that the uploaded file exists on the remote filesystem
|
||||
// and logs the file path and size. The share must already be mounted from a
|
||||
// prior Upload call.
|
||||
func (o *FileStager) ConfirmUpload(ctx context.Context) error {
|
||||
log := zerolog.Ctx(ctx)
|
||||
|
||||
if o.Client.mount == nil {
|
||||
return fmt.Errorf("share not mounted")
|
||||
}
|
||||
|
||||
info, err := o.Client.mount.Stat(o.relativePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("stat remote file: %w", err)
|
||||
}
|
||||
|
||||
log.Info().
|
||||
Str("path", o.File).
|
||||
Int64("size", info.Size()).
|
||||
Msg("Upload confirmed")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user