hint를 확인한다.
그동안의 hint들과는 다르게 좀 길다.
천천히 풀어보자.
우선 크게 shellout 함수와 main 함수가 있다.
shellout 함수는 level19의 권한을 얻어서 쉘을 실행시키는 것이다.
main 함수에는 처음보는 함수들이 많아서 구글검색을 해보았다.
[ fd_set ]
0과 1로 저장되는 비트들의 배열
( FD_로 시작하는 매크로 함수들은 모두 이 구조체를 사용한다. )
[ fd_set fds; ]
fd_set 구조체를 사용하기 위한 변수 fds를 선언
[ FD_ZERO(&fds) ]
함수 원형 : FD_ZERO(fd_set *fdset)
fdset이 가리키는 변수의 모든 비트를 초기화 함( 모든 비트를 0으로 만든다. )
[ FD_SET(STDIN_FILENO, &fds); ]
함수 원형 : FD_SET(int fd, fd_set *fdset) : *fdset 포인터가 가리키는 변수에 fd로 전달되는 파일 디스크립터의 정보 설정
( 1로 설정 )
[ 번외 ]
함수 원형 : FD_CLR(int fd, fd_set *fdset) : fdset 포인터가 가리키는 변수에 fd로 전달되는 파일 디스크립터의 정보를
삭제( 0으로 설정 )
[ FD_ISSET(fileno(stdin), &fds) ]
함수 원형 : FD_ISSET(int fd, fd_set *fdset) : fdset 포인터가 가리키는 변수가 fd로 전달되는 파일 디스크립터의 정보를 지니는지 확인
[ select(FD_SETSIZE, &fds, NULL, NULL, NULL) ]
함수 원형 : int select ( int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout )
nfds : 검사 대상이 되는 fd 범위로 세개의 집합( readfds, writefds, exceptfds )중 가장 큰
파일 디스크립터 값에 1을 더해서 전달
readfds : 시스템에 의해 즉시 입력이 가능한지 확인
writefds : 시스템에 의해 즉시 출력이 가능한지 확인
exceptfds : 예외가 있는지 검사하기 위한 리스트
timeout : select가 반환하기 전에 blocking 될 수 있는 시간의 제한 값( 0 : 즉시반환 / NULL : 무한 block )
리턴값( -1 : 오류발생 / 0 : 타임아웃 / 0보다 큰수 : 변화 발생 파일 디스크립터 수 )
[ 파일 디스크립터 ]
- 운영체제가 만든 파일 또는 소켓을 지칭하기 위해 부여한 숫자
- FCB( File Control Block )이라고 부른다.
- 정수형으로 차례로 부여되며 0, 1, 2는 이미 할당되어 있어서 3부터 할당된다.
-> 0 : Standard Input / 1 : Standard Output / 2 : Standard Error
[ FCB ]
파일을 관리하기 위한 운영체게가 필요로 하는 파일의 정보를 가진다.
[ fflush ]
fflush(stdout) : 출력 버퍼 안에 존재하는 데이터를 비우는 즉시 출력한다.
fflush(stdin) : 입력 버퍼 안에 존재하는 데이터를 비우는 즉시 삭제한다.
< 버퍼에 데이터를 비운다 = 버퍼에 있는 데이터를 꺼내 출력장치로 보낸다. >
이제 메모리 구조를 한번 살펴보자.
우선 string 100byte가 있고 그 밑에 check 4byte가 있으며 그 밑에 x 4byte,
그 밑에 count 4byte, 그 밑에 fds가 있게 된다.
그럼 check 값이 0xdeadbeef와 맞는 조건은 절대 참이 될수 없는 구조이다.
그렇다면 메모리를 뒤로 가게 하여 check부분에 0xdeadbeef를 넣어
참이 되도록 해야한다.
코드를 보게되면 case문에 0x08을 넣게되면 count--가 된다는 것이 보인다.
이것으로 string[count]가 음수가 되어서 check의 위치까지 가게 해 보자.
우선 tmp폴더로 실행파일을 복사해 GDB 디버거를 통해 살펴보자.
main+3을 보면 0x100을 할당하는 것이 보인다.
0x100 = 256byte이므로 tring 100byte, check 4byte, x 4byte, count 4byte,
fds 4byte, dummy 140byte가 된다.
알아야 할 것은 string의 주소와 check의 주소 차이가 얼마인지를 알아야 하므로
우선 check의 주소를 확인하면 main+91을 통해 check의 주소를 알 수 있다.
-> check의 주소 : 0xffffff98
그럼 string의 주소는 main+409부터 보면 cmpl로 비교하는 곳이 4개가 나오는데
이곳이 바로 case 부분인 것 같다.
그렇다면 3번째 case부분을 보게되면 첫번째 jmp로 인해 main+481로 가는데
이 곳의 주소는 0xffffff90이다.
이곳이 바로 count의 주소이고, 또 다른 jmp로 인해 main+499로 가는데
이 곳의 주소는 0xffffff9c이다.
즉 이곳이 string의 주소가 된다.
그렇다면 check와 string의 주소 차이는 4byte이므로 이 두 지점사이의
dummy값은 없는 것이다.
이제 돌아가서 한번 명령어를 실행해보자.
0x08을 한번 넣으면 한칸씩 뒤로가므로 4번넣으면 check의 주소가 된다.
그럼 여기서 deadbeef를 넣으면 아마 level19의 권한을 얻을 것이다.
'코딩 문제 > 해커스쿨' 카테고리의 다른 글
FTZ : level20 (0) | 2019.07.25 |
---|---|
FTZ : level19 (0) | 2019.07.22 |
FTZ : level17 (0) | 2019.07.19 |
FTZ : level16 (0) | 2019.07.14 |
FTZ : level15 (0) | 2019.07.13 |