얼마 전에 Black Hat USA 2021 학회에서 가상 머신 탈출 탐지 및 방어 관련 연구(Alcatraz)를 발표하게 되었다고 말씀을 드렸는데요, 해당 연구가 QEMU와 아마존(Amazon)에서 만든 MicroVM 기술(Firecracker)을 대상으로 하고 있습니다. MicroVM 기술은 전체 시스템을 가상화하여 가상 머신(Virtual Machine, VM)을 생성하는 QEMU와 달리 컨테이너 구동을 위한 최소한의 기능만 구현한 경량(micro) 가상 머신입니다. 그래서 이름도 MicroVM이죠. QEMU보다는 최근에 나온 기술이라 많이 생소하실 텐데요, QEMU보다 공격면이 작고 좀 더 안전하기 때문에 앞으로 주목받을 것 같습니다. ^^;;;

Firecracker는 현재 https://github.com/firecracker-microvm/firecracker에서 프로젝트가 진행 중입니다. 문서가 비교적 잘 정리되어 있어서 따라 하는 것이 크게 어렵지 않은데요, 좀 더 쉽게 사용할 수 있는 방법이 있어서 정리하는 셈 치고 남겨봅니다. ^^;;

1. 우분투 환경에서 도커(Docker) 설치하기

Firecracker는 MicroVM 내부에 도커(Docker)를 구동하기 때문에 먼저 설치해 줍니다. 우분투 환경에서 도커는 아래와 같이 설치할 수 있습니다.

# 도커 패키지 설치
$> sudo apt install docker.io

# 현재 유저 계정을 도커 그룹에 추가, 그룹 추가가 완료되면 로그아웃 및 로그인 수행 필요
$> sudo useradd -aG docker ${USER}

2. 아마존(Amazon) Firecracker 다운로드 및 빌드

Firecracker의 소스코드는 이미 공개되어 있으므로, 아래와 같이 입력하여 소스코드를 받아 빌드합니다.

# 소스코드 다운로드
$> git clone https://github.com/firecracker-microvm/firecracker

# 빌드 및 링크 생성
$> cd firecracker
$> tools/devtool build
$> ln -s build/cargo_target/x86_64-unknown-linux-musl/debug/firecracker firecracker

3. Firecracker 구동용 스크립트 생성하기

Firecracker는 웹 API를 제공합니다. 그래서 curl을 이용해서 직접 커맨드를 실행해도 되지만, 너무 불필요한 작업이 많아서 아래처럼 스크립트를 만들었습니다. 이 글을 쓰게 된 이유이기도 하죠. ^-^)/

#!/bin/bash
# 00_get_kernel_and_rootfs.sh로 저장 후 chmod +x로 실행권한 부여

arch=`uname -m`
dest_kernel="hello-vmlinux.bin"
dest_rootfs="hello-rootfs.ext4"
image_bucket_url="https://s3.amazonaws.com/spec.ccfc.min/img"

if [ ${arch} = "x86_64" ]; then
    kernel="${image_bucket_url}/quickstart_guide/x86_64/kernels/vmlinux.bin"
    rootfs="${image_bucket_url}/hello/fsfiles/hello-rootfs.ext4"
elif [ ${arch} = "aarch64" ]; then
    kernel="${image_bucket_url}/quickstart_guide/aarch64/kernels/vmlinux.bin"
    rootfs="${image_bucket_url}/aarch64/ubuntu_with_ssh/fsfiles/xenial.rootfs.ext4"
else
    echo "Cannot run firecracker on $arch architecture!"
    exit 1
fi

echo "Downloading $kernel..."
curl -fsSL -o $dest_kernel $kernel

echo "Downloading $rootfs..."
curl -fsSL -o $dest_rootfs $rootfs

echo "Saved kernel file to $dest_kernel and root block device to $dest_rootfs."
#!/bin/bash
# 01_start_firecracker.sh로 저장 후 chmod +x로 실행권한 부여

echo "Remove existing socker."
rm -f /tmp/firecracker.socket

echo ""
echo "Start firecracker."
./firecracker --api-sock /tmp/firecracker.socket
#!/bin/bash
# 02_set_guest_kernel.sh로 저장 후 chmod +x로 실행권한 부여

arch=`uname -m`
kernel_path=$(pwd)"/hello-vmlinux.bin"

if [ ${arch} = "x86_64" ]; then
    curl --unix-socket /tmp/firecracker.socket -i \
      -X PUT 'http://localhost/boot-source'   \
      -H 'Accept: application/json'           \
      -H 'Content-Type: application/json'     \
      -d "{
            \"kernel_image_path\": \"${kernel_path}\",
            \"boot_args\": \"console=ttyS0 reboot=k panic=1 pci=off\"
       }"
elif [ ${arch} = "aarch64" ]; then
    curl --unix-socket /tmp/firecracker.socket -i \
      -X PUT 'http://localhost/boot-source'   \
      -H 'Accept: application/json'           \
      -H 'Content-Type: application/json'     \
      -d "{
            \"kernel_image_path\": \"${kernel_path}\",
            \"boot_args\": \"keep_bootcon console=ttyS0 reboot=k panic=1 pci=off\"
       }"
else
    echo "Cannot run firecracker on $arch architecture!"
    exit 1
fi
#!/bin/bash
# 03_set_rootfs.sh로 저장 후 chmod +x로 실행권한 부여

rootfs_path=$(pwd)"/hello-rootfs.ext4"
curl --unix-socket /tmp/firecracker.socket -i \
  -X PUT 'http://localhost/drives/rootfs' \
  -H 'Accept: application/json'           \
  -H 'Content-Type: application/json'     \
  -d "{
        \"drive_id\": \"rootfs\",
        \"path_on_host\": \"${rootfs_path}\",
        \"is_root_device\": true,
        \"is_read_only\": false
   }"
#!/bin/bash
# 04_start_microvm.sh로 저장 후 chmod +x로 실행권한 부여

curl --unix-socket /tmp/firecracker.socket -i \
  -X PUT 'http://localhost/actions'       \
  -H  'Accept: application/json'          \
  -H  'Content-Type: application/json'    \
  -d '{
      "action_type": "InstanceStart"
   }'

4. Firecracker 실행

최초 실행 시에는 커널(Kernel) 파일과 루트파일시스템(rootfs)가 없으므로, 00_get_kernel_and_rootfs.sh를 실행해서 이미지를 다운로드할 필요가 있습니다. 한 번 다운로드한 뒤에는 이미지를 계속 활용하면 되니 01_start_firecracker.sh부터 실행하면 됩니다. 여기서 주의할 점이 있는데요, 01_start_firecracker.sh 스크립트를 실행하면 터미널이 계속 대기상태로 있다는 겁니다. MicroVM이 시작되기를 기다리는 건데요, 이후에 다른 터미널을 열어서 스크립트를 실행하면 대기상태가 풀리고 로그인 쉘이 표시되기 때문에 걱정 안 하셔도 됩니다. 스크립트를 통해 Firecracker를 실행하는 과정은 아래와 같습니다.

# 커널 및 루트파일시스템 이미지를 다운로드하는 단계이므로, 최초 1회만 실행
$> ./00_get_kernel_and_rootfs.sh

# Firecracker 실행, 이때 터미널이 대기상태로 빠지므로 다른 터미널을 열어서 다음 스크립트를 실행
$> ./01_start_firecracker.sh

# MicroVM용 커널 설정
$> ./02_set_guest_kernel.sh

# MicroVM용 루트파일시스템 설정
$> ./03_set_rootfs.sh

# MicroVM 실행
$> ./04_start_microvm.sh

위의 스크립트를 차례로 실행하면, 01_start_firecracker.sh를 실행하여 대기중이던 터미널에서 아래와 같이 부팅이 진행되고 로그인 쉘이 표시됩니다. 기본 로그인 정보는 ID/PWD가 root/root로 설정되어 있습니다. 로그를 보니 Alpine 리눅스를 기반으로 MicroVM 이미지를 생성한 것 같네요.

[    0.000000] Linux version 4.14.225 (ubuntu@ip-172-31-11-16) (gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)) #1 SMP Fri Mar 12 16:47:14 UTC 2021
[    0.000000] Command line: console=ttyS0 reboot=k panic=1 pci=off root=/dev/vda rw virtio_mmio.device=4K@0xd0000000:5
[    0.000000] x86/fpu: Supporting XSAVE feature 0x001: 'x87 floating point registers'
[    0.000000] x86/fpu: Supporting XSAVE feature 0x002: 'SSE registers'
[    0.000000] x86/fpu: Supporting XSAVE feature 0x004: 'AVX registers'
[    0.000000] x86/fpu: Supporting XSAVE feature 0x008: 'MPX bounds registers'
[    0.000000] x86/fpu: Supporting XSAVE feature 0x010: 'MPX CSR'
[    0.000000] x86/fpu: xstate_offset[2]:  576, xstate_sizes[2]:  256
[    0.000000] x86/fpu: xstate_offset[3]:  832, xstate_sizes[3]:   64
[    0.000000] x86/fpu: xstate_offset[4]:  896, xstate_sizes[4]:   64
[    0.000000] x86/fpu: Enabled xstate features 0x1f, context size is 960 bytes, using 'compacted' format.
[    0.000000] e820: BIOS-provided physical RAM map:

... omitted ...

Starting default runlevel

Welcome to Alpine Linux 3.8
Kernel 4.14.225 on an x86_64 (ttyS0)

localhost login:

그럼 즐거운 저녁 되세요 >ㅁ<)-b

가상화 기술의 핵심인 하이퍼바이저(hypervisor)를 샌드박스에 가두어 가상 머신 탈출(virtual machine escape)을 방어하는 연구가 Black Hat USA 2021에 채택되었습니다. Black Hat은 본 학회인 Black Hat USA와 지역 학회인 Black Hat Asia, Black Hat Europe으로 나누어지는데, 이제야 겨우 본 학회에 발을 들였네요. 그동안 계속 떨어지는 바람에 Asia와 Europe만 다녔거든요. 쿨럭...;; 네트워킹하기 좋은 기회인데 코로나 때문에 온라인으로 발표할 예정이라 많이 아쉽긴 합니다. ^^;;;

Black Hat USA 2021 - 발표 내용
Black Hat USA 2021 - 발표자 소개

가상 머신 탈출(virtual machine escape)은 가상 머신에서 동작하는 악성코드(malware)가 가상 머신을 빠져나와 시스템 전체, 즉 호스트(host)와 게스트(guest)에 영향을 미치고 악의적인 행위를 하는 것을 뜻합니다. 가상 머신 탈출은 특히나 Amazon과 같은 클라우드 사업자(cloud vendor)에게 치명적인데, 서비스의 특성상 가상 머신이 사용자에게 제공되고 임의의 코드가 내부에서 실행되기 때문입니다. 악의적인 사용자라면 가상 머신을 임대한 후, 가상 머신의 취약점을 이용해서 탈출할 수 있는 것이죠.

가상 머신 탈출은 이처럼 파급효과가 크기 때문에 #BlackHat 컨퍼런스에 단골로 등장하고 있습니다. 올해 5월에 열린 Black Hat Asia 2021에도 가상 머신 탈출 관련 발표가 있었구요. 앞으로도 문제가 있을 것 같아서 실용적으로 문제를 해결하고자 여러 가지 시도를 했는데, 많은 분들이 도와주셔서 좋은 결과가 나온 것 같습니다. 필드 적용성을 높이려고 현재 동작 중인 시스템은 최대한 수정하지 않았는데, 클라우드 사업자들에게 도움이 되면 좋겠네요. ^^)/

연구 내용이 조금 복잡해서 주요 내용만 간단히 소개해 드리면, 하이퍼바이저는 가상 머신과 직접적으로 연결되어 있습니다. 다수의 가상 머신을 통제하고 서비스를 제공해야 하기 때문이죠. 그런데, 하이퍼바이저는 꽤나 높은 권한에서 동작하므로, 하이퍼바이저에 취약점을 이용해서 가상 머신을 탈출할 수 있습니다. 이를 막고자 제가 만든 경량 하이퍼바이저로 샌드박스(sandbox)를 구성하고, 기존 하이퍼바이저의 권한을 낮춘 후 샌드박스에서 구동하는 것이 핵심 아이디어입니다. 샌드박스를 위해 중첩 가상화(nested virtualization)를 직접 구현했고, 발생하는 부하를 줄이기 위해 Intel 프로세서의 VPID, VMCS Shadowing 같은 기술도 같이 활용했는데... 이 부분은 Black Hat USA 2021 발표와 함께 자세히 말씀드리겠습니다.

끝으로, 항상 지지해 주시는 본부장님과 실장님을 비롯한 동료분들께 감사드립니다. 여러분들의 도움이 없었다면 아이디어도 발굴하지 못했을 겁니다. 앞으로도 많이 부탁드립니다. ^^)-b

ps) 발표는 아래에서 확인하실 수 있습니다.

https://www.blackhat.com/us-21/briefings/schedule/index.html?fbclid=IwAR3TWHTC1UXsa54x9VcdpG82Jdgd0QWgFYgAc5LWqUNxd6CWR6Hp9Dcpvqs#alcatraz-a-practical-hypervisor-sandbox-to-prevent-escapes-from-the-kvmqemu-and-kvm-based-microvms-22875 

 

