웹 서비스 접속 시, 프로젝트 현황을 한눈에 확인할 수 있는 'My Projects' 대시보드 화면이 나타납니다.

Raw Socket Sender (Done)를 누르게 되면 아래와 같은 화면이 나오게 됩니다.

Raw Socket Sender에다가 '127.0.0.1' 주소의 8000번 포트로 데이터 'hi'를 보내면 타임아웃(Timeout) 오류가 발생하는 것을 확인할 수 있습니다.


발생한 문제를 더 자세히 확인하기 위해, 이제 문제 파일을 살펴보겠습니다. 파일을 분석하면 왜 타임아웃이 발생했는지 그 원인을 더 명확하게 파악할 수 있을 것입니다.
app.py
#!/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)
1. 소켓 통신 기능 (/socket)
이 기능은 웹 인터페이스를 통해 서버가 사용자를 대신해 소켓 통신을 수행하는 부분입니다. 마치 원격 조종 리모컨처럼, 사용자는 웹페이지에 접속할 서버의 주소(Host), 포트(Port), 그리고 보낼 데이터(Data)를 입력하기만 하면 됩니다.
- 동작 방식: 사용자의 입력이 서버로 전달되면, 서버는 해당 정보로 소켓을 생성하고 연결을 시도합니다.
- 시간 제한: 통신 과정에서 s.settimeout(3) 코드가 있어, 3초 안에 응답이 없으면 자동으로 연결을 끊습니다. 이는 불필요한 대기를 방지합니다.
- 결과 표시: 통신이 성공하면 받은 데이터를, 실패하면 오류 메시지를 웹페이지에 보여줍니다.
이 기능은 외부 또는 내부 서버에 대한 요청을 웹 브라우저를 통해 실행할 수 있게 해주는 도구라고 할 수 있습니다.
2. 관리자 페이지 (/admin) 접근 제어
/admin 페이지는 매우 까다로운 보안 검사를 통과해야만 접근할 수 있습니다. GET 요청은 허용되지 않고, 오직 POST 요청으로만 접근이 가능하며, 아래의 5가지 조건을 모두 만족해야만 최종적으로 FLAG 값을 반환합니다.
- IP 주소: 접속 IP가 127.0.0.1 (로컬호스트)여야 합니다. 외부에서는 접근이 불가능합니다.
- User-Agent: HTTP 헤더의 User-Agent 값이 Admin Browser여야 합니다.
- 사용자 헤더: HTTP 헤더에 DreamhackUser 키가 존재하고, 그 값이 admin이어야 합니다.
- 쿠키: admin이라는 이름의 쿠키 값이 true여야 합니다.
- 폼 데이터: POST 요청 본문(Form Data)에 포함된 userid 값이 admin이어야 합니다.
/admin 페이지에 접속하면 'Method Not Allowed' 오류가 발생합니다. 이는 /admin 페이지가 GET 요청을 허용하지 않고, POST 요청만 받는다는 것을 의미합니다. 그런데 /socket 기능은 서버가 사용자를 대신해 원하는 곳으로 데이터를 보내주는 역할을 합니다. 우리는 이 점을 이용해 /socket에 서버 자기 자신의 주소(127.0.0.1)와 함께 /admin 페이지로 가기 위한 모든 정보가 담긴 HTTP 요청을 넣으면 풀리는 문제입니다.
이제 Data 필드에 통신에 필요한 특정 값을 삽입하겠습니다. 이 값을 설정함으로써 서버의 응답을 조작하거나, 특정 동작을 유도할 수 있는지 확인해 볼 수 있습니다.
POST /admin HTTP/1.1
Host: 127.0.0.1:8000
User-Agent: Admin Browser
DreamhackUser: admin
Cookie: admin=true
Content-Type: application/x-www-form-urlencoded
Content-Length: 12
userid=admin

🤔 POST 요청의 필수 조건: Content-Type과 Content-Length
두 헤더는 POST 요청의 본문 데이터를 서버가 정확하게 이해하도록 돕는 역할을 합니다.
- Content-Type: 데이터의 종류를 알려줍니다. application/x-www-form-urlencoded는 웹 폼(form)을 통해 보내는 데이터라는 것을 의미합니다.
- Content-Length: 데이터의 크기를 알려줍니다. 서버는 이 길이를 참고해 본문을 읽습니다. 여기서는 userid=admin이라는 데이터를 보내기 때문에, 총 12개의 문자로 구성되어 Content-Length: 12가 되는 것입니다.
이 두 정보가 있어야 서버는 보낸 데이터를 올바르게 해석하고 처리할 수 있습니다.
그 결과 플래그를 성공적으로 획득하였습니다. 이번 실습은 서버 사이드 요청 위조(SSRF)를 통해 IP 주소 및 메서드 제한과 같은 웹 서비스의 취약점을 직접 우회하는 과정을 보여주었습니다.

'Dreamhack > 웹해킹' 카테고리의 다른 글
| [LEVEL 1] csrf-1 (0) | 2025.09.03 |
|---|---|
| [LEVEL 1] simple_sqli (0) | 2025.09.02 |
| [LEVEL 1] Disgusting Ads (0) | 2025.08.28 |
| [🌱Beginner] php7cmp4re (0) | 2025.08.27 |
| [🌱Beginner] phpreg (0) | 2025.08.27 |