ZINO
HomeResearchBlogTagsPlaygroundStackPortfolio
⌘K

레드팀 · AI 자동화 · 시스템 엔지니어링

직접 만들고, 실험하고, 기록한다

개인정보처리방침·이용약관

© 2026 ZINO LAB

← 태그 목록

Tag Archive

#web

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

114
posts
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
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
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-19
[🥉 Bronze 4] 똑같아 보이는 이미지 100장 속 진짜 하나 — DreamHack Find Real One 풀이
blog

[🥉 Bronze 4] 똑같아 보이는 이미지 100장 속 진짜 하나 — DreamHack Find Real One 풀이

문제 설명이 화면에 똑같이 생긴 썸네일 100개로 도배돼 있다. 파일도 서버도 없고, 눈으로는 100개가 전부 같아 보인다. 각 이미지의 클릭 대상(href)을 다수결로 비교하면 99개는 "This is fine" 밈으로, 1개만 실제 flag 이미지로 연결된다는 걸 알 수 있다.
#dreamhack#ctf#web+3
BLOG2026-07-13
[🥉 Bronze 2] 차단은 대소문자를 가리고 통과는 안 가린다 — DreamHack baby-Case 풀이
blog

[🥉 Bronze 2] 차단은 대소문자를 가리고 통과는 안 가린다 — DreamHack baby-Case 풀이

nginx가 /shop을 정확히 막고 앱은 leg=='FLAG'를 막지만, nginx의 경로 매칭은 대소문자를 구분하고 Express 라우팅과 앱의 검색은 대소문자를 무시한다. 이 어긋남을 겹쳐, /Shop에 leg=flag를 보내 두 차단을 동시에 통과해 FLAG를 꺼냈다.
#dreamhack#ctf#web+5
 
BLOG2026-07-13
[🥉 Bronze 4] CSP를 뚫지 않고, 출력을 넘쳐 무너뜨리다 — DreamHack Broken Buffalo Wings 풀이
blog

[🥉 Bronze 4] CSP를 뚫지 않고, 출력을 넘쳐 무너뜨리다 — DreamHack Broken Buffalo Wings 풀이

반영 XSS에 CSP가 걸려 있지만, CSP를 설정하는 header()가 본문을 echo한 "뒤"에 있다. 재귀 str_replace가 반복마다 출력을 뱉는 걸 이용해 PHP output_buffering을 넘기면, 헤더가 먼저 나가버려 CSP header()가 실패한다. CSP가 사라진 페이지에서 XSS로 쿠키를 유출했다.
#dreamhack#ctf#web+5
BLOG2026-07-13
[🥈 Silver 1] Redis가 밥 먹듯 남기는 RDB로 웹셸을 굽다 — DreamHack phpMyRedis 풀이
blog

[🥈 Silver 1] Redis가 밥 먹듯 남기는 RDB로 웹셸을 굽다 — DreamHack phpMyRedis 풀이

Redis eval로 Lua를 돌리고 config로 CONFIG를 만질 수 있는 PHP 앱이다. Lua에서 SAVE는 막혀 있지만, CONFIG로 RDB 저장 위치를 웹 루트로 돌리고 save 정책을 "1초 1변경"으로 바꿔 자동 저장을 유도했다. RDB 덤프에 심은 <?php 웹셸로 RCE, 실행 전용 /flag를 돌려 flag를 얻었다.
#dreamhack#ctf#web+5
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-13
[🥉 Bronze 3] 메뉴판이 곧 정답 — DreamHack amocafe 풀이
blog

[🥉 Bronze 3] 메뉴판이 곧 정답 — DreamHack amocafe 풀이

카페 사이트가 좋아하는 메뉴라며 이상한 문자열을 보여주고, 그 "메뉴 번호"를 맞히면 flag를 준다. 알고 보면 그 문자열은 flag에서 잘라낸 19자리 숫자(org)의 16진수를 살짝 가린 것이다. nibble a는 "10", b는 "_"로만 바뀌어 있어 그대로 되짚으면 풀린다.
#dreamhack#ctf#web+4
 
BLOG2026-07-13
[🥈 Silver 2] nonce를 몰라도 뚫리는 CSP — DreamHack Client Side Template Injection 풀이
blog

[🥈 Silver 2] nonce를 몰라도 뚫리는 CSP — DreamHack Client Side Template Injection 풀이

파라미터가 HTML로 그대로 반영되지만 CSP가 인라인 스크립트를 nonce로 막는다. 그런데 script-src에 unsafe-eval과 ajax.googleapis.com이 함께 열려 있어, 거기서 AngularJS를 불러 CSTI로 코드를 실행한다. 봇의 flag 쿠키를 동일출처 /memo로 흘려 회수했다.
#dreamhack#ctf#web+5
BLOG2026-07-13
[🥉 Bronze 2] 세션 쿠키가 pickle이면 그건 코드다 — DreamHack web-deserialize-python 풀이
blog

[🥉 Bronze 2] 세션 쿠키가 pickle이면 그건 코드다 — DreamHack web-deserialize-python 풀이

세션을 base64(pickle)로 주고받는 Flask 앱이다. check_session이 사용자가 준 값을 그대로 pickle.loads 한다. pickle은 역직렬화 중 객체의 __reduce__를 실행하므로, flag를 읽어 반환하는 객체를 dict에 심어 응답 화면에 flag를 그대로 띄웠다.
#dreamhack#ctf#web+5
BLOG2026-07-13
[🥈 Silver 3] level 파라미터 하나로 뚫는 SQLi — DreamHack baby-sqlite 풀이
blog

