【趣话编程】一个整数+1引发的灾难
原文鏈接
帝國危機
夜幕降臨,喧囂褪去,繁忙的Linux帝國漸漸平靜了下來,誰也沒有想到,一場改變帝國命運的風(fēng)暴正在悄然而至......
“咚咚!”,帝國安全部長辦公室的敲門聲,打破了夜晚的寧靜。
“部長,剛剛發(fā)現(xiàn)有線程在修改passwd文件”,原來是文件系統(tǒng)部門的小黑到訪。
“這有什么大驚小怪的?只要有root權(quán)限,這是允許的嘛!”,安全部長沒有抬頭,繼續(xù)看著每天的系統(tǒng)日志。
“部長,重點在于這線程不是從系統(tǒng)調(diào)用進入內(nèi)核,而是從中斷入口進來的”
安全部長愣了一下,約莫0.2ms之后,放下了手里的日志,站了起來。
“你是說,他是通過中斷描述符表(IDT)進來的?”
小黑點了點頭。
“小王,你趕緊跟他過去IDT看一下,調(diào)查清楚速來報我”,部長對著一旁的助理說到。
小王點了點頭,準(zhǔn)備出發(fā),剛走到門口,又被部長叫住了。
“等等!此事非同小可,我還是親自去一趟吧”
IDT修改謎案
安全部長隨即出發(fā),來到IDT所在的地方,這里一切如舊,未見有何異樣。
部長指著這一排門墻問道:“他是從哪道門進來的?”
“4號”,這時,看守IDT大門的白發(fā)老頭聞訊走了過來回答到。
“奇怪了,IDT表中的函數(shù)入口,都是帝國安排好了的,講道理沒有哪一個會去修改passwd文件才對”,部長看著這些表項,低頭自語。
“部長,這我得跟您匯報一下,那小子進來之前,把第四項的入口地址高32位改成了0x00000000,進來之后他才給恢復(fù)成了0xFFFFFFFF”,老頭說完,拿出了IDT表項的結(jié)構(gòu)圖展了開來:
部長聽完猛的一抬頭,“高32位變成了0x00000000,那整個函數(shù)入口地址不就指向了用戶態(tài)地址空間了?”
小黑和小王都不敢說話,大家都知道這后果有多嚴(yán)重,天知道那家伙利用內(nèi)核權(quán)限執(zhí)行了用戶空間的什么代碼。
“不對,在他進來之前,一個用戶空間的線程怎么能改IDT的內(nèi)容呢?他沒權(quán)限訪問才對,我不信!”
“這個我倒是知道,他改的是時候,我特地留意了一下他的調(diào)用堆棧,不是在用戶空間,是從內(nèi)核空間的函數(shù)——perf_swevent_init方向來的”,老頭說到。
整數(shù)+1的悲劇
部長二話沒說,又帶著大家直奔perf_swevent_init函數(shù)而去。
“老伯,您可還記得具體是哪個位置?”,部長問到。
“就是從那個19行那個static_key_slow_inc函數(shù)過來的”
“讓我看一下”,小王擠到前面來,想在部長面前露一手。
“嗯,這個static_key_slow_inc做的事情是把一個整數(shù)執(zhí)行了原子+1操作。不過它操作的是perf_swevent_enabled數(shù)組,跟IDT八桿子打不到一塊兒去,怎么能修改到IDT呢?”,小王摸了摸頭,往后退了兩步,瞧著是沒看出什么問題。
“不見得!”,部長仍然是緊鎖著眉頭,開口說到,“你們看,它是通過event_id這個數(shù)字作為下標(biāo)來訪問數(shù)組元素,要是這個event_id出錯訪問越界,指向IDT,也不是沒有可能啊!”
小王趕緊掃了一眼event_id,隨后便露出了失望的表情,“不會的,第9行有檢查,你看,超過8以后就會通不過檢查”
線索在這里被切斷了,本來指望在perf_swevent_init這個函數(shù)這里尋找IDT被修改之謎,看來要無功而返了。
不知不覺,時間已經(jīng)很晚了,部長一行決定先回去,再從長計議。
部長走了幾步,見小王沒有跟上來,便回頭叫了他一聲。
“部長請留步,我好像感覺哪里不太對勁”,小王此刻也皺起了眉頭。
“你發(fā)現(xiàn)了什么?”,部長和小黑他們又走了回來。
“部長,你看第3行,這個event_id是一個int型的變量,也就是說這是一個有符號數(shù)。”,小王說到。
“有符號數(shù)怎么了?”,小黑也忍不住開口問了。
“如果······”
“_如果event_id變成了一個負(fù)數(shù),它將能越界訪問數(shù)組,并且還能通過第9行的大小檢查!_”,沒等小王說完,部長道破了玄機!
眾人再一次將目光聚集在了這個event_id上,打算看一下第三行給它賦值的event->attr.config是個什么來頭。
首先是perf_event中的attr成員變量:
接著是perf_event_attr中的config成員變量:
struct perf_event_attr {// ...__u64 config;// ... };看到最后,部長和小王都倒吸了一口涼氣,這config竟然是個64位無符號整數(shù),把它賦值給一個int型變量不出問題就怪了!
見大家都不說話,小黑撓了撓頭,弱弱的問到:“怎么了,你們怎么都不說話,這有什么問題嗎?”
小王把小黑拉到一邊,“問題大了,你看我要是把一個值為0xFFFFFFFF的config賦值給event_id,event_id會變成什么?”
“負(fù),負(fù),負(fù)1?”
“沒錯,有符號數(shù)的最高位是用來標(biāo)記正負(fù)的,如果這個config最高位為1,后面的位經(jīng)過精心設(shè)計,不僅能瞞天過海騙過那里第9行的驗證,還能將某個位置的數(shù)字進行一個原子+1操作。”,小王繼續(xù)說道。
“不錯嘛小王,有進步!”,不知何時部長也走了過來,被部長這么一夸,小王有些不好意思了。
“聽了半天,不就是越界把某個地方的數(shù)加了1嘛,有什么大不了的?”,小黑一臉不屑的樣子。
小王一聽連連搖頭,“你可不要小瞧了這個加1的行為,要是加在某些敏感的地方,那可是要出大事的!“
小黑有些疑惑,“比如說呢?”
“比如記錄中斷和異常的處理函數(shù)的IDT,又比如記錄系統(tǒng)調(diào)用的sys_call_table,這些表中的函數(shù)地址都位于帝國內(nèi)核空間,要是這個加1,加的不是別人,而是這些表中的函數(shù)地址,那可就麻煩了。”,小王繼續(xù)說到。
“我聽明白了,可是就算加個1,也應(yīng)該不是什么大問題吧?”
小王嘆了口氣,“看來你還是不明白,我以這次被修改的IDT表為例,給大家再看一下表中的表項——中斷描述符的格式”
“IDT中的中斷/異常處理函數(shù)的地址不是一個完整的64位,而是拆成了幾部分,其中高32位我給大家紅色標(biāo)示出來了,在64位Linux帝國,內(nèi)核空間的地址高32位都是0xFFFFFFFF,如果······”
“如果利用前面的event_id數(shù)組下標(biāo)越界訪問,把這個地方原子+1,那就變成了0,對不對?”,小黑總算明白了。
真相大白
安全部長為小王的精彩分析鼓起了掌,“不錯不錯,大家都很聰明!事到如今,我們來復(fù)盤一下吧!”
- 第一步:精心設(shè)計一個config值,從應(yīng)用層傳入內(nèi)核空間的perf_swevent_init函數(shù)
- 第二步:利用帝國內(nèi)核漏洞,把一個64位無符號數(shù)賦值給一個int型變量,導(dǎo)致變量溢出為一個負(fù)數(shù)。
- 第三步:利用溢出的event_id越界訪問perf_swevent_enabled,指向IDT的表項,將第四項中斷處理函數(shù)的高32位進行原子+1
- 第四步:修改后的中斷處理函數(shù)指向了用戶空間,提前在此安排惡意代碼
- 第五步:應(yīng)用層執(zhí)行int 4匯編指令,觸發(fā)4號中斷,線程將進入內(nèi)核空間,以至高權(quán)限執(zhí)行提前安排的惡意代碼。
事情總算是水落石出,安全部長回去之后便上報帝國總部,修復(fù)了此漏洞,將event_id的類型從int修正為u64。
即便如此,部長的心情卻并沒有輕松多少,未知的敵人已經(jīng)闖入帝國,它們是誰?做了什么?現(xiàn)在藏在哪里?一個又一個的問題還在不斷在腦中閃現(xiàn)······
未完待續(xù)······
彩蛋
一個悶熱的下午,風(fēng)扇飛速的旋轉(zhuǎn),熱得人喘不過氣。
部長的辦公室出現(xiàn)了一個熟悉的身影,走近一看原來是小馬哥。
“部長,nginx公司又出事了”
預(yù)知后事如何,請關(guān)系后續(xù)精彩······
來源 | 編程技術(shù)宇宙
作者 | 軒轅之風(fēng)
原文鏈接:https://developer.aliyun.com/article/768661?
版權(quán)聲明:如果您發(fā)現(xiàn)本社區(qū)中有涉嫌抄襲的內(nèi)容,歡迎發(fā)送郵件至:developerteam@list.alibaba-inc.com 進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,本社區(qū)將立刻刪除涉嫌侵權(quán)內(nèi)容。總結(jié)
以上是生活随笔為你收集整理的【趣话编程】一个整数+1引发的灾难的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 阿里云交通数据中台解决方案,打造“数字化
- 下一篇: 正确入门Service Mesh:起源、