Tag Archive

#web

이 태그가 포함된 글을 최신순으로 모아봤어요.

135
posts
BLOG2026-08-15
[🥈 Silver 3] 탭 한 글자로 지워진 admin 을 되살린다 — DreamHack My Best Friend 풀이
blog

[🥈 Silver 3] 탭 한 글자로 지워진 admin 을 되살린다 — DreamHack My Best Friend 풀이

인사만 돌려주는 express 서비스가 내부 Private API 를 SSRF 로 호출한다. admin 이라는 문자열과 퍼센트·물음표·샵을 전부 막아 놨지만, 단어 한가운데에 탭 문자를 끼우면 필터는 다른 단어로 보고 통과시키고 axios 가 URL 을 파싱하는 순간 그 탭이 사라지면서 admin 이 되살아난다. express 기본 쿼리 파서가 중복 키를 배열로 묶어 주는 성질까지 얹으면 플래그가 떨어진다.
#dreamhack#ctf#web+6
 
BLOG2026-08-14
[🥈 Silver 4] vim 스왑으로 소스를 줍고 PHP 매직 해시로 admin 로그인 — DreamHack [wargame.kr] jff3_magic 풀이
blog

[🥈 Silver 4] vim 스왑으로 소스를 줍고 PHP 매직 해시로 admin 로그인 — DreamHack [wargame.kr] jff3_magic 풀이

배포 파일이 하나도 없는 블랙박스 웹 문제. 메인 페이지 alert 이 흘린 "hint : swp" 한 줄에서 vim 스왑 파일을 주워 소스를 복구하고, and·or·union 을 전부 막는 자체 방화벽을 HAVING + REGEXP 로 우회해 admin 의 비밀번호 해시를 한 글자씩 뽑았다. 그 해시가 0e 로 시작하는 매직 해시라 PHP 의 느슨한 비교 하나로 로그인이 뚫린다.
#dreamhack#ctf#web+6
 
BLOG2026-08-14
[🥉 Bronze 3] 아무도 없는 테이블이 만든 무조건 참 — DreamHack Logical 풀이
blog

[🥉 Bronze 3] 아무도 없는 테이블이 만든 무조건 참 — DreamHack Logical 풀이

Flask 로그인 폼 하나짜리 문제. 비밀번호를 md5 로 굳혀 넣어 SQL 인젝션은 닫혀 있는데, 정작 users 테이블에 행이 하나도 없다. 조회가 빈 결과를 돌려주면 아이디가 빈 문자열로 떨어지고, 마지막 비교가 그 빈 문자열과 우리가 보낸 아이디를 맞대 본다. 아이디를 비운 요청 한 번으로 로그인이 통과한다.
#dreamhack#ctf#web+6
 
BLOG2026-08-14
[🥉 Bronze 3] 클리어 판정이 통째로 브라우저 안에 있다 — DreamHack 달고나 풀이
blog

[🥉 Bronze 3] 클리어 판정이 통째로 브라우저 안에 있다 — DreamHack 달고나 풀이

캔버스로 달고나 뽑기를 하는 웹 문제. 서버 코드는 24줄뿐이고 flag 를 주는 POST /api/stage5/clear 에는 검증이 한 줄도 없다. 레벨 해금은 localStorage, 90% 판정은 game.js 안. 게다가 5단계는 도형 그리는 함수 자체가 없어 브라우저로는 깰 수 없다. 게임을 건드리지 않고 API 만 한 번 때려 flag 를 받았다.
#dreamhack#ctf#web+5
 
BLOG2026-08-13
[🥇 Gold 3] 비밀번호를 맞히는 대신 비교 대상을 바꾼다 — DreamHack Proton Memo 풀이
blog

[🥇 Gold 3] 비밀번호를 맞히는 대신 비교 대상을 바꾼다 — DreamHack Proton Memo 풀이

메모 편집 기능이 점으로 구분된 속성 경로를 그대로 받아 재귀 setattr 을 돈다. 내 메모 객체에서 출발해 클래스 속성인 Memo.collections 를 타고 들어가면 서버가 만들어 둔 secret 메모의 Password 객체까지 닿는다. 20바이트 난수 비밀번호를 맞히는 대신, 비교에 쓰이는 sha256 해시 한 줄을 내가 아는 값으로 덮어 flag 를 읽었다.
#dreamhack#ctf#web+5
 
BLOG2026-08-10
[🥉 Bronze 3] 수량에 마이너스를 넣으면 가게가 돈을 준다 — DreamHack PEPERO 풀이
blog

[🥉 Bronze 3] 수량에 마이너스를 넣으면 가게가 돈을 준다 — DreamHack PEPERO 풀이

빼빼로 상점의 결제는 소지금에서 100 곱하기 수량을 빼는 식 하나가 전부였다. 수량의 부호를 아무도 검사하지 않아서 음수를 넣으면 뺄셈이 그대로 덧셈이 된다. 브라우저가 걸어 둔 min="0" 은 서버까지 따라오지 않는다는 걸 DevTools와 curl로 확인하고, 소지금을 2000원까지 불려 플래그를 사 온 기록.
#dreamhack#ctf#web+6
 
BLOG2026-08-10
[🥈 Silver 1] 로그인에 성공하는 순간 답이 바뀐다 — DreamHack Login Page 풀이
blog

[🥈 Silver 1] 로그인에 성공하는 순간 답이 바뀐다 — DreamHack Login Page 풀이

부등호도 시간지연도 블랙리스트에 막힌 블라인드 SQL 인젝션. 게다가 쿼리가 행을 반환하면 애플리케이션이 admin 비밀번호를 새로 만들어 버려서, 흔한 "참이면 로그인된다" 오라클을 쓰는 순간 답이 바뀐다. 조건이 참일 때만 MySQL 을 EXP(999) 로 죽여 HTTP 500 을 얻고, 비교는 GREATEST 항등식으로 대신해 32자 비밀번호를 225질의에 복원했다.
#dreamhack#ctf#web+5
 
BLOG2026-08-08
[🥈 Silver 4] 쿠키 한 줄이 서버에서 eval 된다 — DreamHack node-serialize 풀이
blog

[🥈 Silver 4] 쿠키 한 줄이 서버에서 eval 된다 — DreamHack node-serialize 풀이

node-serialize 0.0.4 의 unserialize 는 "_$$ND_FUNC$$_" 로 시작하는 문자열을 eval 로 되살린다. 뒤에 괄호 두 개를 붙여 즉시실행함수로 만들면 역직렬화 시점에 코드가 돈다. 출력 채널이 없어 flag 를 Error 로 던졌고, NODE_ENV 를 안 잡아 둔 Express 가 스택 트레이스를 500 응답 본문에 그대로 실어 보내면서 플래그가 나왔다.
#dreamhack#ctf#web+5
 
BLOG2026-08-08
[🥇 Gold 4] 확장자는 디코딩 전, 파일 경로는 디코딩 후 — DreamHack read_flag 풀이
blog

[🥇 Gold 4] 확장자는 디코딩 전, 파일 경로는 디코딩 후 — DreamHack read_flag 풀이