Black Hat

Black Hat

www.blackhat.com

 

리눅스 커널(Linux Kernel) 관련 일을 하다 보니 가끔씩 보안 패치를 보내기도 하는데요, 메일링 리스트에 패치를 보낼 때마다 리뷰어(Reviewer)며 메인테이너(Maintainer)며 기타 구경꾼들까지 달려들어서 코드를 검토해주는 바람에 거절되는 경우도 많이 있습니다. 특히 최근에는 AMD사의 펌웨어 TPM(fTPM) 관련 패치가 수십 번의 핑퐁 끝에 거절당해서 트라우마까지 생겼거든요. ㅠㅠ 그런데 그런 일보다 더 황당한 일이 있었네요. @0@a..

미네소타 대학에서 논문(On the Feasibility of Stealthily Introducing Vulnerabilities in Open-Source Software via Hypocrite Commits)을 쓰기 위해 보안 취약점이 들어있는 패치를 여러 번에 걸쳐서 메일링 리스트에 보냈고, 그중에 일부는 이미 안정 버전(Stable)에까지 반영이 되어서 급히 제거했다고 합니다. 리눅스 커널은 이미 IoT 장비부터 서버, 우주선에까지 사용되고 있기 때문에 파급효과가 엄청난데... 아무리 논문 욕심이 있었다고 해도 선을 넘은 것 같네요. 메일링 리스트를 보면 너무 길고 양이 많기 때문에, 전체 이야기에 관심이 있다면 ZDNet 기사(Greg Kroah-Hartman bans University of Minnesota from Linux development for deliberately buggy patches)를 보시는 것이 좋을 것 같습니다. ^^;;;

미네소타 대학의 불순한 커널 패치에 대한 ZDNet 기사

과연 얼마나 정교하게 패치를 보냈길래 제대로 검증이 되지 않았나 몇 개를 살펴봤는데요, 꽤나 별 것 아닌 것처럼 보냈습니다. 성공적으로 병합(Merge)된 패치(https://lore.kernel.org/lkml/20210407000913.2207831-1-pakki001@umn.edu/)를 하나 보면 아래와 같습니다.

From: Aditya Pakki <pakki001@umn.edu>
To: pakki001@umn.edu
Cc: Santosh Shilimkar <santosh.shilimkar@oracle.com>,
	"David S. Miller" <davem@davemloft.net>,
	Jakub Kicinski <kuba@kernel.org>,
	netdev@vger.kernel.org, linux-rdma@vger.kernel.org,
	rds-devel@oss.oracle.com, linux-kernel@vger.kernel.org
Subject: [PATCH] net/rds: Avoid potential use after free in rds_send_remove_from_sock
Date: Tue,  6 Apr 2021 19:09:12 -0500
Message-ID: <20210407000913.2207831-1-pakki001@umn.edu> (raw)

In case of rs failure in rds_send_remove_from_sock(), the 'rm' resource
is freed and later under spinlock, causing potential use-after-free.
Set the free pointer to NULL to avoid undefined behavior.

Signed-off-by: Aditya Pakki <pakki001@umn.edu>
---
 net/rds/message.c | 1 +
 net/rds/send.c    | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/net/rds/message.c b/net/rds/message.c
index 071a261fdaab..90ebcfe5fe3b 100644
--- a/net/rds/message.c
+++ b/net/rds/message.c
@@ -180,6 +180,7 @@ void rds_message_put(struct rds_message *rm)
 		rds_message_purge(rm);
 
 		kfree(rm);
+		rm = NULL;
 	}
 }
 EXPORT_SYMBOL_GPL(rds_message_put);
diff --git a/net/rds/send.c b/net/rds/send.c
index 985d0b7713ac..fe5264b9d4b3 100644
--- a/net/rds/send.c
+++ b/net/rds/send.c
@@ -665,7 +665,7 @@ static void rds_send_remove_from_sock(struct list_head *messages, int status)
 unlock_and_drop:
 		spin_unlock_irqrestore(&rm->m_rs_lock, flags);
 		rds_message_put(rm);
-		if (was_on_sock)
+		if (was_on_sock && rm)
 			rds_message_put(rm);
 	}
 
-- 
2.25.1

위의 내용을 보면 기존 소스가 메모리 해제 후 재사용(Use After Free, UAF) 문제가 있어서 이를 수정한다는 내용입니다. 그런데 실제 패치에 추가된 내용을 보시면, rds_message_put() 함수에서 rm = NULL;을 추가해서 rm이라는 변수에 NULL을 넣고 난 다음, rds_send_remove_from_sock() 함수에서 if 문에 rm 변수가 NULL이 아닌 것을 검사하게 만들었습니다. 사실 rm이라는 변수는 rds_send_remove_from_sock() 함수에 있는 지역변수인데요, 이 지역변수를 다른 함수에서 넘겨받아 값을 NULL로 바꿨지만 사실 눈속임에 불과합니다. 즉 쓸모없는 수정이라는 말입니다.

조금 어려운 이야기인데... 다른 함수에서 rm에 NULL을 넣으려면 rm 자체가 일단 싱글 포인터(Single Pointer)이기 때문에 더블 포인터(Double Pointer)로 rm 변수의 주소 값을 넘겨야 하거든요(포인터는 항상 어렵습니다 ㅠㅠ). 리눅스 커널 개발자들은 위에서 보시는 것처럼 패치 형태로 전달하는데요, 패치는 수정된 코드의 위아래 일부분만 보여주기 때문에 자칫 잘못하면 실수를 할 수 있습니다. 이런 부분을 노리고 리뷰가 얼마나 잘되는지를 판단하려고 저런 패치를 보냈다고 생각하니, 의도가 얼마나 불순했는지를 알 수 있습니다. 그래도 이런 패치는 양반입니다. 아무런 영향을 미치지 않으니까요.

그럼 이제 문제의 패치를 보겠습니다. 실제로 보안 문제를 일으키는 패치인데요, 심지어 가명으로 만든 계정으로 보낸 걸로 추측됩니다. 왜냐하면 제임스 본드(James Bond)거든요. ^^;;; 쿨럭..;;; 다행히도 이 패치(https://lore.kernel.org/lkml/20200821070537.30317-1-jameslouisebond@gmail.com/)는 거절되었습니다.

From: James Bond <jameslouisebond@gmail.com>
To: jameslouisebond@gmail.com
Cc: Miquel Raynal <miquel.raynal@bootlin.com>,
	Richard Weinberger <richard@nod.at>,
	Vignesh Raghavendra <vigneshr@ti.com>,
	Arnd Bergmann <arnd@arndb.de>, Ryan Jackson <rjackson@lnxi.com>,
	David Woodhouse <dwmw2@infradead.org>,
	linux-mtd@lists.infradead.org, linux-kernel@vger.kernel.org
Subject: [PATCH] mtd: ck804xrom: fix missing pci device put in error paths
Date: Fri, 21 Aug 2020 02:05:36 -0500
Message-ID: <20200821070537.30317-1-jameslouisebond@gmail.com> (raw)

pci_dev_get increases the refcount of "pdev".
In the error paths, pci_dev_put should be called
to handle the "pdev" and decrease the corresponding refcount.

Fixes: 90afffc8bd79 ("[MTD] [MAPS] Support for BIOS flash chips on the nvidia ck804 southbridge")
Signed-off-by: James Bond <jameslouisebond@gmail.com>
---
 drivers/mtd/maps/ck804xrom.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/mtd/maps/ck804xrom.c b/drivers/mtd/maps/ck804xrom.c
index 460494212f6a..16af8b5ee653 100644
--- a/drivers/mtd/maps/ck804xrom.c
+++ b/drivers/mtd/maps/ck804xrom.c
@@ -195,6 +195,7 @@ static int __init ck804xrom_init_one(struct pci_dev *pdev,
 	if (!window->virt) {
 		printk(KERN_ERR MOD_NAME ": ioremap(%08lx, %08lx) failed\n",
 			window->phys, window->size);
+		pci_dev_put(pdev);
 		goto out;
 	}
 
@@ -222,6 +223,7 @@ static int __init ck804xrom_init_one(struct pci_dev *pdev,
 
 		if (!map) {
 			printk(KERN_ERR MOD_NAME ": kmalloc failed");
+			pci_dev_put(pdev);
 			goto out;
 		}
 		memset(map, 0, sizeof(*map));
@@ -295,6 +297,7 @@ static int __init ck804xrom_init_one(struct pci_dev *pdev,
 		if (mtd_device_register(map->mtd, NULL, 0)) {
 			map_destroy(map->mtd);
 			map->mtd = NULL;
+			pci_dev_put(pdev);
 			goto out;
 		}
 
-- 
2.17.1

위의 패치를 보시면 매번 goto out 예외 처리 루틴으로 넘어가기 전에 pci_dev_put(pdev)를 호출하는 부분을 추가했습니다. 위의 패치 코드만 보면 추가된 부분이 필요할 것같은 느낌이 듭니다. 하지만, 실제로 goto out 부분을 보면 아래처럼 ck804xrom_cleanup(window)를 부르고 그 내부에서 pci_dev_put(pdev)를 다시 호출합니다. 따라서, 제임스 본드(ㅡ_ㅡ;;;)가 보낸 패치를 넣게 되면 pci_dev_put(pdev)를 두 번 호출하게 되고, 보안 취약점 중에 하나인 중복 해제(Double Free) 버그가 생깁니다. 쉽게 말씀드리면, 같은 메모리를 두 번이나 해제해서 malloc/free에 사용하는 힙(Heap) 영역이 꼬이고, 악의적인 사용자가 이를 이용하면 다양한 문제가 발생합니다.

 out:
	/* Free any left over map structures */
	kfree(map);

	/* See if I have any map structures */
	if (list_empty(&window->maps)) {
		ck804xrom_cleanup(window);
		return -ENODEV;
	}
	return 0;
}

이런 류의 패치 시도가 다양하게 있었던 것 같은데... 정말 대단한 집념인 것 같습니다. 그 와중에 해명(https://lore.kernel.org/linux-nfs/YH%2FfM%2FTsbmcZzwnX@kroah.com/)을 하기는 했는데, 자기는 정적 분석 도구(Static Analysis Tool)가 알려주는 대로 수정해서 패치를 보냈다고 하더라구요. 이미 아시는 분도 계시겠지만, 정적 분석 도구가 저런 리포트를 만들어냈다고 보기는 좀 어려울 것 같고, 다분히 사람이 악의적인 의도를 가지고 만들었다고 판단하는 게 옳을 것 같네요. ^^;;;

정말 세상에 별일이 다 있네요. 제가 쓰는 소프트웨어에 더 주의를 기울여야겠습니다.

그럼 즐거운 밤 되세요 >ㅁ<)/~

Black Hat Asia 2021이 5월 4일부터 7일까지 온라인으로 열릴 예정입니다. 온라인임에도 불구하고 많은 분들이 흥미로운 주제를 제출해주셨는데요, 리뷰를 통해 약 37개의 주제가 선정되었습니다. 선정된 목록은 https://www.blackhat.com/asia-21/briefings/schedule/index.html에서 보실 수 있습니다.

Black Hat Asia 2021 발표 목록

현재 설문조사도 같이 진행 중인데요, 발표 목록 중에서 가장 기대되는 발표를 아래 링크에 가셔서 답변을 달아주시면 추첨을 통해 Black Hat Asia 2021 학회 티켓을 드린다고 합니다.
설문조사 링크: https://www.linkedin.com/posts/kwekyewhong_bhasia-cybersecurity-infosec-activity-6780039664008081409-pj0I/

재미있는 주제가 많으니 여유가 되실 때 설문조사에 참여하시고 학회 티켓도 얻으시면 좋을 것 같습니다.

그럼 Black Hat Asia 2021에서 뵙겠습니다. ^-^)/

Black Hat Asia 2021 설문조사

데비안(Debian)이나 우분투(Ubuntu)가 엄청 발전해서 쓰기가 많이 좋아진 것은 사실이지만, 조금만 깊게 들어가면 여전히 손볼 대가 많습니다. ^^;;; 이번에는 커널을 직접 컴파일했을 때 흔히 발생하는 블랙 스크린, 즉 데스크탑 화면이 표시되지 않고 검은색 바탕에 화면 좌측 상단에 커서만 깜빡이는 현상 처리 방법입니다.

이런 상황은 보통 Nvidia나 ATI에서 제공하는 3rd party 드라이버를 사용하는 경우에 발생합니다. 저는 오래된 Nvidia 그래픽카드를 쓰고 있는데요, 아래처럼 패키지를 다시 설치하는 방법으로 해결하고 있습니다. 새로운 커널에서 로딩할 Nvidia 드라이버가 없어서 발생하는 문제거든요. 

# Nvidia의 커널 모듈을 다시 컴파일해주는 패키지입니다.
$> sudo apt reinstall nvidia-kernel-dkms

커널 컴파일 후 비슷한 문제가 생길 때마다 스택 오버플로우(Stack Overflow) 사이트에서 찾아 쓰다가, 생각난 김에 정리해둡니다.

그럼 즐거운 밤 되세요 >ㅁ<)/~

언제부터인지 데비안(Debian)이나 우분투(Ubuntu) 커널을 빌드하고 모듈을 인스톨하면 디버그 심볼이나 기타 정보가 엄청 생성돼서 파일이 너무 커지는 일이 발생했습니다. 사실 모듈이 커지는 것쯤은 별일이 아닐 수 있지만, 문제는 램디스크, 즉 initrd를 생성할 때 발생합니다. 모듈이 너무 커서 initrd 사이즈가 500MB까지 증가하거든요. ^^;;;

저는 커널 보안기능을 주로 개발하기 때문에 수시로 커널을 빌드하는데요, 그러다 보면 커널 모듈 크기 때문에 initrd 빌드 시간이 길어져서 빌드 완료까지 대기하는 전체 시간(이라고 쓰고 노는 시간이라고 읽는... 쿨럭..;;)도 길어지더라구요. 그래서 방법을 찾다 보니 아래처럼 하면 된다는 걸 발견했습니다.

# 먼저 커널과 커널 모듈을 빌드합니다.
$> make -j <cpu 갯수>
$> make modules -j <cpu 갯수>

# 불필요한 정보를 제거한 후 모듈을 설치하고 커널을 설치합니다.
$> make INSTALL_MOD_STRIP=1 modules_install -j <cpu 갯수>
$> make install

이제 시간을 효율적(?!)으로 쓸 수 있겠네요!

그럼 즐거운 저녁 되세요 ㅠㅠ)/

