---
# System prepended metadata

title: Reverse and Android (Midnight CTF)

---

# Reverse
- Overall this is a pretty good quality tournament.
## NeuraCall (499 Points/7solved)
### Challenge analysis
- It is exactly an ELF 64 file compiled in C/C++
![image](https://hackmd.io/_uploads/ryOxqZ5RJg.png)
- I will analyze it with IDA.
- This `main()` functions.
```cpp=
// local variable allocation has failed, the output may be wrong!
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  __int64 v4; // rcx
  __int64 v5; // r12
  unsigned int v6; // [rsp+5h] [rbp-BBh] BYREF
  unsigned int v7; // [rsp+Ch] [rbp-B4h] BYREF
  int v8; // [rsp+10h] [rbp-B0h] BYREF
  _DWORD v9[5]; // [rsp+14h] [rbp-ACh] BYREF
  _DWORD v10[14]; // [rsp+28h] [rbp-98h] BYREF
  int v11; // [rsp+60h] [rbp-60h] BYREF
  int v12; // [rsp+64h] [rbp-5Ch] BYREF
  _BYTE v13[14]; // [rsp+68h] [rbp-58h] BYREF
  char v14; // [rsp+76h] [rbp-4Ah] BYREF
  _BYTE v15[2]; // [rsp+78h] [rbp-48h] BYREF
  char v16; // [rsp+7Ah] [rbp-46h] BYREF
  _BYTE v17[4]; // [rsp+7Ch] [rbp-44h] BYREF
  char v18; // [rsp+80h] [rbp-40h] BYREF
  char v19; // [rsp+84h] [rbp-3Ch] BYREF
  char v20; // [rsp+9Ch] [rbp-24h] BYREF
  _BYTE v21[24]; // [rsp+A0h] [rbp-20h] BYREF
  _BYTE var8[12]; // [rsp+B8h] [rbp-8h] OVERLAPPED BYREF

  *var8 = __readfsqword(0x28u);
  if ( sub_40147F(a1, a2, a3) )
  {
    puts("Are you trying to cheat? Because I'm trying to cry.");
    return 1LL;
  }
  else
  {
    prctl(4, 0LL);
    sub_4013F2();
    v8 = 103;
    v9[0] = 111;
    v9[1] = 109;
    v9[2] = 120;
    v9[3] = 115;
    v9[4] = 66;
    v10[0] = 114;
    v10[1] = 69;
    v10[2] = 108;
    v10[3] = 98;
    v10[4] = 69;
    v10[5] = 109;
    v10[6] = 99;
    v10[7] = 126;
    v10[8] = 69;
    v10[9] = 110;
    v10[10] = 126;
    v10[11] = 70;
    v10[12] = 69;
    v10[13] = 66;
    v11 = 118;
    v12 = 108;
    *v13 = 122;
    sub_401E6D();
    qword_4051E0 = &v12;
    qword_4051E8 = &v11;
    qword_4051F0 = &v13[8];
    qword_4051F8 = v13;
    qword_405200 = &v13[4];
    qword_405208 = &v13[8];
    qword_405210 = *&var8[4];
    qword_405218 = *var8;
    (loc_40206E)(2LL);
    qword_4051E0 = &v13[8];
    qword_4051E8 = &v16;
    qword_4051F0 = &v13[10];
    qword_4051F8 = v4;
    qword_405200 = &v14;
    qword_405208 = &v13[8];
    qword_405210 = &v13[8];
    qword_405218 = *&v13[6];
    sub_401935(6LL, &v13[10], &v13[6]);
    qword_4051E0 = &v19;
    qword_4051E8 = &v18;
    qword_4051F0 = v17;
    qword_4051F8 = v15;
    qword_405200 = &v13[12];
    qword_405208 = &v13[8];
    qword_405210 = *&v13[4];
    qword_405218 = *v13;
    if ( sub_4020CC(5LL) )
    {
      qword_4051E0 = &v13[8];
      qword_4051E8 = &v20;
      qword_4051F0 = v9;
      qword_4051F8 = &v8;
      qword_405200 = v17;
      qword_405208 = v21;
      qword_405210 = v21;
      qword_405218 = &v13[4];
      v7 = (loc_4019FC)(176LL, v15);
      v5 = *&var8[-193];
      qword_4051E0 = v10;
      qword_4051E8 = &v6;
      qword_4051F0 = &v7;
      qword_4051F8 = &var8[-193];
      qword_405200 = v5;
      qword_405208 = v7;
      qword_405210 = v6;
      qword_405218 = v10[0];
      sub_402171(13LL);
      return 0LL;
    }
    else
    {
      qword_4051E0 = -2LL;
      qword_4051E8 = -1LL;
      qword_4051F0 = 0LL;
      qword_4051F8 = 1LL;
      qword_405200 = 2LL;
      qword_405208 = 3LL;
      qword_405210 = 4LL;
      qword_405218 = 5LL;
      sub_402171(10LL);
      return 1LL;
    }
  }
}
```
- Here it is anti debug :(((
```cpp=
_BOOL8 sub_40147F()
{
  int v1; // [rsp+4h] [rbp-11Ch]
  FILE *stream; // [rsp+8h] [rbp-118h]
  char s1[10]; // [rsp+10h] [rbp-110h] BYREF
  char v4[254]; // [rsp+1Ah] [rbp-106h] BYREF
  unsigned __int64 v5; // [rsp+118h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  stream = fopen("/proc/self/status", "r");
  if ( !stream )
    return 0LL;
  while ( fgets(s1, 256, stream) )
  {
    if ( !strncmp(s1, "TracerPid:", 0xAuLL) )
    {
      v1 = atoi(v4);
      fclose(stream);
      return v1 != 0;
    }
  }
  fclose(stream);
  return 0LL;
}

```
- Ahh...Here it is checking and returning success or failure message
```cpp=
    if ( sub_4020CC(5LL) )
    {
      qword_4051E0 = &v13[8];
      qword_4051E8 = &v20;
      qword_4051F0 = v9;
      qword_4051F8 = &v8;
      qword_405200 = v17;
      qword_405208 = v21;
      qword_405210 = v21;
      qword_405218 = &v13[4];
      v7 = (loc_4019FC)(176LL, v15);
      v5 = *&var8[-193];
      qword_4051E0 = v10;
      qword_4051E8 = &v6;
      qword_4051F0 = &v7;
      qword_4051F8 = &var8[-193];
      qword_405200 = v5;
      qword_405208 = v7;
      qword_405210 = v6;
      qword_405218 = v10[0];
      sub_402171(13LL);
      return 0LL;
    }
    else
    {
      qword_4051E0 = -2LL;
      qword_4051E8 = -1LL;
      qword_4051F0 = 0LL;
      qword_4051F8 = 1LL;
      qword_405200 = 2LL;
      qword_405208 = 3LL;
      qword_405210 = 4LL;
      qword_405218 = 5LL;
      sub_402171(10LL);
      return 1LL;
    }
```
```cpp=
long double __fastcall sub_402171(int a1, _DWORD *a2)
{
  long double v2; // fst7
  __int16 *v3; // rax
  long double result; // fst7

  v3 = a1;
  __debugbreak();
  LODWORD(v3) = *a2;
  result = *v3 / v2;
  getpid();
  puts("It... worked? OMG it actually worked?! You get the flag, congratz.");
  return result;
}
```
![image](https://hackmd.io/_uploads/S1aEjW9Akx.png)
- But actually in `sub_4020CC(5LL)` I don't see them checking clearly.

```cpp=
_BOOL8 __fastcall sub_4020CC(int a1)
{
  long double v1; // fst7
  long double v2; // fst7
  __int64 v4; // [rsp+0h] [rbp-29Ah]
  __int64 v5; // [rsp+26Ah] [rbp-30h]
  __int64 v6; // [rsp+27Ah] [rbp-20h]
  __int64 v7; // [rsp+28Ah] [rbp-10h]
  __int128 var8; // [rsp+292h] [rbp-8h]
  __int64 retaddr; // [rsp+2A2h] [rbp+8h]

  *&var8 = 0x123456789ABCDEFLL;
  __debugbreak();
  v2 = *a1 / v1;
  time(a1);
  qword_4051E0 = *(&var8 + 7);
  qword_4051E8 = v4;
  qword_4051F0 = v5;
  qword_4051F8 = v6;
  qword_405200 = retaddr;
  qword_405208 = *(&var8 + 1);
  qword_405210 = var8;
  qword_405218 = v7;
  return sub_401574(14LL, *&v2) == 23;
}
```
- But one thing worth noting is that it will return bool with the comparison result return `sub_401574(14LL, *&v2) == 23;` so I think this is probably the counter condition for the flag with length 23
- Looking back at the `main()` function, we can clearly see that there are 23 arguments passed into it, which are the basis for us to decode.
- Now we need to know what we are doing with these values.
- Pay attention to this function `return sub_401574(14LL, *&v2) == 23;`
```cpp=
long double __fastcall sub_401574(int a1)
{
  long double v1; // fst7
  long double result; // fst7
  __int64 i; // [rsp+10h] [rbp-10h]

  __debugbreak();
  result = *a1 / v1;
  time(a1);
  for ( i = 0LL; *(i - 0x5555444433332223LL); ++i )
    ;
  return result;
}
```
- At first glance it seems meaningless but when you switch to Assembly it will be different.
- It starts here:

```asm=
.text:00000000004015CF ; __unwind {
.text:00000000004015CF                 endbr64
.text:00000000004015D3                 push    rbp
.text:00000000004015D4                 mov     rbp, rsp
.text:00000000004015D7                 sub     rsp, 20h
.text:00000000004015DB                 mov     [rbp-14h], edi
.text:00000000004015DE                 mov     byte ptr [rbp-3], 57h ; 'W'
.text:00000000004015E2                 mov     byte ptr [rbp-2], 54h ; 'T'
.text:00000000004015E6                 mov     byte ptr [rbp-1], 46h ; 'F'
.text:00000000004015EA                 mov     eax, [rbp-14h]
.text:00000000004015ED                 cdqe
.text:00000000004015EF                 mov     rdi, rax
.text:00000000004015F2                 int     3               ; Trap to Debugger
.text:00000000004015F3                 or      [rax], al
.text:00000000004015F5                 lodsd
.text:00000000004015F6                 fsubp   st, st
.text:00000000004015F8                 test    dl, bh
.text:00000000004015F8 ; -----------------------------------------
.text:00000000004015FA                 dw 0FFFFh, 7D80h, 60FFh
.text:0000000000401600 ; ---------------------------------------------------------------------------
.text:0000000000401600                 jle     short loc_401611
.text:0000000000401602                 cmp     byte ptr [rbp-1], 7Ah ; 'z'
.text:0000000000401606                 jg      short loc_401611
.text:0000000000401608                 movzx   eax, byte ptr [rbp-1]
.text:000000000040160C                 sub     eax, 20h ; ' '
.text:000000000040160F                 jmp     short locret_401615
.text:0000000000401611 ; ---------------------------------------------------------------------------
.text:0000000000401611
.text:0000000000401611 loc_401611:                             ; CODE XREF: .text:0000000000401600↑j
.text:0000000000401611                                         ; .text:0000000000401606↑j
.text:0000000000401611                 movzx   eax, byte ptr [rbp-1]
.text:0000000000401615
.text:0000000000401615 locret_401615:                          ; CODE XREF: .text:000000000040160F↑j
.text:0000000000401615                 leave
.text:0000000000401616                 retn
.text:0000000000401616 ; } // starts at 4015CF
```
- This snippet simply normalizes lowcase to upcase by subtracting 0x20 for characters <= 'z'

- This paragraph is quite important.

```asm=
.text:0000000000401617 sub_401617      proc near
.text:0000000000401617
.text:0000000000401617 var_14          = dword ptr -14h
.text:0000000000401617 var_4           = byte ptr -4
.text:0000000000401617 var_3           = byte ptr -3
.text:0000000000401617 var_2           = byte ptr -2
.text:0000000000401617 var_1           = byte ptr -1
.text:0000000000401617
.text:0000000000401617 ; __unwind {
.text:0000000000401617                 endbr64
.text:000000000040161B                 push    rbp
.text:000000000040161C                 mov     rbp, rsp
.text:000000000040161F                 sub     rsp, 20h
.text:0000000000401623                 mov     [rbp+var_14], edi
.text:0000000000401626                 mov     [rbp+var_4], 41h ; 'A'
.text:000000000040162A                 mov     [rbp+var_3], 4Ch ; 'L'
.text:000000000040162E                 mov     [rbp+var_2], 45h ; 'E'
.text:0000000000401632                 mov     [rbp+var_1], 44h ; 'D'
.text:0000000000401636                 mov     eax, [rbp+var_14]
.text:0000000000401639                 cdqe
.text:000000000040163B                 mov     rdi, rax        ; timer
.text:000000000040163E                 int     3               ; Trap to Debugger
.text:000000000040163F                 or      [rax], eax
.text:0000000000401641                 lodsd
.text:0000000000401642                 fidivr  word ptr [rdi+0]
.text:0000000000401648                 call    _time
.text:000000000040164D                 cmp     [rbp+var_4], 2Fh ; '/'
.text:0000000000401651                 jle     short loc_401662
.text:0000000000401653                 cmp     [rbp+var_4], 39h ; '9'
.text:0000000000401657                 jg      short loc_401662
.text:0000000000401659                 movsx   eax, [rbp+var_4]
.text:000000000040165D                 sub     eax, 30h ; '0'
.text:0000000000401660                 jmp     short locret_4016A3
.text:0000000000401662 ; ---------------------------------------------------------------------------
.text:0000000000401662
.text:0000000000401662 loc_401662:                             ; CODE XREF: sub_401617+3A↑j
.text:0000000000401662                                         ; sub_401617+40↑j
.text:0000000000401662                 cmp     [rbp+var_4], 40h ; '@'
.text:0000000000401666                 jle     short loc_401677
.text:0000000000401668                 cmp     [rbp+var_4], 5Ah ; 'Z'
.text:000000000040166C                 jg      short loc_401677
.text:000000000040166E                 movsx   eax, [rbp+var_4]
.text:0000000000401672                 sub     eax, 37h ; '7'
.text:0000000000401675                 jmp     short locret_4016A3
.text:0000000000401677 ; ---------------------------------------------------------------------------
.text:0000000000401677
.text:0000000000401677 loc_401677:                             ; CODE XREF: sub_401617+4F↑j
.text:0000000000401677                                         ; sub_401617+55↑j
.text:0000000000401677                 cmp     [rbp+var_4], 7Bh ; '{'
.text:000000000040167B                 jnz     short loc_401684
.text:000000000040167D                 mov     eax, 24h ; '$'
.text:0000000000401682                 jmp     short locret_4016A3
.text:0000000000401684 ; ---------------------------------------------------------------------------
.text:0000000000401684
.text:0000000000401684 loc_401684:                             ; CODE XREF: sub_401617+64↑j
.text:0000000000401684                 cmp     [rbp+var_4], 7Dh ; '}'
.text:0000000000401688                 jnz     short loc_401691
.text:000000000040168A                 mov     eax, 25h ; '%'
.text:000000000040168F                 jmp     short locret_4016A3
.text:0000000000401691 ; ---------------------------------------------------------------------------
.text:0000000000401691
.text:0000000000401691 loc_401691:                             ; CODE XREF: sub_401617+71↑j
.text:0000000000401691                 cmp     [rbp+var_4], 5Fh ; '_'
.text:0000000000401695                 jnz     short loc_40169E
.text:0000000000401697                 mov     eax, 26h ; '&'
.text:000000000040169C                 jmp     short locret_4016A3
```
- It is normalizing the character in a special way.
>  0-9 -> -= 0x30
>  A-Z -> -= 0x37
>  { -> $
>  } -> %
>  _ -> &

```asm=
.text:00000000004016A5 ; __unwind {
.text:00000000004016A5                 endbr64
.text:00000000004016A9                 push    rbp
.text:00000000004016AA                 mov     rbp, rsp
.text:00000000004016AD                 sub     rsp, 20h
.text:00000000004016B1                 mov     [rbp+var_14], edi
.text:00000000004016B4                 mov     [rbp+var_4], 42h ; 'B'
.text:00000000004016BB                 mov     eax, [rbp+var_14]
.text:00000000004016BE                 cdqe
.text:00000000004016C0                 mov     rdi, rax
.text:00000000004016C3                 int     3               ; Trap to Debugger
.text:00000000004016C4                 or      al, [rax]
.text:00000000004016C6                 lodsd
.text:00000000004016C7                 fidivr  word ptr [rax+0]
.text:00000000004016CD                 call    _getpid
.text:00000000004016D2                 mov     edx, [rbp+var_4]
.text:00000000004016D5                 mov     eax, edx
.text:00000000004016D7                 shl     eax, 3 ;  2^3
.text:00000000004016DA                 sub     eax, edx
.text:00000000004016DC                 add     eax, 3
.text:00000000004016DF                 movsxd  rdx, eax
.text:00000000004016E2                 imul    rdx, 0FFFFFFFFD20D20D3h
.text:00000000004016E9                 shr     rdx, 20h
.text:00000000004016ED                 add     edx, eax
.text:00000000004016EF                 sar     edx, 5
.text:00000000004016F2                 mov     ecx, eax
.text:00000000004016F4                 sar     ecx, 1Fh
.text:00000000004016F7                 sub     edx, ecx
.text:00000000004016F9                 imul    ecx, edx, 27h ; '''
.text:00000000004016FC                 sub     eax, ecx
.text:00000000004016FE                 mov     edx, eax
.text:0000000000401700                 mov     eax, edx
.text:0000000000401702                 leave
.text:0000000000401703                 retn
```
- Here they are performing a mathematical transformation.
> - Total formula too: result = (a * 8 - a + 3) % 39

- This is the final transformation.
```asm=
.text:0000000000401704 ; __unwind {
.text:0000000000401704                 endbr64
.text:0000000000401708                 push    rbp
.text:0000000000401709                 mov     rbp, rsp
.text:000000000040170C                 sub     rsp, 20h
.text:0000000000401710                 mov     [rbp+var_14], edi
.text:0000000000401713                 mov     [rbp+var_4], 99h
.text:000000000040171A                 mov     eax, [rbp+var_14]
.text:000000000040171D                 cdqe
.text:000000000040171F                 mov     rdi, rax
.text:0000000000401722                 int     3               ; Trap to Debugger
.text:0000000000401723                 or      eax, [rax]
.text:0000000000401725                 lodsd
.text:0000000000401726                 fidivr  word ptr [rax+0]
.text:000000000040172C                 call    _getppid
.text:0000000000401731                 mov     eax, [rbp+var_4]
.text:0000000000401734                 xor     eax, 66h
.text:0000000000401737                 leave
.text:0000000000401738                 retn
.text:0000000000401738 ; } // starts at 401704
```
- Here they are performing a mathematical transformation.
> - Total formula too: result = a ^ 0x66

### Solution:
- And here is the version where I covered the program's encryption algorithm in python

```python=
def to_upper(flag):
    return [ord(c.upper()) for c in flag]

def char_to_val(flag):
    res = []
    for c in flag:
        if 0x30 <= c <= 0x39:  # '0'-'9'
            res.append(c - 0x30)
        elif 0x41 <= c <= 0x5A:  # 'A'-'Z'
            res.append(c - 0x37)
        elif c == ord('{'):
            res.append(0x24)
        elif c == ord('}'):
            res.append(0x25)
        elif c == ord('_'):
            res.append(0x26)
        else:
            res.append(c)
    return res

def apply_mod_math(flag):
    return [(7 * c + 3) % 39 for c in flag]

def xor_with_66(flag):
    return [c ^ 0x66 for c in flag]

def main():
    flag = input("Enter flag: ")
    data = [0x67, 0x6F, 0x6D, 0x78, 0x73, 0x42, 0x72, 0x45, 0x6C, 0x62, 0x45, 0x6D, 0x63, 0x7E, 0x45, 0x6E, 0x7E, 0x46, 0x45, 0x42, 0x76, 0x6C, 0x7A]

    if len(flag) != len(data):
        print("Wrong length!")
        return

    f = to_upper(flag)
    f = char_to_val(f)
    f = apply_mod_math(f)
    f = xor_with_66(f)

    if f == data:
        print("Correct!\n")
    else:
        print("Wrong!\n")

if __name__ == "__main__":
    main()

```
- Flag decoding version:

```python=
def reverse_flag(data):
    recovered = []
    for val in data:
        # Step 1: Reverse XOR
        v = val ^ 0x66

        # Step 2: Find x so that (7 * x + 3) % 39 == v
        found = False
        for x in range(39):
            if (7 * x + 3) % 39 == v:
                found = True
                break
        if not found:
            print("No valid value found!")
            return None

        # Step 3: Reverse mapping to characters
        if 0 <= x <= 9:
            c = chr(x + 0x30)
        elif 10 <= x <= 35:
            c = chr(x + 0x37)
        elif x == 0x24:
            c = '{'
        elif x == 0x25:
            c = '}'
        elif x == 0x26:
            c = '_'
        else:
            print(f"Unmapped value: {x}")
            return None

        recovered.append(c)

    return ''.join(recovered)

# Data from main function
data = [0x67, 0x6F, 0x6D, 0x78, 0x73, 0x42, 0x72, 0x45, 0x6C, 0x62, 0x45, 0x6D, 0x63, 0x7E, 0x45, 0x6E, 0x7E, 0x46, 0x45, 0x42, 0x76, 0x6C, 0x7A]

flag = reverse_flag(data)
print("Flag:", flag)

```

> FLAG: MCTF{R8_1S_TH3_N3W_RD1}

> Thanks for reading


## Samurai
- I also rate this challenge as quite good about shell code
### Challenge analysis:
![image](https://hackmd.io/_uploads/Skh47P5Ayx.png)
- Looking at DIE it is indeed a PE file but I see no signs of compilation by C/C++ or anything else.
- Put it in IDA and analyze what it is doing.

```cpp=
int __fastcall main(int argc, const char **argv, const char **envp)
{
  LPVOID v4; // [rsp+28h] [rbp-8h]

  _main();
  if ( argc == 2 )
  {
    puts("Nannnnn je deconne le password ne sert a rien");
    puts("[+] Removing System32");
    Sleep(0x7D0u);
    puts("[+] Sending bitcoin to MCTF Admins");
    Sleep(0x4B0u);
    puts("[+] Attempt to install rootkit");
    Sleep(0xFA0u);
    puts("[-] Failed");
    puts("[+] Attempt to install rootkit harder");
    Sleep(0x1388u);
    puts("[+] Sucess");
    Sleep(0x3E8u);
    puts("[+] Start connection to C2: https://tinyurl.com/533u5pxs");
    v4 = VirtualAlloc(0LL, 0xD1600uLL, 0x1000u, 0x40u);
    memcpy(v4, &shellcode, 0xD1600uLL);
    (v4)();
    return 0;
  }
  else
  {
    printf("Usage: %s password\n", *argv);
    return 1;
  }
}
```
- Woww...Pay attention to the shell code it seems the oscur.exe file only indirectly executes the shell code to check the flag
- Try extracting this shell code to see what it is.
- This file has a total of 857600 bytes.

![image](https://hackmd.io/_uploads/r1dsNDc0Jl.png)
- It is exactly an exe shell code
- We will dump this shell code starting from ```4D 5A``` until the 857600th byte.
- If you use IDA to analyze, dump it my way to dump the most complete shellcode.exe
![image](https://hackmd.io/_uploads/BJaBLP9Rke.png)
![image](https://hackmd.io/_uploads/HkbuIvqAyg.png)
![image](https://hackmd.io/_uploads/Skfa8P5R1e.png)
- Now let's analyze directly on `shellcode.exe` instead of oscur.exe
- On the `shellcode.exe` file there seems to be no `main()` function but only `start()` function.
```cpp=
__int64 start()
{
  unk_1400230F0 = 1;
  return sub_140001180();
}
```
- It seems like nothing :((
![image](https://hackmd.io/_uploads/S1w0wD5AJl.png)
- When I look at Strings I see the string ```Nah I'd Win``` at `sub_140001972`
```cpp=
__int64 __fastcall sub_140001972(__int64 a1, __int64 a2, __int64 a3, unsigned int a4)
{
  __int64 v4; // rax
  const CHAR *v5; // rax
  HKEY hKey; // [rsp+30h] [rbp-10h] BYREF
  char v8; // [rsp+3Bh] [rbp-5h] BYREF

  if ( RegOpenKeyExA(HKEY_CURRENT_USER, "I Really Want to Stay at Your House", 0, 0x20019u, &hKey) )
  {
    sub_140001688(a1, a4);
  }
  else
  {
    v4 = sub_140001898(&v8);
    v5 = sub_140015A10(v4);
    MessageBoxA(0LL, v5, "Nah I'd Win", 0x40u);
    RegCloseKey(hKey);
  }
  return 0LL;
}
```
- Pay attention to these two small functions because they are in the condition that prints out the success message for you.
```cpp=
 v4 = sub_140001898(&v8);
 v5 = sub_140015A10(v4);
```

```cpp=
__int64 sub_140001898()
{
  __int64 v0; // rax
  _BYTE v2[15]; // [rsp+20h] [rbp-20h] BYREF
  _BYTE v3[2]; // [rsp+2Fh] [rbp-11h] BYREF
  __int64 v4; // [rsp+38h] [rbp-8h]

  v4 = 17LL;
  qmemcpy(v2, "z2", 2);
  v2[2] = -113;
  v2[3] = -71;
  v2[4] = 82;
  v2[5] = -113;
  v2[6] = 33;
  v2[7] = 98;
  v2[8] = 84;
  v2[9] = 60;
  v2[10] = -76;
  v2[11] = -111;
  v2[12] = 111;
  v2[13] = -117;
  v2[14] = 4;
  qmemcpy(v3, "p7", sizeof(v3));
  if ( !*sub_14000E810(&unk_140018020) )
  {
    v0 = sub_14000E810(&unk_140018040);
    sub_140015970(v0, v2);
    *sub_14000E810(&unk_140018020) = 1;
    sub_14000E810(&unk_140018040);
    sub_140016970(sub_1400159D0);
  }
  return sub_14000E810(&unk_140018040);
}
```
```cpp=
__int64 __fastcall sub_140015920(__int64 a1)
{
  __int64 result; // rax

  result = *(a1 + 17);
  if ( result )
  {
    sub_140015A40(a1, 17LL, 0xD53DF29FFDB7137LL);
    result = a1;
    *(a1 + 17) = 0;
  }
  return result;
}
```
```cpp=
unsigned __int64 __fastcall sub_140015A40(__int64 a1, unsigned __int64 a2, unsigned __int64 a3)
{
  unsigned __int64 result; // rax
  unsigned __int64 i; // [rsp+8h] [rbp-8h]

  for ( i = 0LL; ; ++i )
  {
    result = i;
    if ( i >= a2 )
      break;
    *(a1 + i) ^= a3 >> (8 * (i & 7u));
  }
  return result;
}
```
- And here are the two main keys to how you can solve it.
- Its main logic is when you enter the flag and the shell code will do XOR with the key and compare with the data.
- So data is stored at `sub_140001898()` and key is stored at `sub_140015920()`.
- Now we just need to perform the reverse XOR to find the flag
### Solution:
- Script solve python:

```python=
data = [0x7A,0x32,0x8F,0xB9,0x52,0x8F,0x21,0x62,0x54,0x3C,0xB4,0x91,0x6F,0x8B,0x04,0x70 ,0x37]
key = [0x37 ,0x71 ,0xdb ,0xff ,0x29 ,0xdf ,0x53 ,0x0d]

for i in range(len(data)):
    flag = data[i] ^ key[i % len(key)]
    print(chr(flag), end="")
```
> FLAG: MCTF{ProcMonFTW}

# Android
- I think this Android challenge is pretty cool.
## Bby Neopasswd
### Challenge analysis:
![image](https://hackmd.io/_uploads/Bk7xhv9Ake.png)
- ![image](https://hackmd.io/_uploads/H16WnD9Ckg.png)
- Normally I would check `MainActivity` first
- But it seems there is nothing interesting here :((
```java=
package com.example.neopasswd;

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;
import com.example.neopasswd.databinding.ActivityMainBinding;

/* loaded from: classes6.dex */
public class MainActivity extends AppCompatActivity {
    private ActivityMainBinding binding;

    @Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        AppCompatDelegate.setDefaultNightMode(2);
        this.binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(this.binding.getRoot());
        AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications).build();
        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_activity_main);
        NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
        NavigationUI.setupWithNavController(this.binding.navView, navController);
    }
}
```
- After rummaging through it for a while, I found something interesting in the `notification` folder.
- There is something very interesting in the `NotificationsFragmen` class.
```java=
package com.example.neopasswd.ui.notifications;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import com.example.neopasswd.databinding.FragmentNotificationsBinding;
import java.util.Objects;

/* loaded from: classes4.dex */
public class NotificationsFragment extends Fragment {
    private FragmentNotificationsBinding binding;

    @Override // androidx.fragment.app.Fragment
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        NotificationsViewModel notificationsViewModel = (NotificationsViewModel) new ViewModelProvider(this).get(NotificationsViewModel.class);
        this.binding = FragmentNotificationsBinding.inflate(inflater, container, false);
        View root = this.binding.getRoot();
        final TextView textView = this.binding.textNotifications;
        LiveData<String> text = notificationsViewModel.getText();
        LifecycleOwner viewLifecycleOwner = getViewLifecycleOwner();
        Objects.requireNonNull(textView);
        text.observe(viewLifecycleOwner, new Observer() { // from class: com.example.neopasswd.ui.notifications.NotificationsFragment$$ExternalSyntheticLambda0
            @Override // androidx.lifecycle.Observer
            public final void onChanged(Object obj) {
                textView.setText((String) obj);
            }
        });
        byte[] bArr = {15, 1, 22, 4, 57, 115, 54, 119, 29, 17, 55, 18, 113, 48, 29, 113, 35, 49, 59, 29, 54, 114, 29, 4, 115, 44, 38, 29, 17, 113, 33, 48, 39, 54, 119, 63};
        return root;
    }

    @Override // androidx.fragment.app.Fragment
    public void onDestroyView() {
        super.onDestroyView();
        this.binding = null;
    }

    private byte[] encryptNotification(byte[] toEncrypt) {
        byte[] encrypted = new byte[toEncrypt.length];
        for (int i = 0; i < encrypted.length; i++) {
            encrypted[i] = (byte) (toEncrypt[i] ^ 66);
        }
        return encrypted;
    }
}
```
- It is checking the flag by XOR it with the key and checking it with the given data decima.
- Let's decode it
### Solution:
- Script solve python:
```python=
data = [15, 1, 22, 4, 57, 115, 54, 119, 29, 17, 55, 18, 113, 48, 29, 113, 35, 49, 59, 29, 54, 114, 29, 4, 115, 44, 38, 29, 17, 113, 33, 48, 39, 54, 119, 63]
key = 66

for i in range(len(data)):
    flag = data[i] ^ key
    print(chr(flag), end="")
```
> FLAG: MCTF{1t5_SuP3r_3asy_t0_F1nd_S3cret5}

## Neopasswd2
- I'm impressed with this second Android challenge :))
### Challenge analysis:
![image](https://hackmd.io/_uploads/H1jdAwqR1l.png)
- It is also a similar apk file and I will use JADX to analyze it.
- In addition, I also use Androi Studio to run and analyze.
- I'll still visit the MainActivity function first to see what's going on.
```java=
package com.example.neopasswd2;

import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.Bundle;
import android.util.Base64;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.view.GravityCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;
import com.example.neopasswd2.UserContract;
import com.example.neopasswd2.databinding.ActivityMainBinding;
import com.google.android.material.navigation.NavigationView;
import com.google.android.material.snackbar.Snackbar;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

/* loaded from: classes3.dex */
public class MainActivity extends AppCompatActivity {
    private ActivityMainBinding binding;
    private boolean isAdmin = false;
    private AppBarConfiguration mAppBarConfiguration;

    public native String getObfuscatedString();

    @Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        AppCompatDelegate.setDefaultNightMode(2);
        this.binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(this.binding.getRoot());
        setSupportActionBar(this.binding.appBarMain.toolbar);
        final SharedPreferences prefs = getSharedPreferences("session", 0);
        String currentUsername = prefs.getString("current_user", null);
        if (currentUsername == null) {
            startActivity(new Intent(this, (Class<?>) LoginActivity.class));
            finish();
            return;
        }
        TextView welcomeText = (TextView) this.binding.appBarMain.getRoot().findViewById(R.id.welcomeText);
        if (welcomeText != null) {
            welcomeText.setText("Logged as : " + currentUsername);
        }
        Cursor cursor = getContentResolver().query(UserContract.CONTENT_URI, null, "username=?", new String[]{currentUsername}, null);
        if (cursor != null && cursor.moveToFirst()) {
            int adminValue = cursor.getInt(cursor.getColumnIndexOrThrow(UserContract.UserEntry.COLUMN_ADMIN));
            this.isAdmin = adminValue == 1;
            cursor.close();
        }
        this.binding.appBarMain.fab.setOnClickListener(new View.OnClickListener() { // from class: com.example.neopasswd2.MainActivity.1
            private boolean firstClick = true;

            @Override // android.view.View.OnClickListener
            public void onClick(View view) {
                if (!MainActivity.this.isAdmin) {
                    Snackbar.make(view, "Sorry, only an admin can read messages. :/", 0).setAnchorView(R.id.fab).show();
                    return;
                }
                if (!this.firstClick) {
                    String decrypted = MainActivity.this.tryDecrypt("Mszhl+UnftsTwm7Ule0V28WQMptqd8uoc4AbDSBKavw=");
                    if (decrypted != null) {
                        Snackbar.make(view, "★" + decrypted, 0).setAnchorView(R.id.fab).show();
                        return;
                    } else {
                        Snackbar.make(view, "Sry bro, you don't have permission to read the notification :(", 0).setAnchorView(R.id.fab).show();
                        return;
                    }
                }
                Snackbar.make(view, "A secret and important notification is about to arrive..", 0).setAnchorView(R.id.fab).show();
                this.firstClick = false;
            }
        });
        final DrawerLayout drawer = this.binding.drawerLayout;
        NavigationView navigationView = this.binding.navView;
        this.mAppBarConfiguration = new AppBarConfiguration.Builder(R.id.nav_home, R.id.nav_gallery, R.id.nav_slideshow).setOpenableLayout(drawer).build();
        final NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
        NavigationUI.setupActionBarWithNavController(this, navController, this.mAppBarConfiguration);
        NavigationUI.setupWithNavController(navigationView, navController);
        navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() { // from class: com.example.neopasswd2.MainActivity.2
            @Override // com.google.android.material.navigation.NavigationView.OnNavigationItemSelectedListener
            public boolean onNavigationItemSelected(MenuItem item) {
                int id = item.getItemId();
                if (id == R.id.nav_logout) {
                    prefs.edit().clear().apply();
                    Intent i = new Intent(MainActivity.this, (Class<?>) LoginActivity.class);
                    i.setFlags(268468224);
                    MainActivity.this.startActivity(i);
                    MainActivity.this.finish();
                    return true;
                }
                boolean handled = NavigationUI.onNavDestinationSelected(item, navController);
                if (handled) {
                    drawer.closeDrawer(GravityCompat.START);
                }
                return handled;
            }
        });
    }

    public int getMaxAllowedLength() {
        return 3;
    }

    /* JADX INFO: Access modifiers changed from: private */
    public String tryDecrypt(String base64Data) {
        try {
            byte[] encrypted = Base64.decode(base64Data, 0);
            if (encrypted.length > getMaxAllowedLength()) {
                return null;
            }
            byte[] key = getObfuscatedString().getBytes("UTF-8");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(2, new SecretKeySpec(key, "AES"));
            byte[] decrypted = cipher.doFinal(encrypted);
            String result = new String(decrypted, "UTF-8").trim();
            return result;
        } catch (Exception e) {
            return null;
        }
    }

    static {
        System.loadLibrary("native-lib");
    }

    @Override // androidx.appcompat.app.AppCompatActivity
    public boolean onSupportNavigateUp() {
        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
        return NavigationUI.navigateUp(navController, this.mAppBarConfiguration) || super.onSupportNavigateUp();
    }
}
```
- Ahaha...So much fun
- I will summarize how it works for you.
> 1. Perform account login (but you can bypass it by logging in with a wrong account and then click register, I think this is a bug of the program)
> 2. When you click on the button it will decrypt the cipher text string available with the key from the program with AES algorithm but required with Admin rights.
> 3. If all the above requirements are correct the program will return the flag
![image](https://hackmd.io/_uploads/S1YD-d50yx.png)
- Please enter random username and password then login.
![image](https://hackmd.io/_uploads/SyWTb_cAJl.png)
- Then click register.
![image](https://hackmd.io/_uploads/B19JfucAyl.png)
- Try pressing it, is there anything?
![image](https://hackmd.io/_uploads/ByDzfdcAyx.png)
![image](https://hackmd.io/_uploads/S1arfO5Ckx.png)
```java=
public void onClick(View view) {
                if (!MainActivity.this.isAdmin) {
                    Snackbar.make(view, "Sorry, only an admin can read messages. :/", 0).setAnchorView(R.id.fab).show();
                    return;
                }
```
- I violated this condition because I do not have admin rights.
- I'll read through this function to see if there are any conditions that would prevent me from getting the flag.
```java=
if (!this.firstClick) {
                    String decrypted = MainActivity.this.tryDecrypt("Mszhl+UnftsTwm7Ule0V28WQMptqd8uoc4AbDSBKavw=");
                    if (decrypted != null) {
                        Snackbar.make(view, "★" + decrypted, 0).setAnchorView(R.id.fab).show();
                        return;
                    } else {
                        Snackbar.make(view, "Sry bro, you don't have permission to read the notification :(", 0).setAnchorView(R.id.fab).show();
                        return;
                    }
                }
                Snackbar.make(view, "A secret and important notification is about to arrive..", 0).setAnchorView(R.id.fab).show();
                this.firstClick = false;
```
- There are two conditions here that I cannot meet.
- In summary I have 3 conditions I cannot overcome it:
> 1. if (!MainActivity.this.isAdmin) 
> 2. if (!this.firstClick)
> 3. if (decrypted != null)
- In the third condition I saw a piece of code that prevented it with a constant 3 because the length of the flag is always greater than 3 so it will always return false.
- Hahaah here:
```java=
if (encrypted.length > getMaxAllowedLength()) {
                return null;
            }

```
```java=
  public int getMaxAllowedLength() {
        return 3;
    }

```
- Now I will patch this apk file with the above conditions to get the flag
- I will perform Decompile apk file on linux using apktool
- First I will unzip the apk file and get the MainActivity smali file to patch it.
- Command unpack apktool:
> apktool d neopasswd2.apk -o app_src 
- This is the address containing the 2 files `MainActivity.smali` and `MainActivity$1.smali` that need to be patched.
> app_src/smali_classes3/com/example/neopasswd2
### Solution:
- First I will bypass this part in `MainActivity.smail`
```java=
 if (!MainActivity.this.isAdmin) {
                    Snackbar.make(view, "Sorry, only an admin can read messages. :/", 0).setAnchorView(R.id.fab).show();
                    return;
                }
```
- It will be equivalent to the code from lines `27-39` in the smali file
- And you need to fix the code 345-352
```smali=
.method public constructor <init>()V
    .locals 1

    .line 30
    invoke-direct {p0}, Landroidx/appcompat/app/AppCompatActivity;-><init>()V

    .line 34
    const/4 v0, 0x0

    iput-boolean v0, p0, Lcom/example/neopasswd2/MainActivity;->isAdmin:Z

    return-void
.end method
```
```smali=
  :cond_2
    iput-boolean v1, p0, Lcom/example/neopasswd2/MainActivity;->isAdmin:Z

    .line 71
    invoke-interface {v5}, Landroid/database/Cursor;->close()V

    .line 74
    .end local v6    # "adminValue":I
```
- Since this line always returns false with admin privileges, I will bypass it by changing ```const/4 v0, 0x0``` to ```const/4 v0, 0x1```
- You need to add ```const/4 v1, 0x1``` to set the value true for `isAdmin`

```smali=
.method public constructor <init>()V
    .locals 1

    .line 30
    invoke-direct {p0}, Landroidx/appcompat/app/AppCompatActivity;-><init>()V

    .line 34
    const/4 v0, 0x1

    iput-boolean v0, p0, Lcom/example/neopasswd2/MainActivity;->isAdmin:Z

    return-void
.end method
```
```smali=
:cond_2
    const/4 v1, 0x1
    iput-boolean v1, p0, Lcom/example/neopasswd2/MainActivity;->isAdmin:Z

    .line 71
    invoke-interface {v5}, Landroid/database/Cursor;->close()V

    .line 74
    .end local v6    # "adminValue":I
```
- Next I will deal with this condition.
```java=
 if (!this.firstClick)
```
- It is in the file ```MainActivity$1.smali``` from lines 81-85 with the jump command being ```if-eqz v0, :cond_1```
```java=
    .line 83
    :cond_0
    iget-boolean v0, p0, Lcom/example/neopasswd2/MainActivity$1;->firstClick:Z

    if-eqz v0, :cond_1
```
- Fixed code:
```java=
    .line 83
    :cond_0
    iget-boolean v0, p0, Lcom/example/neopasswd2/MainActivity$1;->firstClick:Z

    goto :cond_1

    .line 84
```
- Next I deal with the last condition.
- 
```java=
 if (decrypted != null) {
                        Snackbar.make(view, "★" + decrypted, 0).setAnchorView(R.id.fab).show();
                        return;
                    } else {
                        Snackbar.make(view, "Sry bro, you don't have permission to read the notification :(", 0).setAnchorView(R.id.fab).show();
                        return;
                    }

```

```java=
 public int getMaxAllowedLength() {
        return 3;
    }

```
- Instead of assigning `3` here I will assign` 0xFF`
- This code corresponds to lines 160-167 in the file ```MainActivity.smali```
```java=
.method public getMaxAllowedLength()I
    .locals 1

    .line 136
    const/4 v0, 0x3

    return v0
.end method
```
- Instead of ```const/4 v0, 0x3``` I will change it to ``` const/16 v0, 0xff```
- Everything is ready, I will recompile and run to get the flag. Don't forget to replace these two new smali files back to the original place -.-
- I will build a new apk file.
> Command: apktool b app_src -o app_patched.apk
- Then create a new signature.
> Command: keytool -genkeypair -v -keystore my-release-key.keystore -keyalg RSA -keysize 2048 -validity 10000 -alias my-key-alias
- Re-sign the apk file
> Command: apksigner sign --ks my-release-key.keystore --out app_signed.apk app_patched.apk
- Woww... Rerun the new apk file named ```app_signed.apk``` on Android Studio to see if it returns the flag.
![image](https://hackmd.io/_uploads/rkSklK50kl.png)
- Yahhhhh...FLag here.
> FLAG: Th3_c4k3_1s_4_L13!


> Thanks for reading everyone.