kernel 1-day 분석 환경설정하기

해킹/ kernel 2019. 1. 23. 17:07

0.환경

VMware® Workstation 15 Pro, 15.0.0 build-10134415
우분투 18.04.1 LTS VMware 64bit

 

 
당연한 이야기지만 잘 하는 사람은 이 포스팅이 필요가 없다. 처음 공부하면서 삽질을 상당히 오래 할 수밖에 없었는데 그중 해결에 도움이 되었던 삽질만 모아서 글을 쓰는거라 효율적인 방법은 분명 아닐 수 있다.
환경은 위와 같이 우분투 VM에서 진행했다. 윈도우 환경에서의 커널 빌드나 qemu 사용, 디버깅 등에 익숙한 사람들은 상관 없겠지만 처음 커널 익스플로잇을 공부하는 상황에서 최대한 정보가 많은 환경을 기준으로 하다 보니 Ubuntu VM에서 커널을 빌드하고 qemu에서 커널을 실행시키며 디버깅을 진행했다.


커널 디버깅 절차
  1. Ubuntu Vm 내에 qemu 설치
  2. debootstrap을 이용해 rootfs 이미지 생성
  3. 커널 빌드
  4. qemu에서 빌드한 커널 실행
  5. 디버거 실행, 코드 연결
  6. rootfs 이미지 세팅


1. qemu 설치


sudo apt-get install qemu
넘나 당연한 것...


이렇게 설치된다. qemu-ARCH 이렇게 되어있는 것은 해당 아키텍쳐의 바이너리를 실행하기 위한 것으로 알고 있고 qemu-system-ARCH 형식으로 된 것들이 VM처럼 작동해서 내가 원하는 커널을 디버깅하게 해줄 친구들이다.




2. debootstrap을 사용한 rootfs 이미지 생성


qemu에서 커널이 동작하기 위해 파일시스템(rootfs)가 필요하다. debootstrap을 이용하면 debian distro를 디렉토리에 설치할 수 있다. qemu에서 사용할 이미지가 필요하므로 qemu-img를 이용해 이미지를 만들어 디렉토리에 mount 한 후 debootstrap으로 debian rootfs를 이미지가 mount된 폴더 내에 설치한다.


IMG=qemu-image.img
DIR=mount-point.dir
qemu-img create $IMG 1g
mkfs.ext2 $IMG
mkdir $DIR
sudo mount -o loop $IMG $DIR
sudo debootstrap --arch i386 jessie $DIR
sudo umount $DIR
rmdir $DIR

당연하지만 7번째 라인에 --arch i386을 원하는 아키텍쳐에 맞게 설정하면 될듯. 4번째 줄에 mkfs도 원하는 파일시스템으로 바꿀 수 있다.
스크립트를 해석하면 qemu-img로 빈 이미지를 만들고 파일시스템을 지정해 준 다음 마운트포인트가 될 디렉토리를 생성한다. 이 디렉토리에 빈 이미지를 mount 해주고 debootstrap으로 원하는 시스템의 rootfs 파일들을 다운받은 뒤 umount해줘서 Debian rootfs 파일들을 담은 이미지가 만들어지게 된다.

mount 해서 확인해보면 리눅스의 "/" 하나가 폴더안에 들어가 있다.



3. kernel 빌드하기


git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
git tag –l | less
git checkout <tag(v3.8 or ...)>


먼저 git에서 커널 소스코드를  다운로드 하고 git tag로 버전명을 확인, checkout한다. Dirty Cow 취약점은 2.6.22부터 4번대까지 여러 버전에 걸쳐서 존재했던 취약점이라 안전하게 3.8버전으로 checkout 해주었다.



sudo apt-get install git build-essential kernel-package fakeroot libncurses5-dev libssl-dev ccache
커널 빌드에 필요한 기본적인 dependency들을 설치한다. 

이제 문제의 make  환경설정이 남았는데 한번에 되길 바라진 않았지만 온갓 버그들이 난무했다. 포기한 부분 말고 해결한 것 위주로만 서술한다.
kernel/bounds.c:1:0: error: code model kernel does not support PIC mode
나름대로 설정을 해서 make를 돌려보면 이 에러가 가장 많이 발생했다. 경험상 gcc 버전 호환이 안되는 문제일 가능성이 가장 높다.


