Techniques for obscuring sensitive strings in C++
Asked Answered
E

14

105

I need to store sensitive information (a symmetric encryption key that I want to keep private) in my C++ application. The simple approach is to do this:

std::string myKey = "mysupersupersecretpasswordthatyouwillneverguess";

However, running the application through the strings process (or any other that extracts strings from a binary app) will reveal the above string.

What techniques should be used to obscure such sensitive data?

Edit:

OK, so pretty much all of you have said "your executable can be reverse engineered" - of course! This is a pet peeve of mine, so I'm going to rant a bit here:

Why is it that 99% (OK, so perhaps I exaggerate a little) of all security-related questions on this site are answered with a torrent of "there is no possible way to create a perfectly secure program" - that is not a helpful answer! Security is a sliding scale between perfect usability and no security at one end, and perfect security but no usability at the other.

The point is that you pick your position on that sliding scale depending on what you're trying to do and the environment in which your software will run. I'm not writing an app for a military installation, I'm writing an app for a home PC. I need to encrypt data across an untrusted network with a pre-known encryption key. In these cases, "security through obscurity" is probably good enough! Sure, someone with enough time, energy and skill could reverse-engineer the binary and find the password, but guess what? I don't care:

The time it takes me to implement a top-notch secure system is more expensive than the loss of sales due to the cracked versions (not that I'm actually selling this, but you get my point). This blue-sky "lets do it the absolute best way possible" trend in programming amongst new programmers is foolish to say the least.

Thank you for taking the time to answer this question - they were most helpful. Unfortunately I can only accept one answer, but I've up-voted all the useful answers.

Easternmost answered 30/10, 2009 at 8:31 Comment(10)
Perhaps if you describe what you are trying to accomplish with the encryption key we can offer advice on how to avoid needing to do this.Lamb
Exact duplicate: stackoverflow.com/questions/926172/…Zackaryzacks
@Kirill: it's hard to call this question exactly the same as the one you mention. Indeed, the idea is the same. The question is not.Janey
@xtofl, you are free not to vote. To me it looks like two identical questions.Zackaryzacks
It does seem like a duplicate - and I apologise for that. The duplicate did not show up in the searches, so I asked anyway. Interesting that this questions has more, and different answers...Easternmost
(Unrelated to rant) The very definition of a "private key" is the one half of a public/private key pair that you don't give out. In this case too, the private key is the one you keep on your server; the one in your client app is public.Flashcube
@Flashcube - thansk, I hadn't noticed that I had used that phrase. I realise that "private key" usually refers tot eh private part of an asynchronouse key pair. However, in this context I mean it literally - a key that I wish to keep private. I will edit to remove any confusion. Thanks again!Easternmost
Re your rant: the reason we tell you it can't be done is that you're trying to solve the wrong problem. Now that we actually know what you're trying to do - "encrypt data across an untrusted network with a pre-known encryption key" - we can tell you the correct way of solving it: negotiating a unique key. Because your key is the same for all users and easily available, what you are doing is not "encrypting" the data, you're simply "obfuscating it;" in which case, you might as well just XOR it with some number.Rapallo
+1 for the edit. I am, right now, frustrated at the same level as you would be, as seemingly, while editing your question.Epidemic
Duplicate of: How to hide a string in binary code? (not closing though, after 12 years of active work).Foggia
A
47

Basically, anyone with access to your program and a debugger can and will find the key in the application if they want to.

But, if you just want to make sure the key doesn't show up when running strings on your binary, you could for instance make sure that the key is not within the printable range.

Obscuring key with XOR

For instance, you could use XOR to split the key into two byte arrays:

key = key1 XOR key2

If you create key1 with the same byte-length as key you can use (completely) random byte values and then compute key2:

key1[n] = crypto_grade_random_number(0..255)
key2[n] = key[n] XOR key1[n]

You can do this in your build environment, and then only store key1and key2 in your application.

Protecting your binary

Another approach is to use a tool to protect your binary. For instance, there are several security tools that can make sure your binary is obfuscated and starts a virtual machine that it runs on. This makes it hard(er) to debug, and is also the convential way many commercial grade secure applications (also, alas, malware) is protected.

One of the premier tools is Themida, which does an awesome job of protecting your binaries. It is often used by well known programs, such as Spotify, to protect against reverse engineering. It has features to prevent debugging in programs such as OllyDbg and Ida Pro.

There is also a larger list, maybe somewhat outdated, of tools to protect your binary.
Some of them are free.

Password matching

Someone here discussed hashing password+salt.

If you need to store the key to match it against some kind of user submitted password, you should use a one-way hashing function, preferrably by combining username, password and a salt. The problem with this, though, is that your application has to know the salt to be able to do the one-way and compare the resulting hashes. So therefore you still need to store the salt somewhere in your application. But, as @Edward points out in the comments below, this will effectively protect against a dictionary attack using, e.g, rainbow tables.

Finally, you can use a combination of all the techniques above.

Aristocratic answered 30/10, 2009 at 8:37 Comment(6)
If its a password that the user has to enter, store the hash+salt of the password. See answer below.Eldwun
@hapalibashi: How do you store the salt securely in your application? I don't think the OP needed a one-way password matching system, just a generalized way of storing static keys.Aristocratic
I've found when looking at disassembled programs there typically aren't very many XORs, so if you're hoping to use XOR to obscure something, keep in mind they draw attention to themselves.Redfin
@kb - that's an interesting point. I'd guess you'd see bitwise ands and ors happening a lot more than xor. a ^ b == (a & ~b) || (~a & b)Remunerative
Knowing the salt value doesn't usually give an adversary an advantage - the point of the salt is to avoid a "dictionary attack", whereby the attacker has pre-computed the hashes for many likely inputs. Using a salt forces them to pre-compute their dictionary with a new one based on the salt. If each salt is used only once, then the dictionary attack becomes completely useless.Verbiage
It hardly needs to be a crypto-grade PRNG; a simple LFSR or xorshift+ would be fine for defeating static analysis with strings; if an "attacker" finds this XOR code with a debugger they can just look at the result afterwards. You might want to force the high bit of each byte to be set (tmp |= 0x80) so XORing makes ASCII data non-printable, and messes up UTF-8 multi-byte characters. Or maybe it's better for it not to have a pattern like that, and let it be a mix of printable and unprintable.Rhachis
B
13

There is a (very light) header-only project obfuscate made by adamyaxley that works perfectly. It is based on lambda functions and macros and it encrypts strings litteral with a XOR cipher at compile-time. If needed, we can change the seed for each string.

The following code will not store the string "hello world" in the compiled binary.

#include "obfuscate.h"

int main()
{
  std::cout << AY_OBFUSCATE("Hello World") << std::endl;
  return 0;
}

I have tested with c++17 and visual studio 2019, and check via IDA and I confirm the string is hidden. One precious advantage compared to ADVobfuscator is that it is convertible to a std::string (while being still hidden in the compiled binary) :

std::string var = AY_OBFUSCATE("string");
Bondstone answered 2/5, 2020 at 16:15 Comment(7)
Obfuscate can be easily defeated on ARM by simply patching to MOV R0, 1 + BX LRPlump
@SouravBanerjee That is very interesting, from where could you get this information? I am trying to find reviews for obfuscate's library, which is very convenient but the use is not so widespread.Leningrad
@Leningrad Obfuscate is basically single byte XOR which is trivial to defeat. Build any binary using obfuscate with symbols present in there, with little to no effort you can find the routine that loads the XOR key before performing the de-obfuscation operation. Avoid single byte XOR if you're using it to obfuscate important stuff like a private key or an api key.Plump
@SouravBanerjee Thanks for the insight! Is the solution proposed here better? https://mcmap.net/q/20507/-compile-time-string-encryption-using-constexprLeningrad
@Leningrad uniqueness is important when it comes to obfuscating strings. I would suggest you choose a combination of any or all of the following 1. Multiple hashes 2. Simple / complex rotation 3. String generation using dictionary 4. String inversion using dictionary 5. Some variation of Secure Hash Algorithm 6. XORPlump
@SouravBanerjee "Build any binary using obfuscate with symbols present in there". I would not recommend that you distribute your binary with symbols present. Additionally, the cipher for Obfuscate can be customized and the key can be randomly generated each time you use it (see github.com/adamyaxley/Obfuscate/issues/16).Furnivall
@AdamYaxley understandable, however, I simply asked the OP to build using symbols so that they can correlate the disassembly with that of a stripped binary to find the deobfuscation routine faster. I love obfuscate and have used it for simple strings like API endpoints successfully, however, would not recommend this mechanism for truly sensitive data like API keys and private keys.Plump
B
10

First of all, realise that there is nothing you can do that will stop a sufficiently determined hacker, and there are plenty of those around. The protection on every game and console around is cracked eventually, so this is only a temporary fix.

There are 4 things you can do that will increase you chances of staying hidden for a while.

1) Hide the elements of the string in some way -- something obvious like xoring ( the ^ operator) the string with another string will be good enough to make the string impossible to search for.

