# Rolling My Own [300 Points]
```I don't trust password checkers made by other people, so I wrote my own. It doesn't even need to store the password! If you can crack it I'll give you a flag. nc mercury.picoctf.net 35226```
## Hints
1. It's based on [this paper](https://link.springer.com/article/10.1007%2Fs11416-006-0011-3)2. Here's the start of the password: `D1v1`
------
We are given the ELF64 `remote` file.
Running it asks us to input a password. When we try to input any password, it seems to give `Segmentation fault`.
```./remotePassword: jdhhdquedSegmentation fault```
Let's first analyse it in IDA and look at the `main` function:
## Main Function
```C__int64 __fastcall main(int a1, char **a2, char **a3){  unsigned int lengthOfdest; // eax  __int64 v4; // rdx  int i; // [rsp+8h] [rbp-F8h]  int j; // [rsp+8h] [rbp-F8h]  int k; // [rsp+Ch] [rbp-F4h]  void (__fastcall *v9)(__int64 (__fastcall *)()); // [rsp+10h] [rbp-F0h]  _BYTE *ptr; // [rsp+18h] [rbp-E8h]  int v11[4]; // [rsp+20h] [rbp-E0h]  __int64 v12[2]; // [rsp+30h] [rbp-D0h]  char v13[48]; // [rsp+40h] [rbp-C0h] BYREF  char s[64]; // [rsp+70h] [rbp-90h] BYREF  char dest[72]; // [rsp+B0h] [rbp-50h] BYREF  unsigned __int64 v16; // [rsp+F8h] [rbp-8h]
  v16 = __readfsqword(0x28u);  setbuf(stdout, 0LL);  strcpy(v13, "GpLaMjEWpVOjnnmkRGiledp6Mvcezxls");  v11[0] = 8;  v11[1] = 2;  v11[2] = 7;  v11[3] = 1;  memset(s, 0, sizeof(s));  memset(dest, 0, 0x40uLL);  printf("Password: ");  fgets(s, 64, stdin);  s[strlen(s) - 1] = 0;                         // sets null char  for ( i = 0; i <= 3; ++i )  {    strncat(dest, &s[4 * i], 4uLL);             // In each iteration (4 times):                                                // - Append 4 chars of input                                                // - Append 8 chars of string                                                // Total length = 16 + 32 = 48    strncat(dest, &v13[8 * i], 8uLL);  }  ptr = malloc(64uLL);  lengthOfdest = strlen(dest);  sub_E3E((__int64)ptr, (__int64)dest, lengthOfdest);  for ( j = 0; j <= 3; ++j )                    // jumbles up MD5 output shellcode                                                //                                                 // [1st MD5]: x x x x x x x x C C C C x x x x                                                // [2nd MD5]: x x C C C C x x x x x x x x x x  {    for ( k = 0; k <= 3; ++k )      *((_BYTE *)v12 + 4 * k + j) = ptr[16 * k + j + v11[k]];// ptr saved to v12  }  v9 = (void (__fastcall *)(__int64 (__fastcall *)()))mmap(0LL, 0x10uLL, 7, 34, -1, 0LL);  v4 = v12[1];  *(_QWORD *)v9 = v12[0];                       // pointer saved to v9  *((_QWORD *)v9 + 1) = v4;  v9((__int64 (__fastcall *)())flag_func);      // v9 is called as a function  free(ptr);  return 0LL;}```
*As you can see, I have added some comments and renamed some variables to the code. The following is a summary of what it does:*
- We first see that our input is saved into a variable `s` 
  ```c  printf("Password: ");  fgets(s, 64, stdin);  ```
- A `for` loop then appends **4 characters** of our input and **8 characters** of the string `strcpy(v13, "GpLaMjEWpVOjnnmkRGiledp6Mvcezxls");` in each iteration to the variable `dest`.
  - We can deduce that the length of the input should be **16 bytes** since it runs 4 times * 4 = 16 chars. The total length of dest would hence be 32+16=48 chars
  - ```c      for ( i = 0; i <= 3; ++i )      {        strncat(dest, &s[4 * i], 4uLL);             // In each iteration (4 times):                                                    // - Append 4 chars of input                                                    // - Append 8 chars of string                                                    // Total length = 16 + 32 = 48        strncat(dest, &v13[8 * i], 8uLL);      }    ```