[🥈 Silver 3] level 파라미터 하나로 뚫는 SQLi — DreamHack baby-sqlite 풀이

uid·upw 는 따옴표로 감싸 막았지만 level 은 숫자 컨텍스트라 따옴표 없이 주입된다. 공백·콤마·admin·select·따옴표를 전부 거르는 블랙리스트를 /**/·char()||·VALUES 로 우회해 UNION 으로 admin 행을 위조, 로그인 없이 FLAG 를 받아낸다.
#dreamhack#ctf#web+4
BLOG2026-07-12
[🥉 Bronze 3] base64 블록을 반칸 밀어 필터를 빠져나가다 — DreamHack Base64 based 풀이
blog

[🥉 Bronze 3] base64 블록을 반칸 밀어 필터를 빠져나가다 — DreamHack Base64 based 풀이

파일명을 base64로 받아 include하는 PHP 앱인데, 인코딩된 문자열에 flag·hp·../의 base64 조각이 있으면 막는다. flag.php의 정직한 base64는 그 조각을 그대로 품는다. 앞에 ./를 붙여 3바이트 블록 정렬을 반칸 밀면 조각이 쪼개져 필터가 통째로 헛돈다.
#dreamhack#ctf#web+5
BLOG2026-07-12
[🥉 Bronze 2] 회수 가능한 입금으로 코인 값을 흔들다 — DreamHack out of money 풀이
blog

[🥉 Bronze 2] 회수 가능한 입금으로 코인 값을 흔들다 — DreamHack out of money 풀이

DHD 코인 가격이 풀 잔고 비율(dhc_balance/dhd_balance)로만 정해지는데, 그 잔고를 바꾸는 입금이 음수 value로 전액 회수된다. 싸게 사둔 DHD를 DHC 대량 입금으로 값을 9배 띄운 뒤 되팔아 공짜 차익을 만들고, 대출을 상환해도 남는 코인으로 flag를 받았다.
#dreamhack#ctf#web+5
BLOG2026-07-12
[🥉 Bronze 3] 슬래시 하나로 열리는 flag.php — DreamHack simple-phparse 풀이
blog

[🥉 Bronze 3] 슬래시 하나로 열리는 flag.php — DreamHack simple-phparse 풀이

flag.php 는 parse_url() 로 뽑은 경로를 preg_match("/flag.php/i", $path) 로 검사해 직접 접근을 막는다. 요청 경로 앞에 슬래시를 하나 더 붙이면 parse_url 이 flag.php 를 호스트로 해석하고 path 는 빈 문자열이 된다. 필터가 보는 값이 사라지므로 그대로 통과해 flag 가 노출된다.
#dreamhack#ctf#web+4
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-08
[🥉 Bronze 2] PHP 느슨한 비교로 로그인 우회 — DreamHack Type c-j 풀이
blog

[🥉 Bronze 2] PHP 느슨한 비교로 로그인 우회 — DreamHack Type c-j 풀이

check.php 는 (int)$input == $target 형태의 느슨한 비교(==)로 ID/PW 를 검사한다. PHP7 에서 정수와 문자열을 비교하면 문자열이 숫자로 변환된다. sha1("1")은 356 으로, 랜덤 ID 는 대개 0 으로 파싱되므로 input1=0000000000, input2=356aaaaa 로 로그인이 뚫려 플래그가 나온다.
#dreamhack#ctf#web+4
BLOG2026-07-07
[🥉 Bronze 3] X-Forwarded-For 헤더로 커맨드 인젝션 — DreamHack what-is-my-ip 풀이
blog

[🥉 Bronze 3] X-Forwarded-For 헤더로 커맨드 인젝션 — DreamHack what-is-my-ip 풀이

"당신의 IP를 알려주는" 단순한 Flask 앱. 그런데 IP를 구할 때 X-Forwarded-For 헤더값을 bash -c "echo {ip}" 에 그대로 넣는다. 헤더에 ;cat /flag 를 실으면 명령이 그대로 실행되어 플래그가 나온다.
#dreamhack#ctf#web+5
 
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-06
[🥈 Silver 4] 5번 제한을 100번으로 — DreamHack login-1 풀이
blog

[🥈 Silver 4] 5번 제한을 100번으로 — DreamHack login-1 풀이

회원가입은 항상 guest 등급만 만든다. /user/idx의 IDOR로 관리자 계정(Apple, level=1)을 먼저 찾아내고, 비밀번호 재설정의 2자리 백업코드는 5회 오답이면 잠기지만 그 카운트 확인과 증가 사이에 TOCTOU 레이스가 있다. 100개 값을 동시에 쏘면 잠기기 전에 정답이 섞여 나간다.
#dreamhack#ctf#web+5
 
BLOG2026-07-06
[🥉 Bronze 3] cat 대신 실행 — DreamHack File Vulnerability Advanced 풀이
blog

[🥉 Bronze 3] cat 대신 실행 — DreamHack File Vulnerability Advanced 풀이

경로검증 없는 /file 엔드포인트로 path traversal, /admin의 API_KEY는 Dockerfile 더미값이라 무의미해 보이지만 /proc/self/environ을 읽으면 런타임 실제 키가 그대로 나온다. 그 키로 연 /admin RCE로 flag를 찾아도 cat은 500만 뜬다 — flag가 ---x--x--x(실행 전용) ELF라 읽지 말고 실행해야 한다.
#dreamhack#ctf#web+5
BLOG2026-07-06
[🥉 Bronze 1] 검증식을 브라우저 대신 내가 계산하기 — DreamHack funjs 풀이
blog

