완료됨
uaf_overwrite 워게임 질문

안녕하세요. uaf_overwrite 워게임을 풀어보며 해당 풀이 강의를 보면서, 궁금증이 생겨서 질문을 남기게 되었습니다.




이번 문제에서 libc-2.27.so 라이브러리를 이용해야 하는데, 워게임을 다운받고 gdb를 켜서 vmmap으로 파악해보면 libc-2.27.so가 아닌 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2으로 라이브러리가 설정되어있습니다. libc-2.27.so로 바꾸기 위해서는 LD_PRELOAD를 설정해야 한다고 해서,

set environment LD_PRELOAD libc-2.27.so 를 입력해준 후 실행해봐도 아래와 같이 오류가 발생합니다.

image.png

LD_LIBRARY_PATH도 추가로 설정해야 된다는 글을 보고, set environment LD_LIBRARY_PATH ./로 해줬는데 방법이 잘못된건지 잘 되지 않습니다.. 혹시 이런 경우에 라이브러리를 설정해주는 방법을 알려주실 수 있으실까요 ?




이번 문제의 원가젯 조건은 아래와 같고, 소스 코드에서 if(robot->fptr)을 검사하는 부분에서,
robot->fptr();이 실행되기 때문에 여기에 중단점을 걸고 모든 조건을 확인해보았습니다.

image.png

image.png
image.png

gdb로 검사할 때는 robot->fptr에 원가젯 주소를 넣은 상태로 확인해보지는 않아서 정확하지는 않을테지만,

이 상태에서는 0x4f432 인 원가젯만 가능하고, 0x4f3ce4f3d5는 나머지 조건은 만족하지만 rcx == NULL이 만족안되고, 0x10a41c[rsp + 0x70] == NULL을 만족하지 않아서 안된다고 생각했는데, 실제로는 0x10a41c만 가능했습니다.

혹시 writable 조건은 vmmap에서 w가 존재하는 영역에 속하는지 확인하면 되는게 맞고,

image.png

조건이 이렇게 3줄로 되있는건 각줄이 and로 연결되어서 동시에 전부 만족해야 하는게 맞는건가요?

address rsp+0x50 is writable
rsp & 0xf == 0
rcx == NULL || {rcx, "-c", r12, NULL} is a valid argv

그리고, {[rsp+0x70], [rsp+0x78], [rsp+0x80], [rsp+0x88], ...} is a valid argv 이런 조건은 어떻게 판단해야 하는지 궁금합니다..

제가 위에서 원가젯의 조건을 판단하는 방법이 잘못된건가요 ? 만약 틀렸다면 원가젯의 조건을 정석적으로 판단하는 방법은 무엇인지 알려주실 수 있으실까요 ?




이번 문제에서 malloc으로 할당된 메모리에 데이터를 입력할 때, fd가 덮어씌워지게 되는데, 1bytes의 값을 입력할 경우 fd의 하위 1bytes만 덮어씌워지게 됩니다.

그래서 만약 B를 입력하면, fd가 가리키는 libc의 특정 영역과 베이스 사이의 미리 계산한 offset의 하위 1bytes0x42로 바꿔주었는데, ASLR이 적용되어도 libc의 하위 12bits는 항상 0x000이기 때문이 맞나요 ?

libc base는 무조건 페이지내에서의 오프셋이 0이어서 하위 12bits가 항상 0x000임이 보장되나요 ?

#시스템해킹 #공격기법 #use_after_free
작성자 정보
더 깊이 있는 답변이 필요할 때
드림핵 팀과 멘토에게 직접 문의해 보세요!
답변 1

안녕하세요~!
일단 첫 번째 질문부터 답변 드리겠습니다.
해당 문제는 주어진 glibc 버전과 ld(loader)의 호환 문제로 보입니다.
해결책은 다음과 같이 두 가지가 있습니다(제가 아는한에서는...).

  • Docker를 사용하여 주어진 glibc와 호환되는 로더를 사용
  • patchelf를 와 주어진 glibc 버전과 호환되는 ld를 이용하여 바이너리가 해당하는 glibc와 로더를 이용할 수 있도록 패치

저는 전자를 더 추천합니다 참고로 해당 glibc는 ubuntu 18.04에서 preload 했을 때 동작합니다.
(아마 glibc 2.30으로 가면서 로더도 그에 따라 변경되면서 호환성 문제가 생긴것 같네요)


두 번째 질문에 대한 답변입니다.

  1. 맞습니다. memory map에서 w(write) 권한이 설정되어 있는 부분이 writable한 영역입니다.
  2. & 는 비트 연산자로 rsp 레지스터와 0xf 를 AND 연산 했을 때 0이 나와야 성립합니다.
  3. rcx 레지스터가 NULL 값이거나 뒤의 파라미터 ([rsp+0x70], [rsp+0x78], [rsp+0x80], [rsp+0x88]) 값이 유효한 값이어야 한다는 뜻입니다.
address rsp+0x50 is writable    --- 1
rsp & 0xf == 0    --- 2
rcx == NULL || {rcx, "-c", r12, NULL} is a valid argv    ---3

추가로 설명 드리자면 execve("/bin/sh", rsp+0x70, environ) 이 부분을 참고하시면 좋을 것 같습니다.
execve 함수는 2번째 인자로 문자열 포인터를 받는데 이 때 {[rsp+0x70], [rsp+0x78], [rsp+0x80], [rsp+0x88], ...} 로 표현된 이유는 파라미터로 줄 문자열 포인터가 2개라면 [rsp+0x70], [rsp+0x78] 까지 채워줘야 하기 때문에 이런식으로 표현해준 것 같습니다.
요점은 2번째 파라미터로 넘겨주는 부분은 rsp+0x70 부터 라고 생각하시면 될 것 같습니다.

0x10a41c execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL || {[rsp+0x70], [rsp+0x78], [rsp+0x80], [rsp+0x88], ...} is a valid argv

마지막 질문에 대한 답변입니다.
결론부터 말하자면 "넵 맞습니다" base 주소는 항상 하위 12비트는 항상 0입니다.
그 이유는 바이너리가 메모리에 적재될 때 페이지 단위로 적재 됩니다. 리눅스 운영체제에서 기본적인 페이지 단위는 4096 바이트로 이를 헥스값으로 표현하면 0x1000 이 됩니다 따라서 base 주소의 하위 12비트는 항상 0이 됩니다.

답변이 되었으면 좋겠네요!

PS) 첫 번째 적어주신 질문에서 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 는 라이브러리가 아닌 로더에 대한 경로입니다!

2024.08.23. 02:57