최근 다시 여유가 생겨서 그동안에 쌓아두었던 책을 처리(?!)하고 있습니다. ^^;;; 첫 번째 책은 "프리-오픈소스 소프트웨어 혁명의 역사"인데요, 가벼운 마음으로 읽기 시작했는데... 생각보다 흥미진진하고 생각할 거리가 많은 책이었습니다. 비슷한 책으로는 한빛미디어에서 나온 "유닉스의 탄생: 세상을 바꾼 운영체제를 만든 천재들의 숨은 이야기"가 있는 것 같습니다. "유닉스의 탄생"이 좀 더 말랑말랑한... 그러니까 할아버지께 옛날이야기를 듣는 듯한 느낌이라면, 이 책은 뭐랄까요... 사실에 입각한 논문을 읽는... 아주 건조한 느낌이 듭니다. 쿨럭..;;;

프리-오픈소스 소프트웨어 혁명의 역사-지식함지

그도 그럴 것이, 이 책은 그동안 다양한 방법으로 정리를 시도했던 유닉스, 프리 소프트웨어, 오픈소스 소프트웨어 간의 관계를 한 데 묶을 목적으로 나왔기 때문에... 수많은 참고자료를 바탕으로 여러 사건들을 시간에 흐름에 따라 정리하고 있거든요. 그래서 첨가물, 전문용어로 MSG를 최대한 뺀 것이죠. ^^;;;

재미는 좀 덜하지만, 벨(Bell) 연구소의 유닉스(Unix), 리처드 스톨먼(Richard Matthew Stallman)을 비롯한 해커들의 자유 소프트웨어(free software) 및 GNU 활동, 라이너스 토발즈(Linus Benedict Torvalds)의 리눅스(Linux), 이안 머독(Ian Murdock)의 데비안(Debian)의 관계를 아주 명료하게 풀어내 줘서 저한테는 큰 도움이 되었습니다. 제가 또 이런 인과나 연관 관계에 흥미가 많거든요. ^^)/

알고 있던 낱개의 사건이 하나로 이어져서 서로 영향을 주고받았다는 것을 알고 나니... 이 책의 진가가 눈에 보였습니다. 일례로 리눅스에 관심이 많은 분이라면 데비안(Debian)을 한 번쯤은 들어보셨을 텐데요, 스톨먼이 데비안을 지지했다는 사실은 이 책을 보고 처음 알았습니다. ^^;;;

혹시 자유 소프트웨어, 오픈소스, 리눅스 등에 관심이 있다면, 꼭 한 번 읽어보시길 바랍니다.

그럼 즐거운 저녁 되세요 >ㅁ<)/

오늘 출장을 다녀오다 내리는 비에 갑자기 옛날 생각이 났습니다.

대학에 입학했을 때,
"이제 아들 다 키웠네." 하시면서 환하게 웃으시던 그 날의 어머니 말입니다.
제겐 그저 그런 날이었던...

대학 졸업하고 기업에 입사했을 때,
"이제 아들 덕 좀 보겠네" 하시면서 등을 토닥여주시던 그 날의 어머니 말입니다.
제겐 그저 그런 날이었던...

잘 다니던 기업을 퇴사하고 이직을 했을 때,
"조금만 더 버티면 좋겠지만, 어디 가도 잘할 거니까..." 하시면서 걱정스레 웃으시던 그 날의 어머니 말입니다.
제겐 그저 그런 날이었던...

이직한 회사에서 적응하느라 발버둥 칠 때,
"아들아, 암이라는데 너무 늦었단다. 하지만 수술하고 항암 치료하면 나아질 수도 있단다." 하시면서 떨리는 목소리로 전화하시던 그 날의 어머니 말입니다.
지금은 그저 그런 날이 된...

위험한 고비를 넘기셨을 때,
"이제 괜찮다. 걱정 말고 일해라." 하시면서 가쁜 호흡을 몰아쉬시던 그 날의 어머니 말입니다.
지금은 겨우 그저 그런 날이 된...

아이들과 병원을 방문했을 때,
"아이고, 멀리서 오느라 고생이 많다. 다음에는 오지 마라." 하시면서 불편한 몸으로 매번 아이들의 안아주시던 그 날의 어머니 말입니다.
지금도 잊을 수 없는...

어머니를 고이 장지에 모셨을 때,
"신세 진 건 항상 갚아라. 그리고 뭐든 열심히 해라." 하시던 평소 어머니가 떠올라 비석을 한참 쳐다보던 그 날의 저 말입니다.
아직도 생생한...

비를 맞고 집으로 돌아와 서로 투닥거리는 아이들과 저녁을 먹다가,
문득 아이들의 얼굴에서 어머니 모습이 겹쳐 잠시 멈칫했던 오늘도, 그리고 아직 생생한 다른 날도...

어머니께서 가시던 그 날 유난히 가벼웠던 산바람처럼,
담담하게 떠올릴 수 있는 그저 그런 날이 어서 오면 좋겠습니다.

코로나로 어수선한데,
가정 모두 평안하시길 바랍니다. ^^

요즘 진행 중인 프로젝트가 있는데, 어찌하다 보니 디버깅을 위해서 버추어박스의 소스코드를 빌드할 일이 생겼습니다. 전체적인 빌드 과정은 https://www.virtualbox.org/wiki/Linux%20build%20instructions에 잘 정리되어 있긴 한데요... 아시겠지만 실제로 빌드를 해보면 다양한 문제에 봉착하게 되죠... 쿨럭..;;; 오라클 버추어박스의 소스코드를 빌드를 하다 보면 아래와 같은 오류가 발생합니다.

/home/user/project/VirtualBox-6.1.16/doc/manual/en_US/user_KnownIssues.xml:6: warning: failed to load external entity "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"
]>
  ^
/home/userproject/VirtualBox-6.1.16/doc/manual/en_US/user_ChangeLog.xml:6: warning: failed to load external entity "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"
]>
  ^
/home/user/project/VirtualBox-6.1.16/doc/manual/en_US/user_ThirdParty.xml:6: warning: failed to load external entity "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"
]>
  ^
/home/user/project/VirtualBox-6.1.16/doc/manual/en_US/user_PrivacyPolicy.xml:6: warning: failed to load external entity "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"
]>
  ^
/home/user/project/VirtualBox-6.1.16/doc/manual/en_US/user_Glossary.xml:6: warning: failed to load external entity "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"
]>
  ^
warning: failed to load external entity "/usr/share/xml/docbook/schema/dtd/4.5/docbookx.dtd"
Could not parse DTD /usr/share/xml/docbook/schema/dtd/4.5/docbookx.dtd
kBuild: Validating /home/user/project/VirtualBox-6.1.16/doc/manual/en_US/UserManual.xml

실제로 docbookx.dtd 파일에 문제가 있는 건 아닌 것 같고, 소스코드 tarball에서 파일이 한 개 없어서 발생한 오류였습니다. 아래와 같이 입력해서 빈 파일을 만들어주면 실제로 빌드가 잘 됩니다.

user@user:~/project/VirtualBox-6.1.16$ touch out/linux.amd64/release/obj/manual/en_US/validatemanual.run
user@user:~/project/VirtualBox-6.1.16$ chmod +x out/linux.amd64/release/obj/manual/en_US/validatemanual.run

뭔가 일부러 빠뜨렸을 것 같지는 않은데... 그리고 벌써 몇년이나 지난 버그인데, 아직도 고쳐지지 않았더라고요. 여하튼 빌드가 잘 되어 천만다행입니다. ^^;;;

그럼 좋은 밤 되세요.

pip로 재미있는(?) 툴을 설치할 일이 있었습니다. 그런데 이게 웬일? 데비안(Debian)에는 pip가 기본적으로 설치되어 있지 않더라고요. 사실 pip는 파이썬으로 만든 툴을 설치하는 용도로 많이 사용했던지라 좀 당황했습니다. 허나, 찾으려고 하면 또 찾아지는 법이더라고요. ^^;; 데비안 계열에서 설치하려면 아래와 같이 입력하면 됩니다.

$> sudo apt install python-pip python-setuptools python3-pip python3-setuptools

$> sudo apt install python-pip python-setuptools python3-pip python3-setuptools

그럼 즐거운 리눅스 생활되세요 >ㅁ<)/

i3 윈도우 매니저는 불친절하기 그지없는데요, 배경 이미지를 설정하는 것조차 커맨드로 처리해야 합니다. ㅠㅠ feh라는 도구를 활용해서 말이죠. 아래는 ~/.config/i3/config 파일을 수정해서 매번 로그인할 때마다 배경을 로딩하도록 해주는 코드입니다.

# 절대 경로와 함께 아래 라인을 추가해줘야 함
exec --no-startup-id feh --bg-scale /home/user/사진/background.jpg

아아... 정말 불친절하기 그지없네요. 뭔가 한 방에 다 깔끔하게 처리해주는 패키지가 있으면 좋을텐데 말이죠. ^^;;;
그럼 좋은 하루 되세요 >ㅁ<)/

2021년에도 Black Hat Aisa 리뷰 보드로 활동을 하게 되어 보게 된 논문인데, 보안 관련 3대 학회로 손꼽히는 ACM CCS 2020에 채택된 논문입니다. 제목에서도 알 수 있듯이 리눅스 커널 공격에 사용되는 elastic object, 즉 우리말로 옮기면 크기가 가변적인 객체와 관련해서 체계화 연구를 수행한 내용입니다.

Elastic object의 핵심은 구조체 내부에 버퍼나 버퍼의 주소를 가리키는 포인터가 있고, 그리고 길이를 나타내는 필드가 있다는 점입니다. 이러한 특징으로 인해 elastic object의 버퍼 포인터나 길이를 조절하면 임의의 커널 메모리 읽기(arbitrary read)나 범위를 넘겨 읽기(out-of-bound read, OOB read)가 가능하고, KASLR(Kernel Address Space Layout Randomization)을 무력화할 수 있는 커널 함수 포인터나 Stack canary 등을 읽어낼 수 있죠.

Elastic object 공격으로 인한 영향 - 출처: A Systematic Study of Elastic Objects in Kernel Exploitation

사실 이러한 객체는 흔한 듯하면서도 흔치 않은데요, 커널 구조체의 대부분은 크기가 고정되어 있거든요. ^^;;; 이 논문에서는 아래처럼 elastic object를 찾아서 리스트를 만들어 놓고, 이와 관련해서 커널 데이터를 유출할 수 있는 함수들도 정리해두었습니다. 이 과정에서 정적/동적 분석 기법을 활용했다고 하는데, 오탐을 줄이기 위해 함수 리스트 같은 경우는 수동으로(manual) 선정했다고 하는군요.

Elastic kernel objects - 출처: A Systematic Study of Elastic Objects in Kernel Exploitation 
커널에서 유저로 데이터를 전달할 수 있는 함수 목록 - 출처:A Systematic Study of Elastic Objects in Kernel Exploitation

개인적으로 이 논문의 의의는 elastic object 관련 내용을 체계화했다는 것에 있다고 생각합니다. 임의 메모리 읽기나 범위 넘겨 읽기 같은 경우는 논문의 위협 모델(thread model)에도 나와 있지만, 공격자가 이미 임의 메모리 쓰기 취약점(arbitrary memory write)을 가지고 있다고 가정하기 때문에 영향력이 크지 않다고 생각합니다. 사실 임의 메모리 쓰기 취약점이 임의 메모리 읽기 취약점보다 귀하기 때문에 이런 취약점이 더 영향력이 크고 더 유용하거든요. 물론, 제대로 쓰기 위해서는 논문에서 기술한 것처럼, 임의 메모리 읽기 취약점을 이용해서 KASLR을 무력화해야 좀 더 공격 성공 확률이 올라가긴 하지만요. ^^;;;

