[FTZ]level18
저는
열공하는 베짱이가 되기 위해
놀고 싶은 마음을
한 켠에 접어놓고
달려오다보니
level18까지 오게 되었네요.
시작해 볼까요?
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
void shellout(void);
int main()
{
char string[100];
int check;
int x = 0;
int count = 0;
fd_set fds;
printf("Enter your command: ");
fflush(stdout);
while(1)
{
if(count >= 100)
printf("what are you trying to do?\n");
if(check == 0xdeadbeef)
shellout();
else
{
FD_ZERO(&fds);
FD_SET(STDIN_FILENO,&fds);
if(select(FD_SETSIZE, &fds, NULL, NULL, NULL) >= 1)
{
if(FD_ISSET(fileno(stdin),&fds))
{
read(fileno(stdin),&x,1);
switch(x)
{
case '\r':
case '\n':
printf("\a");
break;
case 0x08:
count--;
printf("\b \b");
break;
default:
string[count] = x;
count++;
break;
}
}
}
}
}
}
void shellout(void)
{
setreuid(3099,3099);
execl("/bin/sh","sh",NULL);
}
hint 파일을 확인해보니
이 소스가 보이네요.
확실히 앞 전과는 레벨이 다르네요..
벌써 막막하지만
하나하나 뜯어가봅시다.
먼저 12라인을 보면
fd_set 구조체 타입의 fds가 선언되어 있습니다.
fd_set구조체란?
File Descriptor를 저장하는 구조체 입니다..
파일 디스크립터란?
위키백과에서는
"컴퓨터 프로그래밍 분야에서 파일 서술자 또는 파일 기술자는 특정한 파일에 접근하기 위한 추상적인 키이다."
라고 나옵니다."
예를 들어
전화를 자주하는데 매번 전화번호를 누르고 전화를 걸자니
번거로운 반복과정을 계속하게 되겠죠?
이 과정을 줄이기 위해
1번을 누르고 있으면 전화가 가도록
단축키를 사용합니다.
이 숫자! 1을 파일 디스크립터라고 이해했습니다.
보통 파일을 열면
계속 파일에 접근을 하는 내용을 적어야하지만
파일 디스크립터를 이용해 접근을 하면 편리하고
작업이 끝나면 close()해주면 되지요.
fd_set구조체는?
단축키를 저장한 구조체라고 생각해도 되겠네요!!
fd_set 구조체는 /usr/include/sys/select.h
내에 선언되어 있습니다.
typedef struct {
u_int fd_count;
SOCKET fd_array[FD_SETSIZE];
} fd_set;
fd_count는 파일디스크립터의 개수를 의미하고,
fd_array는 파일디스크립터의 상태를 저장합니다.
1) FD_ZERO(fd_set *fdset)는
fd_set을 초기화하는 함수입니다.
2) FD_SET(int fd, fd_set *fdset)은
해당 파일디스크립터 fd를 1로 설정하는 함수입니다.
3) int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) 는
[1] n은 파일 디스크립터의 개수를 의미합니다.
[2] readfds에 fd_set의 주소를 넣으면 수신할 데이터가 있는지 확인합니다.
[3] writefds에 fd_set의 주소를 넣으면 송신할 데이터가 있는지 확인합니다.
[4] exceptfds에 fd_set의 주소를 넣으면 예외가 있는지 확인합니다.(0이면 체크x)
[5] timeout은 timeout 구조체의 주소를 넣으면 됩니다.
(상태비트에 변경이 있는지 timeout의 시간동안 대기합니다.)
(만약 NULL을 넣는다면 무한정 대기하지요.)
[6] 반환값은 데이터가 변경된 파일의 개수를 반환합니다.
(즉, fd_set의 비트 중 1의 개수를 반환하지요.)
4) FD_ISSET(int fd, fd_set *set)은
fd가 인자로 지정한 방식으로 입력을 받도록 설정되어 있는지를 확인합니다.
[1] fd는 set에서 삭제할 파일디스크립터를 의미합니다.
[2] set은 읽기/쓰기/오류에 대한 모니터링을 할 fd목록을 관리하는 구조체 입니다.
[3] 반환값은
fd가 set에 설정되지 않은 경우 0을 반환하고,
fd가 set에 설정되어 있는 경우 0이 아닌 값을 반환합니다.
5) ssize_t read(int fd, void *buf, size_t count)는
[1] fd는 입력받을 파일디스크립터를 말합니다.
[2] buf는 입력받은 데이터를 저장할 주소입니다.
[3] count는 입력받을 데이터의 크기입니다.
[4] 반환값은 입력을 성공적으로 마쳤다면
입력받은 크기를 반환하고,
실패하면 -1을 반환합니다.
1) '\r', '\n'
'\r'은 Carriage return이며
다음줄의 가장 왼쪽으로 이동하는 문자입니다.
'\n'은 Line Feed이며
다음줄로 이동하는 문자입니다.
이 경우 '\a'를 출력하네요.
2) '\a'
bell이라고 하며, 삑 소리를 발생시키는 문자입니다.
3) 0x08
count를 1만큼 감소시키고
'\b'가 2번 출력합니다.
'\b'는 backspace이며
이전 문자를 지웁니다.
4) default
string[count]에 입력받은 x를 저장하고
count를 증가시킵니다.
왠지 여기서 덮어쓸 수 있을 것 같은 생각이 들지 않나요?
이제 윗부분을 확인하러 갑시다.
check의 값이 0xdeadbeef와 같다면
shellout()함수를 실행합니다.
즉!! check의 값을 변조하면 되겠군요.
string배열 밑에는 check가 있습니다.
string-4 = check가 되겠죠?
우리는 아까 case문에서
0x08을 입력하면
count가 1씩 감소하는 것을 확인했습니다.
그럼 0x08을 4번 입력하면 되겠네요!!
이후 default에서
string에 쓸 수 있습니다.
아마 string[-4]부터 쓰겠죠~?
select함수는 여러 파일의 입출력을 관리하기 위한 함수지만
전부 NULL을 넣기 때문에 실제로는
STDIN인 표준입력만 사용하고 있습니다.
결국 위에 내용은 싹 몰라도 문제가 없다는 .. 그런 말이 됩니다.
이 참에 알아두면 좋긴하지만 말이지요.
하하..
payload를 생각해봅시다.
0x08080808 + 0xdeadbeef
공격코드는
(python -c "print '\x08'*4 + '\xef\xbe\xad\xde'"; cat) | ./attackme
와 같이 작성하면 됩니다.
Level19 Password is "swimming in pink".
'Wargame > FTZ' 카테고리의 다른 글
[FTZ]level20 (0) | 2019.09.09 |
---|---|
[FTZ]level19 (0) | 2019.09.08 |
[FTZ]level17 (0) | 2019.09.08 |
[FTZ]level16 (0) | 2019.09.08 |
[FTZ]level15 (0) | 2019.09.08 |
댓글