如果你忘记了DotnetNuke站点的host和admin密码…

Posted in .Net Programming on 七月 10th, 2008 by Kang

今儿在办公室发生一很无语的事, 有关我的一个DNN(dotnetnuke)项目的. 这个项目是几个月前开始做的, 一个Project Issue Registration System, 后台数据库都已经做完了, 只剩下网站前台, 由于这是个公司的内部项目, 后来的几个网站都是政府还有其它公司的, 优先级比较高, 所以一直在做后来的项目而把这个就一直撂置了, 这几天刚刚把后来的项目都sign off了, 开始有闲, 准备把这个PIRS捡起来做完.

结果把项目打开一运行就傻了, 这是个基于DNN框架的网站, 我把host和admin密码全忘了, 因为当初也是还在开发阶段, 所以根本没设别的用户, 试遍了我自己常用的密码组合, 还有公司的密码组合, 全不行.. 直接无语.  由于host密码不能用密码找回找到, 所以只能走数据库这一步了.

上SQL server, 进去了直奔dbo.aspnet_Membership, 密码是带salt和DNN自己另设的密文加密的, 我头脑一热加手一抖直接把admin和host的密码和salt删了, 然后想用其它站数据库里的密码和salt复制过来, 结果没用, 然后又用hash generator 做了数个加密后的密文+ salt 复制过去, 依然无效. 然后突然想起来,  DNN的加密机制不太一样, 每个DNN站的web.config里自己有个独特的密文, 不同站的不一样, 这一下直接不能上了. 这回事情比原来还严重了, 不仅密码忘了, 原来的密码也没了, salt也没了. -.-|||

OK, 事已至此, 咱上google上找一下先吧, 别自己再乱搞了. 搜索一下, 还真找出不少方案来. 后来才知道, 这些方案在我目前的情况下都不适用, 不过那是后话, 我先把这些解决办法列出来, 方便一下看这篇文章的人.

 

方案一

这个理论上适用于所有情况(但是实际上是痴人说梦..)

  1. 在你DNN网站上注册一个新用户. 如果你的注册按钮被你在站点设置里关掉了, 那就去SQL server上把dbo.Portals表里的UserRegistration值改成2.
  2. 用你开的registration建个新用户(把密码记住.. -.-|||)
  3. 再进数据库, 找到dbo.ASPNet_Membership表
  4. 到你新建的那个用户那一行中把password, passwordsalt的密文都复制到你的host用户那一行的同样列中(host是superuser).
  5. 重新用你的host和你新设用户的密码登录DNN然后再把你新建的那个用户删掉就可以了.

很不幸的是, 我的DNN网站的出于当初开发时的安全考虑被我关闭了, 而且他所说的改UserRegistration值的方法是无效的, 改过后Register依然没有在login module中出现.

 

方案二

这个是需要你已经知道一个帐号的用户名和密码, 然后用下面的SQL query实现对host的密码的替换.

1
2
3
4
5
SELECT password, passwordformat, passwordsalt
FROM aspnet_membership am 
INNER JOIN aspnet_users auON (au.userid = am.userid) 
INNER JOIN aspnet_applications aaON (au.applicationId = aa.applicationid)WHERE au.username = 'admin' 
AND aa.applicationname = 'DotNetNuke'

 

然后把你得到的password, passwordformat, passwordsalt复制到一个地方, 下面的query中从’TestUser’那行开始, 分别对应 用户名, password, passwordsalt, ChangeTime, passwordFormat. 所以你用你刚复制下来的替换下面这几个就可以了.

 

1
2
3
4
5
6
7
8
9
DECLARE @changeDate datetime
SET @changeDate = getdate()
--set the password
exec aspnet_Membership_setPassword 'DotNetNuke', 
'TestUser', 
'DM1tZvBjM+27Eck5eI1TWFeG42XuJnMuin3jqFOtMjS83RN6d7dFbQ==', 
'4e5Bb5jOOMYu/JFXVdRmlA==',
@changeDate, 
2

 

那么很不幸的, 这个方案也是无效的因为我根本没有任何一个知道密码的帐号.而且据我所知DNN 4.4.1以后的版本也不再使用Application ID了, 而是使用Application Name. 所以这里也需要做些小修改才能生效.

 

方案三

在SQL server 运行这个query

1
2
3
4
5
6
7
8
9
10
11
12
Declare @UserName NVarChar(255)
Declare @NewPassword NVarChar(255)
Declare @PasswordSalt NVarChar(128)
Declare @Application NVarChar(255)
-- 在下面的单引号间输入你要做的修改(用户名, 密码)
-- 别留任何空格除非你特意要留..
SET @UserName = 'admin' -- 这个是DNN默认的admin用户, 这里我们要改成host
 
