읽기 권한 없는 파일 복사하기 (feat. hktrace, Codegate2016 Ultra_REV)

By rls1004 | December 10, 2016

읽기 권한 없는 파일 복사하기 (feat. hktrace, Codegate2016 Ultra_REV)





막내선원  rls1004는 Codegate2016 예선에 출제되었던 로컬 익스플로잇 문제 “Ultra_REV” 의 출제자입니다.

롸업도 낭낭히 써두었지요.

기억이 가물가물하신 분들을 위해 롸업 링크를 같이 퍼왔습니다!


Codegate 2015 qualifier “Ultra_REV”(350p) Writeup




서론



2016 CODEGATE CTF 예선에 Ultra_REV 라는 문제를 출제한 적이 있습니다.

이 문제의 서버에는 읽기, 쓰기 없이 오직 실행 권한만 있는 ultra_rev 라는 바이너리가 동작하고 있었습니다.


그런데 대회 모니터링을 하던 도중 /tmp 폴더에 ultra_rev.hk 라는 이상한 파일이 생기더라구요.


…? 뭐지


그래서 알게된 hktrace라는 도구에 대해 글을 쓰려고 합니다.




권한(Permission) 이란?


$ ls -al


다들 잘 아시겠지만 다시 한 번 집고 넘어갑시다~!


◎ d : directory, - : file

네모 박스의 제일 왼쪽 칸에 대한 내용입니다.

제일 많이 보이는 d와 - 이외에도 c(character device), l(symlink), p(named pipe), s(socket), b(block device), D(door) 등이 존재합니다.


◎ r : 읽기 권한 (4), w : 쓰기 권한 (2), x : 실행 권한 (1)

read, write, execute 에 관한 권한 입니다.


◎ 소유자 권한 | 그룹 권한 | 전제 권한

rwx에 관한 권한이 세 번나오는데 각각 순서대로 소유자, 그룹, 전체에 대한 권한을 나타냅니다.

파일의 소유자와 그룹은 차례대로 나타난 rls1004, rls1004 입니다.


리눅스의 권한에 대한 더 자세한 설명은 아래 링크를 참고 해 주세요.


< 엄마보고싶다(1) – 전초전(feat.linux permission) >




id 명령어로 확인한 저의 권한입니다.


$ id


저는 rls1004 이고 rls1004 라는 그룹에 속해있군요.

이번엔 ls -al 명령어로 확인한 sample 이라는 바이너리의 권한입니다.


$ ls -al sample


소유자와 그룹은 rwx, 그 외에는 rx 권한이 있네요~

이 바이너리의 소유자는 asdf, 그룹은 asdf 입니다.


제가 이 파일을 복사할 수 있을까요?



네, 잘 됩니다\^\^

그럼 이런 경우는 어떨까요?



소유자 외에는 실행권한 밖에 없습니다.

이 파일을 복사하려고 하면…



Permission denied 가 뜨면서 거부됩니다.



복사하고 싶죠?




방법


유닉스 운영체제는 모든 것을 파일로 취급하고 이것을 tree 구조로 관리합니다.


# 이미지 출처 : http://hetpro-store.com/TUTORIALES/cc-linux/


◎ procfs

procfs은 유닉스 계열 운영체제에서 프로세스 및 시스템 정보를 계층적 파일 구조같은 형식으로 보여주는 특별한 파일 시스템입니다.

사용자 영역에서 커널 영역의 데이터를 얻기 위해서는 시스템 호출이나 디바이스 드라이버를 사용해야 합니다.

시스템 호출을 통해 정보를 얻으려면 항상 시스템 호출을 호출하는 응용 프로그램을 작성해야 하고,

디바이스 드라이버를 사용하려면 별도의 응용프로그램을 작성할 필요는 없지만 매번 디바이스 드라이버 탐색을 거쳐야 하고,

가상 파일 시스템(VFS)에서 사용하는 모든 자료구조를 생성해야 합니다.

때문에, 작은 데이터를 주고 받을 수 있는 간단한 파일 시스템이 필요했고 그게 procfs 입니다.

프로세스와 커널 관련 정보들이 /proc 라는 이름의 마운트 포인트에 매핑되게 됩니다.



이 /proc 디렉토리에 가면 프로세스 정보 등을 볼 수 있습니다.

숫자로 되어있는 디렉토리들은 pid에 해당하는 프로세스의 정보를 담고 있습니다.

그 중 하나로 들어가볼까요?



maps 라는 파일이 보이는데요, 현재 프로세스가 매핑된 메모리 주소 공간을 보여주는 파일입니다.



자신의 maps 파일을 읽어오는 프로그램을 하나 만들었습니다.



이 파일은 root의 파일이고 root 외에는 읽기 권한이 없습니다.

이제 실행시켜볼까요?



앗, 일반 사용자에게 읽기 권한이 주어지지 않았지만 메모리 영역에는 읽기 권한이 존재합니다!



바이너리가 실행될 때 가상메모리에는 실행에 필요한 모든 내용이 적재되는데,

그중에는 프로그램의 코드가 담긴 코드 영역, 문자열 등이 담긴 데이터 영역이 있습니다.

