1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
|
_ _ _
| | | | | |
| |__ | |_ _ ___ __| |_ __ ___ __ _ _ __ ___
| '_ \| | | | |/ _ \/ _` | '__/ _ \/ _` | '_ ` _ \
| |_) | | |_| | __/ (_| | | | __/ (_| | | | | | |
|_.__/|_|\__,_|\___|\__,_|_| \___|\__,_|_| |_| |_|
================================================================================
i went on a [47d1] and all i got was this lousy vscode worm...
(or)
i tried to write a README and all i got was this lousy writeup
NOTE: some of this writeup had to be redacted for, uh, reasons. any redactions
will be replaced with a relevant sha256 tag, which will be expanded at
the end of the file. example: [480b2b30]
INTRO
late one night, i had an idea. 'memba xcodeghost? pepperidge farm
remembers. what about vscodeghost? except, instead of infecting *OS apps,
it infects vscode. hmm?
it's a vscode worm. alice installs an infected extension, and any
extensions (packaged or minified) become infected. once uploaded to the
marketplace, or otherwise distributed, each install becomes a new alice.
i will readily admit that i [084a1631cd59ed1a3244305b6fbc28a2d93c97a7df]
this worm was written over the course of a 3-week [47d1] in which i
[e6400ef383e6509261b52d3f16e466752487a27e19d0579a3eb5567bb7244055]. lmao
it was bad... [e2b5ed016720f26e10bc200c8c528241298f9a64db8559b9ad8276b8]
shit. [affa8bd6c29038c52a423488d1b037615d7bb8a8384736d46837c662887303f]...
i know i gotta sleep in a few hours. welp. note from future spv: you're a
dick. it's half past 2 and i'm copy editing your shit. fuck you. <3
with that out of the way, let's begin with the writeup & description. i
think the only real way to do this is to go through the architecture
first. otherwise idk that any of this will make sense. it took me a couple
readthroughs of the source to understand what the fuck was going on...
ARCHITECTURE
bluedream is made up of two interlinked components, with separate
directories for each. cnc holds the command-and-control server, with inf
containing the src for the local infection, and its custom build system.
these components will be referred to by their respective directory names,
cnc, and inf.
inf is built by build_payload.js & gen_pwnage.js. build_payload is
passed a payload descriptor (pd) file, which contains a reference to each
JS file to be included. this allows one to easily configure of the exact
features of a payload. build_payload also requires the specification of an
output file, to which the payload, minified & brotli compressed, will be
written.
after this, gen_pwnage wraps the payload. to ease identification of the
infection, as will be described in detail further on. in the end, one will
be left with inf.
cnc is also a custom job. cnc accepts a block descriptor (bd) file,
which is used to serve different payloads to different victims. a bd file
is composed of one or more entries of the form
`(ip)[/(block_size)]:(payload)[,(script)]`. any ip within said block will
be served the payload specified, with the script (if) specified being ran
upon each serving.
with the basics of bluedream's architecture covered, let's get into the
nitty-griddy details of what makes her tick.
TICK TICK TICK
inf runs every time an infected extension is triggered, often at
startup. upon being triggered, inf phones home to cnc at most once a day,
which provides an updated copy of inf. after this, it spreads to any files
in the workspace which contain (compiled) extension code. following this,
inf spreads to all other currently installed extensions, in case the
original vector is removed. lastly, inf will spread to any packaged
extensions in the current workspace.
in the following sections, we will discuss in (potentially excruciating)
detail, how each of these vectors of spread work.
SENTINELS
the packaged infection is stored within extensions. much like a
biological virus, it needs a host. to separate inf from the legitimate
code, two markers are used. the specifics do not matter, but my commie ass
used /*瑲慮猠*/ and /*物杨瑳*/. these are entirely gibberish Chinese characters.
however, pipe them into a hex dump, and they are -- still garbage.
codepoints, though? U+7472, U+616E, etc. interesting. interpret UTF-16 as
ASCII, and they spell out "trans rights" :P
THE MINI
extensions are fun things. while you may write your neat and clean
typescript, at the end of the day, it's javascript all the way down... and
because we decided to take a technology hacked together in under two weeks
and use it to power the entire fucking internet and most user-facing
applications, we developed minification. even then, discord takes up
~100MB because it's its own damned chromium install... i fucking hate
javascript.
minification also provides some convenience for malware developers, like
us. several megabyte, one line, obfuscated javascript isn't "a major cause
for concern", it's just "webpack"! yay!
we pick a spot to insert inf. either between two lines of typescript
boilerplate, in a known location for webpack, or just at the end. this
procedure is used to infect all extensions -- including local output
files, as in `extension_js_spread`, local extension persistence as in
`extension_spread`, and packaged extensions as in `vsix_spread`. what is a
vsix, anyway?
WHAT IS A VSIX, ANYWAY?
vscode uses the vsix format to package extensions. vsix is, in essence,
a glorified zip file. the relevant files for this writeup being
extension.vsixmanifest, extension/package.json*, and
extension/out/extension.js*.
a vsixmanifest is an XML-formatted file that is, (i believe), used in
both visual studio, and vscode. the relevant key in this file is
"Microsoft.VisualStudio.Code.Manifest", which points at the package.json.
within package.json, the key "main" points at the javascript code ran
when the extension is triggered. with this knowledge, we are able to use
the procedure described in THE MINI in order to pwn packaged extensions.
the only difference being that we need logic to process zip files. the
relevant code is in inf/mal/spread/vsix.js. this is a combination of YAZL,
and an utterly nightmarish artisan-rolled zip file parser that to cope
with imma need some artisan-rolled shit of my own...
*these are the file names by convention. they do not technically have to
be present at these particular locations.
CONCLUSION(s)
what would have stopped this attack from working? for one, signing
vscode extensions upon packaging would have helped. vsce, the main tool
used to package (, and often publish) vscode extensions, has had a github
issue for this very topic since july of 2017. vscode never implemented
support, though. [vsce_issue]
i recall reading that extensions are signed upon being uploaded to the
marketplace, but this would not prevent this attack, as extensions are
already pwned by the time they're sent to the marketplace.
and lastly, you are not an APT. sorry. this worm was written over the
course of a 3 week [47d1] -- a coping mechanism after some interpersonal
termoil, in which i experienced such delusions of grandeur that i
genuinely believed that this worm was my golden ticket -- that i would be
set for life, money wise. this is of course, utterly laughable. i threw
away the plan immediately after deployment anyway, as i had recently
[82821f1ccb55943a67b2d7689e08f7c6e548d3778b0835d2d0eebaf43c81e2bd], and i
proceeded to [1c747f5c1eac42e9eb77c538a4f77b3b4c18b0bd8ce62dc88637e585a2].
SHA_TAGS
==========
084a1631cd59ed1a3244305b6fbc28a2d93c97a7df8dc117c90955a728b00d35
1c747f5c1eac42e9eb77c538a4f77b3b4c18b0bd8ce62dc88637e585a2701298
47d140cbea401190e9612d5c7e3bd78a5fbb63def20832036a1b2c4fbbac636b
480b2b30b0c14c30d3f130561a44c77cba580910d2789593e4e379c2d26bc3f8
82821f1ccb55943a67b2d7689e08f7c6e548d3778b0835d2d0eebaf43c81e2bd
affa8bd6c29038c52a423488d1b037615d7bb8a8384736d46837c662887303f9
e2b5ed016720f26e10bc200c8c528241298f9a64db8559b9ad8276b859a660ac
e6400ef383e6509261b52d3f16e466752487a27e19d0579a3eb5567bb7244055
OG: 6628738fac546ec9cf6193e2b3ab753e11f62f4fff8348ee4e0f499e00167210
LINKS
=======
vsce_issue: https://github.com/Microsoft/vscode-vsce/issues/191
~ spv, 2025.
|