해결책(mitigation)의 경우도 격리(isoloation) 기법을 사용했는데요, 이 방법 또한 일반적으로 활용하는 방법입니다. 격리를 하면 elastic object만 따로 떨어지는데, 인접해있는 kernel object를 통해 주요 정보를 유출할 수 있는 기회가 적어지긴 합니다. 다만, 메모리 풀(pool)을 분리해야 하므로 코드 수정이 필요하고, 이로 인한 부수효과(side effect)가 어떠한 형태로 나타나는지 심도 깊은 평가도 필요하지요. 논문에서도 성능 등의 여러 관점에서 평가를 하는데요, 실험실(lab) 환경과 실제 운영 환경은 다르기 때문에 논문의 결과가 실제 필드로 적용되기까지는 많은 시간이 필요합니다. ^^;; 참고로 최근에 도입된 꽤 유명하고 대표적인 격리 기법에는 KPTI(Kernel Page Table Isolation)가 있습니다. 바로, 세간을 떠들썩하게 한 멜트다운(meltdown) 취약점을 막기 위해 도입한 것이죠.

리뷰보드 활동 때문에 새해 첫날부터 머리가 아프... 여튼 흥미로운 논문이었습니다.

그럼 새해 복 많이 받으세요!!

저는 여전히 하고 싶은 일을 하면서 살고 있습니다 -1편,2편,3편,4편,5편, 6편에 이은 7편입니다. 이미 여러 번 말씀을 드려서 아시는 분은 다 아시겠지만, 누군가가 "애기 낳고 키우다 보면 꿈은 자연스럽게 멀어지고 퇴물이 되게 되어 있어"라고 해서 아닐 수도 있다는 걸 보여주기 위해 시작한 글입니다. ^^;;; 애기 낳은 지 벌써 10년이 다 되어 가지만 초심을 조금 잃은(?!) 것 말고는 꾸준히 하고 있습니다. 쿨럭..;;; 사실 1년마다 한 번씩 쓰고 있었는데, 2019년은 무슨 일이 있었는지 모르겠지만 그냥 지나쳐 갔더라구요. 그래서 이번 7편은 2019년과 2020년에 있었던 일을 같이 정리해볼까 합니다.

첫 번째는 유럽 최대 보안 컨퍼런스인 Black Hat Europe 2019에 마이크로소프트사의 BitLocker 취약점 관련 연구를 발표한 것입니다. 사실 BitLocker 내부의 취약점을 발견한 것은 아니구요, BitLocker가 사용하는 암복호화용 키(key)를 보호하는 TPM(Trusted Platform Module)의 취약점을 이용하여 BitLocker를 해독하는 연구였습니다. 굉장히 복잡한 내용이라 어떻게 하면 들으시는 분이 흥미를 잃지 않도록 할까 고민을 많이 했는데, 발표자료를 흥미롭게 봐주셔서 두 번째 이벤트로 이어졌습니다. ^^;;;

 Black Hat Europe 2019에 발표한 BitLocker 관련 논문

 

두 번째는 아시아 최대 보안 컨퍼런스인 Black Hat Asia 2020에 초청된 겁니다. 앞서 말씀드린 Black Hat Europe 2019의 발표가 반응이 좋아서 Black Hat Asia 2020에 초청되었는데요, 온라인으로 처음 시도된 학회라 어색하긴 했지만 나름대로 잘 끝냈습니다. 그리고 초청 논문과 동시에 Black Hat Asia 학회의 리뷰 보드로 선정되어 본격적인 활동을 시작하기도 했죠. ^^;; 꿈에 그리던 자리라서 긴장하며 리뷰를 했는데요, 100개가 넘는 발표 내용을 검토하느라 시력이 급감했습니다. 이미 Black Hat Europe 2019 발표를 위해 며칠 동안 철야를 했던지라 눈 앞이 가물가물했는데, 발표 자료를 리뷰하느라 무리했더니 노안이 시작되었습니다. 쿨럭..;;; 이건 좀 많이 슬프네요. 어설프게 진행되고 있어서 멀리 있는 것도 안 보이지만, 가까이 있는 것도 초점이 잘 안 맞아요. ㅠㅠ)/ 다들 눈 건강 챙기세요!

Black Hat Asia Review Board로 선정

 

세 번째는 HITB+CyberWeek 2020과 OWASP Jakarta에 초청되어 TPM 관련 내용을 소개드렸던 겁니다. 아무래도 코로나 때문에 온라인으로 컨퍼런스가 열리다 보니 지리적인 위치와 관계없이 가벼운 마음으로 발표를 수락할 수 있었던 것 같습니다. 시차가 좀 나서 고생했던 것만 제외하면, 이미 온라인 컨퍼런스와 발표를 Black Hat Asia 2020에서 경험했던지라 별 이슈가 없이 잘 끝났습니다. HITB+Cyberweek에서는 감사의 표시로 조그만 선물을 보내줬는데... 연말에 받아서 지금은 어디에 있는지도 잘 모르겠네요. 쿨럭..;;;

HITB+CyberWeek 기념품

 

마지막으로는 2020년 한 해 동안 작은 과제를 이끌면서 처음부터 마무리까지 다 경험해봤다는 겁니다. 지금까지는 과제를 돕고 과제 내부의 아이템을 개발하는 보조 역할을 했는데요, 올해는 직접 방향을 제시하고 리딩하는 역할까지 맡아서 했습니다. 그.랬.더.니. 마무리를 잘 하긴 했지만... 올 한 해가 너무 바빴고 개인적으로도 여유가 너무 없었습니다. ㅠㅠ)/ 내년에는 이런 일이 없도록 동료들과 잘 일을 분배(?!)해서 해야 할 것 같네요. "여기까지인가..." 싶었던 적이 여러 번이고 "이제 그만 포기해야겠다..."라고 생각한 게 두 번 정도 되는 것 같습니다. ^^;;;;

아아... 다시는 대장 안하고 시...ㅍ

1월 1일 새벽에 쓰는 2019년 2020년 회고는 뭔가... 피곤하네요. 아마 마지막 이벤트 때문인 것 같은데... 쿨럭...;; 다들 새해 복 많이 받으시길 바랍니다. >ㅁ<)/~

해피 뉴 이어!!

ps) 그러고보니 1편을 2009년에 썼군요! 64비트 멀티코어 OS 원리와 구조 책을 집필하기 위한 OS를 개발하면서 썼던 기억이 나는데... 세상에... 회고를 10년 넘게 쓰고 있다니! 내년에도 쓸 수 있으면 좋겠네요. ^^)-b

i3 윈도우 매니저(Window Manager)를 사용하다 보니 이것저것 아쉬운 부분이 있는데요, 이번에는 볼륨 컨트롤입니다. XFCE나 GNOME, KDE 같은 윈도우 매니저들은 웬만한 기능들을 다 가지고 있지만 i3 윈도우 매니저는 대부분 다른 것들을 빌려서 사용해야 합니다. ^^;;; 쿨럭...;;;

볼륨 컨트롤 같은 경우는 GTK로 된 Pulseaudio Volume Control을 설치하니 사용하는데 큰 불편함이 없더라구요. 데비안 계열에서는 아래와 같이 설치하고 사용할 수 있습니다.

# 설치
$> sudo apt install pavucontrol

# 실행
$> pavucontrol

그럼 즐거운 하루 되세요. ^^)/

요즘 제가 메인으로 사용하고 있는 i3 윈도우 매니저(Window Manager, wm)는 틸팅(Tilting)이라는 멋진 기능이 있어서 많은 유저를 확보하고 있는데요, 반면에 GUI로 제공되는 그놈 컨트롤 센터(Gnome Control Center) 같은 툴이 없어서 불편한 점도 있습니다. 그래서 모니터 설정이나 밝기 같은 부분은 xrandr 같은 툴을 활용해야 하죠. ^^;;;

먼저 모니터의 위치를 변경하는 방법입니다. 아래처럼 xrandr --listmonitors를 하면 현재 연결된 모니터를 확인할 수 있는데요, i3 윈도우 매니저는 연결된 모니터를 순서대로 오른쪽에 배치를 해줍니다. 노트북 같은 경우는 내장 모니터와 외장 모니터로 구분되고, 내장 모니터가 왼쪽에, 외장 모니터가 오른쪽에 자동으로 배치됩니다.

$> xrandr --listmonitors
 0: +*eDP-1 1920/344x1080/194+2560+0  eDP-1     <== 내장 모니터
 1: +DP-1 2560/698x1440/392+0+0  DP-1                  <== 외장 모니터

이러한 순서를 바꾸려면 아래와 같이 입력하면 됩니다. --left-of 나 --right-of를 이용해서 말이죠. :)

# 내장 모니터의 왼쪽에 외장 모니터를 위치시킴
$> xrandr --output DP-1 --auto --left-of eDP-1

xrandr는 소프트웨어적으로 밝기도 조절할 수 있는데요, 아래처럼 --brightness를 이용하면 됩니다.

$> xrandr --output eDP-1 --brightness 0.7

그럼 즐거운 하루 되세요. ^^)/

가벼운 윈도우 매니저를 찾다가 올해부터 본격적으로 i3wm을 사용하기 시작했습니다. ^^;;; i3wm의 가장 큰 매력은 틸팅(Tilting) 기능인데요, 창이 뜰 때마다 화면의 절반을 갈라서 자동으로 분할 및 배치를 해준다는 점입니다. 저처럼 개발을 주로 하고 화면을 양분할 해서 코딩 윈도우와 검색 윈도우를 배치하는 사람한테는 멋진 기능이 아닐 수 없죠. 특히 키보드로 창 이동과 배치 등등이 가능한 부분은 마치 VIM을 윈도우 매니저로 사용하는 것 같은 착각을 불러일으키기도 하죠. ^^a...

i3wm의 윈도우 배치 화면

이런 편리한 기능도 가끔은 당황스러울 때가 있는데요, 바로 파일 다이얼로그처럼 기존의 창 위에 표시되는 다이얼로그도 강제로 틸팅되어 배치되기 때문입니다. 그래서 매번 파일 다이얼로그가 뜰 때마다 어색한 정적이 흐르는데요... i3wm의 설정파일을 아래와 같이 수정하면 파일 다이얼로그가 뜰 때 다른 창 위에 자연스럽게 표시됩니다.

# 설정 파일은 ~/.config/i3/config에 있습니다.

bindsym $mod+n exec nautilus --class floatingWM
for_window [class="^floatingWM$"] floating enable

# 위의 파일을 저장하고 아래와 같이 입력하면 바로 적용됩니다.
$> i3 reload
$> i3 restart

다른 윈도우 매니저도 좋지만 VIM처럼 쓸 수 있는 i3wm이 제일 손에 맞는 것 같네요.

그럼 즐거운 하루 보내세요 ^^)/

최근에 서버에 세팅하고 쓰던 오라클 버추어 박스(Oracle Virtual Box)가 갑자기 죽는 바람에 정상적으로 가상 머신이 구동되지 않는 문제가 발생했습니다. 아무래도 오래된 가상 머신이다 보니 저장된 데이터도 많고 특히 스냅샷이 많아서 데이터가 하드디스크의 차분 데이터(Difference)가 많이 쌓여 있었거든요. 그런데 부팅을 했더니 지난주의 스냅샷으로 돌아가버리는 겁니다. ㅠㅠ)/ 정말 돌아버릴 것 같더라구요.

그냥 날리기에는 너무 아까운 데이터들이라 vbox 관련 설정을 보기 시작했습니다. 그랬더니 규칙이 보이더라구요. 결국은 하드디스크의 차분 데이터를 순서대로 쌓고 가장 최근 차분으로 부팅을 시작하도록 vbox 설정을 수정하는 것이 핵심이었습니다. ^^;;

말로 하니 엄청 어려운데... 일단 .vbox 파일을 vi로 열어보시면 미디어 등록(MediaRegistry), 스냅샷(Snapshot), 하드웨어 설정(Hardware), 저장소 컨트롤러(Storage Controller)의 네 부분으로 나뉩니다.

