教務網現驚天大 bug?!談談弱密碼如何暴露你的信息
1. 漏洞發現
最近在爲之前寫的爬蟲做批註的時候發現了一段有趣的代碼,代碼如下
<!–more–>
MisUtils.initAttempt() while MisUtils.descAttempt(): self.response = self.session.send(prepareBody) if self.response.status_code == 200: break if not MisUtils.descAttempt(): Logger.log('Up to max attempts!', ['Maybe remote server unreachable'], level=Logger.error) return False if re.search('用戶名不存在', self.response.text): print(Logger.log('No such a user!', ['Cleaning password file'], level=Logger.error)) reInput = True elif re.search('密碼錯誤', self.response.text): print(Logger.log('Wrong password!', ['Cleaning password file'], level=Logger.error)) reInput = True elif re.search('請輸入驗證碼', self.response.text): print(Logger.log('Please input vertify code!', ['Retrying...'], level=Logger.error)) elif re.search('驗證碼錯誤', self.response.text): print(Logger.log('Wrong vertify code!', ['Retrying...'], level=Logger.error)) else: print(Logger.log('Login successfully!', ['UserName: ' + MisUtils.confDict['userName']], level=Logger.error)) MisUtils.dumpConfFile() break
當時寫的時候沒注意,現在回過頭來看看,驚奇地發現教務網對於用戶名和密碼的驗證順序是如下的優先級:
- 首先驗證用戶名是否存在
- 再驗證密碼是否正確
- 然後驗證碼是否爲空
- 最後纔是驗證碼是否正確
這。。。我只想說,誰寫的驗證邏輯。。。我要這驗證碼有何用?!!!
2. 漏洞驗證
接下來去教務網上驗證一下整個驗證邏輯是否如所想的那樣
- 首先在用戶名輸入框中隨意輸入一串數字,並保持密碼和驗證碼都爲空,嘗試登錄發現 彈出"用戶名不存在"的 JS 腳本
- 再輸入我正確的學號,仍保持密碼和驗證碼都爲空,嘗試登錄彈出"密碼錯誤"的 JS 腳本
- 輸入正確的學號和密碼,並保持驗證碼爲空,嘗試登錄彈出"請輸入驗證碼"的 JS 腳本
- 輸入正確的學號和密碼,但是輸入錯誤的驗證碼,嘗試登錄彈出"驗證碼錯誤"的 JS 腳本
通過以上流程,可以總結得以下兩條有價值的信息:
- 當你輸入的用戶名不存在時,不管密碼和驗證碼輸的啥,總是會提示"用戶名不存在"; 當你輸入的用戶名存在,並且密碼爲空時,會提示"密碼錯誤"。
- 當你輸入的用戶名存在,但是密碼錯誤時,不管驗證碼輸的啥,總會提示"密碼錯誤"; 當輸入的密碼正確並且驗證碼爲空時,會提示"請輸入驗證碼"。
3. 漏洞利用
通過上面的兩條信息可以至少做到兩件事情 ### 枚舉用戶名目前所有在讀研究生的入學時 間大部分爲 2013-2017年,學號的前四位爲入學年份,中間三位與所在的專業有關,最後三 位爲流水號。對所有形如2013XXXXXX 的學號進行嘗試登錄,根據信息 1,能夠枚舉出所有 13年入學的學生學號。
3.1. 暴破
利用上一步得到的用戶名列表,再配合字典就可以進行在線的密碼暴破,這種破解方法一般 來說是非常低效的,嚴重依賴於網絡狀況和服務器的機能。嘗試發現在單線程的情況下,每 秒只能對服務器進行1k 次左右的訪問。也就是說如果使用 16 位的數字加字母作爲密碼, 最多需要2.5 百萬億年的時間才能成功破解。看起來是非常安全吧。然而實際情況又是什麼 樣呢?教務網的初始密碼爲身份證號中的生日,一般來說我們在第一次登錄時都會改密碼, 但還總會有人抱着僥倖的心理用初始密碼,這就爲暴破帶來了捷徑。以生日作爲密碼具有非 常明顯的結構,如 2017 年入學學生的生日大多在1994-1996 之間,利用下面簡單的字典生 成器可得到 1994-1996年所有的日期列表:
big_month_ = [1, 3, 5, 7, 8, 10, 12] small_month_ = [4, 6, 9, 11] def gen_(begin, end): password_ = [] for year in range(begin, end + 1): for month in range(1, 13): if month in big_month_: month_len = 31 elif month in small_month_: month_len = 30 elif month == 2: if year % 4 != 0 or (year % 100 == 0 and year % 400 != 0): month_len = 28 else: month_len = 29 else: month_len = 31 for day in range(1, month_len + 1): password_.append(str(year) + str(month).zfill(2) + str(day).zfill(2)) with open('password.dict', 'w') as fr: for password in password_: fr.write(password) fr.write('\n') def main(): gen_(1994, 1996)
利用得到的用戶名列表和密碼字典就可以對所有未修改的用戶密碼進行暴破。
4. 總結
最後結果怎麼樣?使用 100 個線程,並設置單個線程的訪問頻率爲 1次/秒進行暴破,10 分鐘破解出了接近 40個用戶密碼!並且平均每四個人裏就有一個人沒有修改密碼!可能很 多人會覺得教務網密碼不涉及錢財也就不那麼在意。但其實教務網上有詳細的個人資料,包 括你的姓名、照片和家庭住址。另外還有你的課表,你某時某刻會在哪個教室出現都會被人 知道啊!所以,弱密碼的危害顯而易見,很多的信息泄漏也都是由弱密碼引起的。給自己換 個複雜點的密碼吧,畢竟密碼這種東西一寸長一寸強。現在也快到年底了,騙子也要賺錢備 年貨了,希望大家能夠保護好自己的信息,不要給騙子可乘之機。最後討伐一下這個軟件提 供商,一個商業化的系統裏面竟然有如此明顯的邏輯漏洞,只希望快點被修復吧。