리눅스가 설치된 디렉토리에서 compiler-gcc 헤더파일을 찾아보면 대충 어떤 버전으로 빌드가 가능한지 알 수 있다. 내 우분투는 gcc7을 쓰는데 무려 gcc3과 4밖에 지원하지 않는 듯 하다.


sudo apt-get install gcc-4.8
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 50
update-alternatives --config gcc



호환되는 gcc 버전을 설치한 후에 Priority를 50으로 지정해서 기본 gcc를 4.8로 지정해줬다. 찝찝하니 끝나고 다시 돌려놓을 생각.


번외:

혹시 위의 PIC 버그가 gcc 버전문제가 아니라면 Makefile에서 KBUILD_CFLAGS 부분을 위처럼 수정하면 빌드가 가능하다. 내 경우에 gcc 버전 문제였기 때문인지 저렇게 해서 빌드가 성공해도 qemu에서 정상적으로 돌아가지는 않았다.



cp /boot/config-`uname -r` .config
./scripts/config -e DEBUG_INFO -e GDB_SCRIPTS
make menuconfig
make

현재 ubuntu 환경의 config 파일을 가져와서 수정하는 방식으로 .config 파일을 만들 수 있다. 디버깅에 사용할 빌드이기 때문에 DEBUG_INFO와 GDB_SCRIPTS를 enable 해준다. 이외에 세세한 사항들을 조절하기 위해서는 menuconfig로 들어가서 만져주면 된다.
물론 이방법 외에도 default cofnig를 사용하는 방식이 있다. 첫문장의 /boot/config=`uname -r`을 사용하지 않고  make i386_defconfig 이런 방식으로 해주면 되는데 나는 이렇게 했을때 빌드가 안되거나 되도 실행이 도중에 멈춰서 복사하는 방식을 사용했다.

make 도중 이런 에러도 발생했다. 에러메시지를 보면 kernel/timeconst.pl 의 373번째 라인에 defined를 사용하면 안되는데 사용했다는 에러인 것 같다. 

 :before

: after

해당 파일을 vi 에디터로 열어서 defined만 지워주고 make clean && make 했더니 해결됐다. 컴파일에 성공한 후 설정한 아키텍쳐 디렉토리 아래에 보면 bzImage 파일이 생기는데 이 파일이 qemu에서 사용할 커널 이미지 파일이다.



4. qemu에서 빌드한 커널 실행


#!/bin/bash

qemu-system-x86_64 -kernel bzImage \
-append "root=/dev/sda rw console=ttyS0 nokaslr" \
-m 512 \
-hda qemu-image.img \
-net user,hostfwd=tcp::2222-:22 \
--nographic \
-s -S

qemu를 통한 실행 명령어
-kernel bzImage : 빌드한 커널 이미지, linux/~~~ 에 빌드된걸 실행하는 디렉토리로 가져왔음. 
-append
    root=/dev/sda rw : ubuntu가 사용하고 있는 /dev/sda의 공간을 사용함, rw가 없으면 쓰기가 안되서 root여도 시스템 파일 수정이 안됨
    console=ttyS0 : 지금 사용하고 있는 터미널로 콘솔이 떨어짐 물론 ssh로 접속중이라면 pty 번호가 들어가면 똑같이 됨
    nokalsr : 커널 aslr disable
-m 512 : 메모리 할당
-hda qemu-image.img : 2번에서 만들었던 rootfs 이미지
-net user,hostfwd=tcp::2222=:22 : 네트워크 되면 ssh를 연결해서 접속해려고 포트포워딩을 해줌. 안됨. 필요없음.
--nographic : 위에 -append "console=ttyS0"랑 연관되서 그래픽 표시를 안하고 콘솔만 사용하겠다는 뜻
-s -S : -s는 기본 1234번 포트로 디버거 접속을 대기하겠다는 뜻. -S 는 커널 시작 코드에서 멈춘 후 대기하겠다는 뜻



5. 디버거 실행



