CVE-2020-7245: CTFd Account Takeover

새벽에 코딩하다가 Siguza님이 리트윗한 흥미로운 트윗을 봤다. CTFd Account Takeover 취약점이었는데 CTFd에 취약점이있으면 어떨까라는 이야기를 며칠전에 회사에서 다른 연구원님들과 얘기했었기도 했고(티오리 <3) 예전에 LCBC가 벨루미나에서 어떤 취약점썼는지는 모르지만 대회 인프라가 CTFd였는데 제로데이로 해킹해서 디페이스 한 상황이 너무 멋있었어서(사진있는지 다시 찾아봐야겠다) Account Takeover이지만 관리자 계정 takeover하면 아무튼 디페이스도 할수있으므로 ㅎㅎ 어떻게 발생한 취약점인지 궁금해 하던거 냅두고 분석해보았다.

ㅋㅋ

TL;DR

"admin" == "admin "



Analysis

패치 커밋 메세지

위에서 잠깐 언급했던대로 CVE-2020-7245(CVE도 줌ㅋㅋ)은 Account Takeover 취약점이며 v2.2.2 에서 발생했으며 v2.2.3에서 패치되었다. 트윗을 읽어보면 leading-trailing whitespace를 Registration 기능에서 잘 처리하지않아 발생한 것으로 추론해볼 수 있다. https://github.com/CTFd/CTFd/commit/f660ed1fb769126a2d149c26645bbde457a5c616 패치된 커밋과 커밋 메세지을 살펴보면 더욱 자세한 내용이있는데 usernames are now properly stripped, reset password할 때 토큰에 대해서 username 대신 email address을 사용한다고 커밋메세지에 있다. 취약점에 대한 테스트케이스도 커밋이 되어있었다.

이제 취약점을 자세히 살펴보기전에 전 후 context를 먼저 보아야하는데, CTFd의 reset_password 함수는 다음과 같이 작동한다.

reset_password 함수 중 일부

작동 내용을 요약하면

1. 유저가 초기화할 유저의 email을 /reset_password에 POST 요청
2. team = Users.query.filter_by(email=email_address).first() 해서 매치되는 유저 조회 or 유저가 없다면 exception 발생(get_error())
3. 조회한 유저의 team.name 유저(팀)이름itsdangerous.url_safe.URLSafeTimedSerializer 으로 serialize
4. serialize한 내용을 password reset token으로 /reset_password/<token> url만들어서 리셋 링크 이메일 전송
5. 유저가 이메일로 수신받은 링크 클릭하면 password reset token을 unserialize해서 name 가져옴
6. 가져온 이름으로 user = Users.query.filter_by(name=name).first_or_404() 으로 조회한 오브젝트를 링크받은 유저가 입력한 패스워드로 초기화

이렇게 작동한다.

빠르게 생각하면 일단 과정 자체는 일반적으로 사용되는 이메일을 이용한 비밀번호 초기화 과정이다. 또한 serializer는 많이 쓰이는 라이브러리라 취약점 발생 가능성을 배제하고, 특별히 이상한 점은 email을 받았으면서 name을 serialize해서 토큰으로 만드는게 조금 수상하다.
하지만 이것만으론 버그가 성립이 안되는데, 문제되는 부분은 바로 6번 과정이다.
토큰으로부터 unserialize한 name을 filter_by 하는데 어차피 요청한 유저의 email의 name이라서 상관없다고 생각 할 수 있지만 함정은 SqlAlchemy ORM에서의 filter_by는 trailing whitespace를 무시하고 조회를 한다. 그렇기 때문에 취약점이 발생한다.
이상하게 도큐먼트에 작성이 안되어있고(이것때문에 뭐만들다가 삽질한 경험이있다 흑ㅠ) leading whitespace도 무시되는 줄은 몰랐어서 테스트해봤는데 안되는 거 같다.

leading whitespace가 되는가 싶어서 해본 filter_by 테스트

따라서 취약점을 exploit하려면 회원가입할때 공격자가 본인 email과 saika\x20\x20\x20 같이 뒤에 trailing whitespace 넣은 username으로 회원가입하고 password reset 요청하면 초기화 링크 이메일은 본인한테 오는데 링크를 사용하면 saika 유저의 패스워드를 초기화 할 수 있어 Account Takeover가 된다.



Patch

CVE-2020-7245에 대한 패치는 위의 커밋로그에서도 잠시나왔지만 password reset token 생성에 유저 name 대신 email을 사용하도록 변경 되었고 모든 컬럼(name, email, password) 에 대해서 .strip() 하여 공백을 제거하도록 패치되었다.




PS

나름 재미있는 취약점이었던거같다 대체 왜 굳이 name을 넘겼을까,,,
개발하면서 경험한 behavior가 분석한 취약점에 쓰여서 신기했다.
그리고 분석하려고 CTFd 소스 오디팅하면서 이번에 github 코드 뷰에 추가된 xref 기능 사용해봤는데 너무 편하게 오디팅했다 짱짱

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *