@trailofbits/vsix-audit 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +281 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +703 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/index.d.ts +3 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +4 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/scanner/batch.d.ts +12 -0
  12. package/dist/scanner/batch.d.ts.map +1 -0
  13. package/dist/scanner/batch.js +104 -0
  14. package/dist/scanner/batch.js.map +1 -0
  15. package/dist/scanner/bundler.d.ts +35 -0
  16. package/dist/scanner/bundler.d.ts.map +1 -0
  17. package/dist/scanner/bundler.js +120 -0
  18. package/dist/scanner/bundler.js.map +1 -0
  19. package/dist/scanner/cache.d.ts +45 -0
  20. package/dist/scanner/cache.d.ts.map +1 -0
  21. package/dist/scanner/cache.js +153 -0
  22. package/dist/scanner/cache.js.map +1 -0
  23. package/dist/scanner/cache.test.d.ts +2 -0
  24. package/dist/scanner/cache.test.d.ts.map +1 -0
  25. package/dist/scanner/cache.test.js +149 -0
  26. package/dist/scanner/cache.test.js.map +1 -0
  27. package/dist/scanner/capabilities.d.ts +29 -0
  28. package/dist/scanner/capabilities.d.ts.map +1 -0
  29. package/dist/scanner/capabilities.js +217 -0
  30. package/dist/scanner/capabilities.js.map +1 -0
  31. package/dist/scanner/checks/ast.d.ts +3 -0
  32. package/dist/scanner/checks/ast.d.ts.map +1 -0
  33. package/dist/scanner/checks/ast.js +469 -0
  34. package/dist/scanner/checks/ast.js.map +1 -0
  35. package/dist/scanner/checks/ast.test.d.ts +2 -0
  36. package/dist/scanner/checks/ast.test.d.ts.map +1 -0
  37. package/dist/scanner/checks/ast.test.js +389 -0
  38. package/dist/scanner/checks/ast.test.js.map +1 -0
  39. package/dist/scanner/checks/behavioral.d.ts +3 -0
  40. package/dist/scanner/checks/behavioral.d.ts.map +1 -0
  41. package/dist/scanner/checks/behavioral.js +367 -0
  42. package/dist/scanner/checks/behavioral.js.map +1 -0
  43. package/dist/scanner/checks/blocklist.d.ts +3 -0
  44. package/dist/scanner/checks/blocklist.d.ts.map +1 -0
  45. package/dist/scanner/checks/blocklist.js +32 -0
  46. package/dist/scanner/checks/blocklist.js.map +1 -0
  47. package/dist/scanner/checks/blocklist.test.d.ts +2 -0
  48. package/dist/scanner/checks/blocklist.test.d.ts.map +1 -0
  49. package/dist/scanner/checks/blocklist.test.js +74 -0
  50. package/dist/scanner/checks/blocklist.test.js.map +1 -0
  51. package/dist/scanner/checks/chains.d.ts +35 -0
  52. package/dist/scanner/checks/chains.d.ts.map +1 -0
  53. package/dist/scanner/checks/chains.js +505 -0
  54. package/dist/scanner/checks/chains.js.map +1 -0
  55. package/dist/scanner/checks/chains.test.d.ts +2 -0
  56. package/dist/scanner/checks/chains.test.d.ts.map +1 -0
  57. package/dist/scanner/checks/chains.test.js +250 -0
  58. package/dist/scanner/checks/chains.test.js.map +1 -0
  59. package/dist/scanner/checks/dataflow.d.ts +3 -0
  60. package/dist/scanner/checks/dataflow.d.ts.map +1 -0
  61. package/dist/scanner/checks/dataflow.js +316 -0
  62. package/dist/scanner/checks/dataflow.js.map +1 -0
  63. package/dist/scanner/checks/dependencies.d.ts +13 -0
  64. package/dist/scanner/checks/dependencies.d.ts.map +1 -0
  65. package/dist/scanner/checks/dependencies.js +225 -0
  66. package/dist/scanner/checks/dependencies.js.map +1 -0
  67. package/dist/scanner/checks/dependencies.test.d.ts +2 -0
  68. package/dist/scanner/checks/dependencies.test.d.ts.map +1 -0
  69. package/dist/scanner/checks/dependencies.test.js +248 -0
  70. package/dist/scanner/checks/dependencies.test.js.map +1 -0
  71. package/dist/scanner/checks/finding-quality.test.d.ts +8 -0
  72. package/dist/scanner/checks/finding-quality.test.d.ts.map +1 -0
  73. package/dist/scanner/checks/finding-quality.test.js +164 -0
  74. package/dist/scanner/checks/finding-quality.test.js.map +1 -0
  75. package/dist/scanner/checks/ioc.d.ts +20 -0
  76. package/dist/scanner/checks/ioc.d.ts.map +1 -0
  77. package/dist/scanner/checks/ioc.js +234 -0
  78. package/dist/scanner/checks/ioc.js.map +1 -0
  79. package/dist/scanner/checks/ioc.test.d.ts +2 -0
  80. package/dist/scanner/checks/ioc.test.d.ts.map +1 -0
  81. package/dist/scanner/checks/ioc.test.js +298 -0
  82. package/dist/scanner/checks/ioc.test.js.map +1 -0
  83. package/dist/scanner/checks/manifest.d.ts +6 -0
  84. package/dist/scanner/checks/manifest.d.ts.map +1 -0
  85. package/dist/scanner/checks/manifest.js +123 -0
  86. package/dist/scanner/checks/manifest.js.map +1 -0
  87. package/dist/scanner/checks/manifest.test.d.ts +2 -0
  88. package/dist/scanner/checks/manifest.test.d.ts.map +1 -0
  89. package/dist/scanner/checks/manifest.test.js +108 -0
  90. package/dist/scanner/checks/manifest.test.js.map +1 -0
  91. package/dist/scanner/checks/obfuscation.d.ts +3 -0
  92. package/dist/scanner/checks/obfuscation.d.ts.map +1 -0
  93. package/dist/scanner/checks/obfuscation.js +432 -0
  94. package/dist/scanner/checks/obfuscation.js.map +1 -0
  95. package/dist/scanner/checks/obfuscation.test.d.ts +2 -0
  96. package/dist/scanner/checks/obfuscation.test.d.ts.map +1 -0
  97. package/dist/scanner/checks/obfuscation.test.js +399 -0
  98. package/dist/scanner/checks/obfuscation.test.js.map +1 -0
  99. package/dist/scanner/checks/package.d.ts +17 -0
  100. package/dist/scanner/checks/package.d.ts.map +1 -0
  101. package/dist/scanner/checks/package.js +422 -0
  102. package/dist/scanner/checks/package.js.map +1 -0
  103. package/dist/scanner/checks/package.test.d.ts +2 -0
  104. package/dist/scanner/checks/package.test.d.ts.map +1 -0
  105. package/dist/scanner/checks/package.test.js +518 -0
  106. package/dist/scanner/checks/package.test.js.map +1 -0
  107. package/dist/scanner/checks/patterns.d.ts +5 -0
  108. package/dist/scanner/checks/patterns.d.ts.map +1 -0
  109. package/dist/scanner/checks/patterns.js +251 -0
  110. package/dist/scanner/checks/patterns.js.map +1 -0
  111. package/dist/scanner/checks/patterns.test.d.ts +2 -0
  112. package/dist/scanner/checks/patterns.test.d.ts.map +1 -0
  113. package/dist/scanner/checks/patterns.test.js +147 -0
  114. package/dist/scanner/checks/patterns.test.js.map +1 -0
  115. package/dist/scanner/checks/unicode.d.ts +3 -0
  116. package/dist/scanner/checks/unicode.d.ts.map +1 -0
  117. package/dist/scanner/checks/unicode.js +247 -0
  118. package/dist/scanner/checks/unicode.js.map +1 -0
  119. package/dist/scanner/checks/unicode.test.d.ts +2 -0
  120. package/dist/scanner/checks/unicode.test.d.ts.map +1 -0
  121. package/dist/scanner/checks/unicode.test.js +202 -0
  122. package/dist/scanner/checks/unicode.test.js.map +1 -0
  123. package/dist/scanner/checks/yara.d.ts +23 -0
  124. package/dist/scanner/checks/yara.d.ts.map +1 -0
  125. package/dist/scanner/checks/yara.js +349 -0
  126. package/dist/scanner/checks/yara.js.map +1 -0
  127. package/dist/scanner/checks/yara.test.d.ts +2 -0
  128. package/dist/scanner/checks/yara.test.d.ts.map +1 -0
  129. package/dist/scanner/checks/yara.test.js +126 -0
  130. package/dist/scanner/checks/yara.test.js.map +1 -0
  131. package/dist/scanner/constants.d.ts +18 -0
  132. package/dist/scanner/constants.d.ts.map +1 -0
  133. package/dist/scanner/constants.js +37 -0
  134. package/dist/scanner/constants.js.map +1 -0
  135. package/dist/scanner/detection-coverage.test.d.ts +2 -0
  136. package/dist/scanner/detection-coverage.test.d.ts.map +1 -0
  137. package/dist/scanner/detection-coverage.test.js +216 -0
  138. package/dist/scanner/detection-coverage.test.js.map +1 -0
  139. package/dist/scanner/download.d.ts +76 -0
  140. package/dist/scanner/download.d.ts.map +1 -0
  141. package/dist/scanner/download.js +339 -0
  142. package/dist/scanner/download.js.map +1 -0
  143. package/dist/scanner/download.test.d.ts +2 -0
  144. package/dist/scanner/download.test.d.ts.map +1 -0
  145. package/dist/scanner/download.test.js +149 -0
  146. package/dist/scanner/download.test.js.map +1 -0
  147. package/dist/scanner/index.d.ts +8 -0
  148. package/dist/scanner/index.d.ts.map +1 -0
  149. package/dist/scanner/index.js +167 -0
  150. package/dist/scanner/index.js.map +1 -0
  151. package/dist/scanner/index.test.d.ts +2 -0
  152. package/dist/scanner/index.test.d.ts.map +1 -0
  153. package/dist/scanner/index.test.js +71 -0
  154. package/dist/scanner/index.test.js.map +1 -0
  155. package/dist/scanner/loaders/zoo.d.ts +3 -0
  156. package/dist/scanner/loaders/zoo.d.ts.map +1 -0
  157. package/dist/scanner/loaders/zoo.js +112 -0
  158. package/dist/scanner/loaders/zoo.js.map +1 -0
  159. package/dist/scanner/types.d.ts +118 -0
  160. package/dist/scanner/types.d.ts.map +1 -0
  161. package/dist/scanner/types.js +2 -0
  162. package/dist/scanner/types.js.map +1 -0
  163. package/dist/scanner/utils.d.ts +14 -0
  164. package/dist/scanner/utils.d.ts.map +1 -0
  165. package/dist/scanner/utils.js +25 -0
  166. package/dist/scanner/utils.js.map +1 -0
  167. package/dist/scanner/vsix.d.ts +6 -0
  168. package/dist/scanner/vsix.d.ts.map +1 -0
  169. package/dist/scanner/vsix.js +213 -0
  170. package/dist/scanner/vsix.js.map +1 -0
  171. package/dist/scanner/vsix.test.d.ts +2 -0
  172. package/dist/scanner/vsix.test.d.ts.map +1 -0
  173. package/dist/scanner/vsix.test.js +355 -0
  174. package/dist/scanner/vsix.test.js.map +1 -0
  175. package/package.json +60 -0
  176. package/zoo/blocklist/extensions.json +201 -0
  177. package/zoo/iocs/blockchain-extensions.txt +21 -0
  178. package/zoo/iocs/c2-domains.txt +50 -0
  179. package/zoo/iocs/c2-ips.txt +24 -0
  180. package/zoo/iocs/hashes.txt +47 -0
  181. package/zoo/iocs/malicious-npm.txt +85 -0
  182. package/zoo/iocs/wallets.txt +18 -0
  183. package/zoo/signatures/yara/README.md +46 -0
  184. package/zoo/signatures/yara/blockchain_c2.yar +48 -0
  185. package/zoo/signatures/yara/code_execution.yar +165 -0
  186. package/zoo/signatures/yara/credential_harvesting.yar +116 -0
  187. package/zoo/signatures/yara/crypto_wallet_targeting.yar +92 -0
  188. package/zoo/signatures/yara/data_exfiltration.yar +207 -0
  189. package/zoo/signatures/yara/google_calendar_c2.yar +187 -0
  190. package/zoo/signatures/yara/messaging_c2.yar +103 -0
  191. package/zoo/signatures/yara/multi_stage_attacks.yar +331 -0
  192. package/zoo/signatures/yara/obfuscation_patterns.yar +208 -0
  193. package/zoo/signatures/yara/powershell_attacks.yar +116 -0
  194. package/zoo/signatures/yara/rat_capabilities.yar +243 -0
  195. package/zoo/signatures/yara/self_propagation.yar +239 -0
  196. package/zoo/signatures/yara/unicode_stealth.yar +48 -0
  197. package/zoo/signatures/yara/websocket_c2.yar +83 -0
