Keygenme or Not is a cute reversing challenge I found on root-me.org. (here) It requires an username and an activation key. I particularly enjoyed this challenge and decided to make a blog post about it. The twist of this challenge is that you can solve it without writing any code, thus the name ‘Keygenme or Not’.
If you want to solve this challenge but you’re stuck you should definitely give it one more try because solving it is very easy. Come back if you’d like to see the keygen code and my thoughts about this challenge.
Let’s imagine that this is in fact a useful program with poorly implemented protection. The program KMN is registered to the user ‘root-me.org’ and will work only with their key. Using any other combination of username and key won’t make the program work, even if they’re valid.
Let’s see how KMN authenticates users, it gets the username and the key and after that it calls the _auth function. After the _auth function returns, the program checks if the authentication was successful by comparing _auth’s return value with zero. If the return value of _auth is any other value other than zero then the authentication fails.
__text:0000000100000D81 call _auth
__text:0000000100000D86 cmp eax, 0
We shouldn’t patch the cmp instruction and let the program do it’s “thing”, because the _auth sets some internal state that we need later on.
Now, I hope that you’re versed in reversing because I’ll skip some details. The only part that interests me is the loop that derives the application key from the username. I have reverse engineered this part and posted the C code in this article.
If we look at the locLastCheck we can see that the application key for the username ‘root-me.org’ is hardcoded and all you have to do to get the flag is provide KMN with it.
We can avoid the hardcoded username constraint by pathing the jz instruction to jmp, this way the program will check if the application key is correct and it will work regardless of username. And in order to build a keygen we will have to remove locLastCheck entirely and just print the computed uiSeed.
Below is my code for the keygen:
/*
* Keygen for the "KeygenMe or Not" challenge.
* */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
void exit_error(char * e) {
fprintf(stderr, "%s\n", e);
exit(EXIT_FAILURE);
}
void auth(char * username, unsigned magicNumber) {
unsigned username_length = strnlen(username, 32);
unsigned uiSeed = (username[3] ^ 0x1337) + 0x5EEDED;
unsigned uiAccumulator = 0;
while(true) {
if (uiAccumulator >= username_length) {
/* I've commented the check from below in order to get the uiSeed. This is the keygen, basically */
// if (magicNumber != uiSeed) {
// exit_error("magicNumber is not equal to uiSeed");
// }
/* I guess the block from below gives the title "Keygenme or Not" to this challenge. */
// if (magicNumber == 6235464) {
// printf("Solution found!\n");
// }
printf("Solution found: %u\n", uiSeed);
break;
} else {
/* Checks wheter username is a character between a ' '(space) and ''(empty) */
if (username[uiAccumulator] < 0x20 || username[uiAccumulator] > 0x7F) {
exit_error("err: username[uiAccumulator] < 0x20 || username[uiAccumulator] < 0x7F");
} else {
unsigned dividend = username[uiAccumulator] ^ uiSeed;
uiSeed += dividend % 0x539;
uiAccumulator += 1;
}
}
}
}
int main(int argc, char *argv[], char *envp[]) {
if (argc < 3) {
auth("root-me.org", 6235464);
} else {
auth(argv[1], atoi(argv[2]));
}
return EXIT_SUCCESS;
}
And the demonstration that the patched binary actually works with valid activation keys:
➜ Desktop ./macho_patched
.username.
dnutiu
.activation key.
6232821
Authenticated! You can use this password to valid8
5;@PO-d{bEN~
Thank you for reading and happy holidays!
Top comments (0)