2) Split the string into pieces -- split up your string and pop bits of it into strangely named methods in strange modules. Don't make it easy to search through and find the method with the string in it. Of course some method will have to call all these bits, but it still makes it a little harder.

3) Don't ever build the string in memory -- most hackers use tools that let them see the string in memory after you have encoded it. If possible, avoid this. If for example you are sending the key off to a server, send it character by character, so the whole string is never around. Of course, if you are using it from something like RSA encoding, then this is trickier.

4) Do an ad-hoc algorithm -- on top of all this, add a unique twist or two. Maybe just add 1 to everything you produce, or do any encryption twice, or add a sugar. This just makes it a little harder for the hacker who already knows what to look for when someone is using, for example, vanilla md5 hashing or RSA encryption.

Above all, make sure it isn't too important when (and it will be when if you application becomes popular enough) your key is discovered!

Boswell answered 30/10, 2009 at 8:55 Comment(0)
H
7

A strategy i've used in the past is to create an array of seemingly-random characters. You initially insert, and then locate your particular characters with a algebraic process where each step from 0 to N will yield a number < size of the array which contains the next char in your obfuscated string. (This answer is feeling obfuscated now!)

Example:

Given an array of chars (numbers and dashes are for reference only)

0123456789
----------
ALFHNFELKD
LKFKFLEHGT
FLKRKLFRFK
FJFJJFJ!JL

