首页
社区
课程
招聘
[原创]checksec原理分析与C语言重写1
发表于: 2024-9-23 18:32 5351

[原创]checksec原理分析与C语言重写1

2024-9-23 18:32
5351

checksec是一个用于检测文件、进程、内核安全特性的开源项目,开源在使用shell脚本编写,主要应用在Linux平台。
根据项目主页描述,checksec项目最早由漏洞挖掘专家Tobias Klein编写,目前该项目主要由slimm609、teoberi等人维护。

根据项目主页描述,checksecc是checksec的C语言重写版,并给出了详细的安装和使用教程。项目全部由C语言和Makefile编写,适合C语言的初学者学习。

  本章节将简要分析checksec检测文件的原理,重点分析使用C语言重写该项目的逻辑。下文以checksec.sh和checksecc来区分二者。

checksec.sh文件检测有以下五个用法。

file、dir、listfile选项的逻辑都是检测文件、文件列表的基础安全特性,最新版本支持以下11种。

  fortify-file选项是对危险函数加固情况的具体说明,为了性能考虑,编译时默认不启用加固。该选项包含以下信息。

extended扩展选项在基础特性上增加了3个特性。

   checksec.sh检测文件的核心函数在源码 src/functions/filecheck.sh 中。基本逻辑是使用readelf工具对文件的程序头表、动态节、节头表、函数名进行字符匹配,来判断该文件是否开启了某一安全特性。
  以检测RELRO为例,使用readelf检测文件的程序头表,是否有GNU_RELRO。( GNU_RELRO仅在加载文件时进行解析,而不映射在段中 )如果编译器配置了GNU_RELRO,则说明启用了RELRO保护策略,需要进一步判断是Partial还是Full。接着通过匹配动态节的输出信息,如果有BIND_NOW标志,则说明文件需要在加载时绑定,而非延迟绑定,即RELRO Full策略。

  checksecc是checksec.sh项目的C重写,将fortify特性合并到了extended选项中。并且考虑到GCC、Clang一些安全特性都是通过插桩实现的,将CFI、SafeStack等统一合并到Sanitized,同时添加了Asan、Tsan等检测。
  项目主页的例子,使用clang -fsanitize=address test.c -o test编译出test,检测test可得到如下输出。

  使用C语言重写checksec.sh的重点在于,如何编程替代readelf工具。一种方案是使用原生库libbfd,libbfd是GNU Binutils的原生库,广泛应用于GCC、GDB,包括readelf等涉及二进制文件解析的工具中。
  而checksecc通过编写一个适用于检测安全特性的最简加载器,来替代readelf/libbfd完成工作。加载器的核心实现在 srcs/loader.c 。每个文件通过loader解析到结构体Binary中,Binary中保存着解析时的映射地址、文件类型、架构、名称、大小、入口点。还有符号链表头、节链表头,最后是各种表头。

  还是以检测RELRO为例,上文提到RELRO的检测需要程序头表和动态节,所以我们重点关注loader解析程序头表、节的实现。

  loader对程序头表的解析实现在函数load_elf_programhs中,参数传递Binary指针和程序头信息。该函数根据elf是32位还是64位,分别生成链表保存程序头表的信息。

  查看调用load_elf_programhs函数的load_elf函数,ph_info数组是根据elf文件头解析出来的三个信息:程序头表文件偏移e_phoff、每个程序头的大小e_phentsize和数量e_phnum。

elf文件头有很多信息,参考图如下(图源 《Practical Binary Analysis》)。

loader解析节的逻辑类似,但是有两点注意。

  loader完成解析任务后,交给chk_file执行检测任务。checksecc检测文件的核心逻辑在 srcs/chk_file.c 。
  检测一个elf文件的逻辑在函数chk_file_one_elf中,这里使用函数指针的形式管理所有检测函数,在没有指定extended选项的情况下,chk_file_one_elf会检测以下的安全特性。

  回到检测RELRO上,chk_elf_relro函数的执行过程可以分为三步。这里逻辑和checksec.sh相同,都是先寻找GNU_RELRO,再寻找BIND_NOW,不过C语言实现需要找到具体的字段来匹配。

