Computer Security

#26 Input Test Driver 2 본문

리눅스 커널 해킹

#26 Input Test Driver 2

쿠리 Kuri 2022. 9. 4. 18:30
반응형

문제 분석

 

 

1. cat start.sh 를 이용해 적용된 보호기법을 살펴보자.

cat start.sh

KASLR, SMEP 보호기법이 걸려있다는 것을 알 수 있다.

 

kernel memory address leak, ROP가 필요하다.

 

 

 

 


2. input_test_driver_init() 을 살펴보자.

static int input_test_driver_init(void){
    int result;
    int err;

    result = misc_register(&input_test_driver_driver);
    if(result){
        printk("misc_resgister(): Misc device register failed\n");
        return result;
    }


if(!request_irq(TEST_IRQ, test_interrupt, 0, "test", NULL)){
    printk("request_riq(): can't allocate irq\n");
    return -EBUSY;
}

test_dev = input_allocate_device();
if(!test_dev){
    printk(KERN_ERR "input_allocate_device(): Not enough memory\n");
    err = -ENOMEM;
    goto err_free_irq;
}

test_dev -> name = "V4bel Touchscreen Test";
test_dev -> evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
tesk_dev -> keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
input_set_abs_params(test_dev, ABS_X, X_AXIS_MIN, X_AXIS_MAX, 0, 0);
input_set_abs_params(test_dev, ABS_Y, Y_AXIS_MIN, Y_AXIS_MAX, 0, 0);

err = input_register_device(test_dev);
if(err){
    printk(KERN_ERR "input_register_device(): Failed to register device\n");
    goto err_free_dev;
}

return 0;

err_free_dev:
    input_free_device(test_dev);
err_free_irq:
    free_irq(TEST_IRQ, test_interrupt);
    return err;
    
}

문제 드라이버가 커널에 추가될 때 호출되는 함수이다.

 

misc device 등록 및 input device driver 등록을 위한 작업을 진행한다.

 

input_dev 설정을 확인해 보면 터치 스크린을 관리하는 드라이버임을 확인할 수 있다.

 

 

 

 


3. input_test_driver_write() 를 살펴보자.

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

    mutex_lock(&test_mutex);

    if(ptr) {
        kfree(ptr);     //1. UAF bug: kfree()후 전역 포인터 ptr을 0으로 초기화 하지 않았다.
    }
    if(count<256){
        len = count;
        count = 256;
    } else if(count>0x40000) {
        len = 416;
    } else{
        len = count;
    }

//2. logic bug : kmalloc() 반환 값이 NULL일 때 ptr을 갱신하지 않고 copy_from_user()로 넘어간다.
    if(result = (char *)kmalloc(count, GFP_ATOMIC))
        ptr =result;
   
//3. dangling pointer 쓰기 : 1번과 2번 버그를 이용하면 dangling poniter인 ptr에 대한 쓰기가 가능하다.
    if(copy_from_user(ptr, buf, len) != 0){
        mutex_unlock(&test_mutex);
        return -EFAULT;
    }

    mutex_unlock(&test_mutex)

    return 0;
}

문제 드라이버에 대한 write()를 호출하면 호출되는 함수이다.

 

전역 포인터 ptr에 주소가 있을 경우 ptr을 해제한다.

 

유저가 전달한 count에 관한 조건문을 통과한 뒤, count 크기의 슬랩 객체를 할당한다.

 

할당한 슬랩 객체에 유저가 전달한 buf를 복사한다.

 

 


위 코드에서 터지는 취약점은 다음과 같다.

 

1. UAF bug: kfree()후 전역 포인터 ptr을 0으로 초기화 하지 않았다.

 

2. logic bug : kmalloc() 반환 값이 NULL일 때 ptr을 갱신하지 않고 copy_from_user()로 넘어간다.

 

3. 정상적인 상황에서는 존재하지 않지만, 1번 버그에서 dangling pointer을 생성하고, 2번 버그에서 dangling pointer를 없애지않고 남겨두게 되면, copy_from_user 함수에서는 dangling pointer에 원하는 값을 쓸 수 있는 버그가 터진다.

 

 

 

 


4. input_test_driver_ioctl() 을 살펴보자.

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

static long input_test_driver_ioctl(struct file *flip, 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;   //printk() addr  취약점
               _fp->gift("_fp class allocate");
           }

           _fp->fp_report_ps(ptr, strlen(ptr)); //memory leak 취약점

           mutex_unlock(&test_mutex);

           break;
        default:
           break;
    }
    return 0;

}

문제 드라이버에 대한 ioctl()을 호출하면 호출되는 함수이다.

 

두가지 case가 있는데, 그중 0x1337 case를 살펴보면,

 

 

1. 만약 전역 포인터_fp가 비어있을 경우 kzalloc()을 호출해 fp_struct 구조체 할당 후, 각 함수 포인터 멤버를 초기화 한다.

 

2. _fp->fp_report_ps()를 호출해서 ptr에 복사된 데이터를 길이만큼 이벤트 값을 전달한다.

 

 

 


위 코드에서 터지는 취약점은 다음과 같다.

 

1. printk() addr : printk()주소를 leak하면 이를 기반으로 offset을 구해 KASLR 우회가 가능하다.

 

2. memory leak : 데이터의 길이를 전달하는 파라미터로 strlen()을 사용하기 때문에 ptr 다음 위치의 데이터들을 NULL을 만나기 전까지 전부 보고하게 된다.

 

 

 

 


5. input_test_driver_release()를 살펴보자.

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) {  // fp_struct 해제 : memory leak&rip control에 사용가능
        kfree(_fp);
        _fp = 0;
    }

    mutex_unlock(&test_mutex);

    return 0;
}

문제 드라이버에 대한 close()를 호출하면 호출되는 함수이다.

 

만약 전역 포인터 ptr에 주소가 있을 경우 해당 슬랩 객체를 할당 해제한다.

 

만약 전역 포인터 _fp에 주소가 있을 경우 해당 fp_struct를 할당 해제한다.

 

반응형

'리눅스 커널 해킹' 카테고리의 다른 글

#26 Input Test Driver 3  (0) 2022.09.05
#26 Input Test Driver 1  (0) 2022.09.03
#25 Double Fetch  (0) 2022.09.02
#24 Arbitrary Write 2  (0) 2022.09.01
#23 Arbitrary Write 1  (0) 2022.08.31
Comments