Linking and security

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Linking and security

Post by bzt »

Forked from another topic.

In a nutshell: I'm saying if dynamic linker is in the kernel, then it can be more secure, as it can enforce WNX and deny writing code from user level. Some architecture supports this in hardware (like ARM), while others needs carefully assigned page attributes (like x86). Nullplan seems do disagree, however it looks more like he doesn't get the concept in the first place.
nullplan wrote:
bzt wrote:A dynamic linker is not of a big complexity. Not simple, but not particularly complex either (typically no more than few hundred SLoC).
We're just going to have to agree to disagree on that one. A dynamic linker is a complex piece of software parsing attacker-controlled data, and it is getting nowhere near my kernel.
In a monolithic kernel everything is in the kernel. Why not this one?

And about "parsing attacker-controlled data", isn't that true for everything? How about your kernel loading the interpreter? Or how about ACPI or AML? You don't support power management because that would mean parsing data in kernel?
nullplan wrote:
bzt wrote:Are you serious? What do you think, why did ARM implement WNX permission in hardware if its just a "nonsense"?
Nice evade. You didn't actually answer my question.
Yes, I did. Read the specification. Having either executable or writable pages makes buffer-overflows impossible. There's not much to explain, either you understand this, or you don't. With WNX buffer overflows might modify data, but they can't inject code.
nullplan wrote:And that page doesn't say why you shouldn't allow userspace to map executable pages.
Because that's obvious. If you put the dynamic linker in userspace, then you must allow to write code from userspace. First, you must load the code into memory (memory must be writable), second, you must modify the code with relocations. You can only do that if pages are writable that will be executed later.
nullplan wrote:There is no dipstick for security, no pressure gauge, no measuring tape.
This sentence convinced me you don't have the required knowledge. Yes, there are features that are more secure, like having an MMU and separated address spaces, or like denying writing executable pages. Are you really saying that an M68k without privilege levels of any kind is equally secure to an x86 with rings or ARM with ELs? Think it again.
nullplan wrote:Textrels are an awful hack only present in non-PIC code that has been linked dynamically. PIC code actually manages to put all the relocations into the data section, and the actual code section remains unaltered.
You only wish. There are architectures which does not support PIC at all (x86 protected mode being one of them), and there are special instructions that always need relocation records (like "movabs" in x86_64, and short distance intermediates on ARM). A purely PIC instruction encoding would be nice, I agree, but sadly current processors are not like that.
nullplan wrote:No, no, and no. Malicious code already cannot modify existing code. For instance, here I am looking at all Firefox instances on my system
You are very, very very mistaken. Firefox is full of overflow problems which allow running arbitrary code (and this list is only for the last year). It even uses JIT compiled JavaScript, which by definition requires writeable *and* executable memory.
nullplan wrote:The GOT is in the same segment as the data section. You cannot write-protect one without write-protecting the other.
I beg to differ. Memory protection is per page. You can easily align GOT on page boundary, and have that read-only, while having all the rest of the data writable. It is YOUR kernel, you write the linker script and you write the dynamic linker! You can even put the GOT into its own segment if you wish!

Cheers,
bzt
Gigasoft
Member
Member
Posts: 856
Joined: Sat Nov 21, 2009 5:11 pm

Re: Linking and security

Post by Gigasoft »

Sorry, but your idea does nothing useful in terms of security. You have failed to consider who would be the attacker and who would be the target. In this case, the attack consists of making a program inadvertently modify itself in memory because of a bug in said program. To prevent this from occurring, you decide that programs shouldn't be able to write to pages that are later executed. But this isn't a form of security, it just limits the kinds of programs that can be written. For the programs that remain, there is no difference, since they would not be allocating such pages to begin with. This is taking the role of a nanny and deciding that developers are too stupid to do anything right, so we need to take away anything they could potentially hurt themselves with.

I agree that a window of opportunity exists for shenanigans happening in a different thread while a library is being loaded, but this doesn't mean that no programs should ever be able to modify themselves. If the OS author doesn't want the loader to run in kernel mode, it could be run in a different process instead.
And about "parsing attacker-controlled data", isn't that true for everything? How about your kernel loading the interpreter? Or how about ACPI or AML?
ACPI is not attacker controlled (if it is, you've got far worse problems to worry about). Neither is the executable loader - the OS had better be able to trust itself and its own components.
It even uses JIT compiled JavaScript, which by definition requires writeable *and* executable memory.
Surely, JavaScript doesn't require pages to be executable and writable at the same time. Nothing is being executed until it is already written. Since JavaScript runs in a single thread, in fact no JavaScript runs at all while a function is being compiled.
Having either executable or writable pages makes buffer-overflows impossible. There's not much to explain, either you understand this, or you don't. With WNX buffer overflows might modify data, but they can't inject code.
I take it you don't know what return oriented programming is. An attacker can string together operations from existing code without introducing any code of his own, by making the program successively return to instructions that when executed in succession, perform a malicious operation.
PeterX
Member
Member
Posts: 590
Joined: Fri Nov 22, 2019 5:46 am

Re: Linking and security

Post by PeterX »

Gigasoft wrote:Sorry, but your idea does nothing useful in terms of security. You have failed to consider who would be the attacker and who would be the target. In this case, the attack consists of making a program inadvertently modify itself in memory because of a bug in said program. To prevent this from occurring, you decide that programs shouldn't be able to write to pages that are later executed. But this isn't a form of security, it just limits the kinds of programs that can be written. For the programs that remain, there is no difference, since they would not be allocating such pages to begin with. This is taking the role of a nanny and deciding that developers are too stupid to do anything right, so we need to take away anything they could potentially hurt themselves with.
In contrast, to me to deny self-modification seems indeed a kind of security technique. It DOES reduce attacks or attack-methods, doesn't it?

Can you name useful use cases for self-modifying code?

Greetings
Peter
Gigasoft
Member
Member
Posts: 856
Joined: Sat Nov 21, 2009 5:11 pm

Re: Linking and security

Post by Gigasoft »

In contrast, to me to deny self-modification seems indeed a kind of security technique. It DOES reduce attacks or attack-methods, doesn't it?
No, for any given program it either does nothing because the program didn't need self modification in the first place, or it makes the program impossible to write. In no case does it change an exploitable situation into one with a program that isn't exploitable and still perform its intended function.
Can you name useful use cases for self-modifying code?
Emulators, script engines and software 3D renderers that need to run fast. Programs with overlays. Programs that load executables in a nonstandard format.
PeterX
Member
Member
Posts: 590
Joined: Fri Nov 22, 2019 5:46 am

Re: Linking and security

Post by PeterX »

Gigasoft wrote:
In contrast, to me to deny self-modification seems indeed a kind of security technique. It DOES reduce attacks or attack-methods, doesn't it?
No, for any given program it either does nothing because the program didn't need self modification in the first place, or it makes the program impossible to write. In no case does it change an exploitable situation into one with a program that isn't exploitable and still perform its intended function.
Ah, I understand now. But it could change code that was checked buy a security program from harmless to malicious.

Greetings
Peter
nullplan
Member
Member
Posts: 1790
Joined: Wed Aug 30, 2017 8:24 am

Re: Linking and security

Post by nullplan »

bzt wrote:In a nutshell: I'm saying if dynamic linker is in the kernel, then it can be more secure, as it can enforce WNX and deny writing code from user level. Some architecture supports this in hardware (like ARM), while others needs carefully assigned page attributes (like x86). Nullplan seems do disagree, however it looks more like he doesn't get the concept in the first place.
You don't need to put the dynamic linker into the kernel to enforce W^X. If you want to go farther in your hardening, you can require that all executable pages be mapped read-only from files the user cannot write (except root, obviously), and it still does not require you to put the dynlinker into the kernel.

And that does already harden as much as possible against stack smash classic (the original 1996 paper "Smashing the stack for fun and profit"), but absolutely fails to do anything against ROP. ASLR and stack protection mitigate, but the only thing that eliminates the problem is actually patching the fault.
bzt wrote:In a monolithic kernel everything is in the kernel. Why not this one?
No, only that which must be in the kernel, is in the kernel. My kernel is monolithic, which means device drivers are also in the kernel, but, for instance, userspace programs are not. My kernel is lacking features others have, mostly for lack of will on my part, but sometimes out of conscious decision not to have it running in ring 0. I have support for frame buffers, and support for event-driven input devices of all kinds, and for pseudo-terminals, but not for virtual terminals (like Linux has). This means, I have no font-rendering in the kernel itself, no frame buffer drawing routines, nothing to map scan codes to glyphs (and that can be quite an astonishingly complex task in some languages), etc. So much stuff that is just not part of my kernel. Now, it does still exist. A frame buffer terminal application tying those device drivers together does exist, but it is entirely in userspace. If (for example) the font renderer passes out because a Hiragana character was combined with Devanagari diacritics, it will be a userspace program that dies, not the whole OS.

In that spirit, the dynamic linker is also not part of the kernel. Because it doesn't have to be.
bzt wrote:And about "parsing attacker-controlled data", isn't that true for everything? How about your kernel loading the interpreter? Or how about ACPI or AML? You don't support power management because that would mean parsing data in kernel?
Those things are unavoidably in the kernel. My kernel does the bare minimum of parsing of ELF headers to run the program, but what little it does is littered with boundary checks to ensure faulty data is detected and rejected, rather than leading to unpredictable program behavior, because in a kernel, that is undesirable. As for ACPI and AML, well, I use ACPICA for that (LAI was not a thing when I started that part, sorry Korona). But then, ACPi tables are not attacker-controlled. If the mainboard manufacturer wants to attack my OS, they have way better ways to get to it. Ways that I couldn't detect. Like using SMM, or going in through the embedded controller. Or just using Management Engine or whatever it is AMD calls the equivalent technology. But file system structures, ELF headers, dynamic headers, those are all potentially in the hands of unprivileged users, or hackers exploiting a running program. I know some programs are insecure, I don't have to hand them a privilege escalation to ring 0 on top of everything else.
bzt wrote:Yes, I did. Read the specification. Having either executable or writable pages makes buffer-overflows impossible. There's not much to explain, either you understand this, or you don't.
I am not interested in the minutiae of how ARM implements data execution prevention. I'll just believe you that it has provisions for it.
bzt wrote:With WNX buffer overflows might modify data, but they can't inject code.
And I keep saying ROP like a broken record. Modifying data is enough to get a program to misbehave in arbitrary ways. ROP is Turing-complete. You can do anything with it.
bzt wrote:Because that's obvious. If you put the dynamic linker in userspace, then you must allow to write code from userspace. First, you must load the code into memory (memory must be writable), second, you must modify the code with relocations. You can only do that if pages are writable that will be executed later.
A dynamic linker does not load files into memory by way of file I/O, but by using mmap(). It does not write code into memory, it maps code into memory. The difference is that the memory is never writable that way! It is write protected. While the code is loaded from disk (by way of page fault), it will be DMA'd into the physical memory. Unless the device somehow only supports PIO, but I haven't used that since the start of the millennium.

Second, the relocations do not modify the code. Which I wrote a hundred times already. Relocations, on anything from this century at least, only affect data.
bzt wrote:This sentence convinced me you don't have the required knowledge.
There's that arrogance again. You don't see me impugning your level of competence, do you?
bzt wrote:Yes, there are features that are more secure, like having an MMU and separated address spaces, or like denying writing executable pages. Are you really saying that an M68k without privilege levels of any kind is equally secure to an x86 with rings or ARM with ELs? Think it again.
Depends on what you mean. Are all platforms you named susceptible to attacks? Yes. Now, the effort required to attack an M68k system is way smaller than the effort to attack an x86 system, that is true. But is that enough? Hacking success as prize for hard work?

Is a lock that is not susceptible to most attacks, but absolutely breaks under one specific attack, any good? Any thief worth his salt will just use the one attack that breaks your lock. What would you think of a virus scanner that screens out 99% of all viruses, if the 1% it doesn't detect keeps wrecking your hard drive? "More secure" is a very seductive concept (we humans are very familiar with linear processes, after all), that is also completely invalid, because it does not prevent a determined attacker from breaking the system.

Security is not, when, in hindsight, you can say that you weren't successfully attacked. That might have just been luck. Security is about giving specific guarantees beforehand, about what can and, more importantly, cannot happen. And your little idea doesn't add anything useful to the list of security guarantees.
bzt wrote:You only wish. There are architectures which does not support PIC at all (x86 protected mode being one of them), and there are special instructions that always need relocation records (like "movabs" in x86_64, and short distance intermediates on ARM). A purely PIC instruction encoding would be nice, I agree, but sadly current processors are not like that.
What are you on about? x86, AMD64, and ARM all have mature ELF PIC ABIs, going back decades (well, not quite as far for AMD64, just one and a half decades). And yes, they do restrict code generation. For instance, movabs with external identifiers is not allowed in PIC. And the linker will tell you as much. And pretty much the only way to get them is to write assembly code yourself, badly, and then force the linker to emit textrels, anyway (because the default is to emit an error message.)

Is that what this is about? You wrote some bad assembly a while ago and now you think textrels are the norm? I am pleased to report that on my Linux system, there is not a single textrel anywhere.
bzt wrote:You are very, very very mistaken. Firefox is full of overflow problems which allow running arbitrary code (and this list is only for the last year). It even uses JIT compiled JavaScript, which by definition requires writeable *and* executable memory.
I did not choose Firefox as some kind of paragon of security, I chose it because it is a decently complicated piece of software (some would say overcomplicated), and even then, it does not have writable and executable memory mapped anywhere. Now, it is possible that it is switching some memory from writable to executable and back; my script would not catch that. My point was, that in the entire mess that is Firefox, there is not a single writable and executable mapping as result of dynamic linking. What they do in the program itself is of no consequence to the argument.
bzt wrote:I beg to differ. Memory protection is per page. You can easily align GOT on page boundary,
I suspected as much. Actually I looked into something myself, and I found out that you just invented GNU RELRO.

I am curious, though: If you do have the dynlinker in the kernel, do you support lazy relocations? If so, how? If not, how do you deal with circular dependencies?
Carpe diem!
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: Linking and security

Post by bzt »

Gigasoft wrote:To prevent this from occurring, you decide that programs shouldn't be able to write to pages that are later executed.
I didn't decide anything. Implementing WNX was a decision on ARM engineers part to increase security.
Gigasoft wrote:ACPI is not attacker controlled
Famous last words? :-) You'd be surprised. On BIOS, that would need an Expansion ROM to modify ACPI tables, but on UEFI any arbitrary driver loaded from the disk can do that. Plus many mainstream OS allows the kernel to directly load AML code from disk (which then will be executed in ring 0).
nullplan wrote:You don't need to put the dynamic linker into the kernel to enforce W^X.
Then how do you solve that a dynlinker in userspace must write code (loading, relocating), but it also must map it as executable? Having a syscall that changes writable data pages into executable code is not a solution, because a malicious code can just as well use that syscall.
nullplan wrote:No, only that which must be in the kernel, is in the kernel.
Okay, if you don't want modifiable GOT, then you must map it as read-only, meaning you either fill it up in the kernel where it's writable or you could implement a make memory page read-only syscall. Either way, requires kernel support.
nullplan wrote:My kernel does the bare minimum of parsing of ELF headers to run the program, but what little it does is littered with boundary checks to ensure faulty data is detected and rejected
And you can't implement those boundary checks in a dynlinker, because...? Your argument is invalid. You have admitted that you already have an ELF parser in your kernel! Adding a little bit more code to read the NEEDED records is a piece of cake, even with boundary checks!
nullplan wrote:And I keep saying ROP like a broken record. Modifying data is enough to get a program to misbehave in arbitrary ways. ROP is Turing-complete. You can do anything with it.
Then show us a PoC where you inject an arbitrary code and you execute it! Just one little example!
nullplan wrote:The difference is that the memory is never writable that way! It is write protected.
And you implement data segments and relocations with what? You have this delusion that ELF is mapped read-only and relocations are unsupported, but that's just simply ain't true.
nullplan wrote:Second, the relocations do not modify the code. Which I wrote a hundred times already. Relocations, on anything from this century at least, only affect data.
You have absolutely no clue how relocations work. Just try this on a 64 bit Linux:

Code: Select all

$ readelf -r /usr/lib/libc.so.*
You'll see hundreds and hundreds of text relocation records.
nullplan wrote:You don't see me impugning your level of competence, do you?
Actually yes, I do see. But I'm not saying such foolish nonsense that security isn't measurable or that relocations aren't supported...
nullplan wrote:x86, AMD64, and ARM all have mature ELF PIC ABIs, going back decades (well, not quite as far for AMD64, just one and a half decades). And yes, they do restrict code generation.
The fact that a standard libc.so.6 compiled for x86_64 is full of text relocations does contradict your claims.
nullplan wrote:And pretty much the only way to get them is to write assembly code yourself
Again, you simply don't have the required knowledge. I know it is rude to say, but RTFM for Christ's sake!
Medium model: "This model requires the compiler to use \code{movabs} instructions to access large static data" (large data = bigger than 64k)
Large model: "The compiler is required to use the \code{movabs} instruction, as in the medium code model, even for dealing with addresses inside the text section."

Even better, the example Position-Independent Function Prolog Code for large model explicitly uses "movabs" to load the GOT address.
nullplan wrote:Is that what this is about? You wrote some bad assembly a while ago
First, no I haven't, second, who is impugning now? Should you have read the specs, you'd know you're wrong.
nullplan wrote:I did not choose Firefox as some kind of paragon of security
You've chosen Firefox because, and I quote: "Malicious code already cannot modify existing code. For instance, here I am looking at all Firefox instances" The CVE tracker contradicts you, it is possible to run arbitrary code through overflow-attacks in Firefox.
nullplan wrote:I am curious, though: If you do have the dynlinker in the kernel, do you support lazy relocations? If so, how?
Really? Are you really asking this? Obviously by the lazy linker calling the ELF loader. If that is in kernel space, then it means through a syscall. This is OSDev one-o-one.
nullplan wrote:If not, how do you deal with circular dependencies?
Really? The same way as a userspace linker would! There's absolutely no difference if the linker is in userpsace or if it's in kernel space: you just keep track of already linked objects.