<?xml version="1.0"?>
... 생략 ...
<VirtualBox xmlns="http://www.virtualbox.org/" version="1.16-linux">
  <Machine uuid="{e5321d5b-ec0b-46fb-b91b-21758d33bf65}" name="Linux 2.0" OSType="Oracle_64" currentSnapshot="{d3870e3c-14b2-46ba-9cae-95d8fe7f7cfe}" snapshotFolder="Snapshots" lastStateChange="2020-08-24T15:56:22Z">
    <-- 미디어 등록 -->
    <MediaRegistry>
      <HardDisks>
        <HardDisk uuid="{6f7655b2-370e-492c-93fc-5a965b18fdd7}" location="Linux 2.0.vdi" format="VDI" type="Normal">
          <HardDisk uuid="{5e9971c7-0b7e-4355-bb14-9f12841b2f05}" location="Snapshots/{5e9971c7-0b7e-4355-bb14-9f12841b2f05}.vdi" format="VDI"/>
        </HardDisk>
      </HardDisks>
      <DVDImages>
        <Image uuid="{84d51fa6-d6f3-48be-9cde-ecde3ddd84e1}" location="/usr/lib/virtualbox/additions/VBoxGuestAdditions.iso"/>
      </DVDImages>
    </MediaRegistry>
    
    <-- 스냅샷 관련 정보 -->
    <Snapshot uuid="{d3870e3c-14b2-46ba-9cae-95d8fe7f7cfe}" name="Snapshot 1" timeStamp="2020-08-24T15:56:22Z" stateFile="Snapshots/2020-08-24T15-56-22-357112000Z.sav">
      <Hardware>
        <CPU count="2">
          <PAE enabled="true"/>
          <LongMode enabled="true"/>
          <X2APIC enabled="true"/>
          <HardwareVirtExLargePages enabled="false"/>
        </CPU>
        <Memory RAMSize="8192"/>
        ... 생략 ...
    </Snapshot>
    
    <-- 가상머신 하드웨어 정보 -->
    <Hardware>
      <CPU count="2">
        <PAE enabled="true"/>
        <LongMode enabled="true"/>
        <X2APIC enabled="true"/>
        <HardwareVirtExLargePages enabled="false"/>
      </CPU>
      <Memory RAMSize="8192"/>
      <Paravirt provider="KVM"/>
      <Display controller="VMSVGA" VRAMSize="128"/>
      <VideoCapture screens="1" file="." fps="25"/>
      ... 생략 ...
    </Hardware>
    
    <-- 저장소 컨트롤러 정보 -->
    <StorageControllers>
      <StorageController name="IDE" type="PIIX4" PortCount="2" useHostIOCache="true" Bootable="true">
        <AttachedDevice passthrough="false" type="DVD" hotpluggable="false" port="0" device="0">
          <Image uuid="{84d51fa6-d6f3-48be-9cde-ecde3ddd84e1}"/>
        </AttachedDevice>
      </StorageController>
      <StorageController name="SATA" type="AHCI" PortCount="1" useHostIOCache="false" Bootable="true" IDE0MasterEmulationPort="0" IDE0SlaveEmulationPort="1" IDE1MasterEmulationPort="2" IDE1SlaveEmulationPort="3">
        <AttachedDevice type="HardDisk" hotpluggable="false" port="0" device="0">
          <Image uuid="{5e9971c7-0b7e-4355-bb14-9f12841b2f05}"/>
        </AttachedDevice>
      </StorageController>
    </StorageControllers>
  </Machine>

여기서 중요한 점은 미디어 등록 부분과 마지막에 저장소 컨트롤러 부분인데요, 분석 결과 미디어 등록 부분은 가상 머신의 하드로 사용되는 vdi 파일 원본부터 스냅샷으로 인해 생성된 "Snapshot\{UUID}.vdi" 정보를 생성된 시간 순서대로 쌓아나가는 역할을 했습니다. 그리고 저장소 컨트롤러 정보는 미디어 등록 부분 중 한 위치를 가리키게 되는데요, 보통은 가장 마지막, 즉 가장 최근 차분 디스크의 UUID를 설정해서 최신 하드 정보로 부팅하도록 만듭니다.

위의 설정 파일 같은 경우는 저장소 컨트롤러가 "Snapshot\{5e9971c7-0b7e-4355-bb14-9f12841b2f05}.vdi"로 부팅을 시작하도록 되어 있고, 이는 미디어 등록 정보를 따르면 Linux 2.0.vdi의 최근 차분 정보임을 알 수 있습니다. 즉 아래처럼요.

{5e9971c7-0b7e-4355-bb14-9f12841b2f05}
  |-> {6f7655b2-370e-492c-93fc-5a965b18fdd7}
        |-> Linux 2.0.vdi

사실 어느 정도 정보가 살아 있는 경우는 미디어 등록 정보를 비교적 쉽게 구성할 수 있는데요, 아무런 정보가 없는 경우는 Snapshot 폴더로 이동해서 아래 명령어를 사용하면 부모의 차분 이미지 UUID를 찾아낼 수 있습니다. 이러한 정보를 이용해서 순서대로 나열하면 역시 미디어 등록 정보를 구성할 수도 있죠. 제가 직접 사용한 방법이기도 합니다. ㅠㅠ)/ 차분 정보를 보면 부모의 UUID가 6f7655b2-370e-492c-93fc-5a965b18fdd7 인데요, 아래쪽의 Linux 2.0.vdi를 보면 UUID가 동일한 것을 알 수 있습니다. 이를 통해 Linux 2.0.vdi의 첫 번째 차분임을 알 수 있는 거죠.

# 차분의 정보 표시
$> vboxmanage showhdinfo {5e9971c7-0b7e-4355-bb14-9f12841b2f05}.vdi
UUID:           5e9971c7-0b7e-4355-bb14-9f12841b2f05
Parent UUID:    6f7655b2-370e-492c-93fc-5a965b18fdd7
State:          locked write
Type:           normal (differencing)
Auto-Reset:     off
Location:       /home/user/VirtualBox VMs/Linux 2.0/Snapshots/{5e9971c7-0b7e-4355-bb14-9f12841b2f05}.vdi
Storage format: VDI
Format variant: dynamic default
Capacity:       102400 MBytes
Size on disk:   2 MBytes
Encryption:     disabled
Property:       AllocationBlockSize=
In use by VMs:  Linux 2.0 (UUID: e5321d5b-ec0b-46fb-b91b-21758d33bf65)

# 가상 하드디스크의 정보 표시
$> vboxmanage showhdinfo Linux\ 2.0.vdi 
UUID:           6f7655b2-370e-492c-93fc-5a965b18fdd7
Parent UUID:    base
State:          locked read
Type:           normal (base)
Location:       /home/user/VirtualBox VMs/Linux 2.0/Gooroom 2.0.vdi
Storage format: VDI
Format variant: dynamic default
Capacity:       102400 MBytes
Size on disk:   5967 MBytes
Encryption:     disabled
Property:       AllocationBlockSize=1048576
In use by VMs:  Linux 2.0 (UUID: e5321d5b-ec0b-46fb-b91b-21758d33bf65) [Snapshot 1 (UUID: d3870e3c-14b2-46ba-9cae-95d8fe7f7cfe)]
Child UUIDs:    5e9971c7-0b7e-4355-bb14-9f12841b2f05

어휴... 어쨌든 해결해서 다행이네요.

그럼 좋은 밤 되세요 ^^)/

여러 가지 이유로 Intel 가상화 기술(Virtualization Technology)을 직접 활용한 시스템 소프트웨어를 구현한 이야기를 예전에 잠시 했었습니다. Shadow-box라고 부르고 있는데... 그 녀석(?!)으로 인해 영혼까지 탈탈 털리고 있는 중입니다. ^^;;; 2015년에 처음 개발을 시작했으니까 올해가 5년 차인데, 뮤택스와 스핀락 관련 이야기(https://kkamagui.tistory.com/925)에서 잠깐 소개를 하기도 했죠.

경량 가상화 기술을 이용한 영역(World) 분리

처음 가상화 기술로 영역을 분리하고 나니 잘 되는 듯 하지만 실제로는 잘 안 되는(?!) 상황이 많이 발생했는데요, 이러한 일들이 커널이 변경되어 새로운 기능이 추가되거나 단말이 바뀔 때마다 나와서... 정말 탈탈 털리고 있다는 표현이 맞는 것 같네요. 정말 멜트다운(Meltdown) 때문에 PTI(Page Table Isolation)이 커널에 들어갔을 때는 죽을 맛이었는데... 어쨌거나 큰 산을 넘고 또 넘어서 지금은 많이 안정화가 되었습니다. 물론... 새로운 문제가 나오기 전까지 안정적인 거지만요. ^^;;;

이번에 단말을 바꾸면서 여러가지 시험을 진행하고 있었는데요, 특정 단말에서만 한 20분을 못 넘기고 멈추는 문제가 발생했습니다. 이 문제 때문에 며칠 밤을 새웠는데요, 딱히 의심되는 부분이 없어서 계속 반복 실행하면서 로그를 확인했는데 별다른 로그가 없이 시스템이 정지했습니다. 사실 Shadow-box에 문제가 있거나 페이지 테이블 맵핑에 문제가 있으면 이런 현상이 생겼던 경험이 있어서 페이지 테이블 쪽을 의심하고 있던 찰나... 인터럽트 발생 횟수를 아래처럼 살펴보다 보니 NMI(Non-Maskable Interrupt)가 꽤 많이 발생한 것이 보이더라구요.  

$ cat /proc/interrupts
            CPU0       CPU1       CPU2       CPU3       CPU4       CPU5       CPU6       CPU7       CPU8       CPU9       CPU10      CPU11      CPU12      CPU13      CPU14      CPU15      
 NMI:         51        179        778         16        241         33         77         90         47        127        408        136        268         62        192        219   Non-maskable interrupts

NMI는 마스킹, 즉 비활성화가 불가능해서 언제 어디서건 발생할 수 있기 때문에 핸들러가 반드시 있어야 합니다. 당연히 게스트(Guest), 즉 리눅스가 동작하는 일반 영역에서 발생하면 커널의 핸들러가 불려서 처리가 되니 문제가 없을 거라 생각했는데... 문득 호스트(Host), 즉 커널 보호 기술이 동작하고 있는 영역에서 발생하면 문제가 생길 것 같은 느낌이 들었습니다. 아니나 다를까, 실제로 인터럽트 핸들러를 생성해서 로그를 찍어보니 아주 간간히 NMI 인터럽트가 호스트에서도 발생하고 있었습니다. 그리고 그때마다 게스트의 NMI 핸들러를 불러서 처리를 하고 있더라구요. 아아... 이러면 안 되는데... ㅠ^ㅠ

호스트는 리눅스 커널의 기능을 최소로 활용하기 때문에 NMI 핸들러를 실행할 만큼 자원이나 호출 가능한 함수가 충분치 않거든요. 그래서 이번 기회에 인터럽트 핸들러를 호스트용 하나 더 만들고 게스트와 분리시켜줬습니다. 호스트의 인터럽트 핸들러는 아무것도 할 필요가 없기 때문에 로그만 찍는 더미(Dummy) 핸들로로 모두 연결했죠. 그랬더니 새로운 단말에서도 다시 안정적으로 동작을 하기 시작했습니다. >ㅁ<)/~ 로그를 보니 거의 몇 분 단위로 NMI 인터럽트가 발생하고 있더라구요.

$> sudo journalctl -f | grep shadow-box
 8월 03 00:22:11 machine kernel: shadow-box: VM [3] INT NMI callback
 8월 03 00:24:18 machine kernel: shadow-box: VM [5] INT NMI callback
 8월 03 00:26:02 machine kernel: shadow-box: VM [8] INT NMI callback
 8월 03 00:26:32 machine kernel: shadow-box: VM [0] INT NMI callback
 8월 03 00:27:52 machine kernel: shadow-box: VM [13] INT NMI callback
 8월 03 00:29:45 machine kernel: shadow-box: VM [6] INT NMI callback
 8월 03 00:31:21 machine kernel: shadow-box: VM [7] INT NMI callback
 8월 03 00:36:26 machine kernel: shadow-box: VM [9] INT NMI callback
 8월 03 00:37:36 machine kernel: shadow-box: VM [11] INT NMI callback
 8월 03 00:42:54 machine kernel: shadow-box: VM [1] INT NMI callback
 8월 03 00:44:30 machine kernel: shadow-box: VM [14] INT NMI callback
 8월 03 00:48:18 machine kernel: shadow-box: VM [15] INT NMI callback
 8월 03 00:51:03 machine kernel: shadow-box: VM [4] INT NMI callback
 8월 03 00:54:26 machine kernel: shadow-box: VM [12] INT NMI callback
 8월 03 01:12:17 machine kernel: shadow-box: VM [10] INT NMI callback
 8월 03 01:59:58 machine kernel: shadow-box: VM [2] INT NMI callback

어휴... 진땀을 뺐네요. 자칫 잘못하면 엄청 길어질 뻔했는데, 운이 좋아서 다행입니다.

그럼 좋은 밤 보내세요. ^^

ps) CPU의 인터럽트 핸들러나 실제 핸들링 과정이 궁금하신 분은 64비트 멀티코어 OS 원리와 구조*(http://www.yes24.com/Product/Goods/65061299)를 참고하시면 좋을 것 같습니다.

 

가끔 임시 비밀번호를 만들어 써야하는 경우가 종종 있는데요, 임시 비밀번호지만 뭔가 습관대로 만드는 것이 불안해서 임의의 문자열을 뽑는 기능을 검색해봤습니다. 다양한 방법이 있었는데 스택 오버플로우(unix.stackexchange.com/questions/230673/how-to-generate-a-random-string)에 좋은 방법이 있었습니다. ^^)/~ 즉, 아래처럼 하면 된다는 이야기지요.

$> head /dev/urandom | tr -dc A-Za-z0-9 | head -c 32 ; echo ''
uqpmpi0CpJOEh1NJL9tffi6mmPLe6ASk

$> head /dev/urandom | tr -dc A-Za-z0-9 | head -c 32 ; echo ''
cnxc8yYGGrZwz5noljO18yg8xNBH9kHY

$> head /dev/urandom | tr -dc A-Za-z0-9 | head -c 32 ; echo ''
YUvAlrdtyNUhrr5GSXLKLeA7EkSoZuTV

꽤나 유용하게 사용할 수 있겠군요.

그럼 좋은 하루 되세요 ^^)/

