1. 程式人生 > >以摘要認證(Digest Authentication)方式偽登入某攝像頭

以摘要認證(Digest Authentication)方式偽登入某攝像頭

密碼已知。
分析發現,該攝像頭Web登入採用了Digest Authentication的方式。流程如下:
這裡寫圖片描述
下面大致看一下這部分的驗證流程:
1. 客戶端請求 /api/employees;
2. 服務端返回401未驗證的狀態,並且在返回的資訊中包含了驗證方式Digest,realm的值,QOP(quality of protection)只設置成auth,nonce為一串隨機值,在下面的請求中會一直使用到,當過了存活期後服務端將重新整理生成一個新的nonce值;
3. 客戶端接受到請求返回後,將username:realm:password進行HASH運算,假設運算後的值為HA1。又將請求方法和請求的路徑/api/employees進行HASH運算,假設運算後的值為HA2。再將HA1:nonce:nc:cnonce:qop:HA2進行HASH運算,得到的值放在response中。這裡的cnonce為客戶端生成的nonce值,而nc用於統計,假設開始時為00000001,下次請求後就變成了00000002,不一定每次都加1,但是後面請求中的nc值肯定大於前一次請求中的nc值。
4. 服務端收到請求後將驗證nonce是否過期,如果過期,那麼直接返回401,即第二步的狀態。如果沒有過期,那麼比較nc值,如果比前一次nc值小或者前一次根本沒有儲存的nc值,那麼也將直接返回401狀態。如果前面的驗證都通過,那麼服務端也將按照步驟3中計算最終HASH值的步驟計算出HASH值與客戶端的進行比較,然後比較客戶端提交過來的HASH值與服務端計算出來的HASH進行比較,不匹配返回401,匹配獲取請求的資料並返回狀態200。

總結一下:

HA1=MD5(username:realm:password)
HA2=MD5(method:digestURI)
response=MD5(HA1:nonce:nc:cnonce:qop:HA2)

偽登入程式碼:

#!/usr/bin/python

import hashlib
import httplib
import re

ip="the camera 's ip"
port="the camera 's port"
username="your username"
pwd="your password"

conn = httplib.HTTPConnection(ip, port,timeout=10
) # step 1: get nonce from 401 unauthorized response sent by the server conn.connect() headers={ "Host": ip+":"+port, "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.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", "Connection": "keep-alive", "Upgrade-Insecure-Requests": "1" } conn.request("GET", "http://"+ip+":"+port+"/", "",headers) res = conn.getresponse().getheader('WWW-Authenticate') conn.close() nonce =re.findall(r'nonce="(.*?)"',res)[0] realm=re.findall(r'realm="(.*?)"',res)[0] qop=re.findall(r'qop="(.*?)"',res)[0] opaque=re.findall(r'opaque="(.*?)"',res)[0] print "login successfully..." # step 2: GET http://ip:port/upgrade.htm urI="/upgrade.htm" method="GET" nc="00000001" cnonce="12234dce7db449b5" # cal HA1 m = hashlib.md5() m.update(username+":"+realm+":"+pwd) HA1 = m.hexdigest() # cal HA2 m = hashlib.md5() m.update(method+":"+urI) HA2 = m.hexdigest() # cal client response m = hashlib.md5() m.update(HA1+":"+nonce+":"+nc+":"+cnonce+":"+qop+":"+HA2) response= m.hexdigest() # GET http://ip:port/upgrade.htm headers={ "Host": ip+":"+port, "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.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", "Connection": "keep-alive", "Upgrade-Insecure-Requests": "1", "Authorization": 'Digest username="'+username+'", realm="'+realm+'", nonce="'+nonce+'", uri="'+urI+'", algorithm=MD5, response="'+response+'", opaque="'+opaque+'", qop='+qop+', nc='+nc+', cnonce="'+cnonce+'"' } conn.connect() conn.request(method, "http://"+ip+":"+port+urI, "", headers) res = conn.getresponse() print res.read() conn.close()