make하면 기본 git 폴더에 vmlinux라는 바이너리가 생성된다. 이 바이너리를 gdb에 로드해서 심볼을 읽고 qemu가 생성한 1234번 리모트 포트에 붙어서 디버깅을 시작할 수 있다. 명령어는 "target remote:1234". attach에 성공하고 c 를 입력하면 qemu에서 부팅이 시작된다.


6. rootfs 이미지 세팅

기본적으로 2에서 만든 qemu-img.img의 root 패스워드도 모르는 상태다. qemu-img.img를 mount해준 뒤 chroot로 root path를 고정해놓고 passwd 명령어로 root 패스워드를 변경할 수 있다.

이렇게 mnt라는 폴더를 만들고 qemu-img를 mount 해서 root 패스워드도 지정할 수 있고 apt-get으로 여러 vim, gcc등 이것저것 설치할 수도 있다. 아직 네트워크 디바이스 생성이 안되는 문제를 해결하지 못해서 지금까지의 분석은 이런식으로 필요한 것들을 설치한 후 umount해주고 qemu를 실행하는 방식을 취하고 있다. 해결이 되면 추가로 포스팅 할 예정.
수정이 끝난 후 exit로 chroot를 나갈 수 있다. 이후 꼭 mount 해준 폴더를 umount 해주는 편이 좋을듯 계속 mount되어있으면 무언가 꼬일수도 있다.
끝 - 

7. 추가 - Debootstrap rootfs 생성 관련 옵션
19.03.09



sudo apt-get install debootstrap
debootstrap 설치

mkdir qemu
sudo debootstrap --include=openssh-server,curl,tar,gcc,\
libc6-dev,time,strace,sudo,less,psmisc,\
selinux-utils,policycoreutils,checkpolicy,selinux-policy-default \
stretch qemu
데비안 이미지를 위한 유틸리티 추가

set -eux
# Set some defaults and enable promtless ssh to the machine for root.
sudo sed -i '/^root/ { s/:x:/::/ }' qemu/etc/passwd
echo 'T0:23:respawn:/sbin/getty -L ttyS0 115200 vt100' | sudo tee -a qemu/etc/inittab
printf '\nauto enp0s3\niface enp0s3 inet dhcp\n' | sudo tee -a qemu/etc/network/interfaces
echo 'debugfs /sys/kernel/debug debugfs defaults 0 0' | sudo tee -a qemu/etc/fstab
echo "kernel.printk = 7 4 1 3" | sudo tee -a qemu/etc/sysctl.conf
echo 'debug.exception-trace = 0' | sudo tee -a qemu/etc/sysctl.conf
echo "net.core.bpf_jit_enable = 1" | sudo tee -a qemu/etc/sysctl.conf
echo "net.core.bpf_jit_harden = 2" | sudo tee -a qemu/etc/sysctl.conf
echo "net.ipv4.ping_group_range = 0 65535" | sudo tee -a qemu/etc/sysctl.conf
echo -en "127.0.0.1\tlocalhost\n" | sudo tee qemu/etc/hosts
echo "nameserver 8.8.8.8" | sudo tee -a qemu/etc/resolve.conf
echo "ubuntu" | sudo tee qemu/etc/hostname
sudo mkdir -p qemu/root/.ssh/
rm -rf ssh
mkdir -p ssh
ssh-keygen -f ssh/id_rsa -t rsa -N ''
cat ssh/id_rsa.pub | sudo tee qemu/root/.ssh/authorized_keys
# Build a disk image
dd if=/dev/zero of=qemu.img bs=1M seek=2047 count=1
sudo mkfs.ext4 -F qemu.img
sudo mkdir -p /mnt/qemu
sudo mount -o loop qemu.img /mnt/qemu
sudo cp -a qemu/. /mnt/qemu/.
sudo umount /mnt/qemu
rootfs에 여러 설정들을 추가해서 이미지를 만들어 내는 스크립트

자세한 설명은 생략하거나 추후 보충



















'해킹 > kernel' 카테고리의 다른 글

CVE-2019-7304(dirty sock)  (0) 2019.03.05
Dirty Cow (CVE-2016-5195) 분석 - 2  (1) 2019.01.22
Dirty Cow (CVE-2016-5195) 분석 - 1  (0) 2019.01.22
Tags
Social