마지막 문제입니다.
힌트를 보면 프로그램은 생각보다 간단한듯한데
fgets의 옵션에 79가 있네요...
최대 길이를 79바이트로 제한했습니다.
버퍼오버플로우가 안되는 듯 하네요ㅠㅠ
그런데 이때 printf함수를 보면 인자를 사용했죠?
여기서 포맷스트링 버그가 발생합니다.
여기서
printf(bleh);
이렇게 함수를 사용하게 되면
문자열을 출력하다가
%x 등을 만나면
이들을 문자열로 보지 않고 서식문자로 인식해버립니다.
문자열을 출력하다가
이 문자들(%d, %x, %s 등)을 만나면
메모리의 다음 4바이트를 참조해서 출력하는 것입니다.
메모리 구조를 살펴보기 위해서 gdb를 사용합니다.
확인이 안되네요.
그럼 지금까지의 힌트를 가지고 구조를 유추해보면
RET 4
SFP 4
DUMMY ?
bleh 80
DUMMY ?
printf(method)
로 볼수 있네요.
printf함수에서 bleh 까지의 더미값을 알수 있는 방법은 간단합니다.
아까 포맷스트링버그 에서 다음 4바이트를 참조한다고 했으니까
문자열 값을 저장 시키고 포맷스트링을 통해 계산하면 되겠군요.
%10x를 4번째 넣었을 때 41414141이 나옵니다.
그렇다는 것은
Dummy의 값이 12바이트가 되겠군요.
RET 4
SFP 4
DUMMY ?
bleh 80
DUMMY 12
printf(method)
이제 메모리 구조를 대충 알아냈습니다.
하지만 gdb로 ret을 확인할수 없었습니다.
그래서 .dtors(소멸자)를 사용합니다.
objdump를 이용해 주소를 찾습니다.
주소는 0x08049598이 되겠네요(0x08049594 + 4)
미리미리 eggshell 주소도 알아둡니다. ㅎㅎ
서식문자중에서 %n은 %n이 나오기전에 출력된 자리수를 계산하여 스택의 다음 4바이트에 있는 내용을 주소로 여기고 그 주소에 계산한 자리수를 입력하는 것입니다.
이를 통해 우리가 원하는 값을 원하는 메모리 주소에 쓸 수 있습니다.
eggshell의 주소 0xbffff2a7 과
dtors의 주소 0x08049598를 사용하여 공격코드를 작성합니다.
(python -c 'print "AAAA\x98\x95\x04\x08AAAA\x9a\x95\x04\x08%8x%8x%8x%62079c%n%52568c%n"';cat)|./attackme
여기서
%8x%8x%8x가 지난 시점에서 esp는 "AAAA낮은주소AAAA높은주소"를 가리키게 되고
1번째 %정수c는 첫번째 AAAA를 정수 길이로 출력포맷을 만들며 %c 포맷스트링 지정자에 의해 스택상 4바이트 더 이동합니다. 그러면 이제 esp는 BBBB를 가리키게됩니다.
2번째 %정수c는 두번째 AAAA를 정수 길이 만큼 출력포맷을 만들며 %c 포맷스트링 지정자에 의해 스택상 4바이트 더 이동합니다.
그럼 2번째 %n 이전까지의 길이값을 esp가 가리키는 주소에 저장합니다. (높은 주소)
소멸자의 주소에 쉘코드의 주소를 올려야 하는데 크기가 크기 때문에 2바이트씩 나눠 주므로
bfff와 f2a7을 나눠 넣어줍니다. (리틀에디안 방식으로요)
(bfff=49151)
(f2a7=62119)
이 때, %n은 자신이 나오기 전까지의 모든 자리수를 계산하므로 AAAA\x98\x95\x04\x08AAAA\x9a\x95\x04\x08%8x%8x%8x에 대한 자리수도 계산 합니다.
그러므로 이들의 자리수를 빼줘야합니다.
(4+4+4+4+8+8+8 = 40)
그래서 %62079c를 넣어줍니다. (리틀에디안 방식으미로 f2a7먼저)
bfff를 넣어줄때도 마찬가지로 계산하여
%52568을 넣어줍니다.
(114687-62119 = 52568)
'Security > Pwnable' 카테고리의 다른 글
LOB Level 2 gremlin (0) | 2021.08.09 |
---|---|
LOB Level 1 gate (0) | 2021.08.09 |
FTZ Level 19 (0) | 2021.08.09 |
FTZ Level 18 (0) | 2021.08.09 |
FTZ Level 17 (0) | 2021.08.09 |