GoAhead 4.1.3 은 요청 URL 의 확장자를 퍼센트 디코딩 '전' 문자열에서 뽑고, 실제로 열 파일 경로는 디코딩·정규화가 끝난 뒤에 만든다. 같은 파일을 가리키는 두 문자열이 라우팅 단계에서 갈라지는 셈이라, `flag.js%74` 한 글자로 jst 실행 핸들러를 빠져나가 정적 파일 핸들러가 .jst 원본을 그대로 뱉게 만들 수 있다.
#dreamhack#ctf#web+5
 
BLOG2026-08-08
[🥉 Bronze 2] 서명 없는 세션 쿠키와 초 단위로 새는 가입 시각 — DreamHack What time is it? 풀이
blog

[🥉 Bronze 2] 서명 없는 세션 쿠키와 초 단위로 새는 가입 시각 — DreamHack What time is it? 풀이

세션 토큰이 사용자 이름과 가입 시각에 2026을 곱한 값을 이어 붙인 게 전부였다. 서명이 없으니 admin의 가입 시각만 알면 쿠키를 손으로 만들 수 있고, 그 시각은 /welcome의 가입 피드가 초 단위로 그대로 뿌리고 있었다. 쿠키 한 줄을 갈아 끼워 관리자 화면의 FLAG를 회수한 기록.
#dreamhack#ctf#web+6
 
BLOG2026-08-07
[🥉 Bronze 1] IPv6 로 들어가서 IPv4 로 도착하기 — DreamHack Where-is-localhost 풀이
blog

[🥉 Bronze 1] IPv6 로 들어가서 IPv4 로 도착하기 — DreamHack Where-is-localhost 풀이

입력은 반드시 IPv6 여야 하고, 도착한 요청의 출발지는 반드시 IPv4 루프백이어야 한다. 서로 배타적으로 보이는 두 조건을 `::ffff:127.0.0.1` 하나가 동시에 만족시킨다. 파이썬 ipaddress 에게는 version 6 이고 리눅스 커널에게는 127.0.0.1 이기 때문이다. tcpdump 로 루프백에 흐르는 패킷이 실제로 IPv4 라는 것까지 확인했다.
#dreamhack#ctf#web+6
 
BLOG2026-08-06
[🥇 Gold 1] 빈 catch 하나가 명령어 허용목록을 통째로 삼킨다 — DreamHack node_api 풀이
blog

[🥇 Gold 1] 빈 catch 하나가 명령어 허용목록을 통째로 삼킨다 — DreamHack node_api 풀이

/show_logs 는 첫 토큰을 무조건 get 으로 갈아치워 Redis 를 읽기 전용으로 묶어 둔다. 그런데 쿼리스트링을 배열로 보내면 Array 에는 .split 이 없어 TypeError 가 나고, 본문이 비어 있는 catch 가 그걸 삼키면서 정규화 세 줄이 통째로 건너뛰어진다. 임의 Redis 명령이 열리고, 세션 저장소가 같은 Redis 라서 SET 한 번으로 내 세션이 admin 이 된다.
#dreamhack#ctf#web+6
 
BLOG2026-08-06
[🥇 Gold 4] 취약점을 찾는 게 아니라 막는 문제 — DreamHack PATCH-1 풀이
blog

[🥇 Gold 4] 취약점을 찾는 게 아니라 막는 문제 — DreamHack PATCH-1 풀이

122줄짜리 Flask 메모 API 에 심어 둔 취약점을 전부 패치해야 flag 가 나오는 문제. f-string SQLi 두 곳, format() 이 만들어 낸 Jinja2 SSTI 와 저장형 XSS, 메모 소유자 검증 누락, 소스에 박힌 secret_key 까지 여섯 군데를 로컬에서 재현해 놓고 고쳤다. 공격만 막고 끝나면 안 되고 SLA(기능 회귀) 도 같이 통과해야 한다.
#dreamhack#ctf#web+8
 
BLOG2026-08-05
[🥇 Gold 1] 허용된 속성 세 개로 window.CONFIG 를 만들어 낸다 — DreamHack Guest book v0.2 풀이
blog

[🥇 Gold 1] 허용된 속성 세 개로 window.CONFIG 를 만들어 낸다 — DreamHack Guest book v0.2 풀이

htmlentities 뒤에서 태그를 재조립하던 v0.1 의 버그는 그대로 두고, DOMDocument 화이트리스트만 덧붙인 방명록. 남은 것은 a 태그의 id·name·href 뿐인데 페이지 하단에 location.href = window.CONFIG.main 이 있었다. DOM Clobbering 으로 그 전역을 만들어 내고, 이를 막으려 걸어 둔 config.js 의 Object.freeze 는 경로를 한 칸 미는 RPO 로 걷어냈다.
#dreamhack#ctf#web+5
 
BLOG2026-07-31
[🥈 Silver 4] 파일 이름을 바꾸는 기능이 Object.prototype 을 바꾼다 — DreamHack filestorage 풀이
blog

[🥈 Silver 4] 파일 이름을 바꾸는 기능이 Object.prototype 을 바꾼다 — DreamHack filestorage 풀이

파일을 올리고 읽는 Express 앱인데, 이름 변경 기능이 키를 점으로 쪼개 재귀로 대입한다. __proto__ 를 걸러내지 않아서 Object.prototype 에 값을 심을 수 있고, 파일을 읽는 쪽은 하필 그 값을 경로에 그대로 이어 붙인다. 같은 핸들러의 다른 분기는 점을 지워 버리기 때문에, 두 분기 중 어느 쪽을 타게 만드느냐가 전부였다.
#dreamhack#ctf#web+5
 
BLOG2026-07-30
[🥈 Silver 1] 필터가 보는 문자열과 파일시스템이 받는 문자열은 다르다 — DreamHack Dream Gallery 풀이
blog

[🥈 Silver 1] 필터가 보는 문자열과 파일시스템이 받는 문자열은 다르다 — DreamHack Dream Gallery 풀이

URL을 받아 이미지를 가져와 주는 갤러리. urlopen 앞에 필터가 세 겹 있는데, file:// 접두어 검사는 슬래시를 하나 줄이면 빗나가고 flag 문자열 검사는 퍼센트인코딩으로 빗나간다. Flask가 쿼리스트링을 한 번 풀고 url2pathname이 또 한 번 풀기 때문에, 필터가 검사하는 문자열과 open에 도달하는 경로가 애초에 같지 않다.
#dreamhack#ctf#web+6
 
BLOG2026-07-29
[🥉 Bronze 3] 값을 묻지 말고 지시문을 되풀이시킨다 — DreamHack baby-ai 풀이
blog

[🥉 Bronze 3] 값을 묻지 말고 지시문을 되풀이시킨다 — DreamHack baby-ai 풀이

웹 서버 안에서 0.5B 짜리 로컬 모델을 돌리는 챗봇 문제. 플래그는 시스템 프롬프트 첫 줄에 그대로 박혀 있고, 입력에 admin·flag·pass·secret·private 이 들어가면 모델까지 가지도 못한다. 그런데 그 블랙리스트는 질문만 본다. 비밀을 묻는 대신 지시문을 그대로 되풀이하게 시키면 한 줄에 다 나온다. 키릴 문자 한 글자로 부분문자열 검사 자체를 빗나가게 하는 두 번째 경로도 같이 다룬다.
#dreamhack#ctf#web+8
 