Cheers,
bzt
Octocontrabass
Member
Member
Posts: 5568
Joined: Mon Mar 25, 2013 7:01 pm

Re: Linking and security

Post by Octocontrabass »

bzt wrote:Then show us a PoC where you inject an arbitrary code and you execute it! Just one little example!
There are some examples here, and links where you can find others.
Gigasoft
Member
Member
Posts: 856
Joined: Sat Nov 21, 2009 5:11 pm

Re: Linking and security

Post by Gigasoft »

I didn't decide anything. Implementing WNX was a decision on ARM engineers part to increase security.
But that's not what we're talking about here. WXN merely forces it so that pages can't be executable and writable at the same time. You were saying that user processes should never be able to write to pages that will be executed later, thereby severely crippling what programs can do.
Famous last words? :-) You'd be surprised. On BIOS, that would need an Expansion ROM to modify ACPI tables, but on UEFI any arbitrary driver loaded from the disk can do that.
If an unwelcome visitor has that kind of access then the computer has already been compromised, and it's basically no longer your computer. There is nothing the OS can do to remedy this after the fact. The best it can do is to lock the user out of data that has been encrypted with a TPM-protected key.
nullplan
Member
Member
Posts: 1790
Joined: Wed Aug 30, 2017 8:24 am

Re: Linking and security