[🥉 Bronze 1] 검증식을 브라우저 대신 내가 계산하기 — DreamHack funjs 풀이

input 값을 검사하는 JS가 난독화돼 있지만, 실제로 가려진 건 charCodeAt·length 같은 메서드 이름뿐이다. 정답을 만드는 두 숫자 배열과 사칙연산 사이클은 평문 그대로 배달되므로, 브라우저가 할 계산을 파이썬으로 그대로 옮겨 실행하면 그게 곧 플래그다.
#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-05
[🥈 Silver 4] 이미지 태그 하나면 충분했다 — DreamHack CSRF Advanced 풀이
blog

[🥈 Silver 4] 이미지 태그 하나면 충분했다 — DreamHack CSRF Advanced 풀이

reflected 페이지의 XSS 필터가 frame/script/on 세 단어를 '*'로 지우지만, 정작 CSRF에 필요한 건 <img src=...> 하나뿐이다. 게다가 비밀번호 변경 API의 CSRF 토큰이 md5(username+접속IP)라서, 봇이 항상 127.0.0.1로 접속한다는 사실만으로 토큰을 미리 계산해버릴 수 있었다.
#dreamhack#ctf#web+4
BLOG2026-07-04
[🥈 Silver 3] ASCII()가 거짓말을 하는 블라인드 SQLi — DreamHack blind sql injection advanced 풀이
blog

[🥈 Silver 3] ASCII()가 거짓말을 하는 블라인드 SQLi — DreamHack blind sql injection advanced 풀이

uid 파라미터가 SQL에 그대로 삽입되는 boolean blind SQLi를 자동화하다가, 한글이 섞인 플래그 때문에 ASCII() 이진탐색이 끝없이 참으로 나오는 현상에 부딪혔다. MySQL의 ASCII()와 ORD()가 멀티바이트 문자를 다루는 방식 차이를 파고들어 우회한 과정을 정리한다.
#dreamhack#ctf#web+4
BLOG2026-07-03
[🥉 Bronze 4] 따옴표조차 필요 없던 커맨드 인젝션 — DreamHack command-injection-chatgpt 풀이
blog

[🥉 Bronze 4] 따옴표조차 필요 없던 커맨드 인젝션 — DreamHack command-injection-chatgpt 풀이

ping 테스터의 host 입력값이 아무런 감싸기(따옴표)도 없이 f'ping -c 3 {host}' 문자열로 조립돼 /bin/sh -c 로 그대로 실행된다. 세미콜론 하나만 붙여 cat flag.py 를 이어 붙이면 ping 결과 자리에 플래그가 그대로 출력된다.
#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-28
[🥉 Bronze 2] GET만 열어둔 Flask 라우트를 HEAD로 뚫기 — DreamHack blind-command 풀이
blog

[🥉 Bronze 2] GET만 열어둔 Flask 라우트를 HEAD로 뚫기 — DreamHack blind-command 풀이

Flask 라우트에 methods=['GET']만 걸어도 HEAD와 OPTIONS는 자동으로 따라붙는다. HEAD 요청이면 request.method가 'HEAD'가 되어 else 분기의 os.system(cmd)이 실행되지만, HEAD 응답엔 본문이 없어 출력이 보이지 않는다. sleep 시간차로 flag를 한 글자씩 빼냈다.
#dreamhack#ctf#web+4
BLOG2026-06-28
[🥈 Silver 3] 404 페이지에 식을 넣으면 답을 한다 — DreamHack simple-ssti 풀이
blog

[🥈 Silver 3] 404 페이지에 식을 넣으면 답을 한다 — DreamHack simple-ssti 풀이

Flask 404 핸들러가 사용자 경로(request.path)를 템플릿 문자열에 그대로 박은 뒤 render_template_string 으로 렌더한다. 없는 경로에 Jinja2 식을 실으면 서버가 평가하는 SSTI 가 터지고, FLAG 가 app.secret_key 로 들어가 있어 config 한 줄로 그대로 새어 나온다.
#dreamhack#ctf#web+5
BLOG2026-06-27
[🥉 Bronze 2] 칸 하나짜리 로그인 폼으로 admin 행만 콕 — DreamHack simple_sqli_chatgpt 풀이
blog

[🥉 Bronze 2] 칸 하나짜리 로그인 폼으로 admin 행만 콕 — DreamHack simple_sqli_chatgpt 풀이

로그인 폼에 입력칸이 userlevel 하나뿐인데, 그 값이 따옴표째 쿼리에 박힌다. guest와 admin 둘 다 userlevel이 0이라 흔한 or 1=1은 첫 행 guest를 데려온다. userid를 직접 지정해 admin 행만 남긴 과정을 정리했다.
#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-25
[🌱 새싹] admin 세션 ID가 단 1바이트였다 — DreamHack session 풀이
blog

[🌱 새싹] admin 세션 ID가 단 1바이트였다 — DreamHack session 풀이

로그인 세션을 쿠키 하나로 식별하는 Flask 앱. 일반 사용자 세션은 4바이트 난수로 만들지만, 서버가 시작할 때 미리 깔아 두는 admin 세션은 os.urandom(1) — 단 1바이트(00~ff, 256가지)로 만든다. sessionid 쿠키를 256번 갈아 끼우면 admin 세션을 그대로 주워 flag를 읽는다.
#dreamhack#ctf#web+5
 
BLOG2026-06-25
[🌱 Sprout] 기본 자격증명으로 설정값 읽기 — DreamHack web-misconf-1 풀이
blog