And an equation whose first six results are: 3, 6, 7, 10, 21, 47

Would yield the word "HELLO!" from the array above.

Hays answered 30/10, 2009 at 10:36 Comment(2)
Good idea - I guess you could further improve it by using non-printing characters in the array...Easternmost
Where's the O??Inhibit
B
5

I agree with @Checkers, your executable can be reverse-engineered.

A bit better way is to create it dynamically, for example:

std::string myKey = part1() + part2() + ... + partN();
Barnacle answered 30/10, 2009 at 8:37 Comment(3)
True, that avoids having the string revealed when searching the binary. However, your string is still resident in memory.. Your solution is probably good enough though, for what I'm doing.Easternmost
@Thomi, you can, of course, destroy it as soon as you've done with it. But still, it isn't the best way to handle sensitive strings.Barnacle
...since destroying it doesn't actually guarantee that the memory will be re-used straight away.Easternmost
T
4

Of course, storing private data in software which is shipped to the user is always a risk. Any sufficiently educated (and dedicated) engineer could reverse engineer the data.

That being said, you can often make things secure enough by raising the barrier which people need to overcome to reveal your private data. That's usually a good compromise.

In your case, you could clutter your strings with non-printable data, and then decode that at runtime using a simple helper function, like this:

void unscramble( char *s )
{
    for ( char *str = s + 1; *str != 0; str += 2 ) {
        *s++ = *str;
    }
    *s = '\0';
}

void f()
{
    char privateStr[] = "\001H\002e\003l\004l\005o";
    unscramble( privateStr ); // privateStr is 'Hello' now.

    string s = privateStr;
    // ...
}
Trost answered 30/10, 2009 at 8:47 Comment(0)
E
4

I've created a simple encryption tool for strings, it can automatically generate encrypted strings and has a few extra options to do that, a few examples:

String as a global variable:

// myKey = "mysupersupersecretpasswordthatyouwillneverguess";
unsigned char myKey[48] = { 0xCF, 0x34, 0xF8, 0x5F, 0x5C, 0x3D, 0x22, 0x13, 0xB4, 0xF3, 0x63, 0x7E, 0x6B, 0x34, 0x01, 0xB7, 0xDB, 0x89, 0x9A, 0xB5, 0x1B, 0x22, 0xD4, 0x29, 0xE6, 0x7C, 0x43, 0x0B, 0x27, 0x00, 0x91, 0x5F, 0x14, 0x39, 0xED, 0x74, 0x7D, 0x4B, 0x22, 0x04, 0x48, 0x49, 0xF1, 0x88, 0xBE, 0x29, 0x1F, 0x27 };

