以摘要認證(Digest Authentication)方式偽登入某攝像頭
阿新 • • 發佈:2019-01-09
密碼已知。
分析發現,該攝像頭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()