[🌱 Sprout] 기본 자격증명으로 설정값 읽기 — DreamHack web-misconf-1 풀이

Grafana 6.6.2의 admin 비밀번호가 초기값 admin 그대로다. 기본 자격증명으로 로그인한 뒤 /api/admin/settings 가 실행 중 설정을 통째로 돌려주는데, 민감 키만 가려지고 평범한 키는 평문이라 auth.anonymous.org_name 에 숨겨둔 플래그가 그대로 노출된다.
#dreamhack#ctf#web+4
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
[🥉 Bronze 4] PHP 정규식 3겹 뚫기 — preg_replace 겹침·알파벳 없는 비번·flag 필터 우회 RCE — DreamHack phpreg 풀이
blog

[🥉 Bronze 4] PHP 정규식 3겹 뚫기 — preg_replace 겹침·알파벳 없는 비번·flag 필터 우회 RCE — DreamHack phpreg 풀이

PHP의 preg_replace·preg_match로 만든 관문 세 개를 차례로 뚫는 문제다. 닉네임은 'nyang'을 겹치게 끼워 한 번만 지워지게 만들고, 비밀번호는 알파벳 없이 치환 패턴을 맞추고, 마지막 cmd는 'flag' 필터를 와일드카드로 우회해 RCE로 flag.txt를 읽었다.
#dreamhack#ctf#web+5
BLOG2026-06-22
[🥉 Bronze 4] echo $(...) 명령 주입 — 와일드카드로 'flag' 필터 우회 — DreamHack baby-linux 풀이
blog

[🥉 Bronze 4] echo $(...) 명령 주입 — 와일드카드로 'flag' 필터 우회 — DreamHack baby-linux 풀이

입력을 echo $(...) 안에 그대로 끼워 /bin/sh 로 실행하는 단순한 페이지. 명령 주입은 바로 되지만 cmd 문자열에 'flag' 가 들어가면 차단된다. 셸 와일드카드 f* 로 'flag' 리터럴을 피해 flag.txt 를 읽어냈다.
#dreamhack#ctf#misc+4
BLOG2026-06-22
[🥈 Silver 4] MongoDB NoSQL 인젝션 + 글자별 클래스로 BAN 우회 — DreamHack Mango 풀이
blog

[🥈 Silver 4] MongoDB NoSQL 인젝션 + 글자별 클래스로 BAN 우회 — DreamHack Mango 풀이

Express + Mongoose 로그인에서 쿼리스트링 연산자 주입이 통한다. admin·dh 문자열은 BAN으로 막혀 있지만, 정규식의 각 글자를 `[c]` 캐릭터 클래스로 감싸면 substring 검사의 인접이 끊겨 우회된다. `uid[$regex]`로 admin을 특정하고 `upw[$regex]`로 flag를 한 글자씩 blind 추출했다.
#dreamhack#ctf#web+5
BLOG2026-06-22
[🥉 Bronze 4] re.match는 끝을 보지 않는다 — 정규식 접두사만 맞춰 통과 — DreamHack ex-reg-ex 풀이
blog

[🥉 Bronze 4] re.match는 끝을 보지 않는다 — 정규식 접두사만 맞춰 통과 — DreamHack ex-reg-ex 풀이

이메일 비슷한 정규식을 만족하면 FLAG를 주는 문제다. 핵심은 검사에 쓴 `re.match`가 문자열 시작만 보고 끝(`$`)을 고정하지 않는다는 점. 진짜 이메일을 넣을 필요 없이 패턴 조각을 하나씩 채운 접두사 `draaaaae1am@abc.com` 한 줄로 통과했다.
#dreamhack#ctf#web+4
BLOG2026-06-22
[🥉 Bronze 4] 검증 없는 파일 업로드 — PHP 웹셸 한 줄로 RCE — DreamHack image-storage 풀이
blog

[🥉 Bronze 4] 검증 없는 파일 업로드 — PHP 웹셸 한 줄로 RCE — DreamHack image-storage 풀이

이미지 공유 사이트인데 업로드 파일의 확장자도 MIME도 검사하지 않는다. `<?php system($_GET['cmd']); ?>` 한 줄짜리 웹셸을 shell.php로 올리면 업로드 디렉토리에 그대로 저장되고, 그 URL을 직접 열면 PHP가 실행돼 RCE가 된다. www-data 셸로 루트의 flag.txt를 읽었다.
#dreamhack#ctf#web+4
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-21
[🌱 새싹] 따옴표 하나로 셸을 가른다 — DreamHack command-injection-1 풀이
blog

[🌱 새싹] 따옴표 하나로 셸을 가른다 — DreamHack command-injection-1 풀이

ping 테스터가 입력한 host를 ping -c 3 "{host}" 문자열로 만들어 /bin/sh -c 로 실행한다. 필터가 한 줄도 없어서, 큰따옴표를 닫고 cat /app/flag.py 를 끼워 넣은 뒤 끝의 #로 닫는 따옴표를 주석 처리하면 플래그가 ping 결과 자리에 그대로 출력된다.
#dreamhack#ctf#web+4
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-20
[🥉 Bronze 2] 따옴표 하나로 admin 행만 콕 집기 — DreamHack simple_sqli 풀이
blog

[🥉 Bronze 2] 따옴표 하나로 admin 행만 콕 집기 — DreamHack simple_sqli 풀이

로그인 쿼리에 userid가 따옴표째 그대로 박힌다. admin 행만 정확히 반환시켜야 플래그가 나오는데, 흔한 ` or 1=1`은 첫 행 guest를 데려온다. 주석으로 패스워드 조건을 지워 admin 행만 집어낸 과정을 정리했다.
#dreamhack#ctf#web+5
 
BLOG2026-06-20
[🌱 새싹] 필터는 업로드에만 걸려 있었다 — DreamHack file-download-1 풀이
blog

[🌱 새싹] 필터는 업로드에만 걸려 있었다 — DreamHack file-download-1 풀이

메모 저장소 Flask 앱. 업로드 경로는 파일명에 '..'가 들어가면 막지만, 읽기 경로(/read)에는 같은 검사가 빠져 있다. uploads 디렉토리를 기준으로 경로를 그대로 이어붙이는 탓에 /read?name=../flag.py 한 줄로 상위 디렉토리의 flag.py를 그대로 읽어 플래그를 가져온다.
#dreamhack#ctf#web+4
BLOG2026-06-08
[⭐⭐] nginx ACL을 \xA0 한 바이트로 — DreamHack youth-Case 풀이
blog

[⭐⭐] nginx ACL을 \xA0 한 바이트로 — DreamHack youth-Case 풀이

nginx가 막은 POST /shop을 경로 끝의 raw \xA0(NBSP) 한 바이트로 우회한다. nginx는 \xA0를 경로에 남겨 deny 규칙과 어긋나지만 Node url.parse는 그 공백을 떼어내 /shop으로 라우팅한다. leg 필터는 합자 fl로 toLowerCase/toUpperCase 비대칭을 찔러 통과한다.
#dreamhack#ctf#web+6
BLOG2026-06-08
[🥈 Silver 3] NoSQL 연산자 주입으로 admin 비밀번호 훔치기 — DreamHack Not-only 풀이
blog

[🥈 Silver 3] NoSQL 연산자 주입으로 admin 비밀번호 훔치기 — DreamHack Not-only 풀이

Mongoose 로그인이 req.body 를 그대로 findOne 쿼리에 넣는다. JSON 바디로 $ne 연산자를 보내 비밀번호 없이 로그인하고, /user 가 노출하는 admin 값으로 진짜 admin 유저를 찾는다. $regex blind 로 비밀번호를 한 글자씩 뽑는데, 플래그는 admin 두 명의 비밀번호에 반씩 쪼개져 있었다.
#dreamhack#ctf#web+4
BLOG2026-06-08
[🥈 Silver 3] eval() 화이트리스트 필터를 chr()로 우회하기 — DreamHack Addition calculator 풀이
blog

[🥈 Silver 3] eval() 화이트리스트 필터를 chr()로 우회하기 — DreamHack Addition calculator 풀이

덧셈식을 받아 eval()로 계산하는 Flask 계산기. 따옴표·언더스코어·콤마가 전부 화이트리스트 밖이라 문자열 리터럴을 못 쓰고, flag·system 같은 단어는 블랙리스트로 막힌다. chr() 코드포인트를 +로 이어 "flag.txt"를 조립해 open().read()로 ./flag.txt를 읽어낸다.
#dreamhack#ctf#web+4
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-06
[🥈 Silver 3] 에러 메시지 한 줄이 키를 흘린다 — DreamHack Padding Oracle 풀이
blog

[🥈 Silver 3] 에러 메시지 한 줄이 키를 흘린다 — DreamHack Padding Oracle 풀이

AES-CBC로 암호화된 관리자 글을 키 없이 읽는다. unpad() 실패가 ValueError로 새어나오는 걸 오라클 삼아 패딩 오라클 공격으로 한 바이트씩 복호했다. 고정 IV, 인증 없는 복호, 구분되는 예외 메시지가 겹쳐 암호문이 그대로 평문이 됐다. 삽질과 캡쳐를 곁들인 풀이 노트.
#dreamhack#ctf#web+4
BLOG2026-06-06
[🥈 Silver 4] switch는 느슨하게 비교한다 — DreamHack Switching Command 풀이
blog

[🥈 Silver 4] switch는 느슨하게 비교한다 — DreamHack Switching Command 풀이

PHP의 switch는 == 로 비교한다. 입력을 boolean true로 보내면 ===로 막아둔 "admin" 검사를 비껴가면서 switch case "admin"에는 걸려든다. 인증을 훔친 뒤엔 escapeshellcmd가 손대지 않는 따옴표로 명령 필터를 깨고, 출력이 안 보이는 blind 환경을 world-writable 웹루트에 파일을 떨궈 회수했다.
#dreamhack#ctf#web+5
BLOG2026-06-06
[🥉 Bronze 1] 읽을 수 없는 flag — LFI로 매니저 비번 털어 RCE — DreamHack Tomcat Manager 풀이
blog

[🥉 Bronze 1] 읽을 수 없는 flag — LFI로 매니저 비번 털어 RCE — DreamHack Tomcat Manager 풀이

image.jsp의 path traversal(LFI)로 /flag를 읽으려다 404에 막힌다. /flag가 읽기 권한 없는 실행 전용 파일이었기 때문. LFI로 conf/tomcat-users.xml의 진짜 매니저 비밀번호를 유출하고, Tomcat Manager에 웹셸 WAR을 배포해 RCE를 얻은 뒤 /flag를 "실행"해 플래그를 뽑았다.
#dreamhack#ctf#web+5
BLOG2026-06-06
[🥉 Bronze 2] 블랙리스트가 빠뜨린 백틱 — $IFS로 공백 없이 RCE — DreamHack Another Ping 풀이
blog

[🥉 Bronze 2] 블랙리스트가 빠뜨린 백틱 — $IFS로 공백 없이 RCE — DreamHack Another Ping 풀이

