Number of crackmes:
Number of solutions:
Comments:
Name | Author | Language | Arch | Difficulty | Quality | Platform | Date | Solution | Comments |
---|---|---|---|---|---|---|---|---|---|
x64_crackme_keygen | dev0 | Assembler | x86-64 | 2.0 | 3.4 | Unix/linux etc. | 6:19 PM 12/26/2021 | 2 | 3 |
Crackme | Infos |
---|---|
PassCode | Thank you! |
Keygenme #3 | Writeup with C source keygen |
CrackMe#3_By_7T7 | Writeup with a potential keygen implementation |
Simple password with checksum | |
CrackMe-0x1 | |
BabyRE | Writeup and source file to generate the valid flag |
VMAdventures 1 | |
Keygen | Thank you and please continue to make more! |
crackme v1.0 by bkah | Thank you! I really enjoyed this one. Your assembly was really cool in this. |
CrackMe#2_By_7T7 | |
Wolf by Lucas0001 | Thank you for the challenge! |
CrackReverse | Very interesting and good! Thank you! |
Bruteverse | |
Cookie's Byte Playground | Thank you Blepperton for the learning opportunity! |
find a real key | Good for practicing static analysis and learning what strings/arrays look like in x86 assembly. |
CrackMe #1 | I really this one! Thank icgc! |
Confusion | Thank you bl4ack! |
BlockBreaker | |
Keygenme - Muldimalph | Write up with a C source keygen |
Super Random Guessing | Yariza Crackme 3 | Write up with C source valid string generator |
Keygen me Quick! | Write up with a C source keygen file |
Grandfather Clock | Write up with a C source solution. |
kgenme1.0 | Write up with a C source keygen |
Guess The Password | Write up with a C solution |
CrackThis | Simple write up and password generator C source file |
KeygenMe | Write up and a keygen C source file. |
Comment | Link |
---|---|
Was anyone successful in getting this to run? I had to get the libgcc_s_seh_1.dll and even then it would not run. Statically analyzing it looks like the key is jfsapeejfoipwafonopwehfobebcweofnoe I think the solution shows how to decrypt the success message but does not show or give the key. I might be wrong but it looks like the key is generated before main because it is a global variable and its constructor is called before we get to main. | ==> |
@micmic35 The thread subroutine/function takes the password input and XORs each byte by 0xa0. A std::stringstream is then used to convert each of those bytes to the hexadecimal integer value of that byte. The majority of the code in the function is the MSVC compiler emitting the STL template code for the std::stringstream and std::string which takes a bit to get used to. | ==> |
@expl0itr This is gold! It took a while to figure out the strcmp but I think it finds the optimal function to replace the thunk with based on processor and features. Static linking makes things a bit different and that took me a bit to untangle. I love the thread that checks for debugging. Question though, does the shell get spawned on INT3 after the syscall or on a debugger trapping? | ==> |
Yes the correct input is hardcoded in the binary and you can run that linux command to get the value BUT if you attempt to run the binary as is and use that key, which is supposed to be the correct key, you will never get the correct message because of the two bugs listed above. | ==> |
Is there a logic bug in this? I have 5 "keys" that will get the correct input message. Either way this is really well done. Really cool idea on a stack VM. | ==> |
@NameOfFool unless you patched the binary I don't think that is correct. There is a "hash function" that needs to be reversed to reveal the key to print the correct input message. key sha1 e6f2df5e3248824c62840e71dcfe639e7da7a569 | ==> |
I have two questions on this. I think there are two bugs that make this a bit difficult to solve. The first is that fgets with a size of 64 will only accept 63 bytes of input and null terminate the 64th byte. This is problematic as now you are attempting to match a 64 byte input with a 63 byte input both being AES encrypted with the same key and iv. The key is also a problem with aes-256 being used. The key in aes-256 will be 32 bytes long but in the EVP_EncryptInit_ex the author sets the key parameter to offset 16 in the key buffer on the stack. This then includes the 16 bytes that follow the key buffer on the stack which happen to be the EVP_CIPHER_CTX and I believe the return address. If this was intended, then I think the difficulty should be much higher as you are again trying to take to different length plain texts and generate the same cipher text. And in this case since I believe the input would be a string with length 63 and AES has block size 16, the output cipher lengths will be different at 64 bytes to 80 bytes respectively. I believe AES will apply padding for the 63 byte input to make it 64. The 64 byte input will be padded to 80. | ==> |
@flcksr What do have against 4? Haha. Another good one. Really cool programming on the GUI image. You are really good and it seems you are having a bit of fun with these. Thank you! | ==> |
@flcksr Not sure what you want the keygen to be but the only thing I can think of is either an exhaustive search based on the criteria above or just randomly assigning digits (converted to their ASCII representation) and passing it through the algorithm. It does not appear to be something that will be "clean" or elegant. Also did you write in the assembly for checking the 8? That was a really cool way to do that check. Really good crackme! | ==> |
Adding to what @kira617 said, I think understanding how standard library c++ objects look in memory will be very helpful with this one. Also understand where global variables are in the PE file format. *** spoiler alert *** (1) 4409 is the first four digits (2) must contain 3 8s (3) must not contain a 4 (4) 20 digits (will be formatted to (\d{4}-){4}\d{4} (5) 5 or more unique digits used (6) after the 4409, no consecutive repeating digits (7) sum of all the digits must be 104 (8) contain 2+ digits from [2357] (9) last digit must be odd | ==> |
Loved this!! Never stop when traveling through Hell. Great work creating this @heapoverride! I hope to see more from you! | ==> |
***** Spoiler ***** AGAIN-ANY-K?Y -- where ? can be any ASCII character (or anything stored in a std::string instance) Pretty unique idea. If I am wrong I apologize. It looks like you do the random 16 2 byte array but you have 9 of them. You also have a linked list or linked list like structure that is 66 bytes before the start of 6 of the random arrays. You first pass that through a function that, I don't know how to word this, unravels the bytecode into the instruction set and stores that in a std::vector instance using the push_back function. The thing that honestly made this easier was it appears it is the same instruction set as the first crackme you created. It took me a few times reading over the functions to realize you were using the modes (12 being immediate value, and 13 being register space) like the first crackme. After I picked up on that, I took a chance and devirtualized the unraveled bytecode with the previous crackmes instruction set. Very nice. I still love the way you are using the array indices as the opcodes vs the value stored there. Using dynamic analysis it would appear the opcodes change with each run. You also did a really good job using pointer manipulation to access the values in the vm. Very clever ideas! Very good crackme!! | ==> |
Great idea for obfuscating a bytecode VM instruction set!! One question though. My math is probably wrong but the last JNZ instruction in the second run of the VM appears to jump to 0x44 but the JZ R2 (I am saying that space operated on for 0xd mode are registers) a few instructions above jumps to 0x4c. That is 2 instructions under the exit. Which appears to be correct. But the jump 0x44 would be in the middle of the instruction before the exit. Is that 0x44 supposed to be 0x4a? | ==> |
Very good challenge combining classic algorithms and RE! | ==> |
@GhostmaneX That key works because the result from running "vol c:" on your machine is probably "Volume Serial Number is B628-C8A2" | ==> |
Opcode 3 is a NOP. Would you accept a NOP sled to the two bytecode instructions (Opcode 0 loading the strings) that loads to the two registers with the "Good password!" message and VM name? At that point the user would never even need to enter anything and the correct message would output. | ==> |
There appears to be a bug where the same buffer is used as both inputs into a memcmp | ==> |
A little curious (if you do not mind me asking), what compiler flags did you use for this? | ==> |
Really good challenge! Thank you. | ==> |
I believe the goal is to get the program to output a flag in the form "flag{...}" | ==> |
How did you code the vector operations in the function? | ==> |
I really enjoyed this. Thank you! Was the division by 2 operation intended to be a little lossy? Example, 97 / 2 = 48 and 96 / 2 = 48. | ==> |
@Danofred and @suzaku-coding, where are you getting the char needing to be 0x31 for the 7th character? The disassembly in the weird "strcmp" like function only checks index 4, 8, and 9 from what I see. It looks like one only needs xxxx4xxx0 to pass validation. | ==> |
Good introduction into GUI reversing and things that happen outside of just main. The algorithm is kind of cool too. Simple but needs a bit of critical thought. Great crackme overall and I enjoyed it. | ==> |
Really great crackme/keygen @BinaryNewbie! Really enjoyed it and it really tests static disassembly skills with the algorithm used and how it was coded. I do have to ask even though it is in the c_cpp category, did you have some custom assembly in the binary linked in or inline? Great binary and thank you for the challenge. | ==> |
Really good one for learning and practicing static analysis | ==> |
At the moment I can not beat the Matrix. Reversing it is fairly simple but the valid_key algorithm is a bit troubling. I see that the shift and multiply essentially divide by 2 and zero out the 2 least significant bits. When summed I do not see mathematically how the total that is compared can be computed with the algorithm. I know it has been solved because it is currently a lvl 3.6. Is there a way to mathematically calculate it with using brute force? | ==> |
I believe there is a subtle bug in this. Use a key with a 1 or 0x31 byte value as the last letter and see what happens. It appears the address space between the byte for the character and unsigned int are problematic when using the atoi function. | ==> |
I thought I had it haha. Is there a need to patch? I am stuck on getting something to "happen" but I can't figure out a way to trigger it without a patch since its a register mov instruction previous with an immediate value. | ==> |
Very good crackme! | ==> |
This one was really good! flag{1w1DL1h6_1h3_b1h4Ry_5y513m} | ==> |
Interesting little twist and algorithm used. | ==> |