Skip to content
academia.sh

Lesson 07 / 16

Malware Types

Ten types are classified by spread mode and the foothold point each uses: the number of points per type, the two types that never establish persistence, the number of types using each point type alongside the legitimate item count at that same point, and the number of types that stop fully, stop partially, or stay unaffected when point types are closed cheapest first.

Published Updated

In this lesson

  • Classifies malware types by spread and persistence behavior rather than family name
  • Counts the number of types using each foothold point type together with the legitimate item count at that point
Contents

The previous topic narrowed the number of items on the endpoint and closed the automatically run points one by one. That whole measurement carried a silent assumption: nothing had run on the endpoint yet. This lesson lifts that assumption and asks the reverse question — once code has run, how much of it do the closed points stop?

The answer does not depend on what the running thing is, but on how it spreads and where it takes hold. So the classification is not done by family name but by two measurable behaviors: spread mode and the type of foothold point used. Three numbers are measured: how many foothold points a type uses, how many types use a point type, and how many legitimate items closing that point takes down together with it. No infection method is written in this lesson; what is counted is the item the infection leaves behind.

  • EP1. The ten types, eight point types, and legitimate item counts on the endpoint belong to the field laptop baseline of a fictional regional measurement network. No real sample is run; what is measured is the model built with node.
  • EP2. Type names are behavior definitions. Family name, campaign name, and sample name never appear in the model; two different samples that show the same behavior are a single row in the model.
  • EP3. A type either uses a point type or it does not. In reality the same type picks a different point depending on the environment; in the model the choice is fixed, which makes the point count look smaller than it is.
  • EP4. The legitimate item counts are taken from a single endpoint’s baseline. These numbers are larger on servers and smaller on branch endpoints; the direction of the ratios does not change.
  • EP5. “Closing” means blocking new items from being added to a point type. Because existing legitimate items must go through the same control as well, the cost column counts those items.
  • EP6. Detection is absent from this lesson. Closed points are counted, not seen points; visibility is the subject of the next two lessons.

Two Axes: Spread and Persistence

The model holds four things for each type: the behavior definition, the spread mode, the point types it uses, and the outcome it returns. The spread mode tells how the code arrives at the endpoint; the point list tells where it writes once it has arrived. The two axes are independent, and conflating them is a common measurement error: a type that spreads fast can have weak persistence, and a type that never spreads can hold onto the most points.

// types.mjs — malware types, their spread mode, and the foothold points they use; model
// [type name, spread mode, point types used, outcome]
export const TYPE = [
  ['network-spreading component', 'automatic network', ['service-definition', 'scheduled-task'], 'spread'],
  ['user-opened component', 'user opening', ['startup-entry', 'login-script'], 'remote access'],
  ['data-encrypting component', 'secondary deployment', [], 'availability loss'],
  ['keystroke and screen logger', 'user opening', ['login-script', 'load-path-hook', 'event-subscription'], 'confidentiality loss'],
  ['remote-command listener', 'secondary deployment', ['service-definition', 'event-subscription'], 'sustained access'],
  ['centrally managed agent', 'automatic network', ['startup-entry', 'scheduled-task', 'service-definition'], 'resource takeover'],
  ['compute-draining component', 'secondary deployment', ['scheduled-task'], 'resource drain'],
  ['memory-resident loader', 'user opening', [], 'second-stage retrieval'],
  ['binary-infecting component', 'file attachment', ['legitimate-binary-attach'], 'spread'],
  ['removable-media component', 'removable media', ['media-autorun'], 'spread'],
];
// point type -> number of legitimate items on one endpoint (fictional field laptop baseline)
export const LEGIT = {
  'startup-entry': 14, 'service-definition': 63, 'scheduled-task': 41, 'login-script': 6,
  'load-path-hook': 3, 'event-subscription': 2, 'legitimate-binary-attach': 0,
  'media-autorun': 1,
};
export const POINT = Object.keys(LEGIT);
export const usedBy = (n) => TYPE.filter((t) => t[2].includes(n));
export const status = (t, closed) => t[2].length === 0 ? 'no point'
  : t[2].every((n) => closed.includes(n)) ? 'fully stopped'
  : t[2].some((n) => closed.includes(n)) ? 'partially stopped' : 'unaffected';