ping 유틸리티가 입력을 shell=True로 그대로 실행한다. 공백·세미콜론·파이프는 막았지만 백틱이 블랙리스트에 없어 명령치환이 통하고, 막힌 공백은 $IFS로 대체한다. `cat$IFS$9flag.txt` 한 줄로 flag를 ping 에러 메시지에 흘려보냈다.
#dreamhack#ctf#web+4
BLOG2026-06-05
[🥈 Silver 4] UNION으로 진짜 테이블을 찾아내는 SQL Injection — DreamHack baby-union 풀이
blog

[🥈 Silver 4] UNION으로 진짜 테이블을 찾아내는 SQL Injection — DreamHack baby-union 풀이

로그인 쿼리에 따옴표가 그대로 들어가는 전형적인 UNION SQL Injection. ' or 1=1로 주입을 확인하고, UNION SELECT 1,2,3,4로 컬럼 수와 출력 위치를 잡은 뒤, init.sql이 흘린 가짜 이름 대신 information_schema로 진짜 테이블(onlyflag)을 찾아 concat으로 플래그를 뽑았다.
#dreamhack#ctf#web+4
BLOG2026-06-05
[🥇 Gold 3] CSP nonce를 예측해서 뚫는 stored XSS — DreamHack Mini Social Media 풀이
blog

[🥇 Gold 3] CSP nonce를 예측해서 뚫는 stored XSS — DreamHack Mini Social Media 풀이

CSP가 inline script를 막지만 nonce를 Python random()으로 만든다. 응답 헤더로 새는 nonce를 624워드 모아 MT19937 상태를 복원하면 다음 nonce가 예측된다. reporter username의 stored XSS에 예측한 nonce를 박아 admin 봇 브라우저에서 /admin 플래그를 빼냈다.
#dreamhack#ctf#web+6
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-06-05
[🥇 Gold 3] 쿠키 한 줄로 Thymeleaf 뷰 이름을 갈아끼우다 — DreamHack spring-view 풀이
blog

[🥇 Gold 3] 쿠키 한 줄로 Thymeleaf 뷰 이름을 갈아끼우다 — DreamHack spring-view 풀이

Spring + Thymeleaf 앱에서 lang 쿠키가 그대로 뷰 이름이 된다. 프래그먼트 전처리 __${...}__ 로 SpEL을 실행하는 View-Name SSTI를 잡고, java/class/Runtime 블랙리스트와 쿠키 RFC6265 제약(공백·콤마·세미콜론 금지), 그리고 원격 Java 8 환경까지 차례로 우회해 /flag.txt 를 읽어낸다.
#dreamhack#ctf#web+5
BLOG2026-06-05
[🥉 Bronze 1] innerHTML에 넣은 <script>는 안 뜬다 — DreamHack xss-2 풀이
blog

[🥉 Bronze 1] innerHTML에 넣은 <script>는 안 뜬다 — DreamHack xss-2 풀이

vuln 페이지가 param을 innerHTML로 꽂는다. innerHTML로 삽입된 script는 실행되지 않으니 img/onerror로 우회해 봇의 flag 쿠키를 읽고, 공용 /memo 저장소로 흘려보내 회수했다.
#dreamhack#ctf#web+4
 
BLOG2026-06-05
[🥉 Bronze 2] 백업 기능이 쿠키를 그대로 셸에 넘긴다 — DreamHack Simple Note Manager 풀이
blog

[🥉 Bronze 2] 백업 기능이 쿠키를 그대로 셸에 넘긴다 — DreamHack Simple Note Manager 풀이

메모 백업 기능이 backup-timestamp 쿠키를 그대로 cp 명령에 끼워 넣고 shell=True로 실행한다. 출력이 돌아오지 않는 블라인드 OS 커맨드 인젝션이라, 컨테이너 안의 curl로 flag를 읽어 앱 자신의 /create_note에 다시 집어넣고 /notes에서 회수했다.
#dreamhack#ctf#web+4
BLOG2026-06-04
[🥉 Bronze 2] escapeshellcmd가 안 막는 공백 — curl 인자 주입으로 웹셸 RCE — DreamHack Command Injection Advanced 풀이
blog

[🥉 Bronze 2] escapeshellcmd가 안 막는 공백 — curl 인자 주입으로 웹셸 RCE — DreamHack Command Injection Advanced 풀이

escapeshellcmd는 셸 메타문자는 막지만 공백과 하이픈은 그대로 둔다. curl 명령에 -o 인자를 끼워 넣어 PHP 웹셸을 웹루트 cache/에 떨군 뒤, 그 웹셸로 실행전용 바이너리 /flag를 돌려 플래그를 얻은 과정을 정리했다.
#dreamhack#ctf#web+5
BLOG2026-06-04
[🥉 Bronze 1] SQL인 척하는 JSON, 진짜 구멍은 WebDAV PUT — DreamHack Really Not SQL 풀이
blog

[🥉 Bronze 1] SQL인 척하는 JSON, 진짜 구멍은 WebDAV PUT — DreamHack Really Not SQL 풀이

로그인은 user/<id>.json 파일을 읽는 JSON 유저 저장소다. admin 비밀번호는 모르지만, Apache의 user/ 디렉터리에 mod_dav가 켜져 있고 .htaccess는 DELETE만 막는다. PUT으로 admin.json을 내가 아는 해시로 덮어써 admin이 된 뒤 flag.php를 연 과정.
#dreamhack#ctf#web+5
BLOG2026-06-04
[🥉 Bronze 1] 봇의 admin 헤더를 빌려 /whoami 털기 — DreamHack Are you admin? 풀이
blog