소싯적에 뜻한 바가 있어서 인텔 가상화 기술(Intel Virtualization Technology, VT-x/VT-d)를 직접 이용하여 리눅스 커널을 보호하는 소프트웨어를 만들었습니다. ^^;;; 이름은 Shadow-box라고 지었는데요, 설계와 구현물은 오픈소스로 공개되어 있고 github.com/kkamagui/shadow-box-for-x86에서 보실 수 있습니다. 최근에 커널의 메모리 관련 디버깅 옵션과 연계하는데 문제가 있어서 업그레이드를 힘겹게 진행했는데요, 시험 중에 재미있는 현상을 발견해서 한자 남겨봅니다.

Shadow-box는 가상화 기술을 사용해서 실제 보호 기능을 수행하는 호스트(Host) 영역과 호스트에 의해 보호받는 일반 영역인 게스트(Guest) 영역으로 구분합니다. 여기서 재미있는 부분은 호스트와 게스트가 커널을 공유한다는 점인데요, 공유함과 동시에 호스트에서 게스트 커널의 주요 영역이 변조되었는지를 이벤트 기반(Event-driven) 혹은 주기적 감시(Periodic monitoring)를 통해 확인한다는 겁니다.

Shadow-box의 구조

 비록 호스트는 게스트의 리눅스 커널을 공유하지만 모든 기능을 사용할 수 는 없습니다. 호스트는 적어도 변조가 되지 않은 리눅스 커널 영역에서 구동되어야 하고 혹여 태스크 스위칭이 되어 호스트에서 게스트 프로세스가 실행되거나 하면 안 되기 때문이죠. 그래서 호스트에서는 철저하게 이를 막고 있는데요,  호스트에서 인터럽트 비활성화(Interrupt Disable)와 선점 불가(Preemption Disable)를 사용합니다. ^^;;;

인터럽트 비활성화의 경우는 VT-x가 직접 처리해주는 부분이라 Shadow-box에서 별도의 처리는 하지 않고, 선점 불가의 경우는 리눅스 커널의 preemption_disable(), preemption_enable() 함수를 사용해서 처리하고 있습니다. 그런데 예상치도 못한 변수를 만났습니다. 바로 뮤텍스(Mutex)와 스핀락(Spinlock) 때문이죠.

뮤텍스(Mutex)와 스핀락(Spinlock)은 커널에서 흔히 그리고 전통적으로 사용되었던 동기화 기재(Synchronization)인데요, 뮤텍스는 대기(Sleep)를 전제로 하고 있고 스핀락은 바쁜 대기(Busy-waiting)를 전제로 하고 있습니다. 좀 더 자세히 설명하자면, 뮤텍스의 경우는 이미 누군가가 뮤텍스를 사용(Lock)하고 있다면 다른 사람은 해당 뮤텍스의 대기 큐에 자신을 등록하고 차례가 올 때까지 대기(Sleep) 합니다. 그리고 앞서 뮤텍스를 잡은 사람이 이를 해제(Unlock)하면 대기 큐에 있는 프로세스를 깨우는 것이죠. 그리고 즉시 해당 프로세스가 실행되는 특징이 있습니다. 스핀락의 경우는 누군가가 잡고(Lock) 사용 중이면 다른 사람은 계속 루프를 돌면서 해제(Unlock) 되었는지를 확인합니다. 그리고 앞서 스핀락을 잡은 사람이 이른 해제(Unlock)하면 루프를 돌면서 확인하던 다른 사람은 이를 감지하고 다시 잡은 후 작업을 시작하게 되는 것이죠. 이미 눈치를 채셨겠지만, 대기 큐가 없고 순서도 없는 야생인 것이죠. ^^;;;

실제 리눅스 커널의 뮤텍스의 Unlock 코드는 아래처럼 되어 있습니다. 대기 큐(Wait Queue)를 돌면서 태스크를 깨우는 것을 알 수 있습니다.

static noinline void __sched __mutex_unlock_slowpath(struct mutex *lock, unsigned long ip)
{
	struct task_struct *next = NULL;
	DEFINE_WAKE_Q(wake_q);
	unsigned long owner;

	mutex_release(&lock->dep_map, ip);

	/*
	 * Release the lock before (potentially) taking the spinlock such that
	 * other contenders can get on with things ASAP.
	 *
	 * Except when HANDOFF, in that case we must not clear the owner field,
	 * but instead set it to the top waiter.
	 */
	owner = atomic_long_read(&lock->owner);
	for (;;) {
		unsigned long old;

#ifdef CONFIG_DEBUG_MUTEXES
		DEBUG_LOCKS_WARN_ON(__owner_task(owner) != current);
		DEBUG_LOCKS_WARN_ON(owner & MUTEX_FLAG_PICKUP);
#endif

		if (owner & MUTEX_FLAG_HANDOFF)
			break;

		old = atomic_long_cmpxchg_release(&lock->owner, owner,
						  __owner_flags(owner));
		if (old == owner) {
			if (owner & MUTEX_FLAG_WAITERS)
				break;

			return;
		}

		owner = old;
	}

	spin_lock(&lock->wait_lock);
	debug_mutex_unlock(lock);
	if (!list_empty(&lock->wait_list)) {
		/* get the first entry from the wait-list: */
		struct mutex_waiter *waiter =
			list_first_entry(&lock->wait_list,
					 struct mutex_waiter, list);

		next = waiter->task;

		debug_mutex_wake_waiter(lock, waiter);
		wake_q_add(&wake_q, next);
	}

	if (owner & MUTEX_FLAG_HANDOFF)
		__mutex_handoff(lock, next);

	spin_unlock(&lock->wait_lock);

	wake_up_q(&wake_q);    
}


void wake_up_q(struct wake_q_head *head)
{
	struct wake_q_node *node = head->first;

	while (node != WAKE_Q_TAIL) {
		struct task_struct *task;

		task = container_of(node, struct task_struct, wake_q);
		BUG_ON(!task);
		/* Task can safely be re-inserted now: */
		node = node->next;
		task->wake_q.next = NULL;

		/*
		 * wake_up_process() executes a full barrier, which pairs with
		 * the queueing in wake_q_add() so as not to miss wakeups.
		 */
		wake_up_process(task);
		put_task_struct(task);
	}
}

스핀락의 Unlock은 아래와 같은데요, 이 역시 Unlock과 함께 스케줄러를 호출하는 것을 알 수 있습니다. 단, preemptible 일 때만 그렇게 하는데, 인터럽트가 활성화되어 있고 preempt_enable()인 상태여야 합니다.

static inline void __raw_spin_unlock(raw_spinlock_t *lock)
{
	spin_release(&lock->dep_map, _RET_IP_);
	do_raw_spin_unlock(lock);
	preempt_enable();
}

#define preempt_enable() \
do { \
	barrier(); \
	if (unlikely(preempt_count_dec_and_test())) \
		__preempt_schedule(); \
} while (0)

asmlinkage __visible void __sched notrace preempt_schedule(void)
{
	/*
	 * If there is a non-zero preempt_count or interrupts are disabled,
	 * we do not want to preempt the current task. Just return..
	 */
	if (likely(!preemptible()))
		return;

	preempt_schedule_common();
}
NOKPROBE_SYMBOL(preempt_schedule);
EXPORT_SYMBOL(preempt_schedule);

#define preemptible()	(preempt_count() == 0 && !irqs_disabled())

Shadow-box는 태스크 목록을 관찰할 때 스핀락을 사용하며, 커널 모듈 목록을 관찰할 때 뮤텍스를 어쩔 수 없이 사용합니다. 이 두 자료구조는 리스트 형태로 되어 있는데, 리스트를 순회하는 동안 리스트가 바뀌어 엉뚱한 곳에 데이터를 읽어오거나 접근하거나 하면 안 되기 때문이죠. ^^;;;

결국 해결책을 찾아야 했는데요, 스핀락 같은 경우는 인터럽트가 비활성화되어 있으면 스케줄러를 호출하지 않기 때문에 별 문제가 없는 것으로 확인되었습니다. 다만, 뮤텍스 같은 경우는 잘못하면 호스트에서 게스트의 다른 프로세스를 실행할 수 있는 잠재적인 문제가 있어서 결국 제거하는 방법을 택했습니다. 이로 인해 일부 탐지 기능이 지연되는 문제가 있지만... 안정성이 먼저니까요. ^^;;;

그럼 좋은 밤 되세요 ^^)/

카이스트(KAIST)의 봄 GSIS 세미나 연사로 초청되어 신뢰플랫폼모듈(TPM)의 보안 위협과 대응 방안에 대해 말씀을 드리고 왔습니다! 카이스트는 훌륭하신 교수님들과 석/박사님들이 계셔서 불러주실 때마다 부담이 되지만, 불러주시는 것만으로도 영광이라 꼼꼼히 준비해서 가려고 노력하고 있습니다. ^^;;; 항상 신경 써주시는 김용대(Yongdae Kim) 교수님과 초청해주신 @손수엘 교수님께 감사의 말씀을 드립니다.

사실 카이스트 발표는 5월 중순에 다녀왔는데, 5월에 너무 많은 일이 있어서 이제야 겨우 회고할 시간이 생겼네요. 이번 세미나는 코로나로 인해 카이스트의 원격 강의 장비를 사용했는데, 장비가 어색하기도 했지만 제가 발표하면서 듣는 분들의 표정을 많이 살피는 스타일이라 반응을 볼 수 없어서 아주 당황스러웠습니다. 그나마 손수엘 교수님께서 많이 웃어주셔서 견딜 수 있었는데, 실제로 온라인 강의를 진행하시는 교수님들이 얼마나 고충이 많으실까 하는 생각이 들었습니다. ^^;;;

언택트 시대의 강의를 작게나마 경험해볼 수 있어서 신선하기는 했는데요, 얼른 코로나가 잠잠해져서 다시 오프라인으로 이런저런 말씀을 드릴 날이 오면 좋겠습니다.

ps) 사진은 카이스트 내부의 도서관 사진입니다. 세미나를 하러 간 곳은 N1 빌딩이지만 개인적으로 도서관 건물이 너무 멋져서 넣어 봤습니다. ^^;;;

리눅스를 사용하다보면 배포판(Distribution)이 지원해주지 않는 디바이스를 써야할 때가 있습니다. 사실 그래서 저는 한 세대 전 장비를 선호하기도 한데요, 어쩔 수 없이 써야할 경우가 종종 있습니다. 예를 들면 최신 장비에서 제가 만든 소프트웨어가 잘 동작하는지를 봐야 하는 경우처럼요. ^^;;;

보통 이런 경우는 강제로 커널을 최신 버전으로 업그레이드 하면 되지만 이것 또한 쉽지 않은 경우가 있습니다. 배포판의 경우는 특정 커널 버전을 정해놓고 유지하면서 최신 버전 커널은 천천히 적용하는 게 대부분이거든요. 그렇다면 옛날 커널 버전에서 최신 디바이스의 드라이버를 지원하려면 어떻게 해야 하느냐... 리눅스 커널의 백포트(backport)를 쓰면 되는데요, 백포트는 git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/backport-iwlwifi.git에 있습니다. 다만, 이걸 직접 받아서 빌드하는 것이 쉽지 않은데, 데비안(Debian) 리눅스의 파생 버전인 우분투(Ubuntu) 리눅스는 이를 위한 패키지를 이미 준비해두었더라구요.

Canonical Hardware Enablement team이 이를 담당하고 있는데요, launchpad.net/~canonical-hwe-team/+archive/ubuntu/pc-oem-dkms에서 backport-iwlwifi-dkms_7906-0ubuntu3~18.04.1~oem~ubuntu18.04.1_all.deb를 받으면 됩니다. 그리고 아래처럼 설치하면 설치 중에 자동으로 커널 드라이버가 빌드됩니다.

$> sudo dpkg -i backport-iwlwifi-dkms_7906-0ubuntu3~18.04.1~oem~ubuntu18.04.1_all.deb

제가 확인해본 결과 데비안 10(Buster)에서도 일단 정상적으로 동작하는 것을 확인했고, 최신 WIFI 디바이스인 Killer AX1650도 잘 잡혔습니다. ㅠㅠ)-b 후우... 정말 다행이에요.... 지옥문이 열릴 뻔 했거든요. 쿨럭..;;;

그럼 해피 리눅스하세요. >ㅁ<)/

최근 데비안(Debian) 리눅스에서 그놈(Gnome) 데스크탑을 버리고 i3 윈도우 매니저를 사용하기 시작했습니다. i3는 틸팅(Tilting), 화면을 이등분 해서 창을 정렬하는 것이 특징인데요, 처음에는 좀 당황스럽지만 익숙해지면 vi를 사용하듯이 창을 옮겨다니면서 쓸 수 있습니다. 물론 이렇게까지 익숙해지려면 시간을 많이 투자해야 한다는 것이 함정이지만요... 쿨럭..;;;;

그놈을 버리고 나니 아쉬운 부분이 여럿 있는데요, 그중에 하나가 모니터 밝기 조절입니다. gnome-control-center를 실행해서 해결해 볼까도 했지만 커맨드 라인으로 처리할 수 있는 방법을 찾았습니다. 방법은 간단한데요, xrandr로 연결된 모니터를 찾고 밝기를 조절하는 겁니다. 아래처럼 말이죠. ^^;;;

# 연결된 모니터를 찾음.
$>xrandr -q | grep connected
eDP-1 connected primary 1920x1200+0+0 (normal left inverted right x axis y axis) 263mm x 164mm
DP-1 connected 2560x1440+1920+0 (normal left inverted right x axis y axis) 698mm x 392mm
HDMI-1 disconnected (normal left inverted right x axis y axis)