The eight point types are the classes of automatically run items inventoried in the previous topic: the entry that runs at startup, the service definition, the scheduled task, the user login script, the library load-path hook, the persistent event subscription, attaching to a legitimate binary, and the autorun entry on removable media. The last two stand apart from the rest: attaching to a file does not produce a new entry on the endpoint, and the removable media entry sits on the inserted media, not on the endpoint.

Points per Type, Types per Point

// run1.mjs — classifies types by spread and persistence behavior, counts point types
import { TYPE, POINT, LEGIT, usedBy } from './types.mjs';

const noPersistence = TYPE.filter((t) => t[2].length === 0);
console.log(`${TYPE.length} types, ${POINT.length} point types; types with no persistence: ${noPersistence.length}\n`);
console.log(`${'type'.padEnd(38)} ${'spread'.padStart(20)} ${'point'.padStart(5)} ${'outcome'.padStart(22)}`);
for (const t of TYPE)
  console.log(`${t[0].padEnd(38)} ${t[1].padStart(20)} ${String(t[2].length).padStart(5)} ${t[3].padStart(22)}`);

const spreadModes = [...new Set(TYPE.map((t) => t[1]))];
console.log(`\n${'spread mode'.padEnd(20)} ${'type'.padStart(4)} ${'total points'.padStart(12)} ${'per type'.padStart(10)}`);
for (const s of spreadModes) {
  const g = TYPE.filter((t) => t[1] === s), n = g.reduce((a, t) => a + t[2].length, 0);
  console.log(`${s.padEnd(20)} ${String(g.length).padStart(4)} ${String(n).padStart(12)} ${(n / g.length).toFixed(1).padStart(10)}`);
}

console.log(`\n${'point type'.padEnd(26)} ${'using types'.padStart(12)} ${'legitimate items'.padStart(18)}`);
for (const n of POINT)
  console.log(`${n.padEnd(26)} ${String(usedBy(n).length).padStart(12)} ${String(LEGIT[n]).padStart(18)}`);
console.log(`${'total'.padEnd(26)} ${String(TYPE.reduce((a, t) => a + t[2].length, 0)).padStart(12)} ` +
  `${String(Object.values(LEGIT).reduce((a, b) => a + b, 0)).padStart(18)}`);
10 types, 8 point types; types with no persistence: 2

type                                                 spread point                outcome
network-spreading component               automatic network     2                 spread
user-opened component                          user opening     2          remote access
data-encrypting component              secondary deployment     0      availability loss
keystroke and screen logger                    user opening     3   confidentiality loss
remote-command listener                secondary deployment     2       sustained access
centrally managed agent                   automatic network     3      resource takeover
compute-draining component             secondary deployment     1         resource drain
memory-resident loader                         user opening     0 second-stage retrieval
binary-infecting component                  file attachment     1                 spread
removable-media component                   removable media     1                 spread

spread mode          type total points   per type
automatic network       2            5        2.5
user opening            3            5        1.7
secondary deployment    3            3        1.0
file attachment         1            1        1.0
removable media         1            1        1.0

point type                  using types   legitimate items
startup-entry                         2                 14
service-definition                    3                 63
scheduled-task                        3                 41
login-script                          2                  6
load-path-hook                        1                  3
event-subscription                    2                  2
legitimate-binary-attach              1                  0
media-autorun                         1                  1
total                                15                130

The first number is that two types never establish persistence, and there are two separate reasons for it. The data-encrypting component finishes its job in a single run; it has no need to run after a restart, because the damage it does sits on disk. The memory-resident loader completes its task once it has fetched the second stage. Both disappear on restart, and for both that is not a loss. A defense that relies on closing persistence points does nothing to these two types — none of the fifteen counted points belong to them.

The spread table gives the second difference. The two types that spread automatically over the network use 2.5 points per type; the three types that arrive through secondary deployment use only 1.0. Persistence widens as spread becomes more automatic, because a component that spreads automatically does not know which endpoint it lands on, and writing to more than one point raises the odds of staying there. A manually deployed component knows its target; a single correct point is enough.

The third table shows the measurement’s real tension. How often a point type is used and how many legitimate items sit at that point move in the same direction. The two point types used by the most types are the service definition and the scheduled task — three types each — and 104 of the endpoint’s legitimate items, more than three-quarters of the total 130, sit at exactly these two points. Malicious code does not pick these points at random; it picks the place the operating system and legitimate software both use for routine work. Standing inside the crowd is itself a defense — the crowd is the defense.