Post by nullplan »

bzt wrote:Then how do you solve that a dynlinker in userspace must write code (loading, relocating), but it also must map it as executable? Having a syscall that changes writable data pages into executable code is not a solution, because a malicious code can just as well use that syscall.
For the hundredth time, relocations do not write into the code segment. I shall address the second point below, for the first one look here:

Code: Select all

$ readelf -r /lib/x86_64-linux-gnu/libc-2.27.so


Relocation section '.rela.dyn' at offset 0x18f28 contains 1326 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
0000003e7620  000000000008 R_X86_64_RELATIVE                    3ec560
0000003e7630  000000000008 R_X86_64_RELATIVE                    21470
[...offset is strictly increasing...]
0000003ec7c8  017300000001 R_X86_64_64       00000000003eba00 _IO_2_1_stdin_@@GLIBC_2.2.5 + 0
0000003ec850  017300000001 R_X86_64_64       00000000003eba00 _IO_2_1_stdin_@@GLIBC_2.2.5 + 0

Relocation section '.rela.plt' at offset 0x20b78 contains 46 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
0000003eb038  020100000007 R_X86_64_JUMP_SLO 0000000000098ca0 realloc@@GLIBC_2.2.5 + 0
0000003eb058  000300000007 R_X86_64_JUMP_SLO 0000000000000000 __tls_get_addr@GLIBC_2.3 + 0
[...]
0000003eb020  000000000025 R_X86_64_IRELATIV                    d2ac0
0000003eb018  000000000025 R_X86_64_IRELATIV                    9ece0
So, we can say that all relocations have targets between offset 3e7XXX and offset 3ecXXX. What is at those offsets?

Code: Select all

$ readelf -l .../libc-2.27.so

Elf file type is DYN (Shared object file)
Entry point 0x21cb0
There are 10 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000000040 0x0000000000000040
                 0x0000000000000230 0x0000000000000230  R      0x8
  INTERP         0x00000000001bdfb0 0x00000000001bdfb0 0x00000000001bdfb0
                 0x000000000000001c 0x000000000000001c  R      0x10
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x00000000001e6d08 0x00000000001e6d08  R E    0x200000
  LOAD           0x00000000001e7620 0x00000000003e7620 0x00000000003e7620
                 0x0000000000005240 0x00000000000094c0  RW     0x200000
  DYNAMIC        0x00000000001eab80 0x00000000003eab80 0x00000000003eab80
                 0x00000000000001e0 0x00000000000001e0  RW     0x8
  NOTE           0x0000000000000270 0x0000000000000270 0x0000000000000270
                 0x0000000000000044 0x0000000000000044  R      0x4
  TLS            0x00000000001e7620 0x00000000003e7620 0x00000000003e7620
                 0x0000000000000010 0x0000000000000090  R      0x8
  GNU_EH_FRAME   0x00000000001bdfcc 0x00000000001bdfcc 0x00000000001bdfcc
                 0x00000000000059dc 0x00000000000059dc  R      0x4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10
  GNU_RELRO      0x00000000001e7620 0x00000000003e7620 0x00000000003e7620
                 0x00000000000039e0 0x00000000000039e0  R      0x1
So all the relocations are safely within the second LOAD segment, which has RW (and not E) as its flags.
bzt wrote:Okay, if you don't want modifiable GOT, then you must map it as read-only, meaning you either fill it up in the kernel where it's writable or you could implement a make memory page read-only syscall. Either way, requires kernel support.
As I said, you just invented RELRO. And yes, there is a call to make memory read-only after it was written to. It is called mprotect(). And implementing a system call that changes page protection attributes is way easier than implementing a dynamic linker in ring 0. Especially since the system call is more versatile, it can be used for more than RELRO. musl uses it to implement guard pages for thread stacks.

Of course, for security reasons, mprotect() shouldn't be able to change page protection attributes in arbitrary ways. In hardened setups, mprotect() is often unable to make a page executable that has ever been writable, or make a page writable that is currently executable. This prevents anyone from writing shellcode to memory and executing it. Unfortunately, there is no easy solution to the problem of overwriting the GOT. Yes, I could prevent mprotect() from making write-protected memory writable, but subverted code could just map fresh memory over the GOT with mmap(MAP_FIXED | MAP_ANONYMOUS). And I can't prevent that one without breaking the way many guard page allocators work.

I am a bit torn on the issue of making mmap() unable to map executable pages from files the user can write to. I mean on the one hand this prevents shellcode, on the other, this prevents usage of locally-installed shared libraries. One possible solution would be to enable such mappings in new processes, and then having a special syscall that the dynlinker calls when it is done loading to stop further mappings of that kind being made. That would at least allow linking against your own shared libs, even if dlopen() would not work.
bzt wrote:And you can't implement those boundary checks in a dynlinker, because...?
Never said I couldn't, I just didn't want to. Not if there are other possibilities.
bzt wrote:Then show us a PoC where you inject an arbitrary code and you execute it! Just one little example!
I am not your Google. You came to me to lecture me about how your setup is oh-so-secure. You do the research, you might learn something.
bzt wrote:And you implement data segments and relocations with what? You have this delusion that ELF is mapped read-only and relocations are unsupported, but that's just simply ain't true.
What are you on about? I never said that! Data segments will of course also be mapped, only they will be mapped writable and not executable. No, of course relocations are supported. Textrels, on the other hand, relocations in the code segment, those are unsupported.
bzt wrote:You have absolutely no clue how relocations work.
You don't know the difference between relocations and textrels. I highly doubt name-calling will fix that, though. I refuted the remainder of your point further above.
bzt wrote:The fact that a standard libc.so.6 compiled for x86_64 is full of text relocations does contradict your claims.
Thousands of relocation records, in fact, and not a single textrel.
bzt wrote:Again, you simply don't have the required knowledge. I know it is rude to say,
A bit late to be worrying about politeness at this point.
bzt wrote:but RTFM for Christ's sake!
I read the FM. A long time ago, in fact. I suggest you clean your glasses. I never said movabs was straight-up banned, but "movabs with external identifiers is not allowed in PIC". I should have been more clear that I meant identifiers external to the module being linked, I suppose. To make it short:

Code: Select all

movabsq $printf, %rax #not OK
movabsq $printf@GOT, %rax # OK
And indeed the latter is required for the large model.
bzt wrote:You've chosen Firefox because, and I quote: "Malicious code already cannot modify existing code. For instance, here I am looking at all Firefox instances"
It's called an example. I could have chosen any process currently running in the system, but I chose Firefox, because it is the most complicated software currently running.
bzt wrote: The CVE tracker contradicts you,
No it doesn't, because it can't. Because there is no CVE that there must be a writable and executable memory mapping in Firefox in effect right now. I cannot ignore the evidence right in front of me! If the kernel says there is no writable and executable mapping, then I have to believe that.
bzt wrote:it is possible to run arbitrary code through overflow-attacks in Firefox.
That is my whole point: It is possible to run arbitrary code through ROP, without ever needing a memory segment that is executable and writable.
bzt wrote:Really? Are you really asking this? Obviously by the lazy linker calling the ELF loader. If that is in kernel space, then it means through a syscall. This is OSDev one-o-one.
So there is a syscall to change the GOT after loading. That's what I wanted to know.

For the other question I just remembered that the problem was slightly different, and it only affects libraries loaded with dlopen(). And it doesn't matter anyway since you do support lazy relocation.
Carpe diem!
Ethin
Member
Member
Posts: 625
Joined: Sun Jun 23, 2019 5:36 pm
Location: North Dakota, United States

Re: Linking and security

Post by Ethin »

So... serious question....
Is this topic seriously necessary? This wouldn't even classify as a debate. It almost seems childish -- hell, it probably is. From the first post its whole point is drama, it seems. And the desire to fight someone about linker security. A debate would be a hell of a lot nicer to read, without the name-calling or the personal attacks. You both are intelligent, knowledgeable individuals. Bzt -- in particular -- is it too much to ask you to treat your fellow forumites with some respect?
linguofreak
Member
Member
Posts: 510
Joined: Wed Mar 09, 2011 3:55 am

Re: Linking and security

Post by linguofreak »

bzt wrote:
Gigasoft wrote:
nullplan wrote:You don't need to put the dynamic linker into the kernel to enforce W^X.
Then how do you solve that a dynlinker in userspace must write code (loading, relocating), but it also must map it as executable? Having a syscall that changes writable data pages into executable code is not a solution, because a malicious code can just as well use that syscall.
You can have a "flipWX()" syscall *and* a "linker_exit()" syscall: after linker_exit() is called, flipWX() returns an error.

You could also have the linker running as an entirely separate process: When linker action is needed, the kernel sends a callback to the linker, and the linker then makes syscalls to map the parts of the process's address space that it needs to write to into its own address space, does its business, and then unmaps everything and tells the kernel that it's done. The linker isn't operating in userspace, and no executable page has ever been writable in the new process.
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: Linking and security

Post by bzt »

Ethin wrote:This wouldn't even classify as a debate.
Sadly, that's what you got if you try to explain something backed up by facts. This is what happens when a generation is grown up on fake news.

Just for the records, this is still more civilized and more mature than the debate of Trumb vs. Biden. And those two are not writing forum posts, they are trusted with the nuclear launch codes... Brave new world!
Ethin wrote:From the first post its whole point is drama, it seems. And the desire to fight someone about linker security.
No. The point was to explain something security related, but it is useless as shown above.
Ethin wrote:Bzt -- in particular -- is it too much to ask you to treat your fellow forumites with some respect?
I don't disrespect my forummates. What I can't stand and nobody ever should, is the act of denying facts. This is not personal.

Cheers,
bzt
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: Linking and security

Post by bzt »

Octocontrabass wrote:
bzt wrote:Then show us a PoC where you inject an arbitrary code and you execute it! Just one little example!
There are some examples here, and links where you can find others.
I know ROP, and that link does not demonstrate "injecting an arbitrary code and then executing it". Just to make it extremely simple and crystal clear: PoC code injection with WNX, that's the mission. If you can, then I'll apologize to nullplan, and I'll admit that I (and all the mainstream OS developers as well as the hardware designers) were wrong.

Cheers,
bzt
nullplan
Member
Member
Posts: 1790
Joined: Wed Aug 30, 2017 8:24 am

Re: Linking and security

Post by nullplan »

I thought of continuing the discussion in the manner we did, but then I thought better of it. This debate is absurd. A monolith proponent (me) is arguing for keeping something out of the kernel, and a micro kernel proponent (bzt) is arguing for putting it in. Then he insults me, then he blames it on my age, and then he says it's not personal. And somehow we managed to get from "dynlinker in kernel is good/bad" to "please prove the impossible". You know damn well what you ask is impossible, bzt. I also never made the claim you wish proven. You did. Get that effigy of myself out of here, it's getting straw all over the place.

Oh, and putting yourself on the side of all OS developers across the world, and all hardware designers, is pretty disingenuous when your position is actually different from theirs. Linux has the dynlinker outside the kernel, as do all ELF operating systems I know. And I don't know what Windows does, but am willing to bet what they do is running in user space as well.

I will not respond to this thread again. The positions are laid out, may the audience decide who was more persuasive.

P.S.:
bzt wrote:Just for the records, this is still more civilized and more mature than the debate of Trumb vs. Biden.
What goes on in chimpanzee cages in zoos around the world is more civilized than that debacle, so that is not a high hurdle to clear. I do agree though.
Carpe diem!
Post Reply