代碼中被植入了惡意刪除操作,太狠了!
背景
在交接的代碼中做手腳進(jìn)行刪庫(kù)等操作,之前只是網(wǎng)上聽(tīng)說(shuō)的段子,沒(méi)想到上周還真遇到了,并且親自參與幫忙解決。
事情是這樣的,一老板接手了一套系統(tǒng),可能因?yàn)殡p方在交接時(shí)出現(xiàn)了什么不愉快的事情,對(duì)方不提供源代碼,只是把生產(chǎn)環(huán)境的服務(wù)器打了一個(gè)鏡像給到對(duì)方。
對(duì)方拿到鏡像恢復(fù)之后,系統(tǒng)起來(lái)怎么也無(wú)法正常處理業(yè)務(wù),于是就找到我?guī)兔词鞘裁丛颉=?jīng)過(guò)排查,原來(lái)交接的人在鏡像中做了多處手腳,多處刪除核心數(shù)據(jù)及jar包操作。下面來(lái)給大家細(xì)細(xì)分析排查過(guò)程。
排查過(guò)程
由于只提供了鏡像文件,導(dǎo)致到底啟動(dòng)哪些服務(wù)都是問(wèn)題。好在是Linux操作系統(tǒng),鏡像恢復(fù)之后,通過(guò)history命令可以查看曾經(jīng)執(zhí)行了哪些命令,能夠找到都需要啟動(dòng)哪些服務(wù)。但服務(wù)啟動(dòng)之后,業(yè)務(wù)無(wú)法正常處理,很多業(yè)務(wù)都處于中間態(tài)。
原本系統(tǒng)是可以正常跑業(yè)務(wù)的,打個(gè)鏡像之后再恢復(fù)就不可以了?這就奇怪了。于是對(duì)項(xiàng)目(jar包或war)文件進(jìn)行排查,查看它們的修改時(shí)間。
在文件的修改時(shí)間上還真找到了一些問(wèn)題,發(fā)現(xiàn)在打鏡像的兩個(gè)小時(shí)前,項(xiàng)目中一個(gè)多個(gè)項(xiàng)目底層依賴(lài)的jar包被修改過(guò),另外還有兩個(gè)class文件被修改過(guò)。
于是,就對(duì)它們進(jìn)行了重點(diǎn)排查。首先反編譯了那兩個(gè)被修改過(guò)的class文件,在代碼中找到了可疑的地方。
可疑代碼
在兩個(gè)被修改的類(lèi)中都有上述代碼。最開(kāi)始沒(méi)太留意這段代碼,但直覺(jué)告訴我不太對(duì),一個(gè)查詢(xún)業(yè)務(wù)里面怎么可能出現(xiàn)刪除操作呢?這太不符合常理了。
于是仔細(xì)閱讀上述代碼,發(fā)現(xiàn)上述紅框中的代碼無(wú)論何時(shí)執(zhí)行最終的結(jié)果都是id=1。你是否看出來(lái)了?問(wèn)題就出在三目表達(dá)式上,無(wú)論id是否為null,id被賦的值都是1??吹竭@里,也感慨對(duì)方是用心了。為了隱藏這個(gè)目的,前面寫(xiě)了那么多無(wú)用的代碼。
但只有這個(gè)還不是什么問(wèn)題,畢竟如果只是刪除id為1的值,也只是刪除了一條記錄,影響范圍應(yīng)該有限。
緊接著反編譯了被修改的jar包,依次去找上述刪除方法的底層實(shí)現(xiàn),看到如下代碼:
刪除操作
原來(lái)前面?zhèn)鬟f的id=1?是為了配合where?條件語(yǔ)句啊,當(dāng)id=1?被傳遞進(jìn)來(lái)之后,就形成了where 1=1?的條件語(yǔ)句。這個(gè)大家在mybatis中拼接多條件語(yǔ)句時(shí)經(jīng)常用到。結(jié)果就是一旦執(zhí)行了上述業(yè)務(wù)邏輯,就會(huì)觸發(fā)刪除T_QUART_DATA全表數(shù)據(jù)的操作。
而T_QUART_DATA表中是用于存儲(chǔ)觸發(fā)定時(shí)任務(wù)的表達(dá)式,到這里也就明白了,為啥前面的業(yè)務(wù)跑不起來(lái),全部是中間態(tài)了。因?yàn)橐坏┰跇I(yè)務(wù)邏輯中觸發(fā)開(kāi)關(guān),把定時(shí)任務(wù)的cron表達(dá)式全部刪除,十多個(gè)定時(shí)任務(wù)全部歇菜,業(yè)務(wù)也就跑步起來(lái)了。
找到了問(wèn)題的根源,解決起來(lái)就不是啥事了,由于沒(méi)有源代碼,稍微費(fèi)勁的是只能把原項(xiàng)目整個(gè)反編譯出來(lái),然后將改修改地方進(jìn)行了修改。
又起波折
本以為到此問(wèn)題已經(jīng)解決完畢了,沒(méi)想到第二天又出現(xiàn)問(wèn)題了,項(xiàng)目又跑不起來(lái)了。經(jīng)過(guò)多方排查和定位,感覺(jué)還有定時(shí)任務(wù)再進(jìn)行暗箱操作。
于是通過(guò)Linux的crontab命令查看是否有定時(shí)任務(wù)在執(zhí)行,執(zhí)行crontab -e或crontab -l,還真看到有三個(gè)定時(shí)任務(wù)在執(zhí)行。跟蹤到定時(shí)任務(wù)執(zhí)行的腳本中,而且明目張膽的起名deleteXXX:
刪除腳本
而在具體的腳本中,有如下執(zhí)行操作:
刪除核心依賴(lài)包
這下找到為什么項(xiàng)目中第二天為啥跑不起來(lái)了,原來(lái)Linux的定時(shí)任務(wù)將核心依賴(lài)包刪除了,并且還會(huì)去重啟服務(wù)。
為了搞破壞,真是煞費(fèi)苦心啊。還好的是這個(gè)jar包在前一天已經(jīng)反編譯出來(lái)了,也算有了備份。
小結(jié)
原本以為程序員在代碼中進(jìn)行刪庫(kù)操作或做一些其他小手腳只是網(wǎng)絡(luò)上的段子,大多數(shù)人出于職業(yè)操守或個(gè)人品質(zhì)是不會(huì)做的。沒(méi)想到這還真遇到了,而且對(duì)方為了隱藏刪除操作,還做了一些小偽裝,真的是煞費(fèi)苦心啊。如果有這樣的能力和心思,用在寫(xiě)出更優(yōu)秀的代碼或系統(tǒng)上或許更好。
當(dāng)然,不知道他們?cè)诮唤拥倪^(guò)程中到底發(fā)生了什么,竟然用這樣的方式對(duì)待昔日合作的伙伴。之所以寫(xiě)這篇文章,是想讓大家學(xué)習(xí)如何排查代碼問(wèn)題的過(guò)程,畢竟用到了不少知識(shí)點(diǎn)和技能,但這并不是教大家如何去做手腳。無(wú)論怎樣,最起碼的職業(yè)操守還是要有的,這點(diǎn)不接受反駁。