הסתרת לוגים ב 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 מחולק לשלושה חלקים:
- File Header
- Chunks
- 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, על מנת לעשות זאת. השלבים שלנו הם כאלה:
- נשנה את ה-Next Record Identifier ב File
Header של ה-Evtx להחיל את הערך שהוא מכיל
פחות 1.
- נשנה את ה-File Header CRC32 Checksum של ה-120 Byte-ים הראשונים להחיל את ה CRC32 Checksum החדש (לאחר השינוי של שלב
1).
- נשנה את ה-Last Event Record Number שנמצא ב-Chunk Header להחיל את ה-Event
Record Number של ה-Record האחד לפני אחרון (כי הוא
בעצם הופך להיות ה Event Record האחרון).
- נשנה את ה-Last Event Record ID שנמצא ב-Chunk Header להחיל את ה-Event
Record ID של ה-Record האחרון.
- נשנה את ה-Offset ל-Last
Event Record Data כך שיכיל Offset ל-Event
Record האחד לפני אחרון. גם ה-Offset מוגדר ב-Chunk Header.
- נשנה את הגודל של ה-Size (הראשון מבין השניים) של
הלוג האחד לפני אחרון (זה עם ה EventRecord ID = 14) להחיל את הגודל גם של ה-Record הזה וגם של ה-Record שאנחנו מסתירים (האחרון).
- נשנה את ההעתק של הגודל של ה-Record האחרון כך שיכיל גם הוא
את הסכום של הגדלים של שני ה-Record-ים בהם אנו עוסקים עד כה.
- נשנה את ה-Event Records Checksum שנמצא ב-Chunk Header כך שיכיל את ה-CRC32
Checksum של ה-Event Records לאחר השינויים שעשינו.
- נשנה את ה-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. נתראה במאמרים הבאים.
תגובות
הוסף רשומת תגובה