Wargame/FTZ

[FTZ]level20

D4tai1 2019. 9. 9.

드디어

드디어어어

 

level20

성인이 되기 일보직전입니다.

 

시작해 볼까요?

 

[그림1] hint 파일 확인

자세히 보면 

fgets함수에서는

문제가 없어보이쥬?

 

그르나 

printf함수를 사용할 때

변수를 바로 찍어버리네요.

 

결국

Format String Bug를 이용해서

문제를 해결하라는 건가요?

 

Format String Bug란?

음.. "포맷스트링을 사용하지 않고 출력하는 함수에게 하는 공격?" 정도가 되겠네요.

 

보통 입력에 "%x, %x, %x"와 같은 것을 넣고,

매칭되는 변수가 없을 경우

스택을 4바이트씩 높여가며 해당 주소의 값을 출력해주는

버그가 발생하게 됩니다.

+

%n을 통해서 메모리 변조도 가능하지요.

어떻게 가능하냐면?

printf("abcd%10d%n");

을 하게되면

%n앞에서 사용한 문자열의 바이트 수를 세서

%n이 가리키는 주소에 저장합니다.

위의 경우는 14가 %n이 가리키는 곳에

저장이 되겠지요?

 

 

[그림2] 문자열에 %x를 삽입

정말로 주소가 잘 출력이 되네요.

 

%x를 4번 적었는데

4번째 %x에 41414141이

출력되었네요?

 

그 말은

printf함수와 bleh배열 사이에

0x4f와

0x4212ecc0과

0x4207a750

총 3개의 dummy가 존재한다는 말이겠지유?

 

위 내용을 바탕으로 그림을 그려볼까요?

 

[그림3] stack 상태

위와 같이 구성되어 있겠네요.

 

[그림4] main 확인

허.. symbol이 없네요??

 

누군가 지웠네요!!

 

파일의 symbol정보를 확인하기위해

nm을 사용해보겠습니다.

 

[그림5] nm명령어 실행

예상은 했지만 symbol정보가

다 지워져 있네요..

 

이 프로그램은 어차피 ret를 덮어쓸 수도 없기 때문에

소멸자를 덮어씌우는 방식으로 하는 것이 좋을 듯 싶습니다.

 

사실 Format String Bug는

주로 소멸자가 가리키는 주소를 덮어씌우는 방식을 사용합니다.

 

[그림6] 프로그램 구성도(요약)

 

프로그램이 시작되면

1) Constructor에 적힌 주소에서

생성자 함수를 실행합니다.

 

2) Main함수를 실행합니다.

 

3) Destructor에 적힌 주소에서

소멸자 함수를 실행합니다.

 

4) 프로그램을 종료합니다.

 

그래서 공격 시나리오를 생각해보면

main함수는 끝나고 소멸자함수를 호출할 것입니다.

 

소멸자 주소는 .dtor 섹션+4바이트에 위치하고 있습니다.

여기를

환경변수에 적어놓은

SHELLCODE주소로 변조하려고 합니다.

 

 변조는 위에서 말한 것과 같이

%n이 가리키는 주소문자열 길이만큼 적는다고 했습니다.

 


 

.dtor Section에 있는

__DTOR_END__(소멸자)를

먼저 찾아봅시다.

 

readelf가 objdump보다

상세한 정보를 얻을 수 있으므로

readelf를 이용해보도록 하겠습니다.

 

헤더에 대한 옵션을 적어보면

-h = ELF 파일 헤더

-l = 프로그램 헤더

-S = 섹션 헤더

-e = 위 3가지 헤더

우리는 .dtor의 섹션헤더의 정보가 필요하므로

 

[그림7] readelf -S 를 이용해서 섹션주소 확인

.dtors 영역의 주소를 찾았네요.

.dtors = 0x08049594

그러나 __DTOR_END__(소멸자)의 주소는

+4byte를 더한

0x08049598이 됩니다.

 

다음은 소멸자주소에

덮어 씌울 SHELLCODE를 

환경변수에 등록해 줍니다.

 

[그림8] 환경변수에 SHELLCODE 삽입

export SHELLCODE=$(python -c "print '\x90'*100 + '\x31\xc0\x50\x68\x2f\x2f

\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xd2\x52\x53\x89\xe1\xb0\x0b\xcd\x80'")

환경변수에 SHELLCODE를 삽입하였습니다.

 

[그림9] 환경변수의 주소를 얻어오기

#은 16진수일 때 0x를 붙여서 표기해줍니다.

%p는 빈공간에도 0을 넣어서 출력해주고

%x는 16진수 값만 출력해 줍니다.

 

편하신 것을 사용해도 됩니다.

 

[그림10] SHELLCODE의 주소 확인

 

SHELLCODE의 주소는 0xbffffbca네요.

 

[그림11] __DTOR_END__를 SHELLCODE로 변조

위 그림과 같아지도록

이제 payload를 작성해 보도록 하겠습니다.

 

__DTOR_END__의 하위주소(4) + __DTOR_END__의 상위주소(4) + SHELLCODE의 하위주소(4) + SHELLCODE의 상위주소(4) +%4$n + SHELLCODE의 상위주소(4) + %5$n

 

payload가 이해가 안가는 분만 보세요!!

예시를 들어볼께요.

[그림12] 예시

빨간 박스를 보면 

4바이트 더미가 3개 12바이트인 것을 알 수 있죠?

 

이제 4번째 %p가 bleh를 가리키는 것은 알고 계실겁니다.

 

노란 박스를 보면

%4$p이라고 적었고

이 말은 4번째에 있는 주소의 값을 출력해주게 됩니다.

그래서 0x41414141이 나왔습니다.

만약 %4$n이라면 4번째에 있는 주소에

문자열 길이만큼 덮어쓰겠죠?

 

초록 박스를 보면

%100p가 있는데 이것은

100바이트만큼 공간을 확보하고

첫 번째에 있는 주소의 값을 찍지요.

 

%n은 가리키고 있는 주소에 앞 문자의 길이만큼 찍는다고 했죠?

 

[그림13] 아래설명에 대한 그림

1번째는 0x4f

2번째는 0x4212ecc0

3번째는 0x4207a750

까지는 주소에 이미 있는 값이 출력되지만

4번째는 AAAA를 넣으면 0x41414141이 나오는 것을 보셨죠?

즉,  4번째가 배열의 시작주소을 가리키고 있으며

그 곳은 fgets로 입력을 받고 있는 시작주소입니다.

여기에 소멸자의 주소(0x08049598)를 적고 %4$n을 한다면 

문자열의 길이만큼 소멸자의 주소를 덮어쓰겠네요?

 

여기서 이해가 안가신다면 %n을 다시 확인해보면

좋을 듯 싶습니다.

 

이어서 

스택영역은 최상단에 위치하기 때문에 주소가 큽니다..

그래서 2바이트씩 끊어서 덮어쓰기를 하려고 합니다.

 

소멸자의 주소 = 0x08049598

SHELLCODE의 주소 = 0xbffffbca

 

payload를 다시보면 

 __DTOR_END__의 하위주소(4) + __DTOR_END__의 상위주소(4) + SHELLCODE의 하위주소(4) + SHELLCODE의 상위주소(4) +%4$n + SHELLCODE의 상위주소(4) + %5$n

 에서 

__DTOR_END__(소멸자 함수의 주소를 담고 있음)

[0x08049598, 0x08049599 / 0x0804959a, 0x0804959b ]

= 0x 00 00 00 00 

하위 2바이트는 빨간색이 되겠죠?

0x08049598에 SHELLCODE의 하위 2바이트 주소로 변경하고

 

상위 2바이트는 파란색이 되겠죠?

0x0804959a에 SHELLCODE의 상위 2바이트 주소로 변경합니다.

 

그러면 소멸자를 호출할 때 SHELLCODE가 호출이 될 것으로 예상이 됩니다.

 

SHELLCODE의 하위 2바이트 주소는?

0xfbca, 10진수로 64458입니다.

 

그러나 8바이트를 주소로 작성했기 때문에

빼줍니다.

 

즉, %64450x를 적어주면

기존 8바이트가 있으니

문자열 길이가 64458이 되고

이 값이 0x08049598에 들어가게 됩니다.

 

SHELLCODE의 상위 2바이트 주소는?

0xbfff입니다.

상위바이트 수도 덮기 위해 

기존 바이트를 빼면?

0xbfff - 0xfbca

음수가나오네요?

 

그래서 상위주소를 1 증가시켜 빼기를 합니다.

0x1bfff - 0xfbca

= 0xc435, 10진수로 50229입니다.

 

이제 대망의 공격코드를 작성하는 일만 남았습니다.

 

(python -c 'print ("\x98\x95\x04\x08\x9a\x95\x04\x08"+"%64450c%4$n%50229c%5$n")'; cat) | ./attackme

는 일만 남았습니다.

 

[그림14] 공격코드 작성

화면이 사라져서 

사용했던 공격코드를 다시 적었습니다.

 

기왕이면 스크립트로 만들어볼까요?

 

[그림15] 공격스크립트 작성

(python /tmp/20_exploit.py; cat) | ./attackme

이렇게 실행하시면 됩니다.

'Wargame > FTZ' 카테고리의 다른 글

[FTZ]level19  (0) 2019.09.08
[FTZ]level18  (0) 2019.09.08
[FTZ]level17  (0) 2019.09.08
[FTZ]level16  (0) 2019.09.08
[FTZ]level15  (0) 2019.09.08

댓글