1212
1313import collections
1414import pathlib
15- from typing import TYPE_CHECKING , Dict , List , Tuple
15+ from typing import TYPE_CHECKING
1616
1717import gdb
1818
@@ -27,9 +27,82 @@ class GotAuditCommand(GotCommand, GenericCommand):
2727 _cmdline_ = "got-audit"
2828 _syntax_ = f"{ _cmdline_ } [FUNCTION_NAME ...] "
2929 _example_ = "got-audit read printf exit"
30- _symbols_ : Dict [str , List [str ]] = collections .defaultdict (list )
31- _paths_ : Dict [str , List [str ]] = collections .defaultdict (list )
32- _expected_dups_ = ['__cxa_finalize' ]
30+ _symbols_ : dict [str , list [str ]] = collections .defaultdict (list )
31+ _paths_ : dict [str , list [str ]] = collections .defaultdict (list )
32+
33+ _expected_dups_ = [
34+ "__cxa_finalize" ,
35+
36+ # Symbols that appear in both GNU's libc.so and libm.so
37+ "copysign" , "copysignf" , "copysignl" , "__finite" , "finite" ,
38+ "__finitef" , "finitef" , "__finitel" , "finitel" , "frexp" ,
39+ "frexpf" , "frexpl" , "ldexp" , "ldexpf" , "ldexpl" , "modf" ,
40+ "modff" , "modfl" , "scalbn" , "scalbnf" , "scalbnl" , "__signbit" ,
41+ "__signbitf" , "__signbitl" ,
42+
43+ # Symbols that appear in both GNU's libc.so and libattr.so
44+ "fgetxattr" , "flistxattr" , "fremovexattr" , "fsetxattr" ,
45+ "getxattr" , "lgetxattr" , "listxattr" , "llistxattr" ,
46+ "lremovexattr" , "lsetxattr" , "removexattr" , "setxattr" ,
47+
48+ # Symbols that appear in both GNU's libc.so and libtirpc.so
49+ "authdes_create" , "authdes_pk_create" , "_authenticate" ,
50+ "authnone_create" , "authunix_create" ,
51+ "authunix_create_default" , "bindresvport" , "callrpc" ,
52+ "clnt_broadcast" , "clnt_create" , "clnt_pcreateerror" ,
53+ "clnt_perrno" , "clnt_perror" , "clntraw_create" ,
54+ "clnt_spcreateerror" , "clnt_sperrno" , "clnt_sperror" ,
55+ "clnttcp_create" , "clntudp_bufcreate" , "clntudp_create" ,
56+ "clntunix_create" , "get_myaddress" , "getnetname" ,
57+ "getpublickey" , "getrpcport" , "host2netname" ,
58+ "key_decryptsession" , "key_decryptsession_pk" ,
59+ "key_encryptsession" , "key_encryptsession_pk" , "key_gendes" ,
60+ "key_get_conv" , "key_secretkey_is_set" , "key_setnet" ,
61+ "key_setsecret" , "__libc_clntudp_bufcreate" , "netname2host" ,
62+ "netname2user" , "pmap_getmaps" , "pmap_getport" ,
63+ "pmap_rmtcall" , "pmap_set" , "pmap_unset" , "registerrpc" ,
64+ "_rpc_dtablesize" , "rtime" , "_seterr_reply" , "svcerr_auth" ,
65+ "svcerr_decode" , "svcerr_noproc" , "svcerr_noprog" ,
66+ "svcerr_progvers" , "svcerr_systemerr" , "svcerr_weakauth" ,
67+ "svc_exit" , "svcfd_create" , "svc_getreq" , "svc_getreq_common" ,
68+ "svc_getreq_poll" , "svc_getreqset" , "svcraw_create" ,
69+ "svc_register" , "svc_run" , "svc_sendreply" , "svctcp_create" ,
70+ "svcudp_bufcreate" , "svcudp_create" , "svcunix_create" ,
71+ "svcunixfd_create" , "svc_unregister" , "user2netname" ,
72+ "xdr_accepted_reply" , "xdr_array" , "xdr_authunix_parms" ,
73+ "xdr_bool" , "xdr_bytes" , "xdr_callhdr" , "xdr_callmsg" ,
74+ "xdr_char" , "xdr_cryptkeyarg" , "xdr_cryptkeyarg2" ,
75+ "xdr_cryptkeyres" , "xdr_des_block" , "xdr_double" , "xdr_enum" ,
76+ "xdr_float" , "xdr_free" , "xdr_getcredres" , "xdr_hyper" ,
77+ "xdr_int" , "xdr_int16_t" , "xdr_int32_t" , "xdr_int64_t" ,
78+ "xdr_int8_t" , "xdr_keybuf" , "xdr_key_netstarg" ,
79+ "xdr_key_netstres" , "xdr_keystatus" , "xdr_long" ,
80+ "xdr_longlong_t" , "xdrmem_create" , "xdr_netnamestr" ,
81+ "xdr_netobj" , "xdr_opaque" , "xdr_opaque_auth" , "xdr_pmap" ,
82+ "xdr_pmaplist" , "xdr_pointer" , "xdr_quad_t" , "xdrrec_create" ,
83+ "xdrrec_endofrecord" , "xdrrec_eof" , "xdrrec_skiprecord" ,
84+ "xdr_reference" , "xdr_rejected_reply" , "xdr_replymsg" ,
85+ "xdr_rmtcall_args" , "xdr_rmtcallres" , "xdr_short" ,
86+ "xdr_sizeof" , "xdrstdio_create" , "xdr_string" , "xdr_u_char" ,
87+ "xdr_u_hyper" , "xdr_u_int" , "xdr_uint16_t" , "xdr_uint32_t" ,
88+ "xdr_uint64_t" , "xdr_uint8_t" , "xdr_u_long" ,
89+ "xdr_u_longlong_t" , "xdr_union" , "xdr_unixcred" ,
90+ "xdr_u_quad_t" , "xdr_u_short" , "xdr_vector" , "xdr_void" ,
91+ "xdr_wrapstring" , "xprt_register" , "xprt_unregister" ,
92+
93+ # Symbols that appear in libsasl2 and in its related libs
94+ "_plug_buf_alloc" , "_plug_challenge_prompt" , "_plug_decode" ,
95+ "_plug_decode_free" , "_plug_decode_init" , "_plug_find_prompt" ,
96+ "_plug_free_secret" , "_plug_free_string" ,
97+ "_plug_get_error_message" , "_plug_get_password" ,
98+ "_plug_get_realm" , "_plug_get_simple" , "_plug_iovec_to_buf" ,
99+ "_plug_ipfromstring" , "_plug_make_fulluser" ,
100+ "_plug_make_prompts" , "_plug_parseuser" ,
101+ "_plug_snprintf_os_info" , "_plug_strdup" ,
102+
103+ # Symbols that appear in libresolv and libvncserver
104+ "__b64_ntop" , "__b64_pton" ,
105+ ]
33106
34107 def get_symbols_from_path (self , elf_file ):
35108 nm = gef .session .constants ["nm" ]
@@ -39,15 +112,15 @@ def get_symbols_from_path(self, elf_file):
39112 words = line .split ()
40113 # Record the symbol if it is in the text section or
41114 # an indirect function or weak symbol
42- if len (words ) == 3 and words [- 2 ] in ('T' , 'i' , 'I' , 'v' , 'V' , 'w' , 'W' ):
43- sym = words [- 1 ].split ('@' )[0 ]
115+ if len (words ) == 3 and words [- 2 ] in ("T" , "i" , "I" , "v" , "V" , "w" , "W" ):
116+ sym = words [- 1 ].split ("@" )[0 ]
44117 if elf_file not in self ._symbols_ [sym ]:
45118 self ._symbols_ [sym ].append (elf_file )
46119 self ._paths_ [elf_file ].append (sym )
47120
48121 @only_if_gdb_running
49- def do_invoke (self , argv : List [str ]) -> None :
50- # Build a list of the symbols provided by each path, and
122+ def do_invoke (self , argv : list [str ]) -> None :
123+ # Build a list of the symbols provided by each library path, and
51124 # a list of paths that provide each symbol.
52125 for section in gef .memory .maps :
53126 if (section .path not in self ._paths_
@@ -56,19 +129,27 @@ def do_invoke(self, argv: List[str]) -> None:
56129 self .get_symbols_from_path (section .path )
57130 return super ().do_invoke (argv )
58131
59- def build_line (self , name : str , color : str , address_val : int , got_address : int ) -> str :
132+ def build_line (self , name : str , path : str , color : str , address_val : int , got_address : int ) -> str :
60133 line = Color .colorify (f"{ name } " , color )
61134 found = 0
62135 for section in gef .memory .maps :
63136 if not section .contains (got_address ):
64137 continue
65138 line += f" : { section .path } "
66139 found = 1
67- short_name = name .split ('@' )[0 ]
140+ short_name = name .split ("@" )[0 ]
141+ # Symbol duplication isn't a strong signal for namespace tampering, but it should not be
142+ # allowed without review. Developers should register the symbols that multiple libraries
143+ # export. (Though the current implementation of hard-coding them in this tool should be
144+ # replaced with a more flexible approach.)
68145 if (len (self ._symbols_ [short_name ]) > 1
69146 and short_name not in self ._expected_dups_ ):
70147 line += f" :: ERROR { short_name } found in multiple paths ({ str (self ._symbols_ [short_name ])} )"
148+ # Symbols within a Section are allowed to resolve to an address within the same Section.
149+ # This is usually an unresolved symbol. In any case, we aren't concerned that a library
150+ # will subvert its own functionality through namespace tampering.
71151 if (section .path != "[vdso]"
152+ and section .path != path
72153 and short_name not in self ._paths_ [section .path ]):
73154 line += f" :: ERROR { short_name } not exported by { section .path } "
74155 break
0 commit comments