Open vngkv123 opened 6 years ago
Well, I like the idea but I think it'd be nice to have more flexibility. For example, it could color functions from specific modules, or list of known functions (like your case) or functions given by user. Also it'd be better for user to be able to choose which color will be used.
In this case, I think it'd be nice to write a python script in scripts
directory for your purpose. You use uftrace-options
to list up all the functions with preferred colors.
Please see the example in script command man page. It shows as follows:
$ cat arg.py
#
# uftrace-option: -A a@arg1 -R b@retval
#
def uftrace_entry(ctx):
if "args" in ctx:
print(ctx["name"] + " has args")
def uftrace_exit(ctx):
if "retval" in ctx:
print(ctx["name"] + " has retval")
$ uftrace record -S arg.py abc
a has args
b has retval
$ uftrace script -S arg.py
a has args
b has retval
For your example, I think the below python script can do for your purpose.
$ cat vulnerable-funcs-checker.py
# uftrace-option: -T exec|system|gets|popen|getenv|strcpy|strncpy|strcat|strncat|memcpy|bcopy|printf|sprintf|snprintf|scanf|getchar|getc|read|recv|tmp|temp@color=red
UFTRACE_FUNC = [ "exec", "system", "gets", "popen", "getenv", "strcpy", "strncpy", "strcat", "strncat", "memcpy", "bcopy", "printf", "sprintf", "snprintf", "scanf", "getchar", "getc", "read", "recv", "tmp", "temp" ]
def uftrace_entry(ctx):
# this is called only when the listed functions are entered
pass
def uftrace_exit(ctx):
# this is called only when the listed functions are exited
pass
You can simply use adding -S vulnerable-funcs-checker.py
. the below shows an example:
$ uftrace -S vulnerable-funcs-checker.py --nest-libcall /bin/ps
@honggyukim I just read the man uftrace-script
, and it seemed a little faults with python indents.
Run with the arg.py
in man page occurs an error:
$ uftrace record -S scripts/arg.py tests/abc
File "/home/flavono123/git/PR/uftrace/scripts/arg.py", line 6
print(ctx["name"] + " has args")
^
IndentationError: expected an indented block
WARN: scripts/arg.py cannot be imported!
You hope to be followed right?:
$ cat scripts/arg.py
#
# uftrace-option: -A a@arg1 -R b@retval
#
def uftrace_entry(ctx):
if "args" in ctx:
print(ctx["name"] + " has args")
def uftrace_exit(ctx):
if "retval" in ctx:
print(ctx["name"] + " has retval")
$ uftrace record -S scripts/arg.py tests/abc
a has args
b has retval
$ uftrace script -S scripts/arg.py
a has args
b has retval
Tested on my system, and use abc
compiled from tests/s-abc.c
with -pg -g
as you see.
@vngkv123 as you know, uftrace can parse argument automatically or handcrafted. you can use this for catch the vulnerable situation.
for example, when you want notice about possibility of format string attack then first compare arguments of printf function that check the format string parameter match the number of arguments, and then let user know about it is vulnerable if not match between format string parameter with number of arguments.
Hi @flavono123,
I just read the man uftrace-script, and it seemed a little faults with python indents. Run with the arg.py in man page occurs an error:
You're right. The indentation has to be fixed in the man page. Please send a PR to fix it!
@honggyukim 🙌 I'm happy to PR that, but how can I compile(?) the md
to .1 file?
the doc/uftrace-script.md
looks good:
def uftrace_entry(ctx):
if "args" in ctx:
print(ctx["name"] + " has args")
def uftrace_exit(ctx):
if "retval" in ctx:
print(ctx["name"] + " has retval")
But in the doc/uftrace-script.1
, it seems NOT adjusted, hard-coding is illegal(read-only file):
def\ uftrace_entry(ctx):
\ \ \ \ if\ "args"\ in\ ctx:
\ \ \ \ print(ctx["name"]\ +\ "\ has\ args")
def\ uftrace_exit(ctx):
\ \ \ \ if\ "retval"\ in\ ctx:
\ \ \ \ print(ctx["name"]\ +\ "\ has\ retval")
How can I fix a .1 file? Any references ?
@flavono123 You cannot change the man page, we only do it for .md files. If it's ok in .md but failed in .1, that's not an issue.
@ParkHanbum Checking number of argument seems not possible as it changed whenever called (in different callsites). But we can check the format string itself to have vulnerable specifier ("%n") or the format string is constant or not (by checking its address) from a security perspective.
@flavono123 As @namhyung said, you have to modify doc/uftrace-script.md
file. doc/uftrace-script.1
is generated with make install
so no need to modify it.
And I found that print()
statement begins with a tab instead of white spaces. You can change the tab to 8 white spaces.
@namhyung @honggyukim Oh, I got it. The problem was about tab and white-space! I'll PR for this 😃
@vngkv123 It seems your list of vulnerable functions are too generic. For example, memcpy
and read
are used frequently in many program and blindly saying that they are vulnerable doesn't make sense IMO. You'd be better writing a script to check the buffer and size for that. Thoughts?
So, i've post some question about how to get register state to make script which do as sanitizer. If i can get memory state and register state whenever i want, it is possible to detect some uninitialized stack or some kind of UAF bug and type-confusion, etc)... As you comment, i know that vulnerable function lists are so generic. But, main intention is just to watch those function lists carefully anyway.
I suggest you start with a single specific case first - as I said the format string check or buffer overflow check can be done within the current limitation of uftrace.
For example, you can write a python script whether the src
buffer in memcpy
overlaps with dest
buffer.
void *memcpy(void *dest, const void *src, size_t n);
You can check if dest
+ n
goes beyond src
address. I think this can be one of the single specific cases.
I've try to make some usable script... but if can't get register state and VA_ARG, it's hard to realize it...
#
# vuln.py
#
# uftrace-option: --nest-libcall -T memcpy@filter,arg3 -T printf@filter,arg1/s -T __isoc99_scanf@filter,arg1/s
#
# void *memcpy(void *dest, const void *src, size_t n);
#
# Only "memcpy" calls this script and other functions never.
'''
script_context = {
int tid;
int depth;
long timestamp;
long duration; # exit only
long address;
string name;
list args; # entry only (if available)
value retval; # exit only (if available)
};
'''
import re
UFTRACE_FUNC = [ "memcpy", "printf", "strcpy", "read", "gets", "fgets", "__isoc99_scanf" ]
# FUNC_DICT -> check arg number
FUNC_DICT = { "memcpy" : 1, "printf" : 1, "strcpy" : 1, "__isoc_scanf" : 1, "read" : 2, "gets" : 1, "fgets" : 1 }
fmt_cnt = 0
def red_print(msg):
print "\x1b[1;31m" + msg + "\x1b[1;m"
def uftrace_begin():
pass
def uftrace_entry(ctx):
argv = ctx["args"]
func = ctx["name"]
for tfunc in UFTRACE_FUNC:
if func == tfunc:
check_sanity(func, argv)
def uftrace_exit(ctx):
pass
def uftrace_end():
global fmt_cnt
print "[+} Format string number : ", fmt_cnt
pass
################################################################
def check_sanity(symname, argv):
global fmt_cnt
if "__isoc99_scanf" == symname:
if '%s' in argv[0]:
# very simple case
red_print("[+] Buffer Overflow Detect in " + symname)
if "printf" == symname:
fmt = re.findall(r"%[diouxXeEfFgGaAcsCSPnmztjLlh]", argv[0])
for i in fmt:
fmt_cnt += 1
###################################################################
test code
#include <stdio.h>
#include <unistd.h>
#include <string.h>
char data[256];
int main(int argc, char *argv[]){
printf("Hello, World\n");
char buf[256] = {0, };
scanf("%s", buf);
memcpy(data, buf, 256);
printf("String : %s\n", data);
return 0;
}
result
root@ubuntu:/home/asiagaming/uftrace# ./uftrace -S ./scripts/vuln.py ./test
Hello, World
[+] Buffer Overflow Detect in __isoc99_scanf
String : abcd
[+} Format string number : 1
# DURATION TID FUNCTION
487.005 ms [65194] | __isoc99_scanf("%s");
450.928 us [65194] | printf("String : %s\n");
base pointer
Are no way to solve those problems ? T_T
What is the base pointer? Anyway it currenlt doesn't have a way to pass such info to a script.
For VA_ARGS, you can instead request enough argument even if they are invalid - for example, you can specify 10 arguments for printf() and only use some of them according to the format string.
I used to gdb debugger with "peda" plugin, which is very useful to reverse engineering and debugging.
That plugin show disassemble result in gdb with colorful strings. Below code block is regarded as vulnerable function in GDB-peda plugin.
So, i add some code in
cmd-replay.c
because command-live function callcommand-replay
. Below code block is my added code incmd-replay.c
and add that function to this line. ( 754 ~ 755 )
As you know this is just DEMO version, not perfect code. My standards for filtering vulnerable function are here.
I want to develop this in uftrace for finding vulnerable functoin.