[------------------------------------------------------------------------] [-- Uninformed Research -- informative information for the uninformed. --] [------------------------------------------------------------------------] [-- Genre : Development --] [-- Name : elfsign --] [-- Desc : ELF binary signing and verification --] [-- Url : http://www.uninformed.org/ --] [-- Use : BINARY SECURITY --] [------------------------------------------------------------------------] [-- Author : skape (mmiller@hick.org) --] [-- Date : 01/29/2003 --] [------------------------------------------------------------------------] [-- Table of contents: --] 1) Overview 2) Signing 2.1) The Concept 2.2) Calculating Checksums 2.3) Signing the Binary 3) Verifying 4) Using elfsign and elfverify 5) Conclusion 6) References 7) License [-- 1) Overview --] The concept of binary security is most definitely not a new concept. In the age of viruses and mass anoniminity amongst internet goers, the requirement to verify not only the maker of a given binary but also that the code within it has been unmodified is of quite a great importance. In open source communities most would shrug off the need for such binary signing, but, as we see such open source communities progress towards centralized precompiled packages (redhat, debian, et. all), one should begin to realize that taking the word of another when it comes to binary security is not at all wise. Although most unix users have yet to see many elf viruses in at all the same way as they see windows viruses, the potential for such things to occur is growing ever nearer. In order to quench this problem, to prevent it before it's even allowed to mature into actuality, we must come up with a way to fend off the possibility that a binary package will be installed and be able to run binaries which may potentially contain a virus. Approaches to such a system range from the all-but-pointless md5sum technique whereby a user can verify that a given packages md5 matches that of the md5 the server told it to expect. Another approach would be much the same, but instead of listening to what the server thinks the md5 should be, you listen to a trusted third party server which contains a database of files and md5sums. The second solution sounds strikingly similar to another form of authentication. What could it be? In this instance, it happens to be PKI. With the advent of PKI people were empowered with the ability to identify, encrypt and verify others in a de-centralized and secure fashion. One could send a message to another person and encrypt the message in such a format that only the destined person would be able to read it. One could also verify that a message sent to them was actually sent by the sender. If one were to take the above techniques and apply them instead to the field of binary security, the advantages could potentially be quite grand. In fact, the technique is already being applied. Microsoft has, with their patented Authenticode technology, built a mechanism by which windows binaries can be digitally signed and validated against trusted certificate authorities (CAs). The details about Microsoft's Authenticode technology and how it actually works can be found in the ``References`` section. In the following sections I plan to explain how in both general and specific the concept of how ELF binary signing and validation work. [-- 2) Signing --] I deliberated over how to start this section for some time before deciding that the most direct route would be that of symbolism. Let me present the following analogy that I will reference throughout this section: In modern America fast food has all-but-replaced the dinner table for a means of consumption. As such, the need for common, standard brand names in the fast food industry is a must in order to have customers associate good food with a name. A few examples include Wendy's, McDonalds, and Taco Bell. For those of you who partake in fast food, your mind is instantly able to connect the words with the type and quality of food. The way such fast food companies work is by selling franchise. When a person wishes to run their own franchise, they apply for a CERTIFICATE. This CERTIFICATE uniquely identifies them and gives them access to Wendy's menus, propoganda, and other internal information. Without a certificate that was ISSUED by Wendy's, franchise operators or common people have no access to these things because they are not TRUSTED. The reason they are not trusted is because Wendy's does not trust them. With that said, it's not at all uncommon to encounter a franchise of a larger fast food restraunt that makes horrible food. When the Wendy's on 8th street begins to gain a reputation for having rats in place of meat paddies, word will spread that said Wendy's should no longer be TRUSTED. The issuer may even REVOKE that franchises CERTIFICATE, the thing that uniquely identifies them. On the other hand, though, all other Wendy's franchises are TRUSTED by default due to the fact that people, by default, trust Wendy's, the ISSUER. So to summarize, you have an entity (Wendy's) which issues certificates to lesser entities (Franchises). These certificates are trusted because people trust the issuer (Wendy's). If a restraunt opens up and is not a Wendy's restraunt, they are not trusted by default because they do not have a certificate. Now that you're hungry, I felt it might be best to hurtle you back into reality before your attention diverges away from this paper and instead to your stomache. The above analogy was meant to give a brief explanation of a real worth example of signing and validation. Hopefully it made sense. [-- 2.1) The Concept --] The following are the goals for signing a binary: * Be able to tell that the binary has not been modified. * Be able to tell who it was that created the binary. Let's start with the first problem. [-- 2.2) Calculating Checksums --] In order to show that the binary has not been modified, we need to take all of the data in the binary and represent it in some fashion. A perfect example of a way to represent it would be a one-way hash, like MD5. If we were to take the contents of the ELF binary, such as the section headers, program headers, and the section header contents and accumlate them into an MD5 hash, we will have successfully come up with a representation for our binary in its current state. Now that we have our MD5 hash of the binary, we've successfully solved the first problem. Partly solved, at least. We still need some way to prove that the binary has not been modified. If we were simply to put the hash into the ELF binary, all it would take is for someone to replace that MD5 with their own MD5 of the modified binary, thus allowing them to circumvent what we were trying to accomplish. It would seem that we need some form of encryption, but that form must require no password for validation. How could we possibly do that? Well, this begins to tie in with the signing aspect. We've successfully come up with a hash that proves our binary has been unmodified, but we need some way to say that 'Entity X' claims that this binary is unmodified and here is its proof. The proof being the MD5 sum. [-- 2.3) Signing the Binary --] The signing of the binary begins to tie into the Wendy's analogy. We need to establish a trust heirarchy in order to make things work easily and securely. An example of a trust heirarchy as it relates to certificates is VeriSign. VeriSign is what's known as a Certificate Authority (CA). They're like Wendy's the corporation from the analogy above. They issue certificates to other entities and, in so doing, cause those entities to become 'trusted' due to the fact that VeriSign is trusted. For those of you who aren't familiar with certificates, certificates hold information about a given entity, like their company name, group name, email address, expiration date, and a few other things. Let's say for the sake of simplicity that my company is Bob, Inc. I've been given a certificate that has been issued by VeriSign and now I want to use my certificate to sign my binary applications. I've already calculated my checksums, all that's left for me to do is to do the actual signing. Now this part that I'm about to describe is quite technical so in order to keep this document in scope, I've voted to give a general overview of the signing process. The basic jist of signing is this: 1) Calculate the MD5 checksum for a given binary. 2) Take your certificate and private key that VeriSign (or any other CA) issued you and 'sign' the checksum. 3) Take the signed data and write it to the binary in its own elf section. I realize that the second step describes quite a complex operation, but I like to consider that operation a sort of black box. OpenSSL provides an interface to such a black box via the X509_sign function. This function allows one to sign arbitrary data with a given certificate and private key combination. The data signed can be written out in PEM format and then written to the elf section created for the signature in the binary. A signed binary might look something like this: # readelf -S /root/ownme | grep sig [27] .sig PROGBITS 00000000 001004 00077f # readelf -x 27 /root/ownme Hex dump of section '.sig': 0x00000000 3753434b 50204e49 4745422d 2d2d2d2d -----BEGIN PKCS7 0x00000010 6f4b4a59 515a4649 494d0a2d 2d2d2d2d -----.MIIFZQYJKo 0x00000020 6a564649 496f4363 51414e63 7668495a ZIhvcNAQcCoIIFVj 0x00000030 67424a41 7a437845 51414349 56424343 CCBVICAQExCzAJBg 0x00000040 53434773 444d4155 6747434d 67447255 UrDgMCGgUAMDsGCS 0x00000050 42754161 41484551 440a3362 49534771 qGSIb3.DQEHAaAuB 0x00000060 63355256 4c303557 5a303532 62447843 CxDb250ZW50LVR5c 0x00000070 61687847 63765148 656c5248 49365547 GU6IHRleHQvcGxha 0x00000080 764c7868 3769486c 554b3067 434e3457 W4NCg0KUlHi7hxLv 0x00000090 4343716d 456b7769 0a61474f 7033564d MV3pOGa.iwkEmqCC 0x000000a0 4441616d 4349494d 774d6767 77517a41 AzQwggMwMIICmaAD ... [-- 3) Verifying --] Verification of a signed binary is a less daunting task than the signing part of it. The procedure entails the following: 1) Calculate the MD5 checksum of the binary, skipping over the signature section of the elf binary. 2) Extract the signature stored in the signature section and determine if the binary was signed and issued by a trusted authority. 3) Decrypt the signed checksum in the binary and compare it with your calculated checksum. If they match the binary has not been modified. Once again I've left a kind of black box for steps 2 and 3. In OpenSSL there is a function called X509_verify which will verify that a given signature (extracted from the elf section) was signed by a given certificate authority as well as decrypt the signed data and give you the buffer to work with in plaintext. So, simply enough with one function call you're able to take care of both steps 2 and 3. [-- 4) Using elfsign and elfverify --] Using elfsign to sign a binary image is quite easy. Let's say that I wish to sign the binary /root/ownme with my certificate. My certificate is for my company, 'Test Company', and my certificate was issued by 'Uninformed Research'. In order to sign /root/ownme, I would do the following: # ./elfsign -f /root/ownme -c tester.cert -p tester.privkey Key Password: # readelf -S /root/ownme | grep sig [27] .sig PROGBITS 00000000 001004 00077f Well, it looks like we've successfully signed /root/ownme, but let's make sure. # ./elfverify -f /root/ownme -c ca.crt OK # Sweet, it appears everything is kosher. elfverify has validated the fact that /root/ownme is signed and the certificate that signed the binary was issued the ca.crt. What happens if I don't tell elfverify what root CA was used? # ./elfverify -f /root/ownme Issuer: C=US, ST=Kansas, L=Overland Park, O=Uninformed Research, OU=R&D Signer: C=US, ST=NY, O=Test Company, OU=Test Group, CN=Tester Issuer is not trusted, would you like to trust them? [y/N] Y OK It presents me with a prompt letting me know that the certificate was not trusted by default and asks me if I would like to trust it which, in this instance, I said yes to. What happens if we modify /root/ownme? # diff /root/bob /root/ownme Binary files /root/bob and /root/ownme differ # ./elfverify -f /root/bob -c ca.crt FAIL (The binary digest did not match the signed digest.) What happens if we try to verify a binary that hasn't even been signed? # gcc /root/a.c -o /root/a -ldl -lpthread # ./elfverify -f /root/a -c ca.crt FAIL (The binary is not signed.) [-- 5) Conclusion --] Although this document doesn't go indepth into the concepts of PKI, certificate authorities and the actual process of signing and verifying data, it was my hope that one would be able to walk away with an understanding of how binary signatures are possible and why it is that they are useful in both opened and closed source communities. In the future I would hope to see an introduction of a root certificate authority for open source projects, or, at least for operating system distributions that distribute binary applications. I would also like to see an adoption of an automated technique for authenticating linux binaries at execution time via a kernel module. I think that this support (optional of course), would lend great amounts of security to local unix installations and will help to protect against malicious users overwriting system binaries. [-- 6) References --] * Authenticode Concept http://www.tutorialbox.com/tutors/J++/ch23.htm [-- 7) License --] elfsign is property of Uninformed Research and is freely distributable under the conditions that: 1) Modification of the code retains credit to the original author(s) 2) The authors may not be blamed for any damages incurred from the use of this software.