# 모니터의 밝기를 70%로 조절함.
xrandr --output eDP-1 --brightness 0.7

생각보다 간단하네요. 그럼 좋은 하루 되세요 ^^

이 내용은 부채널 공격(Side-Channel Attack)의 대가인 Daniel Gruss의 졸업 논문인 Software-based Side-Channel Attacks and Defenses in Restricted Environments의 내용을 발췌한 것입니다. ^^;;; 저도 그동안 부채널 공격을 관심있게 보지 않아서 대략적인 내용만 알고 있었는데, 이번에 Black Hat Asia의 리뷰보드(Review Board)가 되면서 관심있게 살펴보게 되었습니다.

사실 최근 부채널 공격의 트렌드는 하드웨어 중심이던 예전과 달리 소프트웨어만으로 공격을 한다는 점인 것 같습니다. Daniel Gruss도 이야기하긴 했지만, 예전에는 암호 연산용 칩이 있을 때 암호 연산을 수행하는 알고리즘의 경로에 따라 전력 소모가 달라진다는 점을 이용해서 전력 측정(Power Measurement) 기법을 사용했습니다. 이러한 공격 기법은 현재 대부분 대비가 되어 있는 상태라 새로운 공격 기법이 등장했는데요, 그게 바로 소프트웨어 기반 마이크로아키텍처 공격(Software-based Microarchitectural Attack) 입니다. 주로 캐시(Cache)와 TLB(Translate Look-aside Buffer)를 이용하는데요, 캐시와 TLB에 대해서는 제가 요약한 캐시 및 TLB 부채널 공격(Cache and TLB Side-Channel Attack) 기법을 참고하시길 바랍니다. ^^;;;

소프트웨어 기반 마이크로아키텍터 공격(Software-based Microarchitectural Attack)

1. 캐시 공격(Cache Attack)

캐시 공격 기법은 연산중에 캐시에 남은 정보를 추출해내는 기법인데요, Evit+Time, Prime+Probe, Flush+Reload, Flush+Flush, Evict+Reload 공격 기법이 대표적으로 최근에 활용된 TLB 공격 기법도 있습니다. 이 부분들은 내용이 많아서 제가 따로 정리한 캐시 및 TLB 부채널 공격(Cache and TLB Side-Channel Attack) 기법를 참고하면 좋을 것 같습니다.

2. 예측자 공격(Attacks on Predictors)

CPU에는 성능을 높이기 위해 다양한 예측자(Predictors)가 들어있는데요, Branch Predictors, Prefetchers, Memory-aliasing Predictors가 있습니다.

  • Branch Predictors: 프로그램을 작성하다보면 루프를 사용하게 되는데요, 루프는 일정한 횟수동안 반복해서 도는 특징이 있습니다. 이러한 루프를 만나면 CPU가 루프의 비교문을 만났을 때 다음에도 루프 안쪽을 실행할 것이라고 예측하고 미리 파이프라인에 명령어를 밀어넣는데, Branch Predictors의 예측을 빗나가게 해서 정보를 추출하는 공격 기법이 Branch Predictor Attack입니다.
  • Prefechers: 코드를 실행하다보면 현재 실행중인 명령어의 다음 명령어가 실행될 것이라 예측하고 성능향상을 위해 명령어와 데이터를 미리 채워놓습니다. Prefecher는 캐시 부채널 공격을 수행할 때 원치 않는 잡음(Noise)를 생성하는 원인이 되기도 하는데요, Prefecher Attack은 직접 이를 이용해서 공격을 수행하기 보다는 잡음을 줄이는 방법으로 활용되곤 한답니다.
  • Memory-aliasing Predictors: 최근 CPU는 실행 능력을 향상시키기 위해 다양한 버퍼(Buffer)를 활용하는데요, Memory-aliasing Predictor는 CPU 내부에 있는 저장 버퍼(Store Buffer)의 값을 캐시에 저장하기 전에 읽어야할 때 이를 읽기 버퍼(Load Buffer)로 옮기는 일을 담당합니다. 저장 버퍼의 값이 읽기 버퍼로 옮겨갈 때 접근 권한(Access Right)가 제대로 검사되지 않으면 정보가 유출될 수 있는데 이것이 바로 Memory-aliasing Predictor Attack입니다. 실제로 Store-to-Leak Forwarding Attack이 이를 이용했습니다.

3. DRAM 공격(DRAM Attack)

DRAM 모듈에 저장된 정보는 Row Buffer를 거쳐서 CPU에게 전달되는데, Row Buffer에 저장된 데이터는 다시 읽었을 때 Row Buffer에 없는 데이터보다 빨리 접근되는 특징이 있습니다. 이러한 공격이 실존하고 이를 이용한 공격이 DRAMA Attack 입니다.

DRAMA 공격 기법 - 출처: Software-based Side-Channel Attacks and Defenses in Restricted Environments

4. 임시 실행 공격(Transient-Execution Attack)

최근 가장 핫한 공격이 바로 임시 실행 공격(Transient-Execution Attack) 인데요, 많이들 알고 계시는 멜트다운(Meltdown), 스펙터(Spectre) 공격 기법이 바로 이 카테고리에 속합니다. 임시 실행 공격은 마이크로아키텍처 공격의 한 종류인데요, 현대 CPU의 비순차실행(Out-of-order Execution)과 예측적 실행(Speculative Execution)을 이용해서 데이터를 유출하는 공격 기법입니다. Daniel Gruss의 전공이기도 하지요.

4.1. 스펙터(Spectre)

스펙터는 임시 실행 공격의 한 종류인데요, 실행 흐름 예측 오류(Control-flow misprediction)과 데이터 흐름 예측 오류(Data-flow misprediction)을 이용합니다. 사실 말보다는 그림으로 보면 더 이해하기가 쉬운데요, 아래는 논문에 있는 스펙터 공격의 흐름입니다.

스펙터(Spectre) 공격 기법 - 출처: Software-based Side-Channel Attacks and Defenses in Restricted Environments

  • 위의 그림에서 보는 것처럼 if문 안의 Index가 0~3일 경우는 참이되어 왼쪽의 Then 부분에 코드가 실행됩니다. 따라서 index의 값으로 data 영역을 접근하고 그 값이 공유 메모리 영역인 shm의 index가 되어 최종적으로 값이 읽혀집니다.
  • 이때, index의 값을 계속 if문이 True가 되도록 실행을 하면 Branch Predictor가 앞으로도 계속 참이 될 것이라고 예측하고 index의 값이 4보다 커도 Then 영역을 미리 실행(Out-of-Execution)합니다. 즉 예측이 실패하는 것이지요.
  • index는 data 영역에서 해당 위치만큼 떨어진 영역을 가리키게 되는데요, 이 index를 주요 정보가 있는 영역, 예를 들어 커널에 저장된 Key가 존재하는 영역이라고 가정한다면 해당 키의 정보가 shm의 Index로 사용되어 캐시에 저장됩니다.
  • 마지막으로 공격자는 shm에서 Index만큼 떨어진 영역이 캐시에 저장되었는지를 확인하는 방식으로 주요 정보에 접근할 수 있습니다.

4.2. 멜트다운(Meltdown)

멜트다운은 임시 실행 공격의 한 종류인데요, 예외(Exception)가 발생했을 때 비순차실행(Out-of-order)의 결과로 데이터가 유출되는 것을 노리는 기법입니다. 역시 그림으로 보는 것이 더 이해가 쉬우니 논문의 그림을 인용하겠습니다.

멜트다운(Meltdown) 공격 기법 - 출처: Software-based Side-Channel Attacks and Defenses in Restricted Environments

  • 위의 그림에서 kernel 부분의 메모리는 접근이 불가능하기 때문에 오류(Exception)이 발생합니다.

  • 이때, 비순차실행 때문에 value가 공유 메모리 영역인 shm의 index로 사용됩니다.

  • 공격자는 스펙터의 공격기법과 같이 shm에서 Index만큼 떨어진 영역이 캐시에 저장되었는지를 확인하는 방식으로 주요 정보에 접근할 수 있습니다.

    지금까지 부채널 공격에 대해서 정리해봤는데요, 최근 부채널 공격은 소프트웨어만 이용해서 공격이 가능한 만큼 파급력이 큽니다. 하지만 다행히도 방어법 또한 마련되어 있는데요, 다음에는 방어법에 대해서 정리해보겠습니다.

    그럼 좋은 밤 되세요 ^^)/

만자로 리눅스(Manjaro Linux)

만자로(Manjaro) 리눅스는 아치(Arch) 리눅스를 바탕으로 만들어졌는데요, 버전을 구분하고 대규모 업데이트가 벌어지는 우분투(Ubuntu)나 페도라(Fedora)와 달리 변경 사항이 있으면 즉시 업데이트를 하는 특징이 있습니다. 아치 리눅스나 만자로 리눅스에서는 이를 롤링 업데이트(Rolling Update)라고 하는데, 업데이트를 그만큼 자주 하다 보니 속도가 빠른 미러 서버를 선택하는 것이 여러모로 좋습니다. ^^;;;

만자로를 설치하면 속도가 빠른 미러 서버를 선택하는데요, 미러 서버가 계속 추가되고 삭제되기 때문에 리스트를 갱신해 주는 것이 좋습니다. 미러 서버 정보는 /etc/pacman.d/mirrorlist에 있는데, 아래와 같은 방법으로 확인해 볼 수 있습니다.

$> cat /etc/pacman.d/mirrorlist 
##
## Manjaro Linux default mirrorlist
## Generated on 2020-02-03 00:40
##
## Please use 'pacman-mirrors -f [NUMBER] [NUMBER]' to modify mirrorlist
## (Use 0 for all mirrors)
##

## Country : Japan
Server = http://ftp.riken.jp/Linux/manjaro/stable/$repo/$arch

## Country : Denmark
Server = https://www.uex.dk/public/manjaro/stable/$repo/$arch

## Country : China
Server = https://mirrors.ustc.edu.cn/manjaro/stable/$repo/$arch

## Country : Iran
Server = https://repo.iut.ac.ir/repo/manjaro/stable/$repo/$arch

## Country : France
Server = https://mirror.oldsql.cc/manjaro/stable/$repo/$arch

이제 미러 서버 목록을 모두 다운로드한 뒤 이를 속도 순으로 정렬해보겠습니다. 미러 서버 목록을 다운받으러면 pacman-mirrors 커맨드와 country 옵션을 사용하면 됩니다.

$> sudo pacman-mirros --country all
::INFO Downloading mirrors from repo.manjaro.org
::INFO Using default mirror file
::INFO Querying mirrors - This may take some time
  ..... Australia      : http://manjaro.melbourneitmirror.net/
  ..... Australia      : http://manjaro.mirror.serversaustralia.com.au/
  1.698 Austria        : http://mirror.inode.at/manjaro/
  1.774 Austria        : http://mirror.easyname.at/manjaro/
  ..... Austria        : ftp://mirror.easyname.at/manjaro/
  1.169 Bangladesh     : http://mirror.xeonbd.com/manjaro/

... 생략 ...

::INFO Writing mirror list
::South_Korea     : https://mirror.d-tl.com/manjaro/stable
::Taiwan          : http://free.nchc.org.tw/manjaro/stable
::Japan           : http://ftp.tsukuba.wide.ad.jp/Linux/manjaro/stable
::Hong_Kong       : http://ftp.cuhk.edu.hk/pub/Linux/manjaro/stable

... 생략 ...

::INFO Mirror list generated and saved to: /etc/pacman.d/mirrorlist

위의 로그를 보면 모든 미러 서버 목록을 읽어와서 응답속도를 측정하고 빠른 순서대로 정렬하여 /etc/pacman.d/mirrorlist에 저장한 것을 확인할 수 있습니다. 예전에 제가 설치했을 때는 한국에 미러 서버가 없었는데 지금은 한국에도 있군요. 그저 감사할 따름입니다. ^^)/

미러 서버를 업데이트했으니 이제 더욱 빠른 속도로 만자로를 업데이트할 수 있습니다. 업데이트는 아래와 같은 방식으로 진행합니다.

$> sudo pacman -Syu

그럼 좋은 하루 되세요 ^^)/

Black Hat은 보안 관련 일을 하시는 분이라면 누구나 한 번쯤은 들어보셨을 산업계 중심의 학회인데요, 수천 명이 참석하는 학회이고 아시아(Black Hat Asia), 미국(Black Hat USA), 유럽(Black Hat Europe)에서 매년 열리고 있습니다. 저도 Black Hat에 발표자로 선정되는 것이 꿈이어서 꾸준히 도전했는데, 논문이 계속 물만 먹다가(?!) Black Hat Asia 2017에 처음으로 논문이 붙었습니다. ^^;;; 그때 발표한 것이 Intel 가상화 기술(VT-x, VT-d)을 이용해서 제가 직접 만든 커널 보호 기술(Shadow-box)이었는데요, Shadow-box에 대한 발표 자료, 백서, 소스코드는 아래에서 보실 수 있습니다.

