הסתרת לוגים ב Event Viewer

הקדמה
במאמר הבא אעסוק בהסתרת לוגים מה-Event Viewer של Windows. נתחיל במעט רקע על ה-Event Viewer, לאחר מכן נעבור למבנה של קבצי ה-Evtx – קבצי הלוג, ובסוף נציג שתי שיטות שבאמצעותן ניתן להסתיר לוגים ב-Event Viewer.

רקע

הערה: התוכן בחלק זה מופנה לקוראים חסרי היכרות עם ה-Event Viewer. במידה ואינכם כאלה, ניתן לדלג עד ההסבר על ה-EVTX Format.

Event Viewer

Event Viewer הוא כלי ב-Windows שמציג מידע מפורט לגבי אירועים שמתרחשים במחשב שלך (או במחשב אחר אם הוא מעביר אותם אליך או שייבאת ממנו את המידע). ה-Event Viewer יודע לפרסר את קבצי ה-EVTX, לסנן לוגים מתוכם באמצעות XML Queries וכך להפוך את העבודה מולם לנוחה. בנוסף הוא יכול לייצא לוגים בצורת EVTX, XML, TXT ו-CSV.

EVTX

Evtx הוא הפורמט החדש (החל מ Vista ו Server 2008) לתיעוד אירועים במערכת Windows. למעשה EVTX בא להחליף את הפורמט EVT ששימש כפורמט הקודם. EVTX מכיל פיצ'רים חדשים, מידע בצורת XML, תכנית גרפית חדשה שמציגה את המידע – Event Viewer ושכתוב של ה Windows Event Log service.
לכל Event יש שדות שהוא מתאר באמצעותם מידע. להלן מספר שדות מעניינים:
1.     Source – מקור הלוג. לדוגמה SQL Server, Outlook אוMicrosoft Windows Logging ללוגים אבטחתיים.
2.     Event ID – מספר המייצג את הסוג הספציפי של ה Event. לדוגמה EventID מספר 4625  מייצג ניסיון התחברות כושל.
3.     Level דרגת החשיבות של ה-Event. ה-Level-ים הבאים קיימים ב-System Logs וב-Application Logs:
a.   Information – מצביע על כך שהתחרש משהו באפליקציה או רכיב במערכת. לדוגמה שמשאב נוצר בהצלחה.
b.     Warning – מצביע על כך שהתרחשה בעיה שעלולה להשפיע על שירות ואף לגרור בעיות קריטיות יותר אם לא תינקט פעולה בעניין.
c.      Error – מצביע על כך שהתרחשה בעיה שעלולה לגרור השפעה על חלקים מחוץ לאפליקציה שבה זה התרחש.
d.     Critical – מצביע על כשל שהתרחש בתוך אפליקציה או רכיב כלשהו בעיה מסוימת שהאפליקציה או הרכיב אינם מסוגלים להתגבר עליו בעצמם.
e.    Success Audit – רלוונטי רק ללוגים אבטחתיים. מציין שפעולה הושלמה בהצלחה. למשל התחברות מוצלחת.
f.     Failure Audit – בהמשך ל-Success Audit, גם Level זה רלוונטי רק ללוגים אבטחתיים. מציין שפעולה נכשלה. למשל, נגיד ניסיון התחברות כושל.
4.     User – שם המשתמש שאליו משויך ה-Event. Event שנוצר כתוצאה משגיאה בתהליך מסוים יקבל את ה-User שאליו משויך התהליך.
5.     Keywords – רשימה של קטגוריות או תגיות שיכולות לעזור בסינון ה-Event-ים. למשל “Security” או “Network”.
6.     Computer – השם של המחשב שבו התרחש ה-Event. בדרך כלל זה יהיה השם של המחשב המקומי אך במידה ומחשב אחר עשה Forward ל-Event למחשב שלך אתה תראה שם את השם לו. בנוסף, זה יכול להיות השם הישן של המחשב שלך אם הוא השתנה בעבר.
7.     Date and Time – הזמן שה-Event קרה.
8.     Process ID – המספר המזהה של התהליך בו ה-Event קרה.
9.     Thread ID – המספר המזהה של ה-Thread בו ה-Event קרה.
ישנם שדות נוספים אך מיותר להרחיב על כולם.
כל Event חייב להיות משויך ל-Chanel אחד מבין שניים אפשריים – Windows Logs שכולל את הלוגים Application.evtx, Security.evtx, System.evtx, Setup.evtx ו- Forwarded.evtx או Application and Services Logs שמכיל לוגים כגון Internet Explorer.evtx או Windows Powershell.evtx. התרשים הבא מתאר את החלוקה הזו:



Windows Event Log Service

ה-Windows Event Log Service הוא שירותי שאחראי לתחזק את הלוגים של המערכת ככלל, של חלקים במערכת ושל אפליקציות המתעדות באמצעות לוגים. השירות חושף פונקציות בהן תוכנות יכולות להשתמש כדי לתחזק ולנהל Event Logs ולבצע עליהם אופרציות מסוימות. כך אדמיניסטרטורים יכולים לעבוד בצורה שוטפת מוך אותם לוגים כדי לאתר ולנתח בעיות במערכת.
כעת צברנו מספיק רקע כדי להיכנס לנבכי הפורמט EVTX.

EVTX Format

אז עד לכאן היה המבוא ל-Event Logs ב Windows. הנושא הבא שנעבור אליו יהיה מבנה קבצי ה-EVTX. הסיבה שאנחנו רוצים להכיר את המבנים האלה, היא כדי "לנתח" קבצי EVTX בשלב הPost Exploitation של התקיפה ולהסיר משם לוגים שיכולים לתעד את הפעילות שלנו. פעולה זו תעזור לנו לנקות ראיות אשר עשויות להסגיר אותנו.
מבנה ה-EVTX מחולק לשלושה חלקים:
  1. File Header
  2.  Chunks
  3. Trailing empty values

File Header

ה-File Header שומר את המידע הבסיסי לגבי ה-EVTX. באמצעותו נוכל לנווט בקובץ. להלן מספר פרמטרים שמעניינים אותנו ב Header:
שדה
ערך
גודל
Offset
Signature – ה-Magic Number של קבצי EVTX.
ElfFile\x00
8
0
First chunk number – המספר של ה Chunk הראשון
משתנה
8
8
Last chunk number – המספר של ה Chunk האחרון
משתנה
8
16
Next record identifier – ה-Record identifier של הלוג הבא שיווצר.
משתנה
8
24
Header size – הגודל של כל ה-Header
128 – קבוע
4
32
Header block size – הגודל של ה-File Header, מה שאומר למעשה שזה ה-Offset ל-Chunk הראשון
4096 – קבוע
2
38
Number of chunks – מספר ה-Chunk-ים בקובץ
משתנה
2
42
Checksum CRC32 של ה-120 Bytes הראשונים של ה-File Header.
משתנה
4
124

הערה: בפשטות, CRC32 Cyclic Redundancy Check 32 הוא אלגוריתם שיוצר Checksum והוא מתואר ב-RFC 1952.
לרשימה המלאה:

Chunks

כל Chunk מכיל Event Records שהם למעשה השורות עצמן בלוג. כל Chunk מורכב מ-Chunk Header, מערך של Event Records ועוד Byte-ים לא משומשים.
ה-Chunk Header מכיל מידע שיעזור לנו להתמצא ב-Chunk הנוכחי. להלן מספר שדות מעניינים במבנה של ה=Chunk Header:
שדה
ערך
גודל
Offset
Signature – החותמת שנמצאת בתחילת כל Chunk.
ElfChnk\x00
8
0
First event record number – המספר של ה-Event record הראשון ב Chunk הזה.
משתנה
8
8
Last event record number – המספר של ה-Event record הראשון ב-Chunk הזה.
משתנה
8
16
First event record identifier – ה-ID של ה-Event record הראשון ב-Chunk הזה.
משתנה
8
24
Last event record identifier – ה-ID של ה-Event record האחרון ב-Chunk הזה.
משתנה
8
32
Header Size – הגודל של ה-Header של ה-Chunk.
128 – קבוע
8
40
Last event record data offset – ה-Offset מתחילת ה-Header של ה-Chunk הנוכחי ל-Event record האחרון ב-Chunk הנוכחי.
משתנה
4
44
Event records checksum –ה-CRC32  של- Event records ב-Chunk הזה
משתנה
4
52
Checksum – ה-CRC32 של 120 ה-Byte-ים הראשונים ושל ה-128-512 של ה-Chunk הנוכחי.
משתנה
4
124

למבנה המלא:

Event Record

ה-Event Record בעל גודל משתנה ומורכב מהשדות הבאים:
שדה
ערך
גודל
Offset
חתימה
\x2a\x2a\x00\x00
4
0
גודל – גודל הרשומה כולל החתימה והגודל
משתנה
4
4
ה-Event record identifier
משתנה
8
8
התאריך והזמן שבהם נכתבה הרשומה.
משתנה
8
16
ה-Event עצמו בצורת Binary XML
משתנה
משתנה
24
העתק של הגודל
משתנה
4
משתנה

מחיקת Records מה-Event Viewer באמצעות עריכת קובץ ה-Evtx

בתרשים הבא ניתן לראות Chunk מ-Evtx כלשהו. הנקודות שמעניינות אותנו בתרשים זה ה-Record-ים בגוונים הכחולים. ה-Record העליון מבניהם (מספר X+1) הולך להיות ה-Record שנשתמש בו כדי להסתיר את ה-Record שמטריד אותנו. ה-Record התחתון מבניהם (מספר X+2) הולך להיות ה Record שנרצה להסתיר. הרעיון שעומד מאחורי השיטה הזו מאוד פשוט להבנה. ה-Event Viewer מסתכל על ה-Size הראשון של ה-Record ועל ההעתק שלו בסוף ה-Record. לפי זה הוא יודע מה גודל ה-Record. לאחר מכן הוא קורא את המידע של ה-Record הראשון, וכאשר הוא הוא מגיע לסוף ה-BinaryXML של ה-Record הראשון הוא מבחינתו סיים לקרוא את תוכן ה-Record. לאחר מכן הוא קופץ ל-Record הבא. מה שזה אומר זה שבעצם אם אני אכלול ב-Size של ה-Record הן את הגודל של ה-Record הראשון והן את הגודל של ה-Record השני ואעשה כך גם בהעתק של ה-Size של ה-Record השני אני בעצם "אכסה" את ה-Record השני.
כך נראה המצב לפני השינוי:


כך נראה המצב לאחר השינוי:
לאחר שעיקר הטכניקה ברור, נעבור לפרקטיקה. מצורף קובץ Example_before.evtx (לינק: https://drive.google.com/open?id=1bnKsPWXW7tMJfsTys6XYCknw_QP52uoj) אותו נערוך. בנוסף מצורף הקובץ Example_After.Evtx (לינק: https://drive.google.com/open?id=1HxAdBNKs2G8KD-SadnDNmiZml-QMcO_j) לאחר השינויים.
ב-Example.Evtx יש לנו 15 לוגים. ה- Record האחרון בעל EventRecord ID = 15 הוא ה-Log אותו ננסה להסתיר. נשתמש ב-Record אחד לפני האחרון, בעל ה-EventRecord ID = 14, על מנת לעשות זאת. השלבים שלנו הם כאלה:
  1. נשנה את ה-Next Record Identifier ב File Header של ה-Evtx להחיל את הערך שהוא מכיל פחות 1.
  2. נשנה את ה-File Header CRC32 Checksum של ה-120 Byte-ים הראשונים להחיל את ה CRC32 Checksum החדש (לאחר השינוי של שלב 1).
  3. נשנה את ה-Last Event Record Number שנמצא ב-Chunk Header להחיל את ה-Event Record Number של ה-Record האחד לפני אחרון (כי הוא בעצם הופך להיות ה Event Record האחרון).
  4. נשנה את ה-Last Event Record ID שנמצא ב-Chunk Header להחיל את ה-Event Record ID של ה-Record האחרון.
  5. נשנה את ה-Offset ל-Last Event Record Data כך שיכיל Offset ל-Event Record האחד לפני אחרון. גם ה-Offset מוגדר ב-Chunk Header.
  6. נשנה את הגודל של ה-Size (הראשון מבין השניים) של הלוג האחד לפני אחרון (זה עם ה EventRecord ID = 14) להחיל את הגודל גם של ה-Record הזה וגם של ה-Record שאנחנו מסתירים (האחרון).
  7. נשנה את ההעתק של הגודל של ה-Record האחרון כך שיכיל גם הוא את הסכום של הגדלים של שני ה-Record-ים בהם אנו עוסקים עד כה.
  8. נשנה את ה-Event Records Checksum שנמצא ב-Chunk Header כך שיכיל את ה-CRC32 Checksum של ה-Event Records לאחר השינויים שעשינו.
  9. נשנה את ה-Checksum של ה-0-120 + 128-512 Byte-ים שנמצא ב-Chunk Header כך שיכיל את ה-CRC32 Checksum של ה-0-120 + 128-512  Byte-ים לאחר השינוי.

אלו השלבים שנבצע. כעת נעבור שלב שלב ונבצע אותו ולאחר מכן נראה את התוצאה. אך לפני שנתחיל, אחד הדברים שאתם עלולים לראות לאחר העריכה של הקבצים כאשר תנסו לפתוח את הקובץ ב-Event Viewer זה את ההודעה הבאה:

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

ביצוע

כעת נעבור לעריכת הלוג.
שלב 1 – נשנה את ה-FileHeader.NextRecordIdentifier.
המצב לפני:


המצב אחרי:

שלב 2 – שינוי ה-FileHeader.CRC32Checksum.
כדי לחשב CRC32 נעזר ב Python:

ה-data יכיל את ה-Byte-ים עליהם נבצע את החישוב. ניקח את 120 ה-Byte-ים הראשונים של ה-File Header שזה אומר 0x00000000-0x00000078. נבצע את החישוב ונקבל 0x8b80df0d. המצב לפני השינוי:
המצב אחרי השינוי:

שלב 3 – נשנה את ה-Chunk.LastEventRecordNumber.
בשלב הזה קודם כל נאתר את ה-Chunk הנכון, כלומר זה שמכיל את ה-Record שלנו. אולם במקרה של הלוג שאנחנו עורכים כעת יש רק Chunk אחד אולם אפשרי למצוא Chunk-ים לפי החיפוש ElfChnk. ה-Offset הוא 16 מתחילת ה-Chunk לכן כך זה יראה לפני השינוי:
וכך אחרי השינוי:

שלב 4 – נשנה את ה-Chunk.LastEventRecordIdentifier.
ה Offset הוא 32 מתחילת ה Chunk. המצב לפני השינוי:
המצב אחרי השינוי:

שלב 5 – נשנה את ה-Chunk.OffsetToLastEventRecord.
ה-Offset החדש צריך להיות זה של ה-Record האחד לפני אחרון. נחפש 2A 2A 00 00 שזה החותמת של כל Record ונמצא את כל ה-Records. אלו הן התוצאות האחרונות:
ה-offset הישן הוא 0x00003000 והחדש הוא 0x00002E68. מכיוון שהערך אמור להיות ביחס לתחילת ה-Chunk והוא מתחיל ב-0x00001000 כמו שראינו כבר אז הערכים יהיו פחות 0x1000. כך המצב נראה לפני השינוי:
כך הוא נראה אחרי השינוי:

שלב 6 – שינוי גודל ה-Record האחד לפני האחרון כך שיכיל את סכום שני ה-Record-ים. הגודל של האחד לפני האחרון הוא 0x198. הגודל של האחרון הוא גם 0x198. הסכום הוא 0x330. כך נראה הגודל (הראשון) של ה Record אחד לפני אחרון, לפני השינוי:
ככה הוא נראה אחרי השינוי:

שלב 7 – שינוי גודל ה-Record האחרון כך שיכיל את סכום שני ה-Record-ים. כבר אמרנו שהסכום של שני ה-Record-ים הוא 0x330. כך נראה הגודל (ההעתק, כלומר האחרון) של ה-Record האחרון, לפני השינוי:
כך הוא נראה אחרי השינוי:

שלב 8 – נשנה את ה-Chunk.EventRecordsChecksum כך שיכיל את ה-CRC32 Checksum של כל ה-Record-ים ב-Chunk הזה לאחר כל השינויים שנעשו. ה-Offset של ה-Data עליו נבצע את החישוב הוא 0x00001200-0x00003198. כך נראה ה CRC32 Checksum הזה לפני השינוי:
תוצאת ה-Checksum היא 0xfc4d014f.
כך הוא נראה אחרי השינוי:

שלב 9 – נשנה את ה-CRC32 של ה-0-120 + 128-512 Byte-ים הראשונים של ה-Chunk הזה. ה-Data הכולל יהיה ה-Data שנמצא מ-Offset 0 עד Offset 78 ביחס לתחילת ה-Chunk כלומר בכתובות 0x00001000-0x000010078 פלוס ה-Data שנמצא מ-0ffset 0x80 עד Offset 0x200 ביחס לחתחילת ה-Chunk כלומר בכתובת 0x000010080-0x00001200.
כך המצב נראה לפני השינוי:
תוצאת ה-Checksum היא 0x68f64381.
כך הוא נראה אחרי השינוי:
לאחר מכן נשמור את השינויים ונפתח את הלוג ב-Event Viewer ונראה שיש לנו רק 14 Record-ים וה-Record האחרון הוא ה-Record האחד לפני האחרון במצב שהיה לפני עריכת הקובץ.

זיהוי

כמו שניתן להעלים את ה-Record ניתן גם להחזיר אותו. לכן אם נרצה להימנע מהאפשרות של החזרת ה-Record נאפס את כל ה-Data שלהם ואז נבצע את ה-Checksum-ים הנחוצים מחדש.

דגשים

מאחר ומחקנו את ה-Log האחרון אין צורך לעדכן את ה-Event Record Identifier (בשני המיקומים) ועל כן פעולה זו לא נקלחה בחשבון בביצוע אך יש ליישמה במקרה של שינוי record שהוא לא האחרון. בנוסף לכך, הרעיון הוא לבנות אוטומציה שתבצע את הפרוצדורה הזו. אשאיר את החלק הזה לכם מאחר וזו לא מטרת המאמר. כאשר נרצה ליישם את המטרה הזו בשטח נצטרך לכבות את השירות של ה-Event Viewer כדי לשנות את הלוגים שהוא מתחזק אחרת לא נוכל לערוך אותם. לאחר העריכה נדליק את ה-Event Vviewer.

מחיקת Records מה-Event Viewer באמצעות WINAPI

מיקרוסופט מנגישה לנו API אשר מאפשר לנו "למחוק" לוגים של Event Viewer. כתבתי את המילה למחוק במירכאות כי ה-API שאני מדבר עליו למעשה יוצר Event Log חדש מ-Event Log קיים על פי XML Query.
הפונקציה בה נשתמש היא EvtExportLog אשר מקבלת את הפרמטרים הבאים:

  •  Session Handle ל-Session מרוחק שמוחזר מהפונקציה EvtOpenSession. יהיה NULL כאשר מדובר בעבודה מול הלוגים המקומיים.
  • Path – הנתיב ל-Event Log.
  • Query – השאילתה שמציינת מה אני רוצה לייצא מהלוג הזה בעצם.
  •  TargetFilePath – הנתיב לקובץ היעד שיקבל את כל ה-Event Records. שימו לב שאסור שהקובץ בנתיב הזה יהיה קיים במערכת אחרת תופק שגיאה.
  • Flags – דגלים שמציינים האם ה-Event-ים הגיעו מ-Channel או מ-Log file.
לתיעוד המלא של הפונקציה:


על מנת לממש את הדרך הזו נבנה קוד שמקבל כפרמטר נתיב לקובץ Evtx ו-Event Record ID. לאחר מכן הקוד ייצא את כל ה-Event Record-ים מה-Evtx המקורי מלבד ה-Event Record ID וישמור את זה בקובץ Evtx חדש. אולם גם כאן נאלץ לכבות את השירות של Event Viewer כדי להחליף בין הקבצים. קוד לדוגמה: https://drive.google.com/open?id=1rhDhPtnJx0h2B0RAmwNkUVDUZkNlN0__

סיכום

דיברנו על שתי דרכים להתחמק מזיהוי ולהקשות על החקירה דרך אחת היא באמצעות הבנת הפורמט של הקבצים שמכילים את הלוגים של ה Event Viewer הלא הם קבצי ה Evtx. דרך שניה היא באמצעות WinAPI שמקל לנו על החיים במקרה הזה. אישית, הדרך הראשונה (כמובן עם אוטומציה) היא העדיפה עליי מכיוון שאין בה שימוש ב-WinAPI בניגוד לדרך השניה. זה יתרון כי ניתן לנטר את הפעולה באמצעות ניטור פונקציית ה-EvtExportLog, אולם היא יותר מורכבת ודורשת יותר עבודה.

מקווה שהמאמר עזר לכם, לכל שאלה או הערה אתם מוזמנים ליצור איתי קשר במייל Orih90@gmail.com. נתראה במאמרים הבאים.

תגובות