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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
|
STLDoctor
=========
STLDoctor is a plain-text protocol service, that allows users to upload STL files
and generate reports that include information on the files..
- model name
- binary header
- file type (bin vs ascii)
- file size
- triangle count
- bounding box size and position
Uploaded models and generated reports are stored in a directory structure.
Unregistered users have their files saved in a collective directory, which
allows users to search for public models via model name. Registered users have
their uploads saved to a private directory. This (theoretically) prevents other
users from accessing their files.
The service is hosted with socat, one process per client.
A cron job is used to periodically clean up models by using their *last modified* date.
RCE Countermeasures
===================
It is good practice to take preventitive measures against unintentional RCE,
which can be used to cause havoc on vulnboxes and make services go mumble.
1. Enable additional security features via flags during compilation:
`CFLAGS = -fPIE -fstack-protector-strong -D_FORTIFY_SOURCE=2 -O2`
- `-fPIE`: enable position independent executable section
- `-fstack-protector-strong`: enable stack canaries in functions with local variables that are prone to overflow
- `-D_FORTIFY_SOURCE=2`: gcc buffer overflow detection
- `-O2`: enable level 2 of compiler optimizations (required for `_FORITFY_SOURCE`)
`LDFLAGS = -Wl,-z,now -Wl,-z,relro`
- `-Wl,-z,now`: tell dynamic linker to resolve symbols ASAP instead of lazy loading
- `-Wl,-z,relro`: tell dynamic linker to make `got` section read-only after resolving symbols
2. Chroot each service instance via socat so it can only access
uploaded files and not corrupt the system.
3. Prevent the service binary from creating child processes.
Checker
=======
The checker checks the following behavior:
- File upload and search (for both bin and ascii):
- Open a session
- Upload a file of random, valid contents with random model name
- Ensure file is listed in search
- Open a new session
- Ensure file is listed in search
- Ensure file is not listed in search for different model name
- Registering and private files (for both bin and ascii):
- Open a session
- Register with a random password
- Upload a file of random, valid contents with random model name
- Open a new session
- Ensure file is not listed in search
- Register with previous password
- Ensure file is listed in search
- Check upload ordering and accessing indeces != 0:
- Open a session
- Upload a file of random, valid contents with random model and solid name
- Upload a different file of random, valid contents with same model name but different solid name
- Open a new session
- search for same model name and pick 1st entry
- Compare returned solid name with expected (1st upload)
- search for same model name and pick 2nd entry
- Compare returned solid name with expected (2nd upload)
The checker tenets:
- A checker SHOULD not be easily identified by the examination of network traffic
satisfied, because checker uses regular user interface and picks strings from a wordlist
to appear more human (TODO)
- A checker SHOULD use unusual, incorrect or pseudomalicious input to detect network filters
satisfied, send various garbage bytes for model name and file contents (TODO)
The checker does the following to upload the first flagstore's flag:
- Open a session
- Use `upload` to upload a file of random, valid contents with a
the flag as the model name
The checker does the following to upload the second flagstore's flag:
- Open a session
- Register as a premium user
- Use `upload` to upload a binary STL with the flag as its solidname
and a random model name chosen from a wordlist with numbers for
collision resistance
The checker should not be easily identifiable, since this could allow
players to only enable certain service functions when the checker is
detected.
Notes on evading detection of the checker through traffic analysis:
- File upload contents are randomly generated valid STLs and the model name is
picked randomly from a wordlist to blend in with regular requests
- The username used for premium user registration is chosen at random from
a wordlist (with numbers appended for collision resistance) to blend
in with other requests
- The STL file which belongs to the second flagstore is not easily identifiable
since the binary file format is used and viewing the file contents does not
help identify the 3d model encoded
- The first flagstore's flag IS easily identifiable, since it is sent in cleartext
to the service, however, the checker makes sure to test upload and searching
functionality of files with other model names in another session as well
Vuln 1: Value Collision
=======================
Discovery
---------
This vulnerability can be found by reading the source code.
The utility function `void freadstr(FILE *f, char **dst)` reads a
null-terminated string from the given file, allocates space for it and saves the
pointer to its contents in `dst`.
```C
void
freadstr(FILE *f, char **dst)
{
size_t start, len;
char c;
start = ftell(f);
for (len = 0; (c = fgetc(f)) != EOF && c; len++);
fseek(f, start, SEEK_SET);
*dst = checkp(calloc(1, len + 1));
fread(*dst, len, 1, f);
fgetc(f);
}
```
To determine whether the end-of-file was reached, the return value of `int
fgetc(FILE *f)` is compared to the constant `EOF`, which has a value of `-1`.
The problem lies in the fact, that this comparison is done following a demotion
of the return value to `char` through the assignment and a subsequent promotion
to `int`, which results in an arithmetic extension. As a result, reading the
value char `0xff` would promote it to `0xffffffff` with a value of `-1`,
preventing the function from reading the complete string.
This allows an attacker to cleverly truncate a string before it has ended to
manipulate the content of strings which follow it. In this case, the model name
is saved before the model hash in the information file. By adding a `0xff` to
the end of our uploaded model's name, the model hash is read as an empty string
following a `search` of the file's contents. Since any following `search` will use
the previously loaded models hash to find the file via prefix match, any files
uploaded by unregistered users may be accessed by a user.
The flag is saved in the model name.
Exploiting
----------
1. Open a session
2. Use `upload` to upload an STL file and specify a model name ending in `0xff`
3. Open a new session
4. Use `search` with the same model name from **step 1** to retrieve to load the
parsed information of the file you just uploaded
5. Use `search` again.. this will now use the cached hash which should be empty,
allowing you to accesss any of the files uploaded by unregistered users
Patching
--------
For an example fix, see the unified format patch `services/src/patches/flagstore1.diff`.
Vuln 2: Invalid Format String
=============================
Discovery
---------
This vulnerability can be found by reading the source code.
The utility function `const char* mhash(const char *str, int len)` is used
to generate a fixed-width hash from a string input in a static buffer
and return a pointer to that buffer.
```C
const char*
mhash(const char *str, int len)
{
static char buf[MHASHLEN + 1];
int i, k, v;
char c, *bp;
if (len == -1) len = strlen(str) + 1;
for (v = 0, i = 0; i < len; i++) v += str[i];
srand(v);
for (bp = buf, i = 0; i < MHASHLEN / 2; i++)
bp += sprintf(bp, "%02x", str[i % len] ^ (rand() % 256));
return buf;
}
```
Although the format specifier suggests otherwise, negative values
passed to printf will result in more than 2 characters. This can
be used to overflow the static buffer.
The buffer is zero-initialized and as such is stored in the `.bss`
section. If we inspect this section of the compiled binary we can
see that it is adjacent to where the `loggedin` variable is stored:
```
0x000083c0 0000 0000 0000 0000 0000 0000 0000 0000 ................ ; obj.buf.1 (our static buffer)
0x000083d0 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x000083e0 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x000083f0 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x00008400 0000 0000 0000 0000 0000 0000 0000 0000 ................ ; obj.loggedin ; obj.echo ; obj.resultdir
```
The arrangement of static variables in `.bss` (atleast using gcc)
from different compilation units depends on their linking order..
observe the command used to create the binary and the order of
static variables from the respective files.
```
build/stldoctor: build/stlfile.o build/util.o main.c | build
$(CC) -o $@ $^ $(CFLAGS) $(LDLIBS)
```
The fact that many compiler flags are set to protect against
buffer overflows is misleading to the players in this case,
because most of them only detect overflows on the *stack*.
Since overwriting the global `loggedin` variable gives you
permission to use the `list` command and the `resultdir` has
not changes (as is usually the case using `auth`), the attacker
can now list the hashes of all registered users.
The next step is to reverse the mhash function using the respective
hashes to log in as them and query information about the files.
TODO..
Exploiting
----------
TODO..
Patching
--------
For an example fix, see the unified format patch `services/src/patches/flagstore2.diff`.
|