myKey[30] -= 0x18;
myKey[39] -= 0x8E;
myKey[3] += 0x16;
myKey[1] += 0x45;
myKey[0] ^= 0xA2;
myKey[24] += 0x8C;
myKey[44] ^= 0xDB;
myKey[15] ^= 0xC5;
myKey[7] += 0x60;
myKey[27] ^= 0x63;
myKey[37] += 0x23;
myKey[2] ^= 0x8B;
myKey[25] ^= 0x18;
myKey[12] ^= 0x18;
myKey[14] ^= 0x62;
myKey[11] ^= 0x0C;
myKey[13] += 0x31;
myKey[6] -= 0xB0;
myKey[22] ^= 0xA3;
myKey[43] += 0xED;
myKey[29] -= 0x8C;
myKey[38] ^= 0x47;
myKey[19] -= 0x54;
myKey[33] -= 0xC2;
myKey[40] += 0x1D;
myKey[20] -= 0xA8;
myKey[34] ^= 0x84;
myKey[8] += 0xC1;
myKey[28] -= 0xC6;
myKey[18] -= 0x2A;
myKey[17] -= 0x15;
myKey[4] ^= 0x2C;
myKey[9] -= 0x83;
myKey[26] += 0x31;
myKey[10] ^= 0x06;
myKey[16] += 0x8A;
myKey[42] += 0x76;
myKey[5] ^= 0x58;
myKey[23] ^= 0x46;
myKey[32] += 0x61;
myKey[41] ^= 0x3B;
myKey[31] ^= 0x30;
myKey[46] ^= 0x6C;
myKey[35] -= 0x08;
myKey[36] ^= 0x11;
myKey[45] -= 0xB6;
myKey[21] += 0x51;
myKey[47] += 0xD9;

As unicode string with decryption loop:

// myKey = "mysupersupersecretpasswordthatyouwillneverguess";
wchar_t myKey[48];

myKey[21] = 0x00A6;
myKey[10] = 0x00B0;
myKey[29] = 0x00A1;
myKey[22] = 0x00A2;
myKey[19] = 0x00B4;
myKey[33] = 0x00A2;
myKey[0] = 0x00B8;
myKey[32] = 0x00A0;
myKey[16] = 0x00B0;
myKey[40] = 0x00B0;
myKey[4] = 0x00A5;
myKey[26] = 0x00A1;
myKey[18] = 0x00A5;
myKey[17] = 0x00A1;
myKey[8] = 0x00A0;
myKey[36] = 0x00B9;
myKey[34] = 0x00BC;
myKey[44] = 0x00B0;
myKey[30] = 0x00AC;
myKey[23] = 0x00BA;
myKey[35] = 0x00B9;
myKey[25] = 0x00B1;
myKey[6] = 0x00A7;
myKey[27] = 0x00BD;
myKey[45] = 0x00A6;
myKey[3] = 0x00A0;
myKey[28] = 0x00B4;
myKey[14] = 0x00B6;
myKey[7] = 0x00A6;
myKey[11] = 0x00A7;
myKey[13] = 0x00B0;
myKey[39] = 0x00A3;
myKey[9] = 0x00A5;
myKey[2] = 0x00A6;
myKey[24] = 0x00A7;
myKey[46] = 0x00A6;
myKey[43] = 0x00A0;
myKey[37] = 0x00BB;
myKey[41] = 0x00A7;
myKey[15] = 0x00A7;
myKey[31] = 0x00BA;
myKey[1] = 0x00AC;
myKey[47] = 0x00D5;
myKey[20] = 0x00A6;
myKey[5] = 0x00B0;
myKey[38] = 0x00B0;
myKey[42] = 0x00B2;
myKey[12] = 0x00A6;

for (unsigned int fngdouk = 0; fngdouk < 48; fngdouk++) myKey[fngdouk] ^= 0x00D5;

String as a global variable:

// myKey = "mysupersupersecretpasswordthatyouwillneverguess";
unsigned char myKey[48] = { 0xAF, 0xBB, 0xB5, 0xB7, 0xB2, 0xA7, 0xB4, 0xB5, 0xB7, 0xB2, 0xA7, 0xB4, 0xB5, 0xA7, 0xA5, 0xB4, 0xA7, 0xB6, 0xB2, 0xA3, 0xB5, 0xB5, 0xB9, 0xB1, 0xB4, 0xA6, 0xB6, 0xAA, 0xA3, 0xB6, 0xBB, 0xB1, 0xB7, 0xB9, 0xAB, 0xAE, 0xAE, 0xB0, 0xA7, 0xB8, 0xA7, 0xB4, 0xA9, 0xB7, 0xA7, 0xB5, 0xB5, 0x42 };

for (unsigned int dzxykdo = 0; dzxykdo < 48; dzxykdo++) myKey[dzxykdo] -= 0x42;
Evangelinaevangeline answered 6/11, 2009 at 17:1 Comment(1)
Nah, I've used stringencrypt.com website to do the work. It has examples for C/C++ stringencrypt.com/c-cpp-encryption you might consider using it to automate simple string encryption.Grivation
H
2

Somewhat dependent on what you are trying to protect as joshperry points out. From experience, I would say that if it is part of some licensing scheme to protect your software then don't bother. They will eventially reverse engineer it. Simply use a simple cipher like ROT-13 to protect it from simple attacks (line running strings over it). If it is to secure users sensitive data I would be questioning whether protecting that data with a private key stored locally is a wise move. Again it comes down to what you are trying to protect.