BLOG2026-07-29
[🥈 Silver 2] 콜론 하나로 빈 호스트를 만들어 file:// 을 통과시킨다 — DreamHack web-HTTP-CLI 풀이
blog

[🥈 Silver 2] 콜론 하나로 빈 호스트를 만들어 file:// 을 통과시킨다 — DreamHack web-HTTP-CLI 풀이

URL 을 받아 대신 긁어다 주는 43줄짜리 TCP 서비스. 호스트 화이트리스트는 눈에 잘 띄는데, 정작 대부분의 시도를 죽이는 건 그 위에 있는 튜플 언패킹이다. netloc 을 콜론으로 쪼갠 결과가 원소 2개가 아니면 검사에 닿기도 전에 ValueError 로 날아간다. netloc 을 콜론 하나로만 채우면 호스트가 빈 문자열이 되고, urllib 은 빈 호스트를 로컬로 보고 파일을 열어 준다.
#dreamhack#ctf#web+6
 
BLOG2026-07-29
[🥇 Gold 2] 이스케이프가 끝난 뒤에 태그를 만들면 소용이 없다 — DreamHack Guest book 풀이
blog

[🥇 Gold 2] 이스케이프가 끝난 뒤에 태그를 만들면 소용이 없다 — DreamHack Guest book 풀이

배포 파일이 함수 하나뿐인 방명록 문제. htmlentities 로 꼼꼼히 막아 놓고, 그 다음 줄에서 정규식으로 다시 a 태그를 조립한다. PHP 7.x 의 htmlentities 는 기본값이 ENT_COMPAT 라 홑따옴표를 건드리지 않고, 그 한 글자가 href 를 닫아 이벤트 핸들러를 붙일 자리를 연다. 정규식이 첫 닫는 괄호에서 끊기는 탓에 괄호 없는 JS 만 써야 한다.
#dreamhack#ctf#web+6
 
BLOG2026-07-29
[🥈 Silver 2] 검사기는 문자열을 보고 curl 은 URL 을 파싱한다 — DreamHack curling 풀이
blog

[🥈 Silver 2] 검사기는 문자열을 보고 curl 은 URL 을 파싱한다 — DreamHack curling 풀이

"simple api server" 라는 한 줄 설명이 전부인 문제. 46줄짜리 Flask 앱이 url 파라미터를 받아 curl 로 대신 요청해 주는데, 호스트 화이트리스트가 접두사 비교라서 URL 의 userinfo 를 끼워 넣으면 통과하고, 엔드포인트 차단은 문자열 끝만 봐서 물음표 하나에 무너진다. 두 구멍을 한 줄에 겹쳐 놓으면 서버가 자기 자신에게 요청을 보내고 플래그를 뱉는다.
#dreamhack#ctf#web+6
 
BLOG2026-07-28
[🥉 Bronze 4] 복사 금지는 브라우저에게만 하는 부탁이다 — DreamHack Ctrl-C 풀이
blog

[🥉 Bronze 4] 복사 금지는 브라우저에게만 하는 부탁이다 — DreamHack Ctrl-C 풀이

우클릭·드래그·F12를 전부 막아 둔 페이지가 2000자짜리 문자열을 보여주고 "복사할 수 있으면 해봐라"라고 한다. 그런데 그 방어는 전부 브라우저 안에서 도는 JS라, HTTP 응답 자체에는 아무 힘도 없다. curl 한 번이면 본문이 그대로 나온다. 뽑은 문자열을 배포된 스크립트에 넣으면 sha256의 base64 조각과 25바이트 상수를 XOR해 플래그가 떨어진다. 상수가 25바이트, XOR 대상이 23바이트라 출력은 입력과 무관하게 늘 두 글자가 고정된다.
#dreamhack#ctf#web+5
 
BLOG2026-07-28
[🥇 Gold 3] 응답을 한 글자도 못 읽는데 flag 32자가 새는 이유 — DreamHack XS-Search 풀이
blog

[🥇 Gold 3] 응답을 한 글자도 못 읽는데 flag 32자가 새는 이유 — DreamHack XS-Search 풀이

검색 결과가 매치했을 때만 iframe 이 하나 생긴다. 교차 출처라 본문·헤더·상태코드는 전부 막혀 있지만 window.length 는 늘 읽히니, 그 정수 하나로 "이 접두사가 flag 에 들어 있나"를 묻는 오라클이 된다. 봇을 33번 불러 32자리 hex 를 복원했다. 그런데 정찰 중에 제외 조건이 and 로 이어진 걸 보고 드모르간을 풀어 보니 Host 헤더만 127.0.0.1:8000 으로 바꾸면 curl 한 번으로도 끝났다. 두 경로를 다 라이브로 돌려 같은 flag 가 나오는 걸 확인했다.
#dreamhack#ctf#web+7
 
BLOG2026-07-21
[🥉 Bronze 4] 우회할 403은 애초에 없었다 — DreamHack 403-forbbiden 풀이
blog

[🥉 Bronze 4] 우회할 403은 애초에 없었다 — DreamHack 403-forbbiden 풀이

가짜 브라우저 창 안에 "403 Forbidden"이 떠 있는 페이지. 제목부터 대놓고 "403 우회"를 암시하니 자연스럽게 X-Forwarded-For, 경로 트릭 같은 고전적인 헤더 우회 기법부터 시도하게 된다. 그런데 그 화면 속 "/secret"이라는 보호된 경로는 애초에 서버에 존재하지도 않는 눈속임이었다. view-source로 스크립트를 열어보면 진짜 취약점은 전혀 다른 곳에 있었다 — 클라이언트가 "정답 조건"을 스스로 판단해서 보내는 POST 요청을, 서버가 그 어떤 맥락도 검증하지 않고 그대로 받아준다는 사실.
#dreamhack#ctf#web+3
 
BLOG2026-07-21
[🥉 Bronze 1] 확장자만 .md면 통과 — 내용은 아무도 안 봤다, DreamHack Pearfect Markdown 풀이
blog

[🥉 Bronze 1] 확장자만 .md면 통과 — 내용은 아무도 안 봤다, DreamHack Pearfect Markdown 풀이

실시간으로 마크다운을 미리보기 해주는 아주 단순한 PHP 페이지. 업로드 라우트는 파일 이름의 확장자가 .md인지만 검사하고, 미리보기 라우트는 그 파일을 include()로 그대로 불러온다. 두 라우트 모두 각자 보면 그럴듯한 방어처럼 보이지만, 확장자 검사는 "내용"을 전혀 보지 않고 include()는 "확장자"를 전혀 가리지 않는다는 게 문제였다. 처음엔 경로traversal로 flag 파일을 직접 읽으려 했지만 빌드 스크립트가 파일명을 32자 랜덤 문자열로 바꿔둔 탓에 실패했고, 결국 업로드한 .md 파일 안에 PHP 코드를 심어 실행시키는 쪽으로 방향을 틀었다.
#dreamhack#ctf#web+5
 
BLOG2026-07-21
[🥉 Bronze 3] 뚫을 필요가 없었다, 제대로 보기만 하면 됐다 — DreamHack [wargame.kr] already got 풀이
blog

[🥉 Bronze 3] 뚫을 필요가 없었다, 제대로 보기만 하면 됐다 — DreamHack [wargame.kr] already got 풀이

