You use the CGameEngine to hold all data (like which level is next).
I thought this was pretty obvious from the article:
virtual void HandleEvents(CGameEngine* game) = 0;
virtual void Update(CGameEngine* game) = 0;
virtual void Draw(CGameEngine* game) = 0;
These functions from CGameState specifically take a pointer to CGameEngine, so you can modify the current game data, and since there is going to be only one CGameEngine instance, it gets "shared" by all CGameState's, along with the data it holds - this is your global data for the game states. So in your example, you could simply add getter/setter functions and/or just a (public) member variable m_Level which holds the current level number to the CGameEngine class. Then, in the "menu" and "game" gamestate classes, you simply read&write to this variable. If you use getter/setter, you can also implement the setter so that it saves the last successfully played level to disk etc.
More likely, you will be changing the level (the CGameEngine::m_Level member variable) inside the CGameState::HandleEvents method, based on input from the user. HandleEvents is probably also where you call CGameEngine::ChangeState to set the next game state after the user selected a level from the menu, or after a game level was completed sucessfully (or not). You could also make all of the CGameState instances singletons, because in your example, you won't need more than one of each, and you probably won't have to re-create them every time the current state changes.
IMHO, the CGameState classes described in the article are more like helper classes. They aren't meant to hold actual game data, but to simplify management of different game states based on the game's actual current state, which you can store anywhere/anyway you want. This was a bit confusing to me at first when I started reading the article, because I always thought that the actual "game state" is the game data - i.e., the variables where you keep track of things, not the classes which handle that data. But that's OOP for you - always abstracting things that really matter.
I haven't read the whole article, but you could also implement CGameEngine to keep a list of multiple active states at the same time (even of the same type), but with only one of them drawing to the screen and accepting input (this is the "foreground" state). The ones not using the screen/input could simply be updated in the background. In this case, you could also keep the m_Level variable as a public member inside one of those background states, like the "menu" CGameState, which becomes a background state once the "playing" CGameState state is the foreground state. Then your "menu" CGameState is kept updated with the current level number while it is in the background, and when you bring it back to the foreground, it's already updated, and all you need to do is start calling it's Update() and Draw() methods. This is harder to implement though, and it shouldn't be needed in your simple menu example, but it also allows you to do cool stuff like having a PIP-view of one game state, while another one is in focus, or Fredericvo's example of crossfading between two game states. For this, the foreground CGameState's Update() and Draw() methods need to be able to call one of the background CGameState's Update() and Draw() methods, which means you either hold a pointer to that background CGameState inside the foreground CGameState, or you can use dynamic_cast to find the specific implementation of the background CGameState among the list of active background CGameState's from CGameEngine. Just be careful not to have too many game states active at the same time, which might slow down your game, or not to cause infinite recursion with the Update() and Draw() functions...
And I'm sure that there are many other ways to expand upon the example from the article...