IT(개발, 보안)/pwnable

[Pwnable 풀이] 14. lotto

인도호랑이 2022. 12. 12. 03:35

14번째 풀이, lotto 문제이다.

 

먼저 ssh 접속을 위해 PuTTY 프로그램을 사용해서 디렉토리 내 파일을 점검했다.

현재 id는 lotto 이므로 flag값을 확인할 수 없고 lotto 실행 파일이 suid로 설정되어 있어 flag 값의 확인이 가능할 것으로 보인다.

 

lotto.c의 코드를 살펴보자.

코드 길이가 길어서 나누어서 올리는 점 양해 부탁드립니다.

 

코드는 play(), help(), main() 함수 세개로 나누어져있다.

 

main() 함수에서는 사용자에게 메뉴 선택권을 주고, help() 함수는 사용자에게 규칙에 대한 설명을 제공한다.

 

play() 함수를 살펴보면 read 함수를 사용해서 표준입력(파일 디스크립터 0)에서 6바이트를 읽어서 submit 배열에 저장하는 점을 유의해야한다. 사실 맨 처음에 바이트가 아닌 수로 생각하고 익스폴로잇을 짰는데 알고보니 익스플로잇을 사용할 정도로 오래 걸리는 작업도 아니었다는 후일담...

 

어쨌든 그렇게 읽어온 바이트 값을 랜덤 생성한 1~45 사이의 값과 for문을 사용해서 비교하여 match 변수에 6번이 기록되면 flag를 상으로 제공한다. 그런데 이 for문을 살펴보면 로또 번호의 중복을 체크하지 않는 것을 확인할 수 있다. 이 말은 즉슨, 만약 6개의 숫자를 동일하게 제출한다면 1등의 확률이 1/45의 6승이 아닌 6/45로 크게 증가하는 것을 알 수 있다  

 

결론적으로, 6개의 동일한 바이트를 입력값으로 제공하면 7.5번에 한번 꼴로 flag를 얻을 수 있기 때문에 그냥 1~45 사이의 byte 값을 가진 문자열, 나의 경우에는 느낌표(!),를 입력하면 된다.

 

그러나 맨 처음 시도할때 정수로 띄어쓰기 까지 해야하는 줄 알았던 나는 1 1 1 1 1 1 로 10번 정도 해보고 뭐지? 하고 아래의 익스플로잇을 작성해서 /tmp 디렉토리에 저장해서 실행하였다. flag 값의 경우에는 현재 파일에 flag 이름을 가진 심볼릭 링크를 실제 flag 파일에 연결해서 사용하면 플래그를 얻을 수 있다.

from pwn import *

p = process(executable = '/home/lotto/lotto', argv = "")

while True:
        print p.recv()
        p.sendline('1')
        print p.recv()
        p.sendline('!!!!!!')
        print p.recvline()
        a = p.recvline()
        if a == 'bad luck...\n':
                print('failed')
        else:
                print a
                break

 

위의 코드는 내 입력값을 느낌표 6개로 고치고 난 뒤이지만 실제로 1 1 1 1 1 1을 넣어보면 한 30초 이내에 flag를 찾아내는 것 같다. 

 

반응형