tritondse
tritondse copied to clipboard
String functions don't work with CleLoader
As a playground, I have created the following simple C application:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char buffer[100];
int counter = 0;
int *nullptr = NULL;
FILE *file = fopen("/tmp/input.txt", "r");
if (file == NULL) {
printf("Failed to open the file.\n");
return 1;
}
while (fgets(buffer, sizeof(buffer), file) != NULL) {
buffer[strcspn(buffer, "\n")] = '\0';
printf("Buffer : {%s}\n", buffer);
if (strstr(buffer, "ABCD") != NULL) {
printf("Found ABCD in buffer, counter ++,\n");
counter++;
} else if (strstr(buffer, "EFGH") != NULL) {
printf("Found EFGH in buffer, counter +10\n");
counter += 10;
} else if (strstr(buffer, "KILL") == buffer) {
printf("Found KILL in buffer, counter +50\n");
counter += 50;
} else if (strlen(buffer) >= 3 && strcmp(buffer + strlen(buffer) - 3, "ESB") == 0 && counter < 66) {
printf("Counter +80\n");
counter += 80;
}
if (strstr(buffer, "EXIT") == buffer) {
printf("Found EXIT in buffer\n");
exit(1);
}
printf("Check counter\n");
if (counter == 69) {
if (strstr(buffer, "GOAWAY") == buffer) {
printf("AWESOME\n");
*(nullptr) = 0;
} else {
printf("WIN\n");
exit(0);
}
} else if (counter > 69) {
printf("GREAT\n");
*(nullptr) = 0;
}
printf("++++++++++++++++++++++++++\n");
}
fclose(file);
return 0;
}
Here is my tritondse script file.
from tritondse import *
from tritondse.probes.basic_trace import BasicDebugTrace
import logging
# logging.basicConfig(level=logging.DEBUG)
def post_exec_hook(se: SymbolicExecutor, pstate: ProcessState):
print("-------------------------------------------------------------------------")
print(f"seed:{se.seed.hash} ({repr(se.seed.content)}) [exitcode:{se.exitcode}]")
def build_config():
cfg = Config()
# cfg.debug = True
cfg.pipe_stderr = True
cfg.pipe_stdout = True
cfg.execution_timeout = 10
cfg.exploration_timeout = 10
cfg.skip_unsupported_import = True
cfg.seed_format = SeedFormat.COMPOSITE
return cfg
cfg = build_config()
seed = Seed(CompositeData(files={"/tmp/input.txt":b"KILL Process\nKiLL Process\nEXIT bye"}))
target = CleLoader("./strstr")
dse = SymbolicExplorator(cfg, target)
dse.add_input_seed(seed)
dse.cbm.register_post_execution_callback(post_exec_hook)
dse.callback_manager.register_probe(BasicDebugTrace())
dse.explore()
I expected the script file to find other paths, but I got an error. When I run the C app, it works fine.
$ ./strstr
Buffer : {KILL Process}
Found KILL in buffer, counter +50
Check counter
++++++++++++++++++++++++++
Buffer : {KiLL Process}
Check counter
++++++++++++++++++++++++++
Buffer : {EXIT bye}
Found EXIT in buffer
$ python strstr.py
CRITICAL:root:Invalid memory access when writting at [@0xf05c4610]:8 bv[7..0] from 0x401310: mov byte ptr [rbp + rax - 0x70], 0
WARNING:root:Memory violation: (Perm.W: 0xf05c4610 unmapped)
-------------------------------------------------------------------------
seed:7413b4a86b31a328bc480420bb20f6d2 (CompositeData(argv=[], files={'/tmp/input.txt': b'KILL Process\nKiLL Process\nEXIT bye'}, variables={})) [exitcode:160]
When I remove strcspn from the C code and replace it with a function that works like strcspn, the running problem disappears, but the main problem with CleLoader and strcspn (It is my guess) doesn't go away. I added strcspn support to tritondse and it works now.
def rtn_strcspn(se: SymbolicExecutor, pstate: ProcessState):
buffer = pstate.memory.read_string(pstate.get_argument_value(0))
reject = pstate.memory.read_string(pstate.get_argument_value(1))
for i, char in enumerate(buffer):
if char in reject:
return i
return 0
When I run the updated script, there is another problem. ABCD is always found, so first condition will always be true!
$ python strstr.py
Buffer : {}
Found ABCD in buffer, counter ++,
Check counter
++++++++++++++++++++++++++
Buffer : {}
Found ABCD in buffer, counter ++,
Check counter
++++++++++++++++++++++++++
Buffer : {}
Found ABCD in buffer, counter ++,
Check counter
++++++++++++++++++++++++++
-------------------------------------------------------------------------
seed:568beb1a4ce3c59b3959e5fab33ea763 (CompositeData(argv=[], files={'/tmp/input.txt': b'KILL Process\nKiLL Process\nEXIT bye\n'}, variables={})) [exitcode:0]
Here's how I added strstr support to tritondse:
def rtn_strstr(se: SymbolicExecutor, pstate: ProcessState):
haystack = pstate.memory.read_string(pstate.get_argument_value(0))
needle = pstate.memory.read_string(pstate.get_argument_value(1))
print (f"haystack :{haystack}, needle: {needle}")
for i in range(len(haystack) - len(needle) + 1):
if haystack[i:i+len(needle)] == needle:
return pstate.get_argument_value(0) +i
return 0
The updated script file works and strstr does all the string checking, but why is the buffer empty? Why does the exception happen in the thread?
Buffer : {}
haystack :KILL Process, needle: ABCD
haystack :KILL Process, needle: EFGH
haystack :KILL Process, needle: KILL
Found KILL in buffer, counter +50
haystack :KILL Process, needle: EXIT
Check counter
++++++++++++++++++++++++++
Buffer : {}
haystack :KiLL Process, needle: ABCD
haystack :KiLL Process, needle: EFGH
haystack :KiLL Process, needle: KILL
haystack :KiLL Process, needle: EXIT
Check counter
++++++++++++++++++++++++++
Buffer : {}
haystack :EXIT bye, needle: ABCD
haystack :EXIT bye, needle: EFGH
haystack :EXIT bye, needle: KILL
haystack :EXIT bye, needle: EXIT
Found EXIT in buffer
-------------------------------------------------------------------------
seed:568beb1a4ce3c59b3959e5fab33ea763 (CompositeData(argv=[], files={'/tmp/input.txt': b'KILL Process\nKiLL Process\nEXIT bye\n'}, variables={})) [exitcode:1]
Buffer : {}
haystack :KILL Process, needle: ABCD
haystack :KILL Process, needle: EFGH
haystack :KILL Process, needle: KILL
Found KILL in buffer, counter +50
haystack :KILL Process, needle: EXIT
Check counter
++++++++++++++++++++++++++
Buffer : {}
haystack :, needle: ABCD
haystack :, needle: EFGH
haystack :, needle: KILL
haystack :, needle: EXIT
Check counter
++++++++++++++++++++++++++
Buffer : {}
haystack :, needle: ABCD
haystack :, needle: EFGH
haystack :, needle: KILL
haystack :, needle: EXIT
Check counter
++++++++++++++++++++++++++
Exception in thread [exec:00000001]:
Traceback (most recent call last):
File "/usr/lib/python3.10/threading.py", line 1016, in _bootstrap_inner
self.run()
File "/usr/lib/python3.10/threading.py", line 953, in run
self._target(*self._args, **self._kwargs)
File "/home/xer0days/.local/lib/python3.10/site-packages/tritondse/symbolic_explorator.py", line 146, in _worker
execution.run(self._executor_stop_at)
File "/home/xer0days/.local/lib/python3.10/site-packages/tritondse/symbolic_executor.py", line 578, in run
self.__emulate()
File "/home/xer0days/.local/lib/python3.10/site-packages/tritondse/symbolic_executor.py", line 379, in __emulate
self._routines_handler(instruction)
File "/home/xer0days/.local/lib/python3.10/site-packages/tritondse/symbolic_executor.py", line 430, in _routines_handler
ret_val = routine(self, self.pstate)
File "/home/xer0days/.local/lib/python3.10/site-packages/tritondse/routines.py", line 1143, in rtn_printf
args = pstate.get_format_arguments(arg0, [pstate.get_argument_value(x) for x in range(1, nbArgs+1)])
File "/home/xer0days/.local/lib/python3.10/site-packages/tritondse/process_state.py", line 903, in get_format_arguments
args[p] = args[p].encode("latin-1").decode()
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte
The exception will be fixed if get_format_arguments() moves to try/except. There's also a FIXME message :)
https://github.com/quarkslab/tritondse/blob/cd23c431ed86379f1e8917209ffdc6f8d4d27bf3/tritondse/routines.py#L1144-L1151
Hi @xer0days, What a detailled issue, I appreciate !
Does it finally works on your side after all the fixups ?
Yes currently the handling of string is a bit "hacky" as it is not easy making symbolic equivalent of string operations.
As you pinpointed format string is rather "opportunistic". I will move the try except to encompass get_format_arguments or will see if I can handle it better. I'll keep you updated.
Hi @RobinDavid, thanks, glad to hear that!
You are right, the functions I used are really hacky, but my main goal was to run the script and obtain preliminary results. Finally, I was able to obtain some results for C code, but that is a simple target!
If CleLoader is the only option for loading shared libraries and it has issues like what I had, I don't think it's reliable for use in automated flows. Please let me know if there's a solution.
Thanks.
Hi! I just pushed a fix (commit a97d65af146963a7e9550758fef64e7c89c6b8a7) for the exception in rtn_printf.