enowars5-service-stldoctor

STL-Analyzing A/D Service for ENOWARS5 in 2021
git clone https://git.sinitax.com/sinitax/enowars5-service-stldoctor
Log | Files | Refs | README | LICENSE | sfeed.txt

README.md (8264B)


      1STLDoctor
      2=========
      3
      4STLDoctor is a plain-text protocol service, that allows users to upload STL files
      5and generate reports that include information on the file's..
      6
      7- model name
      8- binary header
      9- file type (bin vs ascii)
     10- file size
     11- triangle count
     12- bounding box size and position
     13- validity
     14
     15Uploaded models and generated reports are stored in a directory structure.
     16
     17Unregistered user's files are saved in a collective directory, which
     18allows users to search for public models via model name. Registered user's
     19uploads are saved to a private directory. This (theoretically) prevents other
     20users from accessing their files.
     21
     22The service is hosted with ncat, one process per client.
     23
     24Models are periodically checked for removal via their *last modified* date
     25and tracked using index files.
     26
     27For both flagstores the **service returns the flag in plaintext**, which is
     28vulnerable to detection by network filters. However, multiple sessions can be
     29used to somewhat obfuscate the exploit mechanism.
     30
     31
     32RCE Countermeasures
     33===================
     34
     35It is good practice to take preventitive measures against unintentional RCE,
     36which can be used to wreak havoc on vulnboxes and make services go mumble.
     37
     38For this reason, additional security features are enabled via compilation flags:
     39
     40`CFLAGS = -fPIE -fstack-protector-strong -D_FORTIFY_SOURCE=2 -O2`
     41
     42- `-fPIE`: enable position independent executable section
     43- `-fstack-protector-strong`: enable stack canaries in functions with local variables that are prone to overflow
     44- `-D_FORTIFY_SOURCE=2`: gcc buffer overflow detection
     45- `-O2`: enable level 2 of compiler optimizations (required for `_FORITFY_SOURCE`)
     46
     47`LDFLAGS = -Wl,-z,now -Wl,-z,relro`
     48
     49- `-Wl,-z,now`: tell dynamic linker to resolve symbols ASAP instead of lazy loading
     50- `-Wl,-z,relro`: tell dynamic linker to make `got` section read-only after resolving symbols
     51
     52
     53Checker
     54=======
     55
     56The checker is **not easily identifiable by the examination of network traffic**,
     57because it uses the regular user interface and chooses random input in a way
     58that appears human. This prevents players from only enabling certain service
     59functions when the checker is detected.
     60
     61The checker **uses unusual, incorrect or pseudomalicious input to detect network filters**
     62by replicating the same type of data sent in an exploit (negative chars) in other
     63context, which avoids filtering.
     64
     65The checker **tests processing of many different valid and invalid inputs**
     66to verify that the service can properly distinguish the two and verifies
     67input as it should.
     68
     69
     70Vuln 1: Value Collision
     71=======================
     72
     73Discovery
     74---------
     75
     76This vulnerability can be found by reading the source code.
     77
     78The utility function `void freadstr(FILE *f, char **dst)` reads a
     79null-terminated string from the given file, allocates space for it and saves the
     80pointer to its contents in `dst`.
     81
     82```C
     83void
     84freadstr(FILE *f, char **dst)
     85{
     86	size_t start, len;
     87	char c;
     88
     89	start = ftell(f);
     90	for (len = 0; (c = fgetc(f)) != EOF && c; len++);
     91	fseek(f, start, SEEK_SET);
     92
     93	*dst = checkp(calloc(1, len + 1));
     94	fread(*dst, len, 1, f);
     95	fgetc(f);
     96}
     97
     98```
     99
    100To determine whether the end-of-file was reached, the return value of `int
    101fgetc(FILE *f)` is compared to the constant `EOF`, which has a value of `-1`.
    102The problem lies in the fact that this comparison is done following a demotion
    103of the return value to `char` through the assignment, and a subsequent promotion
    104to `int`, which results in an arithmetic extension. As a result, a char with
    105unsigned value `255` is cast to char (`0xff`) and then promoted to `-1`
    106(`0xffffffff`). Since this value corresponds with `EOF`, it prevents the
    107function from reading the complete string.
    108
    109This allows an attacker to cleverly truncate a string before it has ended to
    110manipulate the content of strings which follow it. In this case, the model name
    111is saved before the model hash in the information file. By adding a `0xff` to
    112our uploaded model's name, we can control what value is loaded from the file for
    113the model's hash. Since a `search last` will use the previously loaded models hash
    114to find the file via prefix match, any files uploaded by unregistered users can
    115be accessed by choosing this value accordingly.
    116
    117The flag is saved in the model name.
    118
    119
    120Exploiting
    121----------
    122
    1231. Open a session
    1242. Run `upload` to upload an STL file and specify a model name ending in
    125	`0xff<target-hash-prefix>`
    1263. Open a new session
    1274. Run `search` with the same model name from **step 1** to load the malicious
    128	model hash value
    1295. Run `search last` to use the cached hash prefix to access the target file
    130
    131See the `exploit` method of the checker in `checker/src/checker.py`
    132for an implementation in python.
    133
    134
    135Patching
    136--------
    137
    138For an example fix, see the unified format patch `src/patches/flagstore1.diff`.
    139
    140
    141Vuln 2: Invalid Format String
    142=============================
    143
    144Discovery
    145---------
    146
    147This vulnerability can be found by reading the source code.
    148
    149The utility function `const char* mhash(const char *str, int len)` is used
    150to generate a fixed-width hash from a string input in a static buffer
    151and return a pointer to that buffer.
    152
    153```C
    154const char*
    155mhash(const char *str, int len)
    156{
    157	static char buf[MHASHLEN + 1];
    158	int i, k, v;
    159	char c, *bp;
    160
    161	if (len == -1) len = strlen(str) + 1;
    162
    163	for (v = 0, i = 0; i < len; i++) v += str[i];
    164	srand(v);
    165
    166	for (bp = buf, i = 0; i < MHASHLEN / 2; i++)
    167		bp += sprintf(bp, "%02x", str[i % len] ^ (rand() % 256));
    168
    169	return buf;
    170}
    171```
    172
    173Although the format specifier suggests otherwise, negative values
    174passed to printf will result in more than 2 characters. This can
    175be used to overflow the static buffer.
    176
    177The buffer is zero-initialized and as such is stored in the `.bss`
    178section. If we inspect this section of the compiled binary we can
    179see that it is adjacent to where the `loggedin` variable is stored:
    180
    181```
    1820x000083c0  0000 0000 0000 0000 0000 0000 0000 0000  ................  ; obj.buf.1 (our static buffer)
    1830x000083d0  0000 0000 0000 0000 0000 0000 0000 0000  ................
    1840x000083e0  0000 0000 0000 0000 0000 0000 0000 0000  ................
    1850x000083f0  0000 0000 0000 0000 0000 0000 0000 0000  ................
    1860x00008400  0000 0000 0000 0000 0000 0000 0000 0000  ................  ; obj.loggedin  ; obj.echo  ; obj.resultdir
    187```
    188
    189The arrangement of static variables in `.bss` (atleast using gcc)
    190from different compilation units depends on their linking order..
    191observe the command used to create the binary and the order of
    192static variables from the respective files.
    193
    194```
    195build/stldoctor: build/stlfile.o build/util.o main.c | build
    196	$(CC) -o $@ $^ $(CFLAGS) $(LDLIBS)
    197```
    198
    199The fact that many compiler flags are set to protect against
    200buffer overflows is misleading to the players in this case,
    201because most of them only detect overflows on the *stack*.
    202
    203Since overwriting the global `loggedin` variable gives you
    204permission to use the `list` command and the `resultdir` has
    205not changed (as is usually the case using `auth`), the attacker
    206can now list the hashes of all registered users.
    207
    208The next step is to find a valid preimage for the hashes obtained
    209previously, to log in as them and query information about their files.
    210
    211To calculate the preimage we repeatedly choose a seed for srand. For
    212each seed, we XOR the values encoded in the hex-encoded hash with
    213calls to `rand()`. If after generating each character the sum of the
    214generated values is less than the seed we used, restart. Otherwise,
    215we append some characters to make the sum of the input characters
    216match the seed, such that the seed for srand mhash uses matches the
    217one we chose. The actual value of these 'extra' chars is irrelevant,
    218since mhash only processes the first 20 chars.
    219
    220See `checker/src/revhash/main.c` for an example implementation in C.
    221
    222
    223Exploiting
    224----------
    225
    2261. Open a session
    2272. Run `search \xff\xff\xff\xff\xff000000000000000`
    228	.. this is internally passed to mhash and overflows `loggedin`
    2293. Run `list` to get list of account hashes
    2304. For each hash:
    231	- Compute hash preimage and authenticate with it
    232	- Run `list` to view info
    233
    234
    235See the `exploit` method of the checker in `checker/src/checker.py`
    236for an implementation in python.
    237
    238
    239Patching
    240--------
    241
    242For an example fix, see the unified format patch `src/patches/flagstore2.diff`.
    243
    244