原文:http://bbs.pediy.com/showthread.php?t=162099
校園一卡通的快速破解
校園一卡通在高校被廣泛使用,能夠方便地實現轉賬消費一體化。在學校內可用於食堂,浴室,超市的消費,工作場所的門禁卡。每個學校在實現一卡通方案時會根據特有環境進行定制,下文所討論的情形限定於我所在的學校,未必放之四海而皆准。
校園卡使用了Mifare Classic 4k卡片承載用戶信息,但只有前6個區被使用於存儲個人信息。6個區使用不同口令實現訪問控制(並且每張卡片的口令都不盡相同),讀取和改寫卡片內容都需要口令認證。泛講Mifare Classic的破解,可以借助成熟的工具集nfc-tools (http://code.google.com/p/nfc-tools/downloads/list)。mfoc大概需要10到30 min實現卡口令的破解以及卡數據的dump。如果只是讀寫自己的一卡通,上述的這種通用的方法簡單易行,綽綽有余。但換個場景,局限性就暴露出來了。
設想你走在食堂,突然想復制排在你前面人的卡玩玩 如果使用mfoc,先把他手里的卡拿走,跑到電腦前,完成卡內容dump,再還到他手里。其不可行性不言而喻,通用的破解方法在類似場景就只是理論可行
考慮到校園一卡通並不僅僅是一張通用性的Mifare,獲取卡口令就不再局限於mfoc了。為了操作卡片數據,顯然食堂和浴室的刷卡器具是有辦法直接獲取每張卡片的區域口令並由此修改卡片內消費數據的。如不幸它們獲取卡口令的方法是聯網到數據庫根據卡片UID進行查詢,那除了mfoc以外獲取卡口令的唯一方法就是拿到數據庫的訪問權限了。
幸運的是,事實並非如此,龐大的機構為了減少網絡數據的傳輸,卡口令其實是在本地根據預定算法加UID(UID是卡片內容的前四個字節,在未認證的情況下也可讀)計算出來的,網絡一般只傳輸交易流水。 換言之,如果能夠分析出器具計算卡口令的方法,那使用同樣的算法就可以快速獲取卡口令了。乍看來,獲取這種計算方法的途徑不止一條:
-- 弄到一套校園一卡通卡片讀寫程序的源碼
-- 搬回來食堂刷卡器進行單片機程序的逆向分析
-- 搬回來一台一卡通沖值終端接上鍵盤鼠標然后逆向分析上面的PC程序
這三個方法要么理想化要么興師動眾,究其原因也是考慮的太泛了。具體問題具體分析的話,發現了另一個容易被忽略的途徑:學校公寓進門時要刷卡,類似一種出入人員登記。樓管人員每天就負責盯着電腦屏幕審視每一個持卡人的學號,姓名等個人信息。顯示信息的載體是一個網頁,內容會隨着每一次刷卡而變動。頁面加載的ActiveX控件實現UID的讀取以及卡內區域數據的獲取,當然也包括卡口令的計算。該頁面缺少權限審核,任何人鍵入網址都會加載ActiveX控件。
ActiveX控件只提供一個頂層接口,它會向下調用其它DLL實現扣費,UID獲取,聯網查詢信息,卡口獲取等操作。逆向分析這么盤根錯節的程序也是第一次,大致思路是使用IDA Pro找出可能包含獲取卡口令操作的函數,然后層層跟進以探究竟。從卡初始化到UID讀取然后根據UID計算卡口令,函數調用情況如下:
-> ActiveX控件
-> ReadChipControl::CreateThread
-> AIO_API::LoadLibrary(CardInterface.dll)
-> CardInterface::GetCardNo
-> CardInterface::sub_10002070
-> CardInterface::sub_10001570
-> CardInterface::sub_10023DD0
純粹的靜態分析雖然提示信息豐富,但很多參量無法辨析其含義,所包含的導出函數也無從篩選。一些DLL是使用LoadLibrary動態加載,純粹的靜態分析很難連續跟蹤變量的傳遞和變化。由於沒有校園卡配套的讀卡器讓ActiveX控件完整地進行一輪讀卡,只能使用OllyDBG在動態跟蹤時強制修改跳轉指令使讀卡行為發生。動態跟蹤過程中可以通過查看內存直接看到函數計算結果,快速確定假設是否合理。最終篩選出用於計算卡口令的函數也是借助動態分析:sub_10001570的一個傳入參數在調用結束后被改寫為指向keyB內存區域(之前用mfoc破解中每張卡的每個區域的keyB都是b0 b1 b2 b3 b4 b5。但對於校園卡,keyB並不能用於讀寫卡片)。正是這種直觀的數據迅速明確了sub_10001570是和計算卡口令相關的函數。
進一步跟蹤發現sub_10023DD0(const, v18, outkeyA)的第三個參數指向的內存用來存放計算出來的Mifare Classic 卡片keyA,也即讀寫卡片時用到的口令。const在多次跟蹤中發現其指不隨輸入的UID變化,進一步跟蹤發現其值是直接從data段拷貝過來的。因此v18就成了唯一決定keyA的參數了。V18指向一個8字節的區域,在計算0區的keyA時V18被填充為
DWORD UID
DWORD UNK
UNK為一些不明含義的字節,如果在這個函數調用前把UID強制改為我一卡通UID,計算出來的keyA並不正確。最有可能的原因前序多處跳轉被Ollydbg強制修改而導致程序的執行不完整,進而使得UNK計算不正確。不過好在UNK只有四個字節大小,與其繼續在反匯編的代碼中路漫漫其修遠兮,不如干脆把這個UNK暴力破解出來。
暴力破解的思路非常基本,加載CardInterface.dll,將參數按照上面的分析構造好,每次遞增UNK,然后調用sub_10023DD0。將計算出來的keyA和正確值比對,如果正確就輸出UNK。用Core i7的八個核分別跑一個破解線程跑了不到一天就得到了UNK:02 36 63 00,用同樣的方法也得到了用於計算1區的keyA的UNK為02 36 63 01。規律一下出來了,前面三個字節雖然不懂其含義,但最后一個字節顯然是區號。這種方法可以計算0,1,4,5區口令。
理所當然的用這種方法計算2,3區時發現keyA計算不正確,嘗試暴力破解很久也沒有搜索到正確的UNK。不過由於前兩區的口令已經找到了計算方法,大大增加了信心,也明確了待分析的函數片段。2區主要存儲的是用戶的賬戶余額等信息,再次分析時就重點考察那些名字看起來和修改賬目相關的導出函數,然后遞進看哪里計算了這些區域的卡口令。當計算2,3區時,V18填充方案已經發生了改變
WORD UNK1
DWORD UID
WORD UNK2
難怪之前暴力破解完全沒有反應,重新修改暴力破解程序,很快將UNK1(02 19)和UNK2(02 36)跑了出來。sub_10023DD0里面沒有和卡片溝通以及網絡通信的操作,全部都是數學運算,應該是預定的密碼學算法,由於暫時沒有找到抽練出當中算法的方法,就沒深究。目前已經知道如何調用CardInterface庫內函數根據UID計算卡片區域口令了。
但為了易於使用,獲取卡口令以及讀取卡片內容的功能使用手機或平板的NFC功能實現。可是只能在Windows下進行,如何轉化為Android下可識別的代碼目前還在思考中。暫時的解決方案是在公網服務器使用IIS搭建的CGI調用這個DLL,然后通過網絡把結果返回給平板或者手機。平板或手機使用該組口令就可以讀取卡片數據了,數據含義辨識雖然無法做到盡善盡美,但基本的信息稍加分析都是可以辨識的。例如姓名學號這些信息都是ASCII方式直接存儲,一眼就可以瞟出來。余額,消費記錄,校驗值等可能需要多次消費后進行數據比對...
-> 智能終端使用NFC獲取UID,發送到公網服務器
-> 服務器CGI獲得卡UID,調用DLL計算卡口令
-> 服務器將卡口令返還給智能終端
-> 智能終端讀取卡數據並辨識含義顯示
下面的兩張圖是在Nexus 7下工作的效果,如果網絡通暢,破解卡片-讀卡內容不過1秒上下。
*轉載請注明來自看雪論壇@PEdiy.com