# 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++

- 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;
}
```

- 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:

- 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.

- 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



- 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 :((

- 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:

- 
- 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:

- 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

- Please enter random username and password then login.

- Then click register.

- Try pressing it, is there anything?


```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.

- Yahhhhh...FLag here.
> FLAG: Th3_c4k3_1s_4_L13!
> Thanks for reading everyone.