Closing Order and Three Outcomes

// run2.mjs — closes point types cheapest first, counts stopped and unaffected types
import { TYPE, POINT, LEGIT, status } from './types.mjs';

const order = [...POINT].sort((a, b) => LEGIT[a] - LEGIT[b]);
const counts = (closed) => ['fully stopped', 'partially stopped', 'unaffected', 'no point']
  .map((d) => TYPE.filter((t) => status(t, closed) === d).length);

console.log(`${'closed point type'.padEnd(26)} ${'cost'.padStart(5)} ${'full'.padStart(4)} ${'part'.padStart(5)} ` +
  `${'unaffected'.padStart(10)} ${'no point'.padStart(8)}`);
const closed = [];
for (const n of [null, ...order]) {
  if (n) closed.push(n);
  const [full, partial, unaffected, none] = counts(closed);
  const cost = closed.reduce((a, k) => a + LEGIT[k], 0);
  console.log(`${(n ?? 'none').padEnd(26)} ${String(cost).padStart(5)} ${String(full).padStart(4)} ` +
    `${String(partial).padStart(5)} ${String(unaffected).padStart(10)} ${String(none).padStart(8)}`);
}

const cheap = order.slice(0, 5);
console.log(`\ncheap five point types closed (cost ${cheap.reduce((a, k) => a + LEGIT[k], 0)} legitimate items):`);
for (const t of TYPE)
  console.log(`  ${t[0].padEnd(38)} ${status(t, cheap).padStart(17)} ` +
    `${t[2].filter((n) => !cheap.includes(n)).join(', ') || '-'}`);
closed point type           cost full  part unaffected no point
none                           0    0     0          8        2
legitimate-binary-attach       0    1     0          7        2
media-autorun                  1    2     0          6        2
event-subscription             3    2     2          4        2
load-path-hook                 6    2     2          4        2
login-script                  12    3     2          3        2
startup-entry                 26    4     2          2        2
scheduled-task                67    5     3          0        2
service-definition           130    8     0          0        2

cheap five point types closed (cost 12 legitimate items):
  network-spreading component                   unaffected service-definition, scheduled-task
  user-opened component                  partially stopped startup-entry
  data-encrypting component                       no point -
  keystroke and screen logger                fully stopped -
  remote-command listener                partially stopped service-definition
  centrally managed agent                       unaffected startup-entry, scheduled-task, service-definition
  compute-draining component                    unaffected scheduled-task
  memory-resident loader                          no point -
  binary-infecting component                 fully stopped -
  removable-media component                  fully stopped -

The closing order starts from the cheap end, and the curve is not flat from start to finish. The first three steps stop two types fully and two types partially, at a total cost of three legitimate items. The fourth step — the library load-path hook — spends three more items and changes no column: only one type uses that point, and that type’s other two points are already open. A step that has a cost and returns zero in exchange shows exactly where the assumption that adding a layer is free breaks down.

The last two steps reverse the trend. Closing the scheduled task costs 41 legitimate items, closing the service definition costs 63; together they raise the cost from 26 to 130 and, in exchange, carry the fully-stopped type count from four to eight. That is buying four more types at the cost of reviewing all of the endpoint’s legitimate autorun items. At this point the measurement offers a decision, not a recommendation: are four types worth having a hundred and thirty items approved one by one on every endpoint, or are these two point types too crowded to close and better left to a different defense?

The list below spells out the status of the cheap five one by one and shows why the two columns are kept separate. Three types stop fully; two stop partially, meaning they can still run but their point choice drops to one; three stay unaffected; and two have no point to close. At a cost of twelve legitimate items, three of the ten types stopped. For the remaining seven types, closing points is a finished defense — the only question left is whether they are seen while running.

The Order Itself Is a Decision

Closing cheapest first is not the only option. There is a second ordering that feels more intuitive: close the point used by the most types first. The two orderings cover the same eight points and end up in the same place; where they diverge is the point in between where you stop.

// run3.mjs — two closing orders: cheapest first and most types affected first
import { TYPE, POINT, LEGIT, usedBy, status } from './types.mjs';

