주요 내용으로 건너뛰기

TIMISOARA CTF - Neurosurgery (리눅스 메모리 포렌식) WRITE-UP

TIMISOARA CTF 2018 QUALS - Neurosurgery

일전에 몇 차례 리눅스 환경에서의 메모리 포렌식을 시도해본 적이 있다. 관련해서 간단한 전자책을 출판하는 게 목적이었는데 결과적으로 좌초되고 말았다. 기본적으로 리눅스 환경에서 프로파일을 생성하고 설정해주는 작업이 지나치게 복잡하고, 때문에서인지 CTF 대회 등에서도 Windows 운영체제 위주로만 문제가 출제되기 때문에 적절한 예제를 찾기가 너무나 어려웠기 때문이다.

이와 관련해서 몇 번의 포스팅을 했었다.

참고 1 : Linux 메모리 분석을 위한 프로파일 생성하기

참고 2 : 리눅스 메모리 분석시 프로파일 설정

참고 3 : 리눅스 포렌식 분석을 위한 메모리 추출 - LiME 

그리고나서, 도저히 못하겠어서 때려치는 심정으로 쓴 논문이 아래와 같다..

참고 4 : [논문발표] 리눅스 시스템의 메모리 포렌식 : 난제와 발전방향


서론부터 엄살이 길었는데, 어쨌거나 때려치웠던 주제에 대해서 잊고 지내고 있다가, 최근 Timisoara CTF 2018 Quals 에서 리눅스 메모리 포렌식 관련 문제가 출제되었다는 소식에 흥미를 가지지 않을 수 없었다. 그래서 시간 나면 풀어봐야지.. 하고 미루다가 어제 갑자기 삘받아서 열심히 두들겼다. 작년에 여러 번 삽질해봤으니 이번엔 금방 하겠지라는 안일한 생각으로 시작했으나 결과적으로 또 하루를 무한한 삽질로 허비했다는 참담한 심정이다..ㅋㅋㅋ 나의 삽질기를 공유한다.

1. 문제 개요

문제에 출제된 내용은 다음과 같다.

I consider biology classes the most fun of all and sometimes scary.
For example, in the last lesson we learned about brain dumping. It
was extraordinary (black magic as my dad said, btw he’s a computer
scientist), learning about interactions and activities in the memory.
What? Yes, of course you can look at it, too. The pleasure is mine!
Authors: Sin__ & 0xcpu

대충 설명을 읽어보면, 출제자인 Sin__ & 0xcpu 가 생물학을 공부하던 시절 그 내용들이 꽤나 재미있기도 하고 때로는 소름 끼칠 만큼 깊은 인상을 남겼다고 한다. 학기 마지막 시간에는 뇌를 통째로 복사하는 것을 접한 적이 있는데, 이는 기억(Memory) 속의 상호작용을 탐구하는 굉장히 비상한 내용이었다. 컴퓨터공학을 전공하신 아버지가 말씀하시길 그것은 마치 흑마술(黑魔法, Black Magic)과 같다고 하셨다. 그렇다면? 여러분도 본 CTF를 통해 그와 유사한 경험을 느껴보실 수 있다. 한번 즐겨보자. 


그래서인지 문제 파일의 제목이 neurosurgery(신경외과학)인가 보다. 문제에 사용된 메모리 덤프는 아래의 주소에서 받을 수 있다

원문 : https://drive.google.com/open?id=1IKI7Pn73y8L2u-znw3H7PLYhzSAtYJRK

시간이 오래되어 원저자의 파일이 사라질 것을 대비하여 아래와 같이 필자의 아카이브 사본을 남겨둔다

사본 : https://drive.google.com/open?id=1FUulZSJDRN0zQe_paWDvPeScS82C8mkh


문제에서는 특별히 무결성을 검증할 수 있는 Hash 값을 제시하고 있지는 않다. 

neurosurgery.zip의 압축을 풀고 나면 들어있는 image 파일의 sha-256 Hash 값은 아래와 같으니 참고.

bf2552dc4c821ccbeecb8879f22bb0af4f217aaecbd82553ba28712efd620f29

이미지 설명을 입력하세요.이미지 설명을 입력하세요.


2. 리눅스용 프로파일 생성

