[prev in list] [next in list] [prev in thread] [next in thread]
List: openbsd-tech
Subject: xonly status
From: "Theo de Raadt" <deraadt () openbsd ! org>
Date: 2023-01-29 17:58:58
Message-ID: 60010.1675015138 () cvs ! openbsd ! org
[Download RAW message or body]
We've made good progress in the xonly effort so here's a small summary.
architectures crossed over completely
arm64 - X bit without implied R in mmu
riscv64 - X bit without implied R in mmu
amd64 - using hardware 'PKU' feature
powerpc64 - using feature similar to PKU
hppa - using gateway feature
architectures completed in the kernel, but needing ld.bfd work
sparc64 - sun4u using split software TLB, sun4v cannot do this
octeon - newer mips cpu have a read-inhibit bit
in progress:
macppc - powerpc G5 cpus, using a feature similar to PKU
machines which cannot do this
landisk
sparc64 (sun4v)
i386
alpha
arm (32bit)
macppc G4 cpus
older mips64
amd64 cpu without PKU (but we have some ideas)
A test program has been written which checks a variety of page mappings.
Below, "userland" refers to the program directly accessing it's own memory.
"kernel" refers to a program trying to write() the memory onto a pipe.
It used to look like this:
userland kernel
ld.so readable readable
mmap xz unreadable unreadable
mmap x readable readable
mmap nrx readable readable
mmap nwx readable readable
mmap xnwx readable readable
main readable readable
libc unmapped? readable readable
libc mapped readable readable
On machines with fixes, it now looks like this:
userland kernel
ld.so unreadable unreadable
mmap xz unreadable unreadable
mmap x unreadable unreadable
mmap nrx unreadable unreadable
mmap nwx unreadable unreadable
mmap xnwx unreadable unreadable
main unreadable unreadable
libc unmapped? unreadable unreadable
libc mapped unreadable unreadable
On machines lacking hardware enforcement ability, there is a diff coming
which wraps the kernel copyin() and copyinstr() with some checks. 2 or 4
important code address spaces (in the main program, signal trampoline,
ld.so, and libc.so) are added to a very short list, and checked ahead of
time. This short list needs no locking because these address ranges are
immutable (meaning, no address space changes can be made). This check is
very quick and results in the following:
userland kernel
ld.so readable unreadable
mmap xz unreadable unreadable
mmap x readable readable
mmap nrx readable readable
mmap nwx readable readable
mmap xnwx readable readable
main readable unreadable
libc unmapped? readable unreadable
libc mapped readable unreadable
That blocks the classic "BROP" attack method of trying to write the
text segments out a socket for offline gadget study.
I want to bring attention to the "mmap xz" line. In the test program
this is a new page mapped PROT_EXEC which has never been faulted. It
contains no code, and no code is run there, so the first pagefault that
happens against it is the testing PROT_READ page fault, and the
higher-level VM code says no way -- this page can only be faulted with a
PROT_EXEC request. That leads to a surprising behaviour, and another
test program was written which tries to see what text pages may actually
be read from userland.
On a machine with proper hardware support, you cannot read any of the
text pages.
./foo7b 445a7754960-445a7755190 (830, 1 pg) prot X
n
read 0 pages of 1
cannot read the whole
/usr/lib/libc.so.96.5 4484b4139b0-4484b4b9c30 (a6280, 167 pg) prot X
nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
read 0 pages of 167
cannot read the whole
/usr/libexec/ld.so 4480810c000-44808117666 (b666, 12 pg) prot X
nnnnnnnnnnnn
read 0 pages of 12
cannot read the whole
/usr/libexec/ld.so 4480810a000-4480810c000 (2000, 3 pg) prot RX
nn
read 0 pages of 2
cannot read the whole
The surprise is what happens when this is tried on a machine which
has no hardware enforcement, like i386
./foo7b 1a67c670-1a67cea0 (830, 1 pg) prot RX
n
read 0 pages of 1
cannot read the whole
/usr/lib/libc.so.96.5 d746400-d7daf70 (94b70, 149 pg) prot X
yyynnnnnnnnnyyyyyyyyyyyyyyyyyyyyyyyyynnyyyyyynnnnnnnnyyyyyyynnnn
yyynnnnyyyyyyyyyyyyyynnnnnnynnyyyyyyyyyyyyyynnynnnyyynnyynnnnnyy
yyyyynnnnyyyyyyyyyyyn
read 97 pages of 149
cannot read the whole
/usr/libexec/ld.so 3683000-368d82f (a82f, 11 pg) prot X
nyyyyyyyyyy
read 10 pages of 11
cannot read the whole
/usr/libexec/ld.so 3681000-3683000 (2000, 3 pg) prot RX
nn
read 0 pages of 2
cannot read the whole
libc contains a bunch of page-table mappings which were created by
previous PROT_EXEC failures, from the program running functions in libc.
But other parts of libc text have never been executed, so the attempt
to read those pages creates new PROT_READ faults, and the kernel says no.
[going to skip talking about wide faults]
In non-classic BROP you will first copy the code to another region of
memory before pushing it out the socket for offline analysis. But now
you can only get chunks of libc, you cannot get the whole. Of course
everyone's machine will have a unique libc text layout, which changes at
reboot, and furthermore the parts of libc which are readable will depend
upon the specific program's previous utilization of libc. Since libc is
now full of "holes", it is even harder to download what you want, and
when you do you won't have a complete view. The difficulties faced by
the attacker have been increased in a substantial way. I will be waiting
for a paper about double-blind-rop.
The major application problems have been reasonably isolated, and after
3 weeks they are mostly handled, or we know where the remaining problems
lie:
- libcrypto assembly functions with incorrect data placement
- some ffi variations with the same problem
- managing v8's embedded code blob
- a few languages with very strange machine-dependent optimizations
That is mostly managed in ports now, mostly by fixing the code, or if it
is too difficult or intentionally obscure the specific programs can be
linked --no-exec-only. Maybe someone from the ports team can reply to
this mail to say where things are at.
An additional comment regarding "incorrect data placement" is that some
of the data tables placed into the code segment by upstream projects
intentionally and embarrasingly contains RET instructions and could
possibly be ROP gadgets. The code changes required to satisfy xonly
improve that situation.
On the whole, this effort is going surprisingly well.
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic