본문 바로가기

CTF/CTFs

[Affinity CTF 2020] : Catch Me if You Can

728x90
반응형

#  Write Up으로 삽질하기 

Review

ctftimes를 보다가 begginer 레벨이라 서버가 아직 열려있어 도전해봤다. 문제의 조건과 해결방법은 다음과 같다.

1. 주어진 링크에 접속하면 긴 문자열이 나온다
2. 이 문자열은 image 파일의 내용을 보여주는 것이고 10초마다 바뀐다
3. 10초마다 바뀌는 image 파일의 내용을 서버로 전송하면 성공

주어진 task가 정확해 풀려고 시도를 해봤으나 미약한 지식으로 풀진 못했고 WriteUp을 보고 플래그를 얻을 수 있도록 시도해보자 라는 느낌에서 출발했는데 WriteUp을 보고도 플래그를 얻지 못한 문제이다. 

Walk Throught

문제를 풀기 위해 생각해볼 수 있는 단계별 작업이 다음과 같았다.

# Plan
1. 이미지 파일은 base64 string으로 되어있다.
2. Http Handler 또는 프록시 서버를 이용해 base64 string을 전송받는다
3. base64 string을 파일로 저장한뒤 OCR을 이용해 문자를 뽑아낸다.
4. 뽑아낸 문자를 제출함으로 플래그를 얻는다

하나씩 알아보자 Http Handler 또는 프록시 서버를 이용해서 base64 string을 전송받아야 한다. 하지만 문제가 base64 string을 전송받을 서버를 가지고 있지 않다. WAN에 노출된 서버도 없고 포트 포워딩을 시켜도 그 과정에 시간이 다소 소요되므로 배보다 배꼽이 더 큰 상황이다. 그러니 ngrok을 이용해 라우팅을 받도록 시도했다.

ngrok이 localhost에 80 포트에도 데이터를 똑같이 전송시켜주는 기능이 있지 않을까 했다. 그래서 다음과 같이 HttpHandler를 이용할 수 있는 코드를 작성했다.

class MyHandler(http.server.BaseHTTPRequestHandler):
	def do_POST(self):
        # content_len은 Form-data를 전송받기 위함
		content_len = int(self.headers.get('Content-Length'))
		post_body = self.rfile.read(content_len)[4:]
		b64 = parse_req(post_body)

		self.send_header('Access-Control-Allow-Origin', '*')
		self.end_headers()
		return None
 
 s = http.server.HTTPServer(('localhost',80),MyHandler)
 s.serve_forever()

base64 String을 받을 수 있는 서버를 준비했다. 그리고 브라우저에서 내가 생성한 서버로 base64 string을 전송하게 만드는 코드는 다음과 같다. 이 코드를 브라우저의 콘솔 창에 복붙을 하게 되면 내 쪽 서버로 base64 string을 전송하게 된다. (이 코드가 "long polling" 방식이라고...)

const NgrokDomain = ""

socket.on('catch_me', async function(){
	const image = document.getElementById('catcher').src;

	const headers = {
		'method' : 'POST',
		'Access-Control-Allow-Origin': '*',
		'body' : 'pwn='+image,
	};

    let response = await fetch(NgrokDomain, headers);
    response.text().then(unlock => {
        document.forms[0]["code"].value=unlock;
        document.forms[0].submit(); // you can also send an XHR request directly to /validate
    });
});

그렇다면 "이미지 파일이 base64 string"으로 된 게 무슨 뜻일까 페이지 소스보기를 통해 확인된 이미지 파일은 특정 경로를 나타내는 것이 아닌 "base64 string"으로 "View Page Source" 기능으로 확인했다. base64 string을 뽑아내 파이썬으로 이미지를 저장시키는 코드는 구글을 통해 아래와 같이 만들었다.

PNG_OUT="out.pnt"

def save_png(path,bytestream):
	with open(PNG_OUT,"wb") as file:
		file.write(base64.decodebytes(bytestream.encode("utf-8")))

위에서 말하는 "bytestream"이란 것은 base64 string이다. 문제는 데이터 타입 "string"이기 때문에 encode 함수로 데이터 타입을 bytes로 바꿔줘야 동작되었다. 여기서 지금까지 한 일들을 정리해보자.

1. Base64 String을 받을 수 있는 서버를 설정함 (ngrok)
2. ngrok을 이용해 Base64 String을 가져오는 방법보다 Python의 HttpHandler를 이용해 준비
3. 브라우저의 base64 String을 내가 준비한 서버로 보낼 코드를 만듬
4. 준비한 서버에서 받은 base64 String을 이미지 파일로 저장

즉 Plan에 적어놓은 단계 중 2단계까지 완료가 된 셈이다. 다음으로는 생성된 이미지 파일에서 어떻게 텍스트를 추출할까이다. Python에서 OCR 기능을 제공해주는 패키지 중 pytesseract가 있었다. 설치 과정이 다소 복잡했는데 나중에 한 번 다루기로 하고 pytesseract의 Image를 Text를 추출해주는 코드는 다음과 같았다.

import pytesseract

h = Image.open(imagefile)
unlock  = pytesseract.image_to_string(h)

정말 간단했다. 하지만 문제는 pytesseract가 제대로 텍스트를 추출해주지 못했다는 점. 그래서 이미지를 열어본 결과 배경색이 없었다. 혹시 이미지의 배경색을 바꿔주면 되지 않나 싶어서 다음과 같이 코드를 적용했다.

from PIL import Image
import pytesseract

def get_text(imagefile):

    # Background is black
	fill_color = (255,255,255)    
	with Image.open(imagefile) as im:
		with Image.new('RGBA', im.size, fill_color) as canvas:
			canvas_im = Image.alpha_composite(canvas,im)
			canvas_im = canvas_im.convert("RGB")
			canvas_im.save(PNG_OUT,"PNG")

	h = Image.open(imagefile)
	unlock  = pytesseract.image_to_string(h)

	isCheckitString = unlock.replace(" ","").replace("\n","")
	print(isCheckitString)

 이후 shell에 나타난 문자를 입력했지만.. 통과되진 못했다. 다른 Writeup을 보니 selenium을 이용해서 풀었다고 하는데 시간 남을 때 다시 삽질해야겠다. 마지막으로 지금까지 주저리 써놓았던 글에 대한 내용을 영상으로 남기겠다.

 

 
 Posting think

이번 Challenge에서 남은 궁금증

- 브라우저에서 Long Polling 방식으로 데이터를 보낼 때 CORS 경고문 없애는 방법

- pytesseract의 정확도

- Python HTTP Handler 기능 

 

728x90
반응형

'CTF > CTFs' 카테고리의 다른 글

[N00bCTF][MISC]  (0) 2021.01.03
[ctfLearn][Review] : Inj3ction Time  (0) 2020.12.27
[weCTF2020] : Build Up  (0) 2020.12.26
[NACTF2020][Web] : Calculator & Cookie Recipe  (0) 2020.11.09
Google Beginner's Quest : Space-Time Coordinates  (0) 2020.09.30