2017년에 Black Hat Asia에 발표자로 선정된 뒤로 꾸준히 Black Hat Asia에서 발표를 해왔는데요, 2019년에는 Black Hat Asia에 이어 Black Hat Europe에서도 발표를 해서(무려 두 번!!) 더 특별한 한 해였습니다. 그.리.고! 작년 Black Hat Europe 2019의 발표가 대박이 나서 Black Hat Asia 2020에 초청되어 한 번 더 발표하게 되었습니다. ^0^)/ Black Hat은 지난번 학회에서 반응이 좋았던 발표를 골라서 다음 학회에서 다시 발표할 수 있도록 기회를 주고 있는데요, 제가 이번에 당첨이 된 것이지요! 발표 내용은 제가 발견한 신뢰플랫폼모듈(Trusted Platform Module, TPM)의 취약점과 이를 이용하여 마이크로소프트의 비트락커(BitLocker)를 해제하는 내용입니다.

마이크로소프트의 BitLocker는 사용자의 디스크를 TPM과 함께 암호화하여 정보 유출을 막는 보안 소프트웨어인데요, 윈도우즈 7(Windows 7) 이상을 사용하신다면 이를 활성화하여 데이터를 안전하게 보호할 수 있습니다. 마이크로소프트의 비트락커(BitLocker)에서 정보(비트)를 새어 나오게 만든다고 이름을 BitLeaker로 지었는데, 나름 반응이 좋았던 것 같습니다. ^^;;; Black Hat Europe 2019의 BitLeaker 발표 내용과 소스코드는 아래에서 보실 수 있습니다.

저도 처음에 Black Hat의 프로그램 매니저로부터 연락을 받고 조금 얼떨떨했는데요, 좋은 기회를 얻었으니 열심히 해보겠습니다. ^^;;; 그동안 진행했던 내용을 BitLeaker에 다 넣어 만들어서 그런지, Europe에서 발표할 때 체력의 한계를 느꼈는데(후우...), 이번에 한 번 더 탈진할 것 같은 기분(?!)을 느낄 수 있겠네요. 쿨럭..;;;

그럼 Black Hat Asia 2020에서 뵙겠습니다!

학회 링크: https://www.blackhat.com/asia-20/

https://www.blackhat.com/asia-20/

최신의 프로세서들은 대부분 집합 연관 캐시(Set-associative cache)를 가지고 있습니다. 집합 연관 캐시는 캐시를 다수의 캐시 집합(Cache Set)으로 나누어 관리하며, 캐시 집합은 다시 캐시 웨이(Cache Ways) 또는 캐시 라인(Cache Line)으로 나누어서 관리합니다. 일단 간단한 캐시의 구조부터 보시지요. ^^;;; Michael Schwarz의 논문 Software-based Side-Channel Attacks and Defenses in Restricted Environments에서 가져온 그림입니다.

간단한 캐시의 구조 - 출처:Software-based Side-Channel Attacks and Defenses in Restricted Environments 

위의 그림은 워드(word)가 1바이트인 캐시라고 보시면 됩니다. 그림에 따르면 워드가 1바이트이므로 물리 메모리 주소(Physical Address)의 하위 b 비트가 데이터가 저장되는 위치를 나타내는데 사용될 수 있습니다. 그리고 그 상위의 n 비트는 캐시 집합(Cache set)을 선택하는 용도로 사용되고, 현재 그림에는 2의 n승만큼의 캐시 집합이 존재하는 상태입니다. 마지막으로 상위의 남은 비트는 해시 함수를 통해 어떤 값으로 바뀌는데 이를 태그(Tag)라고 합니다. 태그는 캐시의 웨이 영역에 저장되며, 캐시 교체(Cache Replacement)를 수행할 때 태그 정보와 함께 캐시에 접근한 시간, 횟수 등의 정보를 같이 활용하게 됩니다.

그럼 좋은 하루 되세요 ^^

작년에부터 Black Hat Asia의 리뷰보드로 활동하면서 제출된 발표자료를 살펴볼 일이 생겼습니다. 그중에서도 독보적이었던 것이 캐시와 TLB(Translation Look-aside Buffer)를 이용한 부채널 공격(Cache Side-Channel Attack)과 관련된 발표자료였는데요, IEEE Security and Privacy(S&P)와 ACM Conference on Computer and Communications Security(CCS) 등을 거쳐와서 내용 자체는 더할나위가 없는 수준이었습니다. 다만... 제가 부채널 공격을 전혀 모르고 있는 상태였기 때문에, 이 기회에 주요 공격 기법을 정리해봤습니다.

캐시를 이용한 부채널 공격 기법

캐시(Cache)를 이용한 부채널 공격의 요점은 캐시에 데이터가 존재하면 해당 데이터를 DRAM에서 읽어오는 것보다 빠르기 때문에 해상도(Resolution)이 아주 좋은 카운터가 있으면 데이터 접근 시간(Data Access Time)을 측정함으로써 이를 확인할 수 있다는 겁니다. 최근까지 사용된 캐시 부채널 공격 기법은 아래와 같습니다.

Evict + Time 기법

Evict + Time 기법은 공격자가 공격 대상이 접근하는 메모리 영역을 확인하기 위해 시도하는 기법입니다. Evict에서 추측할 수 있는 것처럼 공격 대상이 접근하는 메모리 영역을 모두 캐시에서 제거하고, 공격 대상을 실행해서 특정 동작, 예를 들면 암호 연산(Cryptographic Algorithm)을 수행하고 그에 걸린 시간을 측정하는 것이지요. 만일 Evict된 캐시에 접근한다면 캐시에 존재하는 데이터에 접근하는 것보다 실행 시간이 더 길어지니 이로써 실제로 수행되는 함수나 실제로 접근하는 데이터를 알 수 있습니다. 실제로 OpenSSL AES 알고리즘을 공격하는데 사용되었습니다.

Prime + Probe 기법

Prime + Probe 기법 역시 암호 연산을 공격하는데 사용되었는데요, 시나리오는 아래와 같습니다.

  • 먼저 공격자가 캐시를 기준값(Prime)으로 채워두고,
  • 공격자가 공격 대상을 실행합니다. 이때, 공격대상이 어떤 데이터를 읽었다면 그 데이터가 캐시에 없기 때문에 메모리에서 읽고 공격자가 채워넣은 기준값(Prime)을 Evict한 후에 해당 데이터를 채워 놓습니다.
  • 공격 대상의 실행이 완료된 후에, 공격자가 다시 캐시의 정보를 순차적으로 읽습니다. 이때, 특정 위치에서 시간이 많이 걸린다면, 해당 데이터는 공격 대상에 의해 Evict 되었기 때문입니다. 이를 바탕으로 해당 캐시의 위치를 파악하여 공격 대상이 접근한 메모리의 위치를 확인할 수 있습니다.

Prime + Probe는 공유 메모리가 필요치 않기 때문에, Intel SGX와 같이 고립된 환경도 공격이 가능한 장점이 있습니다. 부채널 공격으로 유명한 Michael Schwarz의 논문에 보면 아래와 같이 표현되어 있습니다.

Prime + Probe Attack - 출처: Software-based Side-Channel Attacks and Defenses in Restricted Environments

Flush + Reload 기법

Flush + Reload 기법은 x86 명령어 중에 clflush를 이용하는 공격 기법인데요, clflush가 논리적 주소(Logical Address)를 기반으로 해당 영역만 캐시에서 Flush할 수 있는 특성을 이용합니다. 공격 시나리오는 아래와 같습니다.

  • 암호 알고리즘이 들어있는 공유 라이브러리(Shared Library)를 clflush로 캐시에서 제거하고,
  • 공격 대상을 실행하여 암호 연산을 실행합니다.
  • 공격자가 공유 라이브러리가 있는 영역에 순차적으로 접근합니다. 이때, 공격 대상이 해당 영역에 접근한 경우, 이미 캐시에 존재하므로 그렇지 않은 영역과 접근 시간의 차이가 발생합니다. 이를 이용하면 접근 시간의 차이를 이용하여 암호 알고리즘이 수행된 실행 흐름이나 접근된 데이터를 확인할 수 있습니다.

Flush + Reload 기법은 물리 주소(Physical Address)를 알 필요가 없는 장점이 있습니다. 아래 그림은 Michal Schwarz의 논문에서 가져온 Flush + Reload 기법과 관련된 그림입니다.

Flush + Reload - 출처: Software-based Side-Channel Attacks and Defenses in Restricted Environments

Flush + Flush 기법

Flush + Flush 기법 역시 clflush를 이용한 기법입니다. Flush + Reload 기법과 차이점은 공격 대상을 실행한 뒤에 캐시에 순차적으로 접근하여 접근 시간을 보는 것이 아니라 순차적으로 clflush를 다시 수행해서 flush 시간을 측정하는 겁니다. 캐시에 데이터가 존재하면 clflush에 걸리는 시간이 길고, 캐시에 데이터가 존재하지 않으면 clflush에 걸리는 시간이 짧은 특성을 이용한 것입니다.

Evict + Reload 기법

Evict + Reload 기법은 clflush가 없는 경우, 캐시에서 알아내길 원하는 정보를 모두 제거하는 Evict를 사용하는 Flush + Evict 기법의 변형입니다. clflush를 직접 쓸 수 없는 Javascript나 clflush 명령어가 없는 ARM 프로세서 등에서 사용할 목적으로 변형된 것이지요.

TLB(Translation Look-aside Buffer)를 이용한 공격 기법

TLB를 이용한 공격 기법 역시 캐시를 이용한 공격 기법과 비슷한데요, 캐시 대신에 TLB를 이용하는 점이 차이점이고 역시나 TLB에 해당 영역의 (논리적 주소->물리적 주소) 맵핑 정보가 저장되어 있으면 접근 시간이 짧다는 특성을 이용합니다. Michal Schwarz가 쓴 Store-to-Leak Forwarding: Leaking Data on Meltdown-resistant CPUs논문에서 Store-to-Load Forwarding과 TLB를 이용하여 주요 정보를 유출하는 예를 보여줬습니다.

Microarchitecture가 그동안 많이 숨겨져 있었고 성능 최적화에 중점을 두고 발전하다보니 보안적인 문제가 다소 있는 것 같군요.

그럼 좋은 하루 되세요. ^^

소프트 스킬은 오래간만에 읽은 개발 관련 에세이인데요, “평범한 개발자의 비범한 인생 전략”에 이끌려 책을 과감히 샀습니다. 저자가 유머가 있는 분이라 중간마다 삽입된 개그 코드가 저와 잘 맞아서 술술 읽어 내려갈 수 있었습니다. 뭐랄까요… 개발자 특유의 유머 코드가 있달까요^;;;

소프트 스킬-출처 Yes24.com

내용은 사실 연봉협상부터 처세, 투자, 건강관리까지 다양한데요, 개발자 생활을 이제 시작하는 초년생이 본다면 상당히 도움이 되었을 것 같습니다. 특히나 연봉협상이나 처세 부분은 더욱 추천하고 싶네요. 다만, 투자 부분은 조금 실망스러운 부분이었는데, 결국 저자가 30대에 은퇴를 할 수 있었던 것도 개발 능력이 뛰어나다거나 엄청난 회사를 세웠다거나 하는 것이 아니라 투자로 인해 매달 나오는 금액이 자기가 목표한 금액 이상이었기 때문이거든요. ^^;;; 저는 개발자가 나름 뛰어난 능력으로 성공하는 판타지(?!)를 기대하고 읽었는데, 그래서 결말에서 조금 실망했습니다.

제가 비록 투자 부분의 아쉬움을 이야기하긴 했지만… 그 외의 다른 부분들은 의외로 공감한 부분이 많기 때문에 초년생분들께 추천하고 싶네요. 멘토에게 조언을 듣는다고 생각하고 편하게 읽으면 좋을 것 같습니다.

그럼 즐거운 명절 되세요 >ㅁ<)/

지난주에 유서 깊은 성균관대학교의 최형기 교수님 연구실에 세미나를 다녀왔습니다. 최형기 교수님 연구실에는 작년에도 방문했는데요, 그때는 가상화 기술(Virtualization Technology)과 관련한 세미나였습니다. 올해는 Black Hat Europe 2019에서 발표한 신뢰플랫폼모듈(Trusted Platform Module)과 보안 위협에 대한 내용으로 방문을 드렸는데, 역시나 연구실 분들의 열정이 대단하시더라고요. 날카로운 질문을 던지실 때마다 답변하느라 진땀을 뻘뻘 흘리다 보니 2시간이 훌쩍 지나있었습니다. ^^;;;

(발표자료는 https://www.blackhat.com/eu-19/briefings/schedule/#bitleaker-subverting-bitlocker-with-one-vulnerability-17245에서 보실 수 있습니다.)

세미나를 마치고 돌아오는 길에 여러 가지 생각이 들었는데요, 무엇보다 열정을 잃지 말아야겠다는 생각을 했습니다. 사실 연구소에서도 허리보다 위 쪽에 있어서 연구만 하기는 어려운 상황이라, 포지션에 대한 고민을 하고 있던 찰나였는데... 최형기 교수님과 그 연구원분들 덕분에 생각이 좀 정리가 되었습니다. 세미나를 즐겁게 들어주셔서 너무 고맙다는 말씀을 드리고 싶고, 교수님과 연구원분들께 모두 좋은 결과가 있으시길 기원합니다.

ps) 사진은 제가 방문했던 성균관대학교 제2공학관입니다. ^^

+ Recent posts