0. Windows 11 에서 정상적인 실행이 되지 않으므로, hyper-V 등을 통해서 windows 10에서 실행하는 것을 권장한다.

1. 다운로드 : Python3, Visual studio 2022, DynamoRIO(https://github.com/DynamoRIO/dynamorio/releases)

2. 적절한 경로에 DynamoRIO 압축 해제

3. config.h 에서 input 파일 최대 사이즈 수정  (1*1024*1024 를 아래와 같이 수정)

/* Maximum size of input file, in bytes (keep under 100MB): */

#define MAX_FILE            (100 * 1024 * 1024)

4. x64 Native Tools Command Prompt for VS 2022 열기

git clone https://github.com/googleprojectzero/winafl
cd winafl
git submodule update --init --recursive
mkdir build64
cd build64
cmake -G"Visual Studio 17 2022" -A x64 .. -DDynamoRIO_DIR=I:\DynamoRIO\cmake -DTINYINST=1 -DUSE_DRSYMS=1 -DINTELPT=1 -DUSE_COLOR=1
cmake --build . --config Release

5. harness 작성

#include <iostream>
#include <windows.h>

typedef int(__stdcall* _OHMYGOD)(const char* data); 
_OHMYGOD func;

extern "C" __declspec(dllexport) __declspec(noinline) int fuzzme(const char* path)
{   
    int result = func(path);
    return result;
}

int main(int argc, char *argv[])
{    
    HMODULE hMod = GetModuleHandle(0);
   
    hMod = LoadLibrary(L"I:\\victim\\x64\\Release\\victim.dll");
    if (NULL == hMod)
    {
        printf("dll load error\n");
        return 0;
    }
   
    func = (_OHMYGOD)GetProcAddress(hMod, "ohmygod");
    fuzzme(argv[1]);    
}

6. victim 작성 (타겟 DLL)

#include "pch.h"
#include <iostream>

extern "C" __declspec(dllexport) int ohmygod(const char* path)
{
    std::cout << path << std::endl;
    char data[40] = { 0, };
    char buf[30] = { 0, };
    
    HANDLE hFile = CreateFileA(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
    if (hFile)
    {
        DWORD dwRead;
       
        ReadFile(hFile, data, sizeof(data), &dwRead, NULL);
        std::cout << "read : " << dwRead << std::endl;
        if (dwRead)
        {
            memcpy(buf, data, dwRead+40);
            std::cout << buf;
        }
        CloseHandle(hFile);
    }
    return 0;
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

원활한 crash 발생을 위해 각각 Release 로 빌드한다.

7. 입출력 디렉토리 설정

afl은 사용자가 지정한 입력 디렉토리에 있는 파일들을 퍼징 데이터로 사용한다. 

crash를 유발하지 않는 정상 입력 데이터이어야 첫 실행 시 에러가 발생하지 않는다.

아래 데이터를 1.txt로 저장한다.

abcd

8. 공격

afl-fuzz.exe -D I:\\dynamorio\\bin64  -i "d:\\fuzz_input" -o "d:\\fuzz_output" -t 1000 -- -coverage_module victim.dll -target_module harness.exe -target_method fuzzme -fuzz_iterations 10 -nargs 1 -- I:\\harness\\x64\\Release\\harness.exe @@

실행은 반드시 afl-fuzz.exe 디렉토리에서 하여야 한다. 그렇지 않다면 아래와 같은 에러가 발생한다.

주의 : -coverage_module 및 -target_module 에는 파일명만 기입한다.

아래와 같은 화면이 나오면 실행 성공이다.

crash가 발생할 경우, i:\fuzz_output\crashes 경로에 상세 정보가 저장된다.

만약 CPU가 인텔인 경우, -D 옵션 대신 -P옵션을 사용하도록 하자. 속도가 압도적으로 빠르다. (내 PC에서는 5배 이상 차이가 났다.)

afl-fuzz.exe -P -i "d:\\fuzz_input" -o "d:\\fuzz_output" -t 1000 -- -coverage_module victim.dll -target_module harness.exe -target_method fuzzme -fuzz_iterations 10 -nargs 1 -- I:\\harness\\x64\\Release\\harness.exe @@

 

sudo passwd root
su
apt-get update
apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
apt-get update
apt-get install docker-ce docker-ce-cli containerd.io
sudo systemctl start docker

아래의 에러가 발생한다면..

root@DESKTOP-3NGJPPV:/home/server/kiloton-main# sudo systemctl start docker
System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is down

아래처럼 파일을 수정하고, wsl --shutdown으로 종료 후 wsl을 재시작한다.

sudo vim /etc/wsl.conf
[boot]
systemd=true

 

bogus control flow (bcf)

핵심 : 불투명 술어로 코드 제어

예시

int x = 1;
if (x + x == 3)
{
	// 불투명 술어 거짓. 실행되면 안되므로 더미 코드가 들어감
}
if (x - 1 == 0)
{
	// 불투명 술어 참. 실행되어야 하는 코드
}
#include <stdio.h>
#include <time.h>
#include <stdlib.h>

int main()
{
        srand(time(NULL));  
        int i = rand() % 3;      

        if (i == 0)
        {
               printf("0\n");
        }
        else if (i == 1)
        {
               printf("1\n");
        }
        else if (i == 2)
        {
               printf("2\n");
        }
        else if (i == 3)
        {
               printf("3\n");
        }

        return i;
}

IDA Result (Original)

int __cdecl main(int argc, const char **argv, const char **envp)
{
  unsigned int v3; // w0
  int v5; // [xsp+8h] [xbp-8h]

  v3 = time(0LL);
  srand(v3);
  v5 = rand() % 3;
  if ( v5 )
  {
    switch ( v5 )
    {
      case 1:
        printf("1\n");
        break;
      case 2:
        printf("2\n");
        break;
      case 3:
        printf("3\n");
        break;
    }
  }
  else
  {
    printf("0\n");
  }
  return v5;
}

IDA Result (Obfuscation)

int __cdecl main(int argc, const char **argv, const char **envp)
{
  unsigned int v3; // w0
  int v4; // w19

  v3 = time(0LL);
  srand(v3);
  v4 = rand() % 3;
  switch ( v4 )
  {
    case 2:
      puts("2");
      break;
    case 1:
      if ( ((x + x * x) & 1) != 0 && y > 9 )
        goto LABEL_16;
      while ( 1 )
      {
        puts("1");
        if ( ((x + x * x) & 1) == 0 || y < 10 )
          break;
LABEL_16:
        puts("1");
      }
      break;
    case 0:
      if ( ((x + x * x) & 1) != 0 && y > 9 )
        goto LABEL_13;
      while ( 1 )
      {
        puts("0");
        if ( ((x + x * x) & 1) == 0 || y < 10 )
          break;
LABEL_13:
        puts("0");
      }
      break;
  }
  return v4;
}

 

+ Recent posts