9

We are under a PCI PA-DSS certification and one of its requirements is to avoid writing clean PAN (card number) to disk. The application is not writing such information to disk, but if the operating system (Windows, in this case) needs to swap, the memory contents is written to page file. Therefore the application must clean up the memory to prevent from RAM capturer services to read sensitive data.

There are three situations to handle:

  • heap allocation (malloc): before freeing the memory, the area can be cleaned up with memset
  • static or global data: after being used, the area can be cleaned up using memset
  • local data (function member): the data is put on stack and is not accessible after the function is finished

For example:

void test()
{
  char card_number[17];

  strcpy(card_number, "4000000000000000");
}

After test executes, the memory still contains the card_number information.

One instruction could zero the variable card_number at the end of test, but this should be for all functions in the program.

memset(card_number, 0, sizeof(card_number));

Is there a way to clean up the stack at some point, like right before the program finishes?

Juliano
  • 821
  • 6
  • 21
  • 5
    You've tagged the question with c++ and c. Assuming c++, consider using standard containers such as `std::vector` with custom allocators. You can implement a custom allocator to zero out all memory just before it's freed. It then becomes impossible to forget to zero out the memory. – François Andrieux Jun 01 '17 at 18:46
  • Our application is developed in C++, but has a lot of legacy code. – Juliano Jun 01 '17 at 18:48
  • @Juliano Are you looking for a solution to apply to your application, to the legacy code or both? – François Andrieux Jun 01 '17 at 18:50
  • Security is the reason and yes you should clean your mess if you are allocating memory with legacy code, if the data is not sensitive, the system will eventually reuse the memory... – DIEGO CARRASCAL Jun 01 '17 at 18:51
  • 4
    At least in C++, trying to zero out something you will no longer use is very hard to accomplish. If you running on Windows MS give you [`SecureZeroMemory`](https://msdn.microsoft.com/en-us/library/aa366877(VS.85).aspx) – NathanOliver Jun 01 '17 at 18:51
  • Maybe calling a function which has a big enough local array, set to some chaff value by a loop could make sure that at least all previously used stack is overwritten. Call it before leaving the program. Some experimenting to get the array really settup and really filled is necessary, compilers easily spot that something is completely ignored.... Using "volatile" keyword generously should be part of the attempt. This does of course require some kind of stack-depth measurement beforehand. – Yunnosch Jun 01 '17 at 18:51
  • 1
    If you mark the data `volatile` and wipe it with from an *opaque* function (through a function pointer to avoid inlining/optimization) in the destructor, that should clean up the stack. Not sure how to deal with swap file except to use an encrypted swap file. – Galik Jun 01 '17 at 18:54
  • @FrançoisAndrieux both – Juliano Jun 01 '17 at 18:56
  • I agree with @Galik The swapfile is a problem, swapping can hit any time during execution, especially between using and securtiy-deleting. Maybe looking for a way to prevent swapping for your program is required. By the way, is hardware access within the threat scope? I.e. do you need to protect from attackers getting hold of the swap-file harddisk? – Yunnosch Jun 01 '17 at 18:58
  • 1
    I'm wondering, can you bypass these concerns by only storing the data in a securely hashed/encrypted manor? Then if you need to compare it against something you hash/encrypt that something and then compare the hashes. – NathanOliver Jun 01 '17 at 19:01
  • 1
    At least on Linux, a privileged process can lock memory pages so that they can't be swapped. GPG for example [does this](https://www.gnupg.org/faq/gnupg-faq.html#insecure_memory) with private key material. – cdhowie Jun 01 '17 at 19:03
  • How about disabling page file? – user7860670 Jun 01 '17 at 19:03
  • In plain C legacy code, this *might* be one of the actual uses of `alloca()` combined with a grain of `memset()` which is normally heavily advised against. You could write a macro like `#define CLEAR_STACK {memset(alloca(SEC_FRAME_SIZE), 0, sizeof (char));}` that you call after you have returned from a PAN-handling function, or (with a much larger `SEC_FRAME_SIZE`, close to the maximum stack size) at the end of your program. – tofro Jun 01 '17 at 19:04
  • 3
    Note that calling `memset` to wipe sensitive data is *not* secure as the call is allowed to be optimized away. Use `memset_s` to avoid that. – user2722968 Jun 01 '17 at 19:35
  • On Linux, you could use [`mlockall`](http://man7.org/linux/man-pages/man2/mlock.2.html) to lock the process's memory. Windows doesn't seem to have a equivalent however, short of disabling the page file altogether. – dbush Jun 01 '17 at 19:54
  • @Yunnosch We have to assume that the attacker can have access to the hardware – Juliano Jun 01 '17 at 21:21
  • @VTT Unfortunately I think this is not a option, because it can impact the operation. – Juliano Jun 01 '17 at 21:22
  • @NathanOliver I have to use the plain data. – Juliano Jun 01 '17 at 21:23

4 Answers4

4

Cleaning the stack right when the program finishes might be too late, it could have already been swapped out during any point at its runtime. You should keep your sentitive data only in memory locked with VirtualLock so it does not get swapped out. This has to happen before said sensitive data is read.

There is a small limit on how much memory you can lock like this so you can propably not lock the whole stack and should avoid storing sensitive data on the stack at all.

Grollicus
  • 71
  • 3
3

I assume you want to get rid of this situation below:

#include <iostream>

using namespace std;

void test()
{
    char card_number[17];
    strcpy(card_number, "1234567890123456");
    cout << "test() -> " << card_number << endl;
}

void test_trash()
{
    // don't initialize, so get the trash from previous call to test()
    char card_number[17];
    cout << "trash from previous function -> " << card_number << endl;
}

int main(int argc, const char * argv[])
{
    test();
    test_trash();
    return 0;
}

Output:

test() -> 1234567890123456
trash from previous function -> 1234567890123456

You CAN do something like this:

#include <iostream>

using namespace std;

class CardNumber
{
    char card_number[17];

public:
    CardNumber(const char * value)
    {
        strncpy(card_number, value, sizeof(card_number));
    }

    virtual ~CardNumber()
    {
        // as suggested by @piedar, memset_s(), so the compiler
        // doesn't optimize it away.
        memset_s(card_number, sizeof(card_number), 0, sizeof(card_number));
    }

    const char * operator()()
    {
        return card_number;
    }
};

void test()
{
    CardNumber cardNumber("1234567890123456");
    cout << "test() -> " << cardNumber() << endl;
}

void test_trash()
{
    // don't initialize, so get the trash from previous call to test()
    char card_number[17];
    cout << "trash from previous function -> " << card_number << endl;
}

int main(int argc, const char * argv[])
{
    test();
    test_trash();
    return 0;
}

Output:

test() -> 1234567890123456
trash from previous function ->

You can do something similar to clean up memory on the heap or static variables. Obviously, we assume the card number will come from a dynamic source instead of the hard-coded thing...

AND YES: to explicit answer the title of your question: The stack will not be cleaned automatically... you have to clean it by yourself.

Wagner Patriota
  • 5,494
  • 26
  • 49
2

I believe it is necessary, but this is only half of the problem.

There are two issues here:

  1. In principle, nothing prevents the OS from swapping your data while you are still using it. As pointed out in the other answer, you want VirtualLock on windows and mlock on linux.

  2. You need to prevent the optimizer from optimizing out the memset. This also applies to global and dynamically allocated memory. I strongly suggest to take a look at cryptopp SecureWipeBuffer.

In general, you should avoid to do it manually, as it is an error-prone procedure. Instead, consider using a custom allocator or a custom class template for secure data that can be freed in the destructor.

sbabbi
  • 11,070
  • 2
  • 29
  • 57
1

The stack is cleaned up by moving the stack pointer, not by actually popping values from it. The only mechanics are to pop the return into the appropriate registers. You must do it all manually. Also -- volatile can help you avoid optimizations on a per variable basis. You can manually pop the stack clean, but -- you need assembler to do that -- and it is not so simple to start manipulating the stack -- it is not actually your resource -- the compiler owns it as far as you are concerned.

jinzai
  • 436
  • 3
  • 9