SET @NewPassword = 'newpassword' -- 这里是你的新密码
SET @Application = (SELECT [ApplicationID] FROM aspnet_Users WHERE UserName=@UserName)
SET @PasswordSalt = (SELECT PasswordSalt FROM aspnet_Membership WHERE UserID IN (SELECT UserID FROM aspnet_Users WHERE UserName=@UserName))
Exec dbo.aspnet_Membership_ResetPassword @Application, @UserName, @NewPassword, 10, 10, @PasswordSalt, -5

根据这个query来看, 这也是一个DNN 4.4.1以前的方案,但是我做了小量修改, 依然不能在新版本的DNN上有任何效果. 因为他是基于你没有删SALT的基础上的, 而很不幸, 我把密码+SALT都删没了.  失败.

 

方案四

这个是一个老外的query, 无数老外跟着comment了说好用, 但是很不幸, 他的原理是跟方案一相同的, 也需要你有一个已知密码的帐号, 只不过用query的形式完成了.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
DECLARE @databaseName VARCHAR(128)
SELECT @databaseName = DB_NAME()
 
PRINT 'RESET PASSWORD IN DATABASE : ' + @databaseName
PRINT '-----------------------------' + REPLICATE('-', DATALENGTH(@databaseName ));
 
DECLARE @knownUserName NVARCHAR(128)
DECLARE @lostUserName NVARCHAR(128)
DECLARE @lostUserId NVARCHAR(128)
DECLARE @knownPassword NVARCHAR(128)
DECLARE @knownSalt NVARCHAR(128)
 
SET @knownUserName = 'Tonic'
SET @lostUserName = 'host'
 
SELECT @knownPassword = Password, @knownSalt = PasswordSalt
FROM aspnet_Membership
INNER JOIN aspnet_users
ON aspnet_Membership.UserId = aspnet_users.UserId
WHERE UserName = @knownUserName;
 
PRINT ''
PRINT 'Known Password for "' + @knownUserName + '" is : ' + @knownPassword
PRINT 'Known Password Salt for "' + @knownUserName + '" is : ' + @knownSalt
 
SELECT @lostUserId = aspnet_Membership.UserId
FROM aspnet_Membership
INNER JOIN aspnet_users
ON aspnet_Membership.UserId = aspnet_users.UserId
WHERE UserName = @lostUserName;
 
PRINT ''
PRINT 'UserID for "' + @lostUserName + '" is : ' + @lostUserId
PRINT ''
 
IF (DATALENGTH(@lostUserName) <= 0 OR @lostUserName IS NULL)
PRINT 'Invalid Lost User Name ' + @lostUserName
ELSE BEGIN
IF (DATALENGTH(@knownUserName) <= 0 OR @knownUserName IS NULL)
PRINT 'Invalid Lost User Name ' + @lostUserName
ELSE BEGIN
IF (DATALENGTH(@knownPassword) <= 0 OR @knownPassword IS NULL)
PRINT 'Invalid Known Password ' + @knownPassword
ELSE BEGIN
IF (DATALENGTH(@knownSalt) <= 0 OR @knownSalt IS NULL)
PRINT 'Invalid Known Salt ' + @knownSalt
ELSE BEGIN
PRINT ''
PRINT 'BEFORE'
SELECT LEFT(UserName, 12) AS UserName, aspnet_Membership.UserId, LEFT(Email, 20) AS Email, Password, PasswordSalt
FROM aspnet_Membership INNER JOIN aspnet_users ON aspnet_Membership.UserId = aspnet_users.UserId
WHERE UserName IN ( @knownUserName, @lostUserName );
PRINT ''
PRINT 'Changing Password for User Id : "' + @lostUserId + '" to "' + @knownPassword + '"'
PRINT ''
UPDATE aspnet_Membership
SET Password = @knownPassword,
PasswordSalt = @knownSalt
-- SELECT UserId, Password, PasswordSalt
-- FROM aspnet_Membership
WHERE UserId = @lostUserId;
PRINT ''
PRINT 'AFTER'
SELECT LEFT(UserName, 12) AS UserName, aspnet_Membership.UserId, LEFT(Email, 20) AS Email, Password, PasswordSalt
FROM aspnet_Membership INNER JOIN aspnet_users ON aspnet_Membership.UserId = aspnet_users.UserId
WHERE UserName IN ( @knownUserName, @lostUserName );
END
END
END
END
GO
PRINT '' PRINT ' * * * END OF SCRIPT * * *' PRINT '' GO

显然, 前面已经提过, 我没有一个现成的知道密码的帐号, 所以再次失败.

方案五

由于我之前的失败都因为没有一个可以工作的而且知道密码的帐号, 而又不能在网站上注册新帐号, 于是我找到了一个方法可以用SQL创建一个新帐号.

query如下

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
SELECT m.password, m.passwordsalt, m.passwordformat
FROM aspnet_users u
    INNER JOIN aspnet_membership m
        ON (u.userid = m.userid)
WHERE u.UserName = 'TestUser'
SELECT PortalId,
     PortalName
