This page has been machine-translated from the original page.
Table of Contents
Back to the past(Rev)
Using the provided binary and the encrypted file, find a way to retrieve the flag contained in “flag.enc”. Note that the binary would have been run in May 2024. Note: The flag is in the format PWNME{…}
The challenge provides an ELF file with a file-encryption feature, along with an encrypted flag file.
Analyzing this ELF shows that it seeds srand from the program’s execution time and then performs the encryption using random numbers generated by rand.
From the file timestamp, I knew the flag file had been encrypted in May 2024, so at first it seemed like I just needed to implement a solver that used srand and rand to decrypt the file… but I could not actually recover the flag.
After taking a closer look with gdb, I found the reason: the random numbers produced from the same timestamp by the srand/rand functions in my local environment did not match the numbers generated by the statically linked srand/rand functions embedded in the challenge binary.
So I started reversing the challenge binary’s srand/rand implementation.
Below is the solver I wrote by reproducing the above code as custom functions.
In the end, using the random numbers generated from the timestamp Thu May 09 2024 05:01:17 GMT+0900 let me obtain the key that decrypts the correct flag.
#include <stdio.h>
#include <stdint.h>
#include <time.h>
uint64_t seed;
uint64_t result;
int32_t r;
int32_t key;
int32_t tmp;
uint64_t custom_srand(s) {
seed = s - 1;
return 0;
}
uint64_t custom_rand(){
result = 0x5851f42d4c957f2d * seed + 1;
seed = result;
return result >> 0x21;
}
int32_t gen_key(){
r = custom_rand();
tmp = r / 0x7f;
key = (r - ((int8_t)(tmp << 7) - tmp)) & 0xFF;
return key;
}
int main(void){
struct tm time_info = {0}; // Initialize the time structure to zero
time_info.tm_year = 2024 - 1900; // Year (years since 1900)
time_info.tm_mon = 5 - 1; // Month (0 = January, 1 = February, ..., 4 = May)
time_info.tm_mday = 9; // Day
time_info.tm_hour = 22; // Hour (24-hour clock)
time_info.tm_min = 1; // Minute
time_info.tm_sec = 16; // Second
setenv("TZ", "Asia/Tokyo", 1);
tzset();
time_t epoch_time = mktime(&time_info);
// target = 0x70 0x63 0x42 0x50 0x6a
// for (int i = 0 ; i < 60*60*24; i++) {
// custom_srand(epoch_time-i);
// if (gen_key() == 0x70) {
// if (gen_key() == 0x63) {
// if (gen_key() == 0x42) {
// printf("%d\n", epoch_time-i);
// break;
// }
// }
// }
// }
epoch_time = 1715198477;
printf("keys = [");
for (int i = 0; i < 2; i++) {
custom_srand(epoch_time-i);
printf("[");
for (int j = 0; j < 40; j++) {
int32_t r = custom_rand();
int32_t tmp = r / 0x7f;
int32_t key = (r - ((int8_t)(tmp << 7) - tmp)) & 0xFF;
if (j != 39) { printf("%d,", key); }
else { printf("%d", key); }
}
if (i != 1) { printf("],"); }
else { printf("]"); }
}
printf("]\n");
return;
}The solver that uses the random numbers generated by the code above as the key is shown below.
keys = [[112,99,66,80,106,86,57,41,90,9,43,34,111,95,73,32,112,54,87,46,112,74,53,81,36,29,8,83,76,20,114,97,55,89,121,109,60,22,22,56],[101,30,103,28,88,32,47,8,114,95,21,97,98,2,42,21,36,12,88,119,99,108,97,45,5,41,65,101,15,61,86,32,70,7,106,64,78,45,43,86]]
with open("flag.enc", "rb") as f:
data = f.read()
for key in keys:
tmp = ""
for i,d in enumerate(data):
tmp += chr(d^key[i])
if "PWNME" in tmp:
print(tmp)With this, I was able to recover the correct flag.
Summary
I actually wanted to do something smarter, like reusing the original binary’s code via something like dlopen, but that did not work because the target was not a library file and the functions I wanted were not exported.
Still looking for a better method…