So much reversing writeup
I made this challenge. Writeup is going to be how i envisioned you solve it. And im going to be at an advantage because i wrote the c code :) This is a super detailed writeup because it is for DDC introduction
How does ./jokes function?
A quick look
We get a few files
- jokes -> a binary
- reversed_flag -> weird file
- logs.txt -> what has been done to get to this situation before you got the binary
logs.txt
vim flag.c #reverse write my reverse flaggcc flag.c -o flag #reverse compile my reverse flaggcc jokes.c -o jokes # leet compile my absolute hilarious c application./jokes flag # this flag is such a joke and so funny 10hi, if you ever read this then tell the pope i said hi
the logs writes a flag.c file - so the flag is code? flag is compiled and ran as an argument with the jokes binary.
What does ./jokes do?
Lets run it!
~ ; ./jokes1. Tell a joke2. Exit>> 1Why did the C script go to therapy?Because it had serious "array-rangement" issues!1. Tell a joke2. Exit>> 1Why did the C script break up with its sorting algorithm?Because every time they tried to patch things up, it just wanted to "reverse" the relationship!1. Tell a joke2. Exit>> 1Why did the C script get a job as a chef?Because it was an expert at flipping arrays in the kitchen!1. Tell a joke2. Exit>> 1Why did the C script reverse the flag so many times?Because it wanted to... Duh.. And it was random as well.. I guess.. I don't know.. I'm not omniscient..Error opening file: Bad addressFloating point exception (core dumped)
So we run it and the program tells 4 jokes and then dumps the core. But we do get a pretty good error message. It needs a file. the jokes binary before was ran with a flag. Maybe we provide it with a file as well.
Running it with a file does not give an error. The file is left untouched but it seems like reversed_flag was overwritten.
chall ; cat testHow does this binary workchall ; ./jokes testchall ; cat reversed_flagH sdn h tiarbsiwe koryoow
So reversed flag is a shuffling of test now. Lets figure out how it is shuffled
We know that after the forth time we get new joke something different happens.
0x5555555556a5 <tell_joke+68> mov rdx, qword ptr [rax + 0x10] 0x5555555556a9 <tell_joke+72> mov rax, qword ptr [rbp - 0x10] 0x5555555556ad <tell_joke+76> mov rdi, rax 0x5555555556b0 <tell_joke+79> mov eax, 0 ► 0x5555555556b5 <tell_joke+84> call rdx <the_funniest_joke> rdi: 0x7fffffffea59 ◂— 0x45485300706f6f77 /* 'woop' */ rsi: 0x5555555592a0 ◂— "Because it wanted to... Duh.. And it was random as well.. I guess.. I don't know.. I'm not omniscient..\n" rdx: 0x5555555553fe (the_funniest_joke) ◂— endbr64 rcx: 0x7ffff7eb9184 (write+20) ◂— cmp rax, -0x1000 /* 'H=' */
0x5555555556b7 <tell_joke+86> mov eax, 0 0x5555555556bc <tell_joke+91> leave 0x5555555556bd <tell_joke+92> ret
0x5555555556be <menu> endbr64 0x5555555556c2 <menu+4> push rbp
Ok so we call the_funniest_joke function the forth time. Lets see what it does
Disassembling the function gives us a lot of stuff, but this is the very first bit of code after allocating for variables.
puVar7 = auStack_a8; var_40h = *(int64_t *)(in_FS_OFFSET + 0x28); var_a0h = arg1; ptr = (void *)open_file_from_args((char *)arg1, &var_8ch); srand(0x539); var_7ch._0_4_ = (int32_t)var_8ch; var_68h = (int64_t)(int32_t)var_8ch + -1; uVar4 = (((int64_t)(int32_t)var_8ch + 0xfU) / 0x10) * 0x10;
- Another function open_file_from_args. Seems like a util function
- Also
srand(0x539)
which is the same assrand(1337)
hmmmm.
Lets look at open_file_from_args
int64_t open_file_from_args(char *arg1, long *arg2){ undefined4 uVar1; int64_t iVar2; int64_t iVar3; long *size; char *filename; FILE *stream; void *ptr;
iVar2 = fopen(arg1, data.00002008); if (iVar2 == 0) { perror("Error opening file"); iVar3 = 0; } else { fseek(iVar2, 0, 2); uVar1 = ftell(iVar2); *(undefined4 *)arg2 = uVar1; fseek(iVar2, 0, 0); iVar3 = malloc((int64_t)*(int32_t *)arg2); if (iVar3 == 0) { perror("Error allocating memory"); fclose(iVar2); iVar3 = 0; } else { fread(iVar3, (int64_t)*(int32_t *)arg2, 1, iVar2); fclose(iVar2); } } return iVar3;}
Actually not that exiting. Just seems to load in the file and provide a pointer to it.
Lets keep looking at the_funniest_joke
Its running a for loop 10000 times… Thats sus - lets zoom in on that
for (i = 0; i < 10000; i = i + 1) { *(undefined8 *)(puVar6 + iVar1 + -8) = 0x1514; a._4_4_ = rand(); a._4_4_ = a._4_4_ % (int32_t)a; *(undefined8 *)(puVar6 + iVar1 + -8) = 0x1520; b = rand(); b = b % (int32_t)a; if ((a._4_4_ != b) && (iVar3 = b, a._4_4_ <= b)) { while (j = iVar3, k._4_4_ = a._4_4_, a._4_4_ < j) { *(undefined *)(var_60h + (b - j)) = *(undefined *)((int64_t)ptr + (int64_t)j); iVar3 = j + -1; } while (k._4_4_ = k._4_4_ + 1, k._4_4_ < b) { *(undefined *)((int64_t)ptr + (int64_t)k._4_4_) = *(undefined *)(var_60h + (k._4_4_ - a._4_4_)); } } }
I have renamed a few variables so it is easier to see what is going on.
It looks like we pick two random variables based on the srand(1337) Then we modify some values using these values inside the while loop
Lets guess what it does by writing it in some basic python where we dont care about syntax
if a!=b and a<=b: x=b while a<j: j=x k=a something[b-j]=file[j] x = j-1 while x < b k=k+1 file[k]=something[k-a]
So we pick two points a, b
Now we actually have a very nice idea about what the shuffling actually does. while a is less than b Go through an array backwards from b down to a and put those values somewhere Go through those values and put them into the original array starting from a
Visualuzing looks like this
arr = [0,1,2,3,4,5,6,7]
a=2b=5
something = [5,4,3,2]arr = [0,1,5,4,3,2,6,7]
This happens a cool 10000 times and probably shuffles the file very much.
Now the program needs to write it back into a new file
filename = (char *)0x6465737265766572; var_46h = 0x616c665f; var_42h = 0x67; *(undefined8 *)(puVar6 + iVar1 + -8) = 0x15e6; pFVar5 = (FILE *)fopen(&filename, data.00002036); pvVar2 = ptr; stream = pFVar5; if (pFVar5 == (FILE *)0x0) { *(undefined8 *)(puVar6 + iVar1 + -8) = 0x15fd; perror("Error opening file"); if (var_40h == *(int64_t *)(in_FS_OFFSET + 0x28)) { return 0; } } else { *(undefined8 *)(puVar6 + iVar1 + -8) = 0x1637; fwrite(pvVar2, (int64_t)(int32_t)k, 1, pFVar5); pFVar5 = stream; *(undefined8 *)(puVar6 + iVar1 + -8) = 0x1643; fclose(pFVar5); *(undefined8 *)(puVar6 + iVar1 + -8) = 0x164d; exit(0); puVar7 = puVar6 + iVar1; }
Knowing how fwrite works we can guess that k must be the size of file we are writing. But we are writing this shuffled mess into a file again.
Since we now know how to shuffle the array, and the fact that we use a seeded random. Meaning that if we just use the same seed again we will get the same output.
I slow method of piecing together this file would to be making an array of [0,1,2,3...n]
and using that same shuffling method on that array.
Then we weill have a recipe on what index was put where. Then we can loop through the new array and put the pieces back where they belong.
Since we need srand from libc. Its easier just to write this solvescript in c. Its almost the same as python… or something..
My solvescript looks like this
#include <stdlib.h>#include <stdio.h>
unsigned char * open_file_from_args(char *flag, int *size){ ... }
int the_funniest_joke(char *flag){ int size; unsigned char *arr = open_file_from_args(flag, &size); int l[size]; srand(1337); int c[size];
for (int i = 0; i < size; i++) { l[i] = i; }
for (int i = 0; i < 10000; i++) { int a = rand() % size; int b = rand() % size; if(a==b || a > b){ continue; } for(int j = b; j > a; j--){ c[b-j] = l[j]; } for(int j = a+1; j < b; j++){ l[j] = c[j-a]; } } for(int i = 0; i < size; i++){ for(int j = 0; j < size; j++){ if(l[j] == i){ printf("%c", arr[j]); } } }}
int main(int argc, char *argv[]) { the_funniest_joke(argv[1]); return 0;}
I also did write one in python. But it was inconsistent because of some libc issues
import osimport sysfrom ctypes import CDLLlibc = CDLL("libc.so.6")libc.srand(1337)arr = []
if len(sys.argv) != 2: print("Usage: python sol.py <file>") exit(1)
with open(sys.argv[1], "rb") as f: for line in f.readlines(): arr.append(line)
rev = arr[0] + arr[1]
l = [i for i in range(len(rev))]
for i in range(10000): a = libc.rand() % len(rev) b = libc.rand() % len(rev) c = [] if a==b or a>b: continue for j in range(b,a,-1): c.append(l[j]) for j in range(a+1,b): l[j] = c[j-a]
fixed_arr = []
for i in range(len(l)): for j in range(len(l)): if l[j] == i: fixed_arr.append(rev[j]) breakwith open("fixed", "wb") as f: f.write(bytes(fixed_arr))
os.system('strings fixed | rev | grep "DDC" ')
Properly running the solvescript creates a binary file which actually runs!
src main; ./flag | revDDC{wh4t_1f_w3_r3v3r53_1t_4g41n}
Have a nice day!