옛 wargame.kr에서 리마스터해 가져온 문제. 페이지를 열어보면 "you've already got key! :p"라는 한 줄짜리 문구뿐이고, HTML 소스를 봐도 똑같다. 문제 설명이 "HTTP 응답 헤더가 보이나요?"라고 대놓고 힌트를 주는데, 이 짧은 문제 하나가 사실 "브라우저 화면에 보이는 것"과 "서버가 실제로 보낸 것"이 다르다는 웹 리버싱의 가장 기초적인 교훈을 정확히 짚고 있었다. 취약점을 찾아 뚫는 문제가 아니라, HTTP 프로토콜을 있는 그대로 관찰하기만 하면 되는 문제였다.
#dreamhack#ctf#web+3
 
BLOG2026-07-21
[🥉 Bronze 1] "IP인지 검증했다"와 "안전하다"는 다른 말이다 — DreamHack EZ_command_injection 풀이
blog

[🥉 Bronze 1] "IP인지 검증했다"와 "안전하다"는 다른 말이다 — DreamHack EZ_command_injection 풀이

ping을 대신 실행해주는 웹 서비스. 서버는 사용자가 넣은 값을 파이썬 ipaddress.ip_address()로 "진짜 IP 주소인지" 검증한 다음에야 쉘 명령에 끼워 넣는다 — 얼핏 안전해 보이는 방어다. 그런데 IPv6 주소 뒤에 붙는 %인터페이스이름(scope id) 부분은 그 내용을 전혀 검증하지 않고 그대로 보존한다는 사실을 알게 되면서, "유효한 IP 주소"이면서 동시에 완전한 쉘 인젝션 페이로드인 문자열을 만들 수 있었다. 유일한 걸림돌은 절대경로에 들어가는 슬래시 하나 — CIDR 표기법과 헷갈려 파싱이 거부됐다.
#dreamhack#ctf#web+4
 
BLOG2026-07-21
[🥉 Bronze 4] 화면의 "10초 뒤 공개"는 클라이언트만의 약속이었다 — DreamHack Test Your Luck 풀이
blog

[🥉 Bronze 4] 화면의 "10초 뒤 공개"는 클라이언트만의 약속이었다 — DreamHack Test Your Luck 풀이

0부터 10000 사이의 숫자를 맞히면 flag를 준다는 게임이다. 정답은 서버 프로세스가 뜰 때 딱 한 번 뽑혀 전역변수에 박히고, 그 뒤로는 절대 바뀌지 않는다. 화면에는 "결과는 10초 뒤에 공개됩니다"라고 적혀 있지만, 그 10초는 서버가 강제하는 규칙이 아니라 브라우저 자바스크립트의 setTimeout 한 줄일 뿐이다. 직접 요청을 찔러보면 서버는 밀리초 단위로 즉시 대답한다 — 그러면 10001가지 뿐인 정답을 그냥 처음부터 끝까지 다 물어보면 된다.
#dreamhack#ctf#web+5
 
BLOG2026-07-21
[🥇 Gold 4] "/index.php/"조차 무해한 코드가 됐다 — DreamHack Relative Path Overwrite Advanced 풀이
blog

[🥇 Gold 4] "/index.php/"조차 무해한 코드가 됐다 — DreamHack Relative Path Overwrite Advanced 풀이

include() 대상 파일명 필터는 완벽해 보였다 — "..", ":", "/"를 전부 막으니 직접적인 LFI는 불가능하다. 그런데 그 필터는 GET 파라미터 하나만 검사할 뿐, 페이지가 상대경로로 불러오는 스크립트 파일까지는 신경 쓰지 않았다. Apache의 rewrite 규칙 하나가 확장자를 슬쩍 빠뜨리는 버그를 갖고 있었고, 그 덕에 스크립트 요청은 항상 404로 떨어져 우리가 요청한 URL을 그대로 반사하는 페이지에 도달한다. 문제는 반사된 텍스트 맨 앞에 우리가 손댈 수 없는 "/index.php/"가 항상 붙는다는 것 — 그런데 JavaScript 문법에서는 이 문자열이 우연히도 "아무 일도 하지 않는 정규식 리터럴"로 파싱된다는 걸 발견하면서 퍼즐이 풀렸다.
#dreamhack#ctf#web+5
 
BLOG2026-07-20
[🥈 Silver 2] 검사한 곳과 실제로 간 곳이 다를 수 있다 — DreamHack crawling 풀이
blog

[🥈 Silver 2] 검사한 곳과 실제로 간 곳이 다를 수 있다 — DreamHack crawling 풀이

URL을 넣으면 그 페이지를 대신 가져와 보여주는 미니 크롤러다. 127.0.0.1 같은 내부 주소를 직접 넣으면 "전역 IP인지" 검사하는 필터가 바로 막는다. 그런데 이 필터는 딱 하나만 확인한다 — 우리가 처음 준 URL의 호스트가 가리키는 IP. requests 라이브러리는 기본적으로 HTTP 리다이렉트를 자동으로 따라간다는 사실은 전혀 고려되지 않았다. 공인 IP를 가진 내 서버가 302로 127.0.0.1의 관리자 페이지를 가리키게만 하면, 검사는 내 공인 서버를 보고 통과시키고 실제 응답은 내부 페이지에서 그대로 새어나온다.
#dreamhack#ctf#web+4
 
BLOG2026-07-20
[🥉 Bronze 3] 이스케이프한 건 원문이었지, 해시가 아니었다 — DreamHack [wargame.kr] md5 password 풀이
blog

[🥉 Bronze 3] 이스케이프한 건 원문이었지, 해시가 아니었다 — DreamHack [wargame.kr] md5 password 풀이

mysqli_real_escape_string()이 사용자 입력을 꼼꼼히 이스케이프한다. 그런데 정작 쿼리에 들어가는 건 그 입력이 아니라 md5($ps, true) — raw 16바이트 바이너리 해시다. 해시 결과는 이스케이프 대상이 아니었다. MD5 출력은 사실상 무작위 바이트열이라, 그 바이트들이 우연히 SQL 문법으로 읽히는 문자열을 만들어낼 수 있다. 임의 문자로 시작하는 해시는 1/256 확률로 몇 초 안에 찾을 수 있지만, 실제로 익스플로잇이 되려면 5바이트가 정확히 맞아떨어져야 해서 무작위 탐색은 비현실적이다 — 대신 잘 알려진 매직 스트링을 직접 계산으로 검증하고 그대로 썼다.
#dreamhack#wargame.kr#ctf+5
 
BLOG2026-07-20
[🥇 Gold 4] 텍스트는 못 읽어도 속성은 읽힌다 — DreamHack CSS Injection 풀이
blog

[🥇 Gold 4] 텍스트는 못 읽어도 속성은 읽힌다 — DreamHack CSS Injection 풀이

모든 페이지의 <style> 블록에 쿼리스트링 하나가 이스케이프 없이 그대로 꽂힌다. 플래그는 관리자 메모 텍스트 안에 있는데, 처음 세운 계획 — @font-face의 unicode-range로 글자 존재 여부를 외부 요청으로 알아내고 text-indent로 위치를 잘라내는 방법 — 은 실제 브라우저로 검증해보니 완전히 틀렸다. 화면에 안 보이게 잘라낸 글자의 폰트도 브라우저는 똑같이 요청해버린다. 결국 플래그 텍스트가 아니라 마이페이지의 API 토큰이 <input value="..."> 속성으로 노출된다는 걸 발견하고서야 고전적인 속성 선택자 유출로 방향을 틀었다. 관리자 봇을 8번만 왕복시켜 8자리 토큰을 통째로 복원했다.
#dreamhack#ctf#web+4
 
