目录:
☆ 背景介绍
☆ p_flags
☆ sample_1
☆ 修改p_flags
1) 010 Edito
2) LIEF
☆ objcopy不适用
☆ 检查sample_1_patch的.bss可执行
————————————————————————–
☆ 背景介绍
在CTF类的比赛场景中,在Exploit学习环境中,为聚焦特定技术,会刻意降低其他技
术门槛,比如没有”stack canary”、栈区可执行、禁用ASLR,等等。
以前我有个ELF演示,要求代码段可写,正常情况下这是不可能的,需要自己Patch目
标ELF。
有些Exploit教程,在它们写就的时代,内存权限只分读写,不分执行,可读即可执
行。到了现代,这种前置条件已然失效。同样,出于聚焦的考虑,可以Patch目标ELF,
为目标内存区域增加执行权限。本文就这类特定Patch做一介绍。
☆ p_flags
关于ELF规范,参看
https://www.muppetlabs.com/~breadbox/software/ELF.txt
上文只说了32位ELF,从框架上讲,64位ELF没有本质区别。ELF有两种头,一种是
“Program Header”,另一种是”Section Header”。为了执行ELF,只需要Phdr,不需
要Shdr。打造极小ELF时,会删掉Shdr,当然,这需要特定工具。
系统加载ELF时,会根据Phdr中p_flags字段设置相应段的内存权限,比如RWX。
————————————————————————–
/*
* /usr/include/elf.h
*/
typedef struct
{
Elf64_Word p_type; /* Segment type */
Elf64_Word p_flags; /* Segment flags */
Elf64_Off p_offset; /* Segment file offset */
Elf64_Addr p_vaddr; /* Segment virtual address */
Elf64_Addr p_paddr; /* Segment physical address */
Elf64_Xword p_filesz; /* Segment size in file */
Elf64_Xword p_memsz; /* Segment size in memory */
Elf64_Xword p_align; /* Segment alignment */
} Elf64_Phdr;
#define PF_X (1 << 0) /* Segment is executable */
#define PF_W (1 << 1) /* Segment is writable */
#define PF_R (1 << 2) /* Segment is readable */
————————————————————————–
/*
* /usr/include/linux/elf.h
*/
typedef struct elf64_phdr {
Elf64_Word p_type;
Elf64_Word p_flags;
Elf64_Off p_offset; /* Segment file offset */
Elf64_Addr p_vaddr; /* Segment virtual address */
Elf64_Addr p_paddr; /* Segment physical address */
Elf64_Xword p_filesz; /* Segment size in file */
Elf64_Xword p_memsz; /* Segment size in memory */
Elf64_Xword p_align; /* Segment alignment, file & memory */
} Elf64_Phdr;
#define PF_R 0x4
#define PF_W 0x2
#define PF_X 0x1
————————————————————————–
☆ sample_1
https://github.com/angr/angr-examples/blob/master/examples/secuinside2016mbrainfuzz/sample_1
sample_1是angr-examples中的一个ELF,用于练习angr符号执行,最初是2016年提供
的。不知原提供者在什么环境中测试它,只知当时的假设是,.bss可执行。时至2025,
x64/Ubuntu 22.04.1 LTS中,sample_1的.bss实际不可执行。
————————————————————————–
$ readelf -Wl sample_1
…
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000040 0x0000000000400040 0x0000000000400040 0x0001f8 0x0001f8 R E 0x8
INTERP 0x000238 0x0000000000400238 0x0000000000400238 0x00001c 0x00001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x000000 0x0000000000400000 0x0000000000400000 0x0050cc 0x0050cc R E 0x200000
LOAD 0x005e10 0x0000000000605e10 0x0000000000605e10 0x000250 0x000530 RW 0x200000
…
Section to Segment mapping:
Segment Sections…
00
01 .interp
02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame
03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
…
————————————————————————–
.bss对应Phdr[03],这是第二个LOAD段,其Flg列显示RW,没有E,表示可读写、不可
执行。
sample_1特别构造过,无”stack canary”,栈区可执行,理论上利用RetAddr跳到
stack,可执行shellcode。但ASLR使得stack地址浮动,原解题人选择跳到.bss中,
那里有argv[1]的拷贝,是sscanf灌进去的。他们当年做题时,.bss应该有执行权限。
本题主要目的是演示angr求解以及radare2静态分析,为演示全套,必须手工Patch
sample_1,让.bss可执行。
☆ 修改p_flags
1) 010 Editor
010 Editor有ELF模板,定位到Phdr[03]的p_flags后,有下拉框可选,RWX分别对应
一个二进制位,7表示RWX,6表示RW-。
假设sample_1_patch是010 Editor的修改结果,只修改了一个字节,这是最优方案。
$ radiff2 sample_1 sample_1_patch
0x000000ec 06 => 07 0x000000ec
2) LIEF
有个Python模块LIEF用于静态修改ELF,参看:
《将PIE可执行程序转换成动态链接库》
https://scz.617.cn/unix/202012241548.txt
————————————————————————–
import lief
binary = lief.parse( ‘./sample_1’ )
#
# 提前用”readelf -Wl”确定Phdr[i]
#
seg = binary.segments[3]
seg.flags |= lief._lief.ELF.Segment.FLAGS.X
binary.write( ‘./sample_1_patch_1’ )
————————————————————————–
LIEF修改的sample_1_patch_1,不是简单修改字节,实际是重新构造ELF。虽然大小
未变,但字节变化非常多,相比之下010 Editor更聚焦。
$ radiff2 sample_1 sample_1_patch_1
(略)
☆ objcopy不适用
参看
《24.1 如何使代码段可写》
$ objdump -wh sample_1 | grep -P ‘\.bss’
25 .bss 000002e0 0000000000606060 0000000000606060 00006060 2**5 ALLOC
$ objcopy –set-section-flags .bss=ALLOC,LOAD,CODE sample_1 sample_1_patch_bad
$ objdump -wh sample_1_patch_bad | grep -P ‘\.bss’
25 .bss 000002e0 0000000000606060 0000000000606060 00006060 2**5 CONTENTS, ALLOC, LOAD, CODE
前述objcopy命令修改Shdr中sh_flags字段,这并不影响内存中.bss的执行权限,属
于无效Patch。
————————————————————————–
/*
* /usr/include/elf.h
*/
typedef struct
{
Elf64_Word sh_name; /* Section name (string tbl index) */
Elf64_Word sh_type; /* Section type */
Elf64_Xword sh_flags; /* Section flags */
Elf64_Addr sh_addr; /* Section virtual addr at execution */
Elf64_Off sh_offset; /* Section file offset */
Elf64_Xword sh_size; /* Section size in bytes */
Elf64_Word sh_link; /* Link to another section */
Elf64_Word sh_info; /* Additional section information */
Elf64_Xword sh_addralign; /* Section alignment */
Elf64_Xword sh_entsize; /* Entry size if section holds table */
} Elf64_Shdr;
#define SHF_WRITE (1 << 0) /* Writable */
#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */
#define SHF_EXECINSTR (1 << 2) /* Executable */
————————————————————————–
/*
* /usr/include/linux/elf.h
*/
typedef struct elf64_shdr {
Elf64_Word sh_name; /* Section name, index in string tbl */
Elf64_Word sh_type; /* Type of section */
Elf64_Xword sh_flags; /* Miscellaneous section attributes */
Elf64_Addr sh_addr; /* Section virtual addr at execution */
Elf64_Off sh_offset; /* Section file offset */
Elf64_Xword sh_size; /* Size of section in bytes */
Elf64_Word sh_link; /* Index of another section */
Elf64_Word sh_info; /* Additional section information */
Elf64_Xword sh_addralign; /* Section alignment */
Elf64_Xword sh_entsize; /* Entry size if section holds table */
} Elf64_Shdr;
#define SHF_WRITE 0x1
#define SHF_ALLOC 0x2
#define SHF_EXECINSTR 0x4
————————————————————————–
☆ 检查sample_1_patch的.bss可执行
gdb -q -nx –args ./sample_1_patch anything
starti
| info proc mappings | grep 0x605000
显示
0x605000 0x607000 0x2000 0x5000 rwxp /path/sample_1_patch
Perms列显示rwxp,读/写/执行,.bss位于此内存范围。