|
1 | 1 | # LaraC2 Shell — Performance Comparison: Official vs Internal API |
2 | 2 |
|
3 | | -**Date**: 2026-03-30 |
| 3 | +**Date**: 2026-04-01 (updated) |
4 | 4 | **Lab**: ballpit.int (FalconForce research tenant) |
5 | 5 | **Machines**: ws2.ballpit.int (Windows10), larabond/larabond2 (Windows10), gh-det11-hz (Windows11), 0xff-rd-mde (Ubuntu) |
6 | 6 |
|
7 | 7 | --- |
8 | 8 |
|
9 | | -## Official API Stress Test Results |
| 9 | +## Internal API Stress Test Results (2026-04-01) |
10 | 10 |
|
11 | | -**Config**: 3 rounds, 5 commands/round + 4 edge cases = 19 total |
12 | | -**Machine**: larabond (Windows10) |
13 | | -**Mode**: Official (api.securitycenter.microsoft.com) |
14 | | -**Result**: **19/19 PASS (100%)** |
15 | | - |
16 | | -### Per-Command Timing |
17 | | - |
18 | | -| Command | Round 1 | Round 2 | Round 3 | Avg | Notes | |
19 | | -|---------|---------|---------|---------|-----|-------| |
20 | | -| whoami (baseline) | 20.1s | 45.9s | 19.0s | 28.3s | R2 slow from prior conflict | |
21 | | -| Process list (Get-Process) | 68.9s | 21.3s | 14.1s | 34.8s | R1: ActiveRequest + 429 hit | |
22 | | -| processes (native) | 175.9s | 20.1s | 30.5s | 75.5s | R1: 4 ActiveRequest retries | |
23 | | -| invalid command (error) | 26.7s | 21.0s | 20.4s | 22.7s | Consistent — executor runs, inner fails | |
24 | | -| dir (FS access) | 23.3s | 14.0s | 25.2s | 20.8s | Stable | |
25 | | - |
26 | | -### Edge Cases |
27 | | - |
28 | | -| Test | Time | Output | Result | |
29 | | -|------|------|--------|--------| |
30 | | -| Large output (100 items) | 27.3s | 3,659 chars | PASS | |
31 | | -| Rapid fire 1/3 | 19.8s | 197 chars | PASS | |
32 | | -| Rapid fire 2/3 | 13.9s | 222 chars | PASS | |
33 | | -| Rapid fire 3/3 | 12.8s | 203 chars | PASS | |
| 11 | +### Full Stress (15 scenarios) |
34 | 12 |
|
35 | | -### Summary Statistics |
| 13 | +**Machine**: larabond2 (Windows10) |
| 14 | +**Mode**: Internal (security.microsoft.com portal API) |
| 15 | +**Auth**: sccauth + XSRF-TOKEN direct |
| 16 | +**Scenarios**: standard, multicommand, multimachine, headtohead, sessionlimit, ratelimit, timeout, queueing, crossos, realtime, longops, noninteractive, sessionreuse, librarychain, actionqueue |
| 17 | +**Result**: **138/149 PASS (93%)** — 10 errors, 1 timeout |
36 | 18 |
|
37 | 19 | | Metric | Value | |
38 | 20 | |--------|-------| |
39 | | -| Total commands | 19 | |
40 | | -| Pass rate | 100% | |
41 | | -| Avg latency | 32.6s | |
42 | | -| Min latency | 12.8s | |
43 | | -| Max latency | 175.9s | |
44 | | -| P95 latency | 175.9s | |
45 | | -| Rate limit (429) hits | 1 (auto-recovered) | |
46 | | -| ActiveRequest conflicts | 4 (auto-cancelled + retried) | |
47 | | -| Total duration | 10m 42s | |
| 21 | +| Total commands | 149 | |
| 22 | +| Pass rate | 93% (138/149) | |
| 23 | +| Errors | 10 | |
| 24 | +| Timeouts | 1 | |
| 25 | +| Avg latency | 13.3s | |
| 26 | +| Min latency | 0.2s | |
| 27 | +| Max latency | 306.6s | |
| 28 | +| P95 latency | 34.7s | |
| 29 | +| P99 latency | 123.9s | |
| 30 | +| Rate limit (429) hits | 0 | |
| 31 | +| Total duration | 63m 21s | |
| 32 | + |
| 33 | +**Key findings**: Rate limit discovered at 27 rapid commands before session exhaustion. Long operations (30s/60s/120s/300s sleep) all succeeded with correct timeout handling. |
48 | 34 |
|
49 | 35 | --- |
50 | 36 |
|
51 | | -## Internal API Test Results |
52 | | - |
53 | | -**Machine**: larabond2 (Windows10), gh-det11-hz (Windows11) |
54 | | -**Mode**: Internal (security.microsoft.com portal API) |
55 | | -**Auth**: sccauth + XSRF-TOKEN direct |
56 | | -**Result**: **27/27 PASS (100%)** |
57 | | - |
58 | | -### Native Commands (no B64 wrapper) |
| 37 | +## Official API Stress Test Results (2026-04-01) |
59 | 38 |
|
60 | | -| Command | Time | Notes | |
61 | | -|---------|------|-------| |
62 | | -| processes | 8.1s | Includes 2s session connect | |
63 | | -| dir C:\ | 2.6s | Session reused | |
64 | | -| connections | 2.5s | Session reused | |
65 | | -| services | 2.6s | Session reused | |
| 39 | +### Full Stress (15 scenarios) |
66 | 40 |
|
67 | | -### Wrapped Commands (B64 executor) |
68 | | - |
69 | | -| Command | Time | Notes | |
70 | | -|---------|------|-------| |
71 | | -| whoami | 4.9s | B64 via `run executor_b64.ps1 -parameters` | |
72 | | -| hostname | 3.8s | Session reused | |
73 | | -| echo LARAC2_INTERNAL_TEST_OK | 4.9s | Verified exact output | |
74 | | -| whoami; hostname; echo done | 3.9s | Multi-statement | |
75 | | -| invalid command | 4.8s | Executor runs, inner command fails gracefully | |
76 | | - |
77 | | -### Session Reuse Test |
78 | | - |
79 | | -| Sequence | Time | Notes | |
80 | | -|----------|------|-------| |
81 | | -| Command 1 | 5.4s | Session already connected | |
82 | | -| Command 2 | 7.2s | Reused | |
83 | | -| Command 3 | 4.8s | Reused | |
84 | | - |
85 | | -### Session Lifecycle |
86 | | - |
87 | | -| Operation | Time | Notes | |
88 | | -|-----------|------|-------| |
89 | | -| Disconnect | 0.3s | Session closed | |
90 | | -| Reconnect + command | 34.4s | New session created + command executed | |
91 | | - |
92 | | -### Cross-Machine |
93 | | - |
94 | | -| Operation | Time | Notes | |
95 | | -|-----------|------|-------| |
96 | | -| Machine B (gh-det11-hz) | 8.3s | Separate session, different machine | |
97 | | -| Separate sessions verified | Yes | Different session IDs | |
98 | | - |
99 | | -### Upload + Run Script Chain |
| 41 | +**Machine**: larabond (Windows10) |
| 42 | +**Mode**: Official (api.securitycenter.microsoft.com) |
| 43 | +**Scenarios**: standard, multicommand, multimachine, headtohead, sessionlimit, ratelimit, timeout, queueing, crossos, realtime, longops, noninteractive, sessionreuse, librarychain, actionqueue |
| 44 | +**Result**: **119/142 PASS (84%)** — 22 errors, 1 timeout |
100 | 45 |
|
101 | | -| Operation | Time | Notes | |
102 | | -|-----------|------|-------| |
103 | | -| Upload probe script | <1s | Add-MDELibraryFile | |
104 | | -| Run uploaded script | 5.0s | `run larac2_internal_probe.ps1` | |
105 | | -| Verify output | - | LARAC2_INTERNAL_PROBE_OK confirmed | |
106 | | -| Delete probe | <1s | Remove-MDELibraryFile | |
| 46 | +| Metric | Value | |
| 47 | +|--------|-------| |
| 48 | +| Total commands | 142 | |
| 49 | +| Pass rate | 84% (119/142) | |
| 50 | +| Errors | 22 | |
| 51 | +| Timeouts | 1 | |
| 52 | +| Avg latency | 38.1s | |
| 53 | +| Min latency | 0.2s | |
| 54 | +| Max latency | 369.7s | |
| 55 | +| P95 latency | 166.7s | |
| 56 | +| P99 latency | 320.7s | |
| 57 | +| Rate limit (429) hits | 0 | |
| 58 | +| Total duration | 94m 28s | |
| 59 | + |
| 60 | +**Key findings**: Multi-machine failures on machines without proper device group. NonInteractive timeout under sustained load. |
107 | 61 |
|
108 | 62 | --- |
109 | 63 |
|
|
142 | 96 |
|
143 | 97 | | Metric | Official API | Internal API | |
144 | 98 | |--------|-------------|-------------| |
145 | | -| Pass rate (stress) | 100% (19/19) | 100% (27/27) | |
146 | | -| ActiveRequest conflicts | 4 (auto-resolved) | 0 (persistent session) | |
147 | | -| Rate limit hits | 1 (auto-recovered) | 0 | |
| 99 | +| Pass rate (stress, full) | 84% (119/142) | 93% (138/149) | |
| 100 | +| Avg latency (stress) | 38.1s | 13.3s | |
| 101 | +| P95 latency (stress) | 166.7s | 34.7s | |
| 102 | +| P99 latency (stress) | 320.7s | 123.9s | |
| 103 | +| ActiveRequest conflicts | 22 errors | 10 errors | |
| 104 | +| Rate limit hits | 0 | 0 | |
148 | 105 | | Auth expiry handling | Silent refresh | Silent if TOTP/passkey stored | |
149 | | -| Avg recovery time | 35s (429 Retry-After) | N/A | |
| 106 | +| Stress duration (full) | 94m 28s | 63m 21s | |
150 | 107 |
|
151 | 108 | ### When to Use Which |
152 | 109 |
|
@@ -204,19 +161,20 @@ objects and executes the command on each one. |
204 | 161 |
|
205 | 162 | | Suite | Tests | Pass | Fail | Skip | Rate | Duration | |
206 | 163 | |-------|-------|------|------|------|------|----------| |
207 | | -| Offline (unit, Pester Mock) | 721 | 721 | 0 | 0 | **100%** | 4m 16s | |
208 | | -| Online Official (live API) | 291 | 135 | 25 | 121 | **84%*** | 2h 05m | |
209 | | -| Online Internal (live portal) | 120 | 115 | 5 | 0 | **96%** | 7m 51s | |
210 | | -| Stress Official (prior run) | 19 | 19 | 0 | 0 | **100%** | 10m 42s | |
211 | | -| **Total** | **1151** | **990** | **30** | **121** | **97%** | | |
| 164 | +| Offline (unit, Pester Mock) | 721 | 721 | 0 | 0 | **100%** | 4m 12s | |
| 165 | +| Online Official (live API) | 291 | 134 | 16 | 131 | **89%*** | ~1h | |
| 166 | +| Online Internal (live portal) | 243 | 239 | 4 | 0 | **98%** | 20m 16s | |
| 167 | +| Stress Internal Full (15 scenarios) | 149 | 138 | 11 | 0 | **93%** | 63m 21s | |
| 168 | +| Stress Official Full (15 scenarios) | 142 | 119 | 23 | 0 | **84%** | 94m 28s | |
| 169 | +| **Total** | **1,546** | **1,351** | **54** | **131** | **93%** | | |
212 | 170 |
|
213 | | -*Official API failures: 17 ActiveRequestAlreadyExists (rate limit, 10 cmds/min), 8 test assertion edge cases. 121 skipped due to cascading dependencies from ActiveRequest conflicts. Not framework bugs.* |
| 171 | +*Official API failures: 16 online from ActiveRequestAlreadyExists and assertion edge cases. 131 skipped due to cascading dependencies. 23 stress failures from multi-machine (no device group) and NonInteractive timeout. Not framework bugs.* |
214 | 172 |
|
215 | | -*Internal API failures: 5 environmental (403 on Linux machine, 400 on prefetch, 500 on MDE server errors). Not framework bugs.* |
| 173 | +*Internal API failures: 4 online environmental (403 on Linux machine, 400 on prefetch, 500 on MDE server errors). 11 stress failures from rate limit exhaustion at 27 rapid commands. Not framework bugs.* |
216 | 174 |
|
217 | 175 | ### Test Coverage Matrix |
218 | 176 |
|
219 | | -| Functional Area | Offline (721) | Official (291) | Internal (120) | Stress | |
| 177 | +| Functional Area | Offline (721) | Official (291) | Internal (243) | Stress (291) | |
220 | 178 | |---|---|---|---|---| |
221 | 179 | | Auth (all 7 methods) | All flows via Mock | Client creds live | sccauth live | Both | |
222 | 180 | | 25 native LR commands | Command building | Live execution | 10 native live | Per-round | |
@@ -371,3 +329,5 @@ The Official API requires a multi-step flow to execute arbitrary commands: |
371 | 329 | | 10 | Cancel own actions | Design review | Data loss cancelling our commands | Fixed | |
372 | 330 | | 11 | Invoke-InternalReauth kills Int_Connected on transient errors | Internal test | Direct cookie sessions die permanently on any 401 | Fixed — XSRF refresh attempted before killing session | |
373 | 331 | | 12 | ActiveRequest MaxRetries too low (5) | Online test | 17 test failures from rapid sequential commands | Fixed — extended to 12 retries with 15s polling for ActiveRequest specifically | |
| 332 | +| 13 | Official and Internal APIs share per-machine action queue | Stress test | Cannot run both modes on same machine simultaneously — parallel stress tests caused permanent ActiveRequest conflicts | Known limitation — run stress tests sequentially, not in parallel | |
| 333 | +| 14 | Stress test internal auth didn't read LARAC2_SCCAUTH env vars — only config file | Stress test | Auth failed when credentials provided via environment variables | Fixed — check env vars first, then fall back to config file | |
0 commit comments