BLOG2026-07-20
[🥈 Silver 2] 연결은 미리 다 맺어두고 마지막 1바이트만 아꼈다 — DreamHack chocoshop 레이스 컨디션 풀이
blog

[🥈 Silver 2] 연결은 미리 다 맺어두고 마지막 1바이트만 아꼈다 — DreamHack chocoshop 레이스 컨디션 풀이

쿠폰 하나로 1000원, 플래그는 2000원. 세션당 쿠폰은 딱 한 번만 발급된다는 제약이 있었지만, "이미 받았는지 확인"과 "받았다고 기록"하는 두 Redis 명령 사이에는 짧은 틈이 있었다. requests + ThreadPoolExecutor로 30개를 동시에 쏴봤지만 결과는 1장뿐이었다 — 스레드마다 새로 여는 connect() 오버헤드가 도착 시각을 흩어놓은 탓이었다. 소켓을 전부 미리 열어두고, 헤더의 마지막 1바이트만 아껴뒀다가 한 번에 흘려보내는 single-packet 기법까지 가서야 100건 중 90건 넘는 쿠폰을 한 번에 받아냈다.
#dreamhack#ctf#web+5
 
BLOG2026-07-20
[🥉 Bronze 4] 버튼을 쫓아갈 필요가 없었다 — DreamHack Click me! 풀이
blog

[🥉 Bronze 4] 버튼을 쫓아갈 필요가 없었다 — DreamHack Click me! 풀이

마우스가 가까이 가면 도망가는 버튼. "Catch me if you can!"이라는 문제 설명 그대로, 겉보기엔 반응속도와 클릭 타이밍을 겨루는 미니게임 같다. 그런데 버튼의 클릭 이벤트가 하는 일이라곤 특정 URL로 이동하는 것뿐이고, 그 URL에 서버가 어떤 조건도 검증하지 않는다는 걸 소스에서 확인한 순간, 버튼을 쫓아다닐 이유 자체가 사라졌다.
#dreamhack#ctf#web+2
 
BLOG2026-07-20
[🥈 Silver 3] 20,664줄 로그 속의 침입 — DreamHack weblog-1 풀이
blog

[🥈 Silver 3] 20,664줄 로그 속의 침입 — DreamHack weblog-1 풀이

실행 중인 서버를 직접 공격하는 문제가 아니다. 이미 침해당한 게시판의 Apache 접속 로그 2만 줄과 소스 코드가 통째로 주어지고, 라이브 퀴즈 앱이 그 침해 과정을 단계별로 캐묻는다. blind SQLi로 로그를 파싱해 탈취된 자격증명을 재구성하고, 세션 파일 include로 이어진 RCE, 그리고 날짜를 키로 스스로를 복호화하는 웹쉘 드로퍼까지 — 로그 자체가 유일한 증거이자 유일한 공격 도구였다.
#dreamhack#ctf#web+4
 
BLOG2026-07-20
[🥇 Gold 4] 범인은 서버가 아니라 curl이었다 — DreamHack Flask-Dev 풀이
blog

[🥇 Gold 4] 범인은 서버가 아니라 curl이었다 — DreamHack Flask-Dev 풀이

16줄짜리 Flask 앱. 임의 파일을 읽어주는 라우트가 있고, debug=True로 Werkzeug 인터랙티브 콘솔까지 열려 있다. 그런데 정작 "../"로 상위 디렉터리를 탈출하려 하면 매번 서버가 파일명을 그대로 못 찾는다. 범인은 서버의 방어 로직이 아니라, 요청을 보내는 curl 자신이 URL을 미리 손질해 보내고 있었다는 사실이었다.
#dreamhack#ctf#web+4
 
BLOG2026-07-20
[🥉 Bronze 2] true는 모든 문자열과 같다 — DreamHack [wargame.kr] type confusion 풀이
blog

[🥉 Bronze 2] true는 모든 문자열과 같다 — DreamHack [wargame.kr] type confusion 풀이

"Simple Compare Challenge. hint? you can see the title of this challenge. :D" 소스도 없이 딱 이 한 줄이 힌트로 주어진다. view-source로 드러난 PHP 코드는 서버만 아는 랜덤 문자열과 사용자 입력을 == 로 비교하는 게 전부다. 값을 맞히는 문제가 아니라, PHP가 서로 다른 타입을 비교할 때 값 대신 타입으로 승부가 갈린다는 사실을 이용하는 문제였다.
#dreamhack#ctf#web+3
 
BLOG2026-07-20
[🥈 Silver 1] 필터를 뚫지 않고 굶겨 죽였다 — DreamHack Relative Path Overwrite 풀이
blog

[🥈 Silver 1] 필터를 뚫지 않고 굶겨 죽였다 — DreamHack Relative Path Overwrite 풀이

XSS 필터가 걸린 페이지를 만나면 반사적으로 필터를 통과하는 페이로드부터 찾게 된다. 이 문제는 그 방향이 아니었다 — 필터 자체를 구현한 스크립트 파일을 아예 로드되지 못하게 만들면, 필터 로직은 실행조차 되지 않는다. PHP의 상대경로 include와 브라우저의 상대경로 script src가 서로 다른 기준점을 가진다는 점을 이용해, 필터 스크립트를 페이지 자기 자신으로 뒤바꿔치기했다.
#dreamhack#ctf#web+3
 
BLOG2026-07-20
[🥉 Bronze 1] PHP7 타입 저글링 두 방으로 관리자 로그인 — DreamHack easy-login 풀이
blog

[🥉 Bronze 1] PHP7 타입 저글링 두 방으로 관리자 로그인 — DreamHack easy-login 풀이

관리자 비밀번호와 OTP는 매 요청 서버에서 무작위로 재생성돼 브루트포스가 불가능하다. 대신 검증 로직 두 곳이 PHP7 특유의 느슨한 타입 비교에 기대고 있다. otp에 정수 0을 넣으면 "P123456" 같은 비숫자 문자열과 0이 같다고 판정되고, pw에 빈 배열을 넣으면 strcmp()가 타입 에러로 NULL을 반환해 통과된다. login.php를 거치지 않고 index.php에 직접 JSON을 POST해 타입을 완전히 통제했다.
#dreamhack#ctf#web+3
 
BLOG2026-07-13
[🥈 Silver 1] base 태그로 nonce 스크립트를 통째로 훔치다 — DreamHack CSP Bypass Advanced 풀이
blog

[🥈 Silver 1] base 태그로 nonce 스크립트를 통째로 훔치다 — DreamHack CSP Bypass Advanced 풀이

script-src 'self' 'nonce-…' 에 반영 XSS. 앞선 CSP Bypass는 /vuln이 입력을 raw로 돌려줘 자기-스크립트-가젯으로 풀렸지만, Advanced는 /vuln을 HTML로 감싸 그 길을 막았다. CSP에 base-uri가 없다는 틈을 파고들어, base 태그로 페이지의 정품 nonce 스크립트를 공격자 서버로 재해석시켜 임의 JS를 실행했다.
#dreamhack#ctf#web+5
 
