[Pwnable] pwntools 실습-2 (flag-shop)
이번 포스팅은 pwntools 실습 2번 문제로 드림핵 워게임의 flag-shop문제를 풀어보겠습니다.
문제는 https://dreamhack.io/wargame/challenges/1874 에서 다운로드 가능합니다.

먼저 문제를 다운받으면 전포스팅과 마찬가지로 flag와 prob.py가 있습니다.
prob.py를 분석해보겠습니다.
문제 분석
먼저 Welcome to flag-shop ~~부터 3줄정도 print한 뒤 items 리스트가 나옵니다.
items = [
"flag",
"grape",
"orange",
"apple",
"banana",
"mango",
"melon",
"pineapple",
"peach",
"strawberry"
]
items 리스트에는 9가지 과일과 flag가 존재합니다.
for round in range(50):
random.shuffle(items)
print()
for idx, item in enumerate(items):
print(f"{idx}. {item}")
print("Which item do you want to buy?")
choice = int(input("> "))
if items[choice] != "flag":
print("Failed to get flag...")
exit()
함수의 main 부분인 for문을 보면 item안의 항목들을 랜덤으로 섞은 뒤, enumerate()를 통해 인덱스와 값을 .으로 구분하여 출력합니다.
또 "Which item do you want to buy?"라는 문자열과 함께 choice라는 함수로 사용자의 입력을 받은 뒤 입력한 인덱스 값이 flag일경우 50회 반복, 틀릴경우 프로그램을 종료합니다.
이 프로그램에는 시간을 측정하는 함수가 있는데,
start = time.time()
~~~ #for문
end = time.time()
으로 시간을 측정한 뒤, 시간이 10초 이상 걸릴경우 함수를 종료하는 형태이기 때문에 자동화 스크립트를 작성해야 합니다.
문제 풀이
먼저 스크립트를 작성하기 전에 서버에 연결해보고 그 반응을 확인해보도록 하겠습니다.

마찬가지로 가상환경을 열어주고, python을 실행한뒤 remote로 서버에 연결해줍니다.

recvline()을 통해 한줄한줄 읽어줬습니다. there are ~~ 줄이 나오고 \n가 한 줄 나온 뒤부터 idx와 item 목록이 출력되고 있습니다.


이번엔 r.interactive()를 통해 출력되는 문자들을 모아봤습니다. 아까 봤던대로 인덱스와 아이템이름 사이에는 . 으로 구분되어 있음을 확인했습니다.
오른쪽 사진에서는 입력을 넣었는데 50번 반복하는 모습을 확인하였습니다. 입력하기 전에 > 라는 문자가 주어지므로 스크립트를 짤 때 참고하면 편하게 짤 수 있을 것 같았습니다.
스크립트 작성
스크립트를 작성해보겠습니다. 먼저 드림핵에서는 import sys로 파이썬 실행 시 호스트와 포트값을 입력하는 방식을 알려주었는데 뭔가 더 귀찮은 것 같아서 호스트랑 포트값은 직접 넣었습니다.
from pwn import*
r = remote("host주소", port)
r.recvuntil(b"There are 50 rounds.\n")
for 문 시작 전 3줄가량 문자열이 출력되므로 recvuntil()을 통해 바로 for문으로 진입할 수 있게 작성하였습니다.
for _ in range(50):
solve_round(r)
또 이번 문제처럼 반복문을 처리하는 경우 def를 통해 함수를 만들고 50번 반복하도록 반복문을 넣었습니다.
이제 코드 메인 부분을 작성하겠습니다.
def solve_round(r):
r.recvline()
number = 0
for i in range(10):
if b"flag" in r.recvline():
number = i
print(number, end="")
break
r.sendlineafter(b"> ", str(number).encode())
먼저 한번의 for문 반복당 아이템이 10개씩 출력되므로, for문을 작성했고 flag라는 바이트문자가 나올때까지 r.recvline()으로 한줄씩 받다가 flag가 나오면 number에 저장하고 반복문을 break합니다
아까 확인했듯이 입력하기 전 "> " 라는 문자가 있으므로 r.sendlineafter(b"> ")을 이용해 문자가 나온 뒤 입력으로 number을 넣습니다.
*.encode()는 원본 prob.py 소스코드를 봤을 때 바이트로 받지 않기 때문에 생략해도 상관은 없습니다.
print는 숫자 입출력이 잘 되는지 확인하기 위해서 넣었습니다.

최종적으로 작성된 코드입니다.

작동시 정상적으로 플래그를 획득할 수 있습니다!
이상으로 기초문제 풀이에 대한 포스팅을 마치겠습니다.
원래 블로그나 이런걸 안해봐서 그런지 글 쓸 때마다 말투가 조금씩 바뀌네요ㅎ
원래 노션에 작성해뒀던 리버싱 내용이랑 풀이를 다 옮기고 싶은데 포너블도 처음 공부해보면서 작성해보니까 도움이 많이 되지만 너무 어려운것 같습니다.
다음에는 드림핵에 해설 있는 문제말고 풀이를 작성해보도록 하겠습니다.
