졸업프로젝트

취약점이 반영된 검증용 드라이버... 을! 만들어 봤읍니다.

img1244 2026. 2. 25. 23:18

https://eatrawlife.github.io/posts/2025/02/19/01 요기에 나오는 드라이버를 개선되기 전(문제가 있는) 버전으로 원복해서 취약점 검증용 드라이버로 만들어 보는 실습을 해보는게 목표

 

레노버 공식 Yoga 7 14lAL7 바이오스

위 드라이버를 우선 언팩해야 되니까 https://starkeblog.com/lenovo/uefi/2022/08/28/extracting-bios-image-from-lenovo-update-exe.html 를 참고해서 

innoextract와 7-zip을 사용하여 추출을 완료하였다.

 

uefi tool을 활용하여 찾은 ㅊ

 

해당 드라이버의 함수 중 func_0001780이 logo fail문제를 발생시켰다는 기록이 존재했고, 현재는 해결된 사항이므로

uint FUN_00001780(byte *param_1,ulonglong param_2,char param_3,longlong param_4,ushort param_5,
                 ushort param_6,longlong param_7)

{
  ushort uVar1;
  uint uVar2;
  byte bVar3;
  undefined1 *puVar4;
  ushort uVar5;
  ushort uVar6;
  ushort uVar7;
  undefined2 *puVar8;
  ulonglong uVar9;
  ushort uVar10;
  ushort uVar11;
  byte bVar12;
  byte bVar13;
  ushort uVar14;
  ushort uVar15;
  byte bVar16;
  ushort uVar17;
  uint uVar18;
  ushort uVar19;
  ushort uVar20;
  ushort uVar21;
  byte local_68;
  ushort local_66;
  ushort local_64;
  
  uVar11 = 1 << (*param_1 & 0x1f);
  bVar16 = *param_1 + 1;
  uVar21 = 0xffff;
  local_64 = 0xffff;
  uVar6 = uVar11 + 2;
  uVar18 = 0;
  uVar7 = 0;
  uVar15 = 0;
  uVar10 = 0;
  puVar8 = DAT_00001d48;
  if (uVar11 != 0) {
    do {
      *(char *)(puVar8 + 1) = (char)uVar10;
      uVar10 = uVar10 + 1;
      *puVar8 = 0xffff;
      puVar8 = puVar8 + 2;
    } while (uVar10 < uVar11);
  }
  puVar4 = DAT_00001d50;
  uVar9 = (ulonglong)(bVar16 >> 3);
  uVar10 = uVar6;
  uVar1 = uVar6;
  uVar5 = local_64;
  puVar8 = DAT_00001d48;
  bVar12 = bVar16;
  bVar3 = bVar16;
  do {
    if (param_2 < uVar9) {
      return uVar18;
    }
    uVar2 = uVar18 >> 3;
    bVar13 = (byte)uVar18;
    uVar18 = uVar18 + bVar12;
    uVar19 = (ushort)(*(uint *)(param_1 + (ulonglong)uVar2 + 1) >> (bVar13 & 7)) &
             (1 << (bVar12 & 0x1f)) - 1U;
    if (uVar19 == (ushort)(uVar11 + 1)) {
      return uVar18;
    }
    uVar14 = uVar11;
    uVar20 = uVar6;
    bVar13 = bVar16;
    local_68 = bVar16;
    local_66 = uVar6;
    local_64 = uVar11;
    if (uVar19 == uVar11) {
LAB_00001a81:
      uVar21 = uVar19;
      uVar5 = uVar19;
      if ((1 << (bVar13 & 0x1f) <= (int)(uint)uVar20) &&
         (uVar21 = uVar14, uVar5 = local_64, bVar13 < 0xc)) {
        bVar13 = bVar13 + 1;
        uVar21 = uVar19;
        local_68 = bVar13;
        uVar5 = uVar19;
      }
    }
    else {
      if (uVar15 == param_6) {
        return uVar18;
      }
      uVar17 = 0xffff;
      uVar14 = uVar19;
      local_68 = bVar3;
      local_66 = uVar1;
      if (uVar19 < uVar10) {
        for (; uVar14 != 0xffff; uVar14 = puVar8[(ulonglong)uVar14 * 2]) {
          uVar17 = uVar17 + 1;
          puVar4[(short)uVar17] = *(undefined1 *)(puVar8 + (ulonglong)uVar14 * 2 + 1);
        }
        if (uVar21 != uVar11) goto LAB_0000196a;
LAB_0000198b:
        uVar14 = uVar21;
        uVar20 = uVar10;
        bVar13 = bVar12;
        local_64 = uVar5;
        if (-1 < (short)uVar17) {
          do {
            uVar9 = (ulonglong)uVar17;
            uVar17 = uVar17 - 1;
            uVar21 = uVar7 + 1;
            *(undefined4 *)
             (param_7 + (longlong)(int)((uint)uVar15 * (uint)param_5 + (uint)uVar7) * 4) =
                 *(undefined4 *)(param_4 + (ulonglong)(byte)puVar4[uVar9] * 4);
            uVar7 = uVar21;
            puVar8 = DAT_00001d48;
            uVar14 = uVar5;
            uVar20 = local_66;
            bVar13 = bVar3;
            if (uVar21 == param_5) {
              if (param_3 == '\0') {
                uVar15 = uVar15 + 1;
              }
              else if ((uVar15 & 7) == 0) {
                uVar15 = uVar15 + 8;
                if (param_6 <= uVar15) {
                  uVar15 = 4;
                }
              }
              else if ((uVar15 & 3) == 0) {
                uVar15 = uVar15 + 8;
                if (param_6 <= uVar15) {
                  uVar15 = 2;
                }
              }
              else if ((uVar15 & 1) == 0) {
                uVar15 = uVar15 + 4;
                if (param_6 <= uVar15) {
                  uVar15 = 1;
                }
              }
              else {
                uVar15 = uVar15 + 2;
              }
              uVar7 = 0;
              if (uVar15 == param_6) break;
            }
          } while (-1 < (short)uVar17);
        }
        goto LAB_00001a81;
      }
      uVar20 = uVar10;
      bVar13 = bVar12;
      if (uVar21 != uVar11) {
        uVar17 = 0;
        for (uVar1 = uVar21; uVar1 != 0xffff; uVar1 = puVar8[(ulonglong)uVar1 * 2]) {
          uVar17 = uVar17 + 1;
          puVar4[(short)uVar17] = *(undefined1 *)(puVar8 + (ulonglong)uVar1 * 2 + 1);
          bVar12 = bVar3;
        }
        *puVar4 = puVar4[(short)uVar17];
LAB_0000196a:
        uVar9 = (ulonglong)uVar10;
        uVar10 = uVar10 + 1;
        puVar8[uVar9 * 2] = uVar21;
        *(undefined1 *)(puVar8 + uVar9 * 2 + 1) = puVar4[(short)uVar17];
        local_66 = uVar10;
        goto LAB_0000198b;
      }
    }
    local_64 = uVar5;
    uVar9 = (ulonglong)(bVar13 + uVar18 >> 3);
    uVar10 = uVar20;
    uVar1 = local_66;
    uVar5 = local_64;
    bVar12 = bVar13;
    bVar3 = local_68;
  } while( true );
}

해당하는 부분이 under/overflow를 발생하게 끔 만들어 보기로 하였다.

 


line 1244
offset 58
입력 데이터를 검증


line 1266
offset 79
크기나 자료의 깊이 등을 제한



line 1292
offset 106
배열의 범위 확인

위와 같은 부분이 https://www.binarly.io/advisories/brly-logofail-2023-007 에서 언급한 데이터를 검증하지 않는 문제점이 해결된 부분이라고 판단하였다(구조상 유사해 보여서).

 

이 부분을 다시 취약점으로 되돌리기 위해서는 검증 부분을 제거하면 된다고 판단하였고, 우선적으로 편집을 위해서 c로 export하였다.

기드라 자체 기능으로 지원

이후 해당 검증 파트 부분들을

아래와 같이 주석 처리하였다. 이로써 이론적으로 오버플로우가 발생할 수 있는 환경은 조성을 하였고, 이 소스코드를 활용하여 드라이버를 빌드해보자.

 


decompile된 추출 코드 위 아래에 아래와 같은 코드를 추가하고,

[Defines]
  INF_VERSION                    = 0x00010005
  BASE_NAME                      = GifDecoderDxe
  FILE_GUID                      = 1353de63-b74a-4bef-80fd-2c5cfa83040b
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 1.0
  ENTRY_POINT                    = UefiMain

[Sources]
  GifDecoderDxe.c

[Packages]
  MdePkg/MdePkg.dec

[LibraryClasses]
  UefiApplicationEntryPoint
  UefiLib

[BuildOptions]
  MSFT:*_*_*_CC_FLAGS = /W0

GifDecoderDxe.inf 라는 파일을 위와 같이 작성한다. 아래에 빌드 옵션으로 경고는 깔끔하게 무시했다. 

 

[Defines]
  PLATFORM_NAME                  = GifDecoderDxe
  PLATFORM_GUID                  = 056D93D5-69E2-43D4-A61F-10029E65E923
  PLATFORM_VERSION               = 0.1
  DSC_SPECIFICATION              = 0x00010005
  OUTPUT_DIRECTORY               = Build/GifDecoderDxe
  SUPPORTED_ARCHITECTURES        = X64
  BUILD_TARGETS                  = DEBUG|RELEASE
  SKUID_IDENTIFIER               = DEFAULT

[LibraryClasses]
  # 기본적인 UEFI 라이브러리 설정
  UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf
  UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf
  UefiLib|MdePkg/Library/UefiLib/UefiLib.inf
  BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
  BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
  MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
  UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf
  UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf
  DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
  PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
  PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
  DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf

[Components]
  MdeModulePkg/Application/GifDecoderDxe/GifDecoderDxe.inf

edk2 최상위 폴더에 GifDecoderDxe.dsc를 넣어서 필요한 빌드를 정의해 주었다.

 

이후 edksetup과 빌드를 해서 

이거 다 하는 데 몇시간 걸린거지 대체...

D:\project\edk2-edk2-stable202102\edk2-edk2-stable202102\Build\GifDecoderDxe\DEBUG_VS2019\X64

해당 경로에 

이렇게 취약점이 반영된 드라이버를 빌드할 수 있었다.

 


p.s

'fun_00001780은 뭐하는 함수였을까'라는 생각이 들어서 ai한테 물어보니까 LZW(Lempel-Ziv-Welch, 무손실 압축 알고리즘) 중 복구에 해당하는 함수로 추측하였다.

함수의 시작 부분의 다음 로직들이 LZW의 전형적인 방식이라고 하였고(초기 사전의 크기 결정방식와 가변 비트읽기 로직),

함수 중간의 조건문이 사전을 통한 데이터 복원 패턴과 일치하여, LZW 중 복원 함수로 판단하였다.