const cheap = [...POINT].sort((a, b) => LEGIT[a] - LEGIT[b]);
const wide = [...POINT].sort((a, b) => usedBy(b).length - usedBy(a).length || LEGIT[a] - LEGIT[b]);
const full = (k) => TYPE.filter((t) => status(t, k) === 'fully stopped').length;
function curve(order) {
  const k = [], point = {};
  for (const n of order) {
    k.push(n);
    const d = full(k);
    if (point[d] === undefined) point[d] = { step: k.length, cost: k.reduce((a, x) => a + LEGIT[x], 0) };
  }
  return point;
}
const [a, b] = [curve(cheap), curve(wide)];
console.log(`cheap order: ${cheap.join(' > ')}`);
console.log(`wide order : ${wide.join(' > ')}\n`);
console.log(`${'target fully stopped'.padEnd(20)} ${'cheap step'.padStart(10)} ${'cheap cost'.padStart(10)} ` +
  `${'wide step'.padStart(10)} ${'wide cost'.padStart(11)}`);
for (let d = 1; d <= 8; d++) {
  if (!a[d] && !b[d]) continue;
  const f = (x) => x ? [String(x.step), String(x.cost)] : ['-', '-'];
  const [aa, ab] = f(a[d]), [ba, bb] = f(b[d]);
  console.log(`${String(d).padEnd(20)} ${aa.padStart(10)} ${ab.padStart(10)} ${ba.padStart(10)} ${bb.padStart(11)}`);
}
cheap order: legitimate-binary-attach > media-autorun > event-subscription > load-path-hook > login-script > startup-entry > scheduled-task > service-definition
wide order : scheduled-task > service-definition > event-subscription > login-script > startup-entry > legitimate-binary-attach > media-autorun > load-path-hook

target fully stopped cheap step cheap cost  wide step   wide cost
1                             1          0          1          41
2                             2          1          2         104
3                             5         12          3         106
4                             6         26          -           -
5                             7         67          5         126
6                             -          -          6         126
7                             -          -          7         127
8                             8        130          8         130

The two curves diverge at every target, and the cheap ordering stays ahead from start to finish. Stopping one type fully costs zero legitimate items in the cheap ordering and forty-one in the wide ordering. Reaching three types costs twelve in the cheap ordering and one hundred six in the wide ordering — a difference of close to ninefold. Why comes from the alignment in the third table: the points used by the most types are also the most crowded legitimate points, and closing them alone stops no type fully, because those types have other points as well. The wide ordering spends two expensive steps and gains only two types.

The wide ordering’s gap at four types has the same cause: going from the third step to the fifth, the count jumps from three to five, and four never appears. The ordering does not produce a smooth curve; it produces jumps that depend on which type uses which set of points. In row eight, both reach the same place, one hundred thirty items — once you want to close everything, the ordering no longer matters. The ordering only matters if you stop partway, and defenses almost always stop partway.

Summary

  • Ten types were classified by spread mode and foothold point; family name was never used. A total of 15 point uses spread across 8 point types.
  • Two types never establish persistence: one finishes its job in a single run, the other fetches the second stage and stops. A defense that relies on closing points does nothing to these two.
  • Automatic network spread uses 2.5 points per type, secondary deployment uses 1.0: persistence widens as spread becomes more automatic, because the target is not known in advance.
  • The two most-used point types (service definition, scheduled task) hold 104 of the endpoint’s 130 legitimate items. Malicious code picks the crowded point; the crowd makes closing it expensive.
  • At a cost of 12 legitimate items, 3 types stop fully and 2 stop partially; 3 types stay unaffected. Closing every point stops 8 types at a cost of 130 items, and 2 types still remain outside.
  • One step has a cost and changes no column (the load-path hook, 3 items). The ordering itself is also a decision: stopping 3 types costs 12 items in the cheap ordering and 106 in the ordering that starts with the point affecting the most types. Both reach 130 once all eight types are covered; the ordering only matters if you stop partway.

Next Step

The seven types this lesson could not close and the two types that never establish persistence point to the same place: something must be seen and stopped the moment it runs. The layer that takes this on can ask two separate questions — “have I seen this item before” and “what is this item doing.” The two questions answer differently over the same event set, and each carries two kinds of error. That is the next lesson’s question: over the same run set, how many bad items do signature-based and behavior-based detection catch, how many do they miss, how many legitimate items do they flag by mistake, and why do these two errors move in opposite directions when the decision threshold shifts.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ ↵ Esc navigate · open · close