EDIT: If you are going to do it then a combination of techniques that Chris points out will be far better than rot13.

Hassiehassin answered 30/10, 2009 at 9:1 Comment(0)
A
2

As was said before, there's no way to totally protect your string. But there are ways to protect it with a reasonable safety.

When I had to do this, I did put some innocent looking string into the code (a copyright notice, for example, or some faked user prompt or anything else that won't be changed by someone fixing unrelated code), encrypted that using itself as a key, hashed that (adding some salt), and used the result as a key to encrypt what I actually wanted to encrypt.

Of course this could be hacked, but it does take a determined hacker to do so.

Antofagasta answered 30/10, 2009 at 10:5 Comment(3)
Good idea - another form of obscurity is using a string that is still reasonably strong (long, punctuation and all that jazz) but does not obviously look like a password.Easternmost
I was thinking, a way to attack this would be to modify (programmatically) all the strings found in the executable and seeing by modifying which string the decryption fails. With a divide and conquer algorithm it would be quick to find out which is the root string.Leningrad
@Leningrad As the first sentence in the top-voted answer says: "Basically, anyone with access to your program and a debugger can and will find the key in the application if they want to." We're talking about shades of deterrence here.Antofagasta
C
1

If you are on windows user DPAPI, http://msdn.microsoft.com/en-us/library/ms995355.aspx

As a previous post said if you are on mac use the keychain.

Basically all of these cute ideas about how to store your private key inside your binary are sufficiently poor from a security perspective that you should not do them. Anyone getting your private key is a big deal, don't keep it inside your program. Depending on how import your app is you can keep your private keys on a smart card, on a remote computer your code talks to or you can do what most people do and keep it in a very secure place on the local computer (the "key store" which is kind of like a weird secure registry) that is protected by permissions and all the strength of your OS.

This is a solved problem and the answer is NOT to keep the key inside your program :)

Corinthian answered 2/11, 2009 at 20:0 Comment(0)
A
1

Try this. The source code explains how to encrypt and decrypt on the fly all strings in a given Visual Studio c++ project.

Adolescence answered 19/11, 2015 at 19:56 Comment(0)
E
1

One method I recently tried is:

  1. Take hash (SHA256) of the private data and populate it in code as part1
  2. Take XOR of private data and its hash and populate it in code as part2
  3. Populate data: Don't store it as char str[], but populate on runtime using assignment instructions (as shown in macro below)
  4. Now, generate the private data on run time by taking the XOR of part1 and part2
  5. Additional step: Calculate hash of generated data and compare it with part1. It will verify the integrity of private data.

MACRO to populate data:

Suppose, private data is of 4 bytes. We define a macro for it which saves the data with assignment instructions in some random order.

#define POPULATE_DATA(str, i0, i1, i2, i3)\
{\
    char *p = str;\
    p[3] = i3;\
    p[2] = i2;\
    p[0] = i0;\
    p[1] = i1;\
}

Now use this macro in code where you need to save part1 and part2, as follows:

char part1[4] = {0};
char part2[4] = {0};
POPULATE_DATA(part1, 1, 2, 3, 4); 
POPULATE_DATA(part2, 5, 6, 7, 8);
Epidemic answered 24/8, 2016 at 9:32 Comment(0)
J
0

Instead of storing private key in your executable, you may want to request it from the user and store it by means of an external password manager, something similar to Mac OS X Keychain Access.

Jemy answered 30/10, 2009 at 8:40 Comment(2)
well yes and no.. normally I'd agree with you, but in this case I'm trying to hide this from the user of the software, so storing it in an external system isn't a great idea (many keychain systems can expose passwords as plain text to users given suitable authorisation). Keychain software is great for user passwords, but not so great for application encryption keys.Easternmost
if it feels insecure (though given your clarifications maybe it is adequate) you could combine a keychain and something hardcoded :)Eldwun
E
0

Context dependent but you could just store the hash of the key plus a salt (constant string, easy to obscure).

Then when (if) the user enters the key, you add the salt, calculate the hash and compare.

The salt is probably unnecessary in this case, it stops a brute-force dictionary attack if the hash can be isolated (a Google search has also been know to work).

A hacker still only has to insert a jmp instruction somewhere to bypass the whole lot, but that's rather more complicated than a simple text search.

Eldwun answered 30/10, 2009 at 9:22 Comment(1)
This is an encryption key, not a password hash. I need the actual key to encode & decode data. The user never sees the key, it is not ever stored outside the binary.Easternmost

© 2022 - 2024 — McMap. All rights reserved.