- Memory space is then allocated and saved to `ptr` in ` ptr = malloc(64uLL);` 
- A function is called with `ptr`, `dest` and `lengthOfdest` which is the length of dest.
  ```c  lengthOfdest = strlen(dest);  sub_E3E((__int64)ptr, (__int64)dest, lengthOfdest);  ```
## sub_E3E (Hashing Function)
```cunsigned __int64 __fastcall sub_E3E(__int64 mallocPTR, __int64 dest, int lengthOfdest){  int lengthQuotient; // eax  int i; // [rsp+20h] [rbp-90h]  int j; // [rsp+24h] [rbp-8Ch]  int lenOfBytesToHash; // [rsp+28h] [rbp-88h]  int lengthQuotient2; // [rsp+2Ch] [rbp-84h]  char someMD5Struc[96]; // [rsp+30h] [rbp-80h] BYREF  char MD5Hash[24]; // [rsp+90h] [rbp-20h] BYREF  unsigned __int64 v13; // [rsp+A8h] [rbp-8h]
  v13 = __readfsqword(0x28u);  if ( lengthOfdest % 12 )    lengthQuotient = lengthOfdest / 12 + 1;  else    lengthQuotient = lengthOfdest / 12;         // if factors of 12, come here  lengthQuotient2 = lengthQuotient;  for ( i = 0; i < lengthQuotient2; ++i )  {    lenOfBytesToHash = 12;    if ( i == lengthQuotient2 - 1 && lengthOfdest % 12 )// if last iteration && input is not perfect factors of 12                                                // get the length of the last chunk      lenOfBytesToHash = lengthQuotient2 % 12;    MD5_Init(someMD5Struc);    MD5_Update(someMD5Struc, dest, lenOfBytesToHash);// Note: dest is a pointer                                                // MD5_Update(Hash, data, len) - Hashes len of data into hash    dest += lenOfBytesToHash;    MD5_Final(MD5Hash, someMD5Struc);    for ( j = 0; j <= 15; ++j )      *(_BYTE *)(mallocPTR + (j + 16 * i) % 64) = MD5Hash[j];// appends all 3 MD5 hashes into PTR  }  return __readfsqword(0x28u) ^ v13;}```
In sub_E3E, we see references to `MD5` hashing. 
- The first part simply calculates how many blocks of 12 there are (the last block need not be a full block of 12)
  ```c    if ( lengthOfdest % 12 )      lengthQuotient = lengthOfdest / 12 + 1;    else      lengthQuotient = lengthOfdest / 12;         // if factors of 12, come here    lengthQuotient2 = lengthQuotient;  ```
- The `for` loop hashes **blocks of 12 characters (or less) of `dest`** into MD5 hashes. The MD5 hashes are appended to the variable `ptr` that was passed into the function.
- ```C    for ( i = 0; i < lengthQuotient2; ++i )    {      lenOfBytesToHash = 12;      if ( i == lengthQuotient2 - 1 && lengthOfdest % 12 )// if last iteration && input is not perfect factors of 12                                                  // get the length of the last chunk        lenOfBytesToHash = lengthQuotient2 % 12;      MD5_Init(someMD5Struc);      MD5_Update(someMD5Struc, dest, lenOfBytesToHash);// Note: dest is a pointer                                                  // MD5_Update(Hash, data, len) - Hashes len of data into hash      dest += lenOfBytesToHash;      MD5_Final(MD5Hash, someMD5Struc);      for ( j = 0; j <= 15; ++j )        *(_BYTE *)(mallocPTR + (j + 16 * i) % 64) = MD5Hash[j];// appends all 3 MD5 hashes into PTR    }  ```
 
