0%

Code Coverage Measurement with Dynamo Rio & Lighthouse

  1. 커버리지 측정 계산은 어떻게 하는 것일까?
  2. coverage guided 퍼징 시 커버리지 측정이 어디까지 되는것인가?, 어느 부분이 필요한가?
  3. Windows 프로그램을 예시로, A회사의 프로그램을 퍼징하면서 WINAPI의 취약점을 찾을 수 도 있는 것일까?
  • 조사를 통해 의문에 대한 나만의 답을 찾았지만, 잘 못 이해한것일 수 도 있으니 참고만 하자.

환경

실습

  • ipconfig 바이너리를 가지고 코드 커버리지를 측정해 보겠다.
  • 아래 명령어를 통해 커버리지 로그를 얻을 수 있다.
1
2
cd C:..\DynamoRIO-Windows-9.0.1\bin64
drrun -t drcov -dump_text -- [PATH]

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
DRCOV VERSION: 2
DRCOV FLAVOR: drcov
Module Table: version 5, count 18
Columns: id, containing_id, start, end, entry, offset, preferred_base, checksum, timestamp, path
0, 0, 0x0000000071000000, 0x00000000711c0000, 0x00000000710a2e70, 0000000000000000, 0x0000000071000000, 0x0019203b, 0x620aa6ec, C:\Users\pwnki\Desktop\DynamoRIO-Windows-9.0.1\lib64\release\dynamorio.dll
1, 1, 0x00007ff63a6a0000, 0x00007ff63a6a6000, 0x00007ff63a6a0000, 0000000000000000, 0x0000000072000000, 0x0000ab8b, 0x620aa777, C:\Users\pwnki\Desktop\DynamoRIO-Windows-9.0.1\tools\lib64\release\drcov.dll
2, 2, 0x00007ff63a6b0000, 0x00007ff63a6b9000, 0x00007ff63a6b0000, 0000000000000000, 0x0000000073800000, 0x0001401b, 0x620aa776, C:\Users\pwnki\Desktop\DynamoRIO-Windows-9.0.1\ext\lib64\release/drcovlib.dll
3, 3, 0x00007ff63a6c0000, 0x00007ff63a6cf000, 0x00007ff63a6c0000, 0000000000000000, 0x0000000077000000, 0x000149e8, 0x620aa772, C:\Users\pwnki\Desktop\DynamoRIO-Windows-9.0.1\ext\lib64\release/drx.dll
4, 4, 0x00007ff63a6d0000, 0x00007ff63a6db000, 0x00007ff63a6d0000, 0000000000000000, 0x0000000078000000, 0x0001746d, 0x620aa761, C:\Users\pwnki\Desktop\DynamoRIO-Windows-9.0.1\ext\lib64\release/drreg.dll
5, 5, 0x00007ff63a6e0000, 0x00007ff63a6f0000, 0x00007ff63a6e0000, 0000000000000000, 0x0000000073000000, 0x00014cc5, 0x620aa75e, C:\Users\pwnki\Desktop\DynamoRIO-Windows-9.0.1\ext\lib64\release/drmgr.dll
6, 6, 0x00007ff69a690000, 0x00007ff69a69d000, 0x00007ff69a6952c0, 0000000000000000, 0x00007ff69a690000, 0x00016cf7, 0x60005d63, C:\Windows\System32\ipconfig.exe
7, 7, 0x00007ffccdb20000, 0x00007ffccdb37000, 0x00007ffccdb224b0, 0000000000000000, 0x00007ffccdb20000, 0x00014e4a, 0xc962e034, C:\Windows\System32\dhcpcsvc6.DLL
8, 8, 0x00007ffccdbb0000, 0x00007ffccdbcd000, 0x00007ffccdbb29b0, 0000000000000000, 0x00007ffccdbb0000, 0x0002666a, 0x1c1d619d, C:\Windows\System32\dhcpcsvc.DLL
9, 9, 0x00007ffcd2200000, 0x00007ffcd223b000, 0x00007ffcd220a620, 0000000000000000, 0x00007ffcd2200000, 0x0003ecbd, 0xcf9a121a, C:\Windows\System32\IPHLPAPI.DLL
10, 10, 0x00007ffcd2240000, 0x00007ffcd230b000, 0x00007ffcd226be10, 0000000000000000, 0x00007ffcd2240000, 0x000ca5e2, 0x441ccbe1, C:\Windows\System32\DNSAPI.dll
11, 11, 0x00007ffcd2df0000, 0x00007ffcd30b8000, 0x00007ffcd2e01bd0, 0000000000000000, 0x00007ffcd2df0000, 0x002d64e4, 0x1183946c, C:\Windows\System32\KERNELBASE.dll
12, 12, 0x00007ffcd3fe0000, 0x00007ffcd409d000, 0x00007ffcd3ff70d0, 0000000000000000, 0x00007ffcd3fe0000, 0x000c8b3e, 0x2f7cc9b6, C:\Windows\System32\KERNEL32.DLL
13, 13, 0x00007ffcd4f60000, 0x00007ffcd4fcb000, 0x00007ffcd4f74300, 0000000000000000, 0x00007ffcd4f60000, 0x0006cf4b, 0xaff3315b, C:\Windows\System32\WS2_32.dll
14, 14, 0x00007ffcd5000000, 0x00007ffcd5008000, 0x00007ffcd50022f0, 0000000000000000, 0x00007ffcd5000000, 0x000117c5, 0xaa9c8581, C:\Windows\System32\NSI.dll
15, 15, 0x00007ffcd50a0000, 0x00007ffcd513e000, 0x00007ffcd50a7850, 0000000000000000, 0x00007ffcd50a0000, 0x0009e85d, 0x564f9f39, C:\Windows\System32\msvcrt.dll
16, 16, 0x00007ffcd5510000, 0x00007ffcd5634000, 0x00007ffcd556db80, 0000000000000000, 0x00007ffcd5510000, 0x0012ce1d, 0x76243d9a, C:\Windows\System32\RPCRT4.dll
17, 17, 0x00007ffcd56b0000, 0x00007ffcd58a6000, 0x00007ffcd56b0000, 0000000000000000, 0x00007ffcd56b0000, 0x001fd6e7, 0xe5d7ed5c, C:\Windows\SYSTEM32\ntdll.dll
BB Table: 14257 bbs
module id, start, size:
module[ 17]: 0x000000000004cea0, 19
module[ 17]: 0x000000000004ceb3, 14
module[ 17]: 0x000000000008b7c0, 29
module[ 17]: 0x000000000008b7dd, 6
module[ 17]: 0x000000000008b7f1, 10
module[ 17]: 0x000000000008b7fb, 3
module[ 12]: 0x0000000000017020, 8
module[ 12]: 0x0000000000017028, 12
module[ 17]: 0x000000000008b7e3, 3
module[ 6]: 0x00000000000052c0, 9
module[ 6]: 0x00000000000057e4, 44
module[ 6]: 0x00000000000058a3, 21
module[ 6]: 0x00000000000052c9, 9
module[ 6]: 0x00000000000050f0, 49
module[ 6]: 0x000000000000513c, 15
module[ 6]: 0x0000000000005157, 10
module[ 6]: 0x0000000000005161, 34
module[ 6]: 0x0000000000005183, 4
module[ 6]: 0x0000000000005187, 6
module[ 6]: 0x000000000000519d, 11
module[ 6]: 0x000000000000517e, 5
module[ 6]: 0x000000000000518d, 12
module[ 6]: 0x0000000000004fc0, 18
module[ 6]: 0x0000000000004fd6, 25
  • 로그의 일부분으로, 바이너리가 실행에 필요한 모든 dll들을 로드하며, 거기에 대한 커버리지를 측정한다.
  • IDA로 커버리지 확인을 할 바이너리 혹은 DLL을 열고, Lighthouse를 통해 커버리지 로그를 불러오면 아래와 같이 확인할 수 있다.
  • (Ipconfig.exe에 대한 커버리지 확인)

image

  • block 단위로 커버리지를 측정하는 것을 확인할 수 있다.
  • 또한 전체 블록 중 몇 개의 블록을 커버했는지 확인할 수 있다.
  • 이를 바탕으로 커버리지를 수치화하는 방법은 (커버한 블록 / 전체 블록) 으로 계산하는 것 같다.
  • 1번 의문은 커버리지를 측정하기 위해 인스트루먼트 단계에서 전체 블록에 대한 값을 얻을 수 있고,
  • 커버한 블록 갯수 역시 얻을 수 있기에 이 둘을 활용하여 계산 하는 것으로 확인했다.
  • 2번, 3번 의문을 풀기 위해 예시를 들어보겠다.
  • 프로그램 A, A.dll, Windows.dll 세 개의 파일이 있다고 가정하자. (프로그램 마다 다르다.)
  1. 프로그램 A는 A회사에서 만든 모듈로, 코드에는 A.dll에 있는 함수를 호출해서 사용하거나 자체적인 함수가 내장되어 있다.
  2. A.dll은 A 회사에서 만든 자체 함수들이 정의되어 있다. 여기 있는 함수는 Windows에서 제공하는 WINAPI를 바탕으로 만들어진 함수이다.
  3. Windows.dll은 WINAPI 함수들이 정의되어 있다.
  • 전체적인 흐름은 아래와 같다.
    1
    프로그램 A (A.dll에 있는 함수 일부 호출) -> A.dll(자체함수 정의 부분, 내부적으로 WINAPI 호출) -> Windows.dll(WINAPI 함수 정의부분)
  • 여기서 드는 의문은 과연 코드 커버리지는 어디 까지 측정되며, 어디 까지 측정 해야 할까. 그리고 프로그램 A를 퍼징한다면 결국 Windows.dll에 있는 WINAPI에 대한 취약점도 찾을 수 있는 것인가 이다.
  • DynamoRio를 통해 확인 한 결과, 커버리지는 전체 다 측정 되는 것으로 확인할 수 있다.

image

  • 커버리지 로그를 보면 바이너리 실행 시 로드 되는 모든 dll에 대해 인스트루먼트를 진행하고, 커버리지를 측정하는 것을 확인할 수 있다.

  • (로드된 dll에 대한 커버리지)

image

  • 그럼 A 회사 제품에 대한 퍼즈 테스팅을 위해서라면 어디까지의 커버리지 정보가 필요할까
  1. 프로그램 A에 대해 측정한다면, 발생하는 버그는 A.dll에 있는 함수들을 잘 못 사용해서 발생하는것으로, 함수 호출 순서나 방법을 고치면 된다. 혹은 프로그램에 정의된 함수의 로직이 잘 못되어 발생하는 경우이다.

  2. A.dll에 대해 측정한다면, 발생하는 버그는 Windows.dll의 WINAPI들을 잘 못 사용해서 발생하는 것으로, A.dll의 함수 로직을 고쳐야한다.

    • Harness 프로그램을 dll에 있는 함수를 가지고 짠다면, 커버리지를 해당 dll을 측정해야 한다.
  3. Windows.dll에 대해 측정한다면, 발생하는 버그는 WINAPI 함수의 로직에서 발생하는 것이다. 즉 A 회사 제품에 대한 취약점을 찾는 것이 아니다. WINAPI 함수 로직을 고쳐야한다.

  • 또한 보통 3번의 경우는 흔하지 않아 보인다.
  • 결론을 내리면, 의문 2는 퍼즈 테스팅 대상에 따라 다르다고 볼 수 있다. 공격 벡터를 정하고, 해당 기능을 처리하는 부분을 대상으로 퍼즈 테스팅을 진행하기에, 해당 부분의 커버리지가 필요하다.

  • 그 부분이 프로그램에 정의 되어 있다면, 프로그램에 대한 커버리지가 필요하고, dll로 따로 정의 되어있다면 dll에 대한 커버리지가 필요하다.

  • WinAFL 퍼저의 경우 커버리지 측정 모듈을 지정해주는 옵션이 있어 필요한 커버리지만 얻을 수 있다.

  • 의문 3은 흔하지 않은 경우로 생각 된다. A 제품에 대한 보안 검사로 A회사가 자체적으로 만든 프로그램이나 dll에 대해서만 퍼즈 테스팅을 진행하고, 커버리지를 측정하면 될 것 같다.

결론

바이너리와 실행시 로드되는 DLL 전부 인스트루먼트를 진행 하고, 커버리지를 측정한다.
퍼즈 테스팅 대상에 따라 필요한 커버리지 정보가 다르다.

참고

https://www.youtube.com/watch?v=g6Jp3KZNles