@@ -0,0 +1,331 @@
1
+ /*
2
+ Multi-Stage Attack Pattern Detection
3
+ Detects attack chains that combine multiple stages (download->write->execute, etc.)
4
+ */
5
+
6
+ rule LOADER_JS_Download_Write_Execute_Jan25 {
7
+ meta:
8
+ description = "Detects dropper pattern that downloads content, writes to temp directory, and executes"
9
+ severity = "critical"
10
+ score = 90
11
+ author = "vsix-audit"
12
+ date = "2025-01-29"
13
+
14
+ strings:
15
+ // Download stage
16
+ $dl1 = "fetch(" ascii wide
17
+ $dl2 = "axios.get" ascii wide
18
+ $dl3 = "https.get" ascii wide
19
+ $dl4 = "http.get" ascii wide
20
+ $dl5 = "request(" ascii wide
21
+ $dl6 = "got(" ascii wide
22
+
23
+ // Write stage
24
+ $write1 = "writeFileSync" ascii wide
25
+ $write2 = "writeFile" ascii wide
26
+ $write3 = "createWriteStream" ascii wide
27
+
28
+ // Execute stage
29
+ $exec1 = "child_process" ascii wide
30
+ $exec2 = ".exec(" ascii wide
31
+ $exec3 = ".spawn(" ascii wide
32
+ $exec4 = "execSync" ascii wide
33
+ $exec5 = "spawnSync" ascii wide
34
+
35
+ // Temp/hidden location indicators (require these for dropper pattern)
36
+ $temp1 = "/tmp/" ascii wide
37
+ $temp2 = "\\Temp\\" ascii wide
38
+ $temp3 = "os.tmpdir" ascii wide
39
+ $temp4 = "TEMP" ascii wide
40
+
41
+ // Base64 decode before write (payload decoding)
42
+ $decode1 = "atob(" ascii wide
43
+ $decode2 = "Buffer.from" ascii wide
44
+ $decode3 = "base64" ascii wide nocase
45
+
46
+ // Make executable before exec (chmod)
47
+ $chmod1 = "chmodSync" ascii wide
48
+ $chmod2 = "fs.chmod" ascii wide
49
+ $chmod3 = "chmod(" ascii wide
50
+
51
+ // Hidden file indicators (require 4+ bytes for good YARA atoms)
52
+ $hidden1 = "/tmp/." ascii wide // Unix hidden files in /tmp
53
+ $hidden2 = "\\AppData\\Local\\Temp\\." ascii wide // Windows hidden files in temp
54
+
55
+ condition:
56
+ // Require all three stages PLUS dropper indicator
57
+ any of ($dl*) and any of ($write*) and any of ($exec*) and
58
+ (
59
+ any of ($temp*) or // Writing to temp directory
60
+ any of ($decode*) or // Decoding payload before write
61
+ any of ($chmod*) or // Making file executable
62
+ any of ($hidden*) // Writing to hidden location
63
+ )
64
+ }
65
+
66
+ rule RAT_JS_Reverse_Shell_Jan25 {
67
+ meta:
68
+ description = "Detects reverse shell pattern where network socket is piped to shell for remote command execution"
69
+ severity = "high"
70
+ score = 85
71
+ author = "vsix-audit"
72
+ date = "2025-01-29"
73
+
74
+ strings:
75
+ // Network socket creation (raw TCP, not HTTP)
76
+ $net1 = "net.Socket" ascii wide
77
+ $net2 = "net.connect" ascii wide
78
+ $net3 = "net.createConnection" ascii wide
79
+ $net4 = "socket.connect" ascii wide
80
+
81
+ // Shell execution (specific paths, not just "child_process")
82
+ $shell1 = "/bin/sh" ascii wide
83
+ $shell2 = "/bin/bash" ascii wide
84
+ $shell3 = "/bin/zsh" ascii wide
85
+ $shell4 = "cmd.exe" ascii wide
86
+ $shell5 = "powershell.exe" ascii wide nocase
87
+
88
+ // Piping stdin/stdout (require both for reverse shell)
89
+ $pipe_stdin = "stdin" ascii wide
90
+ $pipe_stdout = "stdout" ascii wide
91
+ $pipe_method = ".pipe(" ascii wide
92
+
93
+ // Spawn with shell option (classic reverse shell pattern)
94
+ $spawn_shell = /spawn\s*\([^)]*shell\s*:\s*true/i ascii wide
95
+
96
+ condition:
97
+ // Classic reverse shell: socket + shell path + stdin/stdout piping
98
+ (any of ($net*) and any of ($shell*) and $pipe_stdin and $pipe_stdout) or
99
+ // Or: socket + shell path + pipe method
100
+ (any of ($net*) and any of ($shell*) and $pipe_method) or
101
+ // Or: spawn with shell option + socket
102
+ ($spawn_shell and any of ($net*))
103
+ }
104
+
105
+ rule STEALER_JS_Keylogger_Jan25 {
106
+ meta:
107
+ description = "Detects keylogger pattern that captures keyboard or clipboard input and exfiltrates data"
108
+ severity = "high"
109
+ score = 85
110
+ author = "vsix-audit"
111
+ date = "2025-01-29"
112
+
113
+ strings:
114
+ // High-confidence capture patterns (actual keylogging, not text document events)
115
+ $capture_key1 = "keydown" ascii wide
116
+ $capture_key2 = "keypress" ascii wide
117
+ $capture_key3 = "keyup" ascii wide
118
+ $capture_clip = "clipboard.readText" ascii wide
119
+
120
+ // Low-confidence capture (VS Code API - needs additional indicators)
121
+ $capture_vsc = "onDidChangeTextDocument" ascii wide
122
+
123
+ // Storage with suspicious naming
124
+ $store_suspicious1 = "keylog" ascii wide nocase
125
+ $store_suspicious2 = "keystroke" ascii wide nocase
126
+ $store_suspicious3 = "inputBuffer" ascii wide
127
+ $store_suspicious4 = "capturedKeys" ascii wide
128
+
129
+ // Generic storage (needs other indicators)
130
+ $store_generic1 = "globalState" ascii wide
131
+ $store_generic2 = "appendFile" ascii wide
132
+
133
+ // High-confidence exfil (known bad destinations)
134
+ $exfil_discord = "discord.com/api/webhooks" ascii wide
135
+ $exfil_telegram = "api.telegram.org" ascii wide
136
+
137
+ // Generic exfil (needs other indicators)
138
+ $exfil_generic1 = "axios.post" ascii wide
139
+ $exfil_generic2 = ".post(" ascii wide
140
+
141
+ condition:
142
+ // High confidence: keyboard events + storage + exfil
143
+ (any of ($capture_key*) and any of ($store_suspicious*, $store_generic*) and any of ($exfil_discord, $exfil_telegram, $exfil_generic*)) or
144
+ // High confidence: clipboard read + known bad destination
145
+ ($capture_clip and any of ($exfil_discord, $exfil_telegram)) or
146
+ // Medium confidence: VS Code API + suspicious storage names + exfil
147
+ ($capture_vsc and any of ($store_suspicious*) and any of ($exfil_discord, $exfil_telegram, $exfil_generic*))
148
+ }
149
+
150
+ rule RAT_JS_Persistence_Startup_Jan25 {
151
+ meta:
152
+ description = "Detects persistence mechanism that modifies startup files, registry keys, or scheduled tasks"
153
+ severity = "high"
154
+ score = 80
155
+ author = "vsix-audit"
156
+ date = "2025-01-29"
157
+
158
+ strings:
159
+ // Unix startup file paths (require path context, not just filename)
160
+ $unix_path1 = /\$HOME\/\.bashrc/ ascii wide
161
+ $unix_path2 = /\$HOME\/\.zshrc/ ascii wide
162
+ $unix_path3 = /\$HOME\/\.profile/ ascii wide
163
+ $unix_path4 = /\$HOME\/\.bash_profile/ ascii wide
164
+ $unix_path5 = "process.env.HOME" ascii wide
165
+ $unix_path6 = "os.homedir()" ascii wide
166
+
167
+ // macOS Launch services (specific paths)
168
+ $mac1 = "/Library/LaunchAgents" ascii wide
169
+ $mac2 = "/Library/LaunchDaemons" ascii wide
170
+ $mac3 = "~/Library/LaunchAgents" ascii wide
171
+
172
+ // Crontab manipulation (require command)
173
+ $cron1 = "crontab -" ascii wide
174
+ $cron2 = /crontab\s+(--|-e|-l|-r)/ ascii wide
175
+
176
+ // Windows registry persistence (require full path)
177
+ $win_reg1 = "CurrentVersion\\Run" ascii wide
178
+ $win_reg2 = "CurrentVersion\\RunOnce" ascii wide
179
+ $win_reg3 = "HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Run" ascii wide nocase
180
+ $win_reg4 = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run" ascii wide nocase
181
+
182
+ // Windows scheduled tasks (require schtasks command)
183
+ $win_sched1 = /schtasks\s+\/create/i ascii wide
184
+ $win_sched2 = /schtasks\s+\/change/i ascii wide
185
+
186
+ // Write operations with shell config context
187
+ $write_shell = /write(File|FileSync)\s*\([^)]*\.(bashrc|zshrc|profile|bash_profile)/ ascii wide
188
+
189
+ condition:
190
+ // Unix: home path + shell config reference
191
+ ((any of ($unix_path*)) and any of ($unix_path1, $unix_path2, $unix_path3, $unix_path4)) or
192
+ // macOS Launch services
193
+ any of ($mac*) or
194
+ // Crontab commands
195
+ any of ($cron*) or
196
+ // Windows registry persistence paths
197
+ any of ($win_reg*) or
198
+ // Windows scheduled task creation
199
+ any of ($win_sched*) or
200
+ // Direct write to shell config
201
+ $write_shell
202
+ }
203
+
204
+ rule MAL_JS_Self_Propagation_Publish_Jan25 {
205
+ meta:
206
+ description = "Detects self-propagation worm pattern that accesses publish tokens and runs publish commands"
207
+ severity = "critical"
208
+ score = 95
209
+ author = "vsix-audit"
210
+ date = "2025-01-29"
211
+ reference = "https://www.koi.security/blog/glassworm-first-self-propagating-worm-using-invisible-code-hits-openvsx-marketplace"
212
+
213
+ strings:
214
+ // Credential access
215
+ $cred1 = ".npmrc" ascii wide
216
+ $cred2 = "NPM_TOKEN" ascii wide
217
+ $cred3 = "OPENVSX_TOKEN" ascii wide
218
+ $cred4 = "VSCE_PAT" ascii wide
219
+
220
+ // Publish commands
221
+ $pub1 = "npm publish" ascii wide
222
+ $pub2 = "vsce publish" ascii wide
223
+ $pub3 = "ovsx publish" ascii wide
224
+ $pub4 = "yarn publish" ascii wide
225
+
226
+ condition:
227
+ any of ($cred*) and any of ($pub*)
228
+ }
229
+
230
+ rule MAL_JS_Supply_Chain_Install_Jan25 {
231
+ meta:
232
+ description = "Detects supply chain attack that runs malicious commands during npm package lifecycle hooks"
233
+ severity = "high"
234
+ score = 85
235
+ author = "vsix-audit"
236
+ date = "2025-01-29"
237
+
238
+ strings:
239
+ // Lifecycle script indicators (in package.json context)
240
+ $script1 = "preinstall" ascii wide
241
+ $script2 = "postinstall" ascii wide
242
+ $script3 = "prepublish" ascii wide
243
+
244
+ // System info gathering
245
+ $sys1 = "os.homedir" ascii wide
246
+ $sys2 = "os.userInfo" ascii wide
247
+ $sys3 = "os.hostname" ascii wide
248
+ $sys4 = "process.env.HOME" ascii wide
249
+ $sys5 = "process.env.USER" ascii wide
250
+
251
+ // Network beacon
252
+ $net1 = "fetch(" ascii wide
253
+ $net2 = "axios" ascii wide
254
+ $net3 = "https.request" ascii wide
255
+
256
+ condition:
257
+ any of ($script*) and any of ($sys*) and any of ($net*)
258
+ }
259
+
260
+ rule STEALER_JS_Crypto_Wallet_Jan25 {
261
+ meta:
262
+ description = "Detects cryptocurrency stealer that accesses wallet directories, keys, or seed phrases and exfiltrates"
263
+ severity = "high"
264
+ score = 85
265
+ author = "vsix-audit"
266
+ date = "2025-01-29"
267
+
268
+ strings:
269
+ // Wallet directory paths (more specific than just wallet names)
270
+ $wallet_path1 = ".config/solana" ascii wide
271
+ $wallet_path2 = "AppData\\Roaming\\Ethereum" ascii wide
272
+ $wallet_path3 = "AppData\\Local\\Exodus" ascii wide
273
+ $wallet_path4 = "AppData\\Local\\Phantom" ascii wide
274
+ $wallet_path5 = "Library/Application Support/Exodus" ascii wide
275
+ $wallet_path6 = ".ethereum/keystore" ascii wide
276
+ $wallet_path7 = "wallet.dat" ascii wide
277
+
278
+ // Browser extension wallet paths
279
+ $ext_metamask = "nkbihfbeogaeaoehlefnkodbefgpgknn" ascii wide // MetaMask extension ID
280
+ $ext_phantom = "bfnaelmomeimhlpmgjnjophhpkkoljpa" ascii wide // Phantom extension ID
281
+
282
+ // Seed phrase / key extraction patterns (specific to crypto)
283
+ $key_mnemonic = "mnemonic" ascii wide nocase
284
+ $key_seedphrase = "seedPhrase" ascii wide
285
+ $key_privatekey = "privateKey" ascii wide
286
+ $key_secretkey = "secretKey" ascii wide
287
+
288
+ // File read patterns targeting wallet files
289
+ $read_wallet = /readFile[^)]*wallet/i ascii wide
290
+ $read_keystore = /readFile[^)]*keystore/i ascii wide
291
+
292
+ // High-confidence exfil (known bad destinations)
293
+ $exfil_discord = "discord.com/api/webhooks" ascii wide
294
+ $exfil_telegram = "api.telegram.org" ascii wide
295
+
296
+ condition:
297
+ // Wallet paths + seed/key extraction
298
+ (any of ($wallet_path*, $ext_*) and any of ($key_*)) or
299
+ // Wallet file reads + known bad exfil
300
+ (any of ($read_wallet, $read_keystore) and any of ($exfil_discord, $exfil_telegram)) or
301
+ // Wallet paths + known bad exfil
302
+ (any of ($wallet_path*, $ext_*) and any of ($exfil_discord, $exfil_telegram))
303
+ }
304
+
305
+ rule MAL_JS_GlassWorm_Extension_Modification_Jan25 {
306
+ meta:
307
+ description = "Detects GlassWorm-style attack that modifies other VS Code extensions to inject malicious code"
308
+ severity = "critical"
309
+ score = 95
310
+ author = "vsix-audit"
311
+ date = "2025-01-29"
312
+ reference = "https://www.koi.security/blog/glassworm-first-self-propagating-worm-using-invisible-code-hits-openvsx-marketplace"
313
+
314
+ strings:
315
+ // Extension paths
316
+ $path1 = ".vscode/extensions" ascii wide
317
+ $path2 = ".vscode-server/extensions" ascii wide
318
+ $path3 = "extensions/" ascii wide
319
+
320
+ // File modification
321
+ $mod1 = "writeFileSync" ascii wide
322
+ $mod2 = "writeFile" ascii wide
323
+
324
+ // Extension files
325
+ $file1 = "extension.js" ascii wide
326
+ $file2 = "package.json" ascii wide
327
+ $file3 = ".vsix" ascii wide
328
+
329
+ condition:
330
+ any of ($path*) and any of ($mod*) and any of ($file*)
331
+ }
@@ -0,0 +1,208 @@
1
+ /*
2
+ JavaScript Obfuscation Pattern Detection
3
+ Detects common obfuscation techniques used to hide malicious code
4
+ */
5
+
6
+ rule SUSP_JS_Obfuscator_Hex_Vars_Jan25 {
7
+ meta:
8
+ description = "Detects javascript-obfuscator tool signature with _0x prefixed hexadecimal variable names"
9
+ severity = "high"
10
+ score = 80
11
+ author = "vsix-audit"
12
+ date = "2025-01-29"
13
+
14
+ strings:
15
+ // _0x followed by 4+ hex chars - signature of javascript-obfuscator
16
+ $hex_var = /_0x[a-fA-F0-9]{4,}/ ascii wide
17
+
18
+ condition:
19
+ #hex_var >= 5
20
+ }
21
+
22
+ rule SUSP_JS_FromCharCode_Chain_Jan25 {
23
+ meta:
24
+ description = "Detects String.fromCharCode with many arguments used to hide string content from static analysis"
25
+ severity = "high"
26
+ score = 75
27
+ author = "vsix-audit"
28
+ date = "2025-01-29"
29
+
30
+ strings:
31
+ // fromCharCode with 5+ comma-separated numbers
32
+ $charcode = /String\.fromCharCode\s*\(\s*(\d+\s*,\s*){5,}/ ascii wide
33
+
34
+ condition:
35
+ $charcode
36
+ }
37
+
38
+ rule SUSP_JS_Hex_Escape_Chain_Jan25 {
39
+ meta:
40
+ description = "Detects 10+ consecutive hex escape sequences (\\xNN) indicating obfuscated string content"
41
+ severity = "medium"
42
+ score = 60
43
+ author = "vsix-audit"
44
+ date = "2025-01-29"
45
+
46
+ strings:
47
+ // Consecutive hex escape pairs - provides 8-byte atoms for Aho-Corasick
48
+ // Each matches 2 consecutive \xNN patterns, requiring 5+ matches = 10+ escapes
49
+ $h1 = /\\x[0-9a-fA-F]{2}\\x[0-9a-fA-F]{2}/ ascii wide
50
+
51
+ condition:
52
+ #h1 >= 5
53
+ }
54
+
55
+ rule SUSP_JS_Decimal_Byte_Array_Jan25 {
56
+ meta:
57
+ description = "Detects large array of 20+ decimal byte values likely containing encoded payload data"
58
+ severity = "medium"
59
+ score = 55
60
+ author = "vsix-audit"
61
+ date = "2025-01-29"
62
+
63
+ strings:
64
+ // Comma-separated number patterns (4-byte atoms)
65
+ // Matches ", NN," patterns - 20+ of these indicates byte array
66
+ $num = /,\s?\d{1,3},/ ascii wide
67
+
68
+ // Array opener with number
69
+ $arr_open = /\[\d{1,3},/ ascii wide
70
+
71
+ condition:
72
+ $arr_open and #num >= 19
73
+ }
74
+
75
+ rule SUSP_JS_Bracket_Notation_Chain_Jan25 {
76
+ meta:
77
+ description = "Detects long bracket notation property chains used to hide method calls from static analysis"
78
+ severity = "medium"
79
+ score = 65
80
+ author = "vsix-audit"
81
+ date = "2025-01-29"
82
+
83
+ strings:
84
+ // 4+ consecutive bracket notation accesses
85
+ $bracket_chain = /\[\s*['"][a-zA-Z]+['"]\s*\](\s*\[\s*['"][a-zA-Z]+['"]\s*\]){3,}/ ascii wide
86
+
87
+ condition:
88
+ $bracket_chain
89
+ }
90
+
91
+ rule SUSP_JS_String_Array_Rotation_Jan25 {
92
+ meta:
93
+ description = "Detects javascript-obfuscator string table pattern with large array and hex variable names"
94
+ severity = "medium"
95
+ score = 55
96
+ author = "vsix-audit"
97
+ date = "2025-01-29"
98
+
99
+ strings:
100
+ // Literal anchors for string array separators (4-byte atoms)
101
+ $sep1 = "','" ascii wide
102
+ $sep2 = "\",\"" ascii wide
103
+ $sep3 = "', '" ascii wide
104
+ $sep4 = "\", \"" ascii wide
105
+
106
+ // Array assignment patterns
107
+ $arr1 = "=['" ascii wide
108
+ $arr2 = "=[\"" ascii wide
109
+ $arr3 = "= ['" ascii wide
110
+ $arr4 = "= [\"" ascii wide
111
+
112
+ // javascript-obfuscator hex variable (bounded to reduce backtracking)
113
+ $obf_hex_var = /_0x[a-fA-F0-9]{4,8}/ ascii wide
114
+
115
+ condition:
116
+ // Require separators + array assignment + multiple hex vars
117
+ (2 of ($sep*)) and (1 of ($arr*)) and #obf_hex_var >= 3
118
+ }
119
+
120
+ rule SUSP_JS_Obfuscation_Eval_Jan25 {
121
+ meta:
122
+ description = "Detects obfuscation patterns like hex vars or fromCharCode combined with eval execution"
123
+ severity = "critical"
124
+ score = 95
125
+ author = "vsix-audit"
126
+ date = "2025-01-29"
127
+
128
+ strings:
129
+ // Obfuscation indicators
130
+ $obf1 = /_0x[a-fA-F0-9]{4,}/ ascii wide
131
+ $obf2 = /\\x[a-fA-F0-9]{2}/ ascii wide
132
+ $obf3 = "String.fromCharCode" ascii wide
133
+
134
+ // Eval patterns
135
+ $eval1 = "eval(" ascii wide
136
+ $eval2 = "new Function(" ascii wide
137
+
138
+ condition:
139
+ any of ($obf*) and any of ($eval*)
140
+ }
141
+
142
+ rule SUSP_JS_Packer_Dean_Edwards_Jan25 {
143
+ meta:
144
+ description = "Detects Dean Edwards style JavaScript packer using eval(function(p,a,c,k,e pattern"
145
+ severity = "high"
146
+ score = 85
147
+ author = "vsix-audit"
148
+ date = "2025-01-29"
149
+
150
+ strings:
151
+ // Common packer patterns
152
+ $packer1 = "eval(function(p,a,c,k,e," ascii wide
153
+ $packer2 = /}\s*\(\s*['"][^'"]{100,}['"]/ ascii wide
154
+ $packer3 = ".split('|')" ascii wide
155
+
156
+ condition:
157
+ $packer1 or ($packer2 and $packer3)
158
+ }
159
+
160
+ rule SUSP_JS_JJEncode_Jan25 {
161
+ meta:
162
+ description = "Detects JJEncode JavaScript obfuscation using $=~[] and $$$ patterns to hide code"
163
+ severity = "high"
164
+ score = 90
165
+ author = "vsix-audit"
166
+ date = "2025-01-29"
167
+
168
+ strings:
169
+ $jj1 = "$=~[]" ascii wide
170
+ $jj2 = "_=~[]" ascii wide
171
+ $jj3 = "$$$$" ascii wide
172
+
173
+ condition:
174
+ // Require 2+ patterns to avoid FPs on bundled code with isolated patterns
175
+ 2 of them
176
+ }
177
+
178
+ rule SUSP_JS_AAEncode_Jan25 {
179
+ meta:
180
+ description = "Detects AAEncode JavaScript obfuscation using emoticon-based encoding patterns"
181
+ severity = "high"
182
+ score = 85
183
+ author = "vsix-audit"
184
+ date = "2025-01-29"
185
+
186
+ strings:
187
+ $aa1 = /\(\s*!\s*\[\s*\]\s*\+\s*""\s*\)/ ascii wide
188
+ $aa2 = /\[\s*\+\s*!\s*\+\s*\[\s*\]\s*\]/ ascii wide
189
+
190
+ condition:
191
+ #aa1 > 3 or #aa2 > 3
192
+ }
193
+
194
+ rule SUSP_JS_JSFuck_Jan25 {
195
+ meta:
196
+ description = "Detects JSFuck obfuscation encoding JavaScript using only []()!+ characters"
197
+ severity = "critical"
198
+ score = 95
199
+ author = "vsix-audit"
200
+ date = "2025-01-29"
201
+
202
+ strings:
203
+ // Long sequences of only these characters
204
+ $jsfuck = /[\[\]\(\)!\+]{50,}/ ascii wide
205
+
206
+ condition:
207
+ $jsfuck
208
+ }
@@ -0,0 +1,116 @@
1
+ /*
2
+ PowerShell Attack Detection
3
+ Detects malicious PowerShell patterns commonly used in VS Code extension malware
4
+ */
5
+
6
+ rule SUSP_PS_Hidden_Window_Jan25 {
7
+ meta:
8
+ description = "Detects PowerShell execution with hidden window flag to avoid user detection"
9
+ severity = "critical"
10
+ score = 90
11
+ author = "vsix-audit"
12
+ date = "2025-01-29"
13
+
14
+ strings:
15
+ $hidden1 = "-WindowStyle Hidden" ascii wide nocase
16
+ $hidden2 = "-w hidden" ascii wide nocase
17
+ $hidden3 = "-windowstyle h" ascii wide nocase
18
+
19
+ $ps1 = "powershell" ascii wide nocase
20
+ $ps2 = "pwsh" ascii wide nocase
21
+
22
+ condition:
23
+ any of ($ps*) and any of ($hidden*)
24
+ }
25
+
26
+ rule LOADER_PS_Download_Execute_Jan25 {
27
+ meta:
28
+ description = "Detects PowerShell download and execute cradle using IEX with Invoke-WebRequest or WebClient"
29
+ severity = "critical"
30
+ score = 95
31
+ author = "vsix-audit"
32
+ date = "2025-01-29"
33
+
34
+ strings:
35
+ // Require PowerShell context
36
+ $ps1 = "powershell" ascii wide nocase
37
+ $ps2 = "pwsh" ascii wide nocase
38
+
39
+ // Direct piped IEX patterns (most malicious pattern)
40
+ // These match actual PowerShell IEX cradles, not random JS with curl/iex strings
41
+ $iex_pipe1 = /\|\s*(iex|Invoke-Expression)/i ascii wide
42
+ $iex_pipe2 = /(irm|iwr|Invoke-RestMethod|Invoke-WebRequest)[^;]{0,80}\|\s*(iex|Invoke-Expression)/i ascii wide
43
+
44
+ // .NET WebClient download+execute (classic PowerShell dropper)
45
+ $webclient1 = "Net.WebClient" ascii wide nocase
46
+ $webclient2 = "DownloadString" ascii wide nocase
47
+ $webclient3 = "DownloadFile" ascii wide nocase
48
+
49
+ // Full form Invoke-Expression
50
+ $iex_full = "Invoke-Expression" ascii wide nocase
51
+
52
+ condition:
53
+ // Either: PowerShell context with IEX pipe patterns
54
+ (any of ($ps*) and any of ($iex_pipe*)) or
55
+ // Or: .NET WebClient patterns with Invoke-Expression (no PS context needed)
56
+ (any of ($webclient*) and $iex_full)
57
+ }
58
+
59
+ rule SUSP_PS_Encoded_Command_Jan25 {
60
+ meta:
61
+ description = "Detects PowerShell with base64 encoded command flag used to hide malicious payload"
62
+ severity = "high"
63
+ score = 85
64
+ author = "vsix-audit"
65
+ date = "2025-01-29"
66
+
67
+ strings:
68
+ $enc1 = "-enc " ascii wide nocase
69
+ $enc2 = "-EncodedCommand" ascii wide nocase
70
+ $enc3 = "-ec " ascii wide nocase
71
+
72
+ $ps1 = "powershell" ascii wide nocase
73
+ $ps2 = "pwsh" ascii wide nocase
74
+
75
+ condition:
76
+ any of ($ps*) and any of ($enc*)
77
+ }
78
+
79
+ rule SUSP_PS_Bypass_Policy_Jan25 {
80
+ meta:
81
+ description = "Detects PowerShell execution policy bypass used to run unsigned or restricted scripts"
82
+ severity = "high"
83
+ score = 80
84
+ author = "vsix-audit"
85
+ date = "2025-01-29"
86
+
87
+ strings:
88
+ $bypass1 = "-ExecutionPolicy Bypass" ascii wide nocase
89
+ $bypass2 = "-ep bypass" ascii wide nocase
90
+ $bypass3 = "-exec bypass" ascii wide nocase
91
+ $bypass4 = "Set-ExecutionPolicy" ascii wide nocase
92
+
93
+ $ps1 = "powershell" ascii wide nocase
94
+ $ps2 = "pwsh" ascii wide nocase
95
+
96
+ condition:
97
+ any of ($ps*) and any of ($bypass*)
98
+ }
99
+
100
+ rule SUSP_PS_AMSI_Bypass_Jan25 {
101
+ meta:
102
+ description = "Detects attempt to bypass Windows AMSI (Anti-Malware Scan Interface) for evasion"
103
+ severity = "critical"
104
+ score = 95
105
+ author = "vsix-audit"
106
+ date = "2025-01-29"
107
+
108
+ strings:
109
+ $amsi1 = "AmsiUtils" ascii wide nocase
110
+ $amsi2 = "amsiInitFailed" ascii wide nocase
111
+ $amsi3 = "AmsiScanBuffer" ascii wide nocase
112
+ $amsi4 = "[Ref].Assembly.GetType" ascii wide
113
+
114
+ condition:
115
+ any of them
116
+ }