[🥉 Bronze 1] 봇의 admin 헤더를 빌려 /whoami 털기 — DreamHack Are you admin? 풀이

intro 페이지의 name이 `| safe`로 이스케이프 없이 렌더되는 reflected XSS. 봇은 모든 요청에 admin Basic-Auth 헤더를 자동으로 붙이고 다니므로, 봇 컨텍스트에서 /whoami를 fetch하면 admin으로 인증돼 FLAG가 나온다. 이를 webhook으로 빼낸 과정.
#dreamhack#ctf#web+5
BLOG2026-06-04
[🥉 Bronze 2] 봇의 쿠키를 /memo로 흘려 회수하기 — DreamHack xss-1 풀이
blog

[🥉 Bronze 2] 봇의 쿠키를 /memo로 흘려 회수하기 — DreamHack xss-1 풀이

/vuln이 입력을 이스케이프 없이 그대로 뱉는 reflected XSS. flag를 쿠키로 들고 페이지를 방문하는 봇에게 스크립트를 주입해 document.cookie를 같은 서버의 /memo로 흘려보내고, 그 memo를 직접 읽어 플래그를 회수한 과정을 정리했다.
#dreamhack#ctf#web+5
 
BLOG2026-06-04
[🥉 Bronze 3] sleep 한 줄로 KEY를 흘리게 만들기 — DreamHack BypassIF 풀이
blog

[🥉 Bronze 3] sleep 한 줄로 KEY를 흘리게 만들기 — DreamHack BypassIF 풀이

Admin KEY를 모르는데 KEY는 md5(flag)다. 명령 필터를 통과하는 sleep으로 일부러 타임아웃을 내면 except 분기가 KEY를 응답에 그대로 흘린다. 유출한 KEY를 다시 제출해 플래그를 받아낸 과정을 정리했다.
#dreamhack#ctf#web+4
 
BLOG2026-06-04
[🥉 Bronze 3] X-User·Referer 헤더 위조로 비밀 문서 읽기 — DreamHack DreamDocs 풀이
blog

[🥉 Bronze 3] X-User·Referer 헤더 위조로 비밀 문서 읽기 — DreamHack DreamDocs 풀이

문서 열람 권한을 Referer와 X-User 헤더로만 판단하는 게시판이다. 둘 다 요청자가 마음대로 보내는 값이라, X-User: admin 과 Referer: /share 를 위조하면 confidential 문서의 본문(FLAG)이 그대로 열린다.
#dreamhack#ctf#web+6
 
BLOG2026-06-04
[🥉 Bronze 2] GIF 폴리글랏으로 PHP 웹셸 업로드 — DreamHack Image Uploader 풀이
blog

[🥉 Bronze 2] GIF 폴리글랏으로 PHP 웹셸 업로드 — DreamHack Image Uploader 풀이

확장자와 MIME 타입 검사를 AND(&&)로 묶은 업로더다. 둘 중 하나만 통과하면 되는 허점이라, GIF89a 매직바이트를 앞에 붙인 .php 파일이 image/gif 로 인식돼 그대로 업로드된다. 웹셸을 올려 RCE로 플래그를 읽는 과정을 정리했다.
#dreamhack#ctf#web+6
 
BLOG2026-06-04
[🥉 Bronze 3] 숨긴 _id를 ObjectId 예측으로 복원해 비밀글 읽기 — DreamHack mongoboard 풀이
blog

[🥉 Bronze 3] 숨긴 _id를 ObjectId 예측으로 복원해 비밀글 읽기 — DreamHack mongoboard 풀이

게시판이 비밀글의 _id만 null로 가렸지만, MongoDB ObjectId는 시각·랜덤·카운터로 이뤄진 예측 가능한 값이다. 보이는 글들의 _id와 비밀글의 publish_date만으로 숨긴 _id를 복원해 본문(FLAG)을 읽는 과정을 정리했다.
#dreamhack#ctf#web+5
 
BLOG2026-06-04
[🥈 Silver 4] CouchDB 특수 엔드포인트로 인증 우회 — DreamHack NoSQL-CouchDB 풀이
blog

[🥈 Silver 4] CouchDB 특수 엔드포인트로 인증 우회 — DreamHack NoSQL-CouchDB 풀이

로그인 비교식 result.upw === req.body.upw 의 빈틈. uid에 CouchDB 특수 엔드포인트 _all_docs 를 넣으면 응답에 upw 필드가 없어 undefined가 되고, upw를 아예 보내지 않으면 undefined === undefined 로 비밀번호 없이 인증을 통과해 FLAG를 가져온다.
#dreamhack#ctf#web+4
BLOG2026-06-04
[🥉 Bronze 1] SVG 태그 allowlist를 우회한 innerHTML XSS로 쿠키 탈취 — DreamHack PTML 풀이
blog

[🥉 Bronze 1] SVG 태그 allowlist를 우회한 innerHTML XSS로 쿠키 탈취 — DreamHack PTML 풀이

업로드한 SVG를 innerHTML로 그대로 삽입하는데, 검증은 태그 이름만 allowlist로 본다. 허용 태그(animate·image·svg)에 이벤트 핸들러 속성을 달아 XSS를 띄우고, 봇이 들고 있는 flag 쿠키를 동기 XHR로 유출하는 과정을 정리했다.
#dreamhack#ctf#web+5
 
BLOG2026-06-04
[🥉 Bronze 4] 부분 일치 누출로 4자리 사물함 번호 복원 — DreamHack random-test 풀이
blog

[🥉 Bronze 4] 부분 일치 누출로 4자리 사물함 번호 복원 — DreamHack random-test 풀이

로그인 폼이 전체 값이 아니라 입력 길이만큼의 prefix만 비교하면, 한 글자씩 정답을 떠보는 oracle이 된다. 2.1억 조합짜리 사물함 번호+비밀번호를 176번의 요청으로 복원해 플래그를 얻는 과정을 정리했다.
#dreamhack#ctf#web+5
 
BLOG2026-06-01
[🥇 Gold 2] URLDecode와 curl의 파서 차이로 SSRF 우회 — DreamHack Strong BypassIF 풀이
blog

[🥇 Gold 2] URLDecode와 curl의 파서 차이로 SSRF 우회 — DreamHack Strong BypassIF 풀이

서버는 URLDecode 후 UriComponentsBuilder로 host를 검증하지만, 실제 요청은 디코딩하지 않은 raw URL을 curl에 그대로 넘긴다. 이 한 끗 차이로 검증기는 localhost로, curl은 외부 서버로 보내 Access-Token을 유출하고 /api/flag를 직접 호출했다.
#dreamhack#ctf#web+4
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
[🌱 새싹] 내부 전용 API를 ../ 한 방으로 뚫기 — DreamHack Path Traversal 풀이
blog

[🌱 새싹] 내부 전용 API를 ../ 한 방으로 뚫기 — DreamHack Path Traversal 풀이

127.0.0.1 에서만 호출 가능한 internal API 서버. 사용자 입력이 그대로 내부 요청 URL에 들어가는 SSRF + Path Traversal 취약점을 이용해, `../flag` 한 줄로 접근 제한된 /api/flag 엔드포인트의 플래그를 빼낸다.
#dreamhack#ctf#web+4
 
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-05-24
[⭐⭐⭐] ord마저 막힌 곳에서 글자를 캐내는 법 — DreamHack SQL Injection Bypass WAF Advanced 풀이
blog

[⭐⭐⭐] ord마저 막힌 곳에서 글자를 캐내는 법 — DreamHack SQL Injection Bypass WAF Advanced 풀이

union·select·from·and·or·admin·공백까지 막힌 WAF 환경에서 `||` `&&` `idx=` `ascii()` `substr()` 조합으로 admin 행을 매칭하고, ASCII 이진 탐색으로 44자리 플래그를 추출한 풀이입니다. `ord()`가 `or` 부분문자열 때문에 차단되는 함정이 핵심.
#dreamhack#ctf#web+5
BLOG2026-05-23
[⭐⭐⭐⭐] 디버그 엔드포인트가 세션 전체를 토해낸다 — DreamHack session-basic 풀이
blog

[⭐⭐⭐⭐] 디버그 엔드포인트가 세션 전체를 토해낸다 — DreamHack session-basic 풀이

Flask 세션 인증 서비스에서 개발자가 "나중에 고칠게" 주석으로 남겨둔 /admin 엔드포인트가 session_storage 딕셔너리 전체를 JSON으로 반환한다. 서버 부팅 시 미리 생성된 admin sessionid를 그대로 가져와 쿠키에 박으면 끝.
#dreamhack#ctf#web+4
 
BLOG2026-05-07
[⭐⭐⭐] SQLi + SSRF 더블 슬래시 필터 우회 — DreamHack admin only 풀이
blog

[⭐⭐⭐] SQLi + SSRF 더블 슬래시 필터 우회 — DreamHack admin only 풀이

SQLite UNION 인젝션으로 admin 비밀번호를 탈취·로그인하고, /profile의 SSRF를 통해 //getflag (더블 슬래시) 경로 우회로 /getflag 필터를 bypass해 FLAG를 획득하는 풀이.
#dreamhack#ctf#web+6
 
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
BLOG2026-04-01
[⭐⭐⭐⭐⭐] 한 번 막히고 나서야 보였던 체인: DreamHack EZ-Anti-LLM Pingback SSRF 풀이
blog

[⭐⭐⭐⭐⭐] 한 번 막히고 나서야 보였던 체인: DreamHack EZ-Anti-LLM Pingback SSRF 풀이

WordPress 6.9.1 Pingback Blind SSRF를 이용해 2-Hop 체인으로 내부 Flag 서버의 값을 외부로 끌어낸 DreamHack Level 5 문제 풀이. 삽질 포인트부터 최종 익스플로잇 흐름까지 차근차근 정리했다.
#dreamhack#ctf#ssrf+5
BLOG2026-03-31
[🥈 Silver 3] 미리보기 기능을 타고 내부 Docker API까지 — DreamHack Tap Tap 풀이
blog

[🥈 Silver 3] 미리보기 기능을 타고 내부 Docker API까지 — DreamHack Tap Tap 풀이

Internal API Testing Tool의 검증 우회를 통해 SSRF를 성공시키고, 내부 Docker API에서 컨테이너 로그로 최종 플래그를 획득한 전 과정을 정리합니다.
#dreamhack#ctf#ssrf+3
 
RESEARCH2026-03-25
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
 
RESEARCH2026-03-24
JWT(JSON Web Token) 구조와 인증 흐름 완전 이해
blog

JWT(JSON Web Token) 구조와 인증 흐름 완전 이해

JWT의 구조(Header, Payload, Signature)와 인증 흐름을 실무 관점에서 정리하고, 안전하게 사용하는 방법까지 한 번에 정리합니다.
#jwt#authentication#security+2
 
 
 
 
 
 
 
 
 
 
 
 
 
+5
 
 
+5