BLOG2026-07-12
[🥉 Bronze 1] 게임을 하지 않고 목표 점수를 제출한다 — DreamHack [wargame.kr] fly me to the moon 풀이
blog

[🥉 Bronze 1] 게임을 하지 않고 목표 점수를 제출한다 — DreamHack [wargame.kr] fly me to the moon 풀이

Tunnel 이라는 자바스크립트 게임의 점수가 브라우저에서 계산돼 서버로 그대로 POST 된다. anti-cheat 토큰은 세션당 발급될 뿐 점수가 진짜 플레이로 나왔는지 검증하지 않는다. 게임을 할 필요 없이 목표 점수 31337 을 직접 제출하면 플래그가 나온다. 클라이언트를 믿으면 안 된다는 걸 보여주는 문제.
#dreamhack#ctf#web+4
 
BLOG2026-07-12
[🥈 Silver 1] nonce를 몰라도 XSS가 터진다 — DreamHack DOM XSS 풀이
blog

[🥈 Silver 1] nonce를 몰라도 XSS가 터진다 — DreamHack DOM XSS 풀이

CSP가 script-src에 nonce와 strict-dynamic을 걸어 인라인 스크립트도 onerror도 다 막는다. nonce는 매 응답마다 회전해서 값을 훔칠 수도 없다. 하지만 원시 HTML 주입점이 nonce가 붙은 jquery 스크립트보다 앞에 있어서, base 태그로 그 스크립트의 로드 origin을 내 서버로 돌려버리면 nonce 값을 몰라도 이미 붙은 nonce를 재활용해 내 JS가 실행된다. document.cookie를 동일출처 /memo에 적재해 회수했다.
#dreamhack#ctf#web+6
 
BLOG2026-07-08
[🥈 Silver 4] SQL 컬럼 트렁케이션으로 admin 계정 탈취 — DreamHack tmitter 풀이
blog

[🥈 Silver 4] SQL 컬럼 트렁케이션으로 admin 계정 탈취 — DreamHack tmitter 풀이

트위터 클론 tmitter 는 admin 으로 로그인해야 플래그가 나온다. 회원가입 중복 체크는 '정확 일치'만 막지만, id 컬럼 varchar(32) 를 넘는 "admin"+공백+"1" 을 넣으면 INSERT 때 뒤가 잘려 "admin"+공백으로 저장된다. MySQL 은 PADSPACE 콜레이션이라 끝 공백을 무시해 비교하므로 그 계정이 곧 admin 이 되어 로그인·플래그 획득. 로컬 MariaDB 로 메커니즘을 재현하고, 라이브에서 컬럼 길이까지 프로빙해 확정했다.
#dreamhack#ctf#web+6
 
BLOG2026-07-07
[🥈 Silver 2] nonce는 뚫지 않고, 유출은 fetch를 안 쓴다 — DreamHack CSP Bypass 풀이
blog

[🥈 Silver 2] nonce는 뚫지 않고, 유출은 fetch를 안 쓴다 — DreamHack CSP Bypass 풀이

script-src 'self' 'nonce-X'가 걸린 반사형 XSS. 인라인 스크립트는 nonce가 없어 콘솔에서 바로 막히지만, /vuln 자신이 입력을 그대로 돌려주는 걸 이용해 <script src="/vuln?param=...">로 스스로를 "동일 출처 스크립트"로 위장시키면 nonce 없이 실행된다. 유출도 fetch/img가 아니라 top-level navigation으로 — CSP가 애초에 막지 않는 경로다. 목적지는 외부 서버가 아니라 같은 앱의 전역 메모장.
#dreamhack#ctf#web+5
 
BLOG2026-07-07
[🥉 Bronze 2] 비밀번호 대신 배열 하나 — DreamHack [wargame.kr] strcmp 풀이
blog

[🥉 Bronze 2] 비밀번호 대신 배열 하나 — DreamHack [wargame.kr] strcmp 풀이

매 요청마다 rand()로 다시 뽑는 비밀번호를 strcmp()로 비교한다. sleep(1)까지 붙여 브루트포스를 차단했지만, 애초에 값을 맞힐 필요가 없다. PHP의 strcmp()에 배열을 넘기면 TypeError 대신 NULL을 반환하고, NULL == 0은 PHP 느슨한 비교에서 참이 된다. password 대신 password[]를 보내는 것만으로 인증을 통째로 우회한다.
#dreamhack#ctf#web+5
 
BLOG2026-07-07
[🥉 Bronze 3] 확장자 블랙리스트가 빠뜨린 파일명 — DreamHack Apache htaccess 풀이
blog

[🥉 Bronze 3] 확장자 블랙리스트가 빠뜨린 파일명 — DreamHack Apache htaccess 풀이

업로드 필터는 php/php3/php4/php5/pht/phtml 확장자만 막는다. .htaccess라는 "확장자 없는" 파일명은 블랙리스트에 걸리지 않고, AllowOverride All인 서버는 업로드 디렉토리에 떨어진 그 .htaccess를 그대로 적용한다. AddType 한 줄로 .jpg를 PHP로 만들어 웹쉘을 올리고, 실행 전용 flag 바이너리를 그 웹쉘로 실행시켜 flag를 받는다.
#dreamhack#ctf#web+5
 
BLOG2026-07-06
[🥉 Bronze 2] 대문자 GUEST로 guest 필터 피하기 — DreamHack [wargame.kr] login filtering 풀이
blog

[🥉 Bronze 2] 대문자 GUEST로 guest 필터 피하기 — DreamHack [wargame.kr] login filtering 풀이

guest/blueh4g 계정은 PHP 문자열 비교로 블랙리스트에 걸려 있다. mysqli_real_escape_string이 따옴표를 다 막아 SQL Injection은 안 되지만, MySQL 기본 콜레이션은 문자열을 대소문자 구분 없이 비교한다. id=GUEST(대문자)로 보내면 DB 조회는 guest 행 그대로 매치하면서, PHP의 대소문자 구분 비교는 통과해 블랙리스트를 피해간다.
#dreamhack#ctf#web+5
 
BLOG2026-07-05
[🥈 Silver 3] 공백도 OR도 admin도 못 쓰는데 — 블랙리스트 WAF 뚫은 블라인드 SQLi — DreamHack sql injection bypass WAF 풀이
blog

[🥈 Silver 3] 공백도 OR도 admin도 못 쓰는데 — 블랙리스트 WAF 뚫은 블라인드 SQLi — DreamHack sql injection bypass WAF 풀이