DT_BIND_NOW
表示在将控制权返回给程序之前,必须处理此目标文件的所有重定位项。通过环境或 dlopen(3C) 指定时,提供的此项优先于使用延迟绑定的指令。此元素的用途已被 DF_BIND_NOW 标志取代。请参见执行重定位的时间。

## Checksec Options
--file={file}
--dir={directory}
--listfile={text file with one file per line}
--fortify-file={executable-file}
--extended
## Checksec Options
--file={file}
--dir={directory}
--listfile={text file with one file per line}
--fortify-file={executable-file}
--extended
* FORTIFY_SOURCE support available (libc)     # libc是否支持加固
* Binary compiled with FORTIFY_SOURCE support # 文件是否启用加固
# 对应函数
------ EXECUTABLE-FILE ------- . -------- LIBC --------
FORTIFY-able library functions | Checked function names
-------------------------------------------------------
fdelt_chk                      | __fdelt_chk
read                           | __read_chk
syslog_chk                     | __syslog_chk
fprintf_chk                    | __fprintf_chk
vsnprintf_chk                  | __vsnprintf_chk
fgets                          | __fgets_chk
strncpy                        | __strncpy_chk
snprintf_chk                   | __snprintf_chk
memset                         | __memset_chk
strncat_chk                    | __strncat_chk
memcpy                         | __memcpy_chk
fread                          | __fread_chk
sprintf_chk                    | __sprintf_chk
# 数量的总结
SUMMARY:
* Number of checked functions in libc                
* Total number of library functions in the executable 
* Number of FORTIFY-able functions in the executable
* Number of checked functions in the executable     
* Number of unchecked functions in the executable   
* FORTIFY_SOURCE support available (libc)     # libc是否支持加固
* Binary compiled with FORTIFY_SOURCE support # 文件是否启用加固
# 对应函数
------ EXECUTABLE-FILE ------- . -------- LIBC --------
FORTIFY-able library functions | Checked function names
-------------------------------------------------------
fdelt_chk                      | __fdelt_chk
read                           | __read_chk
syslog_chk                     | __syslog_chk
fprintf_chk                    | __fprintf_chk
vsnprintf_chk                  | __vsnprintf_chk
fgets                          | __fgets_chk
strncpy                        | __strncpy_chk
snprintf_chk                   | __snprintf_chk
memset                         | __memset_chk
strncat_chk                    | __strncat_chk
memcpy                         | __memcpy_chk
fread                          | __fread_chk
sprintf_chk                    | __sprintf_chk
# 数量的总结
SUMMARY:
* Number of checked functions in libc                
* Total number of library functions in the executable 
* Number of FORTIFY-able functions in the executable
* Number of checked functions in the executable     
* Number of unchecked functions in the executable   
filecheck() {
  # check for RELRO support
  if [[ $(${readelf} -l "${1}" 2> /dev/null) =~ "no program headers" ]]; then
    echo_message '\033[32mN/A          \033[m   ' 'N/A,' '<file relro="n/a"' " \"${1}\": { \"relro\":\"n/a\","
  elif ${readelf} -l "${1}" 2> /dev/null | grep -q 'GNU_RELRO'; then
    if ${readelf} -d "${1}" 2> /dev/null | grep -q 'BIND_NOW' || ! ${readelf} -l "${1}" 2> /dev/null | grep -q '\.got\.plt'; then
      echo_message '\033[32mFull RELRO   \033[m   ' 'Full RELRO,' '<file relro="full"' " \"${1}\": { \"relro\":\"full\","
    else
      echo_message '\033[33mPartial RELRO\033[m   ' 'Partial RELRO,' '<file relro="partial"' " \"${1}\": { \"relro\":\"partial\","
    fi
  else
    echo_message '\033[31mNo RELRO     \033[m   ' 'No RELRO,' '<file relro="no"' " \"${1}\": { \"relro\":\"no\","
  fi
...
}
filecheck() {
  # check for RELRO support
  if [[ $(${readelf} -l "${1}" 2> /dev/null) =~ "no program headers" ]]; then
    echo_message '\033[32mN/A          \033[m   ' 'N/A,' '<file relro="n/a"' " \"${1}\": { \"relro\":\"n/a\","
  elif ${readelf} -l "${1}" 2> /dev/null | grep -q 'GNU_RELRO'; then
    if ${readelf} -d "${1}" 2> /dev/null | grep -q 'BIND_NOW' || ! ${readelf} -l "${1}" 2> /dev/null | grep -q '\.got\.plt'; then
      echo_message '\033[32mFull RELRO   \033[m   ' 'Full RELRO,' '<file relro="full"' " \"${1}\": { \"relro\":\"full\","
    else
      echo_message '\033[33mPartial RELRO\033[m   ' 'Partial RELRO,' '<file relro="partial"' " \"${1}\": { \"relro\":\"partial\","
    fi
  else
    echo_message '\033[31mNo RELRO     \033[m   ' 'No RELRO,' '<file relro="no"' " \"${1}\": { \"relro\":\"no\","
  fi
...
}
> checkc --file=./test --extended
File                        ./test
RELRO                       Partial RELRO
STACK CANARY                Canary found
NX                          NX enabled
PIE                         PIE enabled
RPATH                       NO RPATH
RUNPATH                     NO RUNPATH
Stripped                    Not Stripped
Sanitized asan              Yes
Sanitized tsan              NO
Sanitized msan              NO
Sanitized lsan              Yes
Sanitized ubsan             Yes
Sanitized dfsan             NO
Sanitized safestack         NO
Sanitized cet-ibt           NO
Sanitized cet-shadow-stack  NO
Fortified                   FORTIFY SOURCE support available (/lib/x86_64-linux-gnu/libc.so.6) : Yes
Fortified                   Binary compiled with FORTIFY SOURCE support (./test) : Yes
Fortified                   __sprintf_chk Fortified
Fortified                   __longjmp_chk Fortified
Fortified                   __fprintf_chk Fortified
Fortified                   __vsprintf_chk Fortified
Fortified                   __snprintf_chk Fortified
Fortified                   __vsnprintf_chk Fortified
> checkc --file=./test --extended
File                        ./test
RELRO                       Partial RELRO
STACK CANARY                Canary found
NX                          NX enabled
PIE                         PIE enabled
RPATH                       NO RPATH
RUNPATH                     NO RUNPATH
Stripped                    Not Stripped
Sanitized asan              Yes
Sanitized tsan              NO
Sanitized msan              NO
Sanitized lsan              Yes
Sanitized ubsan             Yes
Sanitized dfsan             NO
Sanitized safestack         NO
Sanitized cet-ibt           NO
Sanitized cet-shadow-stack  NO
Fortified                   FORTIFY SOURCE support available (/lib/x86_64-linux-gnu/libc.so.6) : Yes
Fortified                   Binary compiled with FORTIFY SOURCE support (./test) : Yes
Fortified                   __sprintf_chk Fortified
Fortified                   __longjmp_chk Fortified
Fortified                   __fprintf_chk Fortified
Fortified                   __vsprintf_chk Fortified
Fortified                   __snprintf_chk Fortified
Fortified                   __vsnprintf_chk Fortified
// include/loader.h
typedef struct Binary{
    /*  mmap address    */
    void *mem;
   /* file format */
    bin_type bin_type;
    /*   arch  */
    bin_arch bin_arch;
    /*   binary name */
    const char *bin_name;
    /*   binary size */
    uint64_t bin_size;
    /*   entry point */
    uint64_t entry;
    /*  binary symbols  */
    Symbol *sym;
    /*  binary sections */
    Section *sect;
    /*  binary headers */
    Header *hd;
}Binary;
// include/loader.h
typedef struct Binary{
    /*  mmap address    */
    void *mem;
   /* file format */
    bin_type bin_type;
    /*   arch  */
    bin_arch bin_arch;
    /*   binary name */
    const char *bin_name;
    /*   binary size */
    uint64_t bin_size;
    /*   entry point */
    uint64_t entry;
    /*  binary symbols  */
    Symbol *sym;
    /*  binary sections */
    Section *sect;
    /*  binary headers */
    Header *hd;
}Binary;
// srcs/loader.c
void load_elf_programhs(Binary *elf,uint64_t *ph_info){
    void *mem=elf->mem;
    /*  program headers addr    */
    uintptr_t ph_addr=(uintptr_t)mem+ph_info[0];
    /*  head    */
    Programh *ph=MALLOC(1,Programh);
    elf->hd->Pxheader.ph=ph;
    switch (elf->bin_type){
        case BIN_TYPE_ELF32:
            for(uint16_t ph_num=0;ph_num < ph_info[2];ph_num++){
                uintptr_t ph32_addr=ph_addr+ph_num*ph_info[1];
                E32_ph *ph32=(E32_ph*)ph32_addr;
                Programh *new=MALLOC(1,Programh);
                new->sgm_type=load_elf_programh_types(ph32->p_type);
                new->sgm_vma=ph32->p_vaddr;
                new->sgm_flag=ph32->p_flags;
                ph->ph_next=new;
                ph=new;
            }
            break;
         case BIN_TYPE_ELF64:
            for(uint16_t ph_num=0;ph_num < ph_info[2];ph_num++){
                uintptr_t ph64_addr=ph_addr+ph_num*ph_info[1];
                E64_ph *ph64=(E64_ph*)ph64_addr;
                Programh *new=MALLOC(1,Programh);
                new->sgm_type=load_elf_programh_types(ph64->p_type);
                new->sgm_vma=ph64->p_vaddr;
                new->sgm_flag=ph64->p_flags;
                ph->ph_next=new;
                ph=new;
            }
    }
    /*  tail    */
    ph->ph_next=NULL;
}
// srcs/loader.c
void load_elf_programhs(Binary *elf,uint64_t *ph_info){
    void *mem=elf->mem;
    /*  program headers addr    */
    uintptr_t ph_addr=(uintptr_t)mem+ph_info[0];
    /*  head    */
    Programh *ph=MALLOC(1,Programh);
    elf->hd->Pxheader.ph=ph;
    switch (elf->bin_type){
        case BIN_TYPE_ELF32:
            for(uint16_t ph_num=0;ph_num < ph_info[2];ph_num++){
                uintptr_t ph32_addr=ph_addr+ph_num*ph_info[1];
                E32_ph *ph32=(E32_ph*)ph32_addr;
                Programh *new=MALLOC(1,Programh);
                new->sgm_type=load_elf_programh_types(ph32->p_type);
                new->sgm_vma=ph32->p_vaddr;
                new->sgm_flag=ph32->p_flags;
                ph->ph_next=new;
                ph=new;
            }
            break;
         case BIN_TYPE_ELF64:
            for(uint16_t ph_num=0;ph_num < ph_info[2];ph_num++){
                uintptr_t ph64_addr=ph_addr+ph_num*ph_info[1];
                E64_ph *ph64=(E64_ph*)ph64_addr;
                Programh *new=MALLOC(1,Programh);
                new->sgm_type=load_elf_programh_types(ph64->p_type);
                new->sgm_vma=ph64->p_vaddr;
                new->sgm_flag=ph64->p_flags;
                ph->ph_next=new;
                ph=new;
            }
    }
    /*  tail    */
    ph->ph_next=NULL;
}
void load_elf(Binary *elf,void *mem){
    ...
    switch (elf->bin_type)
    {
    case BIN_TYPE_ELF32:
        ...
        /*  program header information   */
        ph_info[0]=elf32_fh->e_phoff;
        ph_info[1]=elf32_fh->e_phentsize;
        ph_info[2]=elf32_fh->e_phnum;
        break;
    case BIN_TYPE_ELF64:
        ...
        /*  program header information   */
        ph_info[0]=elf64_fh->e_phoff;
        ph_info[1]=elf64_fh->e_phentsize;
        ph_info[2]=elf64_fh->e_phnum;
        break;
    }
    /*  load headers */
    load_elf_programhs(elf,mem,ph_info);
    ...
}
void load_elf(Binary *elf,void *mem){
    ...
    switch (elf->bin_type)
    {
    case BIN_TYPE_ELF32:
        ...
        /*  program header information   */
        ph_info[0]=elf32_fh->e_phoff;
        ph_info[1]=elf32_fh->e_phentsize;
        ph_info[2]=elf32_fh->e_phnum;

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 1
支持
分享
最新回复 (2)
雪    币: 76
活跃值: (491)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
支持
2024-9-24 00:00
1
雪    币: 1134
活跃值: (2601)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
3
感谢分享
2024-9-24 21:38
1
游客
登录 | 注册 方可回帖
返回
//