ELFCore drops the permissions of every core segment it turns into a Blob
- Lingua principale
- Python
- Stelle
- 485
- Fork
- 135
- Merge medio
- 2g 2h
- PR unite (30g)
- 15
Descrizione
THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS
A core dump records the permissions every mapping had when the process died, and `ELFCore` reads them correctly: `main_object.segments` reports `r-x`, `rw-` and `---` exactly as the program headers spell them.
Those permissions are then thrown away. `ELFCore.__reload_children` turns each core segment it could not match to a child object into a `Blob`, and `Blob._load` builds a plain `Segment`, whose base `Region.is_executable` returns `True` unconditionally. Every leftover page of the dump — heap, stack, the ELF header page, the `---p` guard pages — reaches the loader as executable memory. This does not depend on the child objects being found: segments that do match a child are consumed, and everything else becomes a `Blob` either way.
angr then follows. `CFGBase._executable_memory_regions` has a `Blob` branch reading "a blob is entirely executable", so CFGFast scans the whole dump linearly.
On an x86_64 core (sha256 `85aae14ffea90564f8c9d3f6d76be8c3eb29b7cb2c21c19833de8e96f6f2a9a7`), whose program headers are
```
PT_LOAD vaddr=0x400000 memsz=0x1000 filesz=0x1000 r--
PT_LOAD vaddr=0x401000 memsz=0x1000 filesz=0x1000 r-x
PT_LOAD vaddr=0x402000 memsz=0x1000 filesz=0x1000 r--
PT_LOAD vaddr=0x403000 memsz=0x1000 filesz=0x1000 rw-
...
```
CLE reports nine `Blob` children, all `r=True w=True x=True`, and CFGFast produces 115 blocks of which 105 lie outside the one `r-x` page. They are the ELF header and the dynamic table read as instructions:
```
0x400066 add byte ptr [rax], al
0x400068 call 0x40006e
...
0x4001d0 add eax, 0x47000000 ; the "GNU" of the build-id note
0x4001d5 push rbp
```
Four dumps checked by hand, all the same shape: mips `e3c4b509799696b210f2ea057b24a18bfa455fc691f78047fdb0083b207e7af4` (27 of 62 blocks in the `rw-` data page), aarch64 `64bf16baa69daf146f7985d4159b0ffddf452f8ac988bb139dd2b9465908e3e4` (40 of 60), arm `8b44187592e240dca26e9dbffd35df0ffdb684c648b9fb9af519a85f1aee9fa9` (380 of 680), and the x86_64 dump above. In a sweep of a large object corpus, core dumps are the largest family whose blocks land outside every executable range the file declares: 216 mips, 117 aarch64, 112 arm and 111 x86_64 dumps.
The fix has two halves and neither works alone:
* CLE gives a blob-backed object no way to carry permissions. `Blob` would have to accept them and `ELFCore` would have to pass the `ELFSegment` flags through.
* angr's `Blob` branch would have to consult those segments instead of assuming the whole object is code. That assumption is right for a raw firmware image, which genuinely carries no permission information, so the change has to distinguish a blob that knows its permissions from one that does not.
That is a decision about how a blob-backed object describes itself, which is why this is a report rather than a pull request.
Reproducing needs no unusual input. On Linux, `ulimit -c unlimited`, crash any program, and then:
```python
project = angr.Project(core_path, main_opts={"backend": "elfcore"})
[(hex(s.vaddr), s.is_executable) for s in project.loader.main_object.segments]
[(type(o).__name__, hex(o.min_addr), [s.is_executable for s in (o.segments or ())])
for o in project.loader.all_objects]
```
The first list carries the real permissions; the second reports every `Blob` executable. The effect is clearest when the crashed executable is not present at the path the dump records, because then no child object loads and every segment becomes a `Blob`.
Guida per i contributori
Nessuna guida per i contributori indicizzata per questo repository
Valutazione
Questa issue non è ancora stata valutata.