이 영역을 복사할 수 있다면 바이너리에 읽기 권한이 없어도 메모리에 적재된 데이터를 가져옴으로써 복사가 가능합니다.




구현하기


hktrace는 이걸 구현하기 위한 방법으로는 ptrace 함수를 사용합니다.


ptrace는 프로세스를 추적하는 기능을 하는 함수입니다.

유닉스 환경에서 프로세스 디버깅에 사용되는 함수인데요,

대표적으로는 무적의 디버깅 도구인 gdb에서 내부적으로 ptrace 함수를 사용합니다



ptrace는 다양한 기능을 함수의 인자로 처리합니다.

man 페이지에 의하면- 파라미터로는 request, pid, addr, data 이렇게 네 가지를 받는데요,

request 인자를 통해 다양한 기능을 수행하고, request에 따라 그 뒤의 인자들은 사용하기도 하고 사용하지 않기도 합니다.


[request]
PTRACE_TRACEME
PTRACE_PEEKTEXT, PTRACE_PEEKDATA
PTRACE_PEEKUSR
PTRACE_POKETEXT, PTRACE_POKEDATA
PTRACE_POKEUSR
PTRACE_SYSCALL, PTRACE_SINGLESTEP
PTRACE_GETREGS, PTRACE_GETFPREGS
PTRACE_GETSIGINFO (since Linux 2.3.99-pre6)
PTRACE_SETREGS, PTRACE_SETFPREGS
PTRACE_SETSIGINFO (since Linux 2.3.99-pre6)
PTRACE_SETOPTIONS (since Linux 2.4.6; see BUGS for caveats)
PTRACE_GETEVENTMSG (since Linux 2.5.46)
PTRACE_CONT PTRACE_SYSEMU, PTRACE_SYSEMU_SINGLESTEP (since Linux 2.6.14)
PTRACE_KILL
PTRACE_ATTACH
PTRACE_DETACH


이렇게..!! 다양한 request 들이 존재합니다.

유용한 기능들이 많은데요, 이중에 저희가 사용할 것은 PTRACE_TRACEME, PTRACE_PEEKTEXT, PTRACE_PEEKDATA 입니다.


간단히 말하면 PTRAC_TRACEME 요청은 부모 프로세스가 자신을 추적할 수 있도록 프로세스의 실행을 잠시 중단시키는 역할을 합니다.

PTRACE_PEEKTEXt, PTRACE_PEEKDATA 는

자식 프로세스의 addr(ptrace 함수의 세 번째 인자)위치에서 워드(word)를 읽어 반환하는 역할을 합니다.


리눅스에서는 text와 data 주소 공간을 분리하지 않기 때문에 두 개의 request는 같은 역할을 합니다.


이제 저희는 부모 프로세스와 자식 프로세스를 생성하여 자식 프로세스는 타겟 프로그램을 실행하게 하고,

부모 프로세스는 자식 프로세스를 추적하게 하여 text와 data 영역을 긁어올 것입니다.



전체적인 흐름입니다!


1.fork() 를 통해 자식 프로세스(위) 생성


2-1. 부모 프로세스(아래)는 wait 상태


2-2. 자식 프로세스는 PTRACE_TRACEME를 통해 부모 프로세스에게 제어권을 줌

2-3. 자식 프로세스가 타겟 프로그램 실행


3-1. PTRACE_TRACME 요청에 의해 타켓 프로그램 정지(시그널 발생)

3-2. 부모 프로세스가 시그널을 인지


4.부모 프로세스는 자식 프로세스의 pid에 해당하는 maps를 읽어 text와 data 영역 복사


5.복사가 끝나면 PTRACE_KILL을 사용하여 자식 프로세스 죽임


전체적인 과정은 위와 같구요, 소스가 길어서 url로 첨부합니다.


http://hkpco.kr/code/hktrace.c




사용하기



일단 제 권한은 rls1004 입니다.



타켓 프로그램은 private라는 실행 파일이구요, 실행하면 This is screte text 가 출력되는 파일입니다.

root 외에는 실행 권한 밖에 없습니다.



hktrace 툴을 사용하여 복사한 모습입니다.



두 파일이 완전히 동일한 파일은 아니지만 내용은 같습니다!




여기서 하나 더 확인 해 볼까요?



BOF원정대(Lord of BOF) Fedora core 4입니다.

현재 titan의 권한이구요.



my-pass 바이너리를 복사하려고 합니다.

my-pass 는 현재 권한의 패스워드를 출력시켜주는 기능을 합니다.



hktrace 툴을 사용하여 복사한 모습입니다.



원래의 바이너리인 /bin/my-pass 에 strings 명령어를 사용해보면 Permission denied가 뜨며 거부되는데요,

복사한 바이너리인 my-pass.hk 바이너리에 strings 명령어를 사용해보면 4개의 패스워드가 모두 출력되는 것을 확인할 수 있습니다.




결론


이런 방법이 가능했던 이유는 파일의 권한과 메모리의 권한이 분리되어 있고

메모리에 대해서는 사용자의 권한을 다시 검사하지 않기 때문입니다.


이것이 취약점인지 아닌지에 대해서는 의견이 분분합니다.


comments powered by Disqus