Potential use-after-free in cvtsudoers · Issue #198 · sudo-project/sudo (original) (raw)
I was recently messing around with the cvtsudoer binary and its parsing of the ldif format to sudoers format when I came across the following potential heap use-after-free crash.
=================================================================
==126586==ERROR: AddressSanitizer: heap-use-after-free on address 0x6020000008f0 at pc 0x55cde0ac70ee bp 0x7ffd52a63a90 sp 0x7ffd52a63a88
READ of size 8 at 0x6020000008f0 thread T0
#0 0x55cde0ac70ed in alias_used_by_userspecs /home/sohom/sudo/plugins/sudoers/./cvtsudoers.c:1186:7
#1 0x55cde0ac70ed in filter_defaults /home/sohom/sudo/plugins/sudoers/./cvtsudoers.c:1348:5
#2 0x55cde0abf975 in main /home/sohom/sudo/plugins/sudoers/./cvtsudoers.c:418:2
#3 0x7f92d567028f (/usr/lib/libc.so.6+0x2328f) (BuildId: 1e94beb079e278ac4f2c8bce1f53091548ea1584)
#4 0x7f92d5670349 in __libc_start_main (/usr/lib/libc.so.6+0x23349) (BuildId: 1e94beb079e278ac4f2c8bce1f53091548ea1584)
#5 0x55cde09c1724 in _start /build/glibc/src/glibc/csu/../sysdeps/x86_64/start.S:115
0x6020000008f0 is located 0 bytes inside of 16-byte region [0x6020000008f0,0x602000000900)
freed by thread T0 here:
#0 0x55cde0a755e2 in __interceptor_free.part.0 asan_malloc_linux.cpp.o
#1 0x55cde0b4e968 in free_cmndspec /home/sohom/sudo/plugins/sudoers/gram.y:1581:6
#2 0x55cde0abe8de in cmndspeclist_matches_filter /home/sohom/sudo/plugins/sudoers/./cvtsudoers.c:998:6
#3 0x55cde0abe8de in filter_userspecs /home/sohom/sudo/plugins/sudoers/./cvtsudoers.c:1095:4
#4 0x55cde0abe8de in main /home/sohom/sudo/plugins/sudoers/./cvtsudoers.c:417:2
#5 0x7f92d567028f (/usr/lib/libc.so.6+0x2328f) (BuildId: 1e94beb079e278ac4f2c8bce1f53091548ea1584)
previously allocated by thread T0 here:
#0 0x55cde0a76951 in __interceptor_calloc (/home/sohom/sudo-san-install/bin/cvtsudoers+0x18a951) (BuildId: 674c78bedc7fa82a30e96bcd9ed458042ecd3a8c)
#1 0x55cde0b1bdbe in array_to_member_list /home/sohom/sudo/plugins/sudoers/./ldap_util.c:130:20
#2 0x55cde0b157df in sudo_ldap_role_to_priv /home/sohom/sudo/plugins/sudoers/./ldap_util.c:457:7
#3 0x55cde0b0f593 in role_to_sudoers /home/sohom/sudo/plugins/sudoers/./parse_ldif.c:420:12
#4 0x55cde0b0b398 in ldif_to_sudoers /home/sohom/sudo/plugins/sudoers/./parse_ldif.c:529:2
#5 0x55cde0b0b398 in sudoers_parse_ldif /home/sohom/sudo/plugins/sudoers/./parse_ldif.c:776:2
#6 0x55cde0abe4bb in parse_ldif /home/sohom/sudo/plugins/sudoers/./cvtsudoers.c:733:8
#7 0x55cde0abe4bb in main /home/sohom/sudo/plugins/sudoers/./cvtsudoers.c:404:11
#8 0x7f92d567028f (/usr/lib/libc.so.6+0x2328f) (BuildId: 1e94beb079e278ac4f2c8bce1f53091548ea1584)
SUMMARY: AddressSanitizer: heap-use-after-free /home/sohom/sudo/plugins/sudoers/./cvtsudoers.c:1186:7 in alias_used_by_userspecs
Shadow bytes around the buggy address:
0x0c047fff80c0: fa fa fd fd fa fa fd fa fa fa fd fd fa fa fd fa
0x0c047fff80d0: fa fa fd fd fa fa fd fa fa fa fd fd fa fa fd fa
0x0c047fff80e0: fa fa fd fd fa fa fd fa fa fa fd fd fa fa fd fa
0x0c047fff80f0: fa fa fd fd fa fa fd fa fa fa fd fd fa fa fd fa
0x0c047fff8100: fa fa fd fd fa fa fd fa fa fa fd fd fa fa fd fa
=>0x0c047fff8110: fa fa 06 fa fa fa 00 fa fa fa 04 fa fa fa[fd]fd
0x0c047fff8120: fa fa fd fa fa fa 00 fa fa fa fd fa fa fa fd fa
0x0c047fff8130: fa fa fd fd fa fa fd fa fa fa fd fa fa fa fd fa
0x0c047fff8140: fa fa fd fd fa fa fd fa fa fa 00 fa fa fa fa fa
0x0c047fff8150: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff8160: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==126586==ABORTING
It was caused by invoking the cvtsudoers with the following command line arguments:
cvtsudoers -i ldif -f sudoers -m cmd='/bin/ls' -p -o /tmp/abc /tmp/sudoers.ldif
where /tmp/sudoers.ldif contains the following data:
objectClass:sudoRole
sudoUser:user0
sudoHost:A00
sudoCommand:/bin/ls
sudoRunAs:0
objectClass:sudoRole
sudoUser:user0
sudoHost:A00
sudoRunAsUser:
sudoCommand:
objectClass:sudoRole
sudoUser:user0
sudoHost:A00
sudoRunAs:
sudoCommand:
Based on my understanding, this is caused because inside role_to_sudoers()
(in /home/sohom/sudo/plugins/sudoers/parse_ldif.c
) we specifically use the following code:
if (reuse_privilege) { |
---|
/* Hostspec unchanged, append cmndlist to previous privilege. */ |
struct privilege *prev_priv = TAILQ_LAST(&us->privileges, privilege_list); |
if (reuse_runas) { |
/* Runas users and groups same if as in previous privilege. */ |
struct member_list *runasuserlist = |
TAILQ_FIRST(&prev_priv->cmndlist)->runasuserlist; |
struct member_list *runasgrouplist = |
TAILQ_FIRST(&prev_priv->cmndlist)->runasgrouplist; |
struct cmndspec *cmndspec = TAILQ_FIRST(&priv->cmndlist); |
/* Free duplicate runas lists. */ |
if (cmndspec->runasuserlist != NULL) { |
free_members(cmndspec->runasuserlist); |
free(cmndspec->runasuserlist); |
} |
if (cmndspec->runasgrouplist != NULL) { |
free_members(cmndspec->runasgrouplist); |
free(cmndspec->runasgrouplist); |
} |
/* Update cmndspec with previous runas lists. */ |
TAILQ_FOREACH(cmndspec, &priv->cmndlist, entries) { |
cmndspec->runasuserlist = runasuserlist; |
cmndspec->runasgrouplist = runasgrouplist; |
} |
} |
which reuses the cs->runasuserlist
pointer from the first command across other privileges if the reuse_runas
variable is set. However, while pruning matches in
bool matched = cmnd_matches_filter(parse_tree, cs->cmnd, conf); |
---|
if (matched) { |
ret = true; |
} else if (conf->prune_matches) { |
/* free_cmndspec() removes cs from the list itself. */ |
free_cmndspec(cs, cmndspecs); |
} |
} |
we use free_cmndspec()
(in plugins/sudoers/gram.c
) where we only check if the previous and next entry in the same cmndlist have the same pointer before freeing the memory associated with a command. Since we do not consider the fact that other privileges might also contain the same pointer, there are dangling pointers left on the heap pointing to freed memory which led to the crash.