cgy12306

Christmas 2020 CTF Screw Driver Write-Up 본문

CTF Write-up

Christmas 2020 CTF Screw Driver Write-Up

cgy12306 2021. 1. 2. 19:23

제가 처음으로 CTF에 출제한 문제입니다.

Best of Best 프로젝트 기간중에 윈도우 드라이버 코딩도 해보고, 거의 매일 밥먹듯이 드라이버 리버싱을 하다보니 드라이버 문제를 내면 많은 사람들이 드라이버에 대해 조금이라도 알아 가실 수 있지 않을까해서 문제 출제를 결심하게 되었습니다.

대회가 종료되고 드림핵에 라이트업을 작성해서 냈지만 너무 간략하게 적은것 같아서 블로그에 다시 작성해봅니다.

driver entry를 보게 되면 device name은 screw로 설정되어 있습니다.

MajorFunction의 14번째 핸들러인 sub_140002F10로 DeviceIoContorl을 통해 kernel과 통신을 할 수 있습니다.

sub_140002F10 함수를 살펴보겠습니다.

v4 변수는 IOCTL을 담고 있습니다.

우선 IOCTL이 0x222004인 분기문을 먼저 보겠습니다.

C:\\file을 0x30 byte만큼 읽어와서 byte_140005060에 넣습니다.

IOCTL 0x222008을 보겠습니다.

함수 sub_140002DF0을 호출하는 것을 볼 수 있습니다.

sub_140002DF0의 내부를 보게 되면 sub_140003940 함수를 계속 호출하고, copy complete를 디버그 프린트 함수를 이용해 출력해주는 것을 볼 수 있습니다.

사실 copy complete를 넣을까 말까 하다가 난이도를 낮추기 위해 추가해 줬습니다


즉, sub_140003940 함수는 memcpy입니다.

전역변수에 있는 byte_140005060[4]부터 byte_140005060[24]까지의 값을 unk_140005040에 복사하게 되고, byte_140005060의 처음 4byte를 XMAS로 채워줍니다.

많은 분들이 이 값이 플래그인줄 알고 XMAS{Round

~

} 이런 값들을 많이 넣어주셨습니다. 하지만 안타깝게도 이 값은 플래그가 아니었습니다. ㅎㅎ

다시 돌아와서 IOCTL 0x222000을 살펴보겠습니다.

v6 값은 InputBufferLength 값입니다. 이 값이 3보다 작을 경우 종료를 하게 됩니다. 3 이상일 경우 sub_14000341C를 호출합니다.

sub_14000341C의 처음 반복문을 보게 되면 byte_140005060의 28번째 값부터 0번째 값까지 byte_140005008에 있는 값과 xor 연산하여 Str1의 변수에 담습니다.

위의 사진은 byte_140005008의 값입니다.

반복문을 통과하고 나서 Str1과 Str2를 비교하여 틀리면 함수를 종료하게 됩니다.

Str2는 사용자가 DeviceIoControl을 통해 보낸 InputBuffer 값입니다.

만약 일치해서 분기문을 통과하게 된다면 Str2에 byte_140005060 처음의 4byte인 XMAS를 복사합니다.

다시 while문을 보겠습니다.

while문을 간단히 설명드리면 시계 방향으로 바깥에서 안쪽으로 도는 달팽이 알고리즘입니다.

Str2[4], byte_140005040[4]부터 Str2[24], byte_140005040[24]까지 뱅글뱅글 돌면서 xor 연산한 값을 다시 Str2에 넣게 됩니다.

모든 반복이 끝나면 함수를 종료합니다.

다시 dispatch 루틴으로 돌아와서 sub_1400032A8을 호출합니다. 인자는 IRP와 iostacklocation이 들어갑니다.

sub_1400032A8의 함수를 보면 hash!!!!라고 디버그 프린트가 찍혀있습니다.

간단히 설명 드리면 사용자가 입력한 InputBuffer 값을 md5로 해쉬하는 함수입니다.

Line 19에서 Str1과 Str2를 비교하여 strcmp의 반환 값을 함수의 반환 값으로 사용하고 있습니다.

Str1은 InputBuffer의 값이고 Str2는 "2b962312cb4af11d3773ffd6c848e765"라는 값인데 이 값은 제가 플래그 값을 md5로 해쉬한 값을 넣어놨습니다.

strcmp는 일치하면 0을 반환하기 때문에 dispatch 루틴에서 sub_1400032A8이 0이 되면 output을 출력해주며 ntstatus의 0을 반환하게 됩니다.

DeviceIoControl을 이용하여 성공적으로 통신을 완료했으면 ntstatus 0(STATUS_SUCCESSFUL)을 반환하기 때문에 이 루틴에 들어와야지만이 플래그가 출력된다는 것을 알 수 있습니다.

exploit code

#include<stdio.h>
#include<string.h>
int main(){

    int x = 0, y = 0, i = 0, j = -1, sw = 1, cnt = 4, size = 5, z = 0;
    char string[30] = "\x73\x6b\x0c\x6a\x54\x0d\x52\x3f\x0c\x64\x6c\x2e\x65\x4a\x33\x0b\x43\x4e\x40\x26\x7f\x72\x1f\x68\x5b\x63\x34\x03\x3c";
    char string2[5][5]={0,};
    char buf[30] ={0,};
    char flag[30] = {0,};
    FILE *fp;

    fp = fopen("file", "r");

    fread(buf, 1, 29, fp);
    memcpy(buf, &"XMAS", 4);
    memcpy(string2[0], buf+4, 5);
    memcpy(string2[1], buf+9, 5);
    memcpy(string2[2], buf+14, 5);
    memcpy(string2[3], buf+19, 5);
    memcpy(string2[4], buf+24, 5);
    for (i = 0; i < 29; i++) {
        flag[i] = buf[28 - i] ^ string[i];
    }
    memcpy(flag, &"XMAS", 4);
    i = 0;
    while (1) {
        for (x = 0; x < size; x++) {
            j += sw;
            flag[cnt] = (flag[cnt] ^ string2[i][j]) + (char)z;
            cnt++;
            z++;
        }
        size -= 1;
        if (size <= 0) break;
        for (y = 0; y < size; y++) {
            i += sw;
            flag[cnt] = (flag[cnt] ^ string2[i][j]) + (char)z;
            cnt++;
            z++;
        }
        sw *= -1;
    }
    printf("%s", flag);

    return 0;
}

사실 이 문제는 풀이자들이 DeviceIoControl을 할 수 있는지 여부를 테스트하는 문제였지만 여러가지 문제점들로 인해 DeviceIoControl을 못 보내도 풀수 있도록 제작이 되었습니다.

만약 DeviceIoContorl을 보낼 수 있으면 굳이 while문을 포팅할 필요가 없는 문제였습니다.

#include<stdio.h>
#include<windows.h>
#include<conio.h>
#include<stdlib.h>
int main(int argc, char* argv[]) {

    HANDLE dhandle;

    WCHAR DeviceLink[] = L"\\\\.\\screw";

    DWORD inbuffersize = 30;
    DWORD outbuffersize = 30;
    char string[30] = "\x73\x6b\x0c\x6a\x54\x0d\x52\x3f\x0c\x64\x6c\x2e\x65\x4a\x33\x0b\x43\x4e\x40\x26\x7f\x72\x1f\x68\x5b\x63\x34\x03\x3c";
    char in[30] ={0,};
    char out[30] = {0,};
    char buf[30] = {0,};
    FILE *fp;

    fp = fopen("C:\\file", "r");
    fread(buf, 1, 29, fp);
    memcpy(buf, &"XMAS", 4);

    for (int i = 0; i < 29; i++) {
        in[i] = buf[28 - i] ^ string[i];
    }

    int ntstatus = 0;
    printf("inbuffer : %s\n", in);

    dhandle = CreateFileW(DeviceLink, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    DeviceIoControl(dhandle, 0x222004, NULL, 0, NULL, 0, NULL, NULL);
    DeviceIoControl(dhandle, 0x222008, NULL, 0, NULL, 0, NULL, NULL);
    ntstatus = DeviceIoControl(dhandle, 0x222000, (LPVOID)in, inbuffersize, out, outbuffersize, NULL, NULL);

    CloseHandle(dhandle);

    printf("outbuffer : %s\n", out);
    return 0;
}

 

부족한 문제를 푸시느라 고생많으셨습니다 ㅠㅠ

'CTF Write-up' 카테고리의 다른 글

Christmas CTF 2020 Diary write-up  (0) 2021.02.01
Codegate 2017 dart-master  (0) 2020.04.18
WITHCON 2016 malloc  (0) 2020.04.18
Codegate 2016 floppy  (0) 2020.04.18
Codegate 2017 Angrybird  (0) 2020.03.02
Comments