GammaCyber

מעבדת בלאקבוקס — חדר בריחה טכנולוגי

כל עשרת שלבי המעבדה ותוכן הראיות הנגיש.

הגשת תשובות, רמזים ובדיקת AI דורשים חיבור לאתר. פתרונות ששת שלבי הליבה נחשפו באתר כדי לפתוח את שלבי ההמשך.

שלב 1 · שלב 1 — מי דיבר עם מי
שלב 1 — מי דיבר עם מי

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

edge_capture.log
10:02:11.083 10.0.0.12:53144 -> 10.0.0.1:53    UDP DNS query intranet.local
10:02:11.129 10.0.0.1:53    -> 10.0.0.12:53144 UDP DNS response NOERROR
10:02:12.402 10.0.0.31:49822 -> 10.0.0.20:443   TCP ACK  len=0
10:02:13.010 10.0.0.47:42001 -> 10.0.0.18:445   TCP SYN  seq=18421
10:02:13.011 10.0.0.7:5353  -> 224.0.0.251:5353 UDP mDNS query printer.local
10:02:13.019 10.0.0.18:445   -> 10.0.0.47:42001 TCP SYN,ACK seq=991 ack=18422
10:02:13.020 10.0.0.47:42001 -> 10.0.0.18:445   TCP RST  seq=18422
10:02:13.184 10.0.0.47:42002 -> 10.0.0.6:445    TCP SYN  seq=55210
10:02:13.201 10.0.0.6:445    -> 10.0.0.47:42002 TCP RST,ACK
10:02:13.398 10.0.0.24:54401 -> 10.0.0.20:443   TCP PSH,ACK len=612
10:02:13.447 10.0.0.47:42003 -> 10.0.0.23:445   TCP SYN  seq=12004
10:02:13.460 10.0.0.23:445   -> 10.0.0.47:42003 TCP RST,ACK
10:02:13.711 10.0.0.47:42004 -> 10.0.0.11:445   TCP SYN  seq=33017
10:02:13.724 10.0.0.11:445   -> 10.0.0.47:42004 TCP RST,ACK
10:02:14.003 10.0.0.16:49214 -> 10.0.0.1:53    UDP DNS query updates.vendor.net
10:02:14.061 10.0.0.47:42005 -> 10.0.0.29:445   TCP SYN  seq=7612
10:02:14.079 10.0.0.29:445   -> 10.0.0.47:42005 TCP RST,ACK
10:02:14.880 10.0.0.47:42110 -> 10.0.0.18:445   TCP ACK  len=0
10:02:14.892 10.0.0.47:42110 -> 10.0.0.18:445   SMB2 SESSION_SETUP user=backup_svc auth=NTLM
10:02:14.917 10.0.0.18:445   -> 10.0.0.47:42110 SMB2 STATUS_LOGON_FAILURE
10:02:15.044 10.0.0.8:68     -> 10.0.0.1:67    UDP DHCP request
פתרון והסבר
הפתרון של השלב

10.0.0.47 ביצעה SYN scan חצי־פתוח על SMB בפורט 445; לאחר שמצאה את 10.0.0.18 פתוחה היא ניסתה אימות NTLM לחשבון backup_svc, שנכשל.

הרעש הלגיטימי מפוזר בין פרוטוקולים ויעדים רגילים. לעומתו, 10.0.0.47 שולחת SYN במהירות לפורט 445 בחמישה יעדים לא־רציפים. מול השרת היחיד שהחזיר SYN-ACK היא משיבה RST, ולכן לחיצת היד לא מושלמת — חתימה קלאסית של SYN scan חצי־פתוח. פחות משנייה אחר כך היא חוזרת לשרת שנמצא ומנסה SMB2 SESSION_SETUP באמצעות NTLM. הלוג מוכיח ניסיון אימות אך לא מספיק כדי לקבוע לבדו Pass-the-Hash.
שלב 2 · שלב 2 — הקובץ שמתחזה
שלב 2 — הקובץ שמתחזה

הקובץ vacation.png נפתח כתמונה תקינה, אך מערכת ה-DLP סימנה בו תוכן נוסף. נתחו את ההקסה ואת פלט הסריקה: אילו שני פורמטים חיים באותו קובץ, ואיפה מתחיל המטען השני?