Let's look back at the `main` function
- The `for` loop immediately after `sub_E3E` **saves certain parts of the MD5 hash** into a `v12` pointer (which is later called as "shellcode")
  ```C   for ( j = 0; j <= 3; ++j )                    // takes specific positions of the MD5 output shellcode marked by a "C"                                                  //                                                   // [1st MD5]: x x x x x x x x C C C C x x x x                                                  // [2nd MD5]: x x C C C C x x x x x x x x x x    {      for ( k = 0; k <= 3; ++k )        *((_BYTE *)v12 + 4 * k + j) = ptr[16 * k + j + v11[k]]; // ptr saved to v12    }  ```
- ```c  v4 = v12[1];  *(_QWORD *)v9 = v12[0];                       // pointer saved to v9  *((_QWORD *)v9 + 1) = v4;  v9((__int64 (__fastcall *)())flag_func);      // v9 is called as a function  ```
  - `v12` pointer is then saved to `v9`, which is called along with the address of `flag_func` as the parameter.
Let's take a look at `flag_func`
## flag_func
```cunsigned __int64 __fastcall sub_102B(__int64 a1){  FILE *stream; // [rsp+18h] [rbp-98h]  char s[136]; // [rsp+20h] [rbp-90h] BYREF  unsigned __int64 v4; // [rsp+A8h] [rbp-8h]
  v4 = __readfsqword(0x28u);  if ( a1 == 0x7B3DC26F1LL )                    // we need to pass a parameter with 0x7B3DC26F1  {    stream = fopen("flag", "r");    if ( !stream )    {      puts("Flag file not found. Contact an admin.");      exit(1);    }    fgets(s, 128, stream);    puts(s);  }  else  {    puts("Hmmmmmm... not quite");  }  return __readfsqword(0x28u) ^ v4;}```
- It seems like when `flag_func` is called with the parameter with value `0x7B3DC26F1`, it will open the flag file (on the remote server) and send the flag to us.
------
## What we have to do
In summary, we have to: Write a **password** that gets converted to an **MD5 hash** with the **specific bytes which form a shellcode that calls `flag_func`** with the parameter  `0x7B3DC26F1`
Let's take a closer look at the part where our shellcode is called **in assembly**:
```assemblymov     rax, [rbp+var_F0] ; a pointer to the bytes from the MD5 hash (i.e our shellcode) is moved into raxlea     rdi, flag_func  ; sub_102B address passed into register rdicall    rax             ; rax (i.e our shellcode) is then called ```
Hence we can write the following shellcode:
```assemblymov rsi, rdi ; save address of flag_func into rsimov rdi, 0x7B3DC26F1 ; move required value into rdi (pass parameters via registers)call rsi```
We can then assemble it using `pwntools` `asm()`
```python>>> shellcode = """... mov rsi, rdi... mov rdi, 0x7B3DC26F1... call rsi... """>>> asm(shellcode, arch="amd64").hex()'4889fe48bff126dcb307000000ffd6'```
I then wrote a [bruteforce script](https://github.com/IRS-Cybersec/ctfdump/blob/master/picoCTF/2021/RE/Rolling%20My%20Own/RMOBruteforce.py) to **look for the password** that **generates the MD5 hashes** with the bytes of the shellcode we need.
And we get the following password:
```Found smth!D1v1Found smth!d3AnFound smth!dC0nFound smth!\rpB```
Entering it into the remote server gets us the flag:
```nc mercury.picoctf.net 35226Password: D1v1d3AndC0n\rpBpicoCTF{r011ing_y0ur_0wn_crypt0_15_h4rd!_dae85416}```
------
## Learning Points
- Anti-decompilation technique using hash functions (as mentioned in the research paper)- Writing "shellcode"
***Side-note:*** Some writeups state the password as `D1v1d3AndC0nqu3r`, which is probably the intended password. However, we only need the specific bytes at specific positions of the MD5 hash to be the ones we wanted for the program to work. Hence `\rpB` works as well.