Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ARM32 LE Shared Object template alignment issue #19668

Open
sfewer-r7 opened this issue Nov 20, 2024 · 0 comments
Open

ARM32 LE Shared Object template alignment issue #19668

sfewer-r7 opened this issue Nov 20, 2024 · 0 comments
Labels

Comments

@sfewer-r7
Copy link
Contributor

sfewer-r7 commented Nov 20, 2024

I wrote an exploit for an 32-bit ARM Little Endian Linux based system a while back, and came across a bug in the template used to generate binaries for ELF shared object binaries.

If you generate a payload via msfvenom for an ELF Shared Object (SO) like this:

$ ruby msfvenom --arch armle --platform linux --payload linux/armle/exec --format elf-so -o ~/hax1.so CMD="something"

A binary is created via a template (here in Ruby we get the template bin file, and here is the actual template source code).

If we load the resulting binary hax1.so into IDA, we can see the following:

LOAD:00000000                 AREA LOAD, CODE, READWRITE, ALIGN=12
LOAD:00000000                 CODE32
LOAD:00000000                 DCD 0x464C457F          ; File format: \x7FELF
LOAD:00000004                 DCB 1                   ; File class: 32-bit
LOAD:00000005                 DCB 1                   ; Data encoding: little-endian
LOAD:00000006                 DCB 1                   ; File version
LOAD:00000007                 DCB 0                   ; OS/ABI: UNIX System V ABI
LOAD:00000008                 DCB 0                   ; ABI Version
LOAD:00000009                 DCB 0, 0, 0, 0, 0, 0, 0 ; Padding
LOAD:00000010                 DCW 3                   ; File type: Shared object
LOAD:00000012                 DCW 0x28                ; Machine: ARM
LOAD:00000014                 DCD 1                   ; File version
LOAD:00000018                 DCD 0xF6                ; Entry point
LOAD:0000001C                 DCD 0x34                ; PHT file offset
LOAD:00000020                 DCD 0x74                ; SHT file offset
LOAD:00000024                 DCD 0                   ; Processor-specific flags
LOAD:00000028                 DCW 0x34                ; ELF header size
LOAD:0000002A                 DCW 0x20                ; PHT entry size
LOAD:0000002C                 DCW 2                   ; Number of entries in PHT
LOAD:0000002E                 DCW 0x28                ; SHT entry size
LOAD:00000030                 DCW 2                   ; Number of entries in SHT
LOAD:00000032                 DCW 1                   ; SHT entry index for string table
LOAD:00000034 ; ELF32 Program Header
LOAD:00000034 ; PHT Entry 0
LOAD:00000034                 DCD 1                   ; Type: LOAD
LOAD:00000038                 DCD 0                   ; File offset
LOAD:0000003C                 DCD 0                   ; Virtual address
LOAD:00000040                 DCD 0                   ; Physical address
LOAD:00000044                 DCD 0x10E               ; Size in file image
LOAD:00000048                 DCD 0x126               ; Size in memory image
LOAD:0000004C                 DCD 7                   ; Flags
LOAD:00000050                 DCD 0x1000              ; Alignment
LOAD:00000054 ; PHT Entry 1
LOAD:00000054                 DCD 2                   ; Type: DYNAMIC
LOAD:00000058                 DCD 7                   ; File offset
LOAD:0000005C                 DCD stru_C4             ; Virtual address
LOAD:00000060                 DCD 0xC4                ; Physical address
LOAD:00000064                 DCD 0xC4                ; Size in file image
LOAD:00000068                 DCD 0x30                ; Size in memory image
LOAD:0000006C                 DCD 0x30                ; Flags
LOAD:00000070                 DCD 0x1000              ; Alignment
LOAD:00000074                 DCD 1, 6, 0
LOAD:00000080                 DCD 0xC4, 0xC4, 0x30, 0, 0
LOAD:00000094                 DCD 8, 7, 0
LOAD:000000A0                 DCD 3, 0
LOAD:000000A8                 DCD 0xF4, 0xF4, 2, 0, 0, 0, 0
LOAD:000000C4 ; ELF Dynamic Information
LOAD:000000C4 stru_C4         Elf32_Dyn <0xC, <0xF6>> ; DATA XREF: LOAD:0000005C↑o
LOAD:000000C4                                         ; DT_INIT
LOAD:000000CC                 Elf32_Dyn <5, <0xF4>>   ; DT_STRTAB
LOAD:000000D4                 Elf32_Dyn <6, <0xF4>>   ; DT_SYMTAB
LOAD:000000DC                 Elf32_Dyn <0xA, <0>>    ; DT_STRSZ
LOAD:000000E4                 Elf32_Dyn <0xB, <0>>    ; DT_SYMENT
LOAD:000000EC                 Elf32_Dyn <0>           ; DT_NULL
LOAD:000000F4                 DCB 0, 0
LOAD:000000F6                 EXPORT .init_proc
LOAD:000000F6 .init_proc      DCW 0x3001
LOAD:000000F8                 DCD 0xFF13E28F, 0x4678E12F, 0x9001300A, 0x1A92A901, 0xDF01270B
LOAD:0000010C                 DCB 0x69, 0x64

We can note that the entry point is located as 0xF6, and the code at LOAD:000000F6 has failed to disassemble.

If we look at the documentation from ARM here we can note the following:

All A32 instructions are 32 bits long. Instructions are stored word-aligned, so the least significant two bits of instruction addresses are always zero in A32 state.

T32 instructions are either 16 or 32 bits long. Instructions are stored half-word aligned.

Note, on ARM a word here is 32 bits and a half-word is 16 bits (Confusing if you are used to Windows development, i.e. DWORD et. al.).

As the entry point is 0xF6, the least significant bit is not set, so we are not in Thumb mode (T32), however the alignment is wrong for Arm mode (A32), it should be either 0xF4 or 0xF8 (word aligned), but it cannot be 0xF6 (half-word aligned).

During my development, a shared object binary like this would fail to load and execute.

The solution was to honor the expected alignment requirements, by adding two extra null bytes before the _start label.

diff --git a/elf_dll_armle_template.s b/elf_dll_armle_template_fix.s
index d159a69..2c026b8 100644
--- a/elf_dll_armle_template.s
+++ b/elf_dll_armle_template_fix.s
@@ -88,5 +88,8 @@ strtab:
  db 0
  db 0
 strtabsz equ $ - strtab
+
+ db 0x00, 0x00 ; add padding to honor T32 alignment
+
 global _start
 _start:

The result of this was the entry point will be 0xF8 and the SO binary loaded as expected in my target system.

This should then work for both A32 and T32 code that gets placed in the SO.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: No status
Development

No branches or pull requests

1 participant