union/select/from/and/or/admin/공백/*// 를 부분 문자열로 막는 블랙리스트 WAF 앞에서, MySQL의 ||/&&와 공백 없는 # 주석으로 문법 자체를 바꿔 우회한 기록이다. 화면엔 uid 컬럼만 반사되는 구조라 admin의 비밀번호(FLAG)는 boolean-blind로 한 글자씩 꺼내야 했다.
#dreamhack#ctf#web+4
 
BLOG2026-07-01
[🥈 Silver 3] 응답엔 결과가 없어도 에러엔 다 있다 — DreamHack error based sql injection 풀이
blog

[🥈 Silver 3] 응답엔 결과가 없어도 에러엔 다 있다 — DreamHack error based sql injection 풀이

uid 값을 그대로 문자열 포매팅해 쿼리를 만드는 Flask + MariaDB 앱. 성공 응답은 항상 같은 쿼리 템플릿만 echo해 UNION이나 boolean 기반으로는 아무것도 구분할 수 없지만, 예외를 str(e)로 그대로 돌려주는 탓에 extractvalue()의 XPATH syntax error에 서브쿼리 결과를 실어 admin 비밀번호(플래그)를 통째로 읽어낸다.
#dreamhack#ctf#web+4
 
BLOG2026-07-01
[🥉 Bronze 1] flag는 못 읽어도 소스는 새어나간다 — DreamHack php-1 풀이
blog

[🥉 Bronze 1] flag는 못 읽어도 소스는 새어나간다 — DreamHack php-1 풀이

PHP Back Office. view.php 는 파일 파라미터에 "flag" 문자열과 콜론을 막아 php://filter 우회까지 봉쇄했지만, 정작 include 를 직접 호출하는 index.php 의 page 파라미터엔 같은 필터가 없다. 확장자 자동 첨부를 거꾸로 이용해 php://filter/convert.base64-encode 로 flag.php 소스를 실행 대신 텍스트로 읽어낸다.
#dreamhack#ctf#web+4
 
BLOG2026-06-30
[🥉 Bronze 3] raw socket 으로 만든 SSRF — localhost 전용 /admin 우회 — DreamHack proxy-1 풀이
blog

[🥉 Bronze 3] raw socket 으로 만든 SSRF — localhost 전용 /admin 우회 — DreamHack proxy-1 풀이

임의 host:port 로 raw 바이트를 보내고 응답을 돌려주는 /socket 이 그대로 SSRF 발판이 된다. flag 를 주는 /admin 은 remote_addr 가 127.0.0.1 일 때만 동작하는데, /socket 으로 서버 자신의 8000 포트에 생 HTTP 요청을 흘려보내면 이 검사를 통과한다. 요청에 필요한 헤더·쿠키·폼까지 직접 적어 보내 flag 를 회수했다.
#dreamhack#ctf#web+5
 
BLOG2026-06-26
[🥉 Bronze 4] 한 값이 74보다 작고 동시에 크다 — DreamHack php7cmp4re 풀이
blog

[🥉 Bronze 4] 한 값이 74보다 작고 동시에 크다 — DreamHack php7cmp4re 풀이

PHP 7의 비교 연산자는 피연산자 타입에 따라 규칙이 바뀐다. 정수와 비교하면 문자열을 숫자로 캐스팅하고, 문자열끼리 비교하면 바이트 단위 strcmp를 쓴다. 이 비대칭을 이용하면 "7g" 한 값이 74보다 작으면서 동시에 "74"보다 크게 만들 수 있다. input1도 같은 원리로 0x39와 0x41 사이 문자를 끼워 통과시킨다.
#dreamhack#ctf#web+4
 
BLOG2026-06-26
[🥉 Bronze 2] 대소문자 한 끗으로 뚫는 XSS 필터 — DreamHack XSS Filtering Bypass 풀이
blog

[🥉 Bronze 2] 대소문자 한 끗으로 뚫는 XSS 필터 — DreamHack XSS Filtering Bypass 풀이

script·on·javascript: 를 지우는 필터가 있는데도, 검사는 소문자로 하고 치환은 원본 그대로 한다는 한 줄 차이 때문에 <sCrIpt> 가 그대로 살아남는다. 봇이 들고 방문하는 flag 쿠키를 동기 XHR로 /memo에 흘려 회수한 과정을 정리했다. location.href 비동기 전송이 driver.quit 레이스에 걸려 실패했던 삽질까지 담았다.
#dreamhack#ctf#web+6
 
BLOG2026-06-24
[🌱 Sprout] GET·POST 요청 체이닝 — 리다이렉트 따라가며 토큰 이어 붙이기 — DreamHack simple-web-request 풀이
blog

[🌱 Sprout] GET·POST 요청 체이닝 — 리다이렉트 따라가며 토큰 이어 붙이기 — DreamHack simple-web-request 풀이

STEP1 → STEP2 → FLAG 세 단계를 정해진 파라미터로 차례로 호출해야 플래그가 나오는 요청 입문 문제다. 각 단계는 앞 단계가 발급한 난수 토큰을 들고 와야 통과하고, 그 토큰은 한 요청 흐름 안에서만 유효하다. 브라우저로 한 번, 그리고 requests.Session 으로 리다이렉트를 자동 추적하며 hidden 토큰을 이어 붙여 플래그를 받았다.
#dreamhack#ctf#web+5
 
BLOG2026-06-24
[🥈 Silver 2] SSRF로 내부 포트 스캔 — netloc 문자열 필터를 0.0.0.0으로 우회 — DreamHack web-ssrf 풀이
blog

[🥈 Silver 2] SSRF로 내부 포트 스캔 — netloc 문자열 필터를 0.0.0.0으로 우회 — DreamHack web-ssrf 풀이

이미지 뷰어가 입력한 URL을 서버가 대신 가져와 보여준다. 같은 프로세스 안에 flag.txt를 서빙하는 내부 SimpleHTTPRequestHandler가 1500~1800 중 한 포트에 떠 있다. 필터는 netloc에 `127.0.0.1`/`localhost` 문자열이 있는지만 보므로, 같은 loopback을 가리키는 `0.0.0.0`으로 우회하고 포트를 전수 스캔해 flag.txt를 읽었다.
#dreamhack#ctf#web+5
 
BLOG2026-06-24
[🌱 새싹] 화면에 흩날리는 글자를 슬라이스로 되맞추기 — DreamHack Flying Chars 풀이
blog

[🌱 새싹] 화면에 흩날리는 글자를 슬라이스로 되맞추기 — DreamHack Flying Chars 풀이

플래그 한 줄을 20조각 이미지로 잘라 10×10px 로 줄인 뒤 제각각 속도로 화면에 흘려보내는 문제다. 눈으로는 못 읽지만 조각은 그대로 서버에 있다. 파일명 숫자순으로 맞추면 깨진 문장이 나오고, HTML 의 img_files 배열(브라우저가 화면에 붙이는 순서) 그대로 이어 붙이면 플래그가 복원된다. 클라이언트에서 가리는 건 숨기는 게 아니라는 전형적인 예제.
#dreamhack#ctf#web+3
 
BLOG2026-06-22
[🌱 새싹] 호박을 만 번 깎으면 떨어지는 플래그 — DreamHack Carve Party 풀이
blog

[🌱 새싹] 호박을 만 번 깎으면 떨어지는 플래그 — DreamHack Carve Party 풀이

서버도 백엔드도 없는 HTML 한 장짜리 문제. 호박을 10000번 클릭하면 canvas에 플래그가 그려지는데, 그 플래그 바이트는 클릭 100번마다 한 번씩 XOR 변환을 거친다. 키로 쓰이는 pie 변수가 함수 밖 전역이라 배치 사이에 값이 이어진다는 점만 잡으면, 손으로 만 번 누르는 대신 100배치를 그대로 시뮬레이션해서 플래그를 꺼낼 수 있다.
#dreamhack#ctf#web+4
 
BLOG2026-06-21
[🥉 Bronze 3] img 한 줄로 봇을 시켜 admin 비밀번호를 바꾸기 — DreamHack csrf-2 풀이
blog

[🥉 Bronze 3] img 한 줄로 봇을 시켜 admin 비밀번호를 바꾸기 — DreamHack csrf-2 풀이

admin의 비밀번호가 곧 FLAG인데, 직접 알아낼 방법은 없다. 대신 127.0.0.1에서 admin 세션으로 도는 봇에게 /change_password를 대신 호출하게 만들면 admin 비번을 내가 정한 값으로 덮어쓸 수 있다. script·frame·on을 막는 필터는 img 태그 한 줄로 통과하고, 바뀐 비번으로 admin 로그인해 flag를 회수한 과정을 정리했다.
#dreamhack#ctf#web+6
 
BLOG2026-06-20
[🥉 Bronze 1] img 한 줄로 봇을 빌려 내부 전용 /admin 호출하기 — DreamHack csrf-1 풀이
blog

[🥉 Bronze 1] img 한 줄로 봇을 빌려 내부 전용 /admin 호출하기 — DreamHack csrf-1 풀이

외부에서 직접 부르면 Access Denied인 /admin/notice_flag를, 127.0.0.1에서 도는 봇의 브라우저로 대신 호출하게 만드는 전형적인 CSRF 문제. script·frame·on을 막는 XSS 필터는 img 태그 한 줄이면 그냥 통과하고, 봇이 그 이미지를 로드하며 flag를 memo에 적어준다. 그 memo를 읽어 플래그를 회수한 과정을 정리했다.
#dreamhack#ctf#web+5
 
BLOG2026-06-06
[🥇 Gold 1] 깨진 정규식 한 줄이 XSS를 연다 — DreamHack Fast XSS 풀이
blog

[🥇 Gold 1] 깨진 정규식 한 줄이 XSS를 연다 — DreamHack Fast XSS 풀이

FastAPI의 TemplateResponse에 임의 kwarg를 주입해 응답 헤더를 손에 넣는다. 템플릿은 autoescape라 body로는 막히지만, uvicorn의 헤더 "이름" 검증 정규식이 문자클래스 조기 종료 버그로 깨져 있어 헤더 이름에 CRLF를 넣을 수 있었다. 응답 분리로 web 오리진에 <script>를 심어 어드민 봇의 FLAG 쿠키를 탈취했다. Theori OSR CTF 출제작.
#dreamhack#ctf#web+5
 
BLOG2026-06-06
[🥈 Silver 3] 비밀번호 자리에 객체를 넣다 — DreamHack CProxy: Inject 풀이
blog

[🥈 Silver 3] 비밀번호 자리에 객체를 넣다 — DreamHack CProxy: Inject 풀이

쿼리는 ? 플레이스홀더로 안전하게 짰는데도 로그인이 뚫린다. express의 qs 바디 파서가 pw를 객체로 만들고, node-mysql이 그 객체를 `pw` = '1' 로 풀어쓰면서 비밀번호 검사가 항상 참이 됐다. admin_inject 계정을 탈취해 저장된 프록시 응답에서 플래그를 꺼냈다. 삽질과 캡쳐를 곁들인 풀이 노트.
#dreamhack#ctf#web+4
 
BLOG2026-06-05
[🥇 Gold 3] CSP nonce를 예측해서 박아 넣다 — DreamHack Dream Lectures 풀이
blog

[🥇 Gold 3] CSP nonce를 예측해서 박아 넣다 — DreamHack Dream Lectures 풀이

강의 신청 사이트의 reason 필드가 autoescape false로 그대로 렌더된다. 진짜 벽은 CSP nonce인데, 이 nonce가 16비트 LFSR로 생성되고 매 응답 헤더에 평문으로 샌다. 한 번 새어 나온 nonce로 LFSR을 복원해 다음 값을 예측하고, apply 한 요청 안에서 봇이 페이지를 읽는 타이밍에 맞춰 script nonce를 박아 CSP를 통과시킨 뒤 navigation으로 플래그를 빼냈다.
#dreamhack#ctf#web+6
 
BLOG2026-05-29
[💎 Diamond 4] 난독화된 안드로이드 DRM을 통째로 빌려 쓰기 — DreamHack Dream DRM 풀이
blog

[💎 Diamond 4] 난독화된 안드로이드 DRM을 통째로 빌려 쓰기 — DreamHack Dream DRM 풀이

request_book의 id 파라미터 SQL Injection으로 서버의 임의 파일(/flag)을 암호문 상태로 읽어내고, 패킹된 native DRM 라이브러리를 NDK 하니스 + dlopen + 가짜 JNIEnv로 직접 호출해 복호화 오라클로 삼는 과정을 정리한다. 오프라인 크래킹·APK 재서명·MITM 등 막다른 길까지 전부 기록한 Theori 채용 CTF 다이아 4 풀이.
#dreamhack#ctf#web+7
 
BLOG2026-05-29
[💎 Diamond 4] jsdom ≠ Chrome 파서 차이로 터지는 mutation XSS — DreamHack web-dreamshop 풀이
blog

[💎 Diamond 4] jsdom ≠ Chrome 파서 차이로 터지는 mutation XSS — DreamHack web-dreamshop 풀이

프론트엔드 번들에 jsdom을 끼워 DOMPurify로 sanitize하는 순간, "검사한 DOM"과 "렌더하는 DOM"이 달라져 mutation XSS가 열린다. vue-router의 %2F 디코드와 axios protocol-relative 처리로 admin 봇의 요청 호스트를 탈취하고, 그 위에 mXSS를 얹어 admin 권한으로 FLAG를 "구매"하는 DreamHack 다이아4 web 문제 풀이.
#dreamhack#ctf#web+7
 
BLOG2026-04-01
[⭐⭐⭐] TAB 하나로 필터 열 개를 뚫다 — DreamHack XSS Filtering Bypass Advanced 풀이
blog

[⭐⭐⭐] TAB 하나로 필터 열 개를 뚫다 — DreamHack XSS Filtering Bypass Advanced 풀이

script, javascript, document, location, (, )까지 10개 이상의 키워드를 차단하는 XSS 필터. 그런데 javas\tcript처럼 TAB 문자(\t)를 키워드 중간에 끼우면 필터는 통과하고 브라우저는 그대로 실행한다. WHATWG URL 스펙이 정의한 정규화 동작과 Python 문자열 필터 사이의 갭을 파고들어 쿠키를 탈취하고 FLAG를 획득하는 과정을 단계별로 정리한다.
#dreamhack#ctf#web+4
 
RESEARCH2026-08-08
React2Shell (CVE-2025-55182): RSC Flight 역직렬화 기반 Pre-Auth RCE 실험 분석
blog

React2Shell (CVE-2025-55182): RSC Flight 역직렬화 기반 Pre-Auth RCE 실험 분석

React Server Components의 Flight 프로토콜에서 역직렬화 결함이 서버 측 원격 코드 실행으로 이어지는 전체 과정을, 정상 흐름과의 비교·payload 구조 분석·RCE 실증·공격 확장 경로까지 실험 기반으로 추적한다. 취약 랩을 직접 세워 인증 없이 원격 셸을 획득하는 공격자 관점 재현과 패치 전후 대조까지 실제 캡처로 담았다.
#security#react#rce+5