나는 이미 챌린지가 끝난 후에 문제를 접했기 때문에 본 메모리 덤프가 리눅스용이라는 것을 알고 있었지만, 만약 챌린지에서 처음 마주한 파일이 어느 운영체제에서 추출된 것인지를 Detect 하는 것 자체가 쉽지 않았을 수 있다.

아래와 같이 strings 명령어를 사용하여 덤프 내용의 문자열 중 Linux version 이 포함된 내용을 grep 하면 된다. 확인 결과 Linux version 4.4.0-116-generic에서 추출된 것으로 보인다.

우선 현재 내가 가지고 있는 모든 리눅스 VM과 장비들을 확인해본 결과 다들 구버전의 커널을 사용하고 있었다. 

jaeyou@cpuu:~$ uname -r
4.15.0-24-generic


문제에서 제시된 4.4.0-116-generic과 똑같은 환경을 세팅하기 위해 아래와 같이 수행한다.

원래 이렇게 하면 커널이 업데이트된다고들 하는데 다 거짓말이었다. 재부팅해도 안 바뀐다 ㅡㅡ

진심 이 과정에서 반나절을 날렸다.. 역시 리눅스 커널 업데이트는 절대로 원격 접속으로 수행하면 안 된다... 중간에 뻑났는데 그 뒤로 안 켜져서 핵 망.. 서버 있는 물리적 위치까지 가기에는 일주일이나 기다려야 한다;;

핵심은 grub를 이용해서 새로 설치된 커널과 부팅 순서를바꿔주어야 한다. grub로 커널 순서 바꿀 때 자신 없으면 하지 말 것.. 안 그러면 VM이고 리얼 머신이고 다 날린다 ㅠ

$ sudo vi /boot/grub/grub.cfg 에서 업데이트된 커널 명칭 확인.

이걸 /etc/default/grub에다가 명시해주어야 하는데, 이상하게 또 이름 지정 방식이 각기 다름. 아래와 같이 하지 않으면 Warning이 뜸. Warning 메시지가 가이드해주는 대로 아래처럼 그냥 따라 하자..

이제 변경사항을 grub에 업데이트 적용하자.

재부팅하고 나서 확인해보자. 4.4.0-116-generic으로 표출된다면 성공!

이제 해당 시스템에서 volatility 용 프로파일을 생성한다. volatility를 설치하고 나면 tools/linux 디렉터리가 있는데, 여기서 그냥 make 명령어를 쳐주면 된다. 만약 dwarfdump가 깔려있지 않다면 먼저 깔아준다.

작업 결과 module.dwarf라는 파일이 생성된다. 이것은 일명 vtypes라고 부르며 해당 커널의 데이터 구조에 관한 명세서이다.

또한 분석할 커널에 대한 Symbol 정보를 담고 있는 System.map 파일이 필요하다. 해당 파일은 대부분 /boot/ 디렉터리에서 찾을 수 있으며, Ubuntu 16.04의 경우 System.map-4.4.0-116-generic라는 이름으로 저장되어 있다. 각자가 테스트하는 환경에서 /boot/디렉터리에 있는 System.map 파일을 조사해보기 바란다.

module.dwarf 파일과 System.map 파일을 모두 입수하였다면, 이것을 압축해야 한다. 압축은 커맨드 라인에서 zip 명령어를 사용하고, 압축하고자 하는 파일들의 경로와 함께 입력하면 된다.

나 같은 삽질을 건너뛰고자 하는 사람들을 위해 내가 만든 프로파일을 공유함..

Ubuntu Linux 16.04 버전 기준이며 커널은 4.4.0-116-generic으로 문제로 출제된 메모리의 환경과 정확히 일치하는 시스템을 구축한 후 프로파일을 생성하였음.

Ubuntu16.04_4.4.0-116-generic.zip


3. Volatility로 분석하기

사실 문제를 풀고 나서 생각해보니 이 문제는 profile 만드는 게 거의 80~90%이고, 그 난이도에 비해서 문제 자체는 그다지 어렵지 않은 것 같다. volatility를 사용해서 linux용 프로파일을 적용하면 금방 풀 수 있다.

$ python vol.py --info
Volatility Foundation Volatility Framework 2.6


