다른 사람이 찾은 보물 훔치기(feat. 보안성검토의 필요성)

cjo security
CJ 온스타일 기술 블로그
8 min readDec 8, 2023

안녕하세요? CJ ENM 커머스부문 정보보안센터 심상윤입니다.
2023년 12월 6일(수) 사내 기술블로그 홍보 이벤트로 보물찾기가 진행되어 참여 후기와 보안담당자로서 소회를 간략하게 기록해보려 합니다.

보물찾기 이벤트는 사내 임직원 전체를 대상으로 진행되었고, 이벤트 진행 당일 보물지도가 사내 게시판에 등록되었습니다.

보물지도

총 보물은 71개가 있었고, 찾은 보물 갯수는 공지를 통하여 실시간 확인이 가능했습니다.

이때부터 몇 분씩 모여다니면서 지도 속 공간을 여기저기 탐험(?)하고 계시는 분들이 보였습니다.
저도 일부러 찾아다니지는 않았지만, 다니는 곳마다 여기저기 유심히 눈여겨보다가 엘리베이터 안에서 QR코드 하나를 찾았지요.
블로그 게시할 의도가 있었던건 아니라서 따로 사진을 찍지는 못했네요.

QR코드를 카메라로 인식 후 아래 URL로 접속이 가능했습니다.
https://xxx.com/lock?uuid=3ca89049-3d48-4ad1-965d-4fd87e2bdd3f
이후 사번 확인과 간단한 퀴즈를 거쳐 아래 이미지가 랜딩 되었습니다.

누군가가 먼저 발견하고 채굴을 한 상태였습니다. URL 파라메터로 넘어가는 UUID가 보물에 대한 key값이고 이 키값에 누가 채굴했는 지를 기록하는 형식으로 만들어진 것 같았습니다.

UUID — 위키백과 참조

네트워크 상에서 서로 모르는 개체들을 식별하고 구별하기 위해서는 각각의 고유한 이름이 필요하다. 이 이름은 고유성(유일성)이 매우 중요하다. 같은 이름을 갖는 개체가 존재한다면 구별이 불가능해 지기 때문이다. 고유성을 완벽하게 보장하려면 중앙관리시스템이 있어서 일련번호를 부여해 주면 간단하지만 동시다발적이고 독립적으로 개발되고 있는 시스템들의 경우 중앙관리시스템은 불가능하다. 개발주체가 스스로 이름을 짓도록 하되 고유성을 충족할 수 있는 방법이 필요하다. 이를 위하여 탄생한 것이 범용고유식별자(UUID)이며 국제기구에서 표준으로 정하고 있다.

UUID 표준에 따라 이름을 부여하면 고유성을 완벽하게 보장할 수는 없지만 실제 사용상에서 중복될 가능성이 거의 없다고 인정되기 때문에 많이 사용되고 있다.

예측 가능하거나 무차별 대입 공격이 가능한 길지않은 숫자를 사용하거나 BASE64값을 사용하지 않고 보물의 key값으로 UUID를 사용한 것은 훌륭한 선택이었습니다.
만약 그렇지 않았다면, 이 단계에서 많은 보물들이 탈취될 수도 있었을 꺼에요.

먼저 찾은 보물URL을 PC에서 접속하면서, 프록시 도구로 요청값과 응답값을 살펴 보았습니다.

  1. GET /lock?uuid=3ca89049–3d48–4ad1–965d-4fd87e2bdd3f HTTP/1.1
  2. GET /api/valid/uuid/3ca89049–3d48–4ad1–965d-4fd87e2bdd3f
    {“itemCode”:”3ca89049–3d48–4ad1–965d-4fd87e2bdd3f”,
    ”itemName”:”스타벅스 아이스아메리카노를”,
    ”winningYn”:”Y”,
    ”winningTime”:”20231206034207"}
  3. /api/valid/empNo/사번
    {“empNo”사번",”getTreasureCode”:””,”getTreasureTime”:””}
  4. GET /api/result/3ca89049–3d48–4ad1–965d-4fd87e2bdd3f?empNo=007065 HTTP/2
    {“isPrize”:false,
    ”alreadyReceived”:false,
    ”beforeUUID”:”3ca89049–3d48–4ad1–965d-4fd87e2bdd3f”,
    ”afterUUID”:”3ca89049–3d48–4ad1–965d-4fd87e2bdd3f”,
    ”itemName”:”스타벅스 아이스아메리카노를”,
    ”beforeItemName”:””}

위와 같은 형태로 진행되었습니다. 추측해보자면..
1번은 보물을 찾으면 해당 보물에 다른 사람이 접근하지 못하도록 lock을 하는 url인듯 합니다.
2번은 uuid에 매핑된 상품 정보를 보여주며, 해당 상품을 누가 가져갔는지와 가져간 시간을 기록해 놓은 것 같습니다.
3번은 사번을 입력 받아 보물을 찾은 사람이 보물을 찾았는지 여부와 찾은 시간을 확인하는 url인것 같습니다.
4번은 2번과 3번의 정보를 조합하여 이 보물을 이 사람한테 부여할지 말지를 판단하는 url인것 같습니다. 이 보물을 다른 사람이 찾지 않았고(isPrize:true), 이사람이 보물을 찾았던 사람이 아니면(alreadyReceived:false) 해당 보물(uuid)에 상품(itemname)을 매핑하는 url인것 같습니다.

여기까지 분석한 이후, isPrize값을 false → true로 변경하면 뭔가 될 것 같다는 느낌이 왔습니다.

  1. GET /api/result/3ca89049–3d48–4ad1–965d-4fd87e2bdd3f?empNo=사번 HTTP/2
    {“isPrize”:true,
    ”alreadyReceived”:false,
    ”beforeUUID”:”3ca89049–3d48–4ad1–965d-4fd87e2bdd3f”,
    ”afterUUID”:”3ca89049–3d48–4ad1–965d-4fd87e2bdd3f”,
    ”itemName”:”스타벅스 아이스아메리카노를”,
    ”beforeItemName”:””}
  2. GET /api/valid/uuid/3ca89049–3d48–4ad1–965d-4fd87e2bdd3f HTTP/2
    {“itemCode”:”3ca89049–3d48–4ad1–965d-4fd87e2bdd3f”,
    ”itemName”:”스타벅스 아이스아메리카노를”,
    ”winningYn”:”Y”,
    ”winningTime”:”20231206072657"}

추측한대로 1번의 isPrize의 값을 false->true로 변경하니 /api/valid/uuld를 호출하면서 상품의 획득 여부와 획득 값을 변경하는 것을 확인하였습니다.

최종적으로 아이스아메리카노를 획득했다는 화면도 보이고, 받기 클릭해서 받아졌다는 메세지도 확인 했습니다.
상품 획득 시간도 확인할 것 같아서 변경하였으나, 꼼꼼히 하지 않아서 최초 확인한 획득 시간 이전으로 변경은 하지 못했습니다. 나중에 보물찾기 결과를 보니 이건 고려하지 않은 것 같네요.

기획하신 분들이 고생을 많이 하신 것 같은데, 이런 고퀄리티 서버를 만들면서까지 재미있는 이벤트를 만들어서 진행하여 많은 사우들이 재미있게 참여했었던 것 같습니다.

위와 같이 다른 사람의 보물을 훔쳤지만, 시스템 상에서 확인한 것이 아니기 때문에 결과 페이지만 상품을 받은 것처럼 보여졌는지 진짜 보물을 찾은 것으로 수정이 되었는지는 확신할 수 없었습니다. 워크샵이 끝나고 보물찾기 결과가 발표될 때 발표자료에서 제 이름을 보고 최종적으로 다른 사람이 찾은 보물을 훔치는 미션에 성공한 것을 알았습니다.

다른 사람의 기쁨을 빼앗을 수는 없어 워크샵 종료 후 바로 자수하고, 상품은 원래 주인에게 전달해 달라고 했습니다~ ^^

회사에서 시스템의 중대한 변화가 있거나, 개인정보 관련된 비즈니스 로직이 변경되면 정보보안센터에서 보안성 검토를 진행합니다.
보물찾기의 경우 사내에서만 진행한 이벤트이고, 내부 시스템과 연관이 없는 시스템이라 이슈가 되지는 않지만, 만약 우리가 운영하고 있는 온스타일에서 이런 이슈들이 발생한다면 기업 이미지에 타격을 받을 것 입니다.

보물찾기 이벤트를 통해 보안성 검토의 중요성을 다시 한 번 상기시키며, 문제가 될 여지가 있으면 언제든지 정보보안센터에 문의해 달라는 메시지를 전하고 싶었습니다.

두서 없이 쓴 글 읽어주셔서 감사합니다.

--

--