https://gall.dcinside.com/mgallery/board/view/?id=fromsoftware&no=2469118


엘든 링 나오기 이전에도 게임했던 할배들은 기억할 것이라 생각함

22년 1월, 엘든 링 나오기 딱 한 달 전 즈음에 다크 소울 3에서 원격 코드 실행 취약점이 터져서 7개월 정도 서버가 닫혔던 적이 있었음

덕분에 서버 복구되기 전까지 사설서버로 피빕하고 코옵했던 기억이 있는데


이 글은 그 취약점이 정확히 어떤 원리였는지 설명하는 글임





https://github.com/tremwil/ds3-nrssr-rce

https://nvd.nist.gov/vuln/detail/CVE-2022-24125

https://nvd.nist.gov/vuln/detail/CVE-2022-24126




1. CVE-2022-24126

세션 데이터 파싱에서 발생하는 임의 코드 실행 취약점


멀티 플레이가 시작될 때 세션 데이터(NRSessionSearchResult)는 메모리 스택 영역에 마련되는 임시 공간에 저장됨

당연하지만 이 임시 공간은 크기가 한정되어 있음

때문에 세션 데이터를 저장할 때 데이터가 임시 공간 안에 저장될 수 있는 크기인지 검사하여야 하는데, 이 검사가 없었음




마련된 저장 공간보다 큰 데이터가 저장 공간에 저장되면 데이터는 저장 공간에서 흘러넘치는데, 이 과정에서 옆 공간의 데이터를 덮어씀

이것을 데이터가 흘러넘친다고 하여 오버플로라고 하고, 흘러넘친 데이터로 인해 덮어씌워진 데이터는 당연하지만 제 역할을 하지 못함



이것만 보면 딱히 큰 영향이 있을 것 같지는 않은데, 문제는 스택 영역에 데이터를 읽고 사용하는 객체가 위치해 있었음

DLMemoryInputStream라는 이름의 이 객체는 하위 2바이트가 vftable_ptr, 즉 가상함수 테이블 포인터임


객체는 데이터를 저장한 후 가상함수 테이블을 호출하는데, 이 가상함수 테이블이 위치한 주소가 여기에 저장됨

객체가 가상함수 테이블을 호출하면 가상함수 테이블 내의 알맞은 함수가 실행됨


이 주소를 조작하면 내가 원하는 함수를 호출하게 할 수 있음

문제는 이 주소를 어떻게 조작하느냐인데, 오버플로를 이용하면 이 주소를 조작할 수 있음


세션 데이터를 딱 DLMemoryInputStream의 하위 2바이트까지 덮어쓸 정도의 길이로 조작하고

마지막 2바이트를 내가 임의로 만들어준 함수의 주소로 정함

이러면 객체가 기존의 가상함수 테이블이 아니라 내가 임의로 정한 함수를 호출함



다만 이것은 자신의 컴퓨터에서만 실행할 수 있기 때문에 그리 큰 취약점은 아님

다크 소울 3에서 임의 코드를 실행해봤자 크게 얻을 건 없음

은행 앱 같은 거였다면 난리가 났을 텐데 게임 내부 데이터같은 거 빼내봤자 크게 난리날 일은 아니잖아


문제는 CVE-2022-24125와 연계되면서 발생함






2.CVE-2022-24125

세션 데이터의 배포 과정에서 발생하는 보안취약점


다크 소울 시리즈는 P2P를 통해 멀티플레이를 구현한 것으로 널리 알려져 있고 실제로 그렇지만 P2P만 사용하지는 않고 당연히 서버가 존재함

각 플레이어들을 매칭시키기 위해서는 멀티플레이에 참여하고자 하는 플레이어들을 연결시켜야 하기 때문임

때문에 서버는 P2P 연결 수립 과정에서 크게 관여함


서버 연결은 다들 익히 알고 있듯 타이틀 화면에서 진행됨

엘든 링이나 세키로도 타이틀 화면에서 서버 연결을 하니까



아무튼, 서버는 세션 호스트로부터 세션 데이터를 받아서 세션에 연결되고자 하는 클라이언트(플레이어)에게 전달함

이 세션 데이터가 바로 위에서 말했던 DLMemoryInputStream임

따라서 세션 데이터는 서버만 보낼 수 있음


이어야 하는데, 문제는 데이터를 보내는 주체가 서버인지 검사하는 로직이 없었음

클라이언트가 타 클라이언트에게 조작한 세션 데이터를 보낼 수 있었다는 것




그러면 그 세션 데이터를 타 클라이언트에게 어떻게 보내는가, 다크 소울 3에서는 세션 데이터가 아래와 같은 메시지를 통해 전달됨


PushRequestSummonSign(소환 사인)

PushRequestAllowBreakInTarget(침입)

PushRequestVisit(서약령)

PushRequestAcceptQuickMatch(암령 소환)


또 RequestSendMessageToPlayers라는 요청 또한 그 수단 중 하나임

말했듯 침입 시에는 PushRequestAllowBreakInTarget라는 메시지가 쓰이는데, 이를 통해 침입하는 클라이언트가 세션의 스폰 위치를 얻고 P2P 세션에 참여할 수 있게 됨


RequestSendMessageToPlayers는 이 메시지를 던지기만 하는 요청이고, 적어도 게임 내에서는 그렇게 쓰였음


RequestSendMessageToPlayers는 이름에서 유추할 수 있듯 이 요청은 단순히 플레이어에게 메시지를 던지는 기능을 지니고 있음

문제는 기능이 단순해도 너무 단순했다는 건데, 덕분에 이 요청은 서버에 연결된 모든 클라이언트에게 메시지를 던질 수 있는 능력이 있었고

이것이 CVE-2022-24126과 연계되어 서버와 연결된 아무 클라이언트에게나 임의 명령을 실행시킬 수 있게 하는 매우 심각한 취약점이 됨



또한, 말했듯이 프롬 소프트웨어 게임은 타이틀 화면에서 서버와 연결함

때문에 이 취약점은 타이틀 화면에서 press any button을 통과한 이후의 모든 클라이언트에게 영향을 미칠 수 있었음






취약점을 발견했던 블루센티넬 개발자는 이 취약점을 프롬에 여러 차례 전송했었는데 프롬이 씹었다고 함

그래서 한 트위치 스트리머의 방송에 이 취약점을 직접 시연했고, 그렇게 서버가 7개월 간 닫히게 됨