FROM Portals
DECLARE @ApplicationName nvarchar(256)
SET @ApplicationName = 'DotNetNuke'
DECLARE @UserName nvarchar(256) 
SET @UserName = 'NewUser' --The new user
DECLARE @Password nvarchar(128)
--From the existing user
SET @Password = 'LLSXX8xW6+0EbrV4JBzL/YenA1D6BBfRnkYY7FtQvNGmmPOhVdPiAA=='
DECLARE @PasswordSalt nvarchar(128)
--From the existing user
SET @PasswordSalt = 'P40ky5tExsx37nUIFnCWZQ=='
DECLARE @Email nvarchar(256)
SET @Email = 'TestingAccount@test.com' --You can set this to whatever you want
DECLARE @PasswordQuestion nvarchar(256)
SET @PasswordQuestion = ''
DECLARE @PasswordAnswer nvarchar(128)
SET @PasswordAnswer = ''
DECLARE @IsApproved bit
SET @IsApproved = 1
DECLARE @CurrentTimeUtc datetime 
SET @CurrentTimeUtc = GETDATE()
DECLARE @CreateDate datetime
SET @CreateDate = @CurrentTimeUtc
DECLARE @UniqueEmail int
SET @UniqueEmail = 0
DECLARE @PasswordFormat int
SET @PasswordFormat = 2 --NOTE: Value from existing user!
DECLARE @PortalId int
SET @PortalId = 0    --The id of your portal
Declare @UserId uniqueidentifier
DECLARE @DNNUserId int
 
--Make the stored procedure call
EXEC dbo.aspnet_Membership_CreateUser @ApplicationName, @Username, @Password,
                @PasswordSalt, @email, @passwordquestion, @PasswordAnswer, 
                @IsApproved, @CurrentTimeUtc, @CreateDate, @UniqueEmail,
                @PasswordFormat, @UserId
 
--Insert the record into the DotNetNuke users table
INSERT INTO users (Username, FirstName, LastName, IsSuperUser, Email,
                    DisplayName, UpdatePassword)
    VALUES(@Username, 'My', 'NewAccount', 0, @Email, 'New Account', 0)
 
--Get the new userid, from the DNN users table
SELECT @dnnuserid = userid
FROM Users
WHERE username = @Username
 
--Now, insert the record into the user portals table
INSERT INTO UserPortals (userId, PortalId, CreatedDate)
    VALUES(@dnnuserid, @PortalId, GETDATE()) 
 
--Now Give the user permissions to the RECISTERED Users group
INSERT INTO UserRoles (userId, roleId)
SELECT @dnnuserid,
        roleId
FROM Roles
WHERE RoleName = 'Registered Users'

然而我不得不再次不幸的告诉大家, 从这个query来看, 要使用这个query建立一个DNN新用户, 也是需要事先有一个知道密码, salt, applicationname的帐户才可以的, 可悲的是我的密码和salt密文已经被我完全删干净了. 再次失败.

 

成功方案

 

经过了这数次失败以后, 我终于放弃google了, 看来这次真的无法找到我要的答案, 既然数据库方向我无法找到结果, 那么只能从代码上动手脚了, 不就是想注册新用户么, 我开始找login module的code.

image 

DNN的这个目录下存放着login module的代码, 打开后开始查找对portal setting里Userregistration属性的判定, 经过一翻查找和测试, 总算找到了, Page_Load里面的这一段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
            If Not Request.IsAuthenticated Then
                If Page.IsPostBack = False Then
                    Try
                        If Not Request.QueryString("username") Is Nothing Then
                            txtUsername.Text = Request.QueryString("username")
                        End If
                        If Not Request.QueryString("verificationcode") Is Nothing Then
                            If PortalSettings.UserRegistration = PortalRegistrationType.VerifiedRegistration Then
                                ' Comment this line above and the "End If" below out if you can't login as admin or host 
                                '   and can't change the register setting in SQL and don't even have an password-known account 
                                '   and really really desperatly want to register a new user.   -- Kang 10.07.2008
 
                                'Display Verification Rows 
                                rowVerification1.Visible = True
                                rowVerification2.Visible = True
                                txtVerification.Text = Request.QueryString("verificationcode")
                            End If
                        End If
 
                    Catch
                        'control not there 
                    End Try
                End If

这段代码中的comment是我加的.. (当时实在很无语), 判定网站是否开放注册的部分就是其中If PortalSettings.UserRegistration = PortalRegistrationType.VerifiedRegistration Then 和下面的End IF这两行, 我把他们前面加个’ 给comment掉再重新编译运行网站,  register就出来了, 之后我把自己新注册的用户在数据库中的superuser设成true(dbo.Users表里面), 然后用这个帐号登录DNN, 把host和admin的salt随便写一个在数据库里加上, 再把他俩的密码reset, 然后重新change password为我想要的, 大功告成. 本来也可以用上面几个方案中给出的换密码的办法, 不过看了这么久的query我实在不想再弄了, 直接去万恶的DNN后台里改了.

希望对跟我一样犯了愚蠢错误的DNN开发人员有所帮助.

真的很长啊, 谢谢观赏.




留言