Profiles
--------
LinuxUbuntu16_04_4_4_0-116-genericx64 - A Profile for Linux Ubuntu16.04_4.4.0-116-generic x64
VistaSP0x64                           - A Profile for Windows Vista SP0 x64
VistaSP0x86                           - A Profile for Windows Vista SP0 x86
VistaSP1x64                           - A Profile for Windows Vista SP1 x64
VistaSP1x86                           - A Profile for Windows Vista SP1 x86
VistaSP2x64                           - A Profile for Windows Vista SP2 x64
VistaSP2x86                           - A Profile for Windows Vista SP2 x86

우선 아까 생성판 프로파일이 제대로 적용되는지 확인해본다. vol.py --info 명령어를 통해 로드되는 Profiles에 LinuxUbuntu16_04가 표출된다. 잘 설치되었다는 뜻이다. 이제 이 프로파일을 적용하는 방법은 아래와 같다.


Volatility에서 사용할 수 있는 Linux Command Reference 를 참고하자.

linux_bash 커맨드는 bash 쉘의 history를 표출해준다.

osboxes@osboxes:~/volatility$ python vol.py -f ../image --profile=LinuxUbuntu16_04_4_4_0-116-genericx64 linux_bash
Volatility Foundation Volatility Framework 2.6
Pid      Name                 Command Time                   Command
-------- -------------------- ------------------------------ -------
    1166 bash                 2018-04-15 15:24:47 UTC+0000   cd ..
    1166 bash                 2018-04-15 15:24:47 UTC+0000   ls
    1166 bash                 2018-04-15 15:24:47 UTC+0000   ls
    1166 bash                 2018-04-15 15:24:47 UTC+0000   sudo rm /media/sf_bin/
    1166 bash                 2018-04-15 15:24:47 UTC+0000   cd .cache/
    1166 bash                 2018-04-15 15:24:47 UTC+0000   ls
    1166 bash                 2018-04-15 15:24:47 UTC+0000   sudo su
    1166 bash                 2018-04-15 15:24:47 UTC+0000   ls -la
    1166 bash                 2018-04-15 15:24:47 UTC+0000   poweroff
    1166 bash                 2018-04-15 15:24:47 UTC+0000   ls -la
    1166 bash                 2018-04-15 15:24:47 UTC+0000   sudo chown panda:panda ht0p
    1166 bash                 2018-04-15 15:24:47 UTC+0000   ls
    1166 bash                 2018-04-15 15:24:47 UTC+0000   ls -la
    1166 bash                 2018-04-15 15:24:47 UTC+0000   sudo umount /media/sf_bin
    1166 bash                 2018-04-15 15:24:47 UTC+0000   sudo rm -r /media/sf_bin/
    1166 bash                 2018-04-15 15:24:47 UTC+0000   chown panda:panda ht0p
    1166 bash                 2018-04-15 15:24:47 UTC+0000   chown panda:panda suleanu
    1166 bash                 2018-04-15 15:24:47 UTC+0000   mv suleanu ht0p
    1166 bash                 2018-04-15 15:24:49 UTC+0000   ls -la
    1166 bash                 2018-04-15 15:24:55 UTC+0000   shred -u .bash_history
    1166 bash                 2018-04-15 15:24:59 UTC+0000   ls
    1166 bash                 2018-04-15 15:25:02 UTC+0000   ls -la
    1166 bash                 2018-04-15 15:25:21 UTC+0000   ls /media/
    1166 bash                 2018-04-15 15:25:24 UTC+0000   ls -la
    1166 bash                 2018-04-15 15:25:30 UTC+0000   ./ht0p \  &
    1166 bash                 2018-04-15 15:25:32 UTC+0000   htop
    1166 bash                 2018-04-15 15:25:32 UTC+0000   htop

bash history 자체가 굉장히 짧다. 중간에 shred -u .bash_history 명령어가 있는 것으로 보아 일부 내용은 일부러 삭제한 것 같다. 어쨌거나 문제 풀이에 필요한 정보들만 남아있다. (문제 출제를 급하게 한 것이 아닐까 싶다. 어쩌면 의도적으로 쉬운 문제를 내기 위한 것일지도 모르니..)

아직 확실하지는 않지만 ht0p라는 파일을 실행하기 위한 일련의 시도가 보이고 있다. suleanu라는 파일을 ht0p로 이름 변경한 후 ./ht0p로 실행하였다. 그렇다면 해당 프로세스를 찾아보자.

