tmxklab
[FTZ] Level11 풀이 본문
1. 문제 확인
1) 사용자 및 패스워드 : level11 / what!@#$?
2) 파일 확인
3) hint 코드 설명
#include <stdio.h>
#include <stdlib.h>
// argc, argv를 통해 인자 값을 받아 실행
int main( int argc, char *argv[])
{
// char형 str변수 256바이트 할당
char str[256];
// 실제 사용자 ID와 유효 사용자 ID 3092로 설정
setreuid(3092, 3092);
// argv[1]인자 값을 str변수에 복사
strcpy(str, argv[1]);
printf(str);
}
- 프로그램 실행 시 인자 값을 받아 str배열에 복사한 뒤 출력해주는 코드
- strcpy()에서 str배열의 크기(256bytes)보다 더 많은 값을 argv[1]에 넣을 수 있음 -> bof취약점 존재
- printf()에서 포맷을 지정하지 않고 str출력 ->포맷스트링 취약점 존재
2. 접근방법
1) attackme 디버깅
① strcpy()를 이용한 bof 공격
- strcpy()에서 버퍼의 크기를 고려하지 않고 str배열의 크기(256bytes)보다 더 큰 값 입력 가능
- 따라서, 256bytes의 더미 값과 return주소 값에 넣을 쉘 코드 사용하면 공격 가능
② printf()를 이용한 포맷스트링 공격
- str변수에 %d와 같은 서식문자가 들어갈 경우 문자열로 보지 않고 서식문자로 인식
- 즉, str변수에 형식 지정자의 개수만큼 인자들이 스택에서 esp가 가리키는 값을 추출함
- %n을 활용하여 리턴 주소 값에 쉘 코드 주소 사용
2) 스택구조
3) 결론
- 여기서는 printf()를 이용한 포맷스트링 공격 실시
3. 풀이
※ 포맷스트링 버그 테스트
- printf()실행 시 인자로 스택에 저장된 str변수의 주소를 참조하여 화면에 출력 시작
- esp는 스택에서 printf()의 인자(str)가 저장된 곳의 시작점을 가리킴
- 만약 str에 "AAAA%x"를 입력 시 "AAAA"는 그대로 출력하고 %x는 esp의 다음 4btyes를 가리켜 출력
- "AAAA%x%x%x%x"를 입력 시 "AAAA + 주소 값 + 주소 값 + 주소 값 + 41414141"출력함(41은 A아스키코드 값)
- 위 결과를 통해 printf()에서 esp가 3번 높은 주소로 이동하면 str변수가 위치한 주소 값을 참조하는 것을 알 수 있음
※ 서식 문자 %n
- %n이 나오기 전에 출력된 자릿수를 계산 후 다음 4bytes에 있는 내용을 주소로 여겨 해당 주소에 계산한 자릿수를 입력
- 만약, "AAAA%n"을 넣게되면 %n의 전에 AAAA가 4자리수이므로 다음 4bytes의 내용을 주소로 여겨 계산한 자릿수 4를 넣게 됨
- 따라서, %n을 사용하여 RET주소에 쉘 코드 주소 적용가능
- 추가로 %c100%n을 사용하면 100자리로 인식하여 100이라는 숫자를 입력
※ 문제 1
- 쉘 코드의 주소를 입력하기 위해 10진수로 변경하여 입력
- 하지만, x86시스템에서 0xffffffff만큼의 크기를 지정할 수 없음 → 쉘 코드의 주소를 2bytes씩 나눠 입력
- 만약, 쉘 코드 주소 값이 0xbffffab8이며 RET주소가 0x08049594이면 fab8(64,184)는 0x08049594의 주소에 넣고, bfff(49,151)은 0x08049594에서 2bytes 증가한 0x08049596에 넣어야 함
- 또한, 리눅스 시스템은 리틀 엔디안방식을 사용하므로 주소 값을 넣을때 리틀 엔디안 방식으로 넣어야 함
쉘 코드 주소 값 : 0xbffffab8 -> 0xbfff(49151) / 0xfab8(64184)
RET주소 값 또는 .dtors 주소 값 : 0x08049594
(페이로드1) AAAA\x94\x95\x04\x08AAAA\x96\x95\x04\x08%8x%8x%8x%64184c%n%49151c%n
※ 문제 2
- 쉘 코드 주소 값이 0xbfff fab8이므로 2bytes씩 나눠야 함
- 또한, %n은 %n이 나오기 전까지 모든 자릿수를 계산하므로 0xfab8이 나오기 전의 모든 자릿 수 고려
- 따라서, 다음과 같이 계산
쉘 코드 주소 값 0xbfff fab8
주소1 : 64,184(0xfab8) - 40(bytes) = 64,144
주소2 : 114,687(0x1bffff) - 64,144 - 40 = 50,503
참고로, 주소 2에서 1을 붙인 이유는 49,151(0xbfff) - 64,144 계산을 하면 음수가 되므로 1을 붙여 계산
(페이로드2) AAAA\x94\x95\x04\x08AAAA\x96\x95\x04\x08%8x%8x%8x%64144c%n%50503c%n
결국, printf()가 실행되고 str의 값을 읽다가 %n이 오면 0xfab8이 esp가 가리키는 다음 4바이트의 내용 \x94\x95\x04\x08에 저장되고 다시 %n이오면 0xbfff가 방금 esp가 가르켰던 다음 4바이트인 \x96\x95\x04\x08에 저장
※ 공격 준비
이제 해야할 일은 ① 쉘 코드 주소와 ② RET주소 또는 .dtors의 주소를 알아내는 과정
① 쉘 코드 주소
- 환경 변수에 쉘 코드 주소 저장 후 getenv()를 사용하여 쉘 코드 주소를 알아내면 됨
② .dtors(RET주소 값은 쉘이 실행될 때 자주 바뀌므로 편한 방식 사용)
- .dtors는 main()가 끝나고 실행되는 명령이 있는 곳
- gcc로 컴파일한 경우에만 존재
- 따라서, .dtors의 주소 값에 +4만큼의 위치에 쉘 코드 주소를 덮어씌우면 됨
- $objdump -h attackme | grep .dtors (해당 명령어로 주소 알아냄)
※ 포맷스트링 공격
공격 코드
./attackme ``python -c 'print "AAAA\x94\x95\x04\x08AAAA\x96\x95\x04\x08%8x%8x%8x%64144c%n%50503c%n"'``
'War Game > FTZ' 카테고리의 다른 글
[FTZ] Level16 풀이 (0) | 2020.01.12 |
---|---|
[FTZ] Level15 풀이 (0) | 2020.01.12 |
[FTZ] Level14 풀이 (0) | 2020.01.12 |
[FTZ] Level13 풀이 (0) | 2020.01.12 |
[FTZ] Level12 풀이 (0) | 2020.01.12 |