64-bit checksec + README
This commit is contained in:
53
README.md
53
README.md
@@ -1,9 +1,48 @@
|
|||||||
# todo
|
# helpelf: an open and lightweight ELF recon tool
|
||||||
|
|
||||||
- display e_type and rest of header for 32/64
|
helpelf aims to be a replacement to the `file` and `checksec` utilities that are used by many hackers. The tool does not rely on readelf like `checksec` does, but instead includes its own ELF parser. Works for both 32 and 64-bit versions of the standard.
|
||||||
- display protections
|
|
||||||
- colorful
|
|
||||||
- maybe display elf sections if command entered (??)
|
|
||||||
|
|
||||||
- security:
|
## Installation
|
||||||
enforce malloc secure fail
|
|
||||||
|
This will install `helpelf` and the alias `he` on the command line. No dependencies are required besides the C toolchain & make utility.
|
||||||
|
|
||||||
|
```
|
||||||
|
make
|
||||||
|
sudo make install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
The output is colorful (not visible here).
|
||||||
|
|
||||||
|
```
|
||||||
|
$ helpelf
|
||||||
|
Usage: helpelf <file> [options]
|
||||||
|
-v --verbose displays more information
|
||||||
|
$ he a.out
|
||||||
|
a.out: 64-bit ELF (little-endian) amd64, NX enabled, PIE disabled, Stack canary enabled, Partial RELRO, Not stripped
|
||||||
|
$ he a.out -v
|
||||||
|
a.out: 64-bit ELF (little-endian) version 1 (executable file), amd64, statically linked, NX enabled, PIE disabled, Stack canary enabled, Partial RELRO, Not stripped
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
The tool was developed according to the ELF 32 and 64 bit standards that can be found here:
|
||||||
|
|
||||||
|
- [ELF-64 Object File Format](https://uclibc.org/docs/elf-64-gen.pdf)
|
||||||
|
- [Tool Interface Standard (TIS) Executable and Linking Format (ELF) Specification](https://refspecs.linuxfoundation.org/elf/elf.pdf)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is under the Unlicense (part of public domain).
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
- optimization: find a workaround for 32/64 sec differentiation
|
||||||
|
- verbose: display detailed header info
|
||||||
|
- verbose: display elf sections
|
||||||
|
|
||||||
|
## Known issues
|
||||||
|
|
||||||
|
- the RELRO check gives false-negatives on Full RELRO (instead shows Partial-RELRO)
|
||||||
|
- any contribution and/or feedback is highly appreciated. Open an issue/pull request if you'd like to add something.
|
||||||
|
|||||||
184
main.c
184
main.c
@@ -468,6 +468,189 @@ void check_sec32(Elf32_Ehdr* header, FILE* fp)
|
|||||||
free(p_headers);
|
free(p_headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Duplicate code. Should find a workaround
|
||||||
|
void check_sec64(Elf64_Ehdr* header, FILE* fp)
|
||||||
|
{
|
||||||
|
/* NX:
|
||||||
|
* Browse thru program header table (e_phnum items) from e_phoff (program header offset)
|
||||||
|
* if its stack and has executable bit then we know NX isnt there
|
||||||
|
*/
|
||||||
|
fseek(fp, header->e_phoff, SEEK_SET);
|
||||||
|
for (size_t i=0; i<header->e_phnum; i++)
|
||||||
|
{
|
||||||
|
Elf64_Phdr p_header;
|
||||||
|
if (!fread(&p_header, 1, sizeof(Elf64_Phdr), fp))
|
||||||
|
{
|
||||||
|
puts("Read error.");
|
||||||
|
exit(-EIO);
|
||||||
|
}
|
||||||
|
if (p_header.p_type == PT_GNU_STACK)
|
||||||
|
{
|
||||||
|
if (p_header.p_flags & PF_X)
|
||||||
|
{
|
||||||
|
nx = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nx = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stack canary:
|
||||||
|
* Same for section header table (e_shnum items) from e_shoff (section header offset)
|
||||||
|
* find sections .dynsym or .symtab:
|
||||||
|
* and look for __stack_chk_fail (call for stack smashing)
|
||||||
|
*/
|
||||||
|
fseek(fp, header->e_shoff, SEEK_SET);
|
||||||
|
|
||||||
|
// Read section headers
|
||||||
|
Elf64_Shdr *s_headers = malloc(header->e_shnum * sizeof(Elf64_Shdr));
|
||||||
|
if (!s_headers)
|
||||||
|
{
|
||||||
|
puts("Memory allocation error.");
|
||||||
|
exit(-ENOMEM);
|
||||||
|
}
|
||||||
|
if (!fread(s_headers, sizeof(Elf64_Shdr), header->e_shnum, fp))
|
||||||
|
{
|
||||||
|
puts("Read error.");
|
||||||
|
exit(-EIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find section name string table (at e_shstrndx) and read it
|
||||||
|
Elf64_Shdr s_header_str = s_headers[header->e_shstrndx];
|
||||||
|
char* s_header_strtab = malloc(s_header_str.sh_size);
|
||||||
|
if (!s_header_strtab)
|
||||||
|
{
|
||||||
|
puts("Memory allocation error.");
|
||||||
|
exit(-ENOMEM);
|
||||||
|
}
|
||||||
|
fseek(fp, s_header_str.sh_offset, SEEK_SET);
|
||||||
|
if (!fread(s_header_strtab, 1, s_header_str.sh_size, fp))
|
||||||
|
{
|
||||||
|
puts("Read error.");
|
||||||
|
exit(-EIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i=0; i<header->e_shnum; i++)
|
||||||
|
{
|
||||||
|
// additional small check for stripping as we're already browsing section headers
|
||||||
|
if (s_headers[i].sh_type == SHT_SYMTAB && s_headers[i].sh_size > 0) stripped = false;
|
||||||
|
|
||||||
|
if (s_headers[i].sh_type == SHT_DYNSYM || s_headers[i].sh_type == SHT_SYMTAB)
|
||||||
|
{
|
||||||
|
// load symbol table
|
||||||
|
unsigned int nsyms = s_headers[i].sh_size / s_headers[i].sh_entsize;
|
||||||
|
Elf64_Sym *syms = malloc(s_headers[i].sh_size);
|
||||||
|
if (!syms)
|
||||||
|
{
|
||||||
|
puts("Memory allocation error.");
|
||||||
|
exit(-EIO);
|
||||||
|
}
|
||||||
|
fseek(fp, s_headers[i].sh_offset, SEEK_SET);
|
||||||
|
if (!fread(syms, s_headers[i].sh_size, 1, fp))
|
||||||
|
{
|
||||||
|
puts("Read error.");
|
||||||
|
exit(-EIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
// load string table
|
||||||
|
Elf64_Shdr str_header = s_headers[s_headers[i].sh_link];
|
||||||
|
char* strtab = malloc(str_header.sh_size);
|
||||||
|
if (!strtab)
|
||||||
|
{
|
||||||
|
puts("Memory allocation error.");
|
||||||
|
exit(-ENOMEM);
|
||||||
|
}
|
||||||
|
fseek(fp, str_header.sh_offset, SEEK_SET);
|
||||||
|
if (!fread(strtab, 1, str_header.sh_size, fp))
|
||||||
|
{
|
||||||
|
puts("Read error.");
|
||||||
|
exit(-EIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterate through symbols, check for __stack_chk_fail
|
||||||
|
for (size_t j=0; j<nsyms; j++)
|
||||||
|
{
|
||||||
|
const char* name = strtab + syms[j].st_name;
|
||||||
|
if (strcmp(name, "__stack_chk_fail") == 0)
|
||||||
|
{
|
||||||
|
canary = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(syms);
|
||||||
|
free(strtab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RELRO:
|
||||||
|
* Segment type PT_GNU_RELRO should be present
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Read program headers
|
||||||
|
fseek(fp, header->e_phoff, SEEK_SET);
|
||||||
|
Elf64_Phdr *p_headers = malloc(header->e_phnum * sizeof(Elf64_Phdr));
|
||||||
|
if (!p_headers)
|
||||||
|
{
|
||||||
|
puts("Memory allocation error.");
|
||||||
|
exit(-ENOMEM);
|
||||||
|
}
|
||||||
|
if (!fread (p_headers, sizeof(Elf64_Phdr), header->e_phnum, fp))
|
||||||
|
{
|
||||||
|
puts("Read error.");
|
||||||
|
exit(-EIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
Elf64_Off dyn_off = 0;
|
||||||
|
Elf64_Word dyn_size = 0;
|
||||||
|
|
||||||
|
for (int i=0; i<header->e_phnum; i++)
|
||||||
|
{
|
||||||
|
if (p_headers[i].p_type == PT_GNU_RELRO)
|
||||||
|
{
|
||||||
|
relro = true;
|
||||||
|
}
|
||||||
|
if (p_headers[i].p_type == PT_DYNAMIC)
|
||||||
|
{
|
||||||
|
dyn_off = p_headers[i].p_offset;
|
||||||
|
dyn_size = p_headers[i].p_filesz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dyn_off && dyn_size)
|
||||||
|
{
|
||||||
|
int n = dyn_size / sizeof(Elf64_Dyn);
|
||||||
|
Elf64_Dyn *dyns = malloc(dyn_size);
|
||||||
|
if (!dyns)
|
||||||
|
{
|
||||||
|
puts("Memory allocation error.");
|
||||||
|
exit(-ENOMEM);
|
||||||
|
}
|
||||||
|
fseek(fp, dyn_off, SEEK_SET);
|
||||||
|
if (!fread(dyns, sizeof(Elf64_Dyn), n, fp))
|
||||||
|
{
|
||||||
|
puts("Read error.");
|
||||||
|
exit(-EIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=0; i<n; i++)
|
||||||
|
{
|
||||||
|
// Has full RELRO?
|
||||||
|
if (dyns[i].d_tag == DT_BIND_NOW)
|
||||||
|
{
|
||||||
|
bind_now = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(dyns);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(s_headers);
|
||||||
|
free(s_header_strtab);
|
||||||
|
free(p_headers);
|
||||||
|
}
|
||||||
|
|
||||||
unsigned char read_elf_obj_size(FILE* fp)
|
unsigned char read_elf_obj_size(FILE* fp)
|
||||||
{
|
{
|
||||||
unsigned char local_elf_obj_size = 0;
|
unsigned char local_elf_obj_size = 0;
|
||||||
@@ -550,6 +733,7 @@ int main(int argc, char* argv[])
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
display_elf64(&elf64_header, elf_file);
|
display_elf64(&elf64_header, elf_file);
|
||||||
|
check_sec64(&elf64_header, elf_file);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
puts("Invalid ELF object size");
|
puts("Invalid ELF object size");
|
||||||
|
|||||||
Reference in New Issue
Block a user