linux_pslist 플러그인을 사용하면 현재 작동 중인 프로세스 목록이 표출된다.

내용이 너무 길어서 일부 내용은 생략하였다.

bash 프로세스(pid:1166)이 ht0p(pid:1192)와 htop(pid:1193) 라는 두 개의 프로세스를 실행한 것을 확인할 수 있다. 우선은 ht0p를 위주로 살펴보자.

 python vol.py -f ../neurosurgery/image --profile=LinuxUbuntu16_04_4_4_0-116-genericx64 linux_proc_maps -p 1192

이미지 설명을 입력하세요.

결과를 보면 /home/panda/ht0p 라는 파일을 실행한 것인데, Offset 0xffff8800355e3800 에서 Start 주소는 0x0000000000400000 이고, 0x00000000004d9000 까지이다. windows 포렌식에서는 memdump나 procdump로 해당 프로세스 메모리를 덤프할 수 있었는데, linux에 관련 명령어를 찾아보니 linux_proc_maps 와 linux_dump_map 으로 할 수 있다고 한다. (참고 : Volatility Linux Process Memory

다시 한번 말하지만, 리눅스와 관련한 정보는 모든 것이 부정확하다고 보면 된다. 실제로 해보면 다 안된다. 다 거짓말이다 ㅡㅡ.. 아마 업데이트가 힘든듯하다. (여기에서 2시간 날림~~~~;;)

그래서 해당 프로세스의 메모리 덤프가 불가능한 상황이므로 다른 방법을 모색한다. 위에서 얻어낸 힌트는 파일의 경로가 /home/panda/ht0p/ 라는 것이고 File Inode Number가 390593이라는 단서이다. 

이제는 해당 '프로세스'를 덤프하는 것이 아니라, '파일'을 찾아서 덤프하는 것으로 방향을 바꾸자.

관련한 키워드는 linux_find_file이다. (참고 : linux_find_file)

이미지 설명을 입력하세요.

파일 경로를 입력하면 해당 파일을 찾아준다. ht0p와 htop 두 가지 파일을 찾아보았는데, htop는 존재하지 않는 듯하고 ht0p만 출력된다.

linux_bash 플러그인과 linux_find_file 플러그인 결과화면

출력된 결과는 아래와 같다.

Inode NumberInodeFile Path
3905930xffff88007bd8e698/home/panda/ht0p

아까 전에 찾은 390593과 일치하는 것을 알 수 있다. 다만 find_file는 Number가 아닌 Inode 값을 입력으로 받기 때문에 아래와 같이 명령어를 수행한다.

ht0p 파일이 추출되었을 것이다. file 명령어를 통해 확인해보면 ELF 64-bit 바이너리이다.

현재 바이너리를 실행할 수 없는 상태이므로, chmod 명령어로 실행 권한을 부여하고 실행해보자

출력된 결과를 보면 flag를 찾았다!

이미지 설명을 입력하세요.

timctf{ch4nc3_f4vors_th3_pr3p4red_m1nd}

chance favors the prepared mind.. 루이 파스퇴르의 명언으로 "기회는 준비되어 있는 정신이 필요하다."는 뜻인데 번역이 마음에 들지 않는다. 나 같으면 "마음의 준비를 다한 자에겐 기회가 찾아온다."

4. 요약

문제 풀이 과정을 요약하면 아래와 같다.

1) 이미지 파일의 운영체제 및 커널 버전을 확인한 후 그와 동일한 환경을 구축한다. (Ubuntu 16.04.4 LTS GNU/Linux 4.4.0-116-generic x86_64) (난이도 극악)

2) 구축된 환경에서 volatility linux profile 을 생성하고 분석할 때 해당 profile을 지정해준다. (난이도 보통)

3) 의심되는 파일을 덤프 추출하여 local system 환경에서 구동한다. (난이도 쉬움)


교훈 : 리눅스 메모리 포렌식은 하지 말자.

정보보안에 관심이 많은 대학원생, 소프트웨어 엔지니어/서버관리자

CPUU 님의 창작활동을 응원하고 싶으세요?

댓글 1

SNS 계정으로 간편하게 로그인하고 댓글을 남겨주세요.