vacation.png — hexdump
binwalk.txt
00000000  89 50 4e 47 0d 0a 1a 0a  00 00 00 0d 49 48 44 52  |.PNG........IHDR|
00000010  00 00 01 40 00 00 00 f0  08 06 00 00 00 71 2d bd  |...@.........q-.|
00000020  6b 00 00 00 19 74 45 58  74 53 6f 66 74 77 61 72  |k....tEXtSoftwar|
00000030  65 00 43 61 6d 65 72 61  20 45 78 70 6f 72 74 00  |e.Camera Export.|
...
00018f20  00 00 00 00 49 45 4e 44  ae 42 60 82 50 4b 03 04  |....IEND.B`.PK..|
00018f30  14 00 00 00 08 00 91 52  36 5b 7a 19 22 8c 2f 01  |.......R6[z."./.|
00018f40  00 00 a0 02 00 00 0c 00  00 00 6e 6f 74 65 73 2e  |..........notes.|
00018f50  74 78 74 8d 90 41 6b c3  30 10 85 ef fd 15 43 2f  |txt..Ak.0.....C/|
...
000190a0  50 4b 03 04 14 00 00 00  08 00 91 52 36 5b 44 a1  |PK.........R6[D.|
000190b0  9c 72 8b 03 00 00 00 08  00 00 0d 00 00 00 73 74  |.r............st|
000190c0  61 67 65 72 2e 62 69 6e  7b 9f 22 18 e1 4c 00 a7  |ager.bin{."..L..|
ראיה נוספת: binwalk.txt
DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             PNG image, 320 x 240, 8-bit/color RGBA
33            0x21            PNG tEXt chunk
102184        0x18F28         End of PNG image (IEND)
102192        0x18F30         ZIP local file header, version 2.0
102560        0x190A0         ZIP local file header, file name: stager.bin
104021        0x19655         End of ZIP archive
פתרון והסבר
הפתרון של השלב

זה Polyglot תקין מסוג PNG+ZIP: התמונה מתחילה ב-0x0, ואחרי IEND מתחיל ארכיון ZIP ב-offset ‏0x18F30 שמכיל notes.txt ו-stager.bin.

הקובץ מתחיל ונגמר כמבנה PNG תקין, ולכן מציג תמונה. מיד אחרי chunk הסיום IEND מופיעה חתימת ZIP ‏50 4B 03 04. קורא PNG סובל בדרך כלל מידע נגרר, בעוד קורא ZIP מוצא את ה-central directory מן הסוף; לכן אותו רצף בתים מתקבל בשני המפענחים. חילוץ החל מ-0x18F30 יחשוף את הארכיון ואת stager.bin, בלי להריץ דבר.
שלב 3 · שלב 3 — הפונקציה האילמת
שלב 3 — הפונקציה האילמת

לפניכם פונקציה בלי שם משמעותי ובלי תיעוד. מה היא מחשבת? תנו שם מדויק לפעולה.

mystery.py
def f(n):
    c = 0
    while n:
        c += n & 1
        n >>= 1
    return c

print(f(7))    # ?
print(f(8))    # ?
print(f(255))  # ?
פתרון והסבר
הפתרון של השלב

הפונקציה סופרת כמה ביטים דלוקים (1) יש בייצוג הבינארי של n — popcount / Hamming weight

זה מנגנון קלאסי: n & 1 מחזיר את הביט הפחות משמעותי, ההזזה ימינה מקדמת לביט הבא, והלולאה נעצרת כשכל הביטים נגמרו. 7=111 → 3, 8=1000 → 1, 255=11111111 → 8. השם המקצועי הוא popcount (או Hamming weight), ומשתמשים בו בהצפנה, בדחיסה ובאלגוריתמי גרפים.
שלב 4 · שלב 4 — ההודעה המוצפנת
שלב 4 — ההודעה המוצפנת

יירטנו blob טקסטואלי בלי מטא־דאטא מסגיר. קבעו את שרשרת העיבוד, מצאו את המפתח הקצר ופענחו את ההודעה. נדרש להסביר גם אילו סימנים סטטיסטיים הובילו אתכם.

intercepted.blob
byte_profile.txt
ajBgPGwubihvNH9LbD9sPXQraC55I2w3azhu
ראיה נוספת: byte_profile.txt
sample_length        : 36 chars
alphabet             : A-Z a-z 0-9 + /
padding              : absent
candidate_decode_len : 27 bytes
shannon_entropy      : 4.31 bits/byte
index_of_coincidence : even=0.071  odd=0.068  all=0.039
key_length_estimate  : 2
printable_if_xor     : high for key candidates 0x20..0x7e
פתרון והסבר
הפתרון של השלב

השרשרת היא Base64 ואז XOR מחזורי במפתח 2D 71; הטקסט המפוענח הוא GAMMA_CYBER:ANALYZE_TRAFFIC.

האלפבית מצביע על Base64, אך הפלט הבינארי שלו אינו טקסט. מדד צירוף גבוה יותר כשמפצלים בתים לפי זוגי/אי־זוגי הוא רמז למפתח XOR מחזורי באורך 2. חיפוש 65,536 המפתחות האפשריים ודירוג לפי ASCII קריא מחזיר 2D 71. לאחר XOR מתקבל GAMMA_CYBER:ANALYZE_TRAFFIC. האנטרופיה בינונית — גבוהה מטקסט רגיל אך נמוכה מהצפנה חזקה — ולכן קידוד ו-XOR קצר הם השערה טובה יותר מצופן מודרני.
שלב 5 · שלב 5 — הבאג הרועד
שלב 5 — הבאג הרועד

שרת מקבל אורך מתוך header חתום. בדיקת הגבולות נראית תקינה במבט ראשון, אך קלט שלילי גורם לכתיבה עצומה. זהו את החולשה, הסבירו את ההמרה המדויקת והציעו תיקון בטוח.

packet.c
asan.log
#include <stdint.h>
#include <string.h>

int copy_payload(const uint8_t *packet, size_t packet_size) {
    int16_t wire_len;
    char dst[64];

    if (packet_size < 2) return -1;
    memcpy(&wire_len, packet, sizeof(wire_len));

    if (wire_len <= 64) {
        memcpy(dst, packet + 2, wire_len);
        return 0;
    }
    return -1;
}
ראיה נוספת: asan.log
input header bytes : f0 ff
wire_len (int16_t)  : -16
bounds check        : -16 <= 64  => true
memcpy size_t arg   : 18446744073709551600

ERROR: AddressSanitizer: negative-size-param / stack-buffer-overflow
WRITE beyond stack object 'dst' (size 64)
פתרון והסבר
הפתרון של השלב

זו חולשת Integer Signedness שמובילה ל-stack buffer overflow: אורך int16_t שלילי עובר את הבדיקה ואז מומר ל-size_t עצום ב-memcpy. יש לדחות אורך לא־חיובי, לבדוק מול sizeof(dst) ומול packet_size, ורק אז להמיר ל-size_t.

wire_len הוא int16_t חתום ולכן ‎-16 מקיים ‎-16 <= 64. בקריאה ל-memcpy הוא מומר ל-size_t, ועל מערכת 64 ביט מתקבל 2^64-16 — גודל עצום שגולש מעבר ל-dst שעל המחסנית. תיקון בטוח בודק תחילה wire_len > 0, שומר אותו במשתנה size_t רק לאחר האימות, דורש len <= sizeof(dst), ודורש len <= packet_size - 2. כך מטפלים גם ב-signedness וגם בקלט קטוע.
שלב 6 · שלב 6 — לאן זה מדבר
שלב 6 — לאן זה מדבר

בתוך לוג DNS רועש מסתתר beacon עם jitter. זהו את הדומיין, הוכיחו את טווח התזמון, קבעו את הקידוד וחברו את תתי־הדומיין כדי לחלץ את המידע שהודלף.

dns_queries.txt
timing_stats.txt
12:00:01  ws-14  clients4.google.com                 A
12:00:03  ws-27  INAVGRJN.sync-cdn.link              TXT
12:00:08  ws-09  login.microsoftonline.com           A
12:00:21  ws-14  api.github.com                      A
12:00:34  ws-27  GUYDUSSJ.sync-cdn.link              TXT
12:00:42  ws-03  time.windows.com                    A
12:00:59  ws-27  KRKEKURN.sync-cdn.link              TXT
12:01:02  ws-18  ocsp.digicert.com                    A
12:01:19  ws-09  fonts.googleapis.com                A
12:01:34  ws-27  IZHVKTSE.sync-cdn.link              TXT
12:01:47  ws-03  telemetry.vendor.net                AAAA
12:02:02  ws-27  INAVGRJN.sync-cdn.link              TXT
12:02:17  ws-14  intranet.local                      A
12:02:35  ws-27  GUYDUSSJ.sync-cdn.link              TXT
12:02:39  ws-18  ntp.org                             A
ראיה נוספת: timing_stats.txt
host            : ws-27
parent_domain   : sync-cdn.link
observed_times  : 12:00:03, 12:00:34, 12:00:59, 12:01:34, 12:02:02, 12:02:35
intervals_sec   : 31, 25, 35, 28, 33
mean            : 30.4 sec
range           : 25..35 sec
exact_periodic  : false
label_alphabet  : A-Z, 2-7
label_length    : 8
פתרון והסבר
הפתרון של השלב

ws-27 מתקשרת ב-DNS beaconing עם sync-cdn.link ב-jitter של 25–35 שניות. תתי־הדומיין מקודדים ב-Base32; חיבור ארבעת ה-chunks הראשונים מפוענח ל-CASE-50:JITTER-FOUND.

האירועים של ws-27 מול sync-cdn.link מופיעים במרווחים 31, 25, 35, 28 ו-33 שניות: ממוצע 30.4 שניות עם jitter מכוון בטווח 25–35, שמטשטש חוק זיהוי של מחזור קבוע. האלפבית A-Z ו-2-7 וה-labels באורך אחיד מצביעים על Base32. חיבור ארבעת החלקים הייחודיים הראשונים ופענוחם מחזיר CASE-50:JITTER-FOUND; שתי הבקשות האחרונות הן חזרות שידור. זהו גם beacon וגם DNS exfiltration, לא רק דומיין חשוד.
מתקדם 1 · מתקדמים 1 — חטיפת Session והרכבת Fragments
מתקדמים 1 — חטיפת Session והרכבת Fragments

נתחו שיחת TCP ופרגמנטים של פרוטוקול מותאם אישית. חשבו את SEQ/ACK הדרושים להזרקה, הרכיבו את המטען לפי offset ולא לפי זמן ההגעה, והסבירו מדוע IDS נאיבי עלול להחמיץ אותו.

session.pcap.txt
# src -> dst                         flags      seq    ack    len
10.1.4.8:41000 -> 10.1.4.20:9001      SYN        1000   0      0
10.1.4.20:9001 -> 10.1.4.8:41000      SYN,ACK    7000   1001   0
10.1.4.8:41000 -> 10.1.4.20:9001      ACK        1001   7001   0
10.1.4.20:9001 -> 10.1.4.8:41000      PSH,ACK    7001   1001   18
10.1.4.8:41000 -> 10.1.4.20:9001      ACK        1001   7019   0

# captured IP fragments, datagram id=0x4a2 (offset shown in bytes)
arrival=1  offset_bytes=16  MF=1  payload="|AUTH|role=admin"
arrival=2  offset_bytes=32  MF=0  payload="|END"
arrival=3  offset_bytes=0   MF=1  payload="HELLO|nonce=7f3a"
מתקדם 2 · מתקדמים 2 — Stack Corruption ו-Reverse
מתקדמים 2 — Stack Corruption ו-Reverse

חשבו offset מדויק לשינוי משתנה סמוך במחסנית, כתבו את ארבעת הבתים הדרושים ב-little-endian, ושחזרו את פעולת פונקציית האסמבלי.

auth.c
check.asm
struct Frame {
    char name[24];
    uint32_t admin_flag;
};

int login(int fd) {
    struct Frame f = {0};
    read(fd, f.name, 32);
    return f.admin_flag == 1;
}
ראיה נוספת: check.asm
; rdi = int32_t *arr, esi = count
check:
    xor eax, eax
    test esi, esi
    jle .done
.loop:
    add eax, dword ptr [rdi]
    add rdi, 4
    dec esi
    jne .loop
.done:
    ret
מתקדם 3 · מתקדמים 3 — Known Plaintext נגד XOR
מתקדמים 3 — Known Plaintext נגד XOR

קובץ תמונה הוצפן ב-XOR מחזורי במפתח קצר. השתמשו בחתימת PNG הידועה כדי לחלץ את המפתח, אמתו אותו על chunk מסוג IHDR והסבירו כיצד מפענחים את כל הקובץ.

encrypted_image.hex
offset  encrypted bytes
0000    c2 63 05 74 46 39 51 39
0008    4b 33 4b 3e 02 7b 0f 61
0010    4b 33 4a 73 4b 33 4b c3

known PNG prefix:
        89 50 4e 47 0d 0a 1a 0a
expected chunk near 000C: 49 48 44 52 (IHDR)
מתקדם 4 · מתקדמים 4 — שחזור קובץ שהודלף ב-DNS
מתקדמים 4 — שחזור קובץ שהודלף ב-DNS

ה-collector כתב אירועים בסדר קליטה מבולגן וכולל retransmissions. סננו את הרעש, סדרו את המקטעים, הסירו כפילויות ופענחו את שם הקובץ שהודלף.

resolver_collector.log
ingest=901 ts=14:23:00 host=hr-04 q=03-51332e43.cache-sync.one TXT
 ingest=902 ts=14:22:05 host=hr-04 q=01-46494e41.cache-sync.one TXT
 ingest=903 ts=14:22:11 host=dev-08 q=packages.internal A
 ingest=904 ts=14:23:29 host=hr-04 q=04-5356.cache-sync.one TXT
 ingest=905 ts=14:22:32 host=hr-04 q=02-4e43455f.cache-sync.one TXT
 ingest=906 ts=14:23:07 host=hr-04 q=03-51332e43.cache-sync.one TXT retry=1
 ingest=907 ts=14:23:34 host=mail-02 q=mx.vendor.net MX