# WU Pico
## 1. Unpackme
> Đề: 
---
* Ở đây, ta nhận được một file có tên là "binary", tiến hành tải file này về và ta được kết quả như sau:

Nhìn sang hint lúc này ta nhận được là UPX là gì. Tiến hành tra google tìm hiểu [UPX](https://upx.github.io/)
* Thử làm xem nếu như không giải nén file theo UPX thì nó sẽ ra gì. Và tất nhiên khi bỏ vào IDA-64 mã sẽ giải được ra như sau:

Ở đây thấy được rẳng chỉ duy nhất 7 dòng và vẫn chưa thấy được vấn đề liên quan cũng như flag của bài
* Quay trở lại lúc ban đầu, lúc đã tải về UPX và giải nén ra, sau đó di chuyển file "unpackme-upx" cùng folder với gói vừa giải nén, sẽ được như sau:

Tiếp đến, mở cmd tại mục này sau đó tiến hành chạy upx.exe

Sau khi đọc hướng dẫn dùng UPX từ Terminal, tiến hành giải mã file "unpackme-upx" bằng cách nhập lệnh `./upx -d unpackme-upx`.Giải mã được ta nhận được như sau:

Bỏ file này vào lại IDA-64, ta thấy được đoạn asm như sau (đây chỉ trong hàm main):
```
; Attributes: bp-based frame
; int __cdecl main(int argc, const char **argv, const char **envp)
public main
main proc near
var_50= qword ptr -50h
var_44= dword ptr -44h
var_3C= dword ptr -3Ch
var_38= qword ptr -38h
var_30= byte ptr -30h
var_8= qword ptr -8
; __unwind {
endbr64
push rbp
mov rbp, rsp
sub rsp, 50h
mov [rbp-44h], edi
mov [rbp-50h], rsi
mov rax, fs:28h
mov [rbp-8], rax
xor eax, eax
mov rax, 4C75257240343A41h
mov rdx, 30623E306B6D4146h
mov [rbp-30h], rax
mov [rbp-28h], rdx
mov rax, 3532666630486637h
mov [rbp-20h], rax
mov dword ptr [rbp-18h], 36665F60h
mov word ptr [rbp-14h], 4Eh ; 'N'
lea rdi, cs:4B3004h ; "What's my favorite number? "
mov eax, 0
call printf
lea rax, [rbp-3Ch]
mov rsi, rax
lea rdi, cs:4B3020h ; "%d"
mov eax, 0
call __isoc99_scanf
mov eax, [rbp-3Ch]
cmp eax, 0B83CBh
jnz short loc_401F42
```
Đoạn code trên được hiểu cơ bản như rằng bảo người dùng nhập vào một số nguyên sau đó so sánh số này với một số ở dạng hex *"B83CB"* nếu đúng thì sẽ thực hiện một số công việc tiếp theo, còn nếu sai thì in ra màn hình *"Sorry, that's not it!"*. Sở dĩ ta hiểu được cơ bản đoạn asm trên là bởi đọc từ trên xuống ta chú ý rằng `call __isoc99_scanf`, `cmp eax, 0B83CBh`, `jnz short loc_401F42`
> jnz có chức năng nhảy tới một vị trí khác trong chương trình nếu giá trị của một thanh ghi hay một biến nào đó khác không.
Tiến hành F5 IDA-64 ta sẽ được đoạn code C như sau:
```
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v3; // cl
int v4; // edx
int v5; // ecx
int v6; // r8d
int v7; // r9d
int v9; // [rsp+14h] [rbp-3Ch] BYREF
__int64 v10; // [rsp+18h] [rbp-38h]
char v11[40]; // [rsp+20h] [rbp-30h] BYREF
unsigned __int64 v12; // [rsp+48h] [rbp-8h]
v12 = __readfsqword(0x28u);
strcpy(v11, "A:4@r%uLFAmk0>b07fH0ff25`_f6N");
printf((unsigned int)"What's my favorite number? ", (_DWORD)argv, 1802322246, v3);
_isoc99_scanf((unsigned int)"%d", (unsigned int)&v9, v4, v5, v6, v7, (char)argv);
if ( v9 == 754635 )
{
v10 = rotate_encrypt(0LL, v11);
fputs(v10, stdout);
putchar(10LL);
free(v10);
}
else
{
puts("Sorry, that's not it!");
}
return 0;
}
```
Kiểm tra lại một lần nữa thấy rằng dịch đoạn code asm là đúng và lúc này ta lưu ý số **754635** tiến hành nhập số này vào chương trình và có được flag:

## 2. Keygenme
> Đề: 
---
* Ở đây ta thấy được một file tên là "binary", tiến hành tải về ở Window ta được file như sau:

* Ta sẽ đưa file này vào unbuntu, ở bài này ưu tiên nên dùng ubuntu bởi openssl ở đây sẽ phù hợp và dễ sử dụng hơn so với kali
Tiến hành mở file vừa tải được bằng IDA, ta thu được đoạn asm của hàm main như sau:
```
; Attributes: bp-based frame
; int __fastcall main(int, char **, char **)
main proc near
var_40= qword ptr -40h
var_34= dword ptr -34h
s= byte ptr -30h
var_8= qword ptr -8
; __unwind {
endbr64
push rbp
mov rbp, rsp
sub rsp, 40h
mov [rbp+var_34], edi
mov [rbp+var_40], rsi
mov rax, fs:28h
mov [rbp+var_8], rax
xor eax, eax
lea rdi, aEnterYourLicen ; "Enter your license key: "
mov eax, 0
call _printf
mov rdx, cs:stdin ; stream
lea rax, [rbp+s]
mov esi, 25h ; '%' ; n
mov rdi, rax ; s
call _fgets
lea rax, [rbp+s]
mov rdi, rax
call sub_1209
test al, al
jz short loc_14F4
```
* Ở đây có một hàm `call sub_1209` là đáng chú ý, thử xem vào hàm này có chứa nội dung gì trong này, ta tiến hành F5 để chuyển sang code C, lúc này ta sẽ bắt gặp được một dòng với hàm "strcpy" có nội dung rất giống dạng với flag:`strcpy(s, "picoCTF{br1ng_y0ur_0wn_k3y_");`
* Đọc qua hết đoạn code đã được chuyển sang C của hàm "sub_1209" ta nhận được một số thông tin như flag sẽ có độ dài **37**. Nhưng khi quan sát chuỗi ở hàm strcpy thì chỉ có **27** và còn một kí tự "}" được sao chép vào "v10" vậy còn tổng cộng là **9**. Ta chú ý đến các dòng gán giá trị vào "v14" vừa đủ 9 thế là đã có một flag hoàn chỉnh.
> MD5 dùng để tính toán giá trị băm với 3 thông số. Đầu tiên, là dữ liệu cần băm. Tiếp đến, kích thước của dữ liệu băm. Cuối cùng, nơi lưu giá trị sau khi băm.
> `sprintf(&s[v4 + 32], "%02x", (unsigned __int8)v10[i + 2]);` được dùng để chuyển đổi mỗi phần tử ở "v10" sang giá trị hex và lưu tại "s" thứ 32
* Tới đây, việc cuối cùng để hoàn thành flag là xem được các giá trị được gán cho "v14". Tiến hành debugger chương trình. Vì đây dạng ELF nên sử dụng remote debug. Ta thử chạy "keygenme" trong ubuntu thì thấy yêu cầu cần có "*openssl*" cũng như "*libcrypto.so.1.1*" nên cần tải thêm cho ubuntu. Sau khi cài xong openssl thì phải tạo thêm "symlink" cho "*libcrypto.so.1.1*"
* **Lưu ý:** Khi tiến hành debugger bắt buộc phải truyền tham số và thêm breakpoint thì khi đó mới debug xem giá trị được nếu không debug sẽ không thể nào nhìn thấy giá trị.

Đến đây ta sẽ thử lấy giá trị đầu tiên, tức là phép gán đầu tiên có nội dung ở stack như sau `[stack]:00007FFDDFD8E78B`, nhảy đến địa chỉ này ta phát hiện một flag như lúc ban đầu nhưng đầy đủ hơn như sau:

Và đó cũng chính là kết quả cuối cùng.
=> Ở bài này chủ yếu luyện debug cũng như kĩ năng về nó.
## 3. Forky
> Đề: 
---
* Ở bài này, mục đề bài đã yêu cầu rằng hãy xác định số cuối cùng được truyền và hàm "*doNothing()*". Tiến hành tải chương trình về là kiểm tra xem đây là loại file gì. Phát hiện ra đây là loại **ELF-32bit**.

* Sau đây là đoạn code của hàm "main":
```
; Attributes: bp-based frame fuzzy-sp
; int __cdecl main(int argc, const char **argv, const char **envp)
public main
main proc near
prot= dword ptr -14h
flags= dword ptr -10h
var_C= dword ptr -0Ch
argc= dword ptr 8
argv= dword ptr 0Ch
envp= dword ptr 10h
; __unwind {
lea ecx, [esp+4]
and esp, 0FFFFFFF0h
push dword ptr [ecx-4]
push ebp
mov ebp, esp
push ebx
push ecx
sub esp, 10h
call __x86_get_pc_thunk_bx
add ebx, (offset _GLOBAL_OFFSET_TABLE_ - $)
mov [ebp+prot], 3
mov [ebp+flags], 21h ; '!'
sub esp, 8
push 0 ; offset
push 0FFFFFFFFh ; fd
push [ebp+flags] ; flags
push [ebp+prot] ; prot
push 4 ; len
push 0 ; addr
call _mmap
add esp, 20h
mov [ebp+var_C], eax
mov eax, [ebp+var_C]
mov dword ptr [eax], 3B9ACA00h
call _fork
call _fork
call _fork
call _fork
mov eax, [ebp+var_C]
mov eax, [eax]
lea edx, [eax+499602D2h]
mov eax, [ebp+var_C]
mov [eax], edx
mov eax, [ebp+var_C]
mov eax, [eax]
sub esp, 0Ch
push eax
call doNothing
add esp, 10h
mov eax, 0
lea esp, [ebp-8]
pop ecx
pop ebx
pop ebp
lea esp, [ecx-4]
retn
; } // starts at 566
main endp
```
* Nhìn qua đoạn code trên thì sử dụng đa số *"eax"* và *"edx"*, những thanh liên quan đến **32bits**. Đọc qua đoạn code trên hiểu được rằng khởi tạo biến với giá trị **3B9ACA00**, sau đó gọi *"fork()"*. Khi kết thúc [**fork**](https://www.geeksforgeeks.org/fork-system-call/) thì sẽ cộng biến đó thêm cho **499602D2** và truyền kết quả sau khi cộng này vào hàm "*doNothing()*".
* Sau khi dịch được đoạn code trên tiến hành tạo một script nhỏ như sau để lấy flag:
```
import numpy as np
a = np.int32(0x3B9ACA00)
b = np.int32(0x499602D2)
print ( a+16*b )
```
với output sẽ là "**-721750240**".
=>Ở bài này chủ yếu nói về [**fork**](https://www.geeksforgeeks.org/fork-system-call/) và overflow.
## 4. Checkpass
> Đề: 
* Ở đây, khi ta nhận được file, sau đó tiến hành kiểm tra file này là gì trên linux thì được kết quả như sau:

* Ở đây có hai thông số ta cần chú ý đó là **ELF 64-bit** và binary này đã bị **[stripped](https://tr0id.medium.com/working-with-stripped-binaries-in-gdb-cacacd7d5a33)**.
* Tiến hành debug binary này, việc đầu tiên nhìn vào hàm main() như sau:
```
; __int64 __fastcall main(int, char **, char **)
main proc near
var_8= qword ptr -8
; __unwind {
push rax
mov rcx, rsi
movsxd rdx, edi
lea rax, sub_5960
mov [rsp+8+var_8], rax
lea rsi, off_2482A8
mov rdi, rsp
call cs:off_24AF00
pop rcx
retn
; } // starts at 66F0
main endp
```
* Ở đây vì đã stripped nên ta đoán rằng char** chính là argv[1] mục đích là lấy dữ liệu đầu vào thông qua argv.
* Tiếp theo, thấy được rằng khai báo con trỏ hàm sau đó gán cho nó địa chỉ của hàm `sub_5960`.
* Vào hàm kiểm tra xem hàm nó làm gì.

* Kết hợp với ASM:



* Qua code đã debug và ASM ta hiểu được rằng khi truyền vào *`sub_6A00`* hai tham số là *`rdi`* và *`rsi`* thì hàm này có kiểu trả về là *rdi* sau đó lấy so sánh với 41 tức là muốn lấy tham số truyền vào và so sánh nó có phải độ dài là 41 hay không. Tiếp theo, so sánh các kí tự đầu có phải là **picoCTF{}** hay không. Nếu tất cả đều chính xác thì sẽ so sánh nhân bên trong dấu ngoặc. Đây cũng chính là phần gây đau đầu nhất :3
* Tiếp đến, gọi đến hàm *`sub_54E0`*.
```
char __fastcall sub_54E0(__int64 output, unsigned __int8 *input, __int64 a3)
{
__int64 v3; // rdx
unsigned __int64 v4; // rax
char result; // al
char enc_flag[32]; // [rsp+8h] [rbp-20h]
v3 = a3 << 8;
enc_flag[0] = sbox[v3 + *input];
enc_flag[1] = sbox[v3 + input[1]];
enc_flag[2] = sbox[v3 + input[2]];
enc_flag[3] = sbox[v3 + input[3]];
enc_flag[4] = sbox[v3 + input[4]];
enc_flag[5] = sbox[v3 + input[5]];
enc_flag[6] = sbox[v3 + input[6]];
enc_flag[7] = sbox[v3 + input[7]];
enc_flag[8] = sbox[v3 + input[8]];
enc_flag[9] = sbox[v3 + input[9]];
enc_flag[10] = sbox[v3 + input[10]];
enc_flag[11] = sbox[v3 + input[11]];
enc_flag[12] = sbox[v3 + input[12]];
enc_flag[13] = sbox[v3 + input[13]];
enc_flag[14] = sbox[v3 + input[14]];
enc_flag[15] = sbox[v3 + input[15]];
enc_flag[16] = sbox[v3 + input[16]];
enc_flag[17] = sbox[v3 + input[17]];
enc_flag[18] = sbox[v3 + input[18]];
enc_flag[19] = sbox[v3 + input[19]];
enc_flag[20] = sbox[v3 + input[20]];
enc_flag[21] = sbox[v3 + input[21]];
enc_flag[22] = sbox[v3 + input[22]];
enc_flag[23] = sbox[v3 + input[23]];
enc_flag[24] = sbox[v3 + input[24]];
enc_flag[25] = sbox[v3 + input[25]];
enc_flag[26] = sbox[v3 + input[26]];
enc_flag[27] = sbox[v3 + input[27]];
enc_flag[28] = sbox[v3 + input[28]];
enc_flag[29] = sbox[v3 + input[29]];
enc_flag[30] = sbox[v3 + input[30]];
enc_flag[31] = sbox[v3 + input[31]];
*(_OWORD *)(output + 16) = 0LL;
*(_OWORD *)output = 0LL;
v4 = *(_QWORD *)&s_i[v3 + 0x40];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)(output + 8) = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3 + 200];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)(output + 25) = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3 + 216];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)(output + 27) = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3 + 224];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)(output + 28) = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3 + 136];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)(output + 17) = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3 + 112];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)(output + 14) = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3 + 96];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)(output + 12) = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3 + 120];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)(output + 15) = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3 + 16];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)(output + 2) = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3 + 168];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)(output + 21) = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3 + 128];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)(output + 16) = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3 + 72];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)(output + 9) = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3 + 152];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)(output + 19) = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3 + 80];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)(output + 10) = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3 + 104];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)(output + 13) = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3 + 48];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)(output + 6) = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3 + 176];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)(output + 22) = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)output = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3 + 240];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)(output + 30) = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3 + 8];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)(output + 1) = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3 + 32];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)(output + 4) = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3 + 208];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)(output + 26) = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3 + 232];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)(output + 29) = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3 + 24];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)(output + 3) = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3 + 248];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)(output + 31) = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3 + 160];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)(output + 20) = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3 + 192];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)(output + 24) = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3 + 56];
if ( v4 > 0x1F )
goto LABEL_34;
*(_BYTE *)(output + 7) = enc_flag[v4];
v4 = *(_QWORD *)&s_i[v3 + 88];
if ( v4 > 0x1F
|| (*(_BYTE *)(output + 11) = enc_flag[v4], v4 = *(_QWORD *)&s_i[v3 + 184], v4 > 0x1F)
|| (*(_BYTE *)(output + 23) = enc_flag[v4], v4 = *(_QWORD *)&s_i[v3 + 40], v4 > 0x1F)
|| (*(_BYTE *)(output + 5) = enc_flag[v4], v4 = *(_QWORD *)&s_i[v3 + 144], v4 >= 0x20) )
{
LABEL_34:
sub_356A0(v4, 32LL, &off_2481F8);
}
result = enc_flag[v4];
*(_BYTE *)(output + 18) = result;
return result;
}
```
* Ở hàm này chính là enc nhân của flag. Ta thấy hàm này được gọi 4 lần và nội dung bên trong hàm là: Đầu tiên có một dữ liệu được lưu tại ***.rodata:0000000000039560***, lưu các giá trị này vào một mảng. Sau đó tính toán thêm một biến `v4` và lấy giá trị của ***.rodata:0000000000039560*** tại vị trí của v4 đó. Cứ thế lặp đi lặp lại 4 lần.Khi xong enc round 4 sẽ kiểm tra vị trí của các bytes ngẫu nhiên. Nếu đúng thì nó sẽ thực hiện swap (có thể đọc thêm ở phần ASM).


> * Ta sẽ lấy ví dụ như sau:
>
> 
>
> 
>
> 
>
> 
>
> * Nếu như so sánh đúng thì *`r14`* sẽ được tải vào *`[rsp+128h+var128]`*, sau đó chuyển *`[rsp+128h+var128]`* này cho cl. Tức là sau khi xong round 4 thì *`[rsp+128h+varDF]`* sẽ được chuyển vào r14 rồi so sánh đúng thì swap vị trí này với cl được gán ban đầu là *`[rsp+128h+varD7]`*.
* Tổng quan của chương trình như sau:
```
# Repeat 4 rounds #
enc_flag = []
for i range (32):
enc_flag = sbox_i[out[i]]
for i range (32):
out[i] = S_i[enc_flag[i]]
# Check and swap #
for i range (32):
check_enc[check_pos[i]] = out[i]
```
* Bây giờ việc còn lại cuối cùng để ra flag chính là đảo ngược lại chương trình này ta sẽ có kết quả. Đảo ngược ở đây cũng giống như việc giá trị 0 đang ở tại vị trí 30 thì giờ ta làm lại tại vị trí 0 thì là số 30 . Cụ thể ta sẽ Swap -> check -> đổi lại sbox -> enc_flag.
* Đây là script để giải challenge này:
https://github.com/Okoonzz/03/blob/scr1pt/ch3ckp@ss.py