php|pikachu上的密码口令暴力破解


文章目录

  • (1)暴力破解是什么
  • (2)暴力破解的一般防范方法
  • (3)前后端常见的漏洞
    • <0>无防护下的暴力破解
    • <1>前端防护的漏洞
    • <2>后端防护的漏洞
    • <3>加入token的暴力破解
      • 【1】什么是Token
      • 【2】Token防什么
      • 【3】有Token情况下的举例

(1)暴力破解是什么 暴力破解在ctf中是归于密码口令里的知识,暴力破解常用于破解用户名或者密码,通过脚本不断的尝试可能的密码类型。类似nmap这一类工具,爆网站的目录的时候也是通过这种逻辑,用各种常用路径的尝试暴力破解文件网站的路径的。
(2)暴力破解的一般防范方法
  • 1、设计更难得验证码
  • 2、后端主要不要有逻辑上的漏洞(不要将验证码以文本得形式以各种方法返回给前端,或者出现输入不同错误导致返回有什么明显不同)
  • 3、php.ini设置合理且注意用完验证码之后就要被销毁
  • 4、设置失败登录限制,登录失败次数太多就要被限制一段时间之后再登录
(3)前后端常见的漏洞 <0>无防护下的暴力破解 拿pikachu上的 ‘ 基于表单的暴力破解 ’ 举例
【源码分析】
打开“基于表单的暴力破解”模块鼠标右键打开菜单,点击查看源码,会发现没有设置javascript代码做验证码防护,再打开后端的bf_form.php文件,审计后端源码,如下图:
if(isset($_POST['submit']) && $_POST['username'] && $_POST['password']){ $username = $_POST['username']; $password = $_POST['password']; $sql = "select * from users where username=? and password=md5(?)"; $line_pre = $link->prepare($sql); $line_pre->bind_param('ss',$username,$password); if($line_pre->execute()){ $line_pre->store_result(); if($line_pre->num_rows>0){ $html.= ' login success
'; } else{ $html.= ' username or password is not exists~
'; } } else{ $html.= '执行错误:'.$line_pre->errno.'错误信息:'.$line_pre->error.'
'; } }

代码做了这么几件事
  • 1、将传过来的username和pasword分别赋值给$username$password
  • 2、进行sql预编译(上面的函数中没有相应的$link代码,其实在这之前有$link=connect();这个语句才能用prepare方法进行预编译)
  • 3、用bind_param方法将$username$password变量传入sql语句中(bind_param中的第一个参数是按照顺序对输入的变量做声明类型的,比如有s–string、d–double、i–integer、b–bool)
  • 4、后用store_result方法将执行之后的结果集转移,然后用num_rows看返回的结果集有几行,若大于零则返回’login success‘
从上面分析出代码对sql做了防范但是没有生成验证码的模块,也没有检验验证码的模块也就是说没有对暴力破解做防护。我们可以直接用burpsuite抓包之后用intruder模块进行爆破。
【操作步骤】
  • 1、首先输入一个随机的账户名和密码,作者这里的密码被我用开发者工具改了一下html不然密码会是一个个实心的黑弹弹

  • 2、之后在inetcpl.cpl中设置代理,用burpsuite抓包

  • 3、发送到intruder模块设置attack type类型为cluster bomb将默认的变量clear,然后加入新的变量。cluster bomb类型允许给不同变量设置不同的字典,且组合字典中的各种值。(下图和intruder模块没有任何关系,截图截错了)

  • 4、在Payloads模块添加爆破的字典,下面要做的就是给第一和第二个变量分别甚至字典,有自己的字典的可以用add from list处添加或者修改payload type的类型为runting list都可以打开目录导入字典。笔者因为已经直到真正的密码是什么,所以没有导入,在simple list模式下添加了几个词段意思一下。


  • 5、之后点击start attack开始爆破,爆破完成之后会发现用户名为admin而密码为123456的返回长度和其它的完全不同,这就说明该结果为正确答案

<1>前端防护的漏洞
  • 1、前端javascript代码生成验证码并且验证,这一类方法就是欺负老实人不懂技术。
  • 2、后端生成的验证码直接输出到了返回的文件中
  • 3、验证码出现在返回的html中
要以pikachu上面的验证码绕过(on client)模块举例,值得一提的是,在pikachu上的关卡没有设置后端验证码直接泄漏到网页的源码里的情况,这种情况是不是就没办法联系?实则不然,这种情况的操作方法和有token的暴力破解类似,所以这里就只按照一般情况下去说,具体验证码泄漏到返回前端源码里的情况可以参考token。
【php|pikachu上的密码口令暴力破解】【源码分析】
在打开验证码绕过(on client)的模块鼠标右键点击查看源码,直接拉到最下面会发现有下面的javascript代码

这一段javascript代码做的事情有如下操作
  • 1、首先定义一个全局变量code
  • 2、定义一个create函数,这个函数做了以下几件事
    • (1)将code定义为空
    • (2)定义一个整型变量codeLengrh
    • (3)通过getElementByid的方法调用了id为checkCode的节点并将生成的句柄赋值给checkCode
    • (4)定义一个有36个元素的数组并且将这个数组赋值给selectChar
    • (5)定义一个循环五次的for循环,其中每次都要执行以下程序先用Math.random方法生成一个大于零小于一的数,之后将这个数乘36作为Math.floor的参数,Math.floor会随机返回一个小于或等于参数的数,并且将这个数用作selectCode变量的下标访问对应的元素,拼接之前的code值并再次赋值给code
    • (6)再对checkCode变量所指的checkCode节点设置value和classname
  • 3、定义一个validate函数,这个函数做了如下的事
    • (1)通过搜索器的方式将id为bf_client且class为vcode的节点的value值返回赋值给inputcode变量
    • (2)然后用if语句判断是否没有数值,和是否和code值一样,若为空的话就不会新生成一个验证码,而输入的验证码错误的话就会新生成一个验证码
而检查后端之后发现如下
if(isset($_POST['submit'])){ if($_POST['username'] && $_POST['password']) { $username = $_POST['username']; $password = $_POST['password']; $sql = "select * from users where username=? and password=md5(?)"; $line_pre = $link->prepare($sql); $line_pre->bind_param('ss', $username, $password); if ($line_pre->execute()) { $line_pre->store_result(); if ($line_pre->num_rows > 0) { $html .= ' login success
'; } else { $html .= ' username or password is not exists~
'; } } else { $html .= '执行错误:' . $line_pre->errno . '错误信息:' . $line_pre->error . '
'; } }else{ $html .= ' please input username and password~
'; } }

可以发现,这一段代码和不做防护的代码不能说相像只能说一模一样,别说验证验证码,连接收一下传过来的验证码都没有,所以不多说了。
基于前后端是如上的情况,我们可以用的绕过方式是,先输入正确的验证码通过javascript代码的检验,用burpsuite抓包直接在intruder模块进行爆破。
【操作步骤】
  • 1、首先输入用户名、密码还有正确的验证码

  • 2、再用burpsuite抓包,由以上的分析可以得出,vcode变量可以是任意值,因为后端没有检验,attack type类型建议设置为cluster bomb,这个类型允许给不同变量设置不同的字典,且会组合各种不同的组合方式

  • 3、然后和没有在前后端设置防护的操作一样设置payload


  • 4、最后点击start attack

<2>后端防护的漏洞
  • 1、后端生成的验证码逻辑过于简单,有明显的规律这样也会导致无法防护成功
  • 2、后端的验证有逻辑上的漏洞,比如密码和用户名输入正确而验证码输入错误,与验证码与密码和用户名都错误时返回的文件长度不同。
  • 3、一个验证码在没有用完就用unset函数删除的情况下默认存在24分钟,这样就会导致前端输入的验证码只要输入一个之前生成的且在24分钟的时效内的验证码都能被验证成功。
这一部分还是要用pikachu上的举例,用的是验证码绕过(on serve)模块
【源码分析】
前端是没有验证的,直接看后端,朋友们可以在pikachu上的vul目录下打开breakforce目录找到bf_server.php查看源码,后端的验证代码如下
$link=connect(); $html=""; if(isset($_POST['submit'])) { if (empty($_POST['username'])) { $html .= "用户名不能为空
"; } else { if (empty($_POST['password'])) { $html .= "密码不能为空
"; } else { if (empty($_POST['vcode'])) { $html .= "验证码不能为空哦!
"; } else { //验证验证码是否正确 if (strtolower($_POST['vcode']) != strtolower($_SESSION['vcode'])) { $html .= "验证码输入错误哦!
"; //应该在验证完成后,销毁该$_SESSION['vcode'] }else{ $username = $_POST['username']; $password = $_POST['password']; $vcode = $_POST['vcode']; $sql = "select * from users where username=? and password=md5(?)"; $line_pre = $link->prepare($sql); $line_pre->bind_param('ss',$username,$password); if($line_pre->execute()){ $line_pre->store_result(); //虽然前面做了为空判断,但最后,却没有验证验证码!!! if($line_pre->num_rows()==1){ $html.=' login success
'; }else{ $html.= ' username or password is not exists~
'; } }else{ $html.= '执行错误:'.$line_pre->errno.'错误信息:'.$line_pre->error.'
'; } } } } } }

这一段代码做了以下几件事:
  • 1、调用connect函数并将产生的句柄赋值给$Link变量
  • 2、定义并给$html变量赋值为空
  • 3、调用多个if函数实现密码为空时返回’密码为空‘,用户名为空时返回’用户名为空‘,验证码为空的时返回’验证码为空‘,全不为空时执行之后的程序
  • 4、若验证码、密码、用户名都不为空,会用$_POST方法调取vcode变量与用$SESSION方法调取存储在会话中存储的vcode变量做比较,若不同则返回’验证码输入错误‘,且比对时用了strtolower函数将两个变量全部转换为了小写
  • 5、若验证码正确,则执行的程序与之前无防护时的操作一样都是先对sql语句进行预编译,然后查询,所以这里就不赘述了
由以上的分析发现在验证码验证错误和验证正确之后没有将验证码用unset函数取消,而验证码默认最多再后台存储24分钟,也就是说在这24分钟的时间内,用这个验证码就能一直尝试登录。
【操作步骤】
  • 1、首先还是输入用户名、密码和验证码,这里的验证码就是后端产生之后返回到前端的,不是前端产生的

  • 2、之后就是burpsuite抓包,由上面的分析可以直到这里的vcode变量就不能随便的输入任意值,还是和之前一样设置变量,设置attack type类型为culster bomb

  • 3、在payload板块设置字典,如下


  • 4、再点击start attack,开始爆破

<3>加入token的暴力破解 【1】什么是Token
Token可以理解为一个随机生成的字符串,这个字符串会被服务器发送给前端,并一般将这个字符串保存到要发送到后端的form表单中,这样跨域的访问就能被马上识别出来,识别身份之后服务器才会根据token发送端可以执行的操作做出相应的动作
【2】Token防什么
先说结论,Token防的是CSRF(跨域请求伪造)不防暴力破解。Token是一串字符串,一般Token是由 请求的URL+时间戳+随机数 组合而成,并且被保存在前端的form表单中,当浏览器发起请求的时候一起被发送到后端,用于身份验证。在burpsuite中的intruder模块可以在options板块找到Grep Exreact部分将html文件中的token在发送之前赋值给请求报文中的某一变量
【3】有Token情况下的举例
还是以pikachu上的模块举例
【源码分析】
$link=connect(); $html=""; if(isset($_POST['submit']) && $_POST['username'] && $_POST['password'] && $_POST['token']){ $username = $_POST['username']; $password = $_POST['password']; $token = $_POST['token']; $sql = "select * from users where username=? and password=md5(?)"; $line_pre = $link->prepare($sql); $line_pre->bind_param('ss',$username,$password); if($token == $_SESSION['token']){ if($line_pre->execute()){ $line_pre->store_result(); if($line_pre->num_rows>0){ $html.= ' login success
'; } else{ $html.= ' username or password is not exists~
'; } }else{ $html.= '执行错误:'.$line_pre->errno.'错误信息:'.$line_pre->error.'
'; } }else{ $html.= ' csrf token error
'; } } //生成token set_token();

后端代码做的还是预编译sql语句,只不过多加了一个生成token的函数—set_token函数,这个函数的代码如下
function set_token(){ if(isset($_SESSION['token'])){ unset($_SESSION['token']); } $_SESSION['token']=str_replace('.','',uniqid(mt_rand(10000,99999),true)); }

通过这个代码执行的操作是,删除原有Token再设置新的Token所以我们在用burpsuite的时候要将上一次请求返回的报文中的Token作为参数加入到请求报文中
【操作步骤】
有Token的情况下在burpsuite上的操作,关键的点是在burpsuite中怎样的操作才能设置将上一个请求得到的返回中的值截取下来作为数据添加到报文中
  • 1、首先输入用户名、密码

  • 2、用burpsuite抓包到payload position板块,这次不能像之前一样将attack type设置为cluster bomb,应该将其设置为pitchfork,如果设置成cluster bomb模块,就会发现burpsuite会持续爆破,这是因为每一次传回来的Token都不同,而这个模式会组合所有的情况,这样就会一直爆破。还要设置三个变量,如下图
    php|pikachu上的密码口令暴力破解
    文章图片

  • 3、再在options板块将线程设置为1,这是因为设置多个线程之后,程序会不知道本次请求的Token要设置的是哪一个返回报文中的Token

  • 4、同样在这一个板块下,还要设置Grep Extra,在板块中找到这一个设置勾选Extra the following items from reponses前的小框,这样就能设置在返回报文中找参数,之后点击add,再点击fetch reponse,并按照下图设置设置截取的内容


  • 5、在payload板块中如下设置每一个变量的字典



  • 6、点击Start Attack开始爆破

    推荐阅读