21 ms·
Being Sneaky in C
- jonahx 11y agoWould setting the malloc'd memory back to the original message before freeing it solve the problem?
- kayamon 11y agoYes. You could also memset any memory to zero once you're done with it, which isn't a bad idea for applications with high security requirements.
- rcthompson 11y agoIf you're modifying memory immediately before freeing it (i.e. after the last time you read it), don't you have to be extra super careful to do so in a way that the compiler won't optimize the operation into nothingness? (I don't program in compiled languages very much, so I don't know the details about this.)
- kayamon 11y agoActually yeah, that's a very good point. Sometimes compilers are just too clever. I think some systems have a "secure memset" function that can be used for things like this - i.e. one that's guaranteed not to be optimized out.
- ape4 11y agoYes, memset_s(). Which makes me think there should be a free_s().
- TheLoneWolfling 11y agomemset_s, or a mythical free_s, is not enough. The compiler can, and will, make copies of data behind the scenes. And not erase said copies. What we really need is a keyword / modifier that says that when X passes out of scope no state related to X may be leaked. Ideally, that can be applied to a function / block as well as a variable. (Or rather, not necessarily no state. Read "as little state as possible", preferably with modifiers that panic unless the compiler can ensure specific things.)
- mpu 11y agoThe C standard works at an abstraction level that makes it unsuitable for security applications, I would advocate for a new language here. It needs serious PL research with informarion flow reasoning, what we need is just a new kind of language much more machine aware (yes, more low-level) than C.
- TheLoneWolfling 11y agoAgreed on some level. On the other hand, something as simple as a keyword marking a variable as "as secure as possible given hardware constraints (read: should wipe any temporary copies and the variable itself after it goes out of scope, should attempt to prevent it from being written to non-volatile storage, that sort of thing)" (sort of like how inline works), with compilers required to bail if the constraint cannot be done to the level specified, would be a massive step in the right direction.
- thaumaturgy 11y agoYes, you're correct. See e.g.: http://www.eliteraspberries.com/blog/2012/10/zero-and-forget--caveats-of-zeroing-memory-in-c.html http://www.eliteraspberries.com/blog/2012/10/zero-and-forget... (which has a link to some HN discussion at the end of the article).
- TheLoneWolfling 11y agoYes. There is, quite literally, no way to ensure data is not leaked (namely, that data is zeroed / etc) in portable C / C++.
- alain94040 11y agoOr in assembly, for that matter. You never what data the CPU is duplicating behind your back: store buffers, prediction, etc.
- TheLoneWolfling 11y agoAt least with assembly most of the time you can't ever get that data back. Although not always. With C it's far too easy to get the data back. Also, you missed the worst example: CPU cache.
- TheLoneWolfling 11y ago...Except, of course, the the compiler can and will optimize away any such memset. Do not attempt to write secure software with C / C++. It is a Bad Idea(TM). Because what you wrote is not what gets run. For instance: there is no way to write a portable secure memset in portable C / C++. (You can write a "secure" memset that works in all current compilers, but that is not the same thing. What doesn't get optimized now can and will be optimized tomorrow.)
- deleted 11y ago[deleted]
- kosmic_k 11y agoSo what is someone working with embedded devices for IoT supposed to do? Much of that work is based on ARM C/C++ Compilers.
- pjc50 11y agoIn practice, of course, memset actually works, because it's a function and the compiler's usage tracing is nowhere near able to spot that you don't reference those zeros that you write. (IoT security is doom for other reasons though, mostly UI, updatability and cloud services)
- vardump 11y agoReally? In my experience most compilers treat it pretty much like an intrinsic and generate specialized set of instructions if they can.
- TheLoneWolfling 11y agoFor now. And then that code you wrote now silently becomes deadly a few years down the line.
- pjc50 11y agoI would be very interested to see the compiler that can optimise away memset across a shared library boundary.
- pjc50 11y agoFor key material, you also ought to page-lock it in memory to prevent it ever being swapped out.
- TheLoneWolfling 11y agoNo. The compiler would just optimize that set right back out.
- thaumaturgy 11y agoOpenBSD specifically modified malloc() a few years ago to prevent this sort of sneakiness (http://www.tw.openbsd.org/papers/eurobsdcon2009/otto-malloc.pdf http://www.tw.openbsd.org/papers/eurobsdcon2009/otto-malloc.... [pdf]). So they route their malloc() calls through mmap() which returns randomized pages, and free() immediately returns memory to the kernel rather than leaving it mapped in the current process. I'd be surprised if these changes haven't made it into FreeBSD, but afaik Linux doesn't work this way (by default, anyway).
- kayamon 11y agoI tested it on Ubuntu 14.04 x64, and the problem occurs there. And of course, some programs use replacements like dlmalloc and do all their own allocation management anyway.
- thaumaturgy 11y ago> And of course, some programs use replacements like dlmalloc and do all their own allocation management anyway. Yeah. I wrote my own allocator in C++ a long time ago. I wouldn't be surprised if there weren't quite a few other bits of software out there doing the same thing.
- kayamon 11y agoWasn't Heartbleed due to exactly that - using their own local allocator instead of calling malloc/free?
- ikeboy 11y agoHeartbleed was due to not checking the bounds. https://www.openssl.org/news/secadv_20140407.txt https://www.openssl.org/news/secadv_20140407.txt https://xkcd.com/1354/ https://xkcd.com/1354/
- thaumaturgy 11y agoPartly. They were using their own allocator (openssl_malloc()), but even then they would've been OK if it weren't for the OBO error elsewhere in the heartbeat implementation. If they were using an OS-supplied malloc() instead of openssl_malloc(), the bug would've still been exploitable on some operating systems, but not others. Either way, "don't write your own allocator" is a good lesson to learn. Unless, of course, you're doing it for fun. In which case, efficient heap management really is a neat exercise.
- ojn 11y agoIt also makes the assumption that it's a little-endian system. On a big-endian system, the high order byte of the timestamp would be modified, which would probably be too obvious.
- patrickmay 11y agoI like the original Ken Thompson sneakiness in C (http://electronicdesign.com/dev-tools/thompson-ritchie-and-kernighan-fathers-c http://electronicdesign.com/dev-tools/thompson-ritchie-and-k...): "Also in his Turing Award lecture, he described how he had incorporated a backdoor security hole in the original UNIX C compiler. To do this, the C compiler recognized when it was recompiling itself and the UNIX login program. When it recompiled itself, it modified the compiler so the compiler backdoor was included. When it recompiled the UNIX login program, the login program would allow Thompson to always be able to log in using a fixed set of credentials."
- deleted 11y ago[deleted]
- deleted 11y ago[deleted]
- mkehrt 11y agoOriginal (PDF): https://www.ece.cmu.edu/~ganger/712.fall02/papers/p761-thompson.pdf https://www.ece.cmu.edu/~ganger/712.fall02/papers/p761-thomp...
- kenko 11y agoThat really isn't the same thing, though. That's language-agnostic conceptual sneakiness that happened to be implemented in C.
- codezero 11y agoThe description of the bug in surveil.txt in the source archive was a bit easier for me to understand, really nifty :)
- amelius 11y agoWould there be a way to do this automatically? Like a "sneaky pre-compiler"?
- deleted 11y ago[deleted]
- itistoday2 11y agoLooking at the source, this is where the alarm bells should go off in a reviewer's head: memcpy(filter->buffer, output->piu_text_utf8, sizeof(output->piu_text_utf8)); 1. memcpy is less safe than memmove and strncpy. strncpy should be used. 2. The two character arrays should use the same constant in defining their length, and that constant should be used both in the struct definitions and here in the copy operation. 3. The code is written in C in spite of it being 2014 at the time.
- rdc12 11y agoThe code is from an entry in the Underhanded C Contest, it is pretty much a given that the entries will be written in C.
- itistoday2 11y agoI am aware of that.
- deleted 11y ago[deleted]
- esmi 11y ago"In C/C++, you can use bugs in one part of a program to cause trouble in another. That’s pretty darn underhanded." I would argue every language has that property. But with C/C++ being so closely tied to the ABI of the machine perhaps they are more underhanded than others. But to me, this branding does feel a bit unfair. Still, a fun contest and an interesting read.
- spdionis 11y agoYou can always cause that kind of